Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 30 Dec 2015 17:34:05 -0800
changeset 300153 ee14715f713146f3e9298fedd5f955b7dbbe45ff
parent 300115 ec3eeea80a5e844869e9897dd496ef312ef17dfe (current diff)
parent 300152 3e4d6d892dd8e8bfb31022ca5268c17d7529c41b (diff)
child 300154 22f51211915bf7daff076180847a7140d35aa353
push id8978
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 14:05:32 +0000
treeherdermozilla-aurora@b9a803752a2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone46.0a1
Merge inbound to m-c a=merge
js/src/jit-test/tests/asm.js/testBug1100237.js
js/src/jit-test/tests/asm.js/testResize.js
js/src/jit-test/tests/asm.js/testTimeout7-nosignals.js
js/src/jit-test/tests/asm.js/testTimeout7.js
js/src/jit-test/tests/basic/testArrayBufferTransfer.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1568,21 +1568,19 @@ pref("browser.tabs.remote.autostart.2", 
 #endif
 
 // For the about:tabcrashed page
 pref("browser.tabs.crashReporting.sendReport", true);
 pref("browser.tabs.crashReporting.includeURL", false);
 pref("browser.tabs.crashReporting.emailMe", false);
 pref("browser.tabs.crashReporting.email", "");
 
-#ifdef NIGHTLY_BUILD
 #ifndef MOZ_MULET
 pref("layers.async-pan-zoom.enabled", true);
 #endif
-#endif
 
 #ifdef E10S_TESTING_ONLY
 // Enable e10s add-on interposition by default.
 pref("extensions.interposition.enabled", true);
 pref("extensions.interposition.prefetching", true);
 #endif
 
 pref("browser.defaultbrowser.notificationbar", false);
--- a/configure.in
+++ b/configure.in
@@ -7986,16 +7986,25 @@ else
 MOZ_ENABLE_SKIA=
 fi
 
 MOZ_ARG_ENABLE_BOOL(skia,
 [  --enable-skia   Enable use of Skia],
 MOZ_ENABLE_SKIA=1,
 MOZ_ENABLE_SKIA=)
 
+dnl Skia GPU support may not reliably build on certain *BSDs (see bug 1234494).
+if test "${OS_TARGET}" = "OpenBSD" -o \
+        "${OS_TARGET}" = "NetBSD" -o \
+        "${OS_ARCH}" = "SunOS"; then
+    MOZ_DISABLE_SKIA_GPU=1
+else
+    MOZ_DISABLE_SKIA_GPU=
+fi
+
 MOZ_ARG_DISABLE_BOOL(skia-gpu,
 [  --disable-skia-gpu  Disable use of Skia-GPU],
 MOZ_DISABLE_SKIA_GPU=1,
 MOZ_DISABLE_SKIA_GPU=)
 
 if test "$USE_FC_FREETYPE"; then
     if test "$COMPILE_ENVIRONMENT"; then
         dnl ========================================================
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -108,17 +108,17 @@ struct DevTools : public ::testing::Test
     return &globalClass;
   }
 
   JSObject* createGlobal()
   {
     /* Create the global object. */
     JS::RootedObject newGlobal(cx);
     JS::CompartmentOptions options;
-    options.setVersion(JSVERSION_LATEST);
+    options.behaviors().setVersion(JSVERSION_LATEST);
     newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
                                    JS::FireOnNewGlobalHook, options);
     if (!newGlobal)
       return nullptr;
 
     JSAutoCompartment ac(cx, newGlobal);
 
     /* Populate the global object with the standard globals, like Object and
new file mode 100755
--- /dev/null
+++ b/dom/apps/tests/create_test_receipts.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python
+
+import jwt
+
+receipt1 = {
+  "typ": "purchase-receipt",
+  "product": {
+    "url": "https://www.mozilla.org",
+    "storedata": "5169314356"
+  },
+  "user": {
+    "type": "directed-identifier",
+    "value": "4fb35151-2b9b-4ba2-8283-c49d381640bd"
+  },
+  "iss": "http://mochi.test:8888",
+  "nbf": 131360185,
+  "iat": 131360188,
+  "detail": "http://mochi.test:8888/receipt/5169314356",
+  "verify": "http://mochi.test:8888/verify/5169314356",
+  "reissue": "http://mochi.test:8888/reissue/5169314356"
+}
+
+receipt2 = {
+  "typ": "purchase-receipt",
+  "product": {
+    "url": "https://www.mozilla.org",
+    "storedata": "5169314357"
+  },
+  "user": {
+    "type": "directed-identifier",
+    "value": "4fb35151-2b9b-4ba2-8283-c49d381640bd"
+  },
+  "iss": "http://mochi.test:8888",
+  "nbf": 131360185,
+  "iat": 131360188,
+  "detail": "http://mochi.test:8888/receipt/5169314356",
+  "verify": "http://mochi.test:8888/verify/5169314356",
+  "reissue": "http://mochi.test:8888/reissue/5169314356"
+}
+
+receipt_without_typ = {
+  "product": {
+    "url": "https://www.mozilla.org",
+    "storedata": "5169314358"
+  },
+  "user": {
+    "type": "directed-identifier",
+    "value": "4fb35151-2b9b-4ba2-8283-c49d381640bd"
+  },
+  "iss": "http://mochi.test:8888",
+  "nbf": 131360185,
+  "iat": 131360188,
+  "detail": "http://mochi.test:8888/receipt/5169314356",
+  "verify": "http://mochi.test:8888/verify/5169314356",
+  "reissue": "http://mochi.test:8888/reissue/5169314356"
+}
+
+receipt_without_product = {
+  "typ": "purchase-receipt",
+  "user": {
+    "type": "directed-identifier",
+    "value": "4fb35151-2b9b-4ba2-8283-c49d381640bd"
+  },
+  "iss": "http://mochi.test:8888",
+  "nbf": 131360185,
+  "iat": 131360188,
+  "detail": "http://mochi.test:8888/receipt/5169314356",
+  "verify": "http://mochi.test:8888/verify/5169314356",
+  "reissue": "http://mochi.test:8888/reissue/5169314356"
+}
+
+receipt_without_user = {
+  "typ": "purchase-receipt",
+  "product": {
+    "url": "https://www.mozilla.org",
+    "storedata": "5169314358"
+  },
+  "iss": "http://mochi.test:8888",
+  "nbf": 131360185,
+  "iat": 131360188,
+  "detail": "http://mochi.test:8888/receipt/5169314356",
+  "verify": "http://mochi.test:8888/verify/5169314356",
+  "reissue": "http://mochi.test:8888/reissue/5169314356"
+}
+
+receipt_without_iss = {
+  "typ": "purchase-receipt",
+  "product": {
+    "url": "https://www.mozilla.org",
+    "storedata": "5169314358"
+  },
+  "user": {
+    "type": "directed-identifier",
+    "value": "4fb35151-2b9b-4ba2-8283-c49d381640bd"
+  },
+  "nbf": 131360185,
+  "iat": 131360188,
+  "detail": "http://mochi.test:8888/receipt/5169314356",
+  "verify": "http://mochi.test:8888/verify/5169314356",
+  "reissue": "http://mochi.test:8888/reissue/5169314356"
+}
+
+receipt_without_nbf = {
+  "typ": "purchase-receipt",
+  "product": {
+    "url": "https://www.mozilla.org",
+    "storedata": "5169314358"
+  },
+  "user": {
+    "type": "directed-identifier",
+    "value": "4fb35151-2b9b-4ba2-8283-c49d381640bd"
+  },
+  "iss": "http://mochi.test:8888",
+  "iat": 131360188,
+  "detail": "http://mochi.test:8888/receipt/5169314356",
+  "verify": "http://mochi.test:8888/verify/5169314356",
+  "reissue": "http://mochi.test:8888/reissue/5169314356"
+}
+
+receipt_without_iat = {
+  "typ": "purchase-receipt",
+  "product": {
+    "url": "https://www.mozilla.org",
+    "storedata": "5169314358"
+  },
+  "user": {
+    "type": "directed-identifier",
+    "value": "4fb35151-2b9b-4ba2-8283-c49d381640bd"
+  },
+  "iss": "http://mochi.test:8888",
+  "nbf": 131360185,
+  "detail": "http://mochi.test:8888/receipt/5169314356",
+  "verify": "http://mochi.test:8888/verify/5169314356",
+  "reissue": "http://mochi.test:8888/reissue/5169314356"
+}
+
+receipt_with_wrong_typ = {
+  "typ": "fake",
+  "product": {
+    "url": "https://www.mozilla.org",
+    "storedata": "5169314358"
+  },
+  "user": {
+    "type": "directed-identifier",
+    "value": "4fb35151-2b9b-4ba2-8283-c49d381640bd"
+  },
+  "iss": "http://mochi.test:8888",
+  "nbf": 131360185,
+  "iat": 131360188,
+  "detail": "http://mochi.test:8888/receipt/5169314356",
+  "verify": "http://mochi.test:8888/verify/5169314356",
+  "reissue": "http://mochi.test:8888/reissue/5169314356"
+}
+
+print("let valid_receipt1 = \"" + jwt.encode(receipt1, "") + "\";\n")
+print("let valid_receipt2 = \"" + jwt.encode(receipt2, "") + "\";\n")
+print("let receipt_without_typ = \"" + jwt.encode(receipt_without_typ, "") + "\";\n")
+print("let receipt_without_product = \"" + jwt.encode(receipt_without_product, "") + "\";\n")
+print("let receipt_without_user = \"" + jwt.encode(receipt_without_user, "") + "\";\n")
+print("let receipt_without_iss = \"" + jwt.encode(receipt_without_iss, "") + "\";\n")
+print("let receipt_without_nbf = \"" + jwt.encode(receipt_without_nbf, "") + "\";\n")
+print("let receipt_without_iat = \"" + jwt.encode(receipt_without_iat, "") + "\";\n")
+print("let receipt_with_wrong_typ = \"" + jwt.encode(receipt_with_wrong_typ, "") + "\";\n")
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1804,18 +1804,18 @@ nsMessageManagerScriptExecutor::InitChil
 {
   AutoSafeJSContext cx;
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES;
 
   JS::CompartmentOptions options;
-  options.setZone(JS::SystemZone)
-         .setVersion(JSVERSION_LATEST);
+  options.creationOptions().setZone(JS::SystemZone);
+  options.behaviors().setVersion(JSVERSION_LATEST);
 
   nsresult rv =
     xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
                                          flags, options, getter_AddRefs(mGlobal));
   NS_ENSURE_SUCCESS(rv, false);
 
 
   JS::Rooted<JSObject*> global(cx, mGlobal->GetJSObject());
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2320,23 +2320,21 @@ CreateNativeGlobalForInner(JSContext* aC
     top = aNewInner->GetTopInternal();
   }
   JS::CompartmentOptions options;
 
   // Sometimes add-ons load their own XUL windows, either as separate top-level
   // windows or inside a browser element. In such cases we want to tag the
   // window's compartment with the add-on ID. See bug 1092156.
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    options.setAddonId(MapURIToAddonID(aURI));
-  }
-
-  if (top) {
-    if (top->GetGlobalJSObject()) {
-      options.setSameZoneAs(top->GetGlobalJSObject());
-    }
+    options.creationOptions().setAddonId(MapURIToAddonID(aURI));
+  }
+
+  if (top && top->GetGlobalJSObject()) {
+    options.creationOptions().setSameZoneAs(top->GetGlobalJSObject());
   }
 
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
                         TreatAsRemoteXUL(aPrincipal);
   uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT;
   flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK;
 
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -4,36 +4,51 @@
  * 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 "nsWrapperCacheInlines.h"
 
 #include "js/Class.h"
 #include "js/Proxy.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
+#include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/HoldDropJSObjects.h"
 #include "nsCycleCollectionTraversalCallback.h"
 #include "nsCycleCollector.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #ifdef DEBUG
 /* static */ bool
 nsWrapperCache::HasJSObjectMovedOp(JSObject* aWrapper)
 {
     return js::HasObjectMovedOp(aWrapper);
 }
 #endif
 
-/* static */ void
+void
 nsWrapperCache::HoldJSObjects(void* aScriptObjectHolder,
                               nsScriptObjectTracer* aTracer)
 {
   cyclecollector::HoldJSObjectsImpl(aScriptObjectHolder, aTracer);
+  if (mWrapper && !JS::ObjectIsTenured(mWrapper)) {
+    CycleCollectedJSRuntime::Get()->NurseryWrapperPreserved(mWrapper);
+  }
+}
+
+void
+nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper)
+{
+  mWrapper = aWrapper;
+  UnsetWrapperFlags(kWrapperFlagsMask & ~WRAPPER_IS_NOT_DOM_BINDING);
+
+  if (aWrapper && !JS::ObjectIsTenured(aWrapper)) {
+    CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this);
+  }
 }
 
 void
 nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder)
 {
   if (PreservingWrapper()) {
     // PreserveWrapper puts new DOM bindings in the JS holders hash, but they
     // can also be in the DOM expando hash, so we need to try to remove them
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -253,24 +253,25 @@ public:
   }
 
   void ReleaseWrapper(void* aScriptObjectHolder);
 
 protected:
   void TraceWrapper(JSTracer* aTrc, const char* name)
   {
     if (mWrapper) {
-      JS_CallObjectTracer(aTrc, &mWrapper, name);
+      JS_CallUnbarrieredObjectTracer(aTrc, &mWrapper, name);
     }
   }
 
   void PoisonWrapper()
   {
     if (mWrapper) {
-      mWrapper.setToCrashOnTouch();
+      // See setToCrashOnTouch() in RootingAPI.h
+      mWrapper = reinterpret_cast<JSObject*>(1);
     }
   }
 
 private:
   friend class mozilla::dom::TabChildGlobal;
   friend class mozilla::dom::ProcessGlobal;
   friend class SandboxPrivate;
   friend class nsInProcessTabChildGlobal;
@@ -282,23 +283,17 @@ private:
     SetWrapperFlags(WRAPPER_IS_NOT_DOM_BINDING);
   }
 
   JSObject *GetWrapperJSObject() const
   {
     return mWrapper;
   }
 
-  void SetWrapperJSObject(JSObject* aWrapper)
-  {
-    mWrapper = aWrapper;
-    UnsetWrapperFlags(kWrapperFlagsMask & ~WRAPPER_IS_NOT_DOM_BINDING);
-  }
-
-  void TraceWrapperJSObject(JSTracer* aTrc, const char* aName);
+  void SetWrapperJSObject(JSObject* aWrapper);
 
   FlagsType GetWrapperFlags() const
   {
     return mFlags & kWrapperFlagsMask;
   }
 
   bool HasWrapperFlag(FlagsType aFlag) const
   {
@@ -313,18 +308,18 @@ private:
   }
 
   void UnsetWrapperFlags(FlagsType aFlagsToUnset)
   {
     MOZ_ASSERT((aFlagsToUnset & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
     mFlags &= ~aFlagsToUnset;
   }
 
-  static void HoldJSObjects(void* aScriptObjectHolder,
-                            nsScriptObjectTracer* aTracer);
+  void HoldJSObjects(void* aScriptObjectHolder,
+                     nsScriptObjectTracer* aTracer);
 
 #ifdef DEBUG
 public:
   void CheckCCWrapperTraversal(void* aScriptObjectHolder,
                                nsScriptObjectTracer* aTracer);
 private:
 #endif // DEBUG
 
@@ -344,18 +339,18 @@ private:
   /**
    * If this bit is set then the wrapper for the native object is not a DOM
    * binding.
    */
   enum { WRAPPER_IS_NOT_DOM_BINDING = 1 << 1 };
 
   enum { kWrapperFlagsMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_NOT_DOM_BINDING) };
 
-  JS::Heap<JSObject*> mWrapper;
-  FlagsType           mFlags;
+  JSObject* mWrapper;
+  FlagsType mFlags;
 };
 
 enum { WRAPPER_CACHE_FLAGS_BITS_USED = 2 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
 
 #define NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY                                 \
   if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) {                            \
--- a/dom/base/nsWrapperCacheInlines.h
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -48,15 +48,9 @@ nsWrapperCache::HasNothingToTrace(nsISup
 }
 
 inline bool
 nsWrapperCache::IsBlackAndDoesNotNeedTracing(nsISupports* aThis)
 {
   return IsBlack() && HasNothingToTrace(aThis);
 }
 
-inline void
-nsWrapperCache::TraceWrapperJSObject(JSTracer* aTrc, const char* aName)
-{
-  JS_CallObjectTracer(aTrc, &mWrapper, aName);
-}
-
 #endif /* nsWrapperCache_h___ */
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3043,17 +3043,17 @@ RegisterDOMNames();
 // returned.  This should be the DOM prototype for the global.
 template <class T, ProtoHandleGetter GetProto>
 JS::Handle<JSObject*>
 CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
              const JSClass* aClass, JS::CompartmentOptions& aOptions,
              JSPrincipals* aPrincipal, bool aInitStandardClasses,
              JS::MutableHandle<JSObject*> aGlobal)
 {
-  aOptions.setTrace(CreateGlobalOptions<T>::TraceGlobal);
+  aOptions.creationOptions().setTrace(CreateGlobalOptions<T>::TraceGlobal);
 
   aGlobal.set(JS_NewGlobalObject(aCx, aClass, aPrincipal,
                                  JS::DontFireOnNewGlobalHook, aOptions));
   if (!aGlobal) {
     NS_WARNING("Failed to create global");
     return nullptr;
   }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -417,16 +417,18 @@ class CGDOMJSClass(CGThing):
         if self.descriptor.isGlobal():
             classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
             traceHook = "JS_GlobalObjectTraceHook"
             reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
         else:
             classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
             traceHook = 'nullptr'
             reservedSlots = slotCount
+        if self.descriptor.interface.isProbablyShortLivingObject():
+            classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE"
         if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
             resolveHook = RESOLVE_HOOK_NAME
             mayResolveHook = MAY_RESOLVE_HOOK_NAME
             enumerateHook = ENUMERATE_HOOK_NAME
         elif self.descriptor.isGlobal():
             resolveHook = "mozilla::dom::ResolveGlobal"
             mayResolveHook = "mozilla::dom::MayResolveGlobal"
             enumerateHook = "mozilla::dom::EnumerateGlobal"
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -559,16 +559,19 @@ class IDLExternalInterface(IDLObjectWith
         pass
 
     def getJSImplementation(self):
         return None
 
     def isJSImplemented(self):
         return False
 
+    def isProbablyShortLivingObject(self):
+        return False
+
     def getNavigatorProperty(self):
         return None
 
     def _getDependentObjects(self):
         return set()
 
 
 class IDLPartialInterface(IDLObject):
@@ -1403,17 +1406,18 @@ class IDLInterface(IDLObjectWithScope, I
                 self.parentScope.globalNames.add(self.identifier.name)
                 self.parentScope.globalNameMapping[self.identifier.name].add(self.identifier.name)
                 self._isOnGlobalProtoChain = True
             elif (identifier == "NeedResolve" or
                   identifier == "OverrideBuiltins" or
                   identifier == "ChromeOnly" or
                   identifier == "Unforgeable" or
                   identifier == "UnsafeInPrerendering" or
-                  identifier == "LegacyEventInit"):
+                  identifier == "LegacyEventInit" or
+                  identifier == "ProbablyShortLivingObject"):
                 # Known extended attributes that do not take values
                 if not attr.noArguments():
                     raise WebIDLError("[%s] must take no arguments" % identifier,
                                       [attr.location])
             elif identifier == "Exposed":
                 convertExposedAttrToGlobalNameSet(attr,
                                                   self._exposureGlobalNames)
             elif (identifier == "Pref" or
@@ -1517,16 +1521,24 @@ class IDLInterface(IDLObjectWithScope, I
             return classId
         assert isinstance(classId, list)
         assert len(classId) == 1
         return classId[0]
 
     def isJSImplemented(self):
         return bool(self.getJSImplementation())
 
+    def isProbablyShortLivingObject(self):
+        current = self
+        while current:
+            if current.getExtendedAttribute("ProbablyShortLivingObject"):
+                return True
+            current = current.parent
+        return False
+
     def getNavigatorProperty(self):
         naviProp = self.getExtendedAttribute("NavigatorProperty")
         if not naviProp:
             return None
         assert len(naviProp) == 1
         assert isinstance(naviProp, list)
         assert len(naviProp[0]) != 0
         return naviProp[0]
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -3806,16 +3806,20 @@ CanvasRenderingContext2D::DrawOrMeasureT
 
   if (currentFontStyle->GetStyle()->size == 0.0F) {
     if (aWidth) {
       *aWidth = 0;
     }
     return NS_OK;
   }
 
+  if (!IsFinite(aX) || !IsFinite(aY)) {
+    return NS_OK;
+  }
+
   const ContextState &state = CurrentState();
 
   // This is only needed to know if we can know the drawing bounding box easily.
   bool doCalculateBounds = NeedToCalculateBounds();
 
   CanvasBidiProcessor processor;
 
   // If we don't have a style context, we can't set up vertical-text flags
new file mode 100644
--- /dev/null
+++ b/dom/canvas/crashtests/1229932-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom() {
+    var canvas = document.createElement('canvas');
+    var ctx = canvas.getContext('2d');
+    ctx.fillText("A", 0, 1e308);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -20,9 +20,10 @@ load 896047-2.html
 load 916128-1.html
 load 934939-1.html
 load 1099143-1.html
 load 1161277-1.html
 load 1183363.html
 load 1190705.html
 load 1223740-1.html
 load 1225381-1.html
+load 1229932-1.html
 load texImage2D.html
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -232,19 +232,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 JSObject*
 Event::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
-  if (mIsMainThreadEvent && !GetWrapperPreserveColor()) {
-    nsJSContext::LikelyShortLivingObjectCreated();
-  }
   return WrapObjectInternal(aCx, aGivenProto);
 }
 
 JSObject*
 Event::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return EventBinding::Wrap(aCx, this, aGivenProto);
 }
--- a/dom/filesystem/Directory.cpp
+++ b/dom/filesystem/Directory.cpp
@@ -29,17 +29,30 @@
 // by Directory#CreateFileW.
 #ifdef CreateFile
 #undef CreateFile
 #endif
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Directory)
+NS_IMPL_CYCLE_COLLECTION_CLASS(Directory)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Directory)
+  tmp->mFileSystem->Unlink();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Directory)
+  tmp->mFileSystem->Traverse(cb);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Directory)
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Directory)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Directory)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Directory)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 // static
--- a/dom/filesystem/FileSystemBase.h
+++ b/dom/filesystem/FileSystemBase.h
@@ -15,17 +15,17 @@ class nsPIDOMWindow;
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 class Directory;
 
 class FileSystemBase
 {
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemBase)
+  NS_INLINE_DECL_REFCOUNTING(FileSystemBase)
 public:
 
   // Create file system object from its string representation.
   static already_AddRefed<FileSystemBase>
   FromString(const nsAString& aString);
 
   FileSystemBase();
 
@@ -90,16 +90,21 @@ public:
     return mPermission;
   }
 
   bool
   RequiresPermissionChecks() const
   {
     return mRequiresPermissionChecks;
   }
+
+  // CC methods
+  virtual void Unlink() {}
+  virtual void Traverse(nsCycleCollectionTraversalCallback &cb) {}
+
 protected:
   virtual ~FileSystemBase();
 
   bool
   LocalPathToRealPath(const nsAString& aLocalPath, nsAString& aRealPath) const;
 
   // The local path of the root (i.e. the OS path, with OS path separators, of
   // the OS directory that acts as the root of this OSFileSystem).
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -11,20 +11,44 @@
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/unused.h"
+#include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
+namespace {
+
+class FileSystemReleaseRunnable : public nsRunnable
+{
+public:
+  explicit FileSystemReleaseRunnable(RefPtr<FileSystemBase>& aDoomed)
+    : mDoomed(nullptr)
+  {
+    aDoomed.swap(mDoomed);
+  }
+
+  NS_IMETHOD Run()
+  {
+    mDoomed->Release();
+    return NS_OK;
+  }
+
+private:
+  FileSystemBase* MOZ_OWNING_REF mDoomed;
+};
+
+} // anonymous namespace
+
 FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem)
   : mErrorValue(NS_OK)
   , mFileSystem(aFileSystem)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
 }
 
@@ -38,16 +62,22 @@ FileSystemTaskBase::FileSystemTaskBase(F
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
 }
 
 FileSystemTaskBase::~FileSystemTaskBase()
 {
+  if (!NS_IsMainThread()) {
+    RefPtr<FileSystemReleaseRunnable> runnable =
+      new FileSystemReleaseRunnable(mFileSystem);
+    MOZ_ASSERT(!mFileSystem);
+    NS_DispatchToMainThread(runnable);
+  }
 }
 
 FileSystemBase*
 FileSystemTaskBase::GetFileSystem() const
 {
   return mFileSystem.get();
 }
 
--- a/dom/filesystem/OSFileSystem.cpp
+++ b/dom/filesystem/OSFileSystem.cpp
@@ -71,10 +71,23 @@ OSFileSystem::IsSafeDirectory(Directory*
 {
   // The concept of "safe directory" is specific to the Device Storage API
   // where a directory is only "safe" if it belongs to the area of device
   // storage that it is being used with.
   MOZ_CRASH("Don't use OSFileSystem with the Device Storage API");
   return true;
 }
 
+void
+OSFileSystem::Unlink()
+{
+  mWindow = nullptr;
+}
+
+void
+OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb)
+{
+  OSFileSystem* tmp = this;
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/OSFileSystem.h
+++ b/dom/filesystem/OSFileSystem.h
@@ -7,17 +7,17 @@
 #ifndef mozilla_dom_OSFileSystem_h
 #define mozilla_dom_OSFileSystem_h
 
 #include "mozilla/dom/FileSystemBase.h"
 
 namespace mozilla {
 namespace dom {
 
-class OSFileSystem : public FileSystemBase
+class OSFileSystem final : public FileSystemBase
 {
 public:
   explicit OSFileSystem(const nsAString& aRootDir);
 
   void
   Init(nsPIDOMWindow* aWindow);
 
   // Overrides FileSystemBase
@@ -29,16 +29,20 @@ public:
   GetRootName(nsAString& aRetval) const override;
 
   virtual bool
   IsSafeFile(nsIFile* aFile) const override;
 
   virtual bool
   IsSafeDirectory(Directory* aDir) const override;
 
+  // CC methods
+  virtual void Unlink() override;
+  virtual void Traverse(nsCycleCollectionTraversalCallback &cb) override;
+
 private:
   virtual ~OSFileSystem() {}
 
    nsCOMPtr<nsPIDOMWindow> mWindow;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -305,28 +305,30 @@ IsMP4SupportedType(const nsACString& aTy
 }
 #endif
 
 /* static */ bool
 DecoderTraits::IsMP4TypeAndEnabled(const nsACString& aType)
 {
 #ifdef MOZ_FMP4
   return IsMP4SupportedType(aType);
+#else
+  return false;
 #endif
-  return false;
 }
 
 static bool
 IsMP3SupportedType(const nsACString& aType,
                    const nsAString& aCodecs = EmptyString())
 {
 #ifdef MOZ_OMX_DECODER
   return false;
+#else
+  return MP3Decoder::CanHandleMediaType(aType, aCodecs);
 #endif
-  return MP3Decoder::CanHandleMediaType(aType, aCodecs);
 }
 
 static bool
 IsAACSupportedType(const nsACString& aType,
                    const nsAString& aCodecs = EmptyString())
 {
   return ADTSDecoder::CanHandleMediaType(aType, aCodecs);
 }
--- a/dom/webidl/Event.webidl
+++ b/dom/webidl/Event.webidl
@@ -6,17 +6,17 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/2012/WD-dom-20120105/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [Constructor(DOMString type, optional EventInit eventInitDict),
- Exposed=(Window,Worker,System)]
+ Exposed=(Window,Worker,System), ProbablyShortLivingObject]
 interface Event {
   [Pure]
   readonly attribute DOMString type;
   [Pure]
   readonly attribute EventTarget? target;
   [Pure]
   readonly attribute EventTarget? currentTarget;
 
--- a/dom/webidl/MutationObserver.webidl
+++ b/dom/webidl/MutationObserver.webidl
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * http://dom.spec.whatwg.org
  */
 
+[ProbablyShortLivingObject]
 interface MutationRecord {
   [Constant]
   readonly attribute DOMString type;
   // .target is not nullable per the spec, but in order to prevent crashes,
   // if there are GC/CC bugs in Gecko, we let the property to be null.
   [Constant]
   readonly attribute Node? target;
   [Constant]
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1840,17 +1840,17 @@ RuntimeService::Init()
       MOZ_CRASH("Unable to connect PBackground actor for the main thread!");
     }
   }
 
   // Initialize JSSettings.
   if (!sDefaultJSSettings.gcSettings[0].IsSet()) {
     sDefaultJSSettings.runtimeOptions = JS::RuntimeOptions();
     sDefaultJSSettings.chrome.maxScriptRuntime = -1;
-    sDefaultJSSettings.chrome.compartmentOptions.setVersion(JSVERSION_LATEST);
+    sDefaultJSSettings.chrome.compartmentOptions.behaviors().setVersion(JSVERSION_LATEST);
     sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC;
 #ifdef JS_GC_ZEAL
     sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
     sDefaultJSSettings.gcZeal = 0;
 #endif
     SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
     SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD,
                            WORKER_DEFAULT_ALLOCATION_THRESHOLD);
@@ -2617,17 +2617,17 @@ RuntimeService::WorkerPrefChanged(const 
 
 void
 RuntimeService::JSVersionChanged(const char* /* aPrefName */, void* /* aClosure */)
 {
   AssertIsOnMainThread();
 
   bool useLatest = Preferences::GetBool("dom.workers.latestJSVersion", false);
   JS::CompartmentOptions& options = sDefaultJSSettings.content.compartmentOptions;
-  options.setVersion(useLatest ? JSVERSION_LATEST : JSVERSION_DEFAULT);
+  options.behaviors().setVersion(useLatest ? JSVERSION_LATEST : JSVERSION_DEFAULT);
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(LogViolationDetailsRunnable, nsRunnable)
 
 NS_IMETHODIMP
 LogViolationDetailsRunnable::Run()
 {
   AssertIsOnMainThread();
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -427,18 +427,19 @@ DedicatedWorkerGlobalScope::WrapGlobalOb
   // xpc::ExtraWarningsForSystemJS() read prefs that are cached on the main
   // thread. This is benignly racey.
   const bool discardSource = (usesSystemPrincipal ||
                               mWorkerPrivate->IsInPrivilegedApp()) &&
                              xpc::ShouldDiscardSystemSource();
   const bool extraWarnings = usesSystemPrincipal &&
                              xpc::ExtraWarningsForSystemJS();
 
-  options.setDiscardSource(discardSource)
-         .extraWarningsOverride().set(extraWarnings);
+  JS::CompartmentBehaviors& behaviors = options.behaviors();
+  behaviors.setDiscardSource(discardSource)
+           .extraWarningsOverride().set(extraWarnings);
 
   return DedicatedWorkerGlobalScopeBinding_workers::Wrap(aCx, this, this,
                                                          options,
                                                          GetWorkerPrincipal(),
                                                          true, aReflector);
 }
 
 void
@@ -791,17 +792,17 @@ const js::Class workerdebuggersandbox_cl
 void
 WorkerDebuggerGlobalScope::CreateSandbox(JSContext* aCx, const nsAString& aName,
                                          JS::Handle<JSObject*> aPrototype,
                                          JS::MutableHandle<JSObject*> aResult)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   JS::CompartmentOptions options;
-  options.setInvisibleToDebugger(true);
+  options.creationOptions().setInvisibleToDebugger(true);
 
   JS::Rooted<JSObject*> sandbox(aCx,
     JS_NewGlobalObject(aCx, js::Jsvalify(&workerdebuggersandbox_class), nullptr,
                        JS::DontFireOnNewGlobalHook, options));
   if (!sandbox) {
     JS_ReportError(aCx, "Can't create sandbox!");
     aResult.set(nullptr);
     return;
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -123,16 +123,26 @@ BasicLayerManager::PushGroupForLayer(gfx
       group.mMaskSurface = GetMaskForLayer(aLayer, &group.mMaskTransform);
       return group;
     }
   }
 
   Matrix maskTransform;
   RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform);
 
+  if (maskSurf) {
+    // The returned transform will transform the mask to device space on the
+    // destination. Since the User->Device space transform will be applied
+    // to the mask by PopGroupAndBlend we need to adjust the transform to
+    // transform the mask to user space.
+    Matrix currentTransform = ToMatrix(group.mFinalTarget->CurrentMatrix());
+    currentTransform.Invert();
+    maskTransform = maskTransform * currentTransform;
+  }
+
   if (aLayer->CanUseOpaqueSurface() &&
       ((didCompleteClip && aRegion.GetNumRects() == 1) ||
        !aContext->CurrentMatrix().HasNonIntegerTranslation())) {
     // If the layer is opaque in its visible region we can push a gfxContentType::COLOR
     // group. We need to make sure that only pixels inside the layer's visible
     // region are copied back to the destination. Remember if we've already
     // clipped precisely to the visible region.
     group.mNeedsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
@@ -167,18 +177,22 @@ BasicLayerManager::PopGroupForLayer(Push
 
   DrawTarget* dt = group.mFinalTarget->GetDrawTarget();
   RefPtr<DrawTarget> sourceDT = group.mGroupTarget->GetDrawTarget();
   group.mGroupTarget = nullptr;
 
   RefPtr<SourceSurface> src = sourceDT->Snapshot();
 
   if (group.mMaskSurface) {
-    dt->SetTransform(group.mMaskTransform * Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
-    dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)),
+    Point finalOffset = group.mFinalTarget->GetDeviceOffset();
+    dt->SetTransform(group.mMaskTransform * Matrix::Translation(-finalOffset));
+    Matrix surfTransform = group.mMaskTransform;
+    surfTransform.Invert();
+    dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, surfTransform *
+                                                           Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)),
                     group.mMaskSurface, Point(0, 0), DrawOptions(group.mOpacity, group.mOperator));
   } else {
     // For now this is required since our group offset is in device space of the final target,
     // context but that may still have its own device offset. Once PushGroup/PopGroup logic is
     // migrated to DrawTargets this can go as gfxContext::GetDeviceOffset will essentially
     // always become null.
     dt->SetTransform(Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
     dt->DrawSurface(src, Rect(group.mGroupOffset.x, group.mGroupOffset.y, src->GetSize().width, src->GetSize().height),
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -358,16 +358,34 @@ TextureClient::Lock(OpenMode aMode)
   if (mRemoveFromCompositableWaiter) {
     mRemoveFromCompositableWaiter->WaitComplete();
     mRemoveFromCompositableWaiter = nullptr;
   }
 
   mIsLocked = mData->Lock(aMode, mReleaseFenceHandle.IsValid() ? &mReleaseFenceHandle : nullptr);
   mOpenMode = aMode;
 
+  auto format = GetFormat();
+  if (mIsLocked && CanExposeDrawTarget() &&
+      aMode == OpenMode::OPEN_READ_WRITE &&
+      NS_IsMainThread() &&
+      // the formats that we apparently expect, in the cairo backend. Any other
+      // format will trigger an assertion in GfxFormatToCairoFormat.
+      (format == SurfaceFormat::A8R8G8B8_UINT32 ||
+      format == SurfaceFormat::X8R8G8B8_UINT32 ||
+      format == SurfaceFormat::A8 ||
+      format == SurfaceFormat::R5G6B5_UINT16)) {
+    if (!BorrowDrawTarget()) {
+      // Failed to get a DrawTarget, means we won't be able to write into the
+      // texture, might as well fail now.
+      Unlock();
+      return false;
+    }
+  }
+
   return mIsLocked;
 }
 
 void
 TextureClient::Unlock()
 {
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(mIsLocked);
@@ -422,16 +440,19 @@ TextureClient::~TextureClient()
 }
 
 void
 TextureClient::UpdateFromSurface(gfx::SourceSurface* aSurface)
 {
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(mIsLocked);
   MOZ_ASSERT(aSurface);
+  // If you run into this assertion, make sure the texture was locked write-only
+  // rather than read-write.
+  MOZ_ASSERT(!mBorrowedDrawTarget);
 
   // XXX - It would be better to first try the DrawTarget approach and fallback
   // to the backend-specific implementation because the latter will usually do
   // an expensive read-back + cpu-side copy if the texture is on the gpu.
   // There is a bug with the DrawTarget approach, though specific to reading back
   // from WebGL (where R and B channel end up inverted) to figure out first.
   if (mData->UpdateFromSurface(aSurface)) {
     return;
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -103,16 +103,19 @@ struct CompositableTransaction
 };
 
 struct AutoEndTransaction {
   explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
   ~AutoEndTransaction() { mTxn->End(); }
   CompositableTransaction* mTxn;
 };
 
+/* static */
+Atomic<bool> ImageBridgeChild::sIsShutDown(false);
+
 void
 ImageBridgeChild::UseTextures(CompositableClient* aCompositable,
                               const nsTArray<TimedTextureClient>& aTextures)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aCompositable->GetIPDLActor());
   MOZ_ASSERT(aCompositable->IsConnected());
 
@@ -366,17 +369,17 @@ ConnectImageBridgeInChildProcess(Transpo
 
 static void ReleaseImageClientNow(ImageClient* aClient,
                                   PImageContainerChild* aChild)
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   if (aClient) {
     aClient->Release();
   }
-  if (aChild && ImageBridgeChild::IsCreated()) {
+  if (aChild && ImageBridgeChild::IsCreated() && !ImageBridgeChild::IsShutDown()) {
     aChild->SendAsyncDelete();
   }
 }
 
 // static
 void ImageBridgeChild::DispatchReleaseImageClient(ImageClient* aClient,
                                                   PImageContainerChild* aChild)
 {
@@ -458,33 +461,33 @@ void ImageBridgeChild::DispatchReleaseTe
 
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
     NewRunnableFunction(&ReleaseTextureClientNow, aClient));
 }
 
 static void UpdateImageClientNow(ImageClient* aClient, RefPtr<ImageContainer>&& aContainer)
 {
-  if (!ImageBridgeChild::IsCreated()) {
+  if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) {
     NS_WARNING("Something is holding on to graphics resources after the shutdown"
                "of the graphics subsystem!");
     return;
   }
   MOZ_ASSERT(aClient);
   MOZ_ASSERT(aContainer);
   sImageBridgeChildSingleton->BeginTransaction();
   aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
   sImageBridgeChildSingleton->EndTransaction();
 }
 
 // static
 void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
                                                  ImageContainer* aContainer)
 {
-  if (!ImageBridgeChild::IsCreated()) {
+  if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) {
     NS_WARNING("Something is holding on to graphics resources after the shutdown"
                "of the graphics subsystem!");
     return;
   }
   if (!aClient || !aContainer || !IsCreated()) {
     return;
   }
 
@@ -540,17 +543,17 @@ void ImageBridgeChild::UpdateAsyncCanvas
   sImageBridgeChildSingleton->BeginTransaction();
   aWrapper->GetCanvasClient()->Updated();
   sImageBridgeChildSingleton->EndTransaction();
 }
 
 static void FlushAllImagesSync(ImageClient* aClient, ImageContainer* aContainer,
                                RefPtr<AsyncTransactionWaiter>&& aWaiter)
 {
-  if (!ImageBridgeChild::IsCreated()) {
+  if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) {
     // How sad. If we get into this branch it means that the ImageBridge
     // got destroyed between the time we ImageBridgeChild::FlushAllImage
     // was called on some thread, and the time this function was proxied
     // to the ImageBridge thread. ImageBridge gets destroyed way to late
     // in the shutdown of gecko for this to be happening for a good reason.
     NS_WARNING("Something is holding on to graphics resources after the shutdown"
                "of the graphics subsystem!");
     aWaiter->DecrementWaitCount();
@@ -569,17 +572,17 @@ static void FlushAllImagesSync(ImageClie
   // Otherwise, aWaiter will be ready to complete now.
   aWaiter->DecrementWaitCount();
 }
 
 // static
 void ImageBridgeChild::FlushAllImages(ImageClient* aClient,
                                       ImageContainer* aContainer)
 {
-  if (!IsCreated()) {
+  if (!IsCreated() || IsShutDown()) {
     return;
   }
   MOZ_ASSERT(aClient);
   MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
   MOZ_ASSERT(!InImageBridgeChildThread());
   if (InImageBridgeChildThread()) {
     NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
     return;
@@ -700,16 +703,19 @@ ImageBridgeChild::StartUpInChildProcess(
                         sImageBridgeChildSingleton.get()));
 
   return sImageBridgeChildSingleton;
 }
 
 void ImageBridgeChild::ShutDown()
 {
   MOZ_ASSERT(NS_IsMainThread());
+
+  sIsShutDown = true;
+
   if (ImageBridgeChild::IsCreated()) {
     MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
 
     {
       ReentrantMonitor barrier("ImageBridge ShutdownStep1 lock");
       ReentrantMonitorAutoEnter autoMon(barrier);
 
       bool done = false;
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_IMAGEBRIDGECHILD_H
 #define MOZILLA_GFX_IMAGEBRIDGECHILD_H
 
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include "mozilla/Attributes.h"         // for override
+#include "mozilla/Atomics.h"
 #include "mozilla/RefPtr.h"             // for already_AddRefed
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
 #include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTrackerHolder
 #include "mozilla/layers/CanvasClient.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/PImageBridgeChild.h"
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
@@ -135,16 +136,25 @@ public:
   static bool StartUpOnThread(base::Thread* aThread);
 
   /**
    * Returns true if the singleton has been created.
    *
    * Can be called from any thread.
    */
   static bool IsCreated();
+  /**
+   * Returns true if the singleton's ShutDown() was called.
+   *
+   * Can be called from any thread.
+   */
+  static bool IsShutDown()
+  {
+    return sIsShutDown;
+  }
 
   /**
    * returns the singleton instance.
    *
    * can be called from any thread.
    */
   static ImageBridgeChild* GetSingleton();
 
@@ -311,15 +321,16 @@ public:
 protected:
   ImageBridgeChild();
   bool DispatchAllocShmemInternal(size_t aSize,
                                   SharedMemory::SharedMemoryType aType,
                                   Shmem* aShmem,
                                   bool aUnsafe);
 
   CompositableTransaction* mTxn;
-  bool mShuttingDown;
+  Atomic<bool> mShuttingDown;
+  static Atomic<bool> sIsShutDown;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -97,16 +97,19 @@ elif CONFIG['CLANG_CL']:
     SOURCES['skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['-mssse3']
     SOURCES['skia/src/opts/SkBlitRow_opts_SSE4.cpp'].flags += ['-msse4.1']
     SOURCES['skia/src/opts/SkOpts_ssse3.cpp'].flags += ['-mssse3']
     SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1']
     SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx']
 
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 
+if not CONFIG['MOZ_ENABLE_SKIA_GPU']:
+    DEFINES['SK_SUPPORT_GPU'] = 0
+
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-deprecated-declarations',
         '-Wno-overloaded-virtual',
         '-Wno-sign-compare',
         '-Wno-unused-function',
     ]
     if CONFIG['CLANG_CXX']:
@@ -222,17 +225,18 @@ def generate_separated_sources(platform_
     },
     'arm': {
       'skia/src/core/SkUtilsArm.cpp',
     },
     'neon': {
       'skia/src/opts/SkOpts_neon.cpp',
       'skia/src/opts/SkBitmapProcState_arm_neon.cpp',
     },
-    'none': set()
+    'none': set(),
+    'gpu': set()
   })
 
   for plat in platform_sources.keys():
     for value in platform_sources[plat]:
       if isblacklisted(value):
         continue
 
       if value in separated['common']:
@@ -243,16 +247,18 @@ def generate_separated_sources(platform_
       if '_SSE' in value or '_SSSE' in value:
         key = 'intel'
       elif '_neon' in value:
         key = 'neon'
       elif '_arm' in value:
         key = 'arm'
       elif '_none' in value:
         key = 'none'
+      elif 'gpu' in value or 'Gpu' in value:
+        key = 'gpu'
       elif all(value in platform_sources.get(p, {})
                for p in platforms if p != plat):
         key = 'common'
 
       separated[key].add(value)
 
   return separated
 
@@ -353,16 +359,19 @@ def write_list(f, name, values, indent):
 def write_mozbuild(sources):
   filename = 'moz.build'
   f = open(filename, 'w')
 
   f.write(header)
 
   write_sources(f, sources['common'], 0)
 
+  f.write("if CONFIG['MOZ_ENABLE_SKIA_GPU']:\n")
+  write_sources(f, sources['gpu'], 4)
+
   f.write("if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk'):\n")
   write_sources(f, sources['android'], 4)
 
   f.write("if CONFIG['MOZ_WIDGET_TOOLKIT'] in {'cocoa', 'uikit'}:\n")
   write_sources(f, sources['mac'], 4)
 
   f.write("if CONFIG['MOZ_WIDGET_GTK']:\n")
   write_sources(f, sources['linux'], 4)
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -176,17 +176,16 @@ UNIFIED_SOURCES += [
     'skia/src/doc/SkDocument.cpp',
     'skia/src/effects/gradients/SkClampRange.cpp',
     'skia/src/effects/gradients/SkGradientBitmapCache.cpp',
     'skia/src/effects/gradients/SkGradientShader.cpp',
     'skia/src/effects/gradients/SkLinearGradient.cpp',
     'skia/src/effects/gradients/SkRadialGradient.cpp',
     'skia/src/effects/gradients/SkSweepGradient.cpp',
     'skia/src/effects/gradients/SkTwoPointConicalGradient.cpp',
-    'skia/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp',
     'skia/src/effects/GrCircleBlurFragmentProcessor.cpp',
     'skia/src/effects/Sk1DPathEffect.cpp',
     'skia/src/effects/Sk2DPathEffect.cpp',
     'skia/src/effects/SkAlphaThresholdFilter.cpp',
     'skia/src/effects/SkArcToPathEffect.cpp',
     'skia/src/effects/SkArithmeticMode.cpp',
     'skia/src/effects/SkBlurDrawLooper.cpp',
     'skia/src/effects/SkBlurImageFilter.cpp',
@@ -200,17 +199,16 @@ UNIFIED_SOURCES += [
     'skia/src/effects/SkComposeImageFilter.cpp',
     'skia/src/effects/SkCornerPathEffect.cpp',
     'skia/src/effects/SkDashPathEffect.cpp',
     'skia/src/effects/SkDiscretePathEffect.cpp',
     'skia/src/effects/SkDisplacementMapEffect.cpp',
     'skia/src/effects/SkDropShadowImageFilter.cpp',
     'skia/src/effects/SkEmbossMask.cpp',
     'skia/src/effects/SkEmbossMaskFilter.cpp',
-    'skia/src/effects/SkGpuBlurUtils.cpp',
     'skia/src/effects/SkImageSource.cpp',
     'skia/src/effects/SkLayerDrawLooper.cpp',
     'skia/src/effects/SkLayerRasterizer.cpp',
     'skia/src/effects/SkLerpXfermode.cpp',
     'skia/src/effects/SkLightingImageFilter.cpp',
     'skia/src/effects/SkLumaColorFilter.cpp',
     'skia/src/effects/SkMagnifierImageFilter.cpp',
     'skia/src/effects/SkMatrixConvolutionImageFilter.cpp',
@@ -227,143 +225,21 @@ UNIFIED_SOURCES += [
     'skia/src/effects/SkTestImageFilters.cpp',
     'skia/src/effects/SkTileImageFilter.cpp',
     'skia/src/effects/SkXfermodeImageFilter.cpp',
     'skia/src/fonts/SkFontMgr_indirect.cpp',
     'skia/src/fonts/SkGScalerContext.cpp',
     'skia/src/fonts/SkRandomScalerContext.cpp',
     'skia/src/fonts/SkRemotableFontMgr.cpp',
     'skia/src/fonts/SkTestScalerContext.cpp',
-    'skia/src/gpu/batches/GrAtlasTextBatch.cpp',
-    'skia/src/gpu/batches/GrBatch.cpp',
-    'skia/src/gpu/batches/GrCopySurfaceBatch.cpp',
-    'skia/src/gpu/batches/GrDashLinePathRenderer.cpp',
-    'skia/src/gpu/batches/GrDefaultPathRenderer.cpp',
-    'skia/src/gpu/batches/GrDrawAtlasBatch.cpp',
-    'skia/src/gpu/batches/GrDrawBatch.cpp',
-    'skia/src/gpu/batches/GrDrawPathBatch.cpp',
-    'skia/src/gpu/batches/GrDrawVerticesBatch.cpp',
-    'skia/src/gpu/batches/GrNinePatch.cpp',
-    'skia/src/gpu/batches/GrNonAAFillRectBatch.cpp',
-    'skia/src/gpu/batches/GrNonAAStrokeRectBatch.cpp',
-    'skia/src/gpu/batches/GrRectBatchFactory.cpp',
-    'skia/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp',
-    'skia/src/gpu/batches/GrTessellatingPathRenderer.cpp',
-    'skia/src/gpu/batches/GrVertexBatch.cpp',
-    'skia/src/gpu/effects/GrBezierEffect.cpp',
-    'skia/src/gpu/effects/GrBicubicEffect.cpp',
-    'skia/src/gpu/effects/GrBitmapTextGeoProc.cpp',
-    'skia/src/gpu/effects/GrConfigConversionEffect.cpp',
-    'skia/src/gpu/effects/GrConstColorProcessor.cpp',
-    'skia/src/gpu/effects/GrConvexPolyEffect.cpp',
-    'skia/src/gpu/effects/GrConvolutionEffect.cpp',
-    'skia/src/gpu/effects/GrCoverageSetOpXP.cpp',
-    'skia/src/gpu/effects/GrCustomXfermode.cpp',
-    'skia/src/gpu/effects/GrDashingEffect.cpp',
-    'skia/src/gpu/effects/GrDisableColorXP.cpp',
-    'skia/src/gpu/effects/GrDistanceFieldGeoProc.cpp',
-    'skia/src/gpu/effects/GrDitherEffect.cpp',
-    'skia/src/gpu/effects/GrMatrixConvolutionEffect.cpp',
-    'skia/src/gpu/effects/GrOvalEffect.cpp',
-    'skia/src/gpu/effects/GrPorterDuffXferProcessor.cpp',
-    'skia/src/gpu/effects/GrRRectEffect.cpp',
-    'skia/src/gpu/effects/GrSimpleTextureEffect.cpp',
-    'skia/src/gpu/effects/GrSingleTextureEffect.cpp',
-    'skia/src/gpu/effects/GrTextureDomain.cpp',
-    'skia/src/gpu/effects/GrTextureStripAtlas.cpp',
-    'skia/src/gpu/effects/GrXfermodeFragmentProcessor.cpp',
-    'skia/src/gpu/effects/GrYUVtoRGBEffect.cpp',
-    'skia/src/gpu/gl/debug/GrBufferObj.cpp',
-    'skia/src/gpu/gl/debug/GrDebugGL.cpp',
-    'skia/src/gpu/gl/debug/GrFrameBufferObj.cpp',
-    'skia/src/gpu/gl/debug/GrProgramObj.cpp',
-    'skia/src/gpu/gl/debug/GrShaderObj.cpp',
-    'skia/src/gpu/gl/debug/GrTextureObj.cpp',
-    'skia/src/gpu/gl/debug/GrTextureUnitObj.cpp',
-    'skia/src/gpu/gl/debug/SkDebugGLContext.cpp',
-    'skia/src/gpu/gl/SkGLContext.cpp',
-    'skia/src/gpu/gl/SkNullGLContext.cpp',
-    'skia/src/gpu/GrAtlasTextBlob.cpp',
-    'skia/src/gpu/GrAtlasTextContext.cpp',
-    'skia/src/gpu/GrBatchFlushState.cpp',
-    'skia/src/gpu/GrBatchFontCache.cpp',
-    'skia/src/gpu/GrBatchTest.cpp',
-    'skia/src/gpu/GrBlend.cpp',
-    'skia/src/gpu/GrBlurUtils.cpp',
-    'skia/src/gpu/GrBufferAllocPool.cpp',
-    'skia/src/gpu/GrCaps.cpp',
-    'skia/src/gpu/GrClip.cpp',
-    'skia/src/gpu/GrClipMaskManager.cpp',
-    'skia/src/gpu/GrContext.cpp',
-    'skia/src/gpu/GrCoordTransform.cpp',
-    'skia/src/gpu/GrDefaultGeoProcFactory.cpp',
-    'skia/src/gpu/GrDrawingManager.cpp',
-    'skia/src/gpu/GrDrawTarget.cpp',
-    'skia/src/gpu/GrFontScaler.cpp',
-    'skia/src/gpu/GrFragmentProcessor.cpp',
-    'skia/src/gpu/GrGpu.cpp',
-    'skia/src/gpu/GrGpuFactory.cpp',
-    'skia/src/gpu/GrGpuResource.cpp',
-    'skia/src/gpu/GrGpuResourceRef.cpp',
-    'skia/src/gpu/GrImageIDTextureAdjuster.cpp',
-    'skia/src/gpu/GrInvariantOutput.cpp',
-    'skia/src/gpu/GrLayerAtlas.cpp',
-    'skia/src/gpu/GrLayerCache.cpp',
-    'skia/src/gpu/GrLayerHoister.cpp',
-    'skia/src/gpu/GrMemoryPool.cpp',
-    'skia/src/gpu/GrOvalRenderer.cpp',
-    'skia/src/gpu/GrPaint.cpp',
-    'skia/src/gpu/GrPath.cpp',
-    'skia/src/gpu/GrPathProcessor.cpp',
-    'skia/src/gpu/GrPathRange.cpp',
-    'skia/src/gpu/GrPathRenderer.cpp',
-    'skia/src/gpu/GrPathRendererChain.cpp',
-    'skia/src/gpu/GrPathRendering.cpp',
-    'skia/src/gpu/GrPathUtils.cpp',
-    'skia/src/gpu/GrPipeline.cpp',
-    'skia/src/gpu/GrPipelineBuilder.cpp',
-    'skia/src/gpu/GrPrimitiveProcessor.cpp',
-    'skia/src/gpu/GrProcessor.cpp',
-    'skia/src/gpu/GrProcessorUnitTest.cpp',
-    'skia/src/gpu/GrProcOptInfo.cpp',
-    'skia/src/gpu/GrProgramElement.cpp',
-    'skia/src/gpu/GrRecordReplaceDraw.cpp',
-    'skia/src/gpu/GrRectanizer_pow2.cpp',
-    'skia/src/gpu/GrRectanizer_skyline.cpp',
-    'skia/src/gpu/GrReducedClip.cpp',
-    'skia/src/gpu/GrRenderTarget.cpp',
-    'skia/src/gpu/GrResourceProvider.cpp',
-    'skia/src/gpu/GrSoftwarePathRenderer.cpp',
-    'skia/src/gpu/GrStencil.cpp',
-    'skia/src/gpu/GrStencilAndCoverTextContext.cpp',
-    'skia/src/gpu/GrStencilAttachment.cpp',
-    'skia/src/gpu/GrStrokeInfo.cpp',
-    'skia/src/gpu/GrSurface.cpp',
-    'skia/src/gpu/GrSWMaskHelper.cpp',
-    'skia/src/gpu/GrTestUtils.cpp',
-    'skia/src/gpu/GrTextBlobCache.cpp',
-    'skia/src/gpu/GrTextContext.cpp',
-    'skia/src/gpu/GrTexture.cpp',
-    'skia/src/gpu/GrTextureAccess.cpp',
-    'skia/src/gpu/GrTextureParamsAdjuster.cpp',
-    'skia/src/gpu/GrTextureProvider.cpp',
-    'skia/src/gpu/GrTraceMarker.cpp',
-    'skia/src/gpu/GrXferProcessor.cpp',
-    'skia/src/gpu/GrYUVProvider.cpp',
-    'skia/src/gpu/SkGpuDevice.cpp',
-    'skia/src/gpu/SkGpuDevice_drawTexture.cpp',
-    'skia/src/gpu/SkGr.cpp',
-    'skia/src/gpu/SkGrPixelRef.cpp',
-    'skia/src/gpu/SkGrTexturePixelRef.cpp',
     'skia/src/image/SkImage.cpp',
     'skia/src/image/SkImage_Generator.cpp',
     'skia/src/image/SkImage_Raster.cpp',
     'skia/src/image/SkImageShader.cpp',
     'skia/src/image/SkSurface.cpp',
-    'skia/src/image/SkSurface_Gpu.cpp',
     'skia/src/image/SkSurface_Raster.cpp',
     'skia/src/images/bmpdecoderhelper.cpp',
     'skia/src/images/SkDecodingImageGenerator.cpp',
     'skia/src/images/SkPageFlipper.cpp',
     'skia/src/images/SkScaledBitmapSampler.cpp',
     'skia/src/lazy/SkDiscardableMemoryPool.cpp',
     'skia/src/lazy/SkDiscardablePixelRef.cpp',
     'skia/src/pathops/SkAddIntersections.cpp',
@@ -450,81 +326,210 @@ SOURCES += [
     'skia/src/core/SkBlitter_ARGB32.cpp',
     'skia/src/core/SkBlitter_RGB16.cpp',
     'skia/src/core/SkBlitter_Sprite.cpp',
     'skia/src/core/SkFontHost.cpp',
     'skia/src/core/SkOpts.cpp',
     'skia/src/core/SkPictureData.cpp',
     'skia/src/core/SkRecorder.cpp',
     'skia/src/core/SkScan_Antihair.cpp',
-    'skia/src/effects/SkArithmeticMode_gpu.cpp',
-    'skia/src/gpu/batches/GrAAConvexPathRenderer.cpp',
-    'skia/src/gpu/batches/GrAAConvexTessellator.cpp',
-    'skia/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp',
-    'skia/src/gpu/batches/GrAAFillRectBatch.cpp',
-    'skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp',
-    'skia/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp',
-    'skia/src/gpu/batches/GrAAStrokeRectBatch.cpp',
-    'skia/src/gpu/gl/builders/GrGLProgramBuilder.cpp',
-    'skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp',
-    'skia/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp',
-    'skia/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp',
-    'skia/src/gpu/gl/GrGLAssembleInterface.cpp',
-    'skia/src/gpu/gl/GrGLBufferImpl.cpp',
-    'skia/src/gpu/gl/GrGLCaps.cpp',
-    'skia/src/gpu/gl/GrGLContext.cpp',
     'skia/src/gpu/gl/GrGLCreateNativeInterface_none.cpp',
-    'skia/src/gpu/gl/GrGLCreateNullInterface.cpp',
-    'skia/src/gpu/gl/GrGLDefaultInterface_native.cpp',
-    'skia/src/gpu/gl/GrGLExtensions.cpp',
-    'skia/src/gpu/gl/GrGLGLSL.cpp',
-    'skia/src/gpu/gl/GrGLGpu.cpp',
-    'skia/src/gpu/gl/GrGLGpuProgramCache.cpp',
-    'skia/src/gpu/gl/GrGLIndexBuffer.cpp',
-    'skia/src/gpu/gl/GrGLInterface.cpp',
-    'skia/src/gpu/gl/GrGLNameAllocator.cpp',
-    'skia/src/gpu/gl/GrGLNoOpInterface.cpp',
-    'skia/src/gpu/gl/GrGLPath.cpp',
-    'skia/src/gpu/gl/GrGLPathRange.cpp',
-    'skia/src/gpu/gl/GrGLPathRendering.cpp',
-    'skia/src/gpu/gl/GrGLProgram.cpp',
-    'skia/src/gpu/gl/GrGLProgramDataManager.cpp',
-    'skia/src/gpu/gl/GrGLProgramDesc.cpp',
-    'skia/src/gpu/gl/GrGLRenderTarget.cpp',
-    'skia/src/gpu/gl/GrGLStencilAttachment.cpp',
-    'skia/src/gpu/gl/GrGLTexture.cpp',
-    'skia/src/gpu/gl/GrGLTextureRenderTarget.cpp',
-    'skia/src/gpu/gl/GrGLUtil.cpp',
-    'skia/src/gpu/gl/GrGLVaryingHandler.cpp',
-    'skia/src/gpu/gl/GrGLVertexArray.cpp',
-    'skia/src/gpu/gl/GrGLVertexBuffer.cpp',
-    'skia/src/gpu/glsl/GrGLSL.cpp',
-    'skia/src/gpu/glsl/GrGLSLBlend.cpp',
-    'skia/src/gpu/glsl/GrGLSLCaps.cpp',
-    'skia/src/gpu/glsl/GrGLSLFragmentProcessor.cpp',
-    'skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp',
-    'skia/src/gpu/glsl/GrGLSLGeometryProcessor.cpp',
-    'skia/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp',
-    'skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp',
-    'skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp',
-    'skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp',
-    'skia/src/gpu/glsl/GrGLSLUtil.cpp',
-    'skia/src/gpu/glsl/GrGLSLVarying.cpp',
-    'skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp',
-    'skia/src/gpu/glsl/GrGLSLXferProcessor.cpp',
-    'skia/src/gpu/GrBatchAtlas.cpp',
-    'skia/src/gpu/GrDrawContext.cpp',
-    'skia/src/gpu/GrResourceCache.cpp',
-    'skia/src/image/SkImage_Gpu.cpp',
     'skia/src/pathops/SkPathOpsDebug.cpp',
     'skia/src/utils/SkMD5.cpp',
     'skia/src/utils/SkParse.cpp',
     'skia/src/utils/SkParsePath.cpp',
     'skia/src/utils/SkSHA1.cpp',
 ]
+if CONFIG['MOZ_ENABLE_SKIA_GPU']:
+    UNIFIED_SOURCES += [
+        'skia/src/effects/gradients/SkTwoPointConicalGradient_gpu.cpp',
+        'skia/src/effects/SkGpuBlurUtils.cpp',
+        'skia/src/gpu/batches/GrAtlasTextBatch.cpp',
+        'skia/src/gpu/batches/GrBatch.cpp',
+        'skia/src/gpu/batches/GrCopySurfaceBatch.cpp',
+        'skia/src/gpu/batches/GrDashLinePathRenderer.cpp',
+        'skia/src/gpu/batches/GrDefaultPathRenderer.cpp',
+        'skia/src/gpu/batches/GrDrawAtlasBatch.cpp',
+        'skia/src/gpu/batches/GrDrawBatch.cpp',
+        'skia/src/gpu/batches/GrDrawPathBatch.cpp',
+        'skia/src/gpu/batches/GrDrawVerticesBatch.cpp',
+        'skia/src/gpu/batches/GrNinePatch.cpp',
+        'skia/src/gpu/batches/GrNonAAFillRectBatch.cpp',
+        'skia/src/gpu/batches/GrNonAAStrokeRectBatch.cpp',
+        'skia/src/gpu/batches/GrRectBatchFactory.cpp',
+        'skia/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp',
+        'skia/src/gpu/batches/GrTessellatingPathRenderer.cpp',
+        'skia/src/gpu/batches/GrVertexBatch.cpp',
+        'skia/src/gpu/effects/GrBezierEffect.cpp',
+        'skia/src/gpu/effects/GrBicubicEffect.cpp',
+        'skia/src/gpu/effects/GrBitmapTextGeoProc.cpp',
+        'skia/src/gpu/effects/GrConfigConversionEffect.cpp',
+        'skia/src/gpu/effects/GrConstColorProcessor.cpp',
+        'skia/src/gpu/effects/GrConvexPolyEffect.cpp',
+        'skia/src/gpu/effects/GrConvolutionEffect.cpp',
+        'skia/src/gpu/effects/GrCoverageSetOpXP.cpp',
+        'skia/src/gpu/effects/GrCustomXfermode.cpp',
+        'skia/src/gpu/effects/GrDashingEffect.cpp',
+        'skia/src/gpu/effects/GrDisableColorXP.cpp',
+        'skia/src/gpu/effects/GrDistanceFieldGeoProc.cpp',
+        'skia/src/gpu/effects/GrDitherEffect.cpp',
+        'skia/src/gpu/effects/GrMatrixConvolutionEffect.cpp',
+        'skia/src/gpu/effects/GrOvalEffect.cpp',
+        'skia/src/gpu/effects/GrPorterDuffXferProcessor.cpp',
+        'skia/src/gpu/effects/GrRRectEffect.cpp',
+        'skia/src/gpu/effects/GrSimpleTextureEffect.cpp',
+        'skia/src/gpu/effects/GrSingleTextureEffect.cpp',
+        'skia/src/gpu/effects/GrTextureDomain.cpp',
+        'skia/src/gpu/effects/GrTextureStripAtlas.cpp',
+        'skia/src/gpu/effects/GrXfermodeFragmentProcessor.cpp',
+        'skia/src/gpu/effects/GrYUVtoRGBEffect.cpp',
+        'skia/src/gpu/gl/debug/GrBufferObj.cpp',
+        'skia/src/gpu/gl/debug/GrDebugGL.cpp',
+        'skia/src/gpu/gl/debug/GrFrameBufferObj.cpp',
+        'skia/src/gpu/gl/debug/GrProgramObj.cpp',
+        'skia/src/gpu/gl/debug/GrShaderObj.cpp',
+        'skia/src/gpu/gl/debug/GrTextureObj.cpp',
+        'skia/src/gpu/gl/debug/GrTextureUnitObj.cpp',
+        'skia/src/gpu/gl/debug/SkDebugGLContext.cpp',
+        'skia/src/gpu/gl/SkGLContext.cpp',
+        'skia/src/gpu/gl/SkNullGLContext.cpp',
+        'skia/src/gpu/GrAtlasTextBlob.cpp',
+        'skia/src/gpu/GrAtlasTextContext.cpp',
+        'skia/src/gpu/GrBatchFlushState.cpp',
+        'skia/src/gpu/GrBatchFontCache.cpp',
+        'skia/src/gpu/GrBatchTest.cpp',
+        'skia/src/gpu/GrBlend.cpp',
+        'skia/src/gpu/GrBlurUtils.cpp',
+        'skia/src/gpu/GrBufferAllocPool.cpp',
+        'skia/src/gpu/GrCaps.cpp',
+        'skia/src/gpu/GrClip.cpp',
+        'skia/src/gpu/GrClipMaskManager.cpp',
+        'skia/src/gpu/GrContext.cpp',
+        'skia/src/gpu/GrCoordTransform.cpp',
+        'skia/src/gpu/GrDefaultGeoProcFactory.cpp',
+        'skia/src/gpu/GrDrawingManager.cpp',
+        'skia/src/gpu/GrDrawTarget.cpp',
+        'skia/src/gpu/GrFontScaler.cpp',
+        'skia/src/gpu/GrFragmentProcessor.cpp',
+        'skia/src/gpu/GrGpu.cpp',
+        'skia/src/gpu/GrGpuFactory.cpp',
+        'skia/src/gpu/GrGpuResource.cpp',
+        'skia/src/gpu/GrGpuResourceRef.cpp',
+        'skia/src/gpu/GrImageIDTextureAdjuster.cpp',
+        'skia/src/gpu/GrInvariantOutput.cpp',
+        'skia/src/gpu/GrLayerAtlas.cpp',
+        'skia/src/gpu/GrLayerCache.cpp',
+        'skia/src/gpu/GrLayerHoister.cpp',
+        'skia/src/gpu/GrMemoryPool.cpp',
+        'skia/src/gpu/GrOvalRenderer.cpp',
+        'skia/src/gpu/GrPaint.cpp',
+        'skia/src/gpu/GrPath.cpp',
+        'skia/src/gpu/GrPathProcessor.cpp',
+        'skia/src/gpu/GrPathRange.cpp',
+        'skia/src/gpu/GrPathRenderer.cpp',
+        'skia/src/gpu/GrPathRendererChain.cpp',
+        'skia/src/gpu/GrPathRendering.cpp',
+        'skia/src/gpu/GrPathUtils.cpp',
+        'skia/src/gpu/GrPipeline.cpp',
+        'skia/src/gpu/GrPipelineBuilder.cpp',
+        'skia/src/gpu/GrPrimitiveProcessor.cpp',
+        'skia/src/gpu/GrProcessor.cpp',
+        'skia/src/gpu/GrProcessorUnitTest.cpp',
+        'skia/src/gpu/GrProcOptInfo.cpp',
+        'skia/src/gpu/GrProgramElement.cpp',
+        'skia/src/gpu/GrRecordReplaceDraw.cpp',
+        'skia/src/gpu/GrRectanizer_pow2.cpp',
+        'skia/src/gpu/GrRectanizer_skyline.cpp',
+        'skia/src/gpu/GrReducedClip.cpp',
+        'skia/src/gpu/GrRenderTarget.cpp',
+        'skia/src/gpu/GrResourceProvider.cpp',
+        'skia/src/gpu/GrSoftwarePathRenderer.cpp',
+        'skia/src/gpu/GrStencil.cpp',
+        'skia/src/gpu/GrStencilAndCoverTextContext.cpp',
+        'skia/src/gpu/GrStencilAttachment.cpp',
+        'skia/src/gpu/GrStrokeInfo.cpp',
+        'skia/src/gpu/GrSurface.cpp',
+        'skia/src/gpu/GrSWMaskHelper.cpp',
+        'skia/src/gpu/GrTestUtils.cpp',
+        'skia/src/gpu/GrTextBlobCache.cpp',
+        'skia/src/gpu/GrTextContext.cpp',
+        'skia/src/gpu/GrTexture.cpp',
+        'skia/src/gpu/GrTextureAccess.cpp',
+        'skia/src/gpu/GrTextureParamsAdjuster.cpp',
+        'skia/src/gpu/GrTextureProvider.cpp',
+        'skia/src/gpu/GrTraceMarker.cpp',
+        'skia/src/gpu/GrXferProcessor.cpp',
+        'skia/src/gpu/GrYUVProvider.cpp',
+        'skia/src/gpu/SkGpuDevice.cpp',
+        'skia/src/gpu/SkGpuDevice_drawTexture.cpp',
+        'skia/src/gpu/SkGr.cpp',
+        'skia/src/gpu/SkGrPixelRef.cpp',
+        'skia/src/gpu/SkGrTexturePixelRef.cpp',
+        'skia/src/image/SkSurface_Gpu.cpp',
+    ]
+    SOURCES += [
+        'skia/src/effects/SkArithmeticMode_gpu.cpp',
+        'skia/src/gpu/batches/GrAAConvexPathRenderer.cpp',
+        'skia/src/gpu/batches/GrAAConvexTessellator.cpp',
+        'skia/src/gpu/batches/GrAADistanceFieldPathRenderer.cpp',
+        'skia/src/gpu/batches/GrAAFillRectBatch.cpp',
+        'skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp',
+        'skia/src/gpu/batches/GrAALinearizingConvexPathRenderer.cpp',
+        'skia/src/gpu/batches/GrAAStrokeRectBatch.cpp',
+        'skia/src/gpu/gl/builders/GrGLProgramBuilder.cpp',
+        'skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp',
+        'skia/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp',
+        'skia/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp',
+        'skia/src/gpu/gl/GrGLAssembleInterface.cpp',
+        'skia/src/gpu/gl/GrGLBufferImpl.cpp',
+        'skia/src/gpu/gl/GrGLCaps.cpp',
+        'skia/src/gpu/gl/GrGLContext.cpp',
+        'skia/src/gpu/gl/GrGLCreateNullInterface.cpp',
+        'skia/src/gpu/gl/GrGLDefaultInterface_native.cpp',
+        'skia/src/gpu/gl/GrGLExtensions.cpp',
+        'skia/src/gpu/gl/GrGLGLSL.cpp',
+        'skia/src/gpu/gl/GrGLGpu.cpp',
+        'skia/src/gpu/gl/GrGLGpuProgramCache.cpp',
+        'skia/src/gpu/gl/GrGLIndexBuffer.cpp',
+        'skia/src/gpu/gl/GrGLInterface.cpp',
+        'skia/src/gpu/gl/GrGLNameAllocator.cpp',
+        'skia/src/gpu/gl/GrGLNoOpInterface.cpp',
+        'skia/src/gpu/gl/GrGLPath.cpp',
+        'skia/src/gpu/gl/GrGLPathRange.cpp',
+        'skia/src/gpu/gl/GrGLPathRendering.cpp',
+        'skia/src/gpu/gl/GrGLProgram.cpp',
+        'skia/src/gpu/gl/GrGLProgramDataManager.cpp',
+        'skia/src/gpu/gl/GrGLProgramDesc.cpp',
+        'skia/src/gpu/gl/GrGLRenderTarget.cpp',
+        'skia/src/gpu/gl/GrGLStencilAttachment.cpp',
+        'skia/src/gpu/gl/GrGLTexture.cpp',
+        'skia/src/gpu/gl/GrGLTextureRenderTarget.cpp',
+        'skia/src/gpu/gl/GrGLUtil.cpp',
+        'skia/src/gpu/gl/GrGLVaryingHandler.cpp',
+        'skia/src/gpu/gl/GrGLVertexArray.cpp',
+        'skia/src/gpu/gl/GrGLVertexBuffer.cpp',
+        'skia/src/gpu/glsl/GrGLSL.cpp',
+        'skia/src/gpu/glsl/GrGLSLBlend.cpp',
+        'skia/src/gpu/glsl/GrGLSLCaps.cpp',
+        'skia/src/gpu/glsl/GrGLSLFragmentProcessor.cpp',
+        'skia/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp',
+        'skia/src/gpu/glsl/GrGLSLGeometryProcessor.cpp',
+        'skia/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp',
+        'skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp',
+        'skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp',
+        'skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp',
+        'skia/src/gpu/glsl/GrGLSLUtil.cpp',
+        'skia/src/gpu/glsl/GrGLSLVarying.cpp',
+        'skia/src/gpu/glsl/GrGLSLVertexShaderBuilder.cpp',
+        'skia/src/gpu/glsl/GrGLSLXferProcessor.cpp',
+        'skia/src/gpu/GrBatchAtlas.cpp',
+        'skia/src/gpu/GrDrawContext.cpp',
+        'skia/src/gpu/GrResourceCache.cpp',
+        'skia/src/image/SkImage_Gpu.cpp',
+    ]
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk'):
     UNIFIED_SOURCES += [
         'skia/src/ports/SkDebug_android.cpp',
         'skia/src/ports/SkOSFile_posix.cpp',
         'skia/src/ports/SkOSLibrary_posix.cpp',
         'skia/src/ports/SkTime_Unix.cpp',
         'skia/src/ports/SkTLS_pthread.cpp',
         'skia/src/utils/SkThreadUtils_pthread.cpp',
@@ -708,16 +713,19 @@ elif CONFIG['CLANG_CL']:
     SOURCES['skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['-mssse3']
     SOURCES['skia/src/opts/SkBlitRow_opts_SSE4.cpp'].flags += ['-msse4.1']
     SOURCES['skia/src/opts/SkOpts_ssse3.cpp'].flags += ['-mssse3']
     SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1']
     SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx']
 
 DEFINES['SKIA_IMPLEMENTATION'] = 1
 
+if not CONFIG['MOZ_ENABLE_SKIA_GPU']:
+    DEFINES['SK_SUPPORT_GPU'] = 0
+
 if CONFIG['GNU_CXX']:
     CXXFLAGS += [
         '-Wno-deprecated-declarations',
         '-Wno-overloaded-virtual',
         '-Wno-sign-compare',
         '-Wno-unused-function',
     ]
     if CONFIG['CLANG_CXX']:
--- a/gfx/thebes/gfxCoreTextShaper.cpp
+++ b/gfx/thebes/gfxCoreTextShaper.cpp
@@ -8,46 +8,105 @@
 #include "gfxMacFont.h"
 #include "gfxFontUtils.h"
 #include "gfxTextRun.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 #include <algorithm>
 
+#include <dlfcn.h>
+
 using namespace mozilla;
 
 // standard font descriptors that we construct the first time they're needed
 CTFontDescriptorRef gfxCoreTextShaper::sDefaultFeaturesDescriptor = nullptr;
 CTFontDescriptorRef gfxCoreTextShaper::sDisableLigaturesDescriptor = nullptr;
 CTFontDescriptorRef gfxCoreTextShaper::sIndicFeaturesDescriptor = nullptr;
 CTFontDescriptorRef gfxCoreTextShaper::sIndicDisableLigaturesDescriptor = nullptr;
 
+static CFStringRef sCTWritingDirectionAttributeName = nullptr;
+
+// See CTStringAttributes.h
+enum {
+    kMyCTWritingDirectionEmbedding = (0 << 1),
+    kMyCTWritingDirectionOverride = (1 << 1)
+};
+
+// Helper to create a CFDictionary with the right attributes for shaping our
+// text, including imposing the given directionality.
+// This will only be called if we're on 10.8 or later.
+CFDictionaryRef
+gfxCoreTextShaper::CreateAttrDict(bool aRightToLeft)
+{
+    // Because we always shape unidirectional runs, and may have applied
+    // directional overrides, we want to force a direction rather than
+    // allowing CoreText to do its own unicode-based bidi processing.
+    SInt16 dirOverride = kMyCTWritingDirectionOverride |
+                         (aRightToLeft ? kCTWritingDirectionRightToLeft
+                                       : kCTWritingDirectionLeftToRight);
+    CFNumberRef dirNumber =
+        ::CFNumberCreate(kCFAllocatorDefault,
+                         kCFNumberSInt16Type, &dirOverride);
+    CFArrayRef dirArray =
+        ::CFArrayCreate(kCFAllocatorDefault,
+                        (const void **) &dirNumber, 1,
+                        &kCFTypeArrayCallBacks);
+    ::CFRelease(dirNumber);
+    CFTypeRef attrs[] = { kCTFontAttributeName, sCTWritingDirectionAttributeName };
+    CFTypeRef values[] = { mCTFont, dirArray };
+    CFDictionaryRef attrDict =
+        ::CFDictionaryCreate(kCFAllocatorDefault,
+                             attrs, values, ArrayLength(attrs),
+                             &kCFTypeDictionaryKeyCallBacks,
+                             &kCFTypeDictionaryValueCallBacks);
+    ::CFRelease(dirArray);
+    return attrDict;
+}
+
+CFDictionaryRef
+gfxCoreTextShaper::CreateAttrDictWithoutDirection()
+{
+    CFTypeRef attrs[] = { kCTFontAttributeName };
+    CFTypeRef values[] = { mCTFont };
+    CFDictionaryRef attrDict =
+        ::CFDictionaryCreate(kCFAllocatorDefault,
+                             attrs, values, ArrayLength(attrs),
+                             &kCFTypeDictionaryKeyCallBacks,
+                             &kCFTypeDictionaryValueCallBacks);
+    return attrDict;
+}
+
 gfxCoreTextShaper::gfxCoreTextShaper(gfxMacFont *aFont)
     : gfxFontShaper(aFont)
+    , mAttributesDictLTR(nullptr)
+    , mAttributesDictRTL(nullptr)
 {
+    static bool sInitialized = false;
+    if (!sInitialized) {
+        CFStringRef* pstr = (CFStringRef*)
+            dlsym(RTLD_DEFAULT, "kCTWritingDirectionAttributeName");
+        if (pstr) {
+            sCTWritingDirectionAttributeName = *pstr;
+        }
+        sInitialized = true;
+    }
+
     // Create our CTFontRef
     mCTFont = CreateCTFontWithFeatures(aFont->GetAdjustedSize(),
                                        GetDefaultFeaturesDescriptor());
-
-    // Set up the default attribute dictionary that we will need each time we
-    // create a CFAttributedString (unless we need to use custom features,
-    // in which case a new dictionary will be created on the fly).
-    mAttributesDict = ::CFDictionaryCreate(kCFAllocatorDefault,
-                                           (const void**) &kCTFontAttributeName,
-                                           (const void**) &mCTFont,
-                                           1, // count of attributes
-                                           &kCFTypeDictionaryKeyCallBacks,
-                                           &kCFTypeDictionaryValueCallBacks);
 }
 
 gfxCoreTextShaper::~gfxCoreTextShaper()
 {
-    if (mAttributesDict) {
-        ::CFRelease(mAttributesDict);
+    if (mAttributesDictLTR) {
+        ::CFRelease(mAttributesDictLTR);
+    }
+    if (mAttributesDictRTL) {
+        ::CFRelease(mAttributesDictRTL);
     }
     if (mCTFont) {
         ::CFRelease(mCTFont);
     }
 }
 
 static bool
 IsBuggyIndicScript(int32_t aScript)
@@ -60,64 +119,91 @@ gfxCoreTextShaper::ShapeText(gfxContext 
                              const char16_t *aText,
                              uint32_t         aOffset,
                              uint32_t         aLength,
                              int32_t          aScript,
                              bool             aVertical,
                              gfxShapedText   *aShapedText)
 {
     // Create a CFAttributedString with text and style info, so we can use CoreText to lay it out.
-
     bool isRightToLeft = aShapedText->IsRightToLeft();
+    const UniChar* text = reinterpret_cast<const UniChar*>(aText);
     uint32_t length = aLength;
 
-    // we need to bidi-wrap the text if the run is RTL,
-    // or if it is an LTR run but may contain (overridden) RTL chars
-    bool bidiWrap = isRightToLeft;
-    if (!bidiWrap && !aShapedText->TextIs8Bit()) {
-        uint32_t i;
-        for (i = 0; i < length; ++i) {
-            if (gfxFontUtils::PotentialRTLChar(aText[i])) {
-                bidiWrap = true;
-                break;
-            }
-        }
-    }
-
-    // If there's a possibility of any bidi, we wrap the text with direction overrides
-    // to ensure neutrals or characters that were bidi-overridden in HTML behave properly.
-    const UniChar beginLTR[]    = { 0x202d, 0x20 };
-    const UniChar beginRTL[]    = { 0x202e, 0x20 };
-    const UniChar endBidiWrap[] = { 0x20, 0x2e, 0x202c };
-
     uint32_t startOffset;
     CFStringRef stringObj;
-    if (bidiWrap) {
-        startOffset = isRightToLeft ?
-            mozilla::ArrayLength(beginRTL) : mozilla::ArrayLength(beginLTR);
-        CFMutableStringRef mutableString =
-            ::CFStringCreateMutable(kCFAllocatorDefault,
-                                    length + startOffset + mozilla::ArrayLength(endBidiWrap));
-        ::CFStringAppendCharacters(mutableString,
-                                   isRightToLeft ? beginRTL : beginLTR,
-                                   startOffset);
-        ::CFStringAppendCharacters(mutableString, reinterpret_cast<const UniChar*>(aText), length);
-        ::CFStringAppendCharacters(mutableString,
-                                   endBidiWrap, mozilla::ArrayLength(endBidiWrap));
-        stringObj = mutableString;
-    } else {
+    CFDictionaryRef attrObj;
+
+    if (sCTWritingDirectionAttributeName) {
         startOffset = 0;
         stringObj = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
-                                                         reinterpret_cast<const UniChar*>(aText),
-                                                         length, kCFAllocatorNull);
+                                                         text, length,
+                                                         kCFAllocatorNull);
+
+        // Get an attributes dictionary suitable for shaping text in the
+        // current direction, creating it if necessary.
+        attrObj = isRightToLeft ? mAttributesDictRTL : mAttributesDictLTR;
+        if (!attrObj) {
+            attrObj = CreateAttrDict(isRightToLeft);
+            (isRightToLeft ? mAttributesDictRTL : mAttributesDictLTR) = attrObj;
+        }
+    } else {
+        // OS is too old to support kCTWritingDirectionAttributeName:
+        // we need to bidi-wrap the text if the run is RTL,
+        // or if it is an LTR run but may contain (overridden) RTL chars
+        bool bidiWrap = isRightToLeft;
+        if (!bidiWrap && !aShapedText->TextIs8Bit()) {
+            uint32_t i;
+            for (i = 0; i < length; ++i) {
+                if (gfxFontUtils::PotentialRTLChar(aText[i])) {
+                    bidiWrap = true;
+                    break;
+                }
+            }
+        }
+
+        // If there's a possibility of any bidi, we wrap the text with
+        // direction overrides to ensure neutrals or characters that were
+        // bidi-overridden in HTML behave properly.
+        static const UniChar beginLTR[]    = { 0x202d, 0x20 };
+        static const UniChar beginRTL[]    = { 0x202e, 0x20 };
+        static const UniChar endBidiWrap[] = { 0x20, 0x2e, 0x202c };
+
+        if (bidiWrap) {
+            startOffset = isRightToLeft ? ArrayLength(beginRTL)
+                                        : ArrayLength(beginLTR);
+            CFMutableStringRef mutableString =
+                ::CFStringCreateMutable(kCFAllocatorDefault,
+                                        length + startOffset +
+                                            ArrayLength(endBidiWrap));
+            ::CFStringAppendCharacters(mutableString,
+                                       isRightToLeft ? beginRTL : beginLTR,
+                                       startOffset);
+            ::CFStringAppendCharacters(mutableString, text, length);
+            ::CFStringAppendCharacters(mutableString, endBidiWrap,
+                                       ArrayLength(endBidiWrap));
+            stringObj = mutableString;
+        } else {
+            startOffset = 0;
+            stringObj =
+                ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
+                                                     text, length,
+                                                     kCFAllocatorNull);
+        }
+
+        // Get an attributes dictionary suitable for shaping text,
+        // creating it if necessary. (This dict is not LTR-specific,
+        // but we use that field to store it anyway.)
+        if (!mAttributesDictLTR) {
+            mAttributesDictLTR = CreateAttrDictWithoutDirection();
+        }
+        attrObj = mAttributesDictLTR;
     }
 
-    CFDictionaryRef attrObj;
     CTFontRef tempCTFont = nullptr;
-
     if (IsBuggyIndicScript(aScript)) {
         // To work around buggy Indic AAT fonts shipped with OS X,
         // we re-enable the Line Initial Smart Swashes feature that is needed
         // for "split vowels" to work in at least Bengali and Kannada fonts.
         // Affected fonts include Bangla MN, Bangla Sangam MN, Kannada MN,
         // Kannada Sangam MN. See bugs 686225, 728557, 953231, 1145515.
         tempCTFont =
             CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
@@ -127,31 +213,28 @@ gfxCoreTextShaper::ShapeText(gfxContext 
     } else if (aShapedText->DisableLigatures()) {
         // For letterspacing (or maybe other situations) we need to make
         // a copy of the CTFont with the ligature feature disabled.
         tempCTFont =
             CreateCTFontWithFeatures(::CTFontGetSize(mCTFont),
                                      GetDisableLigaturesDescriptor());
     }
 
+    // For the disabled-ligature or buggy-indic-font case, we need to replace
+    // the standard CTFont in the attribute dictionary with a tweaked version.
+    CFMutableDictionaryRef mutableAttr = nullptr;
     if (tempCTFont) {
-        attrObj =
-            ::CFDictionaryCreate(kCFAllocatorDefault,
-                                 (const void**) &kCTFontAttributeName,
-                                 (const void**) &tempCTFont,
-                                 1, // count of attributes
-                                 &kCFTypeDictionaryKeyCallBacks,
-                                 &kCFTypeDictionaryValueCallBacks);
+        mutableAttr = ::CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 2,
+                                                      attrObj);
+        ::CFDictionaryReplaceValue(mutableAttr,
+                                   kCTFontAttributeName, tempCTFont);
         // Having created the dict, we're finished with our temporary
         // Indic and/or ligature-disabled CTFontRef.
         ::CFRelease(tempCTFont);
-    } else {
-        // The default case is to use our preallocated attr dict
-        attrObj = mAttributesDict;
-        ::CFRetain(attrObj);
+        attrObj = mutableAttr;
     }
 
     // Now we can create an attributed string
     CFAttributedStringRef attrStringObj =
         ::CFAttributedStringCreate(kCFAllocatorDefault, stringObj, attrObj);
     ::CFRelease(stringObj);
 
     // Create the CoreText line from our string, then we're done with it
@@ -175,41 +258,46 @@ gfxCoreTextShaper::ShapeText(gfxContext 
             range.location - startOffset >= aLength) {
             continue;
         }
         CFDictionaryRef runAttr = ::CTRunGetAttributes(aCTRun);
         if (runAttr != attrObj) {
             // If Core Text manufactured a new dictionary, this may indicate
             // unexpected font substitution. In that case, we fail (and fall
             // back to harfbuzz shaping)...
-            const void* font1 = ::CFDictionaryGetValue(attrObj, kCTFontAttributeName);
-            const void* font2 = ::CFDictionaryGetValue(runAttr, kCTFontAttributeName);
+            const void* font1 =
+                ::CFDictionaryGetValue(attrObj, kCTFontAttributeName);
+            const void* font2 =
+                ::CFDictionaryGetValue(runAttr, kCTFontAttributeName);
             if (font1 != font2) {
                 // ...except that if the fallback was only for a variation
                 // selector or join control that is otherwise unsupported,
                 // we just ignore it.
                 if (range.length == 1) {
                     char16_t ch = aText[range.location - startOffset];
                     if (gfxFontUtils::IsJoinControl(ch) ||
                         gfxFontUtils::IsVarSelector(ch)) {
                         continue;
                     }
                 }
                 NS_WARNING("unexpected font fallback in Core Text");
                 success = false;
                 break;
             }
         }
-        if (SetGlyphsFromRun(aShapedText, aOffset, aLength, aCTRun, startOffset) != NS_OK) {
+        if (SetGlyphsFromRun(aShapedText, aOffset, aLength, aCTRun,
+                             startOffset) != NS_OK) {
             success = false;
             break;
         }
     }
 
-    ::CFRelease(attrObj);
+    if (mutableAttr) {
+        ::CFRelease(mutableAttr);
+    }
     ::CFRelease(line);
 
     return success;
 }
 
 #define SMALL_GLYPH_RUN 128 // preallocated size of our auto arrays for per-glyph data;
                             // some testing indicates that 90%+ of glyph runs will fit
                             // without requiring a separate allocation
--- a/gfx/thebes/gfxCoreTextShaper.h
+++ b/gfx/thebes/gfxCoreTextShaper.h
@@ -26,27 +26,33 @@ public:
                            bool             aVertical,
                            gfxShapedText   *aShapedText);
 
     // clean up static objects that may have been cached
     static void Shutdown();
 
 protected:
     CTFontRef mCTFont;
-    CFDictionaryRef mAttributesDict;
+
+    // attributes for shaping text with LTR or RTL directionality
+    CFDictionaryRef mAttributesDictLTR;
+    CFDictionaryRef mAttributesDictRTL;
 
     nsresult SetGlyphsFromRun(gfxShapedText *aShapedText,
                               uint32_t       aOffset,
                               uint32_t       aLength,
                               CTRunRef       aCTRun,
                               int32_t        aStringOffset);
 
     CTFontRef CreateCTFontWithFeatures(CGFloat aSize,
                                        CTFontDescriptorRef aDescriptor);
 
+    CFDictionaryRef CreateAttrDict(bool aRightToLeft);
+    CFDictionaryRef CreateAttrDictWithoutDirection();
+
     static CTFontDescriptorRef
     CreateFontFeaturesDescriptor(const std::pair<SInt16,SInt16> aFeatures[],
                                  size_t aCount);
 
     static CTFontDescriptorRef GetDefaultFeaturesDescriptor();
     static CTFontDescriptorRef GetDisableLigaturesDescriptor();
     static CTFontDescriptorRef GetIndicFeaturesDescriptor();
     static CTFontDescriptorRef GetIndicDisableLigaturesDescriptor();
--- a/image/Deinterlacer.cpp
+++ b/image/Deinterlacer.cpp
@@ -1,45 +1,55 @@
 /* -*- 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/. */
 
 
 #include "Downscaler.h"
+#include "mozilla/UniquePtrExtensions.h"
 
 namespace mozilla {
 namespace image {
 
 Deinterlacer::Deinterlacer(const nsIntSize& aImageSize)
   : mImageSize(aImageSize)
-  , mBuffer(MakeUnique<uint8_t[]>(mImageSize.width *
-                                  mImageSize.height *
-                                  sizeof(uint32_t)))
-{ }
+{
+  CheckedInt<size_t> bufferSize = mImageSize.width;
+  bufferSize *= mImageSize.height;
+  bufferSize *= sizeof(uint32_t);
+
+  if (!bufferSize.isValid()) {
+    return;
+  }
+
+  mBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize.value());
+}
 
 uint32_t
 Deinterlacer::RowSize() const
 {
   return mImageSize.width * sizeof(uint32_t);
 }
 
 uint8_t*
 Deinterlacer::RowBuffer(uint32_t aRow)
 {
   uint32_t offset = aRow * RowSize();
+  MOZ_ASSERT(IsValid(), "Deinterlacer in invalid state");
   MOZ_ASSERT(offset < mImageSize.width * mImageSize.height * sizeof(uint32_t),
              "Row is outside of image");
   return mBuffer.get() + offset;
 }
 
 void
 Deinterlacer::PropagatePassToDownscaler(Downscaler& aDownscaler)
 {
+  MOZ_ASSERT(IsValid(), "Deinterlacer in invalid state");
   for (int32_t row = 0 ; row < mImageSize.height ; ++row) {
     memcpy(aDownscaler.RowBuffer(), RowBuffer(row), RowSize());
     aDownscaler.CommitRow();
   }
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/Deinterlacer.h
+++ b/image/Deinterlacer.h
@@ -27,16 +27,17 @@
 
 namespace mozilla {
 namespace image {
 
 class Deinterlacer
 {
 public:
   explicit Deinterlacer(const nsIntSize& aImageSize);
+  bool IsValid() { return !!mBuffer; }
 
   uint8_t* RowBuffer(uint32_t aRow);
   void PropagatePassToDownscaler(Downscaler& aDownscaler);
 
 private:
   uint32_t RowSize() const;
 
   nsIntSize mImageSize;
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -1173,16 +1173,22 @@ nsGIFDecoder2::WriteInternal(const char*
         }
       }
 
       if (q[8] & 0x40) {
         mGIFStruct.interlaced = true;
         mGIFStruct.ipass = 1;
         if (mDownscaler) {
           mDeinterlacer.emplace(mDownscaler->FrameSize());
+
+          if (!mDeinterlacer->IsValid()) {
+            mDeinterlacer.reset();
+            mGIFStruct.state = gif_error;
+            break;
+          }
         }
       } else {
         mGIFStruct.interlaced = false;
         mGIFStruct.ipass = 0;
       }
 
       // Only apply the Haeberli display hack on the first frame
       mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0);
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e7c3ea0b876b0cbbd42b49c65b54878f32f1e7d8
GIT binary patch
literal 2360
zc${<hbhEHb{P+L&|K|-1{qq)_dtSBeJ=@l&M=vBDVOVIxKnf5bNi`0@M7AX5fQ5=?
zH4sw3NRn~5!j=gS56wRuV6p*>sOVEXaiV}<ClwvhKu7@xoTd1m+s`%Rva@4=tC5}o
zGbkDqf3h%$0cjnOJSZ<Xuy6;Q^jy8y;`Q0x|0}@qum3~k*%^NRZ}B~<eA_PSksM2x
zzFx+k`)BGvGT362m3M(wUrOe@j8*G$Uhm5Q8}$-q)a(De49$%Q#o(YswmJf={u4|+
zD=--}|2+HZaGDld#(@XB3%|rgt`O01$-t`ZCrsO`|H2H7tKP8x{CFbIsCI$L%iIHB
znCBht^X2%4W;aE^fq{W@O8?5!Z}}I`*#mX~KP*i7fPQG69eCtu@{x&n9Qx(I07FZI
zbun{icJ%}sL)YqEGZp4Ignbu6wFO5Q--CrQ*sKr#-~DgY7qvMm^Latj(kb;7`OBK;
zaW(Q0<rPM-JsbqpF)+b>yaw!*KQOPn`G5O=a|`R;iiqQeOJ{PgESG1rMA3;63e2Fu
zQUG~};rIW>>CZUwS{7}aRD^0Dt}KJ@RCIT-F+kn*>%S;Nqs$zm>i+HKUpOQ`7k>Qa
zvO37A;$ZVSeEQ$O{KX7pHZJ8UReW;YA$_~Ur$ZHArC&Xpr|gnVIIys|6AM7M`Uk{n
zMqp~<W@vFhD8wid5qa$$Jg<F&IsE1S&;Of_8n1fu_Y6;$^VvAd*B&C%Um096{3XcD
zwPM$ue+8$2@nHbg!~zQ~ZlHr3tERIqVRqTu1F?h=$&r8Ij{F6)MiiKu8@I%Ly1jS0
z<VVMVGRv3tix=+O`|RWM{q?e7tt@Z?r$>;23a$`2SfDlmW0Hh$gnR1u|1bX=f88;=
z0xX{!u<B$)7ss&t<9}v`mf7sxpMAKsZhhxNc!veO)JH1-nNZ5w-~U+|8jFOAB@Hg`
zyR}I~U<D`320UIu09@$^DO}P0{}CEROuP)hlEKtGVwU}cMwTT@pHyQS<ABX<WCN`M
DT7QSO
--- a/image/test/crashtests/crashtests.list
+++ b/image/test/crashtests/crashtests.list
@@ -9,16 +9,17 @@ load 570451.png
 skip-if(Android||B2G) load 694165-1.xhtml # Bug 876275
 load 681190.html
 load 732319-1.html
 load 844403-1.html
 load 856616.gif
 skip-if(B2G) load 944353.jpg
 load 1205923-1.html
 load 1212954-1.svg
+load 1235605.gif
 load colormap-range.gif
 HTTP load delayedframe.sjs # A 3-frame animated GIF with an inordinate delay between the second and third frame
 
 # Animated gifs with a very large canvas, but tiny actual content.
 skip-if(B2G) load delaytest.html?523528-1.gif
 skip-if(B2G) load delaytest.html?523528-2.gif
 
 # Bug 1160801 - Ensure that we handle invalid disposal types.
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -509,18 +509,19 @@ XPCShellEnvironment::Init()
     RefPtr<BackstagePass> backstagePass;
     rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
     if (NS_FAILED(rv)) {
         NS_ERROR("Failed to create backstage pass!");
         return false;
     }
 
     JS::CompartmentOptions options;
-    options.setZone(JS::SystemZone)
-           .setVersion(JSVERSION_LATEST);
+    options.creationOptions().setZone(JS::SystemZone);
+    options.behaviors().setVersion(JSVERSION_LATEST);
+
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     rv = xpc->InitClassesWithNewWrappedGlobal(cx,
                                               static_cast<nsIGlobalObject *>(backstagePass),
                                               principal, 0,
                                               options,
                                               getter_AddRefs(holder));
     if (NS_FAILED(rv)) {
         NS_ERROR("InitClassesWithNewWrappedGlobal failed!");
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -127,17 +127,17 @@ enum AsmJSSimdOperation
 // persistent state necessary to represent a compiled asm.js module.
 class js::AsmJSModule
 {
   public:
     class Global
     {
       public:
         enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction,
-                     AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation, ByteLength };
+                     AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation };
         enum VarInitKind { InitConstant, InitImport };
         enum ConstantKind { GlobalConstant, MathConstant };
 
       private:
         struct CacheablePod {
             Which which_;
             union {
                 struct {
@@ -296,31 +296,29 @@ class js::AsmJSModule
 
     typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
 
     class Export
     {
         PropertyName* name_;
         PropertyName* maybeFieldName_;
         struct CacheablePod {
-            uint32_t wasmIndex_;
             uint32_t startOffsetInModule_;  // Store module-start-relative offsets
             uint32_t endOffsetInModule_;    // so preserved by serialization.
         } pod;
 
       public:
         Export() {}
-        Export(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
+        Export(PropertyName* name, PropertyName* maybeFieldName,
                uint32_t startOffsetInModule, uint32_t endOffsetInModule)
           : name_(name),
             maybeFieldName_(maybeFieldName)
         {
             MOZ_ASSERT(name_->isTenured());
             MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured());
-            pod.wasmIndex_ = wasmIndex;
             pod.startOffsetInModule_ = startOffsetInModule;
             pod.endOffsetInModule_ = endOffsetInModule;
         }
 
         void trace(JSTracer* trc) {
             TraceManuallyBarrieredEdge(trc, &name_, "asm.js export name");
             if (maybeFieldName_)
                 TraceManuallyBarrieredEdge(trc, &maybeFieldName_, "asm.js export field");
@@ -333,46 +331,35 @@ class js::AsmJSModule
             return maybeFieldName_;
         }
         uint32_t startOffsetInModule() const {
             return pod.startOffsetInModule_;
         }
         uint32_t endOffsetInModule() const {
             return pod.endOffsetInModule_;
         }
-        static const uint32_t ChangeHeap = UINT32_MAX;
-        bool isChangeHeap() const {
-            return pod.wasmIndex_ == ChangeHeap;
-        }
-        uint32_t wasmIndex() const {
-            MOZ_ASSERT(!isChangeHeap());
-            return pod.wasmIndex_;
-        }
 
         WASM_DECLARE_SERIALIZABLE(Export)
     };
 
     typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
 
     typedef JS::UniquePtr<wasm::Module, JS::DeletePolicy<wasm::Module>> UniqueWasmModule;
 
   private:
     UniqueWasmModule            wasmModule_;
     wasm::UniqueStaticLinkData  linkData_;
     struct CacheablePod {
         uint32_t                minHeapLength_;
-        uint32_t                maxHeapLength_;
-        uint32_t                heapLengthMask_;
         uint32_t                numFFIs_;
         uint32_t                srcLength_;
         uint32_t                srcLengthWithRightBrace_;
         bool                    strict_;
         bool                    hasArrayView_;
         bool                    isSharedView_;
-        bool                    hasFixedMinHeapLength_;
     } pod;
     const ScriptSourceHolder    scriptSource_;
     const uint32_t              srcStart_;
     const uint32_t              srcBodyStart_;
     GlobalVector                globals_;
     ImportVector                imports_;
     ExportVector                exports_;
     PropertyName*               globalArgumentName_;
@@ -386,17 +373,16 @@ class js::AsmJSModule
         srcStart_(srcStart),
         srcBodyStart_(srcBodyStart),
         globalArgumentName_(nullptr),
         importArgumentName_(nullptr),
         bufferArgumentName_(nullptr)
     {
         mozilla::PodZero(&pod);
         pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
-        pod.maxHeapLength_ = 0x80000000;
         pod.strict_ = strict;
 
         MOZ_ASSERT(srcStart_ <= srcBodyStart_);
 
         // AsmJSCheckedImmediateRange should be defined to be at most the minimum
         // heap length so that offsets can be folded into bounds checks.
         MOZ_ASSERT(pod.minHeapLength_ - jit::AsmJSCheckedImmediateRange <= pod.minHeapLength_);
     }
@@ -441,23 +427,16 @@ class js::AsmJSModule
         return srcBodyStart_;
     }
 
     // While these functions may be accessed at any time, their values will
     // change as the module is compiled.
     uint32_t minHeapLength() const {
         return pod.minHeapLength_;
     }
-    uint32_t maxHeapLength() const {
-        return pod.maxHeapLength_;
-    }
-    uint32_t heapLengthMask() const {
-        MOZ_ASSERT(pod.hasFixedMinHeapLength_);
-        return pod.heapLengthMask_;
-    }
 
     void initGlobalArgumentName(PropertyName* n) {
         MOZ_ASSERT(!isFinished());
         MOZ_ASSERT_IF(n, n->isTenured());
         globalArgumentName_ = n;
     }
     void initImportArgumentName(PropertyName* n) {
         MOZ_ASSERT(!isFinished());
@@ -515,21 +494,16 @@ class js::AsmJSModule
     bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) {
         MOZ_ASSERT(!isFinished());
         MOZ_ASSERT(field);
         pod.isSharedView_ = false;
         Global g(Global::ArrayViewCtor, field);
         g.pod.u.viewType_ = vt;
         return globals_.append(g);
     }
-    bool addByteLength() {
-        MOZ_ASSERT(!isFinished());
-        Global g(Global::ByteLength, nullptr);
-        return globals_.append(g);
-    }
     bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) {
         MOZ_ASSERT(!isFinished());
         Global g(Global::MathBuiltinFunction, field);
         g.pod.u.mathBuiltinFunc_ = func;
         return globals_.append(g);
     }
     bool addMathBuiltinConstant(double value, PropertyName* field) {
         MOZ_ASSERT(!isFinished());
@@ -564,40 +538,26 @@ class js::AsmJSModule
         g.pod.u.constant.kind_ = Global::GlobalConstant;
         return globals_.append(g);
     }
     // See Import comment above for FFI vs. Import.
     bool addImport(uint32_t ffiIndex, uint32_t importIndex) {
         MOZ_ASSERT(imports_.length() == importIndex);
         return imports_.emplaceBack(ffiIndex);
     }
-    bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
-                   uint32_t funcSrcBegin, uint32_t funcSrcEnd)
-    {
-        // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource
-        // (the entire file) and ExportedFunctions store offsets relative to
-        // the beginning of the module (so that they are caching-invariant).
+    bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t begin, uint32_t end) {
+        // The begin/end offsets are given relative to the ScriptSource (the
+        // entire file) and ExportedFunctions store offsets relative to the
+        // beginning of the module (so that they are caching-invariant).
         MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT(srcStart_ < funcSrcBegin);
-        MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
-        return exports_.emplaceBack(name, maybeFieldName, wasmIndex,
-                                    funcSrcBegin - srcStart_, funcSrcEnd - srcStart_);
-    }
-    bool addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
-        MOZ_ASSERT(!isFinished());
-        MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
-        MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
-        MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
-        MOZ_ASSERT(max <= pod.maxHeapLength_);
-        MOZ_ASSERT(min <= max);
-        pod.heapLengthMask_ = mask;
-        pod.minHeapLength_ = min;
-        pod.maxHeapLength_ = max;
-        pod.hasFixedMinHeapLength_ = true;
-        return true;
+        MOZ_ASSERT(srcStart_ < begin);
+        MOZ_ASSERT(begin < end);
+        uint32_t startOffsetInModule = begin - srcStart_;
+        uint32_t endOffsetInModule = end - srcStart_;
+        return exports_.emplaceBack(name, maybeFieldName, startOffsetInModule, endOffsetInModule);
     }
 
     const GlobalVector& globals() const {
         return globals_;
     }
     const ImportVector& imports() const {
         return imports_;
     }
@@ -610,26 +570,21 @@ class js::AsmJSModule
             pod.isSharedView_ = true;
     }
     bool hasArrayView() const {
         return pod.hasArrayView_;
     }
     bool isSharedView() const {
         return pod.isSharedView_;
     }
-    bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
+    void requireHeapLengthToBeAtLeast(uint32_t len) {
         MOZ_ASSERT(!isFinished());
-        if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_)
-            return false;
-        if (len > pod.maxHeapLength_)
-            return false;
         len = RoundUpToNextValidAsmJSHeapLength(len);
         if (len > pod.minHeapLength_)
             pod.minHeapLength_ = len;
-        return true;
     }
 
     /*************************************************************************/
     // A module isFinished() when compilation completes. After being finished,
     // a module must be statically and dynamically linked before execution.
 
     bool isFinished() const {
         return !!wasmModule_;
@@ -1715,19 +1670,17 @@ class MOZ_STACK_CLASS ModuleValidator
             Function,
             FuncPtrTable,
             FFI,
             ArrayView,
             ArrayViewCtor,
             MathBuiltinFunction,
             AtomicsBuiltinFunction,
             SimdCtor,
-            SimdOperation,
-            ByteLength,
-            ChangeHeap
+            SimdOperation
         };
 
       private:
         Which which_;
         union {
             struct {
                 Type::Which type_;
                 uint32_t globalDataOffset_;
@@ -1741,20 +1694,16 @@ class MOZ_STACK_CLASS ModuleValidator
             } viewInfo;
             AsmJSMathBuiltinFunction mathBuiltinFunc_;
             AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
             AsmJSSimdType simdCtorType_;
             struct {
                 AsmJSSimdType type_;
                 AsmJSSimdOperation which_;
             } simdOp;
-            struct {
-                uint32_t srcBegin_;
-                uint32_t srcEnd_;
-            } changeHeap;
         } u;
 
         friend class ModuleValidator;
         friend class js::LifoAlloc;
 
         explicit Global(Which which) : which_(which) {}
 
       public:
@@ -1822,24 +1771,16 @@ class MOZ_STACK_CLASS ModuleValidator
         AsmJSSimdOperation simdOperation() const {
             MOZ_ASSERT(which_ == SimdOperation);
             return u.simdOp.which_;
         }
         AsmJSSimdType simdOperationType() const {
             MOZ_ASSERT(which_ == SimdOperation);
             return u.simdOp.type_;
         }
-        uint32_t changeHeapSrcBegin() const {
-            MOZ_ASSERT(which_ == ChangeHeap);
-            return u.changeHeap.srcBegin_;
-        }
-        uint32_t changeHeapSrcEnd() const {
-            MOZ_ASSERT(which_ == ChangeHeap);
-            return u.changeHeap.srcEnd_;
-        }
     };
 
     struct MathBuiltin
     {
         enum Kind { Function, Constant };
         Kind kind;
 
         union {
@@ -1926,18 +1867,16 @@ class MOZ_STACK_CLASS ModuleValidator
 
     ParseNode*           moduleFunctionNode_;
     PropertyName*        moduleFunctionName_;
 
     UniqueChars          errorString_;
     uint32_t             errorOffset_;
     bool                 errorOverRecursed_;
 
-    bool                 canValidateChangeHeap_;
-    bool                 hasChangeHeap_;
     bool                 supportsSimd_;
     bool                 atomicsPresent_;
 
   public:
     ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser)
       : cx_(cx),
         parser_(parser),
         mg_(cx),
@@ -1950,18 +1889,16 @@ class MOZ_STACK_CLASS ModuleValidator
         standardLibraryMathNames_(cx),
         standardLibraryAtomicsNames_(cx),
         standardLibrarySimdOpNames_(cx),
         moduleFunctionNode_(parser.pc->maybeFunction),
         moduleFunctionName_(nullptr),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false),
-        canValidateChangeHeap_(false),
-        hasChangeHeap_(false),
         supportsSimd_(cx->jitSupportsSimd()),
         atomicsPresent_(false)
     {
         MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
     }
 
     ~ModuleValidator() {
         if (errorString_) {
@@ -2084,36 +2021,39 @@ class MOZ_STACK_CLASS ModuleValidator
     }
 
     bool finish(SlowFunctionVector* slowFuncs) {
         uint32_t endBeforeCurly = tokenStream().currentToken().pos.end;
         TokenPos pos;
         JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand));
         uint32_t endAfterCurly = pos.end;
 
-        auto usesHeap = Module::HeapBool(module_->hasArrayView());
-        auto sharedHeap = Module::SharedBool(module_->isSharedView());
+        HeapUsage heapUsage = module_->hasArrayView()
+                              ? module_->isSharedView()
+                                ? HeapUsage::Shared
+                                : HeapUsage::Unshared
+                              : HeapUsage::None;
+
         auto mutedErrors = Module::MutedBool(parser_.ss->mutedErrors());
 
         CacheableChars filename = make_string_copy(parser_.ss->filename());
         if (!filename)
             return false;
 
         CacheableTwoByteChars displayURL;
         if (parser_.ss->hasDisplayURL()) {
             uint32_t length = js_strlen(parser_.ss->displayURL());
             displayURL.reset(js_pod_calloc<char16_t>(length + 1));
             if (!displayURL)
                 return false;
             PodCopy(displayURL.get(), parser_.ss->displayURL(), length);
         }
 
         UniqueStaticLinkData linkData;
-        Module* wasm = mg_.finish(usesHeap, sharedHeap, mutedErrors,
-                                  Move(filename), Move(displayURL),
+        Module* wasm = mg_.finish(heapUsage, mutedErrors, Move(filename), Move(displayURL),
                                   &linkData, slowFuncs);
         if (!wasm)
             return false;
 
         module_->finish(wasm, Move(linkData), endBeforeCurly, endAfterCurly);
         return true;
     }
 
@@ -2214,33 +2154,16 @@ class MOZ_STACK_CLASS ModuleValidator
         Global* global = validationLifo_.new_<Global>(Global::SimdOperation);
         if (!global)
             return false;
         global->u.simdOp.type_ = type;
         global->u.simdOp.which_ = op;
         return globals_.putNew(var, global) &&
                module().addSimdOperation(type, op, opName);
     }
-    bool addByteLength(PropertyName* name) {
-        canValidateChangeHeap_ = true;
-        Global* global = validationLifo_.new_<Global>(Global::ByteLength);
-        return global &&
-               globals_.putNew(name, global) &&
-               module().addByteLength();
-    }
-    bool addChangeHeap(PropertyName* name, ParseNode* fn, uint32_t mask, uint32_t min, uint32_t max) {
-        hasChangeHeap_ = true;
-        Global* global = validationLifo_.new_<Global>(Global::ChangeHeap);
-        if (!global)
-            return false;
-        global->u.changeHeap.srcBegin_ = fn->pn_pos.begin;
-        global->u.changeHeap.srcEnd_ = fn->pn_pos.end;
-        return globals_.putNew(name, global) &&
-               module().addChangeHeap(mask, min, max);
-    }
     bool addArrayViewCtor(PropertyName* var, Scalar::Type vt, PropertyName* field) {
         Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor);
         if (!global)
             return false;
         global->u.viewInfo.viewType_ = vt;
         return globals_.putNew(var, global) &&
                module().addArrayViewCtor(vt, field);
     }
@@ -2254,27 +2177,18 @@ class MOZ_STACK_CLASS ModuleValidator
         global->u.ffiIndex_ = index;
         return globals_.putNew(var, global);
     }
     bool addExport(ParseNode* pn, const Func& func, PropertyName* maybeFieldName) {
         MallocSig::ArgVector args;
         if (!args.appendAll(func.sig().args()))
             return false;
         MallocSig sig(Move(args), func.sig().ret());
-        uint32_t wasmIndex;
-        if (!mg_.declareExport(Move(sig), func.index(), &wasmIndex))
-            return false;
-        if (wasmIndex == AsmJSModule::Export::ChangeHeap)
-            return fail(pn, "too many exports");
-        return module().addExport(func.name(), maybeFieldName, wasmIndex,
-                                  func.srcBegin(), func.srcEnd());
-    }
-    bool addChangeHeapExport(PropertyName* name, const Global& g, PropertyName* maybeFieldName) {
-        return module().addExport(name, maybeFieldName, AsmJSModule::Export::ChangeHeap,
-                                  g.changeHeapSrcBegin(), g.changeHeapSrcEnd());
+        return mg_.declareExport(Move(sig), func.index()) &&
+               module().addExport(func.name(), maybeFieldName, func.srcBegin(), func.srcEnd());
     }
   private:
     const LifoSig* getLifoSig(const LifoSig& sig) {
         return &sig;
     }
     const LifoSig* getLifoSig(const MallocSig& sig) {
         return mg_.newLifoSig(sig);
     }
@@ -2333,29 +2247,23 @@ class MOZ_STACK_CLASS ModuleValidator
         *lifoSig = getLifoSig(sig);
         if (!*lifoSig)
             return false;
         return mg_.declareImport(Move(sig), importIndex) &&
                imports_.add(p, ImportDescriptor(name, **lifoSig), *importIndex) &&
                module().addImport(ffiIndex, *importIndex);
     }
 
-    bool tryOnceToValidateChangeHeap() {
-        bool ret = canValidateChangeHeap_;
-        canValidateChangeHeap_ = false;
-        return ret;
-    }
-    bool hasChangeHeap() const {
-        return hasChangeHeap_;
-    }
-    bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
-        return module().tryRequireHeapLengthToBeAtLeast(len);
-    }
-    uint32_t minHeapLength() const {
-        return module().minHeapLength();
+    bool tryConstantAccess(uint64_t start, uint64_t width) {
+        MOZ_ASSERT(UINT64_MAX - start > width);
+        uint64_t end = start + width;
+        if (end > uint64_t(INT32_MAX) + 1)
+            return false;
+        module().requireHeapLengthToBeAtLeast(end);
+        return true;
     }
 
     bool usesSharedMemory() const {
         return atomicsPresent_;
     }
 
     // Error handling.
     bool hasAlreadyFailed() const {
@@ -2817,28 +2725,25 @@ class MOZ_STACK_CLASS FunctionValidator
     ModuleValidator&  m_;
     ParseNode*        fn_;
 
     FunctionGenerator fg_;
 
     LocalMap          locals_;
     LabelMap          labels_;
 
-    unsigned          heapExpressionDepth_;
-
     bool              hasAlreadyReturned_;
     ExprType          ret_;
 
   public:
     FunctionValidator(ModuleValidator& m, ParseNode* fn)
       : m_(m),
         fn_(fn),
         locals_(m.cx()),
         labels_(m.cx()),
-        heapExpressionDepth_(0),
         hasAlreadyReturned_(false)
     {}
 
     ModuleValidator& m() const        { return m_; }
     const AsmJSModule& module() const { return m_.module(); }
     FuncIR& funcIR() const            { return fg_.func(); }
     ExclusiveContext* cx() const      { return m_.cx(); }
     ParseNode* fn() const             { return fn_; }
@@ -2882,29 +2787,16 @@ class MOZ_STACK_CLASS FunctionValidator
         LocalMap::AddPtr p = locals_.lookupForAdd(name);
         if (p)
             return failName(pn, "duplicate local name '%s' not allowed", name);
         if (!locals_.add(p, name, Local(init.type(), locals_.count())))
             return false;
         return funcIR().addVariable(init.value());
     }
 
-    /*************************************************************************/
-
-    void enterHeapExpression() {
-        heapExpressionDepth_++;
-    }
-    void leaveHeapExpression() {
-        MOZ_ASSERT(heapExpressionDepth_ > 0);
-        heapExpressionDepth_--;
-    }
-    bool canCall() const {
-        return heapExpressionDepth_ == 0 || !m_.hasChangeHeap();
-    }
-
     /****************************** For consistency of returns in a function */
 
     bool hasAlreadyReturned() const {
         return hasAlreadyReturned_;
     }
 
     ExprType returnedType() const {
         return ret_;
@@ -3466,18 +3358,16 @@ CheckGlobalDotImport(ModuleValidator& m,
     if (!base->isKind(PNK_NAME))
         return m.fail(base, "expected name of variable or parameter");
 
     if (base->name() == m.module().globalArgumentName()) {
         if (field == m.cx()->names().NaN)
             return m.addGlobalConstant(varName, GenericNaN(), field);
         if (field == m.cx()->names().Infinity)
             return m.addGlobalConstant(varName, PositiveInfinity<double>(), field);
-        if (field == m.cx()->names().byteLength)
-            return m.addByteLength(varName);
 
         Scalar::Type type;
         if (IsArrayViewCtorName(m, field, &type))
             return m.addArrayViewCtor(varName, type, field);
 
         return m.failName(initNode, "'%s' is not a standard constant or typed array name", field);
     }
 
@@ -3778,18 +3668,16 @@ CheckVarRef(FunctionValidator& f, ParseN
           case ModuleValidator::Global::FFI:
           case ModuleValidator::Global::MathBuiltinFunction:
           case ModuleValidator::Global::AtomicsBuiltinFunction:
           case ModuleValidator::Global::FuncPtrTable:
           case ModuleValidator::Global::ArrayView:
           case ModuleValidator::Global::ArrayViewCtor:
           case ModuleValidator::Global::SimdCtor:
           case ModuleValidator::Global::SimdOperation:
-          case ModuleValidator::Global::ByteLength:
-          case ModuleValidator::Global::ChangeHeap:
             return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
         }
         return true;
     }
 
     return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
 }
 
@@ -3814,17 +3702,17 @@ FoldMaskedArrayIndex(FunctionValidator& 
 
     uint32_t mask2;
     if (IsLiteralOrConstInt(f, maskNode, &mask2)) {
         // Flag the access to skip the bounds check if the mask ensures that an
         // 'out of bounds' access can not occur based on the current heap length
         // constraint. The unsigned maximum of a masked index is the mask
         // itself, so check that the mask is not negative and compare the mask
         // to the known minimum heap length.
-        if (int32_t(mask2) >= 0 && mask2 < f.m().minHeapLength())
+        if (int32_t(mask2) >= 0 && mask2 < f.m().module().minHeapLength())
             *needsBoundsCheck = NO_BOUNDS_CHECK;
         *mask &= mask2;
         *indexExpr = indexNode;
         return true;
     }
 
     return false;
 }
@@ -3844,26 +3732,19 @@ CheckArrayAccess(FunctionValidator& f, P
     if (!global || !global->isAnyArrayView())
         return f.fail(viewName, "base of array access must be a typed array view name");
 
     *viewType = global->viewType();
 
     uint32_t index;
     if (IsLiteralOrConstInt(f, indexExpr, &index)) {
         uint64_t byteOffset = uint64_t(index) << TypedArrayShift(*viewType);
-        if (byteOffset > INT32_MAX)
+        if (!f.m().tryConstantAccess(byteOffset, TypedArrayElemSize(*viewType)))
             return f.fail(indexExpr, "constant index out of range");
 
-        unsigned elementSize = TypedArrayElemSize(*viewType);
-        if (!f.m().tryRequireHeapLengthToBeAtLeast(byteOffset + elementSize)) {
-            return f.failf(indexExpr, "constant index outside heap size range declared by the "
-                                      "change-heap function (0x%x - 0x%x)",
-                                      f.m().minHeapLength(), f.m().module().maxHeapLength());
-        }
-
         *mask = NoMask;
         *needsBoundsCheck = NO_BOUNDS_CHECK;
         f.writeInt32Lit(byteOffset);
         return true;
     }
 
     // Mask off the low bits to account for the clearing effect of a right shift
     // followed by the left shift implicit in the array access. E.g., H32[i>>2]
@@ -3881,47 +3762,39 @@ CheckArrayAccess(FunctionValidator& f, P
         if (shift != requiredShift)
             return f.failf(shiftAmountNode, "shift amount must be %u", requiredShift);
 
         ParseNode* pointerNode = BitwiseLeft(indexExpr);
 
         if (pointerNode->isKind(PNK_BITAND))
             FoldMaskedArrayIndex(f, &pointerNode, mask, needsBoundsCheck);
 
-        f.enterHeapExpression();
-
         Type pointerType;
         if (!CheckExpr(f, pointerNode, &pointerType))
             return false;
 
-        f.leaveHeapExpression();
-
         if (!pointerType.isIntish())
             return f.failf(pointerNode, "%s is not a subtype of int", pointerType.toChars());
     } else {
         // For legacy compatibility, accept Int8/Uint8 accesses with no shift.
         if (TypedArrayShift(*viewType) != 0)
             return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access");
 
         MOZ_ASSERT(*mask == NoMask);
         bool folded = false;
 
         ParseNode* pointerNode = indexExpr;
 
         if (pointerNode->isKind(PNK_BITAND))
             folded = FoldMaskedArrayIndex(f, &pointerNode, mask, needsBoundsCheck);
 
-        f.enterHeapExpression();
-
         Type pointerType;
         if (!CheckExpr(f, pointerNode, &pointerType))
             return false;
 
-        f.leaveHeapExpression();
-
         if (folded) {
             if (!pointerType.isIntish())
                 return f.failf(pointerNode, "%s is not a subtype of intish", pointerType.toChars());
         } else {
             if (!pointerType.isInt())
                 return f.failf(pointerNode, "%s is not a subtype of int", pointerType.toChars());
         }
     }
@@ -4005,24 +3878,20 @@ CheckStoreArray(FunctionValidator& f, Pa
     size_t needsBoundsCheckAt = f.tempU8();
 
     Scalar::Type viewType;
     NeedsBoundsCheck needsBoundsCheck;
     int32_t mask;
     if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), &viewType, &needsBoundsCheck, &mask))
         return false;
 
-    f.enterHeapExpression();
-
     Type rhsType;
     if (!CheckExpr(f, rhs, &rhsType))
         return false;
 
-    f.leaveHeapExpression();
-
     switch (viewType) {
       case Scalar::Int8:
       case Scalar::Int16:
       case Scalar::Int32:
       case Scalar::Uint8:
       case Scalar::Uint16:
       case Scalar::Uint32:
         if (!rhsType.isIntish())
@@ -4652,21 +4521,16 @@ WriteCallLineCol(FunctionValidator& f, P
     f.writeU32(line);
     f.writeU32(column);
 }
 
 static bool
 CheckInternalCall(FunctionValidator& f, ParseNode* callNode, PropertyName* calleeName,
                   ExprType ret, Type* type)
 {
-    if (!f.canCall()) {
-        return f.fail(callNode, "call expressions may not be nested inside heap expressions "
-                                "when the module contains a change-heap function");
-    }
-
     switch (ret) {
       case ExprType::Void:   f.writeOp(Stmt::CallInternal);  break;
       case ExprType::I32:    f.writeOp(I32::CallInternal);   break;
       case ExprType::I64:    MOZ_CRASH("no int64 in asm.js");
       case ExprType::F32:    f.writeOp(F32::CallInternal);   break;
       case ExprType::F64:    f.writeOp(F64::CallInternal);   break;
       case ExprType::I32x4:  f.writeOp(I32X4::CallInternal); break;
       case ExprType::F32x4:  f.writeOp(F32X4::CallInternal); break;
@@ -4723,21 +4587,16 @@ CheckFuncPtrTableAgainstExisting(ModuleV
         return m.fail(usepn, "table too big");
 
     return true;
 }
 
 static bool
 CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, ExprType ret, Type* type)
 {
-    if (!f.canCall()) {
-        return f.fail(callNode, "function-pointer call expressions may not be nested inside heap "
-                                "expressions when the module contains a change-heap function");
-    }
-
     ParseNode* callee = CallCallee(callNode);
     ParseNode* tableNode = ElemBase(callee);
     ParseNode* indexExpr = ElemIndex(callee);
 
     if (!tableNode->isKind(PNK_NAME))
         return f.fail(tableNode, "expecting name of function-pointer array");
 
     PropertyName* name = tableNode->name();
@@ -4808,21 +4667,16 @@ CheckIsExternType(FunctionValidator& f, 
         return f.failf(argNode, "%s is not a subtype of extern", type.toChars());
     return true;
 }
 
 static bool
 CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, ExprType ret,
              Type* type)
 {
-    if (!f.canCall()) {
-        return f.fail(callNode, "FFI call expressions may not be nested inside heap "
-                                "expressions when the module contains a change-heap function");
-    }
-
     PropertyName* calleeName = CallCallee(callNode)->name();
 
     if (ret == ExprType::F32)
         return f.fail(callNode, "FFI calls can't return float");
     if (IsSimdType(ret))
         return f.fail(callNode, "FFI calls can't return SIMD values");
 
     switch (ret) {
@@ -5499,40 +5353,31 @@ CheckSimdLoadStoreArgs(FunctionValidator
       case AsmJSSimdType_int32x4:   *viewType = Scalar::Int32x4;   break;
       case AsmJSSimdType_float32x4: *viewType = Scalar::Float32x4; break;
       case AsmJSSimdType_bool32x4:  MOZ_CRASH("Cannot load/store boolean SIMD type");
     }
 
     ParseNode* indexExpr = NextNode(view);
     uint32_t indexLit;
     if (IsLiteralOrConstInt(f, indexExpr, &indexLit)) {
-        if (indexLit > INT32_MAX)
+        if (!f.m().tryConstantAccess(indexLit, Simd128DataSize))
             return f.fail(indexExpr, "constant index out of range");
 
-        if (!f.m().tryRequireHeapLengthToBeAtLeast(indexLit + Simd128DataSize)) {
-            return f.failf(indexExpr, "constant index outside heap size range declared by the "
-                                      "change-heap function (0x%x - 0x%x)",
-                                      f.m().minHeapLength(), f.m().module().maxHeapLength());
-        }
-
         *needsBoundsCheck = NO_BOUNDS_CHECK;
         f.writeInt32Lit(indexLit);
         return true;
     }
 
-    f.enterHeapExpression();
-
     Type indexType;
     if (!CheckExpr(f, indexExpr, &indexType))
         return false;
+
     if (!indexType.isIntish())
         return f.failf(indexExpr, "%s is not a subtype of intish", indexType.toChars());
 
-    f.leaveHeapExpression();
-
     return true;
 }
 
 static bool
 CheckSimdLoad(FunctionValidator& f, ParseNode* call, AsmJSSimdType opType,
               unsigned numElems, Type* type)
 {
     unsigned numArgs = CallArgListLength(call);
@@ -5948,18 +5793,16 @@ CheckCoercedCall(FunctionValidator& f, P
           case ModuleValidator::Global::AtomicsBuiltinFunction:
             return CheckCoercedAtomicsBuiltinCall(f, call, global->atomicsBuiltinFunction(), ret, type);
           case ModuleValidator::Global::ConstantLiteral:
           case ModuleValidator::Global::ConstantImport:
           case ModuleValidator::Global::Variable:
           case ModuleValidator::Global::FuncPtrTable:
           case ModuleValidator::Global::ArrayView:
           case ModuleValidator::Global::ArrayViewCtor:
-          case ModuleValidator::Global::ByteLength:
-          case ModuleValidator::Global::ChangeHeap:
             return f.failName(callee, "'%s' is not callable function", callee->name());
           case ModuleValidator::Global::SimdCtor:
           case ModuleValidator::Global::SimdOperation:
             return CheckCoercedSimdCall(f, call, global, ret, type);
           case ModuleValidator::Global::Function:
             break;
         }
     }
@@ -7016,235 +6859,16 @@ CheckStatement(FunctionValidator& f, Par
                                                           Stmt::Continue, Stmt::ContinueLabel);
       default:;
     }
 
     return f.fail(stmt, "unexpected statement kind");
 }
 
 static bool
-CheckByteLengthCall(ModuleValidator& m, ParseNode* pn, PropertyName* newBufferName)
-{
-    if (!pn->isKind(PNK_CALL) || !CallCallee(pn)->isKind(PNK_NAME))
-        return m.fail(pn, "expecting call to imported byteLength");
-
-    const ModuleValidator::Global* global = m.lookupGlobal(CallCallee(pn)->name());
-    if (!global || global->which() != ModuleValidator::Global::ByteLength)
-        return m.fail(pn, "expecting call to imported byteLength");
-
-    if (CallArgListLength(pn) != 1 || !IsUseOfName(CallArgList(pn), newBufferName))
-        return m.failName(pn, "expecting %s as argument to byteLength call", newBufferName);
-
-    return true;
-}
-
-static bool
-CheckHeapLengthCondition(ModuleValidator& m, ParseNode* cond, PropertyName* newBufferName,
-                         uint32_t* mask, uint32_t* minLength, uint32_t* maxLength)
-{
-    if (!cond->isKind(PNK_OR) || !AndOrLeft(cond)->isKind(PNK_OR))
-        return m.fail(cond, "expecting byteLength & K || byteLength <= L || byteLength > M");
-
-    ParseNode* cond1 = AndOrLeft(AndOrLeft(cond));
-    ParseNode* cond2 = AndOrRight(AndOrLeft(cond));
-    ParseNode* cond3 = AndOrRight(cond);
-
-    if (!cond1->isKind(PNK_BITAND))
-        return m.fail(cond1, "expecting byteLength & K");
-
-    if (!CheckByteLengthCall(m, BitwiseLeft(cond1), newBufferName))
-        return false;
-
-    ParseNode* maskNode = BitwiseRight(cond1);
-    if (!IsLiteralInt(m, maskNode, mask))
-        return m.fail(maskNode, "expecting integer literal mask");
-    if (*mask == UINT32_MAX)
-        return m.fail(maskNode, "invalid mask value");
-    if ((*mask & 0xffffff) != 0xffffff)
-        return m.fail(maskNode, "mask value must have the bits 0xffffff set");
-
-    if (!cond2->isKind(PNK_LE))
-        return m.fail(cond2, "expecting byteLength <= L");
-
-    if (!CheckByteLengthCall(m, RelationalLeft(cond2), newBufferName))
-        return false;
-
-    ParseNode* minLengthNode = RelationalRight(cond2);
-    uint32_t minLengthExclusive;
-    if (!IsLiteralInt(m, minLengthNode, &minLengthExclusive))
-        return m.fail(minLengthNode, "expecting integer literal");
-    if (minLengthExclusive < 0xffffff || minLengthExclusive == UINT32_MAX)
-        return m.fail(minLengthNode, "literal must be >= 0xffffff and < 0xffffffff");
-
-    // Add one to convert from exclusive (the branch rejects if ==) to inclusive.
-    *minLength = minLengthExclusive + 1;
-
-    if (!cond3->isKind(PNK_GT))
-        return m.fail(cond3, "expecting byteLength > M");
-
-    if (!CheckByteLengthCall(m, RelationalLeft(cond3), newBufferName))
-        return false;
-
-    ParseNode* maxLengthNode = RelationalRight(cond3);
-    if (!IsLiteralInt(m, maxLengthNode, maxLength))
-        return m.fail(maxLengthNode, "expecting integer literal");
-    if (*maxLength > 0x80000000)
-        return m.fail(maxLengthNode, "literal must be <= 0x80000000");
-
-    if (*maxLength < *minLength)
-        return m.fail(maxLengthNode, "maximum length must be greater or equal to minimum length");
-
-    return true;
-}
-
-static bool
-CheckReturnBoolLiteral(ModuleValidator& m, ParseNode* stmt, bool retval)
-{
-    if (stmt->isKind(PNK_STATEMENTLIST)) {
-        ParseNode* next = SkipEmptyStatements(ListHead(stmt));
-        if (!next)
-            return m.fail(stmt, "expected return statement");
-        stmt = next;
-        if (NextNonEmptyStatement(stmt))
-            return m.fail(stmt, "expected single return statement");
-    }
-
-    if (!stmt->isKind(PNK_RETURN))
-        return m.fail(stmt, "expected return statement");
-
-    ParseNode* returnExpr = ReturnExpr(stmt);
-    if (!returnExpr || !returnExpr->isKind(retval ? PNK_TRUE : PNK_FALSE))
-        return m.failf(stmt, "expected 'return %s;'", retval ? "true" : "false");
-
-    return true;
-}
-
-static bool
-CheckReassignmentTo(ModuleValidator& m, ParseNode* stmt, PropertyName* lhsName, ParseNode** rhs)
-{
-    if (!stmt->isKind(PNK_SEMI))
-        return m.fail(stmt, "missing reassignment");
-
-    ParseNode* assign = UnaryKid(stmt);
-    if (!assign || !assign->isKind(PNK_ASSIGN))
-        return m.fail(stmt, "missing reassignment");
-
-    ParseNode* lhs = BinaryLeft(assign);
-    if (!IsUseOfName(lhs, lhsName))
-        return m.failName(lhs, "expecting reassignment of %s", lhsName);
-
-    *rhs = BinaryRight(assign);
-    return true;
-}
-
-static bool
-CheckChangeHeap(ModuleValidator& m, ParseNode* fn, bool* validated)
-{
-    MOZ_ASSERT(fn->isKind(PNK_FUNCTION));
-
-    // We don't yet know whether this is a change-heap function.
-    // The point at which we know we have a change-heap function is once we see
-    // whether the argument is coerced according to the normal asm.js rules. If
-    // it is coerced, it's not change-heap and must validate according to normal
-    // rules; otherwise it must validate as a change-heap function.
-    *validated = false;
-
-    PropertyName* changeHeapName = FunctionName(fn);
-    if (!CheckModuleLevelName(m, fn, changeHeapName))
-        return false;
-
-    unsigned numFormals;
-    ParseNode* arg = FunctionArgsList(fn, &numFormals);
-    if (numFormals != 1)
-        return true;
-
-    PropertyName* newBufferName;
-    if (!CheckArgument(m, arg, &newBufferName))
-        return false;
-
-    ParseNode* stmtIter = SkipEmptyStatements(ListHead(FunctionStatementList(fn)));
-    if (!stmtIter || !stmtIter->isKind(PNK_IF))
-        return true;
-
-    // We can now issue validation failures if we see something that isn't a
-    // valid change-heap function.
-    *validated = true;
-
-    PropertyName* bufferName = m.module().bufferArgumentName();
-    if (!bufferName)
-        return m.fail(fn, "to change heaps, the module must have a buffer argument");
-
-    ParseNode* cond = TernaryKid1(stmtIter);
-    ParseNode* thenStmt = TernaryKid2(stmtIter);
-    if (ParseNode* elseStmt = TernaryKid3(stmtIter))
-        return m.fail(elseStmt, "unexpected else statement");
-
-    uint32_t mask, min = 0, max;  // initialize min to silence GCC warning
-    if (!CheckHeapLengthCondition(m, cond, newBufferName, &mask, &min, &max))
-        return false;
-
-    if (!CheckReturnBoolLiteral(m, thenStmt, false))
-        return false;
-
-    ParseNode* next = NextNonEmptyStatement(stmtIter);
-
-    for (unsigned i = 0; i < m.numArrayViews(); i++, next = NextNonEmptyStatement(stmtIter)) {
-        if (!next)
-            return m.failOffset(stmtIter->pn_pos.end, "missing reassignment");
-        stmtIter = next;
-
-        const ModuleValidator::ArrayView& view = m.arrayView(i);
-
-        ParseNode* rhs;
-        if (!CheckReassignmentTo(m, stmtIter, view.name, &rhs))
-            return false;
-
-        if (!rhs->isKind(PNK_NEW))
-            return m.failName(rhs, "expecting assignment of new array view to %s", view.name);
-
-        ParseNode* ctorExpr = ListHead(rhs);
-        if (!ctorExpr->isKind(PNK_NAME))
-            return m.fail(rhs, "expecting name of imported typed array constructor");
-
-        const ModuleValidator::Global* global = m.lookupGlobal(ctorExpr->name());
-        if (!global || global->which() != ModuleValidator::Global::ArrayViewCtor)
-            return m.fail(rhs, "expecting name of imported typed array constructor");
-        if (global->viewType() != view.type)
-            return m.fail(rhs, "can't change the type of a global view variable");
-
-        if (!CheckNewArrayViewArgs(m, ctorExpr, newBufferName))
-            return false;
-    }
-
-    if (!next)
-        return m.failOffset(stmtIter->pn_pos.end, "missing reassignment");
-    stmtIter = next;
-
-    ParseNode* rhs;
-    if (!CheckReassignmentTo(m, stmtIter, bufferName, &rhs))
-        return false;
-    if (!IsUseOfName(rhs, newBufferName))
-        return m.failName(stmtIter, "expecting assignment of new buffer to %s", bufferName);
-
-    next = NextNonEmptyStatement(stmtIter);
-    if (!next)
-        return m.failOffset(stmtIter->pn_pos.end, "expected return statement");
-    stmtIter = next;
-
-    if (!CheckReturnBoolLiteral(m, stmtIter, true))
-        return false;
-
-    stmtIter = NextNonEmptyStatement(stmtIter);
-    if (stmtIter)
-        return m.fail(stmtIter, "expecting end of function");
-
-    return m.addChangeHeap(changeHeapName, fn, mask, min, max);
-}
-
-static bool
 ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line, unsigned* column)
 {
     TokenStream& tokenStream = m.tokenStream();
 
     tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
     tokenStream.srcCoords.lineNumAndColumnIndex(tokenStream.currentToken().pos.end, line, column);
 
     RootedPropertyName name(m.cx());
@@ -7314,24 +6938,16 @@ CheckFunction(ModuleValidator& m)
     ParseNode* fn = nullptr;
     unsigned line = 0, column = 0;
     if (!ParseFunction(m, &fn, &line, &column))
         return false;
 
     if (!CheckFunctionHead(m, fn))
         return false;
 
-    if (m.tryOnceToValidateChangeHeap()) {
-        bool validated;
-        if (!CheckChangeHeap(m, fn, &validated))
-            return false;
-        if (validated)
-            return true;
-    }
-
     FunctionValidator f(m, fn);
     if (!f.init(FunctionName(fn), line, column))
         return m.fail(fn, "internal compiler failure (probably out of memory)");
 
     ParseNode* stmtIter = ListHead(FunctionStatementList(fn));
 
     if (!CheckProcessingDirectives(m, &stmtIter))
         return false;
@@ -7488,23 +7104,20 @@ CheckModuleExportFunction(ModuleValidato
     if (!pn->isKind(PNK_NAME))
         return m.fail(pn, "expected name of exported function");
 
     PropertyName* funcName = pn->name();
     const ModuleValidator::Global* global = m.lookupGlobal(funcName);
     if (!global)
         return m.failName(pn, "exported function name '%s' not found", funcName);
 
-    if (global->which() == ModuleValidator::Global::Function)
-        return m.addExport(pn, m.function(global->funcIndex()), maybeFieldName);
-
-    if (global->which() == ModuleValidator::Global::ChangeHeap)
-        return m.addChangeHeapExport(funcName, *global, maybeFieldName);
-
-    return m.failName(pn, "'%s' is not a function", funcName);
+    if (global->which() != ModuleValidator::Global::Function)
+        return m.failName(pn, "'%s' is not a function", funcName);
+
+    return m.addExport(pn, m.function(global->funcIndex()), maybeFieldName);
 }
 
 static bool
 CheckModuleExportObject(ModuleValidator& m, ParseNode* object)
 {
     MOZ_ASSERT(object->isKind(PNK_OBJECT));
 
     for (ParseNode* pn = ListHead(object); pn; pn = NextNode(pn)) {
@@ -7631,86 +7244,45 @@ CheckModule(ExclusiveContext* cx, AsmJSP
     if (!m.finish(slowFuncs))
         return false;
 
     *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC;
     return true;
 }
 
 /*****************************************************************************/
-// Runtime calls to asm.js module exports
+// Link-time validation
 
 static AsmJSModuleObject&
 FunctionToModuleObject(JSFunction* fun)
 {
     MOZ_ASSERT(IsAsmJSFunction(fun) || IsAsmJSModule(fun));
-    const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_MODULE_SLOT);
+    const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_MODULE_SLOT);
     return v.toObject().as<AsmJSModuleObject>();
 }
 
 static unsigned
 FunctionToExportIndex(JSFunction* fun)
 {
     MOZ_ASSERT(IsAsmJSFunction(fun));
-    const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT);
+    const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT);
     return v.toInt32();
 }
 
 static bool
-ChangeHeap(JSContext* cx, AsmJSModule& module, const CallArgs& args)
-{
-    HandleValue bufferArg = args.get(0);
-    if (!IsArrayBuffer(bufferArg)) {
-        ReportIncompatible(cx, args);
-        return false;
-    }
-
-    Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
-    uint32_t heapLength = newBuffer->byteLength();
-    if (heapLength & module.heapLengthMask() ||
-        heapLength < module.minHeapLength() ||
-        heapLength > module.maxHeapLength())
-    {
-        args.rval().set(BooleanValue(false));
-        return true;
-    }
-
-    if (!module.hasArrayView()) {
-        args.rval().set(BooleanValue(true));
-        return true;
-    }
-
-    MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
-
-    bool useSignalHandlers = module.wasmModule().compileArgs().useSignalHandlersForOOB;
-    if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, useSignalHandlers))
-        return false;
-
-    args.rval().set(BooleanValue(module.wasmModule().changeHeap(newBuffer, cx)));
-    return true;
-}
-
-static bool
 CallAsmJS(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedFunction callee(cx, &args.callee().as<JSFunction>());
 
     AsmJSModule& module = FunctionToModuleObject(callee).module();
-    const AsmJSModule::Export& exp = module.exports()[FunctionToExportIndex(callee)];
-
-    // The heap-changing function is a special-case and is implemented by C++.
-    if (exp.isChangeHeap())
-        return ChangeHeap(cx, module, args);
-
-    return module.wasmModule().callExport(cx, exp.wasmIndex(), args);
-}
-
-/*****************************************************************************/
-// Link-time validation
+    uint32_t exportIndex = FunctionToExportIndex(callee);
+
+    return module.wasmModule().callExport(cx, exportIndex, args);
+}
 
 static bool
 LinkFail(JSContext* cx, const char* str)
 {
     JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage,
                                  nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
     return false;
 }
@@ -7881,40 +7453,16 @@ ValidateArrayView(JSContext* cx, const A
     bool tac = IsTypedArrayConstructor(v, global.viewType());
     if (!tac)
         return LinkFail(cx, "bad typed array constructor");
 
     return true;
 }
 
 static bool
-ValidateByteLength(JSContext* cx, HandleValue globalVal)
-{
-    RootedPropertyName field(cx, cx->names().byteLength);
-    RootedValue v(cx);
-    if (!GetDataProperty(cx, globalVal, field, &v))
-        return false;
-
-    if (!v.isObject() || !v.toObject().isBoundFunction())
-        return LinkFail(cx, "byteLength must be a bound function object");
-
-    RootedFunction fun(cx, &v.toObject().as<JSFunction>());
-
-    RootedValue boundTarget(cx, ObjectValue(*fun->getBoundFunctionTarget()));
-    if (!IsNativeFunction(boundTarget, fun_call))
-        return LinkFail(cx, "bound target of byteLength must be Function.prototype.call");
-
-    RootedValue boundThis(cx, fun->getBoundFunctionThis());
-    if (!IsNativeFunction(boundThis, ArrayBufferObject::byteLengthGetter))
-        return LinkFail(cx, "bound this value must be ArrayBuffer.protototype.byteLength accessor");
-
-    return true;
-}
-
-static bool
 ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
 {
     RootedValue v(cx);
     if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
         return false;
 
     RootedPropertyName field(cx, global.mathName());
     if (!GetDataProperty(cx, v, field, &v))
@@ -8146,30 +7694,22 @@ CheckBuffer(JSContext* cx, AsmJSModule& 
     }
 
     // This check is sufficient without considering the size of the loaded datum because heap
     // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
     MOZ_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
     if (heapLength < module.minHeapLength()) {
         UniqueChars msg(
             JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
-                        "by const heap accesses and/or change-heap minimum-length requirements).",
+                        "by const heap accesses).",
                         heapLength,
                         module.minHeapLength()));
         return LinkFail(cx, msg.get());
     }
 
-    if (heapLength > module.maxHeapLength()) {
-        UniqueChars msg(
-            JS_smprintf("ArrayBuffer byteLength 0x%x is greater than maximum length of 0x%x",
-                        heapLength,
-                        module.maxHeapLength()));
-        return LinkFail(cx, msg.get());
-    }
-
     // Shell builtins may have disabled signal handlers since the module we're
     // cloning was compiled. LookupAsmJSModuleInCache checks for signal handlers
     // as well for the caching case.
     if (module.wasmModule().compileArgs() != CompileArgs(cx))
         return LinkFail(cx, "Signals have been toggled since compilation");
 
     if (buffer->is<ArrayBufferObject>()) {
         Rooted<ArrayBufferObject*> abheap(cx, &buffer->as<ArrayBufferObject>());
@@ -8206,20 +7746,16 @@ DynamicallyLinkModule(JSContext* cx, con
             if (!ValidateFFI(cx, global, importVal, &ffis))
                 return false;
             break;
           case AsmJSModule::Global::ArrayView:
           case AsmJSModule::Global::ArrayViewCtor:
             if (!ValidateArrayView(cx, global, globalVal))
                 return false;
             break;
-          case AsmJSModule::Global::ByteLength:
-            if (!ValidateByteLength(cx, globalVal))
-                return false;
-            break;
           case AsmJSModule::Global::MathBuiltinFunction:
             if (!ValidateMathBuiltinFunction(cx, global, globalVal))
                 return false;
             break;
           case AsmJSModule::Global::AtomicsBuiltinFunction:
             if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal))
                 return false;
             break;
@@ -8246,30 +7782,28 @@ DynamicallyLinkModule(JSContext* cx, con
 
     return module.wasmModule().dynamicallyLink(cx, buffer, imports);
 }
 
 static JSFunction*
 NewExportedFunction(JSContext* cx, const AsmJSModule& module, const AsmJSModule::Export& func,
                     HandleObject moduleObj, unsigned exportIndex)
 {
-    unsigned numArgs = func.isChangeHeap()
-                       ? 1
-                       : module.wasmModule().exports()[func.wasmIndex()].sig().args().length();
+    unsigned numArgs = module.wasmModule().exports()[exportIndex].sig().args().length();
 
     RootedPropertyName name(cx, func.name());
     JSFunction* fun =
         NewNativeConstructor(cx, CallAsmJS, numArgs, name,
                              gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
                              JSFunction::ASMJS_CTOR);
     if (!fun)
         return nullptr;
 
-    fun->setExtendedSlot(FunctionExtended::ASM_MODULE_SLOT, ObjectValue(*moduleObj));
-    fun->setExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
+    fun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj));
+    fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
     return fun;
 }
 
 static bool
 HandleDynamicLinkFailure(JSContext* cx, const CallArgs& args, AsmJSModule& module,
                          HandlePropertyName name)
 {
     if (cx->isExceptionPending())
@@ -8429,17 +7963,17 @@ NewModuleFunction(ExclusiveContext* cx, 
                                                   : JSFunction::ASMJS_CTOR;
     JSFunction* moduleFun =
         NewNativeConstructor(cx, LinkAsmJS, origFun->nargs(), name,
                              gc::AllocKind::FUNCTION_EXTENDED, TenuredObject,
                              flags);
     if (!moduleFun)
         return nullptr;
 
-    moduleFun->setExtendedSlot(FunctionExtended::ASM_MODULE_SLOT, ObjectValue(*moduleObj));
+    moduleFun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj));
     return moduleFun;
 }
 
 /*****************************************************************************/
 // Caching and cloning
 
 uint8_t*
 AsmJSModule::Global::serialize(uint8_t* cursor) const
@@ -9385,19 +8919,8 @@ js::RoundUpToNextValidAsmJSHeapLength(ui
         return MinHeapLength;
 
     if (length <= 16 * 1024 * 1024)
         return mozilla::RoundUpPow2(length);
 
     MOZ_ASSERT(length <= 0xff000000);
     return (length + 0x00ffffff) & ~0x00ffffff;
 }
-
-bool
-js::OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer)
-{
-    for (Module* m = cx->runtime()->linkedWasmModules; m; m = m->nextLinked()) {
-        if (buffer == m->maybeBuffer() && !m->detachHeap(cx))
-            return false;
-    }
-    return true;
-}
-
--- a/js/src/asmjs/AsmJS.h
+++ b/js/src/asmjs/AsmJS.h
@@ -104,19 +104,16 @@ AsmJSModuleToString(JSContext* cx, Handl
 // asm.js heap:
 
 extern bool
 IsValidAsmJSHeapLength(uint32_t length);
 
 extern uint32_t
 RoundUpToNextValidAsmJSHeapLength(uint32_t length);
 
-extern bool
-OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer);
-
 // The assumed page size; dynamically checked in CompileAsmJS.
 #ifdef _MIPS_ARCH_LOONGSON3A
 static const size_t AsmJSPageSize = 16384;
 #else
 static const size_t AsmJSPageSize = 4096;
 #endif
 
 #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -307,19 +307,18 @@ ModuleGenerator::defineImport(uint32_t i
     Import& import = imports_[index];
     import.initInterpExitOffset(interpExit.begin);
     import.initJitExitOffset(jitExit.begin);
     return codeRanges_.emplaceBack(CodeRange::ImportInterpExit, interpExit) &&
            codeRanges_.emplaceBack(CodeRange::ImportJitExit, jitExit);
 }
 
 bool
-ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* index)
+ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex)
 {
-    *index = exports_.length();
     return exports_.emplaceBack(Move(sig), funcIndex);
 }
 
 uint32_t
 ModuleGenerator::exportFuncIndex(uint32_t index) const
 {
     return exports_[index].funcIndex();
 }
@@ -494,28 +493,27 @@ bool
 ModuleGenerator::defineOutOfBoundsStub(Offsets offsets)
 {
     MOZ_ASSERT(finishedFuncs_);
     staticLinkData_->pod.outOfBoundsOffset = offsets.begin;
     return codeRanges_.emplaceBack(CodeRange::Inline, offsets);
 }
 
 Module*
-ModuleGenerator::finish(Module::HeapBool usesHeap,
-                        Module::SharedBool sharedHeap,
+ModuleGenerator::finish(HeapUsage heapUsage,
                         Module::MutedBool mutedErrors,
                         CacheableChars filename,
                         CacheableTwoByteChars displayURL,
                         UniqueStaticLinkData* staticLinkData,
                         SlowFunctionVector* slowFuncs)
 {
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(finishedFuncs_);
 
-    if (!GenerateStubs(*this, usesHeap))
+    if (!GenerateStubs(*this, UsesHeap(heapUsage)))
         return nullptr;
 
     masm_.finish();
     if (masm_.oom())
         return nullptr;
 
     // Start global data on a new page so JIT code may be given independent
     // protection flags. Note assumption that global data starts right after
@@ -611,18 +609,17 @@ ModuleGenerator::finish(Module::HeapBool
 #endif
 
     *staticLinkData = Move(staticLinkData_);
     *slowFuncs = Move(slowFuncs_);
     return cx_->new_<Module>(args_,
                              funcBytes_,
                              codeBytes,
                              globalBytes_,
-                             usesHeap,
-                             sharedHeap,
+                             heapUsage,
                              mutedErrors,
                              Move(code),
                              Move(imports_),
                              Move(exports_),
                              masm_.extractHeapAccesses(),
                              Move(codeRanges_),
                              Move(callSites),
                              Move(funcNames_),
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -118,17 +118,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     // Imports:
     bool declareImport(MallocSig&& sig, uint32_t* index);
     uint32_t numDeclaredImports() const;
     uint32_t importExitGlobalDataOffset(uint32_t index) const;
     const MallocSig& importSig(uint32_t index) const;
     bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);
 
     // Exports:
-    bool declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* index);
+    bool declareExport(MallocSig&& sig, uint32_t funcIndex);
     uint32_t numDeclaredExports() const;
     uint32_t exportFuncIndex(uint32_t index) const;
     const MallocSig& exportSig(uint32_t index) const;
     bool defineExport(uint32_t index, Offsets offsets);
 
     // Functions:
     bool startFunc(PropertyName* name, unsigned line, unsigned column, FunctionGenerator* fg);
     bool finishFunc(uint32_t funcIndex, const LifoSig& sig, unsigned generateTime, FunctionGenerator* fg);
@@ -142,18 +142,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     // Stubs:
     bool defineInlineStub(Offsets offsets);
     bool defineSyncInterruptStub(ProfilingOffsets offsets);
     bool defineAsyncInterruptStub(Offsets offsets);
     bool defineOutOfBoundsStub(Offsets offsets);
 
     // Null return indicates failure. The caller must immediately root a
     // non-null return value.
-    Module* finish(Module::HeapBool usesHeap,
-                   Module::SharedBool sharedHeap,
+    Module* finish(HeapUsage heapUsage,
                    Module::MutedBool mutedErrors,
                    CacheableChars filename,
                    CacheableTwoByteChars displayURL,
                    UniqueStaticLinkData* staticLinkData,
                    SlowFunctionVector* slowFuncs);
 };
 
 // A FunctionGenerator encapsulates the generation of a single function body.
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -506,18 +506,19 @@ Module::activation()
 {
     MOZ_ASSERT(dynamicallyLinked_);
     return *reinterpret_cast<WasmActivation**>(globalData() + ActivationGlobalDataOffset);
 }
 
 void
 Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap)
 {
+    MOZ_ASSERT(usesHeap());
     MOZ_ASSERT_IF(heap->is<ArrayBufferObject>(), heap->as<ArrayBufferObject>().isAsmJS());
-    MOZ_ASSERT(!maybeHeap_);
+    MOZ_ASSERT(!heap_);
     MOZ_ASSERT(!rawHeapPtr());
 
     uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - protected by Module methods*/);
     uint32_t heapLength = heap->byteLength();
 #if defined(JS_CODEGEN_X86)
     // An access is out-of-bounds iff
     //      ptr + offset + data-type-byte-size > heapLength
     // i.e. ptr > heapLength - data-type-byte-size - offset. data-type-byte-size
@@ -545,24 +546,27 @@ Module::specializeToHeap(ArrayBufferObje
             X86Encoding::AddInt32(access.patchLengthAt(code()), heapLength);
     }
 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
       defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     for (const HeapAccess& access : heapAccesses_)
         Assembler::UpdateBoundsCheck(heapLength, (Instruction*)(access.insnOffset() + code()));
 #endif
 
-    maybeHeap_ = heap;
+    heap_ = heap;
     rawHeapPtr() = ptrBase;
 }
 
 void
 Module::despecializeFromHeap(ArrayBufferObjectMaybeShared* heap)
 {
-    MOZ_ASSERT_IF(maybeHeap_, maybeHeap_ == heap);
+    // heap_/rawHeapPtr can be null if this module holds cloned code from
+    // another dynamically-linked module which we are despecializing from that
+    // module's heap.
+    MOZ_ASSERT_IF(heap_, heap_ == heap);
     MOZ_ASSERT_IF(rawHeapPtr(), rawHeapPtr() == heap->dataPointerEither().unwrap());
 
 #if defined(JS_CODEGEN_X86)
     uint32_t heapLength = heap->byteLength();
     uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - used for value*/);
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
         const HeapAccess& access = heapAccesses_[i];
         if (access.hasLengthCheck())
@@ -576,17 +580,17 @@ Module::despecializeFromHeap(ArrayBuffer
     uint32_t heapLength = heap->byteLength();
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
         const HeapAccess& access = heapAccesses_[i];
         if (access.hasLengthCheck())
             X86Encoding::AddInt32(access.patchLengthAt(code()), -heapLength);
     }
 #endif
 
-    maybeHeap_ = nullptr;
+    heap_ = nullptr;
     rawHeapPtr() = nullptr;
 }
 
 void
 Module::sendCodeRangesToProfiler(JSContext* cx)
 {
 #ifdef JS_ION_PERF
     if (PerfFuncEnabled()) {
@@ -695,30 +699,27 @@ Module::ImportExit&
 Module::importToExit(const Import& import)
 {
     return *reinterpret_cast<ImportExit*>(globalData() + import.exitGlobalDataOffset());
 }
 
 /* static */ Module::CacheablePod
 Module::zeroPod()
 {
-    CacheablePod pod = {0, 0, 0, false, false, false, false, false};
+    CacheablePod pod = {0, 0, 0, HeapUsage::None, false, false, false};
     return pod;
 }
 
 void
 Module::init()
 {
    staticallyLinked_ = false;
    interrupt_ = nullptr;
    outOfBounds_ = nullptr;
    dynamicallyLinked_ = false;
-   prev_ = nullptr;
-   next_ = nullptr;
-   interrupted_ = false;
 
     *(double*)(globalData() + NaN64GlobalDataOffset) = GenericNaN();
     *(float*)(globalData() + NaN32GlobalDataOffset) = GenericNaN();
 }
 
 // Private constructor used for deserialization and cloning.
 Module::Module(const CacheablePod& pod,
                UniqueCodePtr code,
@@ -752,18 +753,17 @@ Module::Module(const CacheablePod& pod,
     init();
 }
 
 // Public constructor for compilation.
 Module::Module(CompileArgs args,
                uint32_t functionBytes,
                uint32_t codeBytes,
                uint32_t globalBytes,
-               HeapBool usesHeap,
-               SharedBool sharedHeap,
+               HeapUsage heapUsage,
                MutedBool mutedErrors,
                UniqueCodePtr code,
                ImportVector&& imports,
                ExportVector&& exports,
                HeapAccessVector&& heapAccesses,
                CodeRangeVector&& codeRanges,
                CallSiteVector&& callSites,
                CacheableCharsVector&& funcNames,
@@ -781,54 +781,45 @@ Module::Module(CompileArgs args,
     displayURL_(Move(displayURL)),
     loadedFromCache_(false),
     profilingEnabled_(false)
 {
     // Work around MSVC 2013 bug around {} member initialization.
     const_cast<uint32_t&>(pod.functionBytes_) = functionBytes;
     const_cast<uint32_t&>(pod.codeBytes_) = codeBytes;
     const_cast<uint32_t&>(pod.globalBytes_) = globalBytes;
-    const_cast<bool&>(pod.usesHeap_) = bool(usesHeap);
-    const_cast<bool&>(pod.sharedHeap_) = bool(sharedHeap);
+    const_cast<HeapUsage&>(pod.heapUsage_) = heapUsage;
     const_cast<bool&>(pod.mutedErrors_) = bool(mutedErrors);
     const_cast<bool&>(pod.usesSignalHandlersForOOB_) = args.useSignalHandlersForOOB;
     const_cast<bool&>(pod.usesSignalHandlersForInterrupt_) = args.useSignalHandlersForInterrupt;
 
-    MOZ_ASSERT_IF(sharedHeap, usesHeap);
     init();
 }
 
 Module::~Module()
 {
-    MOZ_ASSERT(!interrupted_);
-
     if (code_) {
         for (unsigned i = 0; i < imports_.length(); i++) {
             ImportExit& exit = importToExit(imports_[i]);
             if (exit.baselineScript)
                 exit.baselineScript->removeDependentWasmModule(*this, i);
         }
     }
-
-    if (prev_)
-        *prev_ = next_;
-    if (next_)
-        next_->prev_ = prev_;
 }
 
 void
 Module::trace(JSTracer* trc)
 {
     for (const Import& import : imports_) {
         if (importToExit(import).fun)
             TraceEdge(trc, &importToExit(import).fun, "wasm function import");
     }
 
-    if (maybeHeap_)
-        TraceEdge(trc, &maybeHeap_, "wasm buffer");
+    if (heap_)
+        TraceEdge(trc, &heap_, "wasm buffer");
 }
 
 CompileArgs
 Module::compileArgs() const
 {
     CompileArgs args;
     args.useSignalHandlersForOOB = pod.usesSignalHandlersForOOB_;
     args.useSignalHandlersForInterrupt = pod.usesSignalHandlersForInterrupt_;
@@ -977,23 +968,16 @@ Module::staticallyLink(ExclusiveContext*
 bool
 Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
                         const AutoVectorRooter<JSFunction*>& imports)
 {
     MOZ_ASSERT(staticallyLinked_);
     MOZ_ASSERT(!dynamicallyLinked_);
     dynamicallyLinked_ = true;
 
-    // Add this module to the JSRuntime-wide list of dynamically-linked modules.
-    next_ = cx->runtime()->linkedWasmModules;
-    prev_ = &cx->runtime()->linkedWasmModules;
-    cx->runtime()->linkedWasmModules = this;
-    if (next_)
-        next_->prev_ = &next_;
-
     // Push a JitContext for benefit of IsCompilingAsmJS and flush the ICache.
     // We've been inhibiting flushing up to this point so flush it all now.
     JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
     MOZ_ASSERT(IsCompilingAsmJS());
     AutoFlushICache afc("Module::dynamicallyLink");
     AutoFlushICache::setRange(uintptr_t(code()), pod.codeBytes_);
 
     // Initialize imports with actual imported values.
@@ -1002,120 +986,56 @@ Module::dynamicallyLink(JSContext* cx, H
         const Import& import = imports_[i];
         ImportExit& exit = importToExit(import);
         exit.code = code() + import.interpExitCodeOffset();
         exit.fun = imports[i];
         exit.baselineScript = nullptr;
     }
 
     // Specialize code to the actual heap.
-    if (heap)
+    if (usesHeap())
         specializeToHeap(heap);
 
     // See AllocateCode comment above.
     ExecutableAllocator::makeExecutable(code(), pod.codeBytes_);
 
     sendCodeRangesToProfiler(cx);
     return true;
 }
 
-ArrayBufferObjectMaybeShared*
-Module::maybeBuffer() const
+SharedMem<uint8_t*>
+Module::heap() const
 {
     MOZ_ASSERT(dynamicallyLinked_);
-    return maybeHeap_;
-}
-
-SharedMem<uint8_t*>
-Module::maybeHeap() const
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    MOZ_ASSERT_IF(!pod.usesHeap_, rawHeapPtr() == nullptr);
-    return pod.sharedHeap_
+    MOZ_ASSERT(usesHeap());
+    MOZ_ASSERT(rawHeapPtr());
+    return hasSharedHeap()
            ? SharedMem<uint8_t*>::shared(rawHeapPtr())
            : SharedMem<uint8_t*>::unshared(rawHeapPtr());
 }
 
 size_t
 Module::heapLength() const
 {
     MOZ_ASSERT(dynamicallyLinked_);
-    return maybeHeap_ ? maybeHeap_->byteLength() : 0;
+    MOZ_ASSERT(usesHeap());
+    return heap_->byteLength();
 }
 
 void
 Module::deoptimizeImportExit(uint32_t importIndex)
 {
     MOZ_ASSERT(dynamicallyLinked_);
     const Import& import = imports_[importIndex];
     ImportExit& exit = importToExit(import);
     exit.code = code() + import.interpExitCodeOffset();
     exit.baselineScript = nullptr;
 }
 
 bool
-Module::changeHeap(Handle<ArrayBufferObject*> newHeap, JSContext* cx)
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    MOZ_ASSERT(pod.usesHeap_);
-
-    // Content JS should not be able to run (and change heap) from within an
-    // interrupt callback, but in case it does, fail to change heap. Otherwise,
-    // the heap can change at every single instruction which would prevent
-    // future optimizations like heap-base hoisting.
-    if (interrupted_)
-        return false;
-
-    AutoMutateCode amc(cx, *this, "Module::changeHeap");
-    if (maybeHeap_)
-        despecializeFromHeap(maybeHeap_);
-    specializeToHeap(newHeap);
-    return true;
-}
-
-bool
-Module::detachHeap(JSContext* cx)
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    MOZ_ASSERT(pod.usesHeap_);
-
-    // Content JS should not be able to run (and detach heap) from within an
-    // interrupt callback, but in case it does, fail. Otherwise, the heap can
-    // change at an arbitrary instruction and break the assumption below.
-    if (interrupted_) {
-        JS_ReportError(cx, "attempt to detach from inside interrupt handler");
-        return false;
-    }
-
-    // Even if this->active(), to reach here, the activation must have called
-    // out via an import exit stub. FFI stubs check if heapDatum() is null on
-    // reentry and throw an exception if so.
-    MOZ_ASSERT_IF(activation(), activation()->exitReason() == ExitReason::ImportJit ||
-                                activation()->exitReason() == ExitReason::ImportInterp);
-
-    AutoMutateCode amc(cx, *this, "Module::detachHeap");
-    despecializeFromHeap(maybeHeap_);
-    return true;
-}
-
-void
-Module::setInterrupted(bool interrupted)
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    interrupted_ = interrupted;
-}
-
-Module*
-Module::nextLinked() const
-{
-    MOZ_ASSERT(dynamicallyLinked_);
-    return next_;
-}
-
-bool
 Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
 {
     MOZ_ASSERT(dynamicallyLinked_);
 
     const Export& exp = exports_[exportIndex];
 
     // Enable/disable profiling in the Module to match the current global
     // profiling state. Don't do this if the Module is already active on the
@@ -1174,27 +1094,16 @@ Module::callExport(JSContext* cx, uint32
                 return false;
             // Bool32x4 uses the same representation as Int32x4.
             memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
             break;
           }
         }
     }
 
-    // The correct way to handle this situation would be to allocate a new range
-    // of PROT_NONE memory and module.changeHeap to this memory. That would
-    // cause every access to take the out-of-bounds signal-handler path which
-    // does the right thing. For now, just throw an out-of-memory exception
-    // since these can technically pop out anywhere and the full fix may
-    // actually OOM when trying to allocate the PROT_NONE memory.
-    if (usesHeap() && !maybeHeap_) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
-        return false;
-    }
-
     {
         // Push a WasmActivation to describe the wasm frames we're about to push
         // when running this module. Additionally, push a JitActivation so that
         // the optimized wasm-to-Ion FFI call path (which we want to be very
         // fast) can avoid doing so. The JitActivation is marked as inactive so
         // stack iteration will skip over it.
         WasmActivation activation(cx, *this);
         JitActivation jitActivation(cx, /* active */ false);
@@ -1513,18 +1422,18 @@ Module::clone(JSContext* cx, const Stati
                                        CacheBool::NotLoadedFromCache,
                                        ProfilingBool(profilingEnabled_),
                                        Move(funcLabels));
     if (!out)
         return nullptr;
 
     // If the copied machine code has been specialized to the heap, it must be
     // unspecialized in the copy.
-    if (maybeHeap_)
-        out->despecializeFromHeap(maybeHeap_);
+    if (usesHeap())
+        out->despecializeFromHeap(heap_);
 
     if (!out->staticallyLink(cx, linkData))
         return nullptr;
 
     return Move(out);
 }
 
 void
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -320,16 +320,32 @@ class CodeDeleter
     explicit CodeDeleter(uint32_t bytes) : bytes_(bytes) {}
     void operator()(uint8_t* p);
 };
 typedef JS::UniquePtr<uint8_t, CodeDeleter> UniqueCodePtr;
 
 UniqueCodePtr
 AllocateCode(ExclusiveContext* cx, size_t bytes);
 
+// A wasm module can either use no heap, a unshared heap (ArrayBuffer) or shared
+// heap (SharedArrayBuffer).
+
+enum class HeapUsage
+{
+    None = false,
+    Unshared = 1,
+    Shared = 2
+};
+
+static inline bool
+UsesHeap(HeapUsage heapUsage)
+{
+    return bool(heapUsage);
+}
+
 // Module represents a compiled WebAssembly module which lives until the last
 // reference to any exported functions is dropped. Modules must be wrapped by a
 // rooted JSObject immediately after creation so that Module::trace() is called
 // during GC. Modules are created after compilation completes and start in a
 // a fully unlinked state. After creation, a module must be first statically
 // linked and then dynamically linked:
 //
 //  - Static linking patches code or global data that relies on absolute
@@ -370,18 +386,17 @@ class Module
     typedef Vector<CacheableChars, 0, SystemAllocPolicy> FuncLabelVector;
     typedef RelocatablePtrArrayBufferObjectMaybeShared BufferPtr;
 
     // Initialized when constructed:
     struct CacheablePod {
         const uint32_t           functionBytes_;
         const uint32_t           codeBytes_;
         const uint32_t           globalBytes_;
-        const bool               usesHeap_;
-        const bool               sharedHeap_;
+        const HeapUsage          heapUsage_;
         const bool               mutedErrors_;
         const bool               usesSignalHandlersForOOB_;
         const bool               usesSignalHandlersForInterrupt_;
     } pod;
     const UniqueCodePtr          code_;
     const ImportVector           imports_;
     const ExportVector           exports_;
     const HeapAccessVector       heapAccesses_;
@@ -395,24 +410,21 @@ class Module
     // Initialized during staticallyLink:
     bool                         staticallyLinked_;
     uint8_t*                     interrupt_;
     uint8_t*                     outOfBounds_;
     FuncPtrTableVector           funcPtrTables_;
 
     // Initialized during dynamicallyLink:
     bool                         dynamicallyLinked_;
-    BufferPtr                    maybeHeap_;
-    Module**                     prev_;
-    Module*                      next_;
+    BufferPtr                    heap_;
 
     // Mutated after dynamicallyLink:
     bool                         profilingEnabled_;
     FuncLabelVector              funcLabels_;
-    bool                         interrupted_;
 
     class AutoMutateCode;
 
     uint32_t totalBytes() const;
     uint8_t* rawHeapPtr() const;
     uint8_t*& rawHeapPtr();
     WasmActivation*& activation();
     void specializeToHeap(ArrayBufferObjectMaybeShared* heap);
@@ -443,44 +455,42 @@ class Module
     template <class> friend struct js::MallocProvider;
     friend class js::WasmActivation;
 
   public:
     static const unsigned SizeOfImportExit = sizeof(ImportExit);
     static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun);
     static const unsigned SizeOfEntryArg = sizeof(EntryArg);
 
-    enum HeapBool { DoesntUseHeap = false, UsesHeap = true };
-    enum SharedBool { UnsharedHeap = false, SharedHeap = true };
     enum MutedBool { DontMuteErrors = false, MuteErrors = true };
 
     Module(CompileArgs args,
            uint32_t functionBytes,
            uint32_t codeBytes,
            uint32_t globalBytes,
-           HeapBool usesHeap,
-           SharedBool sharedHeap,
+           HeapUsage heapUsage,
            MutedBool mutedErrors,
            UniqueCodePtr code,
            ImportVector&& imports,
            ExportVector&& exports,
            HeapAccessVector&& heapAccesses,
            CodeRangeVector&& codeRanges,
            CallSiteVector&& callSites,
            CacheableCharsVector&& funcNames,
            CacheableChars filename,
            CacheableTwoByteChars displayURL);
     ~Module();
     void trace(JSTracer* trc);
 
     uint8_t* code() const { return code_.get(); }
     uint8_t* globalData() const { return code() + pod.codeBytes_; }
     uint32_t globalBytes() const { return pod.globalBytes_; }
-    bool usesHeap() const { return pod.usesHeap_; }
-    bool sharedHeap() const { return pod.sharedHeap_; }
+    HeapUsage heapUsage() const { return pod.heapUsage_; }
+    bool usesHeap() const { return UsesHeap(pod.heapUsage_); }
+    bool hasSharedHeap() const { return pod.heapUsage_ == HeapUsage::Shared; }
     bool mutedErrors() const { return pod.mutedErrors_; }
     CompileArgs compileArgs() const;
     const ImportVector& imports() const { return imports_; }
     const ExportVector& exports() const { return exports_; }
     const char* functionName(uint32_t i) const { return funcNames_[i].get(); }
     const char* filename() const { return filename_.get(); }
     const char16_t* displayURL() const { return displayURL_.get(); }
     bool loadedFromCache() const { return loadedFromCache_; }
@@ -510,31 +520,19 @@ class Module
     // buffer must be given. The given import vector must match the module's
     // ImportVector.
 
     bool dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
                          const AutoVectorRooter<JSFunction*>& imports);
 
     // The wasm heap, established by dynamicallyLink.
 
-    ArrayBufferObjectMaybeShared* maybeBuffer() const;
-    SharedMem<uint8_t*> maybeHeap() const;
+    SharedMem<uint8_t*> heap() const;
     size_t heapLength() const;
 
-    // asm.js may detach and change the heap at any time. As an internal detail,
-    // the heap may not be changed while the module has been asynchronously
-    // interrupted.
-    //
-    // N.B. These methods and asm.js change-heap support will be removed soon.
-
-    bool changeHeap(Handle<ArrayBufferObject*> newBuffer, JSContext* cx);
-    bool detachHeap(JSContext* cx);
-    void setInterrupted(bool interrupted);
-    Module* nextLinked() const;
-
     // The exports of a wasm module are called by preparing an array of
     // arguments (coerced to the corresponding types of the Export signature)
     // and calling the export's entry trampoline.
 
     bool callExport(JSContext* cx, uint32_t exportIndex, CallArgs args);
 
     // Initially, calls to imports in wasm code call out through the generic
     // callImport method. If the imported callee gets JIT compiled and the types
--- a/js/src/asmjs/WasmSignalHandlers.cpp
+++ b/js/src/asmjs/WasmSignalHandlers.cpp
@@ -622,17 +622,17 @@ EmulateHeapAccess(EMULATOR_CONTEXT* cont
     MOZ_RELEASE_ASSERT(address.disp() >= 0);
     MOZ_RELEASE_ASSERT(address.base() == HeapReg.code());
     MOZ_RELEASE_ASSERT(!address.hasIndex() || address.index() != HeapReg.code());
     MOZ_RELEASE_ASSERT(address.scale() == 0);
     if (address.hasBase()) {
         uintptr_t base;
         StoreValueFromGPReg(SharedMem<void*>::unshared(&base), sizeof(uintptr_t),
                             AddressOfGPRegisterSlot(context, address.base()));
-        MOZ_RELEASE_ASSERT(reinterpret_cast<uint8_t*>(base) == module.maybeHeap());
+        MOZ_RELEASE_ASSERT(reinterpret_cast<uint8_t*>(base) == module.heap());
     }
     if (address.hasIndex()) {
         uintptr_t index;
         StoreValueFromGPReg(SharedMem<void*>::unshared(&index), sizeof(uintptr_t),
                             AddressOfGPRegisterSlot(context, address.index()));
         MOZ_RELEASE_ASSERT(uint32_t(index) == index);
     }
 #endif
@@ -640,38 +640,38 @@ EmulateHeapAccess(EMULATOR_CONTEXT* cont
     // Determine the actual effective address of the faulting access. We can't
     // rely on the faultingAddress given to us by the OS, because we need the
     // address of the start of the access, and the OS may sometimes give us an
     // address somewhere in the middle of the heap access.
     uint8_t* accessAddress = ComputeAccessAddress(context, address);
     MOZ_RELEASE_ASSERT(size_t(faultingAddress - accessAddress) < access.size(),
                        "Given faulting address does not appear to be within computed "
                        "faulting address range");
-    MOZ_RELEASE_ASSERT(accessAddress >= module.maybeHeap(),
+    MOZ_RELEASE_ASSERT(accessAddress >= module.heap(),
                        "Access begins outside the asm.js heap");
-    MOZ_RELEASE_ASSERT(accessAddress + access.size() <= module.maybeHeap() + AsmJSMappedSize,
+    MOZ_RELEASE_ASSERT(accessAddress + access.size() <= module.heap() + AsmJSMappedSize,
                        "Access extends beyond the asm.js heap guard region");
-    MOZ_RELEASE_ASSERT(accessAddress + access.size() > module.maybeHeap() + module.heapLength(),
+    MOZ_RELEASE_ASSERT(accessAddress + access.size() > module.heap() + module.heapLength(),
                        "Computed access address is not actually out of bounds");
 
     // The basic sandbox model is that all heap accesses are a heap base
     // register plus an index, and the index is always computed with 32-bit
     // operations, so we know it can only be 4 GiB off of the heap base.
     //
     // However, we wish to support the optimization of folding immediates
     // and scaled indices into addresses, and any address arithmetic we fold
     // gets done at full pointer width, so it doesn't get properly wrapped.
     // We support this by extending AsmJSMappedSize to the greatest size
     // that could be reached by such an unwrapped address, and then when we
     // arrive here in the signal handler for such an access, we compute the
     // fully wrapped address, and perform the load or store on it.
     //
     // Taking a signal is really slow, but in theory programs really shouldn't
     // be hitting this anyway.
-    intptr_t unwrappedOffset = accessAddress - module.maybeHeap().unwrap(/*safe - for value*/);
+    intptr_t unwrappedOffset = accessAddress - module.heap().unwrap(/*safe - for value*/);
     uint32_t wrappedOffset = uint32_t(unwrappedOffset);
     size_t size = access.size();
     MOZ_RELEASE_ASSERT(wrappedOffset + size > wrappedOffset);
     bool inBounds = wrappedOffset < module.heapLength() &&
                     wrappedOffset + size < module.heapLength();
 
     // If this is storing Z of an XYZ, check whether X is also in bounds, so
     // that we don't store anything before throwing.
@@ -679,20 +679,20 @@ EmulateHeapAccess(EMULATOR_CONTEXT* cont
     uint32_t wrappedBaseOffset = uint32_t(unwrappedOffset - heapAccess->offsetWithinWholeSimdVector());
     if (wrappedBaseOffset >= module.heapLength())
         inBounds = false;
 
     if (inBounds) {
         // We now know that this is an access that is actually in bounds when
         // properly wrapped. Complete the load or store with the wrapped
         // address.
-        SharedMem<uint8_t*> wrappedAddress = module.maybeHeap() + wrappedOffset;
-        MOZ_RELEASE_ASSERT(wrappedAddress >= module.maybeHeap());
+        SharedMem<uint8_t*> wrappedAddress = module.heap() + wrappedOffset;
+        MOZ_RELEASE_ASSERT(wrappedAddress >= module.heap());
         MOZ_RELEASE_ASSERT(wrappedAddress + size > wrappedAddress);
-        MOZ_RELEASE_ASSERT(wrappedAddress + size <= module.maybeHeap() + module.heapLength());
+        MOZ_RELEASE_ASSERT(wrappedAddress + size <= module.heap() + module.heapLength());
         switch (access.kind()) {
           case Disassembler::HeapAccess::Load:
             SetRegisterToLoadedValue(context, wrappedAddress.cast<void*>(), size, access.otherOperand());
             break;
           case Disassembler::HeapAccess::LoadSext32:
             SetRegisterToLoadedValueSext32(context, wrappedAddress.cast<void*>(), size, access.otherOperand());
             break;
           case Disassembler::HeapAccess::Store:
@@ -757,19 +757,19 @@ HandleFault(PEXCEPTION_POINTERS exceptio
     if (!activation)
         return false;
 
     const Module& module = activation->module();
 
     // These checks aren't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(record->ExceptionInformation[1]);
-    if (!module.maybeHeap() ||
-        faultingAddress < module.maybeHeap() ||
-        faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
+    if (!module.usesHeap() ||
+        faultingAddress < module.heap() ||
+        faultingAddress >= module.heap() + AsmJSMappedSize)
     {
         return false;
     }
 
     if (!module.containsFunctionPC(pc)) {
         // On Windows, it is possible for InterruptRunningCode to execute
         // between a faulting heap access and the handling of the fault due
         // to InterruptRunningCode's use of SuspendThread. When this happens,
@@ -902,19 +902,19 @@ HandleMachException(JSRuntime* rt, const
 
     const Module& module = activation->module();
     if (!module.containsFunctionPC(pc))
         return false;
 
     // These checks aren't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
-    if (!module.maybeHeap() ||
-        faultingAddress < module.maybeHeap() ||
-        faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
+    if (!module.usesHeap() ||
+        faultingAddress < module.heap() ||
+        faultingAddress >= module.heap() + AsmJSMappedSize)
     {
         return false;
     }
 
     const HeapAccess* heapAccess = module.lookupHeapAccess(pc);
     if (!heapAccess)
         return false;
 
@@ -1112,19 +1112,19 @@ HandleFault(int signum, siginfo_t* info,
 
     const Module& module = activation->module();
     if (!module.containsFunctionPC(pc))
         return false;
 
     // These checks aren't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
-    if (!module.maybeHeap() ||
-        faultingAddress < module.maybeHeap() ||
-        faultingAddress >= module.maybeHeap() + AsmJSMappedSize)
+    if (!module.usesHeap() ||
+        faultingAddress < module.heap() ||
+        faultingAddress >= module.heap() + AsmJSMappedSize)
     {
         return false;
     }
 
     const HeapAccess* heapAccess = module.lookupHeapAccess(pc);
     if (!heapAccess)
         return false;
 
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -94,17 +94,17 @@ static const unsigned FramePushedAfterSa
 #endif
 static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*);
 
 // Generate a stub that enters wasm from a C++ caller via the native ABI.
 // The signature of the entry point is Module::CodePtr. The exported wasm
 // function has an ABI derived from its specific signature, so this function
 // must map from the ABI of CodePtr to the export's signature's ABI.
 static bool
-GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, Module::HeapBool usesHeap)
+GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
 {
     MacroAssembler& masm = mg.masm();
     const MallocSig& sig = mg.exportSig(exportIndex);
 
     masm.haltingAlign(CodeAlignment);
 
     Offsets offsets;
     offsets.begin = masm.currentOffset();
@@ -327,40 +327,22 @@ FillArgumentArray(MacroAssembler& masm, 
                 masm.canonicalizeDouble(ScratchDoubleReg);
                 masm.storeDouble(ScratchDoubleReg, dstAddr);
             }
             break;
         }
     }
 }
 
-// If an import call detaches its heap (viz., via ArrayBuffer.transfer), it must
-// call change-heap to another heap (viz., the new heap returned by transfer)
-// before returning to asm.js code. If the application fails to do this (if the
-// heap pointer is null), jump to a stub.
-static void
-CheckForHeapDetachment(MacroAssembler& masm, Register scratch, Label* onDetached)
-{
-    MOZ_ASSERT(int(masm.framePushed()) >= int(ShadowStackSpace));
-    AssertStackAlignment(masm, ABIStackAlignment);
-#if defined(JS_CODEGEN_X86)
-    CodeOffset offset = masm.movlWithPatch(PatchedAbsoluteAddress(), scratch);
-    masm.append(AsmJSGlobalAccess(offset, HeapGlobalDataOffset));
-    masm.branchTestPtr(Assembler::Zero, scratch, scratch, onDetached);
-#else
-    masm.branchTestPtr(Assembler::Zero, HeapReg, HeapReg, onDetached);
-#endif
-}
-
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into an appropriate InvokeImport C++
 // function, having boxed all the ABI arguments into a homogeneous Value array.
 static bool
-GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool usesHeap,
-                       Label* throwLabel, Label* onDetached, ProfilingOffsets* offsets)
+GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Label* throwLabel,
+                       ProfilingOffsets* offsets)
 {
     MacroAssembler& masm = mg.masm();
     const MallocSig& sig = mg.importSig(importIndex);
 
     masm.setFramePushed(0);
 
     // Argument types for InvokeImport_*:
     static const MIRType typeArray[] = { MIRType_Pointer,   // ImportExit
@@ -435,23 +417,16 @@ GenerateInterpExitStub(ModuleGenerator& 
         masm.loadDouble(argv, ReturnDoubleReg);
         break;
       case ExprType::I32x4:
       case ExprType::F32x4:
       case ExprType::B32x4:
         MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
     }
 
-    // The heap pointer may have changed during the FFI, so reload it and test
-    // for detachment.
-    if (usesHeap) {
-        masm.loadAsmJSHeapRegisterFromGlobalData();
-        CheckForHeapDetachment(masm, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
-    }
-
     GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, offsets);
 
     if (masm.oom())
         return false;
 
     offsets->end = masm.currentOffset();
     return true;
 }
@@ -461,18 +436,18 @@ static const unsigned MaybeSavedGlobalRe
 #else
 static const unsigned MaybeSavedGlobalReg = 0;
 #endif
 
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into a compatible JIT function,
 // having boxed all the ABI arguments into the JIT stack frame layout.
 static bool
-GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, Module::HeapBool usesHeap,
-                    Label* throwLabel, Label* onDetached, ProfilingOffsets* offsets)
+GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
+                    Label* throwLabel, ProfilingOffsets* offsets)
 {
     MacroAssembler& masm = mg.masm();
     const MallocSig& sig = mg.importSig(importIndex);
 
     masm.setFramePushed(0);
 
     // JIT calls use the following stack layout (sp grows to the left):
     //   | retaddr | descriptor | callee | argc | this | arg1..N |
@@ -535,18 +510,17 @@ GenerateJitExitStub(ModuleGenerator& mg,
     FillArgumentArray(masm, sig.args(), argOffset, offsetToCallerStackArgs, scratch);
     argOffset += sig.args().length() * sizeof(Value);
     MOZ_ASSERT(argOffset == jitFrameBytes);
 
     // 6. Jit code will clobber all registers, even non-volatiles. GlobalReg and
     //    HeapReg are removed from the general register set for asm.js code, so
     //    these will not have been saved by the caller like all other registers,
     //    so they must be explicitly preserved. Only save GlobalReg since
-    //    HeapReg must be reloaded (from global data) after the call since the
-    //    heap may change during the FFI call.
+    //    HeapReg can be reloaded (from global data) after the call.
 #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.
         //
@@ -693,22 +667,20 @@ GenerateJitExitStub(ModuleGenerator& mg,
       case ExprType::F32x4:
       case ExprType::B32x4:
         MOZ_CRASH("SIMD types shouldn't be returned from an import");
     }
 
     Label done;
     masm.bind(&done);
 
-    // The heap pointer has to be reloaded anyway since JIT code could have
-    // clobbered it. Additionally, the import may have detached the heap buffer.
-    if (usesHeap) {
+    // Ion code does not respect system callee-saved register conventions so
+    // reload the heap register.
+    if (usesHeap)
         masm.loadAsmJSHeapRegisterFromGlobalData();
-        CheckForHeapDetachment(masm, ABIArgGenerator::NonReturn_VolatileReg0, onDetached);
-    }
 
     GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, offsets);
 
     if (oolConvert.used()) {
         masm.bind(&oolConvert);
         masm.setFramePushed(nativeFramePushed);
 
         // Coercion calls use the following stack layout (sp grows to the left):
@@ -759,42 +731,16 @@ GenerateJitExitStub(ModuleGenerator& mg,
 
     if (masm.oom())
         return false;
 
     offsets->end = masm.currentOffset();
     return true;
 }
 
-// Generate a stub that is called when returning from an exit where the module's
-// buffer has been detached. This stub first calls a C++ function to report an
-// exception and then jumps to the generic throw stub to pop everything off the
-// stack.
-static bool
-GenerateOnDetachedStub(ModuleGenerator& mg, Label* onDetached, Label* throwLabel)
-{
-    MacroAssembler& masm = mg.masm();
-
-    masm.haltingAlign(CodeAlignment);
-    Offsets offsets;
-    offsets.begin = masm.currentOffset();
-    masm.bind(onDetached);
-
-    // For now, OnDetached always throws (see OnDetached comment).
-    masm.assertStackAlignment(ABIStackAlignment);
-    masm.call(SymbolicAddress::OnDetached);
-    masm.jump(throwLabel);
-
-    if (masm.oom())
-        return false;
-
-    offsets.end = masm.currentOffset();
-    return mg.defineInlineStub(offsets);
-}
-
 // Generate a stub that is called immediately after the prologue when there is a
 // stack overflow. This stub calls a C++ function to report the error and then
 // jumps to the throw stub to pop the activation.
 static bool
 GenerateStackOverflowStub(ModuleGenerator& mg, Label* throwLabel)
 {
     MacroAssembler& masm = mg.masm();
 
@@ -924,17 +870,17 @@ static const LiveRegisterSet AllRegsExce
 // code. That means we must first save *all* registers and restore *all*
 // registers (except the stack pointer) when we resume. The address to resume to
 // (assuming that js::HandleExecutionInterrupt doesn't indicate that the
 // execution should be aborted) is stored in WasmActivation::resumePC_.
 // Unfortunately, loading this requires a scratch register which we don't have
 // after restoring all registers. To hack around this, push the resumePC on the
 // stack so that it can be popped directly into PC.
 static bool
-GenerateAsyncInterruptStub(ModuleGenerator& mg, Module::HeapBool usesHeap, Label* throwLabel)
+GenerateAsyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
 {
     MacroAssembler& masm = mg.masm();
 
     masm.haltingAlign(CodeAlignment);
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
@@ -1121,45 +1067,36 @@ GenerateThrowStub(ModuleGenerator& mg, L
     if (masm.oom())
         return false;
 
     offsets.end = masm.currentOffset();
     return mg.defineInlineStub(offsets);
 }
 
 bool
-wasm::GenerateStubs(ModuleGenerator& mg, Module::HeapBool usesHeap)
+wasm::GenerateStubs(ModuleGenerator& mg, bool usesHeap)
 {
     for (unsigned i = 0; i < mg.numDeclaredExports(); i++) {
         if (!GenerateEntry(mg, i, usesHeap))
             return false;
     }
 
     Label onThrow;
 
-    {
-        Label onDetached;
-
-        for (size_t i = 0; i < mg.numDeclaredImports(); i++) {
-            ProfilingOffsets interp;
-            if (!GenerateInterpExitStub(mg, i, usesHeap, &onThrow, &onDetached, &interp))
-                return false;
+    for (size_t i = 0; i < mg.numDeclaredImports(); i++) {
+        ProfilingOffsets interp;
+        if (!GenerateInterpExitStub(mg, i, &onThrow, &interp))
+            return false;
 
-            ProfilingOffsets jit;
-            if (!GenerateJitExitStub(mg, i, usesHeap, &onThrow, &onDetached, &jit))
-                return false;
+        ProfilingOffsets jit;
+        if (!GenerateJitExitStub(mg, i, usesHeap, &onThrow, &jit))
+            return false;
 
-            if (!mg.defineImport(i, interp, jit))
-                return false;
-        }
-
-        if (onDetached.used()) {
-            if (!GenerateOnDetachedStub(mg, &onDetached, &onThrow))
-                return false;
-        }
+        if (!mg.defineImport(i, interp, jit))
+            return false;
     }
 
     if (mg.masm().asmStackOverflowLabel()->used()) {
         if (!GenerateStackOverflowStub(mg, &onThrow))
             return false;
     }
 
     if (mg.masm().asmSyncInterruptLabel()->used()) {
@@ -1173,17 +1110,17 @@ wasm::GenerateStubs(ModuleGenerator& mg,
     }
 
     // Generate unconditionally: the out-of-bounds exit may be used later even
     // if signal handling isn't used for out-of-bounds at the moment.
     if (!GenerateOutOfBoundsStub(mg, &onThrow))
         return false;
 
     // Generate unconditionally: the async interrupt may be taken at any time.
-    if (!GenerateAsyncInterruptStub(mg, usesHeap, &onThrow))
+    if (!GenerateAsyncInterruptStub(mg, &onThrow))
         return false;
 
     if (onThrow.used()) {
         if (!GenerateThrowStub(mg, &onThrow))
             return false;
     }
 
     return true;
--- a/js/src/asmjs/WasmStubs.h
+++ b/js/src/asmjs/WasmStubs.h
@@ -20,14 +20,14 @@
 #define wasm_stubs_h
 
 #include "asmjs/WasmGenerator.h"
 
 namespace js {
 namespace wasm {
 
 bool
-GenerateStubs(ModuleGenerator& mg, Module::HeapBool usesHeap);
+GenerateStubs(ModuleGenerator& mg, bool usesHeap);
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_stubs_h
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -38,44 +38,26 @@ extern MOZ_EXPORT int64_t
 __aeabi_idivmod(int, int);
 
 extern MOZ_EXPORT int64_t
 __aeabi_uidivmod(int, int);
 
 }
 #endif
 
-namespace js {
-namespace wasm {
-
-void
-ReportOverRecursed()
+static void
+WasmReportOverRecursed()
 {
-    JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
-    ReportOverRecursed(cx);
+    ReportOverRecursed(JSRuntime::innermostWasmActivation()->cx());
 }
 
-bool
-HandleExecutionInterrupt()
+static bool
+WasmHandleExecutionInterrupt()
 {
-    WasmActivation* act = JSRuntime::innermostWasmActivation();
-    act->module().setInterrupted(true);
-    bool ret = CheckForInterrupt(act->cx());
-    act->module().setInterrupted(false);
-    return ret;
-}
-
-} // namespace wasm
-} // namespace js
-
-static void
-OnDetached()
-{
-    JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
-    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
+    return CheckForInterrupt(JSRuntime::innermostWasmActivation()->cx());
 }
 
 static void
 OnOutOfBounds()
 {
     JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
 }
@@ -182,25 +164,23 @@ wasm::AddressOf(SymbolicAddress imm, Exc
     switch (imm) {
       case SymbolicAddress::Runtime:
         return cx->runtimeAddressForJit();
       case SymbolicAddress::RuntimeInterruptUint32:
         return cx->runtimeAddressOfInterruptUint32();
       case SymbolicAddress::StackLimit:
         return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
       case SymbolicAddress::ReportOverRecursed:
-        return FuncCast(wasm::ReportOverRecursed, Args_General0);
-      case SymbolicAddress::OnDetached:
-        return FuncCast(OnDetached, Args_General0);
+        return FuncCast(WasmReportOverRecursed, Args_General0);
       case SymbolicAddress::OnOutOfBounds:
         return FuncCast(OnOutOfBounds, Args_General0);
       case SymbolicAddress::OnImpreciseConversion:
         return FuncCast(OnImpreciseConversion, Args_General0);
       case SymbolicAddress::HandleExecutionInterrupt:
-        return FuncCast(wasm::HandleExecutionInterrupt, Args_General0);
+        return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
       case SymbolicAddress::InvokeImport_Void:
         return FuncCast(InvokeImport_Void, Args_General3);
       case SymbolicAddress::InvokeImport_I32:
         return FuncCast(InvokeImport_I32, Args_General3);
       case SymbolicAddress::InvokeImport_F64:
         return FuncCast(InvokeImport_F64, Args_General3);
       case SymbolicAddress::CoerceInPlace_ToInt32:
         return FuncCast(CoerceInPlace_ToInt32, Args_General1);
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -552,17 +552,16 @@ enum class SymbolicAddress
     ExpD,
     LogD,
     PowD,
     ATan2D,
     Runtime,
     RuntimeInterruptUint32,
     StackLimit,
     ReportOverRecursed,
-    OnDetached,
     OnOutOfBounds,
     OnImpreciseConversion,
     HandleExecutionInterrupt,
     InvokeImport_Void,
     InvokeImport_I32,
     InvokeImport_F64,
     CoerceInPlace_ToInt32,
     CoerceInPlace_ToNumber,
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -519,17 +519,17 @@ js::atomics_isLockFree(JSContext* cx, un
 // simulator build with ARMHWCAP=vfp set.  Do not set any other flags; other
 // vfp/neon flags force ARMv7 to be set.
 
 static void
 GetCurrentAsmJSHeap(SharedMem<void*>* heap, size_t* length)
 {
     JSRuntime* rt = js::TlsPerThreadData.get()->runtimeFromMainThread();
     wasm::Module& module = rt->wasmActivationStack()->module();
-    *heap = module.maybeHeap().cast<void*>();
+    *heap = module.heap().cast<void*>();
     *length = module.heapLength();
 }
 
 int32_t
 js::atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value)
 {
     SharedMem<void*> heap;
     size_t heapLength;
--- a/js/src/builtin/String.js
+++ b/js/src/builtin/String.js
@@ -63,17 +63,17 @@ function String_substr(start, length) {
     // doubles. (The result is the same either way.)
     var end = (length === undefined) ? size : ToInteger(length);
 
     // Step 8.
     if (intStart < 0)
         intStart = std_Math_max(intStart + size, 0);
 
     // Step 9.
-    var resultLength = std_Math_min(std_Math_max(end, 0), size - intStart)
+    var resultLength = std_Math_min(std_Math_max(end, 0), size - intStart);
 
     // Step 10.
     if (resultLength <= 0)
         return "";
 
     // Step 11.
     // While |intStart| and |resultLength| are bounded to the length of |str|
     // and thus definitely in the int32 range, they can still be typed as
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2748,29 +2748,29 @@ DumpStringRepresentation(JSContext* cx, 
 #endif
 
 static bool
 SetLazyParsingDisabled(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool disable = !args.hasDefined(0) || ToBoolean(args[0]);
-    JS::CompartmentOptionsRef(cx->compartment()).setDisableLazyParsing(disable);
+    cx->compartment()->behaviors().setDisableLazyParsing(disable);
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 SetDiscardSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool discard = !args.hasDefined(0) || ToBoolean(args[0]);
-    JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(discard);
+    cx->compartment()->behaviors().setDiscardSource(discard);
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 GetConstructorName(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -73,17 +73,18 @@ var ignoreCallees = {
     "JSRuntime.destroyPrincipals" : true,
     "icu_50::UObject.__deleting_dtor" : true, // destructors in ICU code can't cause GC
     "mozilla::CycleCollectedJSRuntime.DescribeCustomObjects" : true, // During tracing, cannot GC.
     "mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC.
     "PLDHashTableOps.hashKey" : true,
     "z_stream_s.zfree" : true,
     "GrGLInterface.fCallback" : true,
     "std::strstreambuf._M_alloc_fun" : true,
-    "std::strstreambuf._M_free_fun" : true
+    "std::strstreambuf._M_free_fun" : true,
+    "struct js::gc::Callback<void (*)(JSRuntime*, void*)>.op" : true,
 };
 
 function fieldCallCannotGC(csu, fullfield)
 {
     if (csu in ignoreClasses)
         return true;
     if (fullfield in ignoreCallees)
         return true;
@@ -182,16 +183,18 @@ var ignoreFunctions = {
     // particular runnable it posts can't even GC, but the analysis isn't
     // currently smart enough to determine that. In either case, this is (a)
     // only in GTests, and (b) only when the Gtest has already failed. We have
     // static and dynamic checks for no GC in the non-test code, and in the test
     // code we fall back to only the dynamic checks.
     "void test::RingbufferDumper::OnTestPartResult(testing::TestPartResult*)" : true,
 
     "float64 JS_GetCurrentEmbedderTime()" : true,
+
+    "uint64 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
 };
 
 function isProtobuf(name)
 {
     return name.match(/\bgoogle::protobuf\b/) ||
            name.match(/\bmozilla::devtools::protobuf\b/);
 }
 
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -194,17 +194,17 @@ BytecodeCompiler::createScriptSource()
 bool
 BytecodeCompiler::maybeCompressSource()
 {
     if (!sourceCompressor) {
         maybeSourceCompressor.emplace(cx);
         sourceCompressor = maybeSourceCompressor.ptr();
     }
 
-    if (!cx->compartment()->options().discardSource()) {
+    if (!cx->compartment()->behaviors().discardSource()) {
         if (options.sourceIsLazy) {
             scriptSource->setSourceRetrievable();
         } else if (!scriptSource->setSourceCopy(cx, sourceBuffer, sourceArgumentsNotIncluded,
                                                 sourceCompressor))
         {
             return false;
         }
     }
@@ -212,18 +212,18 @@ BytecodeCompiler::maybeCompressSource()
     return true;
 }
 
 bool
 BytecodeCompiler::canLazilyParse()
 {
     return options.canLazilyParse &&
            !HasNonSyntacticStaticScopeChain(enclosingStaticScope) &&
-           !cx->compartment()->options().disableLazyParsing() &&
-           !cx->compartment()->options().discardSource() &&
+           !cx->compartment()->behaviors().disableLazyParsing() &&
+           !cx->compartment()->behaviors().discardSource() &&
            !options.sourceIsLazy &&
            !cx->lcovEnabled();
 }
 
 bool
 BytecodeCompiler::createParser()
 {
     if (canLazilyParse()) {
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -751,16 +751,19 @@ class GCRuntime
     int32_t getMallocBytes() const { return mallocBytesUntilGC; }
     void resetMallocBytes();
     bool isTooMuchMalloc() const { return mallocBytesUntilGC <= 0; }
     void updateMallocCounter(JS::Zone* zone, size_t nbytes);
     void onTooMuchMalloc();
 
     void setGCCallback(JSGCCallback callback, void* data);
     void callGCCallback(JSGCStatus status) const;
+    void setObjectsTenuredCallback(JSObjectsTenuredCallback callback,
+                                   void* data);
+    void callObjectsTenuredCallback();
     bool addFinalizeCallback(JSFinalizeCallback callback, void* data);
     void removeFinalizeCallback(JSFinalizeCallback func);
     bool addWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback, void* data);
     void removeWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback);
     bool addWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback, void* data);
     void removeWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback);
     JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
 
@@ -1268,16 +1271,17 @@ class GCRuntime
 
     js::Vector<JSObject*, 0, js::SystemAllocPolicy> selectedForMarking;
 #endif
 
     bool validate;
     bool fullCompartmentChecks;
 
     Callback<JSGCCallback> gcCallback;
+    Callback<JSObjectsTenuredCallback> tenuredCallback;
     CallbackVector<JSFinalizeCallback> finalizeCallbacks;
     CallbackVector<JSWeakPointerZoneGroupCallback> updateWeakPointerZoneGroupCallbacks;
     CallbackVector<JSWeakPointerCompartmentCallback> updateWeakPointerCompartmentCallbacks;
 
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs
      * from maxMallocBytes down to zero.
      */
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2210,16 +2210,18 @@ js::TenuringTracer::moveObjectToTenured(
 
     if (src->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE) {
         if (src->is<InlineTypedObject>()) {
             InlineTypedObject::objectMovedDuringMinorGC(this, dst, src);
         } else if (src->is<UnboxedArrayObject>()) {
             tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
         } else if (src->is<ArgumentsObject>()) {
             tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
+        } else if (JSObjectMovedOp op = dst->getClass()->ext.objectMovedOp) {
+            op(dst, src);
         } else {
             // Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above
             // to ensure any additional nursery buffers they hold are moved.
             MOZ_CRASH("Unhandled JSCLASS_SKIP_NURSERY_FINALIZE Class");
         }
     }
 
     return tenuredSize;
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -491,16 +491,20 @@ js::Nursery::collect(JSRuntime* rt, JS::
     TIME_END(sweepArrayBufferViewList);
 
     // Update any slot or element pointers whose destination has been tenured.
     TIME_START(updateJitActivations);
     js::jit::UpdateJitActivationsForMinorGC(rt, &mover);
     forwardedBuffers.finish();
     TIME_END(updateJitActivations);
 
+    TIME_START(objectsTenuredCallback);
+    rt->gc.callObjectsTenuredCallback();
+    TIME_END(objectsTenuredCallback);
+
     // Sweep.
     TIME_START(freeMallocedBuffers);
     freeMallocedBuffers();
     TIME_END(freeMallocedBuffers);
 
     TIME_START(sweep);
     sweep();
     TIME_END(sweep);
@@ -571,16 +575,17 @@ js::Nursery::collect(JSRuntime* rt, JS::
             {"mkSlts", TIME_TOTAL(traceSlots)},
             {"mcWCll", TIME_TOTAL(traceWholeCells)},
             {"mkGnrc", TIME_TOTAL(traceGenericEntries)},
             {"ckTbls", TIME_TOTAL(checkHashTables)},
             {"mkRntm", TIME_TOTAL(markRuntime)},
             {"mkDbgr", TIME_TOTAL(markDebugger)},
             {"clrNOC", TIME_TOTAL(clearNewObjectCache)},
             {"collct", TIME_TOTAL(collectToFP)},
+            {" tenCB", TIME_TOTAL(objectsTenuredCallback)},
             {"swpABO", TIME_TOTAL(sweepArrayBufferViewList)},
             {"updtIn", TIME_TOTAL(updateJitActivations)},
             {"frSlts", TIME_TOTAL(freeMallocedBuffers)},
             {" clrSB", TIME_TOTAL(clearStoreBuffer)},
             {" sweep", TIME_TOTAL(sweep)},
             {"resize", TIME_TOTAL(resize)},
             {"pretnr", TIME_TOTAL(pretenure)},
             {"logPtT", TIME_TOTAL(logPromotionsToTenured)}
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -55,31 +55,32 @@ void breakpoint() {
     // breakpoint on it, that breakpoint will hit at all sorts of random
     // times. So make it perform a distinctive side effect.
     fprintf(stderr, "Called " __FILE__ ":breakpoint\n");
 }
 
 GDBFragment* GDBFragment::allFragments = nullptr;
 
 int
-main (int argc, const char** argv)
+main(int argc, const char** argv)
 {
     if (!JS_Init()) return 1;
     JSRuntime* runtime = checkPtr(JS_NewRuntime(1024 * 1024));
     JS_SetGCParameter(runtime, JSGC_MAX_BYTES, 0xffffffff);
     JS_SetNativeStackQuota(runtime, 5000000);
 
     JSContext* cx = checkPtr(JS_NewContext(runtime, 8192));
     JS_SetErrorReporter(runtime, reportError);
 
     JSAutoRequest ar(cx);
 
     /* Create the global object. */
     JS::CompartmentOptions options;
-    options.setVersion(JSVERSION_LATEST);
+    options.behaviors().setVersion(JSVERSION_LATEST);
+
     RootedObject global(cx, checkPtr(JS_NewGlobalObject(cx, &global_class,
                         nullptr, JS::FireOnNewGlobalHook, options)));
     JSAutoCompartment ac(cx, global);
 
     /* Populate the global object with the standard globals,
        like Object and Array. */
     checkBool(JS_InitStandardClasses(cx, global));
 
deleted file mode 100644
--- a/js/src/jit-test/tests/asm.js/testBug1100237.js
+++ /dev/null
@@ -1,33 +0,0 @@
-load(libdir + "asm.js");
-
-var byteLength = Function.prototype.call.bind(
-    Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get
-);
-var m = asmCompile("glob", "s", "b", `
-    "use asm";
-    var I32 = glob.Int32Array;
-    var i32 = new I32(b);
-    var len = glob.byteLength;
-    function ch(b2) {
-        if (len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 80000000) {
-            return false;
-        }
-        i32 = new I32(b2);
-        b = b2;
-        return true
-    }
-    function get(i) {
-        i = i | 0;
-        return i32[i >> 2] | 0
-    }
-    return {
-        get: get,
-        changeHeap: ch
-    }
-`);
-var buf1 = new ArrayBuffer(16777216)
-var { get, changeHeap } = asmLink(m, this, null, buf1)
-assertEq(changeHeap(new ArrayBuffer(33554432)), true)
-assertEq(get(), 0)
-assertEq(changeHeap(buf1), true);
-get();
--- a/js/src/jit-test/tests/asm.js/testNeuter.js
+++ b/js/src/jit-test/tests/asm.js/testNeuter.js
@@ -18,97 +18,25 @@ var m = asmCompile('stdlib', 'foreign', 
                        return i32[i>>2]|0
                    }
                    return {get:get, set:set}`);
 
 var buffer = new ArrayBuffer(BUF_MIN);
 var {get, set} = asmLink(m, this, null, buffer);
 set(4, 42);
 assertEq(get(4), 42);
-neuter(buffer, "change-data");
-neuter(buffer, "same-data");
-assertThrowsInstanceOf(() => get(4), InternalError);
-
-var buf1 = new ArrayBuffer(BUF_MIN);
-var buf2 = new ArrayBuffer(BUF_MIN);
-var {get:get1, set:set1} = asmLink(m, this, null, buf1);
-var {get:get2, set:set2} = asmLink(m, this, null, buf2);
-set1(0, 13);
-set2(0, 42);
-neuter(buf1, "change-data");
-assertThrowsInstanceOf(() => get1(0), InternalError);
-assertEq(get2(0), 42);
+assertThrowsInstanceOf(() => neuter(buffer, "change-data"), InternalError);
+assertThrowsInstanceOf(() => neuter(buffer, "same-data"), InternalError);
 
 var m = asmCompile('stdlib', 'foreign', 'buffer',
                   `"use asm";
                    var i32 = new stdlib.Int32Array(buffer);
                    var ffi = foreign.ffi;
                    function inner(i) {
                        i=i|0;
                        ffi();
                        return i32[i>>2]|0
                    }
                    return inner`);
 
 var buffer = new ArrayBuffer(BUF_MIN);
-function ffi1() { neuter(buffer, "change-data"); }
+function ffi1() { assertThrowsInstanceOf(() => neuter(buffer, "change-data"), InternalError) }
 var inner = asmLink(m, this, {ffi:ffi1}, buffer);
-assertThrowsInstanceOf(() => inner(8), InternalError);
-
-var byteLength = Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
-var m = asmCompile('stdlib', 'foreign', 'buffer',
-                  `"use asm";
-                   var ffi = foreign.ffi;
-                   var I32 = stdlib.Int32Array;
-                   var i32 = new I32(buffer);
-                   var len = stdlib.byteLength;
-                   function changeHeap(newBuffer) {
-                       if (len(newBuffer) & 0xffffff || len(newBuffer) <= 0xffffff || len(newBuffer) > 0x80000000)
-                           return false;
-                       i32 = new I32(newBuffer);
-                       buffer = newBuffer;
-                       return true;
-                   }
-                   function get(i) {
-                       i=i|0;
-                       return i32[i>>2]|0;
-                   }
-                   function inner(i) {
-                       i=i|0;
-                       ffi();
-                       return get(i)|0;
-                   }
-                   return {changeHeap:changeHeap, get:get, inner:inner}`);
-
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
-var buf3 = new ArrayBuffer(BUF_CHANGE_MIN);
-var buf4 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf2)[13] = 42;
-new Int32Array(buf3)[13] = 1024;
-new Int32Array(buf4)[13] = 1337;
-
-function ffi2() { neuter(buf1, "change-data"); assertEq(changeHeap(buf2), true); }
-var {changeHeap, get:get2, inner} = asmLink(m, this, {ffi:ffi2}, buf1);
-assertEq(inner(13*4), 42);
-
-function ffi3() {
-    assertEq(get2(13*4), 42);
-    assertEq(get2(BUF_CHANGE_MIN), 0)
-    assertEq(get3(13*4), 42);
-    assertEq(get3(BUF_CHANGE_MIN), 0)
-    neuter(buf2, "change-data");
-    assertThrowsInstanceOf(()=>get2(13*4), InternalError);
-    assertThrowsInstanceOf(()=>get2(BUF_CHANGE_MIN), InternalError);
-    assertThrowsInstanceOf(()=>get3(13*4), InternalError);
-    assertThrowsInstanceOf(()=>get3(BUF_CHANGE_MIN), InternalError);
-    assertEq(changeHeap(buf3), true);
-    assertThrowsInstanceOf(()=>get2(13*4), InternalError);
-    assertThrowsInstanceOf(()=>get2(BUF_CHANGE_MIN), InternalError);
-    assertEq(get3(13*4), 1024);
-    assertEq(get3(BUF_CHANGE_MIN), 0);
-    assertEq(changeHeap(buf4), true);
-}
-var {changeHeap, get:get3, inner} = asmLink(m, this, {ffi:ffi3}, buf2);
-assertEq(inner(13*4), 1337);
-assertThrowsInstanceOf(()=>get2(0), InternalError);
-assertEq(get3(BUF_CHANGE_MIN), 0);
-assertEq(get3(13*4), 1337);
--- a/js/src/jit-test/tests/asm.js/testProfiling.js
+++ b/js/src/jit-test/tests/asm.js/testProfiling.js
@@ -197,26 +197,16 @@ assertStackContainsSeq(stacks, ">,f1,>,<
 for (var i = 0; i < 20; i++)
     assertEq(f1(), 32);
 enableSingleStepProfiling();
 assertEq(f1(), 32);
 var stacks = disableSingleStepProfiling();
 assertStackContainsSeq(stacks, ">,f1,>,<,f1,>,>,<,f1,>,f2,>,<,f1,>,<,f2,>,<,f1,>,f2,>,<,f1,>,>,<,f1,>,<,f1,>,f1,>,>");
 
 
-// Detachment exit
-var buf = new ArrayBuffer(BUF_CHANGE_MIN);
-var ffi = function() { neuter(buf, 'change-data') }
-var f = asmLink(asmCompile('g','ffis','buf', USE_ASM + 'var ffi = ffis.ffi; var i32 = new g.Int32Array(buf); function f() { ffi() } return f'), this, {ffi:ffi}, buf);
-enableSingleStepProfiling();
-assertThrowsInstanceOf(f, InternalError);
-var stacks = disableSingleStepProfiling();
-assertStackContainsSeq(stacks, ">,f,>,<,f,>,inline stub,f,>,<,f,>,inline stub,f,>");
-
-
 if (isSimdAvailable() && typeof SIMD !== 'undefined') {
     // SIMD out-of-bounds exit
     var buf = new ArrayBuffer(0x10000);
     var f = asmLink(asmCompile('g','ffi','buf', USE_ASM + 'var f4=g.SIMD.float32x4; var f4l=f4.load; var u8=new g.Uint8Array(buf); function f(i) { i=i|0; return f4l(u8, 0xFFFF + i | 0); } return f'), this, {}, buf);
     enableSingleStepProfiling();
     assertThrowsInstanceOf(() => f(4), RangeError);
     var stacks = disableSingleStepProfiling();
     // TODO check that expected is actually the correctly expected string, when
deleted file mode 100644
--- a/js/src/jit-test/tests/asm.js/testResize.js
+++ /dev/null
@@ -1,366 +0,0 @@
-// |jit-test| test-also-noasmjs
-load(libdir + "asm.js");
-load(libdir + "asserts.js");
-
-// Tests for importing typed array view constructors
-
-assertAsmTypeFail('glob', USE_ASM + "var I32=glob.Int32Arra; function f() {} return f");
-var m = asmCompile('glob', USE_ASM + "var I32=glob.Int32Array; function f() {} return f");
-assertAsmLinkFail(m, {});
-assertAsmLinkFail(m, {Int32Array:null});
-assertAsmLinkFail(m, {Int32Array:{}});
-assertAsmLinkFail(m, {Int32Array:Uint32Array});
-assertEq(asmLink(m, {Int32Array:Int32Array})(), undefined);
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + "var I32=glob.Int32Array; function f() {} return f");
-assertEq(asmLink(m, this)(), undefined);
-assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
-
-assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new I3(buf); function f() {} return f');
-assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var I32=0; var i32=new I32(buf); function f() {} return f');
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new I32(buf); function f() {} return f');
-assertAsmLinkFail(m, this, null, {});
-assertAsmLinkAlwaysFail(m, this, null, null);
-assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
-assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
-
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var I32=glob.Int32Array; var i32=new glob.Int32Array(buf); function f() {} return f');
-assertAsmLinkFail(m, this, null, {});
-assertAsmLinkAlwaysFail(m, this, null, null);
-assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
-assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
-
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var F32=glob.Float32Array; var i32=new glob.Int32Array(buf); function f() {} return f');
-assertAsmLinkFail(m, this, null, {});
-assertAsmLinkAlwaysFail(m, this, null, null);
-assertAsmLinkFail(m, this, null, new ArrayBuffer(100));
-assertEq(asmLink(m, this, null, BUF_64KB)(), undefined);
-
-// Tests for link-time validation of byteLength import
-
-assertAsmTypeFail('glob', 'ffis', 'buf', USE_ASM + 'var byteLength=glob.byteLength; function f() { return byteLength(1)|0 } return f');
-
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var byteLength=glob.byteLength; function f() { return 42 } return f');
-assertEq('byteLength' in this, false);
-assertAsmLinkFail(m, this);
-this['byteLength'] = null;
-assertAsmLinkFail(m, this);
-this['byteLength'] = {};
-assertAsmLinkFail(m, this);
-this['byteLength'] = function(){}
-assertAsmLinkFail(m, this);
-this['byteLength'] = (function(){}).bind(null);
-assertAsmLinkFail(m, this);
-this['byteLength'] = Function.prototype.call.bind();
-assertAsmLinkFail(m, this);
-this['byteLength'] = Function.prototype.call.bind({});
-assertAsmLinkFail(m, this);
-this['byteLength'] = Function.prototype.call.bind(function f() {});
-assertAsmLinkFail(m, this);
-this['byteLength'] = Function.prototype.call.bind(Math.sin);
-assertAsmLinkFail(m, this);
-this['byteLength'] =
-  Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
-assertEq(asmLink(m, this)(), 42);
-
-var m = asmCompile('glob', 'ffis', 'buf', USE_ASM + 'var b1=glob.byteLength, b2=glob.byteLength; function f() { return 43 } return f');
-assertEq(asmLink(m, this)(), 43);
-
-// Tests for validation of change-heap function
-
-const BYTELENGTH_IMPORT = "var len = glob.byteLength; ";
-const IMPORT0 = BYTELENGTH_IMPORT;
-const IMPORT1 = "var I8=glob.Int8Array; var i8=new I8(b); " + BYTELENGTH_IMPORT;
-const IMPORT2 = "var I8=glob.Int8Array; var i8=new I8(b); var I32=glob.Int32Array; var i32=new I32(b); var II32=glob.Int32Array; " + BYTELENGTH_IMPORT;
-
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function f() { return 42 } function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function b(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function f(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2=1) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2,xyz) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(...r) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2,...r) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch({b2}) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { ;if((len((b2))) & (0xffffff) || (len((b2)) <= (0xffffff)) || len(b2) > 0x80000000) {;;return false;;} ; i8=new I8(b2);; b=b2;; return true;; } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function ch2(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { 3; if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { b2=b2|0; if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 || 1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 || 1 || 1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(1 & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || 1 || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(i8(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(xyz) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff && len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) | 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) == 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xfffffe || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0x1ffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0x7fffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) < 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xfffffe || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1000000 || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || 1) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) < 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || 1 > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0.0) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0xffffff) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x1000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffffff || len(b2) > 0x1000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1000000 || len(b2) > 0x1000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1000000 || len(b2) > 0x1000001) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000001) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) ; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) {} i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) {return false} i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return true; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT0 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i7=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=1; b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new 1; b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I7(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new b(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8; b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(1); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2,1); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); xyz=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=1; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; 1; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return 1 } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return false } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; if (0) return true; 1 } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new I32(b2); b=b2; return true } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I32(b2); i32=new I8(b2); b=b2; return true } function f() { return 42 } return f');
-       asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new II32(b2); b=b2; return true } function f() { return 42 } return f');
-
-// Tests for no calls in heap index expressions
-
-const CHANGE_FUN = 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new I32(b2); b=b2; return true }';
-const SETUP = USE_ASM + IMPORT2 + 'var imul=glob.Math.imul; var ffi=ffis.ffi;' + CHANGE_FUN;
-
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { i32[0] } return f');
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { i32[0] = 0 } return f');
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] } return f');
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = 0 } return f');
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(imul(i,i)|0) >> 2] = 0 } return f');
-       asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = (imul(i,i)|0) } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(ffi()|0) >> 2] } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(g()|0) >> 2] } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(TBL[i&0]()|0) >> 2] } function g() { return 0 } var TBL=[g]; return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(g()|0) >> 2] = 0 } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = g()|0 } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i32[(g()|0)>>2] >> 2] } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i32[(g()|0)>>2] >> 2] = 0 } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = i32[(g()|0)>>2] } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[((i32[i>>2]|0) + (g()|0)) >> 2] } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[((i32[i>>2]|0) + (g()|0)) >> 2] = 0 } function g() { return 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = (i32[i>>2]|0) + (g()|0) } function g() { return 0 } return f');
-if (isSimdAvailable() && typeof SIMD !== 'undefined')
-    asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'var i4 = glob.SIMD.Int32x4; var ext = i4.extractLane; var add = i4.add;' + CHANGE_FUN + 'function f(i) { i=i|0; i32[ext(i4(i,1,2,i),0) >> 2]; i32[ext(add(i4(0,0,0,0),i4(1,1,1,1)),0) >> 2]; } return f');
-
-// Tests for constant heap accesses when change-heap is used
-
-const HEADER = USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= MIN || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } ';
-assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { i8[0x1000000] = 0 } return f');
-       asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { i8[0xffffff] = 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { i8[0x1000001] = 0 } return f');
-       asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { i8[0x1000000] = 0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { return i8[0x1000000]|0 } return f');
-       asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0xffffff') + 'function f() { return i8[0xffffff]|0 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { return i8[0x1000001]|0 } return f');
-       asmCompile('glob', 'ffis', 'b', HEADER.replace('MIN', '0x1000000') + 'function f() { return i8[0x1000000]|0 } return f');
-
-// Tests for validation of heap length
-
-var body = USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0x1ffffff || len(b2) > 0x4000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return ch';
-var m = asmCompile('glob', 'ffis', 'b', body);
-assertAsmLinkFail(m, this, null, new ArrayBuffer(BUF_CHANGE_MIN));
-assertAsmLinkFail(m, this, null, new ArrayBuffer(0x1000000));
-var changeHeap = asmLink(m, this, null, new ArrayBuffer(0x2000000));
-assertEq(changeHeap(new ArrayBuffer(0x1000000)), false);
-assertEq(changeHeap(new ArrayBuffer(0x2000000)), true);
-assertEq(changeHeap(new ArrayBuffer(0x2000001)), false);
-assertEq(changeHeap(new ArrayBuffer(0x4000000)), true);
-assertEq(changeHeap(new ArrayBuffer(0x5000000)), false);
-assertThrowsInstanceOf(() => changeHeap(null), TypeError);
-assertThrowsInstanceOf(() => changeHeap({}), TypeError);
-assertThrowsInstanceOf(() => changeHeap(new Int32Array(100)), TypeError);
-
-var detached = new ArrayBuffer(BUF_CHANGE_MIN);
-neuter(detached, "change-data");
-assertEq(changeHeap(detached), false);
-
-// Tests for runtime changing heap
-
-const CHANGE_HEAP = 'var changeHeap = glob.byteLength;';
-
-var changeHeapSource = `function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }`;
-var body = `var I32=glob.Int32Array; var i32=new I32(b);
-            var len=glob.byteLength;` +
-            changeHeapSource +
-           `function get(i) { i=i|0; return i32[i>>2]|0 }
-            function set(i, v) { i=i|0; v=v|0; i32[i>>2] = v }
-            return {get:get, set:set, changeHeap:ch}`;
-
-var m = asmCompile('glob', 'ffis', 'b', USE_ASM + body);
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-var {get, set, changeHeap} = asmLink(m, this, null, buf1);
-
-assertEq(m.toString(), "function anonymous(glob, ffis, b) {\n" + USE_ASM + body + "\n}");
-assertEq(m.toSource(), "(function anonymous(glob, ffis, b) {\n" + USE_ASM + body + "\n})");
-assertEq(changeHeap.toString(), changeHeapSource);
-assertEq(changeHeap.toSource(), changeHeapSource);
-
-set(0, 42);
-set(4, 13);
-set(4, 13);
-assertEq(get(0), 42);
-assertEq(get(4), 13);
-set(BUF_CHANGE_MIN, 262);
-assertEq(get(BUF_CHANGE_MIN), 0);
-var buf2 = new ArrayBuffer(2*BUF_CHANGE_MIN);
-assertEq(changeHeap(buf2), true);
-assertEq(get(0), 0);
-assertEq(get(4), 0);
-set(BUF_CHANGE_MIN, 262);
-assertEq(get(BUF_CHANGE_MIN), 262);
-set(2*BUF_CHANGE_MIN, 262);
-assertEq(get(2*BUF_CHANGE_MIN), 0);
-changeHeap(buf1);
-assertEq(get(0), 42);
-assertEq(get(4), 13);
-set(BUF_CHANGE_MIN, 262);
-assertEq(get(BUF_CHANGE_MIN), 0);
-
-if (ArrayBuffer.transfer) {
-    var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-    var {get, set, changeHeap} = asmLink(m, this, null, buf1);
-    set(0, 100);
-    set(BUF_CHANGE_MIN - 4, 101);
-    set(BUF_CHANGE_MIN, 102);
-    var buf2 = ArrayBuffer.transfer(buf1);
-    assertEq(changeHeap(buf2), true);
-    assertEq(buf1.byteLength, 0);
-    assertEq(buf2.byteLength, BUF_CHANGE_MIN);
-    assertEq(get(0), 100);
-    assertEq(get(BUF_CHANGE_MIN-4), 101);
-    assertEq(get(BUF_CHANGE_MIN), 0);
-    assertEq(get(2*BUF_CHANGE_MIN-4), 0);
-    var buf3 = ArrayBuffer.transfer(buf2, 3*BUF_CHANGE_MIN);
-    assertEq(changeHeap(buf3), true);
-    assertEq(buf2.byteLength, 0);
-    assertEq(buf3.byteLength, 3*BUF_CHANGE_MIN);
-    assertEq(get(0), 100);
-    assertEq(get(BUF_CHANGE_MIN-4), 101);
-    assertEq(get(BUF_CHANGE_MIN), 0);
-    assertEq(get(2*BUF_CHANGE_MIN), 0);
-    set(BUF_CHANGE_MIN, 102);
-    set(2*BUF_CHANGE_MIN, 103);
-    assertEq(get(BUF_CHANGE_MIN), 102);
-    assertEq(get(2*BUF_CHANGE_MIN), 103);
-    var buf4 = ArrayBuffer.transfer(buf3, 2*BUF_CHANGE_MIN);
-    assertEq(changeHeap(buf4), true);
-    assertEq(buf3.byteLength, 0);
-    assertEq(buf4.byteLength, 2*BUF_CHANGE_MIN);
-    assertEq(get(0), 100);
-    assertEq(get(BUF_CHANGE_MIN-4), 101);
-    assertEq(get(BUF_CHANGE_MIN), 102);
-    assertEq(get(2*BUF_CHANGE_MIN), 0);
-    var buf5 = ArrayBuffer.transfer(buf4, 3*BUF_CHANGE_MIN);
-    assertEq(changeHeap(buf5), true);
-    assertEq(buf4.byteLength, 0);
-    assertEq(buf5.byteLength, 3*BUF_CHANGE_MIN);
-    assertEq(get(0), 100);
-    assertEq(get(BUF_CHANGE_MIN-4), 101);
-    assertEq(get(BUF_CHANGE_MIN), 102);
-    assertEq(get(2*BUF_CHANGE_MIN), 0);
-    var buf6 = ArrayBuffer.transfer(buf5, 0);
-    assertEq(buf5.byteLength, 0);
-    assertEq(buf6.byteLength, 0);
-    assertEq(changeHeap(buf6), false);
-}
-
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
-var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
-                   `var len=glob.byteLength;
-                    function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; b=b2; return true }
-                    return ch`);
-var changeHeap = asmLink(m, this, null, buf1);
-assertEq(changeHeap(buf2), true);
-neuter(buf2, "change-data");
-assertEq(changeHeap(buf1), true);
-neuter(buf1, "change-data");
-
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf1)[0] = 13;
-var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf2)[0] = 42;
-
-// Tests for changing heap during an FFI:
-
-// Set the warmup to '2' so we can hit both interp and ion FFI exits
-setJitCompilerOption("ion.warmup.trigger", 2);
-setJitCompilerOption("baseline.warmup.trigger", 0);
-setJitCompilerOption("offthread-compilation.enable", 0);
-
-var changeToBuf = null;
-var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
-                   `var ffi=ffis.ffi;
-                    var I32=glob.Int32Array; var i32=new I32(b);
-                    var len=glob.byteLength;
-                    function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }
-                    function test(i) { i=i|0; var sum=0; sum = i32[i>>2]|0; sum = (sum + (ffi()|0))|0; sum = (sum + (i32[i>>2]|0))|0; return sum|0 }
-                    return {test:test, changeHeap:ch}`);
-var ffi = function() { changeHeap(changeToBuf); return 1 }
-var {test, changeHeap} = asmLink(m, this, {ffi:ffi}, buf1);
-changeToBuf = buf1;
-assertEq(test(0), 27);
-changeToBuf = buf2;
-assertEq(test(0), 56);
-changeToBuf = buf2;
-assertEq(test(0), 85);
-changeToBuf = buf1;
-assertEq(test(0), 56);
-changeToBuf = buf1;
-assertEq(test(0), 27);
-
-var ffi = function() { return { valueOf:function() { changeHeap(changeToBuf); return 100 } } };
-var {test, changeHeap} = asmLink(m, this, {ffi:ffi}, buf1);
-changeToBuf = buf1;
-assertEq(test(0), 126);
-changeToBuf = buf2;
-assertEq(test(0), 155);
-changeToBuf = buf2;
-assertEq(test(0), 184);
-changeToBuf = buf1;
-assertEq(test(0), 155);
-changeToBuf = buf1;
-assertEq(test(0), 126);
-
-if (ArrayBuffer.transfer) {
-    var buf = new ArrayBuffer(BUF_CHANGE_MIN);
-    new Int32Array(buf)[0] = 3;
-    var ffi = function() {
-        var buf2 = ArrayBuffer.transfer(buf, 2*BUF_CHANGE_MIN);
-        new Int32Array(buf2)[BUF_CHANGE_MIN/4] = 13;
-        assertEq(changeHeap(buf2), true);
-        return 1
-    }
-    var {test, changeHeap} = asmLink(m, this, {ffi:ffi}, buf);
-    assertEq(test(BUF_CHANGE_MIN), 14);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/asm.js/testTimeout7-nosignals.js
+++ /dev/null
@@ -1,29 +0,0 @@
-// |jit-test| exitstatus: 6;
-load(libdir + "asm.js");
-
-// This test may iloop for valid reasons if not compiled with asm.js (namely,
-// inlining may allow the heap load to be hoisted out of the loop).
-if (!isAsmJSCompilationAvailable())
-    quit(6);
-
-setJitCompilerOption("signals.enable", 0);
-
-var byteLength =
-  Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
-
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf1)[0] = 13;
-var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf2)[0] = 42;
-
-// Test changeHeap from interrupt (as if that could ever happen...)
-var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
-                   `var I32=glob.Int32Array; var i32=new I32(b);
-                    var len=glob.byteLength;
-                    function changeHeap(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }
-                    function f() {}
-                    function loop(i) { i=i|0; while((i32[i>>2]|0) == 13) { f() } }
-                    return {loop:loop, changeHeap:changeHeap}`);
-var { loop, changeHeap } = asmLink(m, this, null, buf1);
-timeout(1, function() { assertEq(changeHeap(buf2), false); return false });
-loop(0);
deleted file mode 100644
--- a/js/src/jit-test/tests/asm.js/testTimeout7.js
+++ /dev/null
@@ -1,27 +0,0 @@
-// |jit-test| exitstatus: 6;
-load(libdir + "asm.js");
-
-// This test may iloop for valid reasons if not compiled with asm.js (namely,
-// inlining may allow the heap load to be hoisted out of the loop).
-if (!isAsmJSCompilationAvailable())
-    quit(6);
-
-var byteLength =
-  Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get);
-
-var buf1 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf1)[0] = 13;
-var buf2 = new ArrayBuffer(BUF_CHANGE_MIN);
-new Int32Array(buf2)[0] = 42;
-
-// Test changeHeap from interrupt (as if that could ever happen...)
-var m = asmCompile('glob', 'ffis', 'b', USE_ASM +
-                   `var I32=glob.Int32Array; var i32=new I32(b);
-                    var len=glob.byteLength;
-                    function changeHeap(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true }
-                    function f() {}
-                    function loop(i) { i=i|0; while((i32[i>>2]|0) == 13) { f() } }
-                    return {loop:loop, changeHeap:changeHeap}`);
-var { loop, changeHeap } = asmLink(m, this, null, buf1);
-timeout(1, function() { assertEq(changeHeap(buf2), false); return false });
-loop(0);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testArrayBufferTransfer.js
+++ /dev/null
@@ -1,123 +0,0 @@
-load(libdir + "asserts.js");
-load(libdir + "asm.js");
-
-// Currently, ArrayBuffer.transfer is #ifdef NIGHTLY_BUILD. When
-// ArrayBuffer.transfer is enabled on release, this test should be removed.
-if (!ArrayBuffer.transfer)
-    quit();
-
-var XF = ArrayBuffer.transfer;
-
-assertEq(typeof XF, "function");
-assertEq(XF.length, 2);
-
-// arg 1 errors
-assertThrowsInstanceOf(()=>XF(), Error);
-assertThrowsInstanceOf(()=>XF(undefined), Error);
-assertThrowsInstanceOf(()=>XF(null), Error);
-assertThrowsInstanceOf(()=>XF({}), Error);
-assertThrowsInstanceOf(()=>XF(new Int32Array(1)), Error);
-var buf = new ArrayBuffer(1);
-neuter(buf, 'change-data');
-assertThrowsInstanceOf(()=>XF(buf), TypeError);
-
-// arg 2 errors
-var buf = new ArrayBuffer(1);
-assertThrowsInstanceOf(()=>XF(buf, -1), Error);
-assertThrowsInstanceOf(()=>XF(buf, {valueOf() { return -1 }}), Error);
-assertThrowsInstanceOf(()=>XF(buf, {toString() { return "-1" }}), Error);
-assertThrowsValue(()=>XF(buf, {valueOf() { throw "wee" }}), "wee");
-
-// arg 2 is coerced via ToInt32
-var buf = new ArrayBuffer(1);
-assertThrowsInstanceOf(()=>XF(buf, Math.pow(2,31)), Error);
-buf = XF(buf, Math.pow(2,32));
-assertEq(buf.byteLength, 0);
-buf = XF(buf, Math.pow(2,32) + 10);
-assertEq(buf.byteLength, 10);
-
-assertThrowsInstanceOf(()=>XF(buf, {valueOf() { neuter(buf, "change-data"); return 10; }}), TypeError);
-var buf = new ArrayBuffer(100);
-assertThrowsInstanceOf(()=>XF(buf, {valueOf() { ArrayBuffer.transfer(buf, 0); return 100; }}), TypeError);
-
-// on undefined second argument, stay the same size:
-var buf1 = new ArrayBuffer(0);
-var buf2 = XF(buf1);
-assertEq(buf1.byteLength, 0);
-assertEq(buf2.byteLength, 0);
-assertThrowsInstanceOf(()=>XF(buf1), TypeError);
-
-var buf1 = new ArrayBuffer(3);
-var buf2 = XF(buf1);
-assertEq(buf1.byteLength, 0);
-assertEq(buf2.byteLength, 3);
-assertThrowsInstanceOf(()=>XF(buf1), TypeError);
-
-var buf1 = new ArrayBuffer(9);
-var buf2 = XF(buf1, undefined);
-assertEq(buf1.byteLength, 0);
-assertEq(buf2.byteLength, 9);
-assertThrowsInstanceOf(()=>XF(buf1), TypeError);
-
-// cross-compartment wrapper
-var buf3 = newGlobal().eval("new ArrayBuffer(10)");
-var buf4 = XF(buf3, 20);
-assertEq(buf4.byteLength, 20);
-assertThrowsInstanceOf(()=>XF(buf3), TypeError);
-
-// test going to from various sizes
-function test(N1, N2) {
-    var buf1 = new ArrayBuffer(N1);
-    var i32 = new Int32Array(buf1);
-    for (var i = 0; i < i32.length; i++)
-        i32[i] = i;
-
-    var buf2 = XF(buf1, N2);
-
-    assertEq(buf1.byteLength, 0);
-    assertEq(i32.length, 0);
-    assertEq(buf2.byteLength, N2);
-    var i32 = new Int32Array(buf2);
-    for (var i = 0; i < Math.min(N1, N2)/4; i++)
-        assertEq(i32[i], i);
-    for (var i = Math.min(N1, N2)/4; i < i32.length; i++) {
-        assertEq(i32[i], 0);
-        i32[i] = -i;
-    }
-}
-test(0, 0);
-test(0, 4);
-test(4, 0);
-test(4, 4);
-test(0, 1000);
-test(4, 1000);
-test(1000, 0);
-test(1000, 4);
-test(1000, 1000);
-
-// asm.js:
-function testAsmJS(N1, N2) {
-    var buf1 = new ArrayBuffer(N1);
-    asmLink(asmCompile('stdlib', 'ffis', 'buf', USE_ASM + "var i32=new stdlib.Int32Array(buf); function f() {} return f"), this, null, buf1);
-    var i32 = new Int32Array(buf1);
-    for (var i = 0; i < i32.length; i+=100)
-        i32[i] = i;
-
-    var buf2 = XF(buf1, N2);
-
-    assertEq(buf1.byteLength, 0);
-    assertEq(i32.length, 0);
-    assertEq(buf2.byteLength, N2);
-    var i32 = new Int32Array(buf2);
-    var i = 0;
-    for (; i < Math.min(N1, N2)/4; i+=100)
-        assertEq(i32[i], i);
-    for (; i < i32.length; i+=100) {
-        assertEq(i32[i], 0);
-        i32[i] = -i;
-    }
-}
-testAsmJS(BUF_MIN, 0);
-testAsmJS(BUF_MIN, BUF_MIN);
-testAsmJS(BUF_MIN, 2*BUF_MIN);
-testAsmJS(2*BUF_MIN, BUF_MIN);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testBug1235874.js
@@ -0,0 +1,1 @@
+evaluate('evalcx("1")', { fileName: null });
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1486,17 +1486,18 @@ BaselineCompiler::emit_JSOP_SYMBOL()
 
 typedef JSObject* (*DeepCloneObjectLiteralFn)(JSContext*, HandleObject, NewObjectKind);
 static const VMFunction DeepCloneObjectLiteralInfo =
     FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
 
 bool
 BaselineCompiler::emit_JSOP_OBJECT()
 {
-    if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
+    JSCompartment* comp = cx->compartment();
+    if (comp->creationOptions().cloneSingletons()) {
         RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
         if (!obj)
             return false;
 
         prepareVMCall();
 
         pushArg(ImmWord(TenuredObject));
         pushArg(ImmGCPtr(obj));
@@ -1505,17 +1506,17 @@ BaselineCompiler::emit_JSOP_OBJECT()
             return false;
 
         // Box and push return value.
         masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
         frame.push(R0);
         return true;
     }
 
-    JS::CompartmentOptionsRef(cx).setSingletonsAsValues();
+    comp->behaviors().setSingletonsAsValues();
     frame.push(ObjectValue(*script->getObject(pc)));
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_CALLSITEOBJ()
 {
     RootedObject cso(cx, script->getObject(pc));
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7891,45 +7891,39 @@ CodeGenerator::generateAsmJS(wasm::FuncO
         // See comment below.
         Label* target = frameSize() > 0 ? &onOverflow : masm.asmStackOverflowLabel();
         masm.branchPtr(Assembler::AboveOrEqual,
                        wasm::SymbolicAddress::StackLimit,
                        masm.getStackPointer(),
                        target);
     }
 
-
     if (!generateBody())
         return false;
 
     masm.bind(&returnLabel_);
     wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets);
 
     if (onOverflow.used()) {
         // The stack overflow stub assumes that only sizeof(AsmJSFrame) bytes have
         // been pushed. The overflow check occurs after incrementing by
         // framePushed, so pop that before jumping to the overflow exit.
         masm.bind(&onOverflow);
         masm.addToStackPtr(Imm32(frameSize()));
         masm.jump(masm.asmStackOverflowLabel());
     }
 
-
 #if defined(JS_ION_PERF)
     // Note the end of the inline code and start of the OOL code.
     gen->perfSpewer().noteEndInlineCode(masm);
 #endif
 
     if (!generateOutOfLineCode())
         return false;
 
-    // Flush constant pools now so that pool hints encoded in the code stream
-    // get converted into actual instructions.
-    masm.flushBuffer();
-
     offsets->end = masm.currentOffset();
 
     MOZ_ASSERT(!masm.failureLabel()->used());
     MOZ_ASSERT(snapshots_.listSize() == 0);
     MOZ_ASSERT(snapshots_.RVATableSize() == 0);
     MOZ_ASSERT(recovers_.size() == 0);
     MOZ_ASSERT(bailouts_.empty());
     MOZ_ASSERT(graph.numConstants() == 0);
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -279,26 +279,25 @@ CompileCompartment::hasObjectMetadataCal
 // true. So even if there is a concurrent write, this concurrent write will
 // always have the same value.  If there is a concurrent read, then we will
 // clone a singleton instead of using the value which is baked in the JSScript,
 // and this would be an unfortunate allocation, but this will not change the
 // semantics of the JavaScript code which is executed.
 void
 CompileCompartment::setSingletonsAsValues()
 {
-    return JS::CompartmentOptionsRef(compartment()).setSingletonsAsValues();
+    compartment()->behaviors().setSingletonsAsValues();
 }
 
 JitCompileOptions::JitCompileOptions()
   : cloneSingletons_(false),
     spsSlowAssertionsEnabled_(false),
     offThreadCompilationAvailable_(false)
 {
 }
 
 JitCompileOptions::JitCompileOptions(JSContext* cx)
 {
-    JS::CompartmentOptions& options = cx->compartment()->options();
-    cloneSingletons_ = options.cloneSingletons();
+    cloneSingletons_ = cx->compartment()->creationOptions().cloneSingletons();
     spsSlowAssertionsEnabled_ = cx->runtime()->spsProfiler.enabled() &&
                                 cx->runtime()->spsProfiler.slowAssertionsEnabled();
     offThreadCompilationAvailable_ = OffThreadCompilationAvailable(cx);
 }
--- a/js/src/jit/ExecutableAllocator.cpp
+++ b/js/src/jit/ExecutableAllocator.cpp
@@ -335,37 +335,35 @@ ExecutableAllocator::addSizeOfCode(JS::C
                                                        - pool->m_otherCodeBytes;
         }
     }
 }
 
 void
 ExecutableAllocator::reprotectAll(ProtectionSetting protection)
 {
-    if (!nonWritableJitCode)
-        return;
-
+#ifdef NON_WRITABLE_JIT_CODE
     if (!m_pools.initialized())
         return;
 
     for (ExecPoolHashSet::Range r = m_pools.all(); !r.empty(); r.popFront())
         reprotectPool(rt_, r.front(), protection);
+#endif
 }
 
 /* static */ void
 ExecutableAllocator::reprotectPool(JSRuntime* rt, ExecutablePool* pool, ProtectionSetting protection)
 {
+#ifdef NON_WRITABLE_JIT_CODE
     // Don't race with reprotectAll called from the signal handler.
     MOZ_ASSERT(rt->jitRuntime()->preventBackedgePatching() || rt->handlingJitInterrupt());
 
-    if (!nonWritableJitCode)
-        return;
-
     char* start = pool->m_allocation.pages;
     reprotectRegion(start, pool->m_freePtr - start, protection);
+#endif
 }
 
 /* static */ void
 ExecutableAllocator::poisonCode(JSRuntime* rt, JitPoisonRangeVector& ranges)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
     // Don't race with reprotectAll called from the signal handler.
@@ -402,10 +400,8 @@ ExecutableAllocator::poisonCode(JSRuntim
         ExecutablePool* pool = ranges[i].pool;
         if (pool->isMarked()) {
             reprotectPool(rt, pool, Executable);
             pool->unmark();
         }
         pool->release();
     }
 }
-
-bool ExecutableAllocator::nonWritableJitCode = true;
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -152,16 +152,18 @@ struct JitPoisonRange
 
     JitPoisonRange(jit::ExecutablePool* pool, void* start, size_t size)
       : pool(pool), start(start), size(size)
     {}
 };
 
 typedef Vector<JitPoisonRange, 0, SystemAllocPolicy> JitPoisonRangeVector;
 
+#define NON_WRITABLE_JIT_CODE 1
+
 class ExecutableAllocator
 {
 #ifdef XP_WIN
     mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator;
 #endif
     JSRuntime* rt_;
 
   public:
@@ -178,18 +180,16 @@ class ExecutableAllocator
     void* alloc(size_t n, ExecutablePool** poolp, CodeKind type);
 
     void releasePoolPages(ExecutablePool* pool);
 
     void addSizeOfCode(JS::CodeSizes* sizes) const;
 
     static void initStatic();
 
-    static bool nonWritableJitCode;
-
   private:
     static size_t pageSize;
     static size_t largeAllocSize;
 
     static const size_t OVERSIZE_ALLOCATION = size_t(-1);
 
     static size_t roundUpAllocationSize(size_t request, size_t granularity);
 
@@ -201,24 +201,26 @@ class ExecutableAllocator
     ExecutablePool* createPool(size_t n);
     ExecutablePool* poolForSize(size_t n);
 
     static void reprotectPool(JSRuntime* rt, ExecutablePool* pool, ProtectionSetting protection);
 
   public:
     static void makeWritable(void* start, size_t size)
     {
-        if (nonWritableJitCode)
-            reprotectRegion(start, size, Writable);
+#ifdef NON_WRITABLE_JIT_CODE
+        reprotectRegion(start, size, Writable);
+#endif
     }
 
     static void makeExecutable(void* start, size_t size)
     {
-        if (nonWritableJitCode)
-            reprotectRegion(start, size, Executable);
+#ifdef NON_WRITABLE_JIT_CODE
+        reprotectRegion(start, size, Executable);
+#endif
     }
 
     void makeAllWritable() {
         reprotectAll(Writable);
     }
     void makeAllExecutable() {
         reprotectAll(Executable);
     }
--- a/js/src/jit/ExecutableAllocatorPosix.cpp
+++ b/js/src/jit/ExecutableAllocatorPosix.cpp
@@ -76,17 +76,17 @@ ExecutableAllocator::systemRelease(const
 }
 
 static const unsigned FLAGS_RW = PROT_READ | PROT_WRITE;
 static const unsigned FLAGS_RX = PROT_READ | PROT_EXEC;
 
 void
 ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting)
 {
-    MOZ_ASSERT(nonWritableJitCode);
+    MOZ_ASSERT(NON_WRITABLE_JIT_CODE);
     MOZ_ASSERT(pageSize);
 
     // Calculate the start of the page containing this region,
     // and account for this extra memory within size.
     intptr_t startPtr = reinterpret_cast<intptr_t>(start);
     intptr_t pageStartPtr = startPtr & ~(pageSize - 1);
     void* pageStart = reinterpret_cast<void*>(pageStartPtr);
     size += (startPtr - pageStartPtr);
@@ -96,13 +96,14 @@ ExecutableAllocator::reprotectRegion(voi
     size &= ~(pageSize - 1);
 
     mprotect(pageStart, size, (setting == Writable) ? FLAGS_RW : FLAGS_RX);
 }
 
 /* static */ unsigned
 ExecutableAllocator::initialProtectionFlags(ProtectionSetting protection)
 {
-    if (!nonWritableJitCode)
-        return FLAGS_RW | FLAGS_RX;
-
+#ifdef NON_WRITABLE_JIT_CODE
     return (protection == Writable) ? FLAGS_RW : FLAGS_RX;
+#else
+    return FLAGS_RW | FLAGS_RX;
+#endif
 }
--- a/js/src/jit/ExecutableAllocatorWin.cpp
+++ b/js/src/jit/ExecutableAllocatorWin.cpp
@@ -237,17 +237,17 @@ void
 ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
 {
     DeallocateExecutableMemory(alloc.pages, alloc.size, pageSize);
 }
 
 void
 ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting)
 {
-    MOZ_ASSERT(nonWritableJitCode);
+    MOZ_ASSERT(NON_WRITABLE_JIT_CODE);
     MOZ_ASSERT(pageSize);
 
     // Calculate the start of the page containing this region,
     // and account for this extra memory within size.
     intptr_t startPtr = reinterpret_cast<intptr_t>(start);
     intptr_t pageStartPtr = startPtr & ~(pageSize - 1);
     void* pageStart = reinterpret_cast<void*>(pageStartPtr);
     size += (startPtr - pageStartPtr);
@@ -260,13 +260,14 @@ ExecutableAllocator::reprotectRegion(voi
     int flags = (setting == Writable) ? PAGE_READWRITE : PAGE_EXECUTE_READ;
     if (!VirtualProtect(pageStart, size, flags, &oldProtect))
         MOZ_CRASH();
 }
 
 /* static */ unsigned
 ExecutableAllocator::initialProtectionFlags(ProtectionSetting protection)
 {
-    if (!nonWritableJitCode)
-        return PAGE_EXECUTE_READWRITE;
-
+#ifdef NON_WRITABLE_JIT_CODE
     return (protection == Writable) ? PAGE_READWRITE : PAGE_EXECUTE_READ;
+#else
+    return PAGE_EXECUTE_READWRITE;
+#endif
 }
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -521,19 +521,19 @@ void InvalidateAll(FreeOp* fop, JS::Zone
 void FinishInvalidation(FreeOp* fop, JSScript* script);
 
 // On windows systems, really large frames need to be incrementally touched.
 // The following constant defines the minimum increment of the touch.
 #ifdef XP_WIN
 const unsigned WINDOWS_BIG_FRAME_TOUCH_INCREMENT = 4096 - 1;
 #endif
 
-// If ExecutableAllocator::nonWritableJitCode is |true|, this class will ensure
-// JIT code is writable (has RW permissions) in its scope. If nonWritableJitCode
-// is |false|, it's a no-op.
+// If NON_WRITABLE_JIT_CODE is enabled, this class will ensure
+// JIT code is writable (has RW permissions) in its scope.
+// Otherwise it's a no-op.
 class MOZ_STACK_CLASS AutoWritableJitCode
 {
     // Backedge patching from the signal handler will change memory protection
     // flags, so don't allow it in a AutoWritableJitCode scope.
     JitRuntime::AutoPreventBackedgePatching preventPatching_;
     JSRuntime* rt_;
     void* addr_;
     size_t size_;
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -2002,17 +2002,17 @@ MacroAssembler::convertTypedOrValueToInt
         jump(fail);
         break;
       default:
         MOZ_CRASH("Bad MIRType");
     }
 }
 
 bool
-MacroAssembler::asmMergeWith(const MacroAssembler& other)
+MacroAssembler::asmMergeWith(MacroAssembler& other)
 {
     size_t sizeBeforeMerge = size();
 
     if (!MacroAssemblerSpecific::asmMergeWith(other))
         return false;
 
     retargetWithOffset(sizeBeforeMerge, other.asmSyncInterruptLabel(), asmSyncInterruptLabel());
     retargetWithOffset(sizeBeforeMerge, other.asmStackOverflowLabel(), asmStackOverflowLabel());
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1422,17 +1422,17 @@ class MacroAssembler : public MacroAssem
     }
     Label* asmOnConversionErrorLabel() {
         return &asmOnConversionErrorLabel_;
     }
     const Label* asmOnConversionErrorLabel() const {
         return &asmOnConversionErrorLabel_;
     }
 
-    bool asmMergeWith(const MacroAssembler& masm);
+    bool asmMergeWith(MacroAssembler& masm);
     void finish();
     void link(JitCode* code);
 
     void assumeUnreachable(const char* output);
 
     template<typename T>
     void assertTestInt32(Condition cond, const T& value, const char* output);
 
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -629,19 +629,20 @@ void
 Assembler::finish()
 {
     flush();
     MOZ_ASSERT(!isFinished);
     isFinished = true;
 }
 
 bool
-Assembler::asmMergeWith(const Assembler& other)
+Assembler::asmMergeWith(Assembler& other)
 {
     flush();
+    other.flush();
     if (!AssemblerShared::asmMergeWith(size(), other))
         return false;
     return m_buffer.appendBuffer(other.m_buffer);
 }
 
 void
 Assembler::executableCopy(uint8_t* buffer)
 {
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1391,17 +1391,17 @@ class Assembler : public AssemblerShared
     static const Register getStackPointer() {
         return StackPointer;
     }
 
   private:
     bool isFinished;
   public:
     void finish();
-    bool asmMergeWith(const Assembler& other);
+    bool asmMergeWith(Assembler& other);
     void executableCopy(void* buffer);
     void copyJumpRelocationTable(uint8_t* dest);
     void copyDataRelocationTable(uint8_t* dest);
     void copyPreBarrierTable(uint8_t* dest);
 
     // Size of the instruction stream, in bytes, after pools are flushed.
     size_t size() const;
     // Size of the jump relocation table, in bytes.
--- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -26,17 +26,16 @@ BEGIN_TEST(testRedefineGlobalEval)
         nullptr, nullptr, nullptr, nullptr,
         GlobalEnumerate, GlobalResolve, nullptr, nullptr,
         nullptr, nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
     /* Create the global object. */
     JS::CompartmentOptions options;
-    options.setVersion(JSVERSION_LATEST);
     JS::Rooted<JSObject*> g(cx, JS_NewGlobalObject(cx, &cls, nullptr, JS::FireOnNewGlobalHook, options));
     if (!g)
         return false;
 
     JSAutoCompartment ac(cx, g);
     JS::Rooted<JS::Value> v(cx);
     CHECK(JS_GetProperty(cx, g, "Object", &v));
 
--- a/js/src/jsapi-tests/testGCFinalizeCallback.cpp
+++ b/js/src/jsapi-tests/testGCFinalizeCallback.cpp
@@ -118,17 +118,16 @@ BEGIN_TEST(testGCFinalizeCallback)
     CHECK(JS_IsGlobalObject(global3));
 
     return true;
 }
 
 JSObject* createTestGlobal()
 {
     JS::CompartmentOptions options;
-    options.setVersion(JSVERSION_LATEST);
     return JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook, options);
 }
 
 virtual bool init() override
 {
     if (!JSAPITest::init())
         return false;
 
--- a/js/src/jsapi-tests/testPreserveJitCode.cpp
+++ b/js/src/jsapi-tests/testPreserveJitCode.cpp
@@ -85,13 +85,13 @@ testPreserveJitCode(bool preserveJitCode
 
     return true;
 }
 
 JSObject*
 createTestGlobal(bool preserveJitCode)
 {
     JS::CompartmentOptions options;
-    options.setVersion(JSVERSION_LATEST);
-    options.setPreserveJitCode(preserveJitCode);
+    options.creationOptions().setPreserveJitCode(preserveJitCode);
+    options.behaviors().setVersion(JSVERSION_LATEST);
     return JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook, options);
 }
 END_TEST(test_PreserveJitCode)
--- a/js/src/jsapi-tests/testSourcePolicy.cpp
+++ b/js/src/jsapi-tests/testSourcePolicy.cpp
@@ -4,31 +4,39 @@
 
 #include "jsscript.h"
 
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testBug795104)
 {
     JS::CompileOptions opts(cx);
-    JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(true);
+    JS::CompartmentBehaviorsRef(cx->compartment()).setDiscardSource(true);
+
     const size_t strLen = 60002;
     char* s = static_cast<char*>(JS_malloc(cx, strLen));
     CHECK(s);
+
     s[0] = '"';
     memset(s + 1, 'x', strLen - 2);
     s[strLen - 1] = '"';
+
     // We don't want an rval for our Evaluate call
     opts.setNoScriptRval(true);
+
     JS::RootedValue unused(cx);
     CHECK(JS::Evaluate(cx, opts, s, strLen, &unused));
+
     JS::RootedFunction fun(cx);
     JS::AutoObjectVector emptyScopeChain(cx);
+
     // But when compiling a function we don't want to use no-rval
     // mode, since it's not supported for functions.
     opts.setNoScriptRval(false);
+
     CHECK(JS::CompileFunction(cx, emptyScopeChain, opts, "f", 0, nullptr, s, strLen, &fun));
     CHECK(fun);
+
     JS_free(cx, s);
 
     return true;
 }
 END_TEST(testBug795104)
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -225,22 +225,24 @@ JSObject* newDelegate()
             nullptr,
             DelegateObjectMoved
         },
         JS_NULL_OBJECT_OPS
     };
 
     /* Create the global object. */
     JS::CompartmentOptions options;
-    options.setVersion(JSVERSION_LATEST);
-    JS::RootedObject global(cx);
-    global = JS_NewGlobalObject(cx, Jsvalify(&delegateClass), nullptr, JS::FireOnNewGlobalHook,
-                                options);
+    options.behaviors().setVersion(JSVERSION_LATEST);
+
+    JS::RootedObject global(cx, JS_NewGlobalObject(cx, Jsvalify(&delegateClass), nullptr,
+                                                   JS::FireOnNewGlobalHook, options));
+    if (!global)
+        return nullptr;
+
     JS_SetReservedSlot(global, 0, JS::Int32Value(42));
-
     return global;
 }
 
 bool
 checkSize(JS::HandleObject map, uint32_t expected)
 {
     JS::RootedObject keys(cx);
     CHECK(JS_NondeterministicGetWeakMapKeys(cx, map, &keys));
--- a/js/src/jsapi-tests/tests.cpp
+++ b/js/src/jsapi-tests/tests.cpp
@@ -69,31 +69,31 @@ bool JSAPITest::evaluate(const char* byt
         fail(JSAPITestString(bytes), filename, lineno);
 }
 
 bool JSAPITest::definePrint()
 {
     return JS_DefineFunction(cx, global, "print", (JSNative) print, 0, 0);
 }
 
-JSObject * JSAPITest::createGlobal(JSPrincipals* principals)
+JSObject* JSAPITest::createGlobal(JSPrincipals* principals)
 {
     /* Create the global object. */
     JS::RootedObject newGlobal(cx);
     JS::CompartmentOptions options;
-    options.setVersion(JSVERSION_LATEST);
+    options.behaviors().setVersion(JSVERSION_LATEST);
     newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, JS::FireOnNewGlobalHook,
                                    options);
     if (!newGlobal)
         return nullptr;
 
     JSAutoCompartment ac(cx, newGlobal);
 
-    /* Populate the global object with the standard globals, like Object and
-       Array. */
+    // Populate the global object with the standard globals like Object and
+    // Array.
     if (!JS_InitStandardClasses(cx, newGlobal))
         return nullptr;
 
     global = newGlobal;
     return newGlobal;
 }
 
 int main(int argc, char* argv[])
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -632,17 +632,17 @@ JS_PUBLIC_API(JSVersion)
 JS_GetVersion(JSContext* cx)
 {
     return VersionNumber(cx->findVersion());
 }
 
 JS_PUBLIC_API(void)
 JS_SetVersionForCompartment(JSCompartment* compartment, JSVersion version)
 {
-    compartment->options().setVersion(version);
+    compartment->behaviors().setVersion(version);
 }
 
 static const struct v2smap {
     JSVersion   version;
     const char* string;
 } v2smap[] = {
     {JSVERSION_ECMA_3,  "ECMAv3"},
     {JSVERSION_1_6,     "1.6"},
@@ -822,17 +822,17 @@ JS_PUBLIC_API(JSString*)
 JS::StringOfAddonId(JSAddonId* id)
 {
     return id;
 }
 
 JS_PUBLIC_API(JSAddonId*)
 JS::AddonIdOfObject(JSObject* obj)
 {
-    return obj->compartment()->addonId;
+    return obj->compartment()->creationOptions().addonIdOrNull();
 }
 
 JS_PUBLIC_API(void)
 JS_SetZoneUserData(JS::Zone* zone, void* data)
 {
     zone->data = data;
 }
 
@@ -1390,16 +1390,24 @@ JS_MaybeGC(JSContext* cx)
 
 JS_PUBLIC_API(void)
 JS_SetGCCallback(JSRuntime* rt, JSGCCallback cb, void* data)
 {
     AssertHeapIsIdle(rt);
     rt->gc.setGCCallback(cb, data);
 }
 
+JS_PUBLIC_API(void)
+JS_SetObjectsTenuredCallback(JSRuntime* rt, JSObjectsTenuredCallback cb,
+                             void* data)
+{
+    AssertHeapIsIdle(rt);
+    rt->gc.setObjectsTenuredCallback(cb, data);
+}
+
 JS_PUBLIC_API(bool)
 JS_AddFinalizeCallback(JSRuntime* rt, JSFinalizeCallback cb, void* data)
 {
     AssertHeapIsIdle(rt);
     return rt->gc.addFinalizeCallback(cb, data);
 }
 
 JS_PUBLIC_API(void)
@@ -1782,57 +1790,75 @@ JS_GetConstructor(JSContext* cx, HandleO
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                              proto->getClass()->name);
         return nullptr;
     }
     return &cval.toObject();
 }
 
 bool
-JS::CompartmentOptions::extraWarnings(JSRuntime* rt) const
+JS::CompartmentBehaviors::extraWarnings(JSRuntime* rt) const
 {
     return extraWarningsOverride_.get(rt->options().extraWarnings());
 }
 
 bool
-JS::CompartmentOptions::extraWarnings(JSContext* cx) const
+JS::CompartmentBehaviors::extraWarnings(JSContext* cx) const
 {
     return extraWarnings(cx->runtime());
 }
 
-JS::CompartmentOptions&
-JS::CompartmentOptions::setZone(ZoneSpecifier spec)
+JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptions::setZone(ZoneSpecifier spec)
 {
     zone_.spec = spec;
     return *this;
 }
 
-JS::CompartmentOptions&
-JS::CompartmentOptions::setSameZoneAs(JSObject* obj)
+JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptions::setSameZoneAs(JSObject* obj)
 {
     zone_.pointer = static_cast<void*>(obj->zone());
     return *this;
 }
 
-JS::CompartmentOptions&
-JS::CompartmentOptionsRef(JSCompartment* compartment)
-{
-    return compartment->options();
-}
-
-JS::CompartmentOptions&
-JS::CompartmentOptionsRef(JSObject* obj)
-{
-    return obj->compartment()->options();
-}
-
-JS::CompartmentOptions&
-JS::CompartmentOptionsRef(JSContext* cx)
-{
-    return cx->compartment()->options();
+const JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptionsRef(JSCompartment* compartment)
+{
+    return compartment->creationOptions();
+}
+
+const JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptionsRef(JSObject* obj)
+{
+    return obj->compartment()->creationOptions();
+}
+
+const JS::CompartmentCreationOptions&
+JS::CompartmentCreationOptionsRef(JSContext* cx)
+{
+    return cx->compartment()->creationOptions();
+}
+
+JS::CompartmentBehaviors&
+JS::CompartmentBehaviorsRef(JSCompartment* compartment)
+{
+    return compartment->behaviors();
+}
+
+JS::CompartmentBehaviors&
+JS::CompartmentBehaviorsRef(JSObject* obj)
+{
+    return obj->compartment()->behaviors();
+}
+
+JS::CompartmentBehaviors&
+JS::CompartmentBehaviorsRef(JSContext* cx)
+{
+    return cx->compartment()->behaviors();
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewGlobalObject(JSContext* cx, const JSClass* clasp, JSPrincipals* principals,
                    JS::OnNewGlobalHookOption hookOption,
                    const JS::CompartmentOptions& options /* = JS::CompartmentOptions() */)
 {
     AssertHeapIsIdle(cx);
@@ -1852,18 +1878,17 @@ JS_GlobalObjectTraceHook(JSTracer* trc, 
     // We can safely skip it.
     if (!global->isOwnGlobal())
         return;
 
     // Trace the compartment for any GC things that should only stick around if we know the
     // compartment is live.
     global->compartment()->trace(trc);
 
-    JSTraceOp trace = global->compartment()->options().getTrace();
-    if (trace)
+    if (JSTraceOp trace = global->compartment()->creationOptions().getTrace())
         trace(trc, global);
 }
 
 JS_PUBLIC_API(void)
 JS_FireOnNewGlobalObject(JSContext* cx, JS::HandleObject global)
 {
     // This hook is infallible, because we don't really want arbitrary script
     // to be able to throw errors during delicate global creation routines.
@@ -3884,17 +3909,17 @@ JS::OwningCompileOptions::setIntroducerF
 
 JS::CompileOptions::CompileOptions(JSContext* cx, JSVersion version)
     : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
       introductionScriptRoot(cx)
 {
     this->version = (version != JSVERSION_UNKNOWN) ? version : cx->findVersion();
 
     strictOption = cx->runtime()->options().strictMode();
-    extraWarningsOption = cx->compartment()->options().extraWarnings(cx);
+    extraWarningsOption = cx->compartment()->behaviors().extraWarnings(cx);
     werrorOption = cx->runtime()->options().werror();
     if (!cx->runtime()->options().asmJS())
         asmJSOption = AsmJSOption::Disabled;
     else if (cx->compartment()->debuggerObservesAsmJS())
         asmJSOption = AsmJSOption::DisabledByDebugger;
     else
         asmJSOption = AsmJSOption::Enabled;
     throwOnAsmJSValidationFailureOption = cx->runtime()->options().throwOnAsmJSValidationFailure();
@@ -5904,31 +5929,33 @@ JS_IsIdentifier(const char16_t* chars, s
 }
 
 namespace JS {
 
 JS_PUBLIC_API(bool)
 DescribeScriptedCaller(JSContext* cx, UniqueChars* filename, unsigned* lineno,
                        unsigned* column)
 {
+    if (filename)
+        filename->reset();
     if (lineno)
         *lineno = 0;
     if (column)
         *column = 0;
 
     NonBuiltinFrameIter i(cx);
     if (i.done())
         return false;
 
     // If the caller is hidden, the embedding wants us to return false here so
     // that it can check its own stack (see HideScriptedCaller).
     if (i.activation()->scriptedCallerIsHidden())
         return false;
 
-    if (filename) {
+    if (filename && i.filename()) {
         UniqueChars copy = make_string_copy(i.filename());
         if (!copy)
             return false;
         *filename = Move(copy);
     }
 
     if (lineno)
         *lineno = i.computeLine(column);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -567,16 +567,19 @@ typedef bool
 typedef enum JSGCStatus {
     JSGC_BEGIN,
     JSGC_END
 } JSGCStatus;
 
 typedef void
 (* JSGCCallback)(JSRuntime* rt, JSGCStatus status, void* data);
 
+typedef void
+(* JSObjectsTenuredCallback)(JSRuntime* rt, void* data);
+
 typedef enum JSFinalizeStatus {
     /**
      * Called when preparing to sweep a group of compartments, before anything
      * has been swept.  The collector will not yield to the mutator before
      * calling the callback with JSFINALIZE_GROUP_END status.
      */
     JSFINALIZE_GROUP_START,
 
@@ -1649,16 +1652,20 @@ extern JS_PUBLIC_API(void)
 JS_GC(JSRuntime* rt);
 
 extern JS_PUBLIC_API(void)
 JS_MaybeGC(JSContext* cx);
 
 extern JS_PUBLIC_API(void)
 JS_SetGCCallback(JSRuntime* rt, JSGCCallback cb, void* data);
 
+extern JS_PUBLIC_API(void)
+JS_SetObjectsTenuredCallback(JSRuntime* rt, JSObjectsTenuredCallback cb,
+                             void* data);
+
 extern JS_PUBLIC_API(bool)
 JS_AddFinalizeCallback(JSRuntime* rt, JSFinalizeCallback cb, void* data);
 
 extern JS_PUBLIC_API(void)
 JS_RemoveFinalizeCallback(JSRuntime* rt, JSFinalizeCallback cb);
 
 /*
  * Weak pointers and garbage collection
@@ -2151,17 +2158,114 @@ JS_GetConstructor(JSContext* cx, JS::Han
 
 namespace JS {
 
 enum ZoneSpecifier {
     FreshZone = 0,
     SystemZone = 1
 };
 
-class JS_PUBLIC_API(CompartmentOptions)
+/**
+ * CompartmentCreationOptions specifies options relevant to creating a new
+ * compartment, that are either immutable characteristics of that compartment
+ * or that are discarded after the compartment has been created.
+ *
+ * Access to these options on an existing compartment is read-only: if you
+ * need particular selections, make them before you create the compartment.
+ */
+class JS_PUBLIC_API(CompartmentCreationOptions)
+{
+  public:
+    CompartmentCreationOptions()
+      : addonId_(nullptr),
+        traceGlobal_(nullptr),
+        invisibleToDebugger_(false),
+        mergeable_(false),
+        preserveJitCode_(false),
+        cloneSingletons_(false)
+    {
+        zone_.spec = JS::FreshZone;
+    }
+
+    // A null add-on ID means that the compartment is not associated with an
+    // add-on.
+    JSAddonId* addonIdOrNull() const { return addonId_; }
+    CompartmentCreationOptions& setAddonId(JSAddonId* id) {
+        addonId_ = id;
+        return *this;
+    }
+
+    JSTraceOp getTrace() const {
+        return traceGlobal_;
+    }
+    CompartmentCreationOptions& setTrace(JSTraceOp op) {
+        traceGlobal_ = op;
+        return *this;
+    }
+
+    void* zonePointer() const {
+        MOZ_ASSERT(uintptr_t(zone_.pointer) > uintptr_t(JS::SystemZone));
+        return zone_.pointer;
+    }
+    ZoneSpecifier zoneSpecifier() const { return zone_.spec; }
+    CompartmentCreationOptions& setZone(ZoneSpecifier spec);
+    CompartmentCreationOptions& setSameZoneAs(JSObject* obj);
+
+    // Certain scopes (i.e. XBL compilation scopes) are implementation details
+    // of the embedding, and references to them should never leak out to script.
+    // This flag causes the this compartment to skip firing onNewGlobalObject
+    // and makes addDebuggee a no-op for this global.
+    bool invisibleToDebugger() const { return invisibleToDebugger_; }
+    CompartmentCreationOptions& setInvisibleToDebugger(bool flag) {
+        invisibleToDebugger_ = flag;
+        return *this;
+    }
+
+    // Compartments used for off-thread compilation have their contents merged
+    // into a target compartment when the compilation is finished. This is only
+    // allowed if this flag is set. The invisibleToDebugger flag must also be
+    // set for such compartments.
+    bool mergeable() const { return mergeable_; }
+    CompartmentCreationOptions& setMergeable(bool flag) {
+        mergeable_ = flag;
+        return *this;
+    }
+
+    // Determines whether this compartment should preserve JIT code on
+    // non-shrinking GCs.
+    bool preserveJitCode() const { return preserveJitCode_; }
+    CompartmentCreationOptions& setPreserveJitCode(bool flag) {
+        preserveJitCode_ = flag;
+        return *this;
+    }
+
+    bool cloneSingletons() const { return cloneSingletons_; }
+    CompartmentCreationOptions& setCloneSingletons(bool flag) {
+        cloneSingletons_ = flag;
+        return *this;
+    }
+
+  private:
+    JSAddonId* addonId_;
+    JSTraceOp traceGlobal_;
+    union {
+        ZoneSpecifier spec;
+        void* pointer; // js::Zone* is not exposed in the API.
+    } zone_;
+    bool invisibleToDebugger_;
+    bool mergeable_;
+    bool preserveJitCode_;
+    bool cloneSingletons_;
+};
+
+/**
+ * CompartmentBehaviors specifies behaviors of a compartment that can be
+ * changed after the compartment's been created.
+ */
+class JS_PUBLIC_API(CompartmentBehaviors)
 {
   public:
     class Override {
       public:
         Override() : mode_(Default) {}
 
         bool get(bool defaultValue) const {
             if (mode_ == Default)
@@ -2182,150 +2286,130 @@ class JS_PUBLIC_API(CompartmentOptions)
             Default,
             ForceTrue,
             ForceFalse
         };
 
         Mode mode_;
     };
 
-    explicit CompartmentOptions()
+    CompartmentBehaviors()
       : version_(JSVERSION_UNKNOWN)
-      , invisibleToDebugger_(false)
-      , mergeable_(false)
       , discardSource_(false)
       , disableLazyParsing_(false)
-      , cloneSingletons_(false)
-      , traceGlobal_(nullptr)
       , singletonsAsTemplates_(true)
-      , addonId_(nullptr)
-      , preserveJitCode_(false)
     {
-        zone_.spec = JS::FreshZone;
     }
 
     JSVersion version() const { return version_; }
-    CompartmentOptions& setVersion(JSVersion aVersion) {
+    CompartmentBehaviors& setVersion(JSVersion aVersion) {
         MOZ_ASSERT(aVersion != JSVERSION_UNKNOWN);
         version_ = aVersion;
         return *this;
     }
 
-    // Certain scopes (i.e. XBL compilation scopes) are implementation details
-    // of the embedding, and references to them should never leak out to script.
-    // This flag causes the this compartment to skip firing onNewGlobalObject
-    // and makes addDebuggee a no-op for this global.
-    bool invisibleToDebugger() const { return invisibleToDebugger_; }
-    CompartmentOptions& setInvisibleToDebugger(bool flag) {
-        invisibleToDebugger_ = flag;
-        return *this;
-    }
-
-    // Compartments used for off-thread compilation have their contents merged
-    // into a target compartment when the compilation is finished. This is only
-    // allowed if this flag is set.  The invisibleToDebugger flag must also be
-    // set for such compartments.
-    bool mergeable() const { return mergeable_; }
-    CompartmentOptions& setMergeable(bool flag) {
-        mergeable_ = flag;
-        return *this;
-    }
-
     // For certain globals, we know enough about the code that will run in them
     // that we can discard script source entirely.
     bool discardSource() const { return discardSource_; }
-    CompartmentOptions& setDiscardSource(bool flag) {
+    CompartmentBehaviors& setDiscardSource(bool flag) {
         discardSource_ = flag;
         return *this;
     }
 
     bool disableLazyParsing() const { return disableLazyParsing_; }
-    CompartmentOptions& setDisableLazyParsing(bool flag) {
+    CompartmentBehaviors& setDisableLazyParsing(bool flag) {
         disableLazyParsing_ = flag;
         return *this;
     }
 
-    bool cloneSingletons() const { return cloneSingletons_; }
-    CompartmentOptions& setCloneSingletons(bool flag) {
-        cloneSingletons_ = flag;
-        return *this;
-    }
-
     bool extraWarnings(JSRuntime* rt) const;
     bool extraWarnings(JSContext* cx) const;
     Override& extraWarningsOverride() { return extraWarningsOverride_; }
 
-    void* zonePointer() const {
-        MOZ_ASSERT(uintptr_t(zone_.pointer) > uintptr_t(JS::SystemZone));
-        return zone_.pointer;
-    }
-    ZoneSpecifier zoneSpecifier() const { return zone_.spec; }
-    CompartmentOptions& setZone(ZoneSpecifier spec);
-    CompartmentOptions& setSameZoneAs(JSObject* obj);
-
-    void setSingletonsAsValues() {
-        singletonsAsTemplates_ = false;
-    }
     bool getSingletonsAsTemplates() const {
         return singletonsAsTemplates_;
     }
-
-    // A null add-on ID means that the compartment is not associated with an
-    // add-on.
-    JSAddonId* addonIdOrNull() const { return addonId_; }
-    CompartmentOptions& setAddonId(JSAddonId* id) {
-        addonId_ = id;
-        return *this;
-    }
-
-    CompartmentOptions& setTrace(JSTraceOp op) {
-        traceGlobal_ = op;
-        return *this;
-    }
-    JSTraceOp getTrace() const {
-        return traceGlobal_;
-    }
-
-    bool preserveJitCode() const { return preserveJitCode_; }
-    CompartmentOptions& setPreserveJitCode(bool flag) {
-        preserveJitCode_ = flag;
+    CompartmentBehaviors& setSingletonsAsValues() {
+        singletonsAsTemplates_ = false;
         return *this;
     }
 
   private:
     JSVersion version_;
-    bool invisibleToDebugger_;
-    bool mergeable_;
     bool discardSource_;
     bool disableLazyParsing_;
-    bool cloneSingletons_;
     Override extraWarningsOverride_;
-    union {
-        ZoneSpecifier spec;
-        void* pointer; // js::Zone* is not exposed in the API.
-    } zone_;
-    JSTraceOp traceGlobal_;
 
     // To XDR singletons, we need to ensure that all singletons are all used as
     // templates, by making JSOP_OBJECT return a clone of the JSScript
     // singleton, instead of returning the value which is baked in the JSScript.
     bool singletonsAsTemplates_;
-
-    JSAddonId* addonId_;
-    bool preserveJitCode_;
 };
 
-JS_PUBLIC_API(CompartmentOptions&)
-CompartmentOptionsRef(JSCompartment* compartment);
-
-JS_PUBLIC_API(CompartmentOptions&)
-CompartmentOptionsRef(JSObject* obj);
-
-JS_PUBLIC_API(CompartmentOptions&)
-CompartmentOptionsRef(JSContext* cx);
+/**
+ * CompartmentOptions specifies compartment characteristics: both those that
+ * can't be changed on a compartment once it's been created
+ * (CompartmentCreationOptions), and those that can be changed on an existing
+ * compartment (CompartmentBehaviors).
+ */
+class JS_PUBLIC_API(CompartmentOptions)
+{
+  public:
+    explicit CompartmentOptions()
+      : creationOptions_(),
+        behaviors_()
+    {}
+
+    CompartmentOptions(const CompartmentCreationOptions& compartmentCreation,
+                       const CompartmentBehaviors& compartmentBehaviors)
+      : creationOptions_(compartmentCreation),
+        behaviors_(compartmentBehaviors)
+    {}
+
+    // CompartmentCreationOptions specify fundamental compartment
+    // characteristics that must be specified when the compartment is created,
+    // that can't be changed after the compartment is created.
+    CompartmentCreationOptions& creationOptions() {
+        return creationOptions_;
+    }
+    const CompartmentCreationOptions& creationOptions() const {
+        return creationOptions_;
+    }
+
+    // CompartmentBehaviors specify compartment characteristics that can be
+    // changed after the compartment is created.
+    CompartmentBehaviors& behaviors() {
+        return behaviors_;
+    }
+    const CompartmentBehaviors& behaviors() const {
+        return behaviors_;
+    }
+
+  private:
+    CompartmentCreationOptions creationOptions_;
+    CompartmentBehaviors behaviors_;
+};
+
+JS_PUBLIC_API(const CompartmentCreationOptions&)
+CompartmentCreationOptionsRef(JSCompartment* compartment);
+
+JS_PUBLIC_API(const CompartmentCreationOptions&)
+CompartmentCreationOptionsRef(JSObject* obj);
+
+JS_PUBLIC_API(const CompartmentCreationOptions&)
+CompartmentCreationOptionsRef(JSContext* cx);
+
+JS_PUBLIC_API(CompartmentBehaviors&)
+CompartmentBehaviorsRef(JSCompartment* compartment);
+
+JS_PUBLIC_API(CompartmentBehaviors&)
+CompartmentBehaviorsRef(JSObject* obj);
+
+JS_PUBLIC_API(CompartmentBehaviors&)
+CompartmentBehaviorsRef(JSContext* cx);
 
 /**
  * During global creation, we fire notifications to callbacks registered
  * via the Debugger API. These callbacks are arbitrary script, and can touch
  * the global in arbitrary ways. When that happens, the global should not be
  * in a half-baked state. But this creates a problem for consumers that need
  * to set slots on the global to put it in a consistent state.
  *
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -393,23 +393,23 @@ checkReportFlags(JSContext* cx, unsigned
          * Error in strict code; warning with extra warnings option; okay
          * otherwise.  We assume that if the top frame is a native, then it is
          * strict if the nearest scripted frame is strict, see bug 536306.
          */
         jsbytecode* pc;
         JSScript* script = cx->currentScript(&pc);
         if (script && IsCheckStrictOp(JSOp(*pc)))
             *flags &= ~JSREPORT_WARNING;
-        else if (cx->compartment()->options().extraWarnings(cx))
+        else if (cx->compartment()->behaviors().extraWarnings(cx))
             *flags |= JSREPORT_WARNING;
         else
             return true;
     } else if (JSREPORT_IS_STRICT(*flags)) {
         /* Warning/error only when JSOPTION_STRICT is set. */
-        if (!cx->compartment()->options().extraWarnings(cx))
+        if (!cx->compartment()->behaviors().extraWarnings(cx))
             return true;
     }
 
     /* Warnings become errors when JSOPTION_WERROR is set. */
     if (JSREPORT_IS_WARNING(*flags) && cx->runtime()->options().werror())
         *flags &= ~JSREPORT_WARNING;
 
     return false;
@@ -1155,18 +1155,18 @@ ExclusiveContext::stackLimitAddressForJi
 }
 
 JSVersion
 JSContext::findVersion() const
 {
     if (JSScript* script = currentScript(nullptr, ALLOW_CROSS_COMPARTMENT))
         return script->getVersion();
 
-    if (compartment() && compartment()->options().version() != JSVERSION_UNKNOWN)
-        return compartment()->options().version();
+    if (compartment() && compartment()->behaviors().version() != JSVERSION_UNKNOWN)
+        return compartment()->behaviors().version();
 
     return runtime()->defaultVersion();
 }
 
 #ifdef DEBUG
 
 JS::AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext* cx)
     : cx(cx)
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -36,27 +36,27 @@
 using namespace js;
 using namespace js::gc;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 using mozilla::PodArrayZero;
 
 JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = JS::CompartmentOptions())
-  : options_(options),
+  : creationOptions_(options.creationOptions()),
+    behaviors_(options.behaviors()),
     zone_(zone),
     runtime_(zone->runtimeFromMainThread()),
     principals_(nullptr),
     isSystem_(false),
     isSelfHosting(false),
     marked(true),
     warnedAboutFlagsArgument(false),
     warnedAboutExprClosure(false),
     warnedAboutRegExpMultiline(false),
-    addonId(options.addonIdOrNull()),
 #ifdef DEBUG
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
     performanceMonitoring(runtime_),
     data(nullptr),
     objectMetadataCallback(nullptr),
@@ -66,34 +66,34 @@ JSCompartment::JSCompartment(Zone* zone,
     neuteredTypedObjects(0),
     objectMetadataState(ImmediateMetadata()),
     propertyTree(thisForCtor()),
     selfHostingScriptSource(nullptr),
     objectMetadataTable(nullptr),
     lazyArrayBuffers(nullptr),
     nonSyntacticLexicalScopes_(nullptr),
     gcIncomingGrayPointers(nullptr),
-    gcPreserveJitCode(options.preserveJitCode()),
     debugModeBits(0),
     watchpointMap(nullptr),
     scriptCountsMap(nullptr),
     debugScriptMap(nullptr),
     debugScopes(nullptr),
     enumerators(nullptr),
     compartmentStats(nullptr),
     scheduledForDestruction(false),
     maybeAlive(true),
     jitCompartment_(nullptr),
     mappedArgumentsTemplate_(nullptr),
     unmappedArgumentsTemplate_(nullptr),
     lcovOutput()
 {
     PodArrayZero(sawDeprecatedLanguageExtension);
     runtime_->numCompartments++;
-    MOZ_ASSERT_IF(options.mergeable(), options.invisibleToDebugger());
+    MOZ_ASSERT_IF(creationOptions_.mergeable(),
+                  creationOptions_.invisibleToDebugger());
 }
 
 JSCompartment::~JSCompartment()
 {
     reportTelemetry();
 
     // Write the code coverage information in a file.
     JSRuntime* rt = runtimeFromMainThread();
@@ -163,29 +163,22 @@ JSRuntime::createJitRuntime(JSContext* c
         return nullptr;
 
     // Protect jitRuntime_ from being observed (by InterruptRunningJitCode)
     // while it is being initialized. Unfortunately, initialization depends on
     // jitRuntime_ being non-null, so we can't just wait to assign jitRuntime_.
     JitRuntime::AutoPreventBackedgePatching apbp(cx->runtime(), jrt);
     jitRuntime_ = jrt;
 
+    AutoEnterOOMUnsafeRegion noOOM;
     if (!jitRuntime_->initialize(cx)) {
-        ReportOutOfMemory(cx);
-
-        js_delete(jitRuntime_);
-        jitRuntime_ = nullptr;
-
-        JSCompartment* comp = cx->runtime()->atomsCompartment();
-        if (comp->jitCompartment_) {
-            js_delete(comp->jitCompartment_);
-            comp->jitCompartment_ = nullptr;
-        }
-
-        return nullptr;
+        // Handling OOM here is complicated: if we delete jitRuntime_ now, we
+        // will destroy the ExecutableAllocator, even though there may still be
+        // JitCode instances holding references to ExecutablePools.
+        noOOM.crash("OOM in createJitRuntime");
     }
 
     return jitRuntime_;
 }
 
 bool
 JSCompartment::ensureJitCompartmentExists(JSContext* cx)
 {
@@ -1150,32 +1143,34 @@ JSCompartment::reportTelemetry()
 {
     // Only report telemetry for web content and add-ons, not chrome JS.
     if (isSystem_)
         return;
 
     // Hazard analysis can't tell that the telemetry callbacks don't GC.
     JS::AutoSuppressGCAnalysis nogc;
 
-    int id = addonId
+    int id = creationOptions_.addonIdOrNull()
              ? JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS
              : JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT;
 
     // Call back into Firefox's Telemetry reporter.
     for (size_t i = 0; i < DeprecatedLanguageExtensionCount; i++) {
         if (sawDeprecatedLanguageExtension[i])
             runtime_->addTelemetry(id, i);
     }
 }
 
 void
 JSCompartment::addTelemetry(const char* filename, DeprecatedLanguageExtension e)
 {
     // Only report telemetry for web content and add-ons, not chrome JS.
-    if (isSystem_ || (!addonId && (!filename || strncmp(filename, "http", 4) != 0)))
+    if (isSystem_)
+        return;
+    if (!creationOptions_.addonIdOrNull() && (!filename || strncmp(filename, "http", 4) != 0))
         return;
 
     sawDeprecatedLanguageExtension[e] = true;
 }
 
 AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(ExclusiveContext* ecx
                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
     : CustomAutoRooter(ecx)
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -218,17 +218,18 @@ namespace js {
 class DebugScopes;
 class ObjectWeakMap;
 class WatchpointMap;
 class WeakMapBase;
 } // namespace js
 
 struct JSCompartment
 {
-    JS::CompartmentOptions       options_;
+    const JS::CompartmentCreationOptions creationOptions_;
+    JS::CompartmentBehaviors behaviors_;
 
   private:
     JS::Zone*                    zone_;
     JSRuntime*                   runtime_;
 
   public:
     /*
      * The principals associated with this compartment. Note that the
@@ -275,20 +276,16 @@ struct JSCompartment
     bool                         isSystem_;
   public:
     bool                         isSelfHosting;
     bool                         marked;
     bool                         warnedAboutFlagsArgument;
     bool                         warnedAboutExprClosure;
     bool                         warnedAboutRegExpMultiline;
 
-    // A null add-on ID means that the compartment is not associated with an
-    // add-on.
-    JSAddonId*                   const addonId;
-
 #ifdef DEBUG
     bool                         firedOnNewGlobalObject;
 #endif
 
     void mark() { marked = true; }
 
   private:
     friend struct JSRuntime;
@@ -307,18 +304,20 @@ struct JSCompartment
     }
     void leave() {
         enterCompartmentDepth--;
     }
     bool hasBeenEntered() { return !!enterCompartmentDepth; }
 
     JS::Zone* zone() { return zone_; }
     const JS::Zone* zone() const { return zone_; }
-    JS::CompartmentOptions& options() { return options_; }
-    const JS::CompartmentOptions& options() const { return options_; }
+
+    const JS::CompartmentCreationOptions& creationOptions() const { return creationOptions_; }
+    JS::CompartmentBehaviors& behaviors() { return behaviors_; }
+    const JS::CompartmentBehaviors& behaviors() const { return behaviors_; }
 
     JSRuntime* runtimeFromMainThread() {
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
         return runtime_;
     }
 
     // Note: Unrestricted access to the zone's runtime from an arbitrary
     // thread can easily lead to races. Use this method very carefully.
@@ -468,19 +467,16 @@ struct JSCompartment
      *
      * The objects in the list are either cross-compartment wrappers, or
      * debugger wrapper objects.  The list link is either in the second extra
      * slot for the former, or a special slot for the latter.
      */
     JSObject*                    gcIncomingGrayPointers;
 
   private:
-    /* Whether to preserve JIT code on non-shrinking GCs. */
-    bool                         gcPreserveJitCode;
-
     enum {
         IsDebuggee = 1 << 0,
         DebuggerObservesAllExecution = 1 << 1,
         DebuggerObservesAsmJS = 1 << 2,
         DebuggerObservesCoverage = 1 << 3,
         DebuggerNeedsDelazification = 1 << 4
     };
 
@@ -548,17 +544,18 @@ struct JSCompartment
      * These methods mark pointers that cross compartment boundaries. They are
      * called in per-zone GCs to prevent the wrappers' outgoing edges from
      * dangling (full GCs naturally follow pointers across compartments) and
      * when compacting to update cross-compartment pointers.
      */
     void traceOutgoingCrossCompartmentWrappers(JSTracer* trc);
     static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc);
 
-    bool preserveJitCode() { return gcPreserveJitCode; }
+    /* Whether to preserve JIT code on non-shrinking GCs. */
+    bool preserveJitCode() { return creationOptions_.preserveJitCode(); }
 
     void sweepAfterMinorGC();
 
     void sweepInnerViews();
     void sweepCrossCompartmentWrappers();
     void sweepSavedStacks();
     void sweepGlobalObject(js::FreeOp* fop);
     void sweepObjectPendingMetadata();
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -722,17 +722,17 @@ ErrorReport::ReportAddonExceptionToTelem
     // Let's ignore TOP level exceptions. For regular add-ons those will not be reported anyway,
     // for SDK based once it should not be a valid case either.
     // At this point the frame stack is unwound but the exception object stored the stack so let's
     // use that for getting the function name.
     if (!stack)
         return;
 
     JSCompartment* comp = stack->compartment();
-    JSAddonId* addonId = comp->addonId;
+    JSAddonId* addonId = comp->creationOptions().addonIdOrNull();
 
     // We only want to send the report if the scope that just have thrown belongs to an add-on.
     // Let's check the compartment of the youngest function on the stack, to determine that.
     if (!addonId)
         return;
 
     RootedString funnameString(cx);
     JS::SavedFrameResult result = GetSavedFrameFunctionDisplayName(cx, stack, &funnameString);
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -694,23 +694,23 @@ class FunctionExtended : public JSFuncti
     static const unsigned ARROW_NEWTARGET_SLOT = 0;
 
     static const unsigned METHOD_HOMEOBJECT_SLOT = 0;
 
     /*
      * All asm.js/wasm functions store their compiled module (either
      * WasmModuleObject or AsmJSModuleObject) in the first extended slot.
      */
-    static const unsigned ASM_MODULE_SLOT = 0;
+    static const unsigned WASM_MODULE_SLOT = 0;
 
     /*
      * wasm/asm.js exported functions store the index of the export in the
      * module's export vector in the second slot.
      */
-    static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
+    static const unsigned WASM_EXPORT_INDEX_SLOT = 1;
 
     static inline size_t offsetOfExtendedSlot(unsigned which) {
         MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
         return offsetof(FunctionExtended, extendedSlots) + which * sizeof(HeapValue);
     }
     static inline size_t offsetOfArrowNewTargetSlot() {
         return offsetOfExtendedSlot(ARROW_NEWTARGET_SLOT);
     }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1601,16 +1601,31 @@ GCRuntime::setGCCallback(JSGCCallback ca
 
 void
 GCRuntime::callGCCallback(JSGCStatus status) const
 {
     if (gcCallback.op)
         gcCallback.op(rt, status, gcCallback.data);
 }
 
+void
+GCRuntime::setObjectsTenuredCallback(JSObjectsTenuredCallback callback,
+                                     void* data)
+{
+    tenuredCallback.op = callback;
+    tenuredCallback.data = data;
+}
+
+void
+GCRuntime::callObjectsTenuredCallback()
+{
+    if (tenuredCallback.op)
+        tenuredCallback.op(rt, tenuredCallback.data);
+}
+
 namespace {
 
 class AutoNotifyGCActivity {
   public:
     explicit AutoNotifyGCActivity(GCRuntime& gc) : gc_(gc) {
         if (!gc_.isIncrementalGCInProgress()) {
             gcstats::AutoPhase ap(gc_.stats, gcstats::PHASE_GC_BEGIN);
             gc_.callGCCallback(JSGC_BEGIN);
@@ -6764,19 +6779,21 @@ js::NewCompartment(JSContext* cx, Zone* 
     return compartment.forget();
 }
 
 void
 gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
 {
     // The source compartment must be specifically flagged as mergable.  This
     // also implies that the compartment is not visible to the debugger.
-    MOZ_ASSERT(source->options_.mergeable());
-
-    MOZ_ASSERT(source->addonId == target->addonId);
+    MOZ_ASSERT(source->creationOptions_.mergeable());
+    MOZ_ASSERT(source->creationOptions_.invisibleToDebugger());
+
+    MOZ_ASSERT(source->creationOptions().addonIdOrNull() ==
+               target->creationOptions().addonIdOrNull());
 
     JSRuntime* rt = source->runtimeFromMainThread();
 
     AutoPrepareForTracing prepare(rt, SkipAtoms);
 
     // Cleanup tables and other state in the source compartment that will be
     // meaningless after merging into the target compartment.
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1270,17 +1270,17 @@ DeepCloneValue(JSContext* cx, Value* vp,
     return true;
 }
 
 JSObject*
 js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKind)
 {
     /* NB: Keep this in sync with XDRObjectLiteral. */
     MOZ_ASSERT_IF(obj->isSingleton(),
-                  JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
+                  cx->compartment()->behaviors().getSingletonsAsTemplates());
     MOZ_ASSERT(obj->is<PlainObject>() || obj->is<UnboxedPlainObject>() ||
                obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
     MOZ_ASSERT(newKind != SingletonObject);
 
     if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
         AutoValueVector values(cx);
         if (!GetScriptArrayObjectElements(cx, obj, values))
             return nullptr;
@@ -1384,17 +1384,17 @@ JS_InitializePropertiesFromCompatibleNat
 template<XDRMode mode>
 bool
 js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
 {
     /* NB: Keep this in sync with DeepCloneObjectLiteral. */
 
     JSContext* cx = xdr->cx();
     MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->isSingleton(),
-                  JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
+                  cx->compartment()->behaviors().getSingletonsAsTemplates());
 
     // Distinguish between objects and array classes.
     uint32_t isArray = 0;
     {
         if (mode == XDR_ENCODE) {
             MOZ_ASSERT(obj->is<PlainObject>() ||
                        obj->is<UnboxedPlainObject>() ||
                        obj->is<ArrayObject>() ||
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -653,18 +653,20 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         MOZ_ASSERT(script->functionNonDelazifying() == fun);
 
         if (!fun && script->treatAsRunOnce()) {
             // This is a toplevel or eval script that's runOnce.  We want to
             // make sure that we're not XDR-saving an object we emitted for
             // JSOP_OBJECT that then got modified.  So throw if we're not
             // cloning in JSOP_OBJECT or if we ever didn't clone in it in the
             // past.
-            const JS::CompartmentOptions& opts = JS::CompartmentOptionsRef(cx);
-            if (!opts.cloneSingletons() || !opts.getSingletonsAsTemplates()) {
+            JSCompartment* comp = cx->compartment();
+            if (!comp->creationOptions().cloneSingletons() ||
+                !comp->behaviors().getSingletonsAsTemplates())
+            {
                 JS_ReportError(cx,
                                "Can't serialize a run-once non-function script "
                                "when we're not doing singleton cloning");
                 return false;
             }
         }
 
         nargs = script->bindings.numArgs();
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1344,24 +1344,24 @@ Evaluate(JSContext* cx, unsigned argc, V
         if (saveFrameChain && !asfc.save())
             return false;
 
         JSAutoCompartment ac(cx, global);
         RootedScript script(cx);
 
         {
             if (saveBytecode) {
-                if (!JS::CompartmentOptionsRef(cx).cloneSingletons()) {
+                if (!JS::CompartmentCreationOptionsRef(cx).cloneSingletons()) {
                     JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
                                          JSSMSG_CACHE_SINGLETON_FAILED);
                     return false;
                 }
 
                 // cloneSingletons implies that singletons are used as template objects.
-                MOZ_ASSERT(JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
+                MOZ_ASSERT(JS::CompartmentBehaviorsRef(cx).getSingletonsAsTemplates());
             }
 
             if (loadBytecode) {
                 script = JS_DecodeScript(cx, loadBuffer, loadLength);
             } else {
                 mozilla::Range<const char16_t> chars = codeChars.twoByteRange();
                 (void) JS::Compile(cx, options, chars.start().get(), chars.length(), &script);
             }
@@ -2755,17 +2755,17 @@ WorkerMain(void* arg)
     }
 
     JS::SetLargeAllocationFailureCallback(rt, my_LargeAllocFailCallback, (void*)cx);
 
     do {
         JSAutoRequest ar(cx);
 
         JS::CompartmentOptions compartmentOptions;
-        compartmentOptions.setVersion(JSVERSION_DEFAULT);
+        compartmentOptions.behaviors().setVersion(JSVERSION_DEFAULT);
         RootedObject global(cx, NewGlobalObject(cx, compartmentOptions, nullptr));
         if (!global)
             break;
 
         JSAutoCompartment ac(cx, global);
 
         JS::CompileOptions options(cx);
         options.setFileAndLine("<string>", 1)
@@ -3776,21 +3776,23 @@ EscapeForShell(AutoCStringVector& argv)
         argv.replace(i, escaped);
     }
     return true;
 }
 #endif
 
 static Vector<const char*, 4, js::SystemAllocPolicy> sPropagatedFlags;
 
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
 static bool
 PropagateFlagToNestedShells(const char* flag)
 {
     return sPropagatedFlags.append(flag);
 }
+#endif
 
 static bool
 NestedShell(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     AutoCStringVector argv(cx);
 
@@ -3947,43 +3949,48 @@ WrapWithProto(JSContext* cx, unsigned ar
     args.rval().setObject(*wrapped);
     return true;
 }
 
 static bool
 NewGlobal(JSContext* cx, unsigned argc, Value* vp)
 {
     JSPrincipals* principals = nullptr;
+
     JS::CompartmentOptions options;
-    options.setVersion(JSVERSION_DEFAULT);
+
+    JS::CompartmentCreationOptions& creationOptions = options.creationOptions();
+    JS::CompartmentBehaviors& behaviors = options.behaviors();
+
+    behaviors.setVersion(JSVERSION_DEFAULT);
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 1 && args[0].isObject()) {
         RootedObject opts(cx, &args[0].toObject());
         RootedValue v(cx);
 
-        if (!JS_GetProperty(cx, opts, "sameZoneAs", &v))
-            return false;
-        if (v.isObject())
-            options.setSameZoneAs(UncheckedUnwrap(&v.toObject()));
-
         if (!JS_GetProperty(cx, opts, "invisibleToDebugger", &v))
             return false;
         if (v.isBoolean())
-            options.setInvisibleToDebugger(v.toBoolean());
+            creationOptions.setInvisibleToDebugger(v.toBoolean());
 
         if (!JS_GetProperty(cx, opts, "cloneSingletons", &v))
             return false;
         if (v.isBoolean())
-            options.setCloneSingletons(v.toBoolean());
+            creationOptions.setCloneSingletons(v.toBoolean());
+
+        if (!JS_GetProperty(cx, opts, "sameZoneAs", &v))
+            return false;
+        if (v.isObject())
+            creationOptions.setSameZoneAs(UncheckedUnwrap(&v.toObject()));
 
         if (!JS_GetProperty(cx, opts, "disableLazyParsing", &v))
             return false;
         if (v.isBoolean())
-            options.setDisableLazyParsing(v.toBoolean());
+            behaviors.setDisableLazyParsing(v.toBoolean());
 
         if (!JS_GetProperty(cx, opts, "principal", &v))
             return false;
         if (!v.isUndefined()) {
             uint32_t bits;
             if (!ToUint32(cx, v, &bits))
                 return false;
             principals = cx->new_<ShellPrincipals>(bits);
@@ -6501,20 +6508,19 @@ Shell(JSContext* cx, OptionParser* op, c
     if (op->getBoolOption("fuzzing-safe"))
         fuzzingSafe = true;
     else
         fuzzingSafe = (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0');
 
     if (op->getBoolOption("disable-oom-functions"))
         disableOOMFunctions = true;
 
-    RootedObject glob(cx);
     JS::CompartmentOptions options;
-    options.setVersion(JSVERSION_DEFAULT);
-    glob = NewGlobalObject(cx, options, nullptr);
+    options.behaviors().setVersion(JSVERSION_DEFAULT);
+    RootedObject glob(cx, NewGlobalObject(cx, options, nullptr));
     if (!glob)
         return 1;
 
     JSAutoCompartment ac(cx, glob);
 
     int result = ProcessArgs(cx, op);
 
     if (enableDisassemblyDumps)
@@ -6685,17 +6691,17 @@ main(int argc, char** argv, char** envp)
         || !op.addStringOption('\0', "ion-parallel-compile", "on/off",
                                "--ion-parallel compile is deprecated. Use --ion-offthread-compile.")
         || !op.addBoolOption('\0', "baseline", "Enable baseline compiler (default)")
         || !op.addBoolOption('\0', "no-baseline", "Disable baseline compiler")
         || !op.addBoolOption('\0', "baseline-eager", "Always baseline-compile methods")
         || !op.addIntOption('\0', "baseline-warmup-threshold", "COUNT",
                             "Wait for COUNT calls or iterations before baseline-compiling "
                             "(default: 10)", -1)
-        || !op.addBoolOption('\0', "non-writable-jitcode", "Allocate JIT code as non-writable memory.")
+        || !op.addBoolOption('\0', "non-writable-jitcode", "(NOP for fuzzers) Allocate JIT code as non-writable memory.")
         || !op.addBoolOption('\0', "no-fpu", "Pretend CPU does not support floating-point operations "
                              "to test JIT codegen (no-op on platforms other than x86).")
         || !op.addBoolOption('\0', "no-sse3", "Pretend CPU does not support SSE3 instructions and above "
                              "to test JIT codegen (no-op on platforms other than x86 and x64).")
         || !op.addBoolOption('\0', "no-sse4", "Pretend CPU does not support SSE4 instructions"
                              "to test JIT codegen (no-op on platforms other than x86 and x64).")
         || !op.addBoolOption('\0', "enable-avx", "AVX is disabled by default. Enable AVX. "
                              "(no-op on platforms other than x86 and x64).")
@@ -6764,21 +6770,16 @@ main(int argc, char** argv, char** envp)
 #ifdef DEBUG
     /*
      * Process OOM options as early as possible so that we can observe as many
      * allocations as possible.
      */
     OOM_printAllocationCount = op.getBoolOption('O');
 #endif
 
-    if (op.getBoolOption("non-writable-jitcode")) {
-        js::jit::ExecutableAllocator::nonWritableJitCode = true;
-        PropagateFlagToNestedShells("--non-writable-jitcode");
-    }
-
 #ifdef JS_CODEGEN_X86
     if (op.getBoolOption("no-fpu"))
         js::jit::CPUInfo::SetFloatingPointDisabled();
 #endif
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     if (op.getBoolOption("no-sse3")) {
         js::jit::CPUInfo::SetSSE3Disabled();
--- a/js/src/tests/js1_8_5/extensions/clone-transferables.js
+++ b/js/src/tests/js1_8_5/extensions/clone-transferables.js
@@ -1,21 +1,17 @@
 // |reftest| skip-if(!xulRuntime.shell)
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
 function test() {
-    // Note: -8 and -200 will trigger asm.js link failures because 8 and 200
-    // bytes are below the minimum allowed size, and the buffer will not
-    // actually be converted to an asm.js buffer.
     for (var size of [0, 8, 16, 200, 1000, 4096, -8, -200, -8192, -65536]) {
-        var buffer_ctor = (size < 0) ? AsmJSArrayBuffer : ArrayBuffer;
         size = Math.abs(size);
 
-        var old = new buffer_ctor(size);
+        var old = new ArrayBuffer(size);
         var copy = deserialize(serialize(old, [old]));
         assertEq(old.byteLength, 0);
         assertEq(copy.byteLength, size);
 
         var constructors = [ Int8Array,
                              Uint8Array,
                              Int16Array,
                              Uint16Array,
@@ -24,17 +20,17 @@ function test() {
                              Float32Array,
                              Float64Array,
                              Uint8ClampedArray,
                              DataView ];
 
         for (var ctor of constructors) {
             var dataview = (ctor === DataView);
 
-            var buf = new buffer_ctor(size);
+            var buf = new ArrayBuffer(size);
             var old_arr = new ctor(buf);
             assertEq(buf.byteLength, size);
             assertEq(buf, old_arr.buffer);
             if (!dataview)
                 assertEq(old_arr.length, size / old_arr.BYTES_PER_ELEMENT);
 
             var copy_arr = deserialize(serialize(old_arr, [ buf ]));
             assertEq(buf.byteLength, 0, "donor array buffer should be neutered");
@@ -47,46 +43,46 @@ function test() {
             buf = null;
             old_arr = null;
             gc(); // Tickle the ArrayBuffer -> view management
         }
 
         for (var ctor of constructors) {
             var dataview = (ctor === DataView);
 
-            var buf = new buffer_ctor(size);
+            var buf = new ArrayBuffer(size);
             var old_arr = new ctor(buf);
             var dv = new DataView(buf); // Second view
             var copy_arr = deserialize(serialize(old_arr, [ buf ]));
             assertEq(buf.byteLength, 0, "donor array buffer should be neutered");
             assertEq(old_arr.byteLength, 0, "donor typed array should be neutered");
             if (!dataview)
                 assertEq(old_arr.length, 0, "donor typed array should be neutered");
             assertEq(dv.byteLength, 0, "all views of donor array buffer should be neutered");
 
             buf = null;
             old_arr = null;
             gc(); // Tickle the ArrayBuffer -> view management
         }
 
         // Mutate the buffer during the clone operation. The modifications should be visible.
         if (size >= 4) {
-            old = new buffer_ctor(size);
+            old = new ArrayBuffer(size);
             var view = new Int32Array(old);
             view[0] = 1;
             var mutator = { get foo() { view[0] = 2; } };
             var copy = deserialize(serialize([ old, mutator ], [old]));
             var viewCopy = new Int32Array(copy[0]);
             assertEq(view.length, 0); // Neutered
             assertEq(viewCopy[0], 2);
         }
 
         // Neuter the buffer during the clone operation. Should throw an exception.
         if (size >= 4) {
-            old = new buffer_ctor(size);
+            old = new ArrayBuffer(size);
             var mutator = {
                 get foo() {
                     deserialize(serialize(old, [old]));
                 }
             };
             // The throw is not yet implemented, bug 919259.
             //var copy = deserialize(serialize([ old, mutator ], [old]));
         }
--- a/js/src/tests/js1_8_5/extensions/shell.js
+++ b/js/src/tests/js1_8_5/extensions/shell.js
@@ -11,22 +11,8 @@
 var workerDir = '';
 
 // explicitly turn on js185
 // XXX: The browser currently only supports up to version 1.8
 if (typeof version != 'undefined')
 {
   version(185);
 }
-
-
-// Note that AsmJS ArrayBuffers have a minimum size, currently 4096 bytes. If a
-// smaller size is given, a regular ArrayBuffer will be returned instead.
-function AsmJSArrayBuffer(size) {
-    var ab = new ArrayBuffer(size);
-    (new Function('global', 'foreign', 'buffer', '' +
-'        "use asm";' +
-'        var i32 = new global.Int32Array(buffer);' +
-'        function g() {};' +
-'        return g;' +
-''))(Function("return this")(),null,ab);
-    return ab;
-}
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -125,19 +125,16 @@ const Class ArrayBufferObject::class_ = 
 
 const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
     JS_FN("slice", ArrayBufferObject::fun_slice, 2, JSFUN_GENERIC_NATIVE),
     JS_FS_END
 };
 
 const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
     JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
-#ifdef NIGHTLY_BUILD
-    JS_FN("transfer", ArrayBufferObject::fun_transfer, 2, 0),
-#endif
     JS_FS_END
 };
 
 bool
 js::IsArrayBuffer(HandleValue v)
 {
     return v.isObject() && v.toObject().is<ArrayBufferObject>();
 }
@@ -228,233 +225,16 @@ bool
 ArrayBufferObject::fun_isView(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setBoolean(args.get(0).isObject() &&
                            JS_IsArrayBufferViewObject(&args.get(0).toObject()));
     return true;
 }
 
-#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
-static void
-ReleaseAsmJSMappedData(void* base)
-{
-    MOZ_ASSERT(uintptr_t(base) % AsmJSPageSize == 0);
-#  ifdef XP_WIN
-    VirtualFree(base, 0, MEM_RELEASE);
-#  else
-    munmap(base, AsmJSMappedSize);
-#   if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
-    // Tell Valgrind/Memcheck to recommence reporting accesses in the
-    // previously-inaccessible region.
-    if (AsmJSMappedSize > 0) {
-        VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, AsmJSMappedSize);
-    }
-#   endif
-#  endif
-    MemProfiler::RemoveNative(base);
-}
-#else
-static void
-ReleaseAsmJSMappedData(void* base)
-{
-    MOZ_CRASH("asm.js only uses mapped buffers when using signal-handler OOB checking");
-}
-#endif
-
-#ifdef NIGHTLY_BUILD
-# if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
-static bool
-TransferAsmJSMappedBuffer(JSContext* cx, const CallArgs& args,
-                          Handle<ArrayBufferObject*> oldBuffer, size_t newByteLength)
-{
-    size_t oldByteLength = oldBuffer->byteLength();
-    MOZ_ASSERT(oldByteLength % AsmJSPageSize == 0);
-    MOZ_ASSERT(newByteLength % AsmJSPageSize == 0);
-
-    ArrayBufferObject::BufferContents stolen =
-        ArrayBufferObject::stealContents(cx, oldBuffer, /* hasStealableContents = */ true);
-    if (!stolen)
-        return false;
-
-    MOZ_ASSERT(stolen.kind() == ArrayBufferObject::ASMJS_MAPPED);
-    uint8_t* data = stolen.data();
-
-    if (newByteLength > oldByteLength) {
-        void* diffStart = data + oldByteLength;
-        size_t diffLength = newByteLength - oldByteLength;
-#  ifdef XP_WIN
-        if (!VirtualAlloc(diffStart, diffLength, MEM_COMMIT, PAGE_READWRITE)) {
-            ReleaseAsmJSMappedData(data);
-            ReportOutOfMemory(cx);
-            return false;
-        }
-#  else
-        // To avoid memset, use MAP_FIXED to clobber the newly-accessible pages
-        // with zero pages.
-        int flags = MAP_FIXED | MAP_PRIVATE | MAP_ANON;
-        if (mmap(diffStart, diffLength, PROT_READ | PROT_WRITE, flags, -1, 0) == MAP_FAILED) {
-            ReleaseAsmJSMappedData(data);
-            ReportOutOfMemory(cx);
-            return false;
-        }
-#  endif
-        MemProfiler::SampleNative(diffStart, diffLength);
-    } else if (newByteLength < oldByteLength) {
-        void* diffStart = data + newByteLength;
-        size_t diffLength = oldByteLength - newByteLength;
-#  ifdef XP_WIN
-        if (!VirtualFree(diffStart, diffLength, MEM_DECOMMIT)) {
-            ReleaseAsmJSMappedData(data);
-            ReportOutOfMemory(cx);
-            return false;
-        }
-#  else
-        if (madvise(diffStart, diffLength, MADV_DONTNEED) ||
-            mprotect(diffStart, diffLength, PROT_NONE))
-        {
-            ReleaseAsmJSMappedData(data);
-            ReportOutOfMemory(cx);
-            return false;
-        }
-#  endif
-    }
-
-    ArrayBufferObject::BufferContents newContents =
-        ArrayBufferObject::BufferContents::create<ArrayBufferObject::ASMJS_MAPPED>(data);
-
-    RootedObject newBuffer(cx, ArrayBufferObject::create(cx, newByteLength, newContents));
-    if (!newBuffer) {
-        ReleaseAsmJSMappedData(data);
-        return false;
-    }
-
-    args.rval().setObject(*newBuffer);
-    return true;
-}
-# endif  // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
-
-/*
- * Experimental implementation of ArrayBuffer.transfer:
- *   https://gist.github.com/andhow/95fb9e49996615764eff
- * which is currently in the early stages of proposal for ES7.
- */
-bool
-ArrayBufferObject::fun_transfer(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    HandleValue oldBufferArg = args.get(0);
-    HandleValue newByteLengthArg = args.get(1);
-
-    if (!oldBufferArg.isObject()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-        return false;
-    }
-
-    RootedObject oldBufferObj(cx, &oldBufferArg.toObject());
-    ESClassValue cls;
-    if (!GetBuiltinClass(cx, oldBufferObj, &cls))
-        return false;
-    if (cls != ESClass_ArrayBuffer) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-        return false;
-    }
-
-    // Beware: oldBuffer can point across compartment boundaries. ArrayBuffer
-    // contents are not compartment-specific so this is safe.
-    Rooted<ArrayBufferObject*> oldBuffer(cx);
-    if (oldBufferObj->is<ArrayBufferObject>()) {
-        oldBuffer = &oldBufferObj->as<ArrayBufferObject>();
-    } else {
-        JSObject* unwrapped = CheckedUnwrap(oldBufferObj);
-        if (!unwrapped || !unwrapped->is<ArrayBufferObject>()) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-            return false;
-        }
-        oldBuffer = &unwrapped->as<ArrayBufferObject>();
-    }
-
-    size_t oldByteLength = oldBuffer->byteLength();
-    size_t newByteLength;
-    if (newByteLengthArg.isUndefined()) {
-        newByteLength = oldByteLength;
-    } else {
-        int32_t i32;
-        if (!ToInt32(cx, newByteLengthArg, &i32))
-            return false;
-        if (i32 < 0) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
-            return false;
-        }
-        newByteLength = size_t(i32);
-    }
-
-    if (oldBuffer->isNeutered()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
-        return false;
-    }
-
-    UniquePtr<uint8_t, JS::FreePolicy> newData;
-    if (!newByteLength) {
-        if (!ArrayBufferObject::neuter(cx, oldBuffer, oldBuffer->contents()))
-            return false;
-    } else {
-# if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
-        // With a 4gb mapped asm.js buffer, we can simply enable/disable access
-        // to the delta as long as the requested length is page-sized.
-        if (oldBuffer->isAsmJSMapped() && (newByteLength % AsmJSPageSize) == 0)
-            return TransferAsmJSMappedBuffer(cx, args, oldBuffer, newByteLength);
-# endif
-
-        // Since we try to realloc below, only allow stealing malloc'd buffers.
-        // If !hasMallocedContents, stealContents will malloc a copy which we
-        // can then realloc.
-        bool steal = oldBuffer->hasMallocedContents();
-        auto stolenContents = ArrayBufferObject::stealContents(cx, oldBuffer, steal);
-        if (!stolenContents)
-            return false;
-
-        UniquePtr<uint8_t, JS::FreePolicy> oldData(stolenContents.data());
-        if (newByteLength > oldByteLength) {
-            // In theory, realloc+memset(0) can be optimized to avoid touching
-            // any pages (by using OS page mapping tricks). However, in
-            // practice, we don't seem to get this optimization in Firefox with
-            // jemalloc so calloc+memcpy are faster.
-            newData.reset(cx->runtime()->pod_callocCanGC<uint8_t>(newByteLength));
-            if (newData) {
-                memcpy(newData.get(), oldData.get(), oldByteLength);
-            } else {
-                // Try malloc before giving up since it might be able to succed
-                // by resizing oldData in-place.
-                newData.reset(cx->pod_realloc(oldData.get(), oldByteLength, newByteLength));
-                if (!newData)
-                    return false;
-                oldData.release();
-                memset(newData.get() + oldByteLength, 0, newByteLength - oldByteLength);
-            }
-        } else if (newByteLength < oldByteLength) {
-            newData.reset(cx->pod_realloc(oldData.get(), oldByteLength, newByteLength));
-            if (!newData)
-                return false;
-            oldData.release();
-        } else {
-            newData = Move(oldData);
-        }
-    }
-
-    RootedObject newBuffer(cx, JS_NewArrayBufferWithContents(cx, newByteLength, newData.get()));
-    if (!newBuffer)
-        return false;
-    newData.release();
-
-    args.rval().setObject(*newBuffer);
-    return true;
-}
-#endif  // defined(NIGHTLY_BUILD)
-
 /*
  * new ArrayBuffer(byteLength)
  */
 bool
 ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -506,18 +286,20 @@ ArrayBufferObject::neuterView(JSContext*
     // Notify compiled jit code that the base pointer has moved.
     MarkObjectStateChange(cx, view);
 }
 
 /* static */ bool
 ArrayBufferObject::neuter(JSContext* cx, Handle<ArrayBufferObject*> buffer,
                           BufferContents newContents)
 {
-    if (buffer->isAsmJS() && !OnDetachAsmJSArrayBuffer(cx, buffer))
+    if (buffer->isAsmJS()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
         return false;
+    }
 
     // When neutering buffers where we don't know all views, the new data must
     // match the old data. All missing views are typed objects, which do not
     // expect their data to ever change.
     MOZ_ASSERT_IF(buffer->forInlineTypedObject(),
                   newContents.data() == buffer->dataPointer());
 
     // When neutering a buffer with typed object views, any jitcode which
@@ -737,16 +519,43 @@ ArrayBufferObject::dataPointer() const
 }
 
 SharedMem<uint8_t*>
 ArrayBufferObject::dataPointerShared() const
 {
     return SharedMem<uint8_t*>::unshared(getSlot(DATA_SLOT).toPrivate());
 }
 
+#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
+static void
+ReleaseAsmJSMappedData(void* base)
+{
+    MOZ_ASSERT(uintptr_t(base) % AsmJSPageSize == 0);
+#  ifdef XP_WIN
+    VirtualFree(base, 0, MEM_RELEASE);
+#  else
+    munmap(base, AsmJSMappedSize);
+#   if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
+    // Tell Valgrind/Memcheck to recommence reporting accesses in the
+    // previously-inaccessible region.
+    if (AsmJSMappedSize > 0) {
+        VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, AsmJSMappedSize);
+    }
+#   endif
+#  endif
+    MemProfiler::RemoveNative(base);
+}
+#else
+static void
+ReleaseAsmJSMappedData(void* base)
+{
+    MOZ_CRASH("asm.js only uses mapped buffers when using signal-handler OOB checking");
+}
+#endif
+
 void
 ArrayBufferObject::releaseData(FreeOp* fop)
 {
     MOZ_ASSERT(ownsData());
 
     switch (bufferKind()) {
       case PLAIN:
       case ASMJS_MALLOCED:
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1656,17 +1656,17 @@ Debugger::fireNewGlobalObject(JSContext*
     MOZ_ASSERT(!cx->isExceptionPending());
     return status;
 }
 
 void
 Debugger::slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
 {
     MOZ_ASSERT(!JS_CLIST_IS_EMPTY(&cx->runtime()->onNewGlobalObjectWatchers));
-    if (global->compartment()->options().invisibleToDebugger())
+    if (global->compartment()->creationOptions().invisibleToDebugger())
         return;
 
     /*
      * Make a copy of the runtime's onNewGlobalObjectWatchers before running the
      * handlers. Since one Debugger's handler can disable another's, the list
      * can be mutated while we're walking it.
      */
     AutoObjectVector watchers(cx);
@@ -3139,17 +3139,17 @@ Debugger::addDebuggee(JSContext* cx, uns
 }
 
 /* static */ bool
 Debugger::addAllGlobalsAsDebuggees(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "addAllGlobalsAsDebuggees", args, dbg);
     for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
         for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
-            if (c == dbg->object->compartment() || c->options().invisibleToDebugger())
+            if (c == dbg->object->compartment() || c->creationOptions().invisibleToDebugger())
                 continue;
             c->scheduledForDestruction = false;
             GlobalObject* global = c->maybeGlobal();
             if (global) {
                 Rooted<GlobalObject*> rg(cx, global);
                 if (!dbg->addDebuggeeGlobal(cx, rg))
                     return false;
             }
@@ -3360,17 +3360,17 @@ Debugger::addDebuggeeGlobal(JSContext* c
     if (debuggees.has(global))
         return true;
 
     // Callers should generally be unable to get a reference to a debugger-
     // invisible global in order to pass it to addDebuggee. But this is possible
     // with certain testing aides we expose in the shell, so just make addDebuggee
     // throw in that case.
     JSCompartment* debuggeeCompartment = global->compartment();
-    if (debuggeeCompartment->options().invisibleToDebugger()) {
+    if (debuggeeCompartment->creationOptions().invisibleToDebugger()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                              JSMSG_DEBUG_CANT_DEBUG_GLOBAL);
         return false;
     }
 
     /*
      * Check for cycles. If global's compartment is reachable from this
      * Debugger object's compartment by following debuggee-to-debugger links,
@@ -4242,17 +4242,17 @@ Debugger::findAllGlobals(JSContext* cx, 
 
     {
         // Accumulate the list of globals before wrapping them, because
         // wrapping can GC and collect compartments from under us, while
         // iterating.
         JS::AutoCheckCannotGC nogc;
 
         for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
-            if (c->options().invisibleToDebugger())
+            if (c->creationOptions().invisibleToDebugger())
                 continue;
 
             c->scheduledForDestruction = false;
 
             GlobalObject* global = c->maybeGlobal();
 
             if (cx->runtime()->isSelfHostingGlobal(global))
                 continue;
@@ -4296,17 +4296,17 @@ Debugger::makeGlobalObjectReference(JSCo
     Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
     if (!global)
         return false;
 
     // If we create a D.O referring to a global in an invisible compartment,
     // then from it we can reach function objects, scripts, environments, etc.,
     // none of which we're ever supposed to see.
     JSCompartment* globalCompartment = global->compartment();
-    if (globalCompartment->options().invisibleToDebugger()) {
+    if (globalCompartment->creationOptions().invisibleToDebugger()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                              JSMSG_DEBUG_INVISIBLE_COMPARTMENT);
         return false;
     }
 
     args.rval().setObject(*global);
     return dbg->wrapDebuggeeValue(cx, args.rval());
 }
@@ -7797,17 +7797,17 @@ DebuggerObject_unwrap(JSContext* cx, uns
         args.rval().setNull();
         return true;
     }
 
     // Don't allow unwrapping to create a D.O whose referent is in an
     // invisible-to-Debugger global. (If our referent is a *wrapper* to such,
     // and the wrapper is in a visible compartment, that's fine.)
     JSCompartment* unwrappedCompartment = unwrapped->compartment();
-    if (unwrappedCompartment->options().invisibleToDebugger()) {
+    if (unwrappedCompartment->creationOptions().invisibleToDebugger()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                              JSMSG_DEBUG_INVISIBLE_COMPARTMENT);
         return false;
     }
 
     args.rval().setObject(*unwrapped);
     if (!dbg->wrapDebuggeeValue(cx, args.rval()))
         return false;
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -112,18 +112,19 @@ class DebuggerWeakMap : private WeakMap<
 
     bool init(uint32_t len = 16) {
         return Base::init(len) && zoneCounts.init();
     }
 
     template<typename KeyInput, typename ValueInput>
     bool relookupOrAdd(AddPtr& p, const KeyInput& k, const ValueInput& v) {
         MOZ_ASSERT(v->compartment() == this->compartment);
-        MOZ_ASSERT(!k->compartment()->options_.mergeable());
-        MOZ_ASSERT_IF(!InvisibleKeysOk, !k->compartment()->options_.invisibleToDebugger());
+        MOZ_ASSERT(!k->compartment()->creationOptions().mergeable());
+        MOZ_ASSERT_IF(!InvisibleKeysOk,
+                      !k->compartment()->creationOptions().invisibleToDebugger());
         MOZ_ASSERT(!Base::has(k));
         if (!incZoneCount(k->zone()))
             return false;
         bool ok = Base::relookupOrAdd(p, k, v);
         if (!ok)
             decZoneCount(k->zone());
         return ok;
     }
@@ -1076,17 +1077,17 @@ Debugger::observesGlobal(GlobalObject* g
     return debuggees.has(debuggee);
 }
 
 /* static */ void
 Debugger::onNewScript(JSContext* cx, HandleScript script)
 {
     // We early return in slowPathOnNewScript for self-hosted scripts, so we can
     // ignore those in our assertion here.
-    MOZ_ASSERT_IF(!script->compartment()->options().invisibleToDebugger() &&
+    MOZ_ASSERT_IF(!script->compartment()->creationOptions().invisibleToDebugger() &&
                   !script->selfHosted(),
                   script->compartment()->firedOnNewGlobalObject);
     if (script->compartment()->isDebuggee())
         slowPathOnNewScript(cx, script);
 }
 
 /* static */ void
 Debugger::onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -275,30 +275,31 @@ GlobalObject::new_(JSContext* cx, const 
                    JS::OnNewGlobalHookOption hookOption,
                    const JS::CompartmentOptions& options)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
 
     JSRuntime* rt = cx->runtime();
 
+    auto zoneSpecifier = options.creationOptions().zoneSpecifier();
     Zone* zone;
-    if (options.zoneSpecifier() == JS::SystemZone)
+    if (zoneSpecifier == JS::SystemZone)
         zone = rt->gc.systemZone;
-    else if (options.zoneSpecifier() == JS::FreshZone)
+    else if (zoneSpecifier == JS::FreshZone)
         zone = nullptr;
     else
-        zone = static_cast<Zone*>(options.zonePointer());
+        zone = static_cast<Zone*>(options.creationOptions().zonePointer());
 
     JSCompartment* compartment = NewCompartment(cx, zone, principals, options);
     if (!compartment)
         return nullptr;
 
     // Lazily create the system zone.
-    if (!rt->gc.systemZone && options.zoneSpecifier() == JS::SystemZone) {
+    if (!rt->gc.systemZone && zoneSpecifier == JS::SystemZone) {
         rt->gc.systemZone = compartment->zone();
         rt->gc.systemZone->isSystem = true;
     }
 
     Rooted<GlobalObject*> global(cx);
     {
         AutoCompartment ac(cx, compartment);
         global = GlobalObject::createInternal(cx, clasp);
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -197,17 +197,18 @@ static const JSClass parseTaskGlobalClas
 
 ParseTask::ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal, JSContext* initCx,
                      const char16_t* chars, size_t length,
                      JS::OffThreadCompileCallback callback, void* callbackData)
   : cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     exclusiveContextGlobal(initCx->runtime(), exclusiveContextGlobal),
     callback(callback), callbackData(callbackData),
-    script(nullptr), sourceObject(nullptr), errors(cx), overRecursed(false)
+    script(initCx->runtime()), sourceObject(initCx->runtime()),
+    errors(cx), overRecursed(false)
 {
 }
 
 bool
 ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
 {
     if (!this->options.copy(cx, options))
         return false;
@@ -344,30 +345,36 @@ bool
 js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
                               const char16_t* chars, size_t length,
                               JS::OffThreadCompileCallback callback, void* callbackData)
 {
     // Suppress GC so that calls below do not trigger a new incremental GC
     // which could require barriers on the atoms compartment.
     gc::AutoSuppressGC suppress(cx);
 
-    JS::CompartmentOptions compartmentOptions(cx->compartment()->options());
-    compartmentOptions.setZone(JS::FreshZone);
-    compartmentOptions.setInvisibleToDebugger(true);
-    compartmentOptions.setMergeable(true);
+    JSCompartment* currentCompartment = cx->compartment();
+
+    JS::CompartmentOptions compartmentOptions(currentCompartment->creationOptions(),
+                                              currentCompartment->behaviors());
+
+    auto& creationOptions = compartmentOptions.creationOptions();
+
+    creationOptions.setInvisibleToDebugger(true)
+                   .setMergeable(true)
+                   .setZone(JS::FreshZone);
 
     // Don't falsely inherit the host's global trace hook.
-    compartmentOptions.setTrace(nullptr);
+    creationOptions.setTrace(nullptr);
 
     JSObject* global = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
                                           JS::FireOnNewGlobalHook, compartmentOptions);
     if (!global)
         return false;
 
-    JS_SetCompartmentPrincipals(global->compartment(), cx->compartment()->principals());
+    JS_SetCompartmentPrincipals(global->compartment(), currentCompartment->principals());
 
     // Initialize all classes required for parsing while still on the main
     // thread, for both the target and the new global so that prototype
     // pointers can be changed infallibly after parsing finishes.
     if (!EnsureParserCreatedClasses(cx))
         return false;
     {
         AutoCompartment ac(cx, global);
@@ -1379,17 +1386,17 @@ HelperThread::handleParseWorkload()
         ExclusiveContext* parseCx = task->cx;
         Rooted<ClonedBlockObject*> globalLexical(parseCx, &parseCx->global()->lexicalScope());
         Rooted<ScopeObject*> staticScope(parseCx, &globalLexical->staticBlock());
         task->script = frontend::CompileScript(parseCx, &task->alloc,
                                                globalLexical, staticScope, nullptr,
                                                task->options, srcBuf,
                                                /* source_ = */ nullptr,
                                                /* extraSct = */ nullptr,
-                                               /* sourceObjectOut = */ &(task->sourceObject));
+                                               /* sourceObjectOut = */ task->sourceObject.address());
     }
 
     // The callback is invoked while we are still off the main thread.
     task->callback(task, task->callbackData);
 
     // FinishOffThreadScript will need to be called on the script to
     // migrate it into the correct compartment.
     {
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -474,20 +474,20 @@ struct ParseTask
 
     // Callback invoked off the main thread when the parse finishes.
     JS::OffThreadCompileCallback callback;
     void* callbackData;
 
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
-    JSScript* script;
+    PersistentRootedScript script;
 
     // Holds the ScriptSourceObject generated for the script compilation.
-    ScriptSourceObject* sourceObject;
+    PersistentRooted<ScriptSourceObject*> sourceObject;
 
     // Any errors or warnings produced during compilation. These are reported
     // when finishing the script.
     Vector<frontend::CompileError*> errors;
     bool overRecursed;
 
     ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
               JSContext* initCx, const char16_t* chars, size_t length,
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -2983,23 +2983,23 @@ END_CASE(JSOP_TOSTRING)
 
 CASE(JSOP_SYMBOL)
     PUSH_SYMBOL(cx->wellKnownSymbols().get(GET_UINT8(REGS.pc)));
 END_CASE(JSOP_SYMBOL)
 
 CASE(JSOP_OBJECT)
 {
     ReservedRooted<JSObject*> ref(&rootObject0, script->getObject(REGS.pc));
-    if (JS::CompartmentOptionsRef(cx).cloneSingletons()) {
+    if (cx->compartment()->creationOptions().cloneSingletons()) {
         JSObject* obj = DeepCloneObjectLiteral(cx, ref, TenuredObject);
         if (!obj)
             goto error;
         PUSH_OBJECT(*obj);
     } else {
-        JS::CompartmentOptionsRef(cx).setSingletonsAsValues();
+        cx->compartment()->behaviors().setSingletonsAsValues();
         PUSH_OBJECT(*ref);
     }
 }
 END_CASE(JSOP_OBJECT)
 
 CASE(JSOP_CALLSITEOBJ)
 {
 
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1817,17 +1817,17 @@ GetNonexistentProperty(JSContext* cx, Ha
     if (nameLookup)
         return ReportIsNotDefined(cx, id);
 
     // Give a strict warning if foo.bar is evaluated by a script for an object
     // foo with no property named 'bar'.
     //
     // Don't warn if extra warnings not enabled or for random getprop
     // operations.
-    if (!cx->compartment()->options().extraWarnings(cx))
+    if (!cx->compartment()->behaviors().extraWarnings(cx))
         return true;
 
     jsbytecode* pc;
     RootedScript script(cx, cx->currentScript(&pc));
     if (!script)
         return true;
 
     if (*pc != JSOP_GETPROP && *pc != JSOP_GETELEM)
@@ -1996,17 +1996,17 @@ MaybeReportUndeclaredVarAssignment(JSCon
         JSScript* script = cx->currentScript(&pc, JSContext::ALLOW_CROSS_COMPARTMENT);
         if (!script)
             return true;
 
         // If the code is not strict and extra warnings aren't enabled, then no
         // check is needed.
         if (IsStrictSetPC(pc))
             flags = JSREPORT_ERROR;
-        else if (cx->compartment()->options().extraWarnings(cx))
+        else if (cx->compartment()->behaviors().extraWarnings(cx))
             flags = JSREPORT_WARNING | JSREPORT_STRICT;
         else
             return true;
     }
 
     JSAutoByteString bytes(cx, propname);
     return !!bytes &&
            JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr,
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -197,17 +197,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     canUseSignalHandlers_(false),
     defaultFreeOp_(thisFromCtor()),
     debuggerMutations(0),
     securityCallbacks(const_cast<JSSecurityCallbacks*>(&NullSecurityCallbacks)),
     DOMcallbacks(nullptr),
     destroyPrincipals(nullptr),
     readPrincipals(nullptr),
     errorReporter(nullptr),
-    linkedWasmModules(nullptr),
     propertyRemovals(0),
 #if !EXPOSE_INTL_API
     thousandsSeparator(0),
     decimalSeparator(0),
     numGrouping(0),
 #endif
     mathCache_(nullptr),
     activeCompilations_(0),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1185,19 +1185,16 @@ struct JSRuntime : public JS::shadow::Ru
     JSReadPrincipalsOp readPrincipals;
 
     /* Optional error reporter. */
     JSErrorReporter     errorReporter;
 
     /* AsmJSCache callbacks are runtime-wide. */
     JS::AsmJSCacheOps   asmJSCacheOps;
 
-    /* Head of the linked list of linked wasm modules. */
-    js::wasm::Module*   linkedWasmModules;
-
     /*
      * The propertyRemovals counter is incremented for every JSObject::clear,
      * and for each JSObject::remove method call that frees a slot in the given
      * object. See js_NativeGet and js_NativeSet in jsobj.cpp.
      */
     uint32_t            propertyRemovals;
 
 #if !EXPOSE_INTL_API
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1714,18 +1714,18 @@ js::FillSelfHostingCompileOptions(Compil
 
 GlobalObject*
 JSRuntime::createSelfHostingGlobal(JSContext* cx)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
 
     JS::CompartmentOptions options;
-    options.setDiscardSource(true);
-    options.setZone(JS::FreshZone);
+    options.creationOptions().setZone(JS::FreshZone);
+    options.behaviors().setDiscardSource(true);
 
     JSCompartment* compartment = NewCompartment(cx, nullptr, nullptr, options);
     if (!compartment)
         return nullptr;
 
     static const Class shgClass = {
         "self-hosting-global", JSCLASS_GLOBAL_FLAGS,
         nullptr, nullptr, nullptr, nullptr,
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -537,20 +537,23 @@ mozJSComponentLoader::PrepareObjectForLo
     bool createdNewGlobal = false;
 
     if (!mLoaderGlobal) {
         RefPtr<BackstagePass> backstagePass;
         rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
         NS_ENSURE_SUCCESS(rv, nullptr);
 
         CompartmentOptions options;
-        options.setZone(SystemZone)
-               .setVersion(JSVERSION_LATEST)
+
+        options.creationOptions()
+               .setZone(SystemZone)
                .setAddonId(aReuseLoaderGlobal ? nullptr : MapURIToAddonID(aURI));
 
+        options.behaviors().setVersion(JSVERSION_LATEST);
+
         // Defer firing OnNewGlobalObject until after the __URI__ property has
         // been defined so the JS debugger can tell what module the global is
         // for
         rv = xpc->InitClassesWithNewWrappedGlobal(aCx,
                                                   static_cast<nsIGlobalObject*>(backstagePass),
                                                   mSystemPrincipal,
                                                   nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK,
                                                   options,
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -983,40 +983,44 @@ xpc::CreateSandboxObject(JSContext* cx, 
             RefPtr<nsNullPrincipal> nullPrin = nsNullPrincipal::Create();
             NS_ENSURE_TRUE(nullPrin, NS_ERROR_FAILURE);
             principal = nullPrin;
         }
     }
     MOZ_ASSERT(principal);
 
     JS::CompartmentOptions compartmentOptions;
+
+    auto& creationOptions = compartmentOptions.creationOptions();
+
     if (options.sameZoneAs)
-        compartmentOptions.setSameZoneAs(js::UncheckedUnwrap(options.sameZoneAs));
+        creationOptions.setSameZoneAs(js::UncheckedUnwrap(options.sameZoneAs));
     else if (options.freshZone)
-        compartmentOptions.setZone(JS::FreshZone);
+        creationOptions.setZone(JS::FreshZone);
     else
-        compartmentOptions.setZone(JS::SystemZone);
+        creationOptions.setZone(JS::SystemZone);
 
-    compartmentOptions.setInvisibleToDebugger(options.invisibleToDebugger)
-                      .setDiscardSource(options.discardSource)
-                      .setTrace(TraceXPCGlobal);
+    creationOptions.setInvisibleToDebugger(options.invisibleToDebugger)
+                   .setTrace(TraceXPCGlobal);
 
     // Try to figure out any addon this sandbox should be associated with.
     // The addon could have been passed in directly, as part of the metadata,
     // or by being constructed from an addon's code.
     JSAddonId* addonId = nullptr;
     if (options.addonId) {
         addonId = JS::NewAddonId(cx, options.addonId);
         NS_ENSURE_TRUE(addonId, NS_ERROR_FAILURE);
     } else if (JSObject* obj = JS::CurrentGlobalOrNull(cx)) {
         if (JSAddonId* id = JS::AddonIdOfObject(obj))
             addonId = id;
     }
 
-    compartmentOptions.setAddonId(addonId);
+    creationOptions.setAddonId(addonId);
+
+    compartmentOptions.behaviors().setDiscardSource(options.discardSource);
 
     const Class* clasp = options.writeToGlobalPrototype
                        ? &SandboxWriteToProtoClass
                        : &SandboxClass;
 
     RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp),
                                                      principal, compartmentOptions));
     if (!sandbox)
--- a/js/xpconnect/src/XPCLocale.cpp
+++ b/js/xpconnect/src/XPCLocale.cpp
@@ -12,16 +12,17 @@
 #include "nsJSUtils.h"
 #include "nsIPlatformCharset.h"
 #include "nsILocaleService.h"
 #include "nsICollation.h"
 #include "nsUnicharUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/Preferences.h"
 #include "nsIUnicodeDecoder.h"
 
 #include "xpcpublic.h"
 
 using namespace JS;
 using mozilla::dom::EncodingUtils;
 
 /**
@@ -247,16 +248,25 @@ private:
 };
 
 bool
 xpc_LocalizeRuntime(JSRuntime* rt)
 {
   JS_SetLocaleCallbacks(rt, new XPCLocaleCallbacks());
 
   // Set the default locale.
+
+  // Check a pref to see if we should use US English locale regardless
+  // of the system locale.
+  if (Preferences::GetBool("javascript.use_us_english_locale", false)) {
+    return JS_SetDefaultLocale(rt, "en-US");
+  }
+
+  // No pref has been found, so get the default locale from the
+  // application's locale.
   nsCOMPtr<nsILocaleService> localeService =
     do_GetService(NS_LOCALESERVICE_CONTRACTID);
   if (!localeService)
     return false;
 
   nsCOMPtr<nsILocale> appLocale;
   nsresult rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
   if (NS_FAILED(rv))
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -114,22 +114,21 @@ JSObject2WrappedJSMap::UpdateWeakPointer
                 if (!wrapper->GetJSObjectPreserveColor())
                     dying.AppendElement(dont_AddRef(wrapper));
             }
             wrapper = wrapper->GetNextWrapper();
         }
 
         // Remove or update the JSObject key in the table if necessary.
         JSObject* obj = e.front().key();
-        JSObject* prior = obj;
         JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
         if (!obj)
             e.removeFront();
-        else if (obj != prior)
-            e.rekeyFront(obj);
+        else
+            e.front().mutableKey() = obj;
     }
 }
 
 void
 JSObject2WrappedJSMap::ShutdownMarker()
 {
     for (Map::Range r = mTable.all(); !r.empty(); r.popFront()) {
         nsXPCWrappedJS* wrapper = r.front().value();
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -22,18 +22,20 @@
 
 // no virtuals in the maps - all the common stuff inlined
 // templates could be used to good effect here.
 
 /*************************/
 
 class JSObject2WrappedJSMap
 {
-    typedef js::HashMap<JSObject*, nsXPCWrappedJS*, js::PointerHasher<JSObject*, 3>,
-                        js::SystemAllocPolicy> Map;
+    using Map = js::HashMap<JS::Heap<JSObject*>,
+                            nsXPCWrappedJS*,
+                            js::MovableCellHasher<JS::Heap<JSObject*>>,
+                            js::SystemAllocPolicy>;
 
 public:
     static JSObject2WrappedJSMap* newMap(int length) {
         JSObject2WrappedJSMap* map = new JSObject2WrappedJSMap();
         if (!map->mTable.init(length)) {
             // This is a decent estimate of the size of the hash table's
             // entry storage. The |2| is because on average the capacity is
             // twice the requested length.
@@ -61,17 +63,16 @@ public:
     inline nsXPCWrappedJS* Add(JSContext* cx, nsXPCWrappedJS* wrapper) {
         NS_PRECONDITION(wrapper,"bad param");
         JSObject* obj = wrapper->GetJSObjectPreserveColor();
         Map::AddPtr p = mTable.lookupForAdd(obj);
         if (p)
             return p->value();
         if (!mTable.add(p, obj, wrapper))
             return nullptr;
-        JS_StoreObjectPostBarrierCallback(cx, KeyMarkCallback, obj, this);
         return wrapper;
     }
 
     inline void Remove(nsXPCWrappedJS* wrapper) {
         NS_PRECONDITION(wrapper,"bad param");
         mTable.remove(wrapper->GetJSObjectPreserveColor());
     }
 
@@ -90,27 +91,16 @@ public:
 
     // Report the sum of SizeOfIncludingThis() for all wrapped JS in the map.
     // Each wrapped JS is only in one map.
     size_t SizeOfWrappedJS(mozilla::MallocSizeOf mallocSizeOf) const;
 
 private:
     JSObject2WrappedJSMap() {}
 
-    /*
-     * This function is called during minor GCs for each key in the HashMap that
-     * has been moved.
-     */
-    static void KeyMarkCallback(JSTracer* trc, JSObject* key, void* data) {
-        JSObject2WrappedJSMap* self = static_cast<JSObject2WrappedJSMap*>(data);
-        JSObject* prior = key;
-        JS_CallUnbarrieredObjectTracer(trc, &key, "XPCJSRuntime::mWrappedJSMap key");
-        self->mTable.rekeyIfMoved(prior, key);
-    }
-
     Map mTable;
 };
 
 /*************************/
 
 class Native2WrappedNativeMap
 {
 public:
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -1473,18 +1473,18 @@ XRE_XPCShellMain(int argc, char** argv, 
             fprintf(gErrFile, "+++ Failed to create BackstagePass: %8x\n",
                     static_cast<uint32_t>(rv));
             return 1;
         }
 
         // Make the default XPCShell global use a fresh zone (rather than the
         // System Zone) to improve cross-zone test coverage.
         JS::CompartmentOptions options;
-        options.setZone(JS::FreshZone)
-               .setVersion(JSVERSION_LATEST);
+        options.creationOptions().setZone(JS::FreshZone);
+        options.behaviors().setVersion(JSVERSION_LATEST);
         nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
         rv = xpc->InitClassesWithNewWrappedGlobal(cx,
                                                   static_cast<nsIGlobalObject*>(backstagePass),
                                                   systemprincipal,
                                                   0,
                                                   options,
                                                   getter_AddRefs(holder));
         if (NS_FAILED(rv))
@@ -1499,17 +1499,17 @@ XRE_XPCShellMain(int argc, char** argv, 
             JS::Rooted<JSObject*> glob(cx, holder->GetJSObject());
             if (!glob) {
                 return 1;
             }
 
             // Even if we're building in a configuration where source is
             // discarded, there's no reason to do that on XPCShell, and doing so
             // might break various automation scripts.
-            JS::CompartmentOptionsRef(glob).setDiscardSource(false);
+            JS::CompartmentBehaviorsRef(glob).setDiscardSource(false);
 
             backstagePass->SetGlobalObject(glob);
 
             JSAutoCompartment ac(cx, glob);
 
             if (!JS_InitReflectParse(cx, glob)) {
                 return 1;
             }
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -177,17 +177,17 @@ XPCWrappedNative::WrapNewGlobal(xpcObjec
     AutoMarkingNativeScriptableInfoPtr si(cx, XPCNativeScriptableInfo::Construct(&sciWrapper));
     MOZ_ASSERT(si.get());
 
     // Finally, we get to the JSClass.
     const JSClass* clasp = si->GetJSClass();
     MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
 
     // Create the global.
-    aOptions.setTrace(XPCWrappedNative::Trace);
+    aOptions.creationOptions().setTrace(XPCWrappedNative::Trace);
     RootedObject global(cx, xpc::CreateGlobalObject(cx, clasp, principal, aOptions));
     if (!global)
         return NS_ERROR_FAILURE;
     XPCWrappedNativeScope* scope = CompartmentPrivate::Get(global)->scope;
 
     // Immediately enter the global's compartment, so that everything else we
     // create ends up there.
     JSAutoCompartment ac(cx, global);
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -406,24 +406,24 @@ InitGlobalObject(JSContext* aJSContext, 
     if (ShouldDiscardSystemSource()) {
         nsIPrincipal* prin = GetObjectPrincipal(aGlobal);
         bool isSystem = nsContentUtils::IsSystemPrincipal(prin);
         if (!isSystem) {
             short status = prin->GetAppStatus();
             isSystem = status == nsIPrincipal::APP_STATUS_PRIVILEGED ||
                        status == nsIPrincipal::APP_STATUS_CERTIFIED;
         }
-        JS::CompartmentOptionsRef(aGlobal).setDiscardSource(isSystem);
+        JS::CompartmentBehaviorsRef(aGlobal).setDiscardSource(isSystem);
     }
 
     if (ExtraWarningsForSystemJS()) {
         nsIPrincipal* prin = GetObjectPrincipal(aGlobal);
         bool isSystem = nsContentUtils::IsSystemPrincipal(prin);
         if (isSystem)
-            JS::CompartmentOptionsRef(aGlobal).extraWarningsOverride().set(true);
+            JS::CompartmentBehaviorsRef(aGlobal).extraWarningsOverride().set(true);
     }
 
     // Stuff coming through this path always ends up as a DOM global.
     MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL);
 
     if (!(aFlags & nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK))
         JS_FireOnNewGlobalObject(aJSContext, aGlobal);
 
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1230,16 +1230,17 @@ RestyleManager::AttributeWillChange(Elem
                                     int32_t aNameSpaceID,
                                     nsIAtom* aAttribute,
                                     int32_t aModType,
                                     const nsAttrValue* aNewValue)
 {
   RestyleHintData rsdata;
   nsRestyleHint rshint =
     mPresContext->StyleSet()->HasAttributeDependentStyle(aElement,
+                                                         aNameSpaceID,
                                                          aAttribute,
                                                          aModType,
                                                          false,
                                                          aNewValue,
                                                          rsdata);
   PostRestyleEvent(aElement, rshint, NS_STYLE_HINT_NONE, &rsdata);
 }
 
@@ -1321,16 +1322,17 @@ RestyleManager::AttributeChanged(Element
     // this notification, so it's not that big a deal.
   }
 
   // See if we can optimize away the style re-resolution -- must be called after
   // the frame's AttributeChanged() in case it does something that affects the style
   RestyleHintData rsdata;
   nsRestyleHint rshint =
     mPresContext->StyleSet()->HasAttributeDependentStyle(aElement,
+                                                         aNameSpaceID,
                                                          aAttribute,
                                                          aModType,
                                                          true,
                                                          aOldValue,
                                                          rsdata);
   PostRestyleEvent(aElement, rshint, hint, &rsdata);
 }
 
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1235467-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="transform: translateY(50%);">
+<div style="transform-style: preserve-3d; background-image: -moz-element(#a); position: sticky;" id="a">Q</div>
+</div>
+</body>
+</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -472,8 +472,9 @@ load 1043163-1.html
 load 1061028.html
 load 1107508-1.html
 load 1116104.html
 load 1127198-1.html
 load 1140198.html
 pref(layout.css.grid.enabled,true) load 1156588.html
 load 1162813.xul
 load 1163583.html
+load 1235467-1.html
--- a/layout/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -185,17 +185,17 @@ PrintDisplayItemTo(nsDisplayListBuilder*
           (aItem->ZIndex() ? nsPrintfCString("z=%d ", aItem->ZIndex()).get() : ""),
           rect.x, rect.y, rect.width, rect.height,
           layerRect.x, layerRect.y, layerRect.width, layerRect.height,
           vis.x, vis.y, vis.width, vis.height,
           component.x, component.y, component.width, component.height,
           clip.ToString().get(),
           DisplayItemScrollClip::ToString(aItem->ScrollClip()).get(),
           aItem->IsUniform(aBuilder, &color) ? " uniform" : "",
-          aItem->ReferenceFrame(), aItem->GetAnimatedGeometryRoot());
+          aItem->ReferenceFrame(), aItem->GetAnimatedGeometryRoot()->mFrame);
 
   nsRegionRectIterator iter(opaque);
   for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
     aStream << nsPrintfCString(" (opaque %d,%d,%d,%d)", r->x, r->y, r->width, r->height);
   }
 
   if (aItem->ShouldFixToViewport(aBuilder)) {
     aStream << " fixed";
--- a/layout/base/tests/test_bug582181-1.html
+++ b/layout/base/tests/test_bug582181-1.html
@@ -10,17 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body onload="test()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=582181">Mozilla Bug 582181</a>
 <p id="display"></p>
 <div id="content" dir="rtl">
-<textarea rows="4" id="testInput">فارسی
+<textarea rows="4" style="resize: none" id="testInput">فارسی
 [[en:Farsi]]</textarea>
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 582181 **/
 
 SimpleTest.waitForExplicitFinish();
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2244,18 +2244,21 @@ nsIFrame::BuildDisplayListForStackingCon
       clipState.Restore();
     }
     // Revert to the dirtyrect coming in from the parent, without our transform
     // taken into account.
     buildingDisplayList.SetDirtyRect(dirtyRectOutsideTransform);
     // Revert to the outer reference frame and offset because all display
     // items we create from now on are outside the transform.
     nsPoint toOuterReferenceFrame;
-    const nsIFrame* outerReferenceFrame =
-      aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
+    const nsIFrame* outerReferenceFrame = this;
+    if (this != aBuilder->RootReferenceFrame()) {
+      outerReferenceFrame =
+        aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
+    }
     buildingDisplayList.SetReferenceFrameAndCurrentOffset(outerReferenceFrame,
       GetOffsetToCrossDoc(outerReferenceFrame));
 
     nsDisplayTransform *transformItem =
       new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList, dirtyRect);
     resultList.AppendNewToTop(transformItem);
 
     if (HasPerspective()) {
--- a/layout/reftests/svg/svg-integration/reftest.list
+++ b/layout/reftests/svg/svg-integration/reftest.list
@@ -3,18 +3,18 @@
 == clipPath-html-02.xhtml clipPath-html-02-ref.svg
 == clipPath-html-02-extref.xhtml clipPath-html-02-ref.svg
 == clipPath-html-03.xhtml clipPath-html-03-ref.svg
 == clipPath-html-03-extref.xhtml clipPath-html-03-ref.svg
 == clipPath-html-04.xhtml clipPath-html-04-ref.xhtml
 == clipPath-html-04-extref.xhtml clipPath-html-04-ref.xhtml
 fuzzy-if(true,140,70) == clipPath-html-05.xhtml clipPath-html-05-ref.xhtml # Bug 776089
 fuzzy-if(true,140,70) == clipPath-html-05-extref.xhtml clipPath-html-05-ref.xhtml # Bug 776089
-fuzzy-if(Android&&AndroidVersion==18,255,30) == clipPath-html-06.xhtml clipPath-html-06-ref.xhtml
-fuzzy-if(Android&&AndroidVersion==18,255,30) == clipPath-html-06-extref.xhtml clipPath-html-06-ref.xhtml
+fuzzy-if(Android,255,30) == clipPath-html-06.xhtml clipPath-html-06-ref.xhtml
+fuzzy-if(Android,255,30) == clipPath-html-06-extref.xhtml clipPath-html-06-ref.xhtml
 == clipPath-html-07.xhtml clipPath-html-07-ref.svg
 == clipPath-html-08.xhtml clipPath-html-07-ref.svg # reuse 07-ref.svg
 == clipPath-html-zoomed-01.xhtml clipPath-html-01-ref.svg
 == clipPath-transformed-html-01.xhtml ../pass.svg
 == clipPath-transformed-html-02.xhtml ../pass.svg
 == conditions-outer-svg-01.xhtml ../pass.svg
 == conditions-outer-svg-02.xhtml ../pass.svg
 == dynamic-conditions-outer-svg-01.xhtml ../pass.svg
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/arabic-final-ligature-spacing-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<style>
+p { font: 36px Geeza Pro, serif; }
+</style>
+
+<p>&#x633;&#x644;&#x627;&#x645;</p>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/arabic-final-ligature-spacing.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<style>
+p { font: 36px Geeza Pro, serif; }
+span { font-variant: small-caps; }
+</style>
+
+<!-- bug 739117: breaking the run after the lam-alef ligature
+     should NOT cause extra space to appear -->
+<p>&#x633;&#x644;&#x627;<span>&#x645;</span></p>
--- a/layout/reftests/text/reftest.list
+++ b/layout/reftests/text/reftest.list
@@ -160,16 +160,17 @@ random-if(!winWidget) == arial-bold-lam-
 # Fallback (presentation-forms) shaping with a font that lacks GSUB/GPOS
 # These tests are not valid with Mac or FT2 font backends because our masking of complex-script ranges
 # in the 'cmap' will prevent the test font (without GSUB) being used.
 skip-if(B2G||Mulet) fails-if(cocoaWidget||Android) HTTP(..) == arabic-fallback-1.html arabic-fallback-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 fails-if(cocoaWidget||Android||B2G) HTTP(..) == arabic-fallback-2.html arabic-fallback-2-ref.html
 fails-if(cocoaWidget||Android||B2G) HTTP(..) == arabic-fallback-3.html arabic-fallback-3-ref.html
 fails-if(!cocoaWidget&&!Android&&!B2G) HTTP(..) != arabic-fallback-4.html arabic-fallback-4-notref.html
 == arabic-marks-1.html arabic-marks-1-ref.html
+fails-if(OSX<1008) == arabic-final-ligature-spacing.html arabic-final-ligature-spacing-ref.html
 # harfbuzz fallback mark stacking in the absence of GPOS:
 HTTP(..) != fallback-mark-stacking-1.html fallback-mark-stacking-1-notref.html
 
 == 726392-1.html 726392-1-ref.html
 == 726392-2.html 726392-2-ref.html
 skip-if(B2G||Mulet) == 726392-3.html 726392-3-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == 745555-1.html 745555-1-ref.html
 == 745555-2.html 745555-2-ref.html
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -3005,17 +3005,18 @@ nsCSSRuleProcessor::HasAttributeDependen
         if (entry) {
           EnumerateSelectors(entry->mSelectors, &data);
         }
       }
 
       EnumerateSelectors(cascade->mPossiblyNegatedIDSelectors, &data);
     }
 
-    if (aData->mAttribute == nsGkAtoms::_class) {
+    if (aData->mAttribute == nsGkAtoms::_class &&
+        aData->mNameSpaceID == kNameSpaceID_None) {
       const nsAttrValue* otherClasses = aData->mOtherValue;
       NS_ASSERTION(otherClasses ||
                    aData->mModType == nsIDOMMutationEvent::REMOVAL,
                    "All class values should be StoresOwnData and parsed"
                    "via Element::BeforeSetAttr, so available here");
       // For WillChange, enumerate classes that will be removed to see which
       // rules apply before the change.
       // For Changed, enumerate classes that have been added to see which rules
--- a/layout/style/nsRuleProcessorData.h
+++ b/layout/style/nsRuleProcessorData.h
@@ -567,30 +567,33 @@ struct MOZ_STACK_CLASS PseudoElementStat
   nsCSSPseudoElements::Type mPseudoType;
   mozilla::dom::Element* const mPseudoElement; // weak ref
 };
 
 struct MOZ_STACK_CLASS AttributeRuleProcessorData :
                           public ElementDependentRuleProcessorData {
   AttributeRuleProcessorData(nsPresContext* aPresContext,
                              mozilla::dom::Element* aElement,
+                             int32_t aNameSpaceID,
                              nsIAtom* aAttribute,
                              int32_t aModType,
                              bool aAttrHasChanged,
                              const nsAttrValue* aOtherValue,
                              TreeMatchContext& aTreeMatchContext)
     : ElementDependentRuleProcessorData(aPresContext, aElement, nullptr,
                                         aTreeMatchContext),
+      mNameSpaceID(aNameSpaceID),
       mAttribute(aAttribute),
       mOtherValue(aOtherValue),
       mModType(aModType),
       mAttrHasChanged(aAttrHasChanged)
   {
     NS_PRECONDITION(!aTreeMatchContext.mForStyling, "Not styling here!");
   }
+  int32_t mNameSpaceID; // Namespace of the attribute involved.
   nsIAtom* mAttribute; // |HasAttributeDependentStyle| for which attribute?
   // non-null if we have the value.
   const nsAttrValue* mOtherValue;
   int32_t mModType;    // The type of modification (see nsIDOMMutationEvent).
   bool mAttrHasChanged; // Whether the attribute has already changed.
 };
 
 #endif /* !defined(nsRuleProcessorData_h_) */
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -2383,22 +2383,23 @@ nsStyleSet::HasStateDependentStyle(Eleme
   InitStyleScopes(treeContext, aElement);
   StatefulPseudoElementData data(PresContext(), aElement, aStateMask,
                                  aPseudoType, treeContext, aPseudoElement);
   WalkRuleProcessors(SheetHasStatefulPseudoElementStyle, &data, false);
   return data.mHint;
 }
 
 struct MOZ_STACK_CLASS AttributeData : public AttributeRuleProcessorData {
-  AttributeData(nsPresContext* aPresContext,
-                Element* aElement, nsIAtom* aAttribute, int32_t aModType,
+  AttributeData(nsPresContext* aPresContext, Element* aElement,
+                int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType,
                 bool aAttrHasChanged, const nsAttrValue* aOtherValue,
                 TreeMatchContext& aTreeMatchContext)
-    : AttributeRuleProcessorData(aPresContext, aElement, aAttribute, aModType,
-                                 aAttrHasChanged, aOtherValue, aTreeMatchContext),
+    : AttributeRuleProcessorData(aPresContext, aElement, aNameSpaceID,
+                                 aAttribute, aModType, aAttrHasChanged,
+                                 aOtherValue, aTreeMatchContext),
       mHint(nsRestyleHint(0))
   {}
   nsRestyleHint mHint;
   RestyleHintData mHintData;
 };
 
 static bool
 SheetHasAttributeStyle(nsIStyleRuleProcessor* aProcessor, void *aData)
@@ -2408,27 +2409,28 @@ SheetHasAttributeStyle(nsIStyleRuleProce
     aProcessor->HasAttributeDependentStyle(data, data->mHintData);
   data->mHint = nsRestyleHint(data->mHint | hint);
   return true; // continue
 }
 
 // Test if style is dependent on content state
 nsRestyleHint
 nsStyleSet::HasAttributeDependentStyle(Element*       aElement,
+                                       int32_t        aNameSpaceID,
                                        nsIAtom*       aAttribute,
                                        int32_t        aModType,
                                        bool           aAttrHasChanged,
                                        const nsAttrValue* aOtherValue,
                                        mozilla::RestyleHintData&
                                          aRestyleHintDataResult)
 {
   TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited,
                                aElement->OwnerDoc());
   InitStyleScopes(treeContext, aElement);
-  AttributeData data(PresContext(), aElement, aAttribute,
+  AttributeData data(PresContext(), aElement, aNameSpaceID, aAttribute,
                      aModType, aAttrHasChanged, aOtherValue, treeContext);
   WalkRuleProcessors(SheetHasAttributeStyle, &data, false);
   if (!(data.mHint & eRestyle_Subtree)) {
     // No point keeping the list of selectors around if we are going to
     // restyle the whole subtree unconditionally.
     aRestyleHintDataResult = Move(data.mHintData);
   }
   return data.mHint;
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -284,16 +284,17 @@ class nsStyleSet final
                                        mozilla::EventStates aStateMask);
   nsRestyleHint HasStateDependentStyle(mozilla::dom::Element* aElement,
                                        nsCSSPseudoElements::Type aPseudoType,
                                        mozilla::dom::Element* aPseudoElement,
                                        mozilla::EventStates aStateMask);
 
   // Test if style is dependent on the presence of an attribute.
   nsRestyleHint HasAttributeDependentStyle(mozilla::dom::Element* aElement,
+                                           int32_t        aNameSpaceID,
                                            nsIAtom*       aAttribute,
                                            int32_t        aModType,
                                            bool           aAttrHasChanged,
                                            const nsAttrValue* aOtherValue,
                                            mozilla::RestyleHintData&
                                              aRestyleHintDataResult);
 
   /*
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/1156581-1.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" style="filter: url(#a); clip: rect(0px, 4rem, 2px, 2px);">
+    <script>
+        function boom()
+        {
+            document.getElementById("a").style.overflow = "hidden";
+            document.documentElement.style.fontSize = "10px";
+        }
+        window.addEventListener("load", boom, false);
+    </script>
+
+    <set id="a"/>
+</svg>
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -187,12 +187,13 @@ load 974746-1.svg
 load 975773-1.svg
 load 979407-1.svg
 load 979407-2.svg
 load 993443.svg
 load 1016145.svg
 load 1028512.svg
 load 1140080-1.svg
 load 1149542-1.svg
+load 1156581-1.svg
 load 1182496-1.html
 load 1209525-1.svg
 load 1223281-1.svg
 load extref-test-1.xhtml
--- a/layout/svg/nsSVGEffects.cpp
+++ b/layout/svg/nsSVGEffects.cpp
@@ -765,24 +765,25 @@ nsSVGEffects::RemoveAllRenderingObserver
   }
 }
 
 void
 nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
 
-  if (!aFrame->GetContent()->IsElement())
+  nsIContent* content = aFrame->GetContent();
+  if (!content || !content->IsElement())
     return;
 
   // If the rendering has changed, the bounds may well have changed too:
   aFrame->Properties().Delete(nsSVGUtils::ObjectBoundingBoxProperty());
 
   nsSVGRenderingObserverList *observerList =
-    GetObserverList(aFrame->GetContent()->AsElement());
+    GetObserverList(content->AsElement());
   if (observerList) {
     observerList->InvalidateAll();
     return;
   }
 
   // Check ancestor SVG containers. The root frame cannot be of type
   // eSVGContainer so we don't have to check f for null here.
   for (nsIFrame *f = aFrame->GetParent();
@@ -816,12 +817,13 @@ nsSVGEffects::InvalidateDirectRenderingO
       }
     }
   }
 }
 
 void
 nsSVGEffects::InvalidateDirectRenderingObservers(nsIFrame *aFrame, uint32_t aFlags /* = 0 */)
 {
-  if (aFrame->GetContent() && aFrame->GetContent()->IsElement()) {
-    InvalidateDirectRenderingObservers(aFrame->GetContent()->AsElement(), aFlags);
+  nsIContent* content = aFrame->GetContent();
+  if (content && content->IsElement()) {
+    InvalidateDirectRenderingObservers(content->AsElement(), aFlags);
   }
 }
--- a/media/webrtc/trunk/webrtc/common_types.cc
+++ b/media/webrtc/trunk/webrtc/common_types.cc
@@ -25,17 +25,19 @@ RTPHeaderExtension::RTPHeaderExtension()
       transmissionTimeOffset(0),
       hasAbsoluteSendTime(false),
       absoluteSendTime(0),
       hasTransportSequenceNumber(false),
       transportSequenceNumber(0),
       hasAudioLevel(false),
       audioLevel(0),
       hasVideoRotation(false),
-      videoRotation(0) {
+      videoRotation(0),
+      hasRID(false),
+      rid(NULL) {
 }
 
 RTPHeader::RTPHeader()
     : markerBit(false),
       payloadType(0),
       sequenceNumber(0),
       timestamp(0),
       ssrc(0),
--- a/media/webrtc/trunk/webrtc/common_types.h
+++ b/media/webrtc/trunk/webrtc/common_types.h
@@ -567,16 +567,17 @@ enum VideoReceiveState
   kReceiveStateNoIncoming,         // No errors, but no incoming video since last decode
 };
 
 // Video codec
 enum { kConfigParameterSize = 128};
 enum { kPayloadNameSize = 32};
 enum { kMaxSimulcastStreams = 4};
 enum { kMaxTemporalStreams = 4};
+enum { kRIDSize = 32};
 
 enum VideoCodecComplexity
 {
     kComplexityNormal = 0,
     kComplexityHigh    = 1,
     kComplexityHigher  = 2,
     kComplexityMax     = 3
 };
@@ -680,25 +681,27 @@ union VideoCodecUnion {
 struct SimulcastStream {
   unsigned short      width;
   unsigned short      height;
   unsigned char       numberOfTemporalLayers;
   unsigned int        maxBitrate;  // kilobits/sec.
   unsigned int        targetBitrate;  // kilobits/sec.
   unsigned int        minBitrate;  // kilobits/sec.
   unsigned int        qpMax; // minimum quality
+  char                rid[kRIDSize];
 
   bool operator==(const SimulcastStream& other) const {
     return width == other.width &&
            height == other.height &&
            numberOfTemporalLayers == other.numberOfTemporalLayers &&
            maxBitrate == other.maxBitrate &&
            targetBitrate == other.targetBitrate &&
            minBitrate == other.minBitrate &&
-           qpMax == other.qpMax;
+           qpMax == other.qpMax &&
+           strcmp(rid, other.rid) == 0;
   }
 
   bool operator!=(const SimulcastStream& other) const {
     return !(*this == other);
   }
 };
 
 enum VideoCodecMode {
@@ -723,16 +726,17 @@ struct VideoCodec {
   unsigned int        targetBitrate;  // kilobits/sec.
 
   unsigned char       maxFramerate;
 
   VideoCodecUnion     codecSpecific;
 
   unsigned int        qpMax;
   unsigned char       numberOfSimulcastStreams;
+  unsigned char       ridId;
   SimulcastStream     simulcastStream[kMaxSimulcastStreams];
 
   VideoCodecMode      mode;
 
   // When using an external encoder/decoder this allows to pass
   // extra options without requiring webrtc to be aware of them.
   Config*  extra_options;
 
@@ -843,16 +847,20 @@ struct RTPHeaderExtension {
   bool hasAudioLevel;
   uint8_t audioLevel;
 
   // For Coordination of Video Orientation. See
   // http://www.etsi.org/deliver/etsi_ts/126100_126199/126114/12.07.00_60/
   // ts_126114v120700p.pdf
   bool hasVideoRotation;
   uint8_t videoRotation;
+
+  // RID values for simulcast; see draft-roach-avtext-rid
+  bool hasRID;
+  char *rid; // UTF8 string
 };
 
 struct RTPHeader {
   RTPHeader();
 
   bool markerBit;
   uint8_t payloadType;
   uint16_t sequenceNumber;
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_receiver.h
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_receiver.h
@@ -90,14 +90,16 @@ class RtpReceiver {
   virtual bool LastReceivedTimeMs(int64_t* receive_time_ms) const = 0;
 
   // Returns the remote SSRC of the currently received RTP stream.
   virtual uint32_t SSRC() const = 0;
 
   // Returns the current remote CSRCs.
   virtual int32_t CSRCs(uint32_t array_of_csrc[kRtpCsrcSize]) const = 0;
 
+  virtual void GetRID(char rid[256]) const = 0;
+
   // Returns the current energy of the RTP stream received.
   virtual int32_t Energy(uint8_t array_of_energy[kRtpCsrcSize]) const = 0;
 };
 }  // namespace webrtc
 
 #endif  // WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_RECEIVER_H_
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -213,16 +213,21 @@ class RtpRtcp : public Module {
     /*
     *   Set CSRC
     *
     *   csrcs   - vector of CSRCs
     */
     virtual void SetCsrcs(const std::vector<uint32_t>& csrcs) = 0;
 
     /*
+    *   Set RID value for the RID header extension or RTCP SDES
+    */
+    virtual int32_t SetRID(const char *rid) = 0;
+
+    /*
     * Turn on/off sending RTX (RFC 4588). The modes can be set as a combination
     * of values of the enumerator RtxMode.
     */
     virtual void SetRtxSendStatus(int modes) = 0;
 
     /*
     * Get status of sending RTX (RFC 4588). The returned value can be
     * a combination of values of the enumerator RtxMode.
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
@@ -76,16 +76,17 @@ enum StorageType {
 
 enum RTPExtensionType {
   kRtpExtensionNone,
   kRtpExtensionTransmissionTimeOffset,
   kRtpExtensionAudioLevel,
   kRtpExtensionAbsoluteSendTime,
   kRtpExtensionVideoRotation,
   kRtpExtensionTransportSequenceNumber,
+  kRtpExtensionRID,
 };
 
 enum RTCPAppSubTypes
 {
     kAppSubtypeBwe     = 0x00
 };
 
 enum RTCPPacketType
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h
@@ -21,16 +21,18 @@ namespace webrtc {
 const uint16_t kRtpOneByteHeaderExtensionId = 0xBEDE;
 
 const size_t kRtpOneByteHeaderLength = 4;
 const size_t kTransmissionTimeOffsetLength = 4;
 const size_t kAudioLevelLength = 2;
 const size_t kAbsoluteSendTimeLength = 4;
 const size_t kVideoRotationLength = 2;
 const size_t kTransportSequenceNumberLength = 3;
+// kRIDLength is variable
+const size_t kRIDLength = 4; // max 1-byte header extension length
 
 struct HeaderExtension {
   HeaderExtension(RTPExtensionType extension_type)
       : type(extension_type), length(0), active(true) {
     Init();
   }
 
   HeaderExtension(RTPExtensionType extension_type, bool active)
@@ -53,16 +55,19 @@ struct HeaderExtension {
         length = kAbsoluteSendTimeLength;
         break;
       case kRtpExtensionVideoRotation:
         length = kVideoRotationLength;
         break;
       case kRtpExtensionTransportSequenceNumber:
         length = kTransportSequenceNumberLength;
         break;
+      case kRtpExtensionRID:
+        length = kRIDLength;
+        break;
       default:
         assert(false);
     }
   }
 
   const RTPExtensionType type;
   uint8_t length;
   bool active;
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.cc
@@ -76,16 +76,17 @@ RtpReceiverImpl::RtpReceiverImpl(int32_t
       last_receive_time_(0),
       last_received_payload_length_(0),
       ssrc_(0),
       num_csrcs_(0),
       current_remote_csrc_(),
       last_received_timestamp_(0),
       last_received_frame_time_ms_(-1),
       last_received_sequence_number_(0),
+      rid_(NULL),
       nack_method_(kNackOff) {
   assert(incoming_audio_messages_callback);
   assert(incoming_messages_callback);
 
   memset(current_remote_csrc_, 0, sizeof(current_remote_csrc_));
 }
 
 RtpReceiverImpl::~RtpReceiverImpl() {
@@ -150,16 +151,25 @@ int32_t RtpReceiverImpl::CSRCs(uint32_t 
   assert(num_csrcs_ <= kRtpCsrcSize);
 
   if (num_csrcs_ > 0) {
     memcpy(array_of_csrcs, current_remote_csrc_, sizeof(uint32_t)*num_csrcs_);
   }
   return num_csrcs_;
 }
 
+void RtpReceiverImpl::GetRID(char rid[256]) const {
+  CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
+  if (rid_) {
+    strncpy(rid, rid_, 256);
+  } else {
+    rid[0] = '\0';
+  }
+}
+
 int32_t RtpReceiverImpl::Energy(
     uint8_t array_of_energy[kRtpCsrcSize]) const {
   return rtp_media_receiver_->Energy(array_of_energy);
 }
 
 bool RtpReceiverImpl::IncomingRtpPacket(
   const RTPHeader& rtp_header,
   const uint8_t* payload,
@@ -217,17 +227,23 @@ bool RtpReceiverImpl::IncomingRtpPacket(
     return false;
   }
 
   {
     CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
 
     last_receive_time_ = clock_->TimeInMilliseconds();
     last_received_payload_length_ = payload_data_length;
-
+    // RID rarely if ever changes
+    if (rtp_header.extension.hasRID &&
+        (!rid_ || strcmp(rtp_header.extension.rid, rid_) != 0)) {
+      delete [] rid_;
+      rid_ = new char[strlen(rtp_header.extension.rid)+1];
+      strcpy(rid_, rtp_header.extension.rid);
+    }
     if (in_order) {
       if (last_received_timestamp_ != rtp_header.timestamp) {
         last_received_timestamp_ = rtp_header.timestamp;
         last_received_frame_time_ms_ = clock_->TimeInMilliseconds();
       }
       last_received_sequence_number_ = rtp_header.sequenceNumber;
     }
   }
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.h
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_receiver_impl.h
@@ -56,16 +56,18 @@ class RtpReceiverImpl : public RtpReceiv
   // Returns the last received timestamp.
   bool Timestamp(uint32_t* timestamp) const override;
   bool LastReceivedTimeMs(int64_t* receive_time_ms) const override;
 
   uint32_t SSRC() const override;
 
   int32_t CSRCs(uint32_t array_of_csrc[kRtpCsrcSize]) const override;
 
+  void GetRID(char rid[256]) const override;
+
   int32_t Energy(uint8_t array_of_energy[kRtpCsrcSize]) const override;
 
   TelephoneEventHandler* GetTelephoneEventHandler() override;
 
  private:
   bool HaveReceivedFrame() const;
 
   void CheckSSRCChanged(const RTPHeader& rtp_header);
@@ -91,13 +93,14 @@ class RtpReceiverImpl : public RtpReceiv
   // SSRCs.
   uint32_t ssrc_;
   uint8_t num_csrcs_;
   uint32_t current_remote_csrc_[kRtpCsrcSize];
 
   uint32_t last_received_timestamp_;
   int64_t last_received_frame_time_ms_;
   uint16_t last_received_sequence_number_;
+  char *rid_; // scope
 
   NACKMethod nack_method_;
 };
 }  // namespace webrtc
 #endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_IMPL_H_
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -312,16 +312,21 @@ void ModuleRtpRtcpImpl::SetSSRC(const ui
   SetRtcpReceiverSsrcs(ssrc);
 }
 
 void ModuleRtpRtcpImpl::SetCsrcs(const std::vector<uint32_t>& csrcs) {
   rtcp_sender_.SetCsrcs(csrcs);
   rtp_sender_.SetCsrcs(csrcs);
 }
 
+int32_t ModuleRtpRtcpImpl::SetRID(const char *rid) {
+  //XXX rtcp_sender_.SetRID(rid);
+  return rtp_sender_.SetRID(rid);
+}
+
 // TODO(pbos): Handle media and RTX streams separately (separate RTCP
 // feedbacks).
 RTCPSender::FeedbackState ModuleRtpRtcpImpl::GetFeedbackState() {
   StreamDataCounters rtp_stats;
   StreamDataCounters rtx_stats;
   rtp_sender_.GetDataCounters(&rtp_stats, &rtx_stats);
 
   RTCPSender::FeedbackState state;
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -74,16 +74,18 @@ class ModuleRtpRtcpImpl : public RtpRtcp
 
   uint32_t SSRC() const override;
 
   // Configure SSRC, default is a random number.
   void SetSSRC(uint32_t ssrc) override;
 
   void SetCsrcs(const std::vector<uint32_t>& csrcs) override;
 
+  int32_t SetRID(const char *rid) override;
+
   RTCPSender::FeedbackState GetFeedbackState();
 
   int CurrentSendFrequencyHz() const;
 
   void SetRtxSendStatus(int mode) override;
   int RtxSendStatus() const override;
 
   void SetRtxSsrc(uint32_t ssrc) override;
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
@@ -126,16 +126,17 @@ RTPSender::RTPSender(int32_t id,
       payload_type_(-1),
       payload_type_map_(),
       rtp_header_extension_map_(),
       transmission_time_offset_(0),
       absolute_send_time_(0),
       rotation_(kVideoRotation_0),
       cvo_mode_(kCVONone),
       transport_sequence_number_(0),
+      rid_(NULL),
       // NACK.
       nack_byte_count_times_(),
       nack_byte_count_(),
       nack_bitrate_(clock, bitrates_->retransmit_bitrate_observer()),
       packet_history_(clock),
       // Statistics
       statistics_crit_(CriticalSectionWrapper::CreateCriticalSection()),
       rtp_stats_callback_(NULL),
@@ -259,16 +260,28 @@ void RTPSender::SetVideoRotation(VideoRo
 }
 
 int32_t RTPSender::SetTransportSequenceNumber(uint16_t sequence_number) {
   CriticalSectionScoped cs(send_critsect_.get());
   transport_sequence_number_ = sequence_number;
   return 0;
 }
 
+int32_t RTPSender::SetRID(const char* rid) {
+  CriticalSectionScoped cs(send_critsect_.get());
+  // TODO(jesup) avoid allocations
+  if (!rid_ || strlen(rid_) < strlen(rid)) {
+    // rid rarely changes length....
+    delete [] rid_;
+    rid_ = new char[strlen(rid)+1];
+  }
+  strcpy(rid_, rid);
+  return 0;
+}
+
 int32_t RTPSender::RegisterRtpHeaderExtension(RTPExtensionType type,
                                               uint8_t id) {
   CriticalSectionScoped cs(send_critsect_.get());
   if (type == kRtpExtensionVideoRotation) {
     cvo_mode_ = kCVOInactive;
     return rtp_header_extension_map_.RegisterInactive(type, id);
   }
   return rtp_header_extension_map_.Register(type, id);
@@ -1216,16 +1229,19 @@ uint16_t RTPSender::BuildRTPHeaderExtens
         block_length = BuildAbsoluteSendTimeExtension(extension_data);
         break;
       case kRtpExtensionVideoRotation:
         block_length = BuildVideoRotationExtension(extension_data);
         break;
       case kRtpExtensionTransportSequenceNumber:
         block_length = BuildTransportSequenceNumberExtension(extension_data);
         break;
+      case kRtpExtensionRID:
+        block_length = BuildRIDExtension(extension_data);
+        break;
       default:
         assert(false);
     }
     total_block_length += block_length;
     type = rtp_header_extension_map_.Next(type);
   }
   if (total_block_length == 0) {
     // No extension added.
@@ -1391,16 +1407,40 @@ uint8_t RTPSender::BuildTransportSequenc
   data_buffer[pos++] = (id << 4) + len;
   ByteWriter<uint16_t>::WriteBigEndian(data_buffer + pos,
                                        transport_sequence_number_);
   pos += 2;
   assert(pos == kTransportSequenceNumberLength);
   return kTransportSequenceNumberLength;
 }
 
+uint8_t RTPSender::BuildRIDExtension(
+    uint8_t* data_buffer) const {
+  //   0                   1                   2
+  //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  //  |  ID   | L=?   |UTF-8 RID value......          |...
+  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+  // Get id defined by user.
+  uint8_t id;
+  if (rtp_header_extension_map_.GetId(kRtpExtensionRID,
+                                      &id) != 0) {
+    // Not registered.
+    return 0;
+  }
+  size_t pos = 0;
+  // RID value is not null-terminated in header, so no +1
+  const uint8_t len = strlen(rid_);
+  data_buffer[pos++] = (id << 4) + len;
+  memcpy(data_buffer + pos, rid_, len);
+  pos += len;
+  return pos;
+}
+
 bool RTPSender::FindHeaderExtensionPosition(RTPExtensionType type,
                                             const uint8_t* rtp_packet,
                                             size_t rtp_packet_length,
                                             const RTPHeader& rtp_header,
                                             size_t* position) const {
   // Get length until start of header extension block.
   int extension_block_pos =
       rtp_header_extension_map_.GetLengthUntilBlockStartInBytes(type);
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.h
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender.h
@@ -158,30 +158,32 @@ class RTPSender : public RTPSenderInterf
                            VideoCodecInformation* codec_info = NULL,
                            const RTPVideoHeader* rtp_hdr = NULL);
 
   // RTP header extension
   int32_t SetTransmissionTimeOffset(int32_t transmission_time_offset);
   int32_t SetAbsoluteSendTime(uint32_t absolute_send_time);
   void SetVideoRotation(VideoRotation rotation);
   int32_t SetTransportSequenceNumber(uint16_t sequence_number);
+  int32_t SetRID(const char* rid);
 
   int32_t RegisterRtpHeaderExtension(RTPExtensionType type, uint8_t id);
   virtual bool IsRtpHeaderExtensionRegistered(RTPExtensionType type) override;
   int32_t DeregisterRtpHeaderExtension(RTPExtensionType type);
 
   size_t RtpHeaderExtensionTotalLength() const;
 
   uint16_t BuildRTPHeaderExtension(uint8_t* data_buffer, bool marker_bit) const;
 
   uint8_t BuildTransmissionTimeOffsetExtension(uint8_t *data_buffer) const;
   uint8_t BuildAudioLevelExtension(uint8_t* data_buffer) const;
   uint8_t BuildAbsoluteSendTimeExtension(uint8_t* data_buffer) const;
   uint8_t BuildVideoRotationExtension(uint8_t* data_buffer) const;
   uint8_t BuildTransportSequenceNumberExtension(uint8_t* data_buffer) const;
+  uint8_t BuildRIDExtension(uint8_t* data_buffer) const;
 
   bool UpdateAudioLevel(uint8_t* rtp_packet,
                         size_t rtp_packet_length,
                         const RTPHeader& rtp_header,
                         bool is_voiced,
                         uint8_t dBov) const;
 
   virtual bool UpdateVideoRotation(uint8_t* rtp_packet,
@@ -385,16 +387,17 @@ class RTPSender : public RTPSenderInterf
   std::map<int8_t, RtpUtility::Payload*> payload_type_map_;
 
   RtpHeaderExtensionMap rtp_header_extension_map_;
   int32_t transmission_time_offset_;
   uint32_t absolute_send_time_;
   VideoRotation rotation_;
   CVOMode cvo_mode_;
   uint16_t transport_sequence_number_;
+  char* rid_;
 
   // NACK
   uint32_t nack_byte_count_times_[NACK_BYTECOUNT_SIZE];
   size_t nack_byte_count_[NACK_BYTECOUNT_SIZE];
   Bitrate nack_bitrate_;
 
   RTPPacketHistory packet_history_;
 
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
@@ -286,26 +286,26 @@ bool RtpHeaderParser::Parse(RTPHeader& h
   header.ssrc           = SSRC;
   header.numCSRCs       = CC;
   header.paddingLength  = P ? *(_ptrRTPDataEnd - 1) : 0;
 
   // 12 == sizeof(RFC rtp header) == kRtpMinParseLength, each CSRC=4 bytes
   header.headerLength   = 12 + (CC * 4);
   // not a full validation, just safety against underflow.  Padding must
   // start after the header.  We can have 0 payload bytes left, note.
-  if (header.paddingLength + header.headerLength > length) {
+  if (header.paddingLength + header.headerLength > (ptrdiff_t) length) {
     return false;
   }
 
   for (uint8_t i = 0; i < CC; ++i) {
     uint32_t CSRC = ByteReader<uint32_t>::ReadBigEndian(ptr);
     ptr += 4;
     header.arrOfCSRCs[i] = CSRC;
   }
-  assert((ptr - _ptrRTPDataBegin) == header.headerLength);
+  assert((ptr - _ptrRTPDataBegin) == (ptrdiff_t) header.headerLength);
 
   // If in effect, MAY be omitted for those packets for which the offset
   // is zero.
   header.extension.hasTransmissionTimeOffset = false;
   header.extension.transmissionTimeOffset = 0;
 
   // May not be present in packet.
   header.extension.hasAbsoluteSendTime = false;
@@ -314,16 +314,20 @@ bool RtpHeaderParser::Parse(RTPHeader& h
   // May not be present in packet.
   header.extension.hasAudioLevel = false;
   header.extension.audioLevel = 0;
 
   // May not be present in packet.
   header.extension.hasVideoRotation = false;
   header.extension.videoRotation = 0;
 
+  // May not be present in packet.
+  header.extension.hasRID = false;
+  header.extension.rid = NULL;
+
   if (X) {
     /* RTP header extension, RFC 3550.
      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |      defined by profile       |           length              |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                        header extension                       |
@@ -478,16 +482,31 @@ void RtpHeaderParser::ParseOneByteExtens
           //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
           uint16_t sequence_number = ptr[0] << 8;
           sequence_number += ptr[1];
           header.extension.transportSequenceNumber = sequence_number;
           header.extension.hasTransportSequenceNumber = true;
           break;
         }
+        case kRtpExtensionRID: {
+          //   0                   1                   2
+          //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+          //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+          //  |  ID   | L=?   |UTF-8 RID value......          |...
+          //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+          // TODO(jesup) - avoid allocating on each packet - high watermark the RID buffer?
+          char* ptrRID = new char[len+1];
+          memcpy(ptrRID, ptr, len);
+          ptrRID[len] = '\0';
+          header.extension.rid = ptrRID;
+          header.extension.hasRID = true;
+          break;
+        }
         default: {
           LOG(LS_WARNING) << "Extension type not implemented: " << type;
           return;
         }
       }
     }
     ptr += (len + 1);
     uint8_t num_bytes = ParsePaddingBytes(ptrRTPDataExtensionEnd, ptr);
--- a/media/webrtc/trunk/webrtc/video_engine/include/vie_rtp_rtcp.h
+++ b/media/webrtc/trunk/webrtc/video_engine/include/vie_rtp_rtcp.h
@@ -108,16 +108,21 @@ class WEBRTC_DLLEXPORT ViERTP_RTCP {
   // channel.
   virtual int GetRemoteSSRC(const int video_channel,
                             unsigned int& SSRC) const = 0;
 
   // This function returns the CSRCs of the incoming RTP packets.
   virtual int GetRemoteCSRCs(const int video_channel,
                              unsigned int CSRCs[kRtpCsrcSize]) const = 0;
 
+  // This function gets the RID value (if any) for the incoming RTP stream
+  // for the specified channel.
+  virtual int GetRemoteRID(const int video_channel,
+                           char rid[256]) const = 0;
+
   // This sets a specific payload type for the RTX stream. Note that this
   // doesn't enable RTX, SetLocalSSRC must still be called to enable RTX.
   virtual int SetRtxSendPayloadType(const int video_channel,
                                     const uint8_t payload_type) = 0;
 
   virtual int SetRtxReceivePayloadType(const int video_channel,
                                        const uint8_t payload_type) = 0;
 
@@ -260,16 +265,25 @@ class WEBRTC_DLLEXPORT ViERTP_RTCP {
   virtual int SetSendVideoRotationStatus(int video_channel,
                                          bool enable,
                                          int id) = 0;
 
   virtual int SetReceiveVideoRotationStatus(int video_channel,
                                             bool enable,
                                             int id) = 0;
 
+  virtual int SetSendRIDStatus(int video_channel,
+                               bool enable,
+                               int id,
+                               const char *rid) = 0;
+
+  virtual int SetReceiveRIDStatus(int video_channel,
+                                  bool enable,
+                                  int id) = 0;
+
   // Enables/disables RTCP Receiver Reference Time Report Block extension/
   // DLRR Report Block extension (RFC 3611).
   virtual int SetRtcpXrRrtrStatus(int video_channel, bool enable) = 0;
 
   // Enables transmission smoothening, i.e. packets belonging to the same frame
   // will be sent over a longer period of time instead of sending them
   // back-to-back.
   virtual int SetTransmissionSmoothingStatus(int video_channel,
--- a/media/webrtc/trunk/webrtc/video_engine/vie_channel.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_channel.cc
@@ -118,16 +118,17 @@ ViEChannel::ViEChannel(int32_t channel_i
       intra_frame_observer_(intra_frame_observer),
       rtt_stats_(rtt_stats),
       paced_sender_(paced_sender),
       packet_router_(packet_router),
       bandwidth_observer_(bandwidth_observer),
       send_timestamp_extension_id_(kInvalidRtpExtensionId),
       absolute_send_time_extension_id_(kInvalidRtpExtensionId),
       video_rotation_extension_id_(kInvalidRtpExtensionId),
+      rid_extension_id_(kInvalidRtpExtensionId),
       external_transport_(NULL),
       decoder_reset_(true),
       wait_for_key_frame_(false),
       effect_filter_(NULL),
       color_enhancement_(false),
       mtu_(0),
       sender_(sender),
       disable_default_encoder_(disable_default_encoder),
@@ -365,20 +366,29 @@ int32_t ViEChannel::SetSendCodec(const V
   send_payload_router_->set_active(false);
   send_payload_router_->SetSendingRtpModules(std::list<RtpRtcp*>());
   packet_router_->RemoveRtpModule(rtp_rtcp_.get());
   for (RtpRtcp* module : simulcast_rtp_rtcp_)
     packet_router_->RemoveRtpModule(module);
   if (rtp_rtcp_->Sending() && new_stream) {
     restart_rtp = true;
     rtp_rtcp_->SetSendingStatus(false);
+    int i = 0;
     for (std::list<RtpRtcp*>::iterator it = simulcast_rtp_rtcp_.begin();
-         it != simulcast_rtp_rtcp_.end(); ++it) {
+         it != simulcast_rtp_rtcp_.end(); ++it, ++i) {
       (*it)->SetSendingStatus(false);
       (*it)->SetSendingMediaStatus(false);
+      if (video_codec.simulcastStream[i].rid[0] != 0) {
+        (*it)->RegisterSendRtpHeaderExtension(
+          kRtpExtensionRID, video_codec.ridId);
+        (*it)->SetRID(video_codec.simulcastStream[i].rid);
+      } else {
+        (*it)->DeregisterSendRtpHeaderExtension(
+          kRtpExtensionRID);
+      }
     }
   }
 
   bool fec_enabled = false;
   uint8_t payload_type_red;
   uint8_t payload_type_fec;
   rtp_rtcp_->GenericFECStatus(fec_enabled, payload_type_red, payload_type_fec);
 
@@ -498,16 +508,29 @@ int32_t ViEChannel::SetSendCodec(const V
                   kRtpExtensionVideoRotation, video_rotation_extension_id_) !=
               0) {
             LOG(LS_WARNING) << "Register VideoRotation extension failed";
           }
         } else {
           rtp_rtcp->DeregisterSendRtpHeaderExtension(
               kRtpExtensionVideoRotation);
         }
+        if (rid_extension_id_ != kInvalidRtpExtensionId) {
+          // Deregister in case the extension was previously enabled.
+          rtp_rtcp->DeregisterSendRtpHeaderExtension(
+              kRtpExtensionRID);
+          if (rtp_rtcp->RegisterSendRtpHeaderExtension(
+                  kRtpExtensionRID,
+                  rid_extension_id_) != 0) {
+            LOG(LS_WARNING) << "Register RID extension failed";
+          }
+        } else {
+          rtp_rtcp->DeregisterSendRtpHeaderExtension(
+              kRtpExtensionRID);
+        }
         rtp_rtcp->RegisterRtcpStatisticsCallback(
             rtp_rtcp_->GetRtcpStatisticsCallback());
         rtp_rtcp->RegisterSendChannelRtpStatisticsCallback(
             rtp_rtcp_->GetSendChannelRtpStatisticsCallback());
       }
       // |RegisterSimulcastRtpRtcpModules| resets all old weak pointers and old
       // modules can be deleted after this step.
       vie_receiver_.RegisterSimulcastRtpRtcpModules(simulcast_rtp_rtcp_);
@@ -961,16 +984,47 @@ int ViEChannel::SetSendVideoRotationStat
   }
   return error;
 }
 
 int ViEChannel::SetReceiveVideoRotationStatus(bool enable, int id) {
   return vie_receiver_.SetReceiveVideoRotationStatus(enable, id) ? 0 : -1;
 }
 
+int ViEChannel::SetSendRIDStatus(bool enable, int id, const char *rid) {
+  CriticalSectionScoped cs(rtp_rtcp_cs_.get());
+  int error = 0;
+  if (enable) {
+    // Enable the extension, but disable possible old id to avoid errors.
+    rid_extension_id_ = id;
+    rtp_rtcp_->DeregisterSendRtpHeaderExtension(
+        kRtpExtensionRID);
+    error = rtp_rtcp_->RegisterSendRtpHeaderExtension(
+        kRtpExtensionRID, id);
+    rtp_rtcp_->SetRID(rid);
+    // NOTE: simulcast streams must be set via the SetSendCodec() API
+  } else {
+    // Disable the extension.
+    rid_extension_id_ = kInvalidRtpExtensionId;
+    rtp_rtcp_->DeregisterSendRtpHeaderExtension(
+        kRtpExtensionRID);
+    // This may be overkill...
+    for (std::list<RtpRtcp*>::iterator it = simulcast_rtp_rtcp_.begin();
+         it != simulcast_rtp_rtcp_.end(); it++) {
+      (*it)->DeregisterSendRtpHeaderExtension(
+          kRtpExtensionRID);
+    }
+  }
+  return error;
+}
+
+int ViEChannel::SetReceiveRIDStatus(bool enable, int id) {
+  return vie_receiver_.SetReceiveRIDStatus(enable, id) ? 0 : -1;
+}
+
 void ViEChannel::SetRtcpXrRrtrStatus(bool enable) {
   CriticalSectionScoped cs(rtp_rtcp_cs_.get());
   rtp_rtcp_->SetRtcpXrRrtrStatus(enable);
 }
 
 void ViEChannel::SetTransmissionSmoothingStatus(bool enable) {
   assert(paced_sender_ && "No paced sender registered.");
   paced_sender_->SetStatus(enable);
@@ -1032,16 +1086,22 @@ int32_t ViEChannel::GetRemoteCSRC(uint32
 
   int num_csrcs = vie_receiver_.GetCsrcs(arrayCSRC);
   if (num_csrcs > 0) {
     memcpy(CSRCs, arrayCSRC, num_csrcs * sizeof(uint32_t));
   }
   return 0;
 }
 
+int32_t ViEChannel::GetRemoteRID(char rid[256])
+{
+  vie_receiver_.GetRID(rid);
+  return 0;
+}
+
 int ViEChannel::SetRtxSendPayloadType(int payload_type) {
   rtp_rtcp_->SetRtxSendPayloadType(payload_type);
   for (std::list<RtpRtcp*>::iterator it = simulcast_rtp_rtcp_.begin();
        it != simulcast_rtp_rtcp_.end(); it++) {
     (*it)->SetRtxSendPayloadType(payload_type);
   }
   SetRtxSendStatus(true);
   return 0;
--- a/media/webrtc/trunk/webrtc/video_engine/vie_channel.h
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_channel.h
@@ -133,16 +133,18 @@ class ViEChannel
   void EnableRemb(bool enable);
   int SetSendTimestampOffsetStatus(bool enable, int id);
   int SetReceiveTimestampOffsetStatus(bool enable, int id);
   int SetSendAbsoluteSendTimeStatus(bool enable, int id);
   int SetReceiveAbsoluteSendTimeStatus(bool enable, int id);
   bool GetReceiveAbsoluteSendTimeStatus() const;
   int SetSendVideoRotationStatus(bool enable, int id);
   int SetReceiveVideoRotationStatus(bool enable, int id);
+  int SetSendRIDStatus(bool enable, int id, const char *rid);
+  int SetReceiveRIDStatus(bool enable, int id);
   void SetRtcpXrRrtrStatus(bool enable);
   void SetTransmissionSmoothingStatus(bool enable);
   void EnableTMMBR(bool enable);
   int32_t EnableKeyFrameRequestCallback(const bool enable);
 
   // Sets SSRC for outgoing stream.
   int32_t SetSSRC(const uint32_t SSRC,
                   const StreamType usage,
@@ -152,16 +154,19 @@ class ViEChannel
   int32_t GetLocalSSRC(uint8_t idx, unsigned int* ssrc);
 
   // Gets SSRC for the incoming stream.
   int32_t GetRemoteSSRC(uint32_t* ssrc);
 
   // Gets the CSRC for the incoming stream.
   int32_t GetRemoteCSRC(uint32_t CSRCs[kRtpCsrcSize]);
 
+  // Gets the RID (if any) for the incoming stream.
+  int32_t GetRemoteRID(char rid[256]);
+
   int SetRtxSendPayloadType(int payload_type);
   void SetRtxReceivePayloadType(int payload_type);
 
   // Sets the starting sequence number, must be called before StartSend.
   int32_t SetStartSequenceNumber(uint16_t sequence_number);
 
   void SetRtpStateForSsrc(uint32_t ssrc, const RtpState& rtp_state);
   RtpState GetRtpStateForSsrc(uint32_t ssrc);
@@ -549,16 +554,17 @@ class ViEChannel
   RtcpRttStats* rtt_stats_;
   PacedSender* paced_sender_;
   PacketRouter* packet_router_;
 
   rtc::scoped_ptr<RtcpBandwidthObserver> bandwidth_observer_;
   int send_timestamp_extension_id_;
   int absolute_send_time_extension_id_;
   int video_rotation_extension_id_;
+  int rid_extension_id_;
 
   Transport* external_transport_;
 
   bool decoder_reset_;
   // Current receive codec used for codec change callback.
   VideoCodec receive_codec_;
   bool wait_for_key_frame_;
   rtc::scoped_ptr<ThreadWrapper> decode_thread_;
--- a/media/webrtc/trunk/webrtc/video_engine/vie_receiver.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_receiver.cc
@@ -56,16 +56,17 @@ ViEReceiver::ViEReceiver(const int32_t c
       remote_bitrate_estimator_(remote_bitrate_estimator),
       ntp_estimator_(new RemoteNtpTimeEstimator(clock_)),
       rtp_dump_(NULL),
       receiving_(false),
       receiving_rtcp_(false),
       restored_packet_in_use_(false),
       receiving_ast_enabled_(false),
       receiving_cvo_enabled_(false),
+      receiving_rid_enabled_(false),
       last_packet_log_ms_(-1) {
   assert(remote_bitrate_estimator);
 }
 
 ViEReceiver::~ViEReceiver() {
   UpdateHistograms();
   if (rtp_dump_) {
     rtp_dump_->Stop();
@@ -139,16 +140,20 @@ bool ViEReceiver::IsFecEnabled() const {
 uint32_t ViEReceiver::GetRemoteSsrc() const {
   return rtp_receiver_->SSRC();
 }
 
 int ViEReceiver::GetCsrcs(uint32_t* csrcs) const {
   return rtp_receiver_->CSRCs(csrcs);
 }
 
+void ViEReceiver::GetRID(char rid[256]) const {
+  rtp_receiver_->GetRID(rid);
+}
+
 void ViEReceiver::SetRtpRtcpModule(RtpRtcp* module) {
   rtp_rtcp_ = module;
 }
 
 RtpReceiver* ViEReceiver::GetRtpReceiver() const {
   return rtp_receiver_.get();
 }
 
@@ -201,16 +206,32 @@ bool ViEReceiver::SetReceiveVideoRotatio
     }
   } else {
     receiving_cvo_enabled_ = false;
     return rtp_header_parser_->DeregisterRtpHeaderExtension(
         kRtpExtensionVideoRotation);
   }
 }
 
+bool ViEReceiver::SetReceiveRIDStatus(bool enable, int id) {
+  if (enable) {
+    if (rtp_header_parser_->RegisterRtpHeaderExtension(
+            kRtpExtensionRID, id)) {
+      receiving_rid_enabled_ = true;
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    receiving_rid_enabled_ = false;
+    return rtp_