Merge inbound to mozilla-central. a=merge
authorCiure Andrei <aciure@mozilla.com>
Sun, 27 May 2018 01:17:13 +0300
changeset 420044 4e9446f9e8f0a75c7ffe063f1dfb311cc90d56cf
parent 420043 9b7db28b360d85d57cfdd3da2154b2dfa8bd938e (current diff)
parent 420020 50808ac6967ff99d90173c55023242133f3c1bb7 (diff)
child 420045 6a28f933f2d2139fa5b3303008a431f2212447b7
child 420066 9dfb7673f106393b792268f05a50cdc211ca1405
push id103686
push useraciure@mozilla.com
push dateSat, 26 May 2018 22:23:51 +0000
treeherdermozilla-inbound@6a28f933f2d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
4e9446f9e8f0 / 62.0a1 / 20180526223142 / files
nightly linux64
4e9446f9e8f0 / 62.0a1 / 20180526223142 / files
nightly mac
4e9446f9e8f0 / 62.0a1 / 20180526223142 / files
nightly win32
4e9446f9e8f0 / 62.0a1 / 20180526223142 / files
nightly win64
4e9446f9e8f0 / 62.0a1 / 20180526223142 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -152,28 +152,27 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   /* nsIContentFrameMessageManager is accessible only in TabChildGlobal. */
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIContentFrameMessageManager,
                                      !mChrome && !mIsProcessManager)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
 
-nsresult 
-MessageManagerCallback::DoGetRemoteType(nsAString& aRemoteType) const
+void
+MessageManagerCallback::DoGetRemoteType(nsAString& aRemoteType,
+                                        ErrorResult& aError) const
 {
   aRemoteType.Truncate();
   mozilla::dom::ChromeMessageSender* parent = GetProcessMessageManager();
   if (!parent) {
-    return NS_OK;
+    return;
   }
 
-  ErrorResult rv;
-  parent->GetRemoteType(aRemoteType, rv);
-  return rv.StealNSResult();
+  parent->GetRemoteType(aRemoteType, aError);
 }
 
 bool
 MessageManagerCallback::BuildClonedMessageDataForParent(nsIContentParent* aParent,
                                                         StructuredCloneData& aData,
                                                         ClonedMessageData& aClonedData)
 {
   return aData.BuildClonedMessageDataForParent(aParent, aClonedData);
@@ -403,36 +402,16 @@ nsFrameMessageManager::GetDelayedScripts
     }
 
     nsTArray<JS::Value>* array = aList.AppendElement(2);
     array->AppendElement(url);
     array->AppendElement(JS::BooleanValue(mPendingScriptsGlobalStates[i]));
   }
 }
 
-nsresult
-nsFrameMessageManager::GetDelayedScripts(JSContext* aCx,
-                                         JS::MutableHandle<JS::Value> aList)
-{
-  ErrorResult rv;
-  nsTArray<nsTArray<JS::Value>> list;
-  SequenceRooter<nsTArray<JS::Value>> listRooter(aCx, &list);
-  GetDelayedScripts(aCx, list, rv);
-  rv.WouldReportJSException();
-  if (rv.Failed()) {
-    return rv.StealNSResult();
-  }
-
-  if (!ToJSValue(aCx, list, aList)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return NS_OK;
-}
-
 static bool
 JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData)
 {
   nsAString* result = static_cast<nsAString*>(aData);
   result->Append(static_cast<const char16_t*>(aBuf),
                  static_cast<uint32_t>(aLen));
   return true;
 }
@@ -518,76 +497,62 @@ nsFrameMessageManager::SendMessage(JSCon
                                    const nsAString& aMessageName,
                                    JS::Handle<JS::Value> aObj,
                                    JS::Handle<JSObject*> aObjects,
                                    nsIPrincipal* aPrincipal,
                                    bool aIsSync,
                                    nsTArray<JS::Value>& aResult,
                                    ErrorResult& aError)
 {
+  NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
+  NS_ASSERTION(!IsBroadcaster(), "Should not call SendSyncMessage in chrome");
+  NS_ASSERTION(!GetParentManager(),
+               "Should not have parent manager in content!");
+
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
     "nsFrameMessageManager::SendMessage", EVENTS, aMessageName);
 
   if (sSendingSyncMessage && aIsSync) {
     // No kind of blocking send should be issued on top of a sync message.
     aError.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
   StructuredCloneData data;
   if (!aObj.isUndefined() &&
       !GetParamsForMessage(aCx, aObj, JS::UndefinedHandleValue, data)) {
     aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
-  SendMessage(aCx, aMessageName, data, aObjects, aPrincipal, aIsSync, aResult,
-              aError);
-}
-
-void
-nsFrameMessageManager::SendMessage(JSContext* aCx,
-                                   const nsAString& aMessageName,
-                                   StructuredCloneData& aData,
-                                   JS::Handle<JSObject*> aObjects,
-                                   nsIPrincipal* aPrincipal,
-                                   bool aIsSync,
-                                   nsTArray<JS::Value>& aResult,
-                                   ErrorResult& aError)
-{
-  NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
-  NS_ASSERTION(!IsBroadcaster(), "Should not call SendSyncMessage in chrome");
-  NS_ASSERTION(!GetParentManager(),
-               "Should not have parent manager in content!");
-
 #ifdef FUZZING
-  if (aData.DataLength() > 0) {
+  if (data.DataLength() > 0) {
     MessageManagerFuzzer::TryMutate(
       aCx,
       aMessageName,
-      &aData,
+      &data,
       JS::UndefinedHandleValue);
   }
 #endif
 
-  if (!AllowMessage(aData.DataLength(), aMessageName)) {
+  if (!AllowMessage(data.DataLength(), aMessageName)) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (!mCallback) {
     aError.Throw(NS_ERROR_NOT_INITIALIZED);
     return;
   }
 
   nsTArray<StructuredCloneData> retval;
 
   TimeStamp start = TimeStamp::Now();
   sSendingSyncMessage |= aIsSync;
-  bool ok = mCallback->DoSendBlockingMessage(aCx, aMessageName, aData, aObjects,
+  bool ok = mCallback->DoSendBlockingMessage(aCx, aMessageName, data, aObjects,
                                              aPrincipal, &retval, aIsSync);
   if (aIsSync) {
     sSendingSyncMessage = false;
   }
 
   uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
   if (latencyMs >= kMinTelemetrySyncMessageManagerLatencyMs) {
     NS_ConvertUTF16toUTF8 messageName(aMessageName);
@@ -1045,17 +1010,17 @@ nsFrameMessageManager::GetProcessMessage
   return pmm.forget();
 }
 
 void
 nsFrameMessageManager::GetRemoteType(nsAString& aRemoteType, ErrorResult& aError) const
 {
   aRemoteType.Truncate();
   if (mCallback) {
-    aError = mCallback->DoGetRemoteType(aRemoteType);
+    mCallback->DoGetRemoteType(aRemoteType, aError);
   }
 }
 
 namespace {
 
 struct MessageManagerReferentCount
 {
   MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -96,17 +96,18 @@ public:
     return NS_OK;
   }
 
   virtual mozilla::dom::ChromeMessageSender* GetProcessMessageManager() const
   {
     return nullptr;
   }
 
-  virtual nsresult DoGetRemoteType(nsAString& aRemoteType) const;
+  virtual void DoGetRemoteType(nsAString& aRemoteType,
+                               ErrorResult& aError) const;
 
 protected:
   bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
                                        StructuredCloneData& aData,
                                        ClonedMessageData& aClonedData);
   bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
                                       StructuredCloneData& aData,
                                       ClonedMessageData& aClonedData);
@@ -298,56 +299,33 @@ protected:
 
   void DispatchAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
                             JS::Handle<JS::Value> aObj,
                             JS::Handle<JSObject*> aObjects,
                             nsIPrincipal* aPrincipal,
                             JS::Handle<JS::Value> aTransfers,
                             mozilla::ErrorResult& aError);
 
-  nsresult SendMessage(const nsAString& aMessageName,
-                       JS::Handle<JS::Value> aJSON,
-                       JS::Handle<JS::Value> aObjects,
-                       nsIPrincipal* aPrincipal,
-                       JSContext* aCx,
-                       uint8_t aArgc,
-                       JS::MutableHandle<JS::Value> aRetval,
-                       bool aIsSync);
   void SendMessage(JSContext* aCx, const nsAString& aMessageName,
                    JS::Handle<JS::Value> aObj, JS::Handle<JSObject*> aObjects,
                    nsIPrincipal* aPrincipal, bool aIsSync, nsTArray<JS::Value>& aResult,
                    mozilla::ErrorResult& aError);
-  void SendMessage(JSContext* aCx, const nsAString& aMessageName,
-                   StructuredCloneData& aData, JS::Handle<JSObject*> aObjects,
-                   nsIPrincipal* aPrincipal, bool aIsSync,
-                   nsTArray<JS::Value>& aResult, mozilla::ErrorResult& aError);
 
   void ReceiveMessage(nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader,
                       bool aTargetClosed, const nsAString& aMessage, bool aIsSync,
                       StructuredCloneData* aCloneData, mozilla::jsipc::CpowHolder* aCpows,
                       nsIPrincipal* aPrincipal, nsTArray<StructuredCloneData>* aRetVal,
                       mozilla::ErrorResult& aError);
 
   void LoadScript(const nsAString& aURL, bool aAllowDelayedLoad,
                   bool aRunInGlobalScope, mozilla::ErrorResult& aError);
   void RemoveDelayedScript(const nsAString& aURL);
-  nsresult GetDelayedScripts(JSContext* aCx,
-                             JS::MutableHandle<JS::Value> aList);
   void GetDelayedScripts(JSContext* aCx, nsTArray<nsTArray<JS::Value>>& aList,
                          mozilla::ErrorResult& aError);
 
-  enum ProcessCheckerType {
-    PROCESS_CHECKER_PERMISSION,
-    PROCESS_CHECKER_MANIFEST_URL,
-    ASSERT_APP_HAS_PERMISSION
-  };
-  bool AssertProcessInternal(ProcessCheckerType aType,
-                             const nsAString& aCapability,
-                             mozilla::ErrorResult& aError);
-
   // We keep the message listeners as arrays in a hastable indexed by the
   // message name. That gives us fast lookups in ReceiveMessage().
   nsClassHashtable<nsStringHashKey,
                    nsAutoTObserverArray<nsMessageListenerInfo, 1>> mListeners;
   nsTArray<RefPtr<mozilla::dom::MessageListenerManager>> mChildManagers;
   bool mChrome;     // true if we're in the chrome process
   bool mGlobal;     // true if we're the global frame message manager
   bool mIsProcessManager; // true if the message manager belongs to the process realm
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -70,17 +70,17 @@ public:
   };
 
   enum IsIncremental {
     IncrementalGC,
     NonIncrementalGC
   };
 
   // Setup all the statics etc - safe to call multiple times after Startup().
-  void EnsureStatics();
+  static void EnsureStatics();
 
   static void GarbageCollectNow(JS::gcreason::Reason reason,
                                 IsIncremental aIncremental = NonIncrementalGC,
                                 IsShrinking aShrinking = NonShrinkingGC,
                                 int64_t aSliceMillis = 0);
 
   static void CycleCollectNow(nsICycleCollectorListener *aListener = nullptr);
 
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -33,17 +33,17 @@ nsWrapperCache::HoldJSObjects(void* aScr
     CycleCollectedJSRuntime::Get()->NurseryWrapperPreserved(mWrapper);
   }
 }
 
 void
 nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper)
 {
   mWrapper = aWrapper;
-  UnsetWrapperFlags(kWrapperFlagsMask & ~WRAPPER_IS_NOT_DOM_BINDING);
+  UnsetWrapperFlags(kWrapperFlagsMask);
 
   if (aWrapper && !JS::ObjectIsTenured(aWrapper)) {
     CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this);
   }
 }
 
 void
 nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder)
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -58,23 +58,18 @@ static_assert(sizeof(void*) == 4, "Only 
  * a wrapper cache by calling QueryInterface on it. Note that this breaks XPCOM
  * rules a bit (this object doesn't derive from nsISupports).
  *
  * The cache can store objects other than wrappers. We allow wrappers to use a
  * separate JSObject to store their state (mostly expandos). If the wrapper is
  * collected and we want to preserve this state we actually store the state
  * object in the cache.
  *
- * The cache can store 2 types of objects:
- *
- *  If WRAPPER_IS_NOT_DOM_BINDING is set (IsDOMBinding() returns false):
- *    - the JSObject of an XPCWrappedNative wrapper
- *
- *  If WRAPPER_IS_NOT_DOM_BINDING is not set (IsDOMBinding() returns true):
- *    - a DOM binding object (regular JS object or proxy)
+ * The cache can store 3 types of objects: a DOM binding object (regular JS object or
+ * proxy), an nsOuterWindowProxy or an XPCWrappedNative wrapper.
  *
  * The finalizer for the wrapper clears the cache.
  *
  * A compacting GC can move the wrapper object. Pointers to moved objects are
  * usually found and updated by tracing the heap, however non-preserved wrappers
  * are weak references and are not traced, so another approach is
  * necessary. Instead a class hook (objectMovedOp) is provided that is called
  * when an object is moved and is responsible for ensuring pointers are
@@ -192,21 +187,16 @@ public:
     }
   }
 
   bool PreservingWrapper() const
   {
     return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
   }
 
-  bool IsDOMBinding() const
-  {
-    return !HasWrapperFlag(WRAPPER_IS_NOT_DOM_BINDING);
-  }
-
   /**
    * Wrap the object corresponding to this wrapper cache. If non-null is
    * returned, the object has already been stored in the wrapper cache.
    */
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) = 0;
 
   /**
    * Returns true if the object has a wrapper that is known live from the point
@@ -336,29 +326,16 @@ protected:
     if (mWrapper) {
       // Set the pointer to a value that will cause a crash if it is
       // dereferenced.
       mWrapper = reinterpret_cast<JSObject*>(1);
     }
   }
 
 private:
-  // Friend declarations for things that need to be able to call
-  // SetIsNotDOMBinding().  The goal is to get rid of all of these, and
-  // SetIsNotDOMBinding() too.  Once that's done, we can remove the
-  // couldBeDOMBinding bits in DoGetOrCreateDOMReflector, as well as any other
-  // consumers of IsDOMBinding().
-  friend class SandboxPrivate;
-  void SetIsNotDOMBinding()
-  {
-    MOZ_ASSERT(!mWrapper && !(GetWrapperFlags() & ~WRAPPER_IS_NOT_DOM_BINDING),
-               "This flag should be set before creating any wrappers.");
-    SetWrapperFlags(WRAPPER_IS_NOT_DOM_BINDING);
-  }
-
   void SetWrapperJSObject(JSObject* aWrapper);
 
   FlagsType GetWrapperFlags() const
   {
     return mFlags & kWrapperFlagsMask;
   }
 
   bool HasWrapperFlag(FlagsType aFlag) const
@@ -396,33 +373,27 @@ private:
    * causes between the native object and the JS object, so it is important that
    * any native object that supports preserving of its wrapper
    * traces/traverses/unlinks the cached JS object (see
    * NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER and
    * NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER).
    */
   enum { WRAPPER_BIT_PRESERVED = 1 << 0 };
 
-  /**
-   * 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) };
+  enum { kWrapperFlagsMask = WRAPPER_BIT_PRESERVED };
 
   JSObject* mWrapper;
   FlagsType mFlags;
 protected:
 #ifdef BOOL_FLAGS_ON_WRAPPER_CACHE
   uint32_t mBoolFlags;
 #endif
 };
 
-enum { WRAPPER_CACHE_FLAGS_BITS_USED = 2 };
+enum { WRAPPER_CACHE_FLAGS_BITS_USED = 1 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
 
 #define NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY                                 \
   if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) {                            \
     *aInstancePtr = static_cast<nsWrapperCache*>(this);                       \
     return NS_OK;                                                             \
   }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1058,44 +1058,47 @@ CreateInterfaceObjects(JSContext* cx, JS
         *protoCache = nullptr;
       }
       return;
     }
     *constructorCache = interface;
   }
 }
 
-bool
+// Only set aAllowNativeWrapper to false if you really know you need it; if in
+// doubt use true. Setting it to false disables security wrappers.
+static bool
 NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
                                          JS::Handle<JSObject*> aScope,
                                          JS::MutableHandle<JS::Value> aRetval,
                                          xpcObjectHelper& aHelper,
                                          const nsIID* aIID,
                                          bool aAllowNativeWrapper)
 {
   js::AssertSameCompartment(aCx, aScope);
   nsresult rv;
   // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
   // on all threads.
   nsWrapperCache *cache = aHelper.GetWrapperCache();
 
-  if (cache && cache->IsDOMBinding()) {
+  if (cache) {
       JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper());
       if (!obj) {
         obj = cache->WrapObject(aCx, nullptr);
+        if (!obj) {
+          return Throw(aCx, NS_ERROR_UNEXPECTED);
+        }
       }
 
-      if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
+      if (aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
         return false;
       }
 
-      if (obj) {
-        aRetval.setObject(*obj);
-        return true;
-      }
+      aRetval.setObject(*obj);
+      return true;
   }
 
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!XPCConvert::NativeInterface2JSObject(aRetval, aHelper, aIID,
                                             aAllowNativeWrapper, &rv)) {
     // I can't tell if NativeInterface2JSObject throws JS exceptions
     // or not.  This is a sloppy stab at the right semantics; the
@@ -1133,17 +1136,17 @@ TryPreserveWrapper(JSObject* obj)
 bool
 InstanceClassHasProtoAtDepth(const js::Class* clasp,
                              uint32_t protoID, uint32_t depth)
 {
   const DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp);
   return static_cast<uint32_t>(domClass->mInterfaceChain[depth]) == protoID;
 }
 
-// Only set allowNativeWrapper to false if you really know you need it, if in
+// Only set allowNativeWrapper to false if you really know you need it; if in
 // doubt use true. Setting it to false disables security wrappers.
 bool
 XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
                    xpcObjectHelper& helper, const nsIID* iid,
                    bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval)
 {
   if (!NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid,
                                                 allowNativeWrapper)) {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -867,28 +867,16 @@ CheckWrapperCacheCast<T, true>
 {
   static bool Check()
   {
     return true;
   }
 };
 #endif
 
-MOZ_ALWAYS_INLINE bool
-CouldBeDOMBinding(void*)
-{
-  return true;
-}
-
-MOZ_ALWAYS_INLINE bool
-CouldBeDOMBinding(nsWrapperCache* aCache)
-{
-  return aCache->IsDOMBinding();
-}
-
 inline bool
 TryToOuterize(JS::MutableHandle<JS::Value> rval)
 {
   if (js::IsWindow(&rval.toObject())) {
     JSObject* obj = js::ToWindowProxyIfWindow(&rval.toObject());
     MOZ_ASSERT(obj);
     rval.set(JS::ObjectValue(*obj));
   }
@@ -1068,33 +1056,25 @@ AssertReflectorHasGivenProto(JSContext* 
 template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
 MOZ_ALWAYS_INLINE bool
 DoGetOrCreateDOMReflector(JSContext* cx, T* value,
                           JS::Handle<JSObject*> givenProto,
                           JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(value);
   MOZ_ASSERT_IF(givenProto, js::IsObjectInContextCompartment(givenProto, cx));
-  // We can get rid of this when we remove support for
-  // nsWrapperCache::SetIsNotDOMBinding.
-  bool couldBeDOMBinding = CouldBeDOMBinding(value);
   JSObject* obj = value->GetWrapper();
   if (obj) {
 #ifdef DEBUG
     AssertReflectorHasGivenProto(cx, obj, givenProto);
     // Have to reget obj because AssertReflectorHasGivenProto can
     // trigger gc so the pointer may now be invalid.
     obj = value->GetWrapper();
 #endif
   } else {
-    // Inline this here while we have non-dom objects in wrapper caches.
-    if (!couldBeDOMBinding) {
-      return false;
-    }
-
     obj = value->WrapObject(cx, givenProto);
     if (!obj) {
       // At this point, obj is null, so just return false.
       // Callers seem to be testing JS_IsExceptionPending(cx) to
       // figure out whether WrapObject() threw.
       return false;
     }
 
@@ -1118,19 +1098,17 @@ DoGetOrCreateDOMReflector(JSContext* cx,
     MOZ_ASSERT(clasp, "What happened here?");
     MOZ_ASSERT_IF(clasp->mDOMObjectIsISupports, (IsBaseOf<nsISupports, T>::value));
     MOZ_ASSERT(CheckWrapperCacheCast<T>::Check());
   }
 #endif
 
   rval.set(JS::ObjectValue(*obj));
 
-  bool sameCompartment =
-    js::GetObjectCompartment(obj) == js::GetContextCompartment(cx);
-  if (sameCompartment && couldBeDOMBinding) {
+  if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) {
     return TypeNeedsOuterization<T>::value ? TryToOuterize(rval) : true;
   }
 
   if (wrapBehavior == eDontWrapIntoContextCompartment) {
     if (TypeNeedsOuterization<T>::value) {
       JSAutoRealm ar(cx, obj);
       return TryToOuterize(rval);
     }
@@ -1299,75 +1277,16 @@ WrapNewBindingNonWrapperCachedObject(JSC
                                      T& value,
                                      JS::MutableHandle<JS::Value> rval,
                                      JS::Handle<JSObject*> givenProto = nullptr)
 {
   return WrapNewBindingNonWrapperCachedObject(cx, scope, &value, rval,
                                               givenProto);
 }
 
-// Only set allowNativeWrapper to false if you really know you need it, if in
-// doubt use true. Setting it to false disables security wrappers.
-bool
-NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
-                                         JS::Handle<JSObject*> aScope,
-                                         JS::MutableHandle<JS::Value> aRetval,
-                                         xpcObjectHelper& aHelper,
-                                         const nsIID* aIID,
-                                         bool aAllowNativeWrapper);
-
-/**
- * A method to handle new-binding wrap failure, by possibly falling back to
- * wrapping as a non-new-binding object.
- */
-template <class T>
-MOZ_ALWAYS_INLINE bool
-HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
-                                T* value, JS::MutableHandle<JS::Value> rval)
-{
-  if (JS_IsExceptionPending(cx)) {
-    return false;
-  }
-
-  xpcObjectHelper helper(value, GetWrapperCache(value));
-  return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval,
-                                                  helper, nullptr, true);
-}
-
-// Helper for calling HandleNewBindingWrappingFailure with smart pointers
-// (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
-
-template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
-struct HandleNewBindingWrappingFailureHelper
-{
-  static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope,
-                          const T& value, JS::MutableHandle<JS::Value> rval)
-  {
-    return HandleNewBindingWrappingFailure(cx, scope, value.get(), rval);
-  }
-};
-
-template <class T>
-struct HandleNewBindingWrappingFailureHelper<T, false>
-{
-  static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope, T& value,
-                          JS::MutableHandle<JS::Value> rval)
-  {
-    return HandleNewBindingWrappingFailure(cx, scope, &value, rval);
-  }
-};
-
-template<class T>
-inline bool
-HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
-                                T& value, JS::MutableHandle<JS::Value> rval)
-{
-  return HandleNewBindingWrappingFailureHelper<T>::Wrap(cx, scope, value, rval);
-}
-
 template<bool Fatal>
 inline bool
 EnumValueNotFound(JSContext* cx, JS::HandleString str, const char* type,
                   const char* sourceDescription);
 
 template<>
 inline bool
 EnumValueNotFound<false>(JSContext* cx, JS::HandleString str, const char* type,
@@ -1533,17 +1452,17 @@ UpdateWrapper(T* p, void*, JSObject* obj
 bool
 TryPreserveWrapper(JSObject* obj);
 
 // Can only be called with a DOM JSClass.
 bool
 InstanceClassHasProtoAtDepth(const js::Class* clasp,
                              uint32_t protoID, uint32_t depth);
 
-// Only set allowNativeWrapper to false if you really know you need it, if in
+// Only set allowNativeWrapper to false if you really know you need it; if in
 // doubt use true. Setting it to false disables security wrappers.
 bool
 XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
                    xpcObjectHelper& helper, const nsIID* iid,
                    bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval);
 
 // Special-cased wrapping for variants
 bool
@@ -1665,63 +1584,34 @@ WrapNativeISupports(JSContext* cx, T* p,
   JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
   JS::Rooted<JS::Value> v(cx);
   return XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v) ?
          v.toObjectOrNull() :
          nullptr;
 }
 
 
-// Fallback for when our parent is not a WebIDL binding object.
-template<typename T, bool isISupports=IsBaseOf<nsISupports, T>::value>
-struct WrapNativeFallback
-{
-  static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
-  {
-    return nullptr;
-  }
-};
-
-// Fallback for when our parent is not a WebIDL binding object but _is_ an
-// nsISupports object.
-template<typename T >
-struct WrapNativeFallback<T, true >
-{
-  static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
-  {
-    return WrapNativeISupports(cx, parent, cache);
-  }
-};
-
-// Wrapping of our native parent, for cases when it's a WebIDL object (though
-// possibly preffed off).
+// Wrapping of our native parent, for cases when it's a WebIDL object.
 template<typename T, bool hasWrapObject=NativeHasMember<T>::WrapObject>
 struct WrapNativeHelper
 {
   static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
   {
     MOZ_ASSERT(cache);
 
     JSObject* obj;
     if ((obj = cache->GetWrapper())) {
       // GetWrapper always unmarks gray.
       MOZ_ASSERT(JS::ObjectIsNotGray(obj));
       return obj;
     }
 
-    // Inline this here while we have non-dom objects in wrapper caches.
-    if (!CouldBeDOMBinding(parent)) {
-      // WrapNativeFallback never returns a gray thing.
-      obj = WrapNativeFallback<T>::Wrap(cx, parent, cache);
-      MOZ_ASSERT(JS::ObjectIsNotGray(obj));
-    } else {
-      // WrapObject never returns a gray thing.
-      obj = parent->WrapObject(cx, nullptr);
-      MOZ_ASSERT(JS::ObjectIsNotGray(obj));
-    }
+    // WrapObject never returns a gray thing.
+    obj = parent->WrapObject(cx, nullptr);
+    MOZ_ASSERT(JS::ObjectIsNotGray(obj));
 
     return obj;
   }
 };
 
 // Wrapping of our native parent, for cases when it's not a WebIDL object.  In
 // this case it must be nsISupports.
 template<typename T>
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -15186,18 +15186,16 @@ class CGJSImplClass(CGBindingImplClass):
                 descriptor.interface.parent.identifier.name).jsImplParent
             baseClasses = [ClassBase(parentClass)]
             isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n"
             ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" %
                       (descriptor.name, parentClass))
             constructorBody = dedent("""
                 // Make sure we're an nsWrapperCache already
                 MOZ_ASSERT(static_cast<nsWrapperCache*>(this));
-                // And that our ancestor has not called SetIsNotDOMBinding()
-                MOZ_ASSERT(IsDOMBinding());
                 """)
             extradefinitions = fill(
                 """
                 NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent)
                 NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})
                 NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})
                 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
                 NS_INTERFACE_MAP_END_INHERITING(${parentClass})
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -143,18 +143,16 @@ template <class T>
 MOZ_MUST_USE
 typename EnableIf<IsBaseOf<nsWrapperCache, T>::value, bool>::Type
 ToJSValue(JSContext* aCx,
           T& aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
-  // Make sure non-webidl objects don't sneak in here
-  MOZ_ASSERT(aArgument.IsDOMBinding());
 
   return GetOrCreateDOMReflector(aCx, aArgument, aValue);
 }
 
 // Accept typed arrays built from appropriate nsTArray values
 template<typename T>
 MOZ_MUST_USE
 typename EnableIf<IsBaseOf<AllTypedArraysBase, T>::value, bool>::Type
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -194,20 +194,19 @@ public:
                 uint64_t aNextTabParentId);
 
   static void GetAll(nsTArray<ContentParent*>& aArray);
 
   static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);
 
   const nsAString& GetRemoteType() const;
 
-  virtual nsresult DoGetRemoteType(nsAString& aRemoteType) const override
+  virtual void DoGetRemoteType(nsAString& aRemoteType, ErrorResult& aError) const override
   {
     aRemoteType = GetRemoteType();
-    return NS_OK;
   }
 
   enum CPIteratorPolicy {
     eLive,
     eAll
   };
 
   class ContentParentIterator {
--- a/dom/serviceworkers/ServiceWorkerPrivate.cpp
+++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp
@@ -48,17 +48,19 @@ using namespace mozilla::dom;
 namespace mozilla {
 namespace dom {
 
 using mozilla::ipc::PrincipalInfo;
 
 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(ServiceWorkerPrivate)
 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(ServiceWorkerPrivate)
 NS_IMPL_CYCLE_COLLECTION(ServiceWorkerPrivate, mSupportsArray)
-
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ServiceWorkerPrivate)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mSandbox)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ServiceWorkerPrivate, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ServiceWorkerPrivate, Release)
 
 // Tracks the "dom.serviceWorkers.disable_open_click_delay" preference.  Modified
 // on main thread, read on worker threads.
 // It is updated every time a "notificationclick" event is dispatched. While
 // this is done without synchronization, at the worst, the thread will just get
 // an older value within which a popup is allowed to be displayed, which will
@@ -107,16 +109,18 @@ ServiceWorkerPrivate::ServiceWorkerPriva
 ServiceWorkerPrivate::~ServiceWorkerPrivate()
 {
   MOZ_ASSERT(!mWorkerPrivate);
   MOZ_ASSERT(!mTokenCount);
   MOZ_ASSERT(!mInfo);
   MOZ_ASSERT(mSupportsArray.IsEmpty());
 
   mIdleWorkerTimer->Cancel();
+
+  DropJSObjects(this);
 }
 
 namespace {
 
 class CheckScriptEvaluationWithCallback final : public WorkerRunnable
 {
   nsMainThreadPtrHandle<ServiceWorkerPrivate> mServiceWorkerPrivate;
   nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
@@ -210,32 +214,34 @@ ServiceWorkerPrivate::CheckScriptEvaluat
                                                                    aScriptEvaluationCallback);
   if (NS_WARN_IF(!r->Dispatch())) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
-JSObject*
-ServiceWorkerPrivate::GetOrCreateSandbox(JSContext* aCx)
+void
+ServiceWorkerPrivate::GetOrCreateSandbox(JSContext* aCx,
+                                         JS::MutableHandle<JSObject*> aSandbox)
 {
   AssertIsOnMainThread();
 
   if (!mSandbox) {
     nsIXPConnect* xpc = nsContentUtils::XPConnect();
 
     JS::Rooted<JSObject*> sandbox(aCx);
     nsresult rv = xpc->CreateSandbox(aCx, mInfo->Principal(), sandbox.address());
-    NS_ENSURE_SUCCESS(rv, nullptr);
+    NS_ENSURE_SUCCESS_VOID(rv);
 
-    mSandbox = new JSObjectHolder(aCx, sandbox);
+    mSandbox = sandbox;
+    HoldJSObjects(this);
   }
 
-  return mSandbox->GetJSObject();
+  aSandbox.set(mSandbox);
 }
 
 namespace {
 
 enum ExtendableEventResult {
     Rejected = 0,
     Resolved
 };
@@ -594,21 +600,19 @@ ServiceWorkerPrivate::SendMessageEvent(i
   // Ideally we would simply move the StructuredCloneData to the
   // SendMessageEventRunnable, but we cannot because it uses non-threadsafe
   // ref-counting.  The following gnarly code unpacks the IPC-friendly
   // StructuredCloneData and re-packs it into the thread-friendly
   // StructuredCloneHolder.  In the future we should remove this and make
   // it easier to simple move the data to the other thread.  See bug 1458936.
 
   AutoSafeJSContext cx;
-  JSObject* sandbox = GetOrCreateSandbox(cx);
-  NS_ENSURE_TRUE(sandbox, NS_ERROR_FAILURE);
-
-  JS::Rooted<JSObject*> global(cx, sandbox);
-  NS_ENSURE_TRUE(sandbox, NS_ERROR_FAILURE);
+  JS::Rooted<JSObject*> global(cx);
+  GetOrCreateSandbox(cx, &global);
+  NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
 
   // The CreateSandbox call returns a proxy to the actual sandbox object.  We
   // don't need a proxy here.
   global = js::UncheckedUnwrap(global);
 
   JSAutoRealm ar(cx, global);
 
   JS::Rooted<JS::Value> messageData(cx);
--- a/dom/serviceworkers/ServiceWorkerPrivate.h
+++ b/dom/serviceworkers/ServiceWorkerPrivate.h
@@ -72,17 +72,17 @@ public:
 // ExtendableEventWorkerRunnable.
 class ServiceWorkerPrivate final
 {
   friend class KeepAliveToken;
 
 public:
   NS_IMETHOD_(MozExternalRefCountType) AddRef();
   NS_IMETHOD_(MozExternalRefCountType) Release();
-  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ServiceWorkerPrivate)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(ServiceWorkerPrivate)
 
   typedef mozilla::FalseType HasThreadSafeRefCnt;
 
 protected:
   nsCycleCollectingAutoRefCnt mRefCnt;
   NS_DECL_OWNINGTHREAD
 
 public:
@@ -200,18 +200,18 @@ private:
                       bool* aNewWorkerCreated = nullptr,
                       nsILoadGroup* aLoadGroup = nullptr);
 
   ~ServiceWorkerPrivate();
 
   already_AddRefed<KeepAliveToken>
   CreateEventKeepAliveToken();
 
-  JSObject*
-  GetOrCreateSandbox(JSContext* aCx);
+  void
+  GetOrCreateSandbox(JSContext* aCx, JS::MutableHandle<JSObject*> aSandbox);
 
   // The info object owns us. It is possible to outlive it for a brief period
   // of time if there are pending waitUntil promises, in which case it
   // will be null and |SpawnWorkerIfNeeded| will always fail.
   ServiceWorkerInfo* MOZ_NON_OWNING_REF mInfo;
 
   // The WorkerPrivate object can only be closed by this class or by the
   // RuntimeService class if gecko is shutting down. Closing the worker
@@ -222,17 +222,17 @@ private:
 
   // We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the
   // worker a grace period after each event.
   RefPtr<KeepAliveToken> mIdleKeepAliveToken;
 
   // Sandbox global used to re-pack structured clone data before sending
   // to the service worker thread.  Ideally we would remove this and just
   // make StructuredCloneData thread safe enough to pass to the worker thread.
-  RefPtr<JSObjectHolder> mSandbox;
+  JS::Heap<JSObject*> mSandbox;
 
   uint64_t mDebuggerCount;
 
   uint64_t mTokenCount;
 
   // Meant for keeping objects alive while handling requests from the worker
   // on the main thread. Access to this array is provided through
   // |StoreISupports| and |RemoveISupports|. Note that the array is also
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1067,21 +1067,18 @@ xpc::CreateSandboxObject(JSContext* cx, 
     // flag, but such a change would break code in subtle ways for minimal
     // benefit. So we just switch it off here.
     priv->wantXrays =
       AccessCheck::isChrome(sandbox) ? false : options.wantXrays;
 
     {
         JSAutoRealm ar(cx, sandbox);
 
-        nsCOMPtr<nsIScriptObjectPrincipal> sbp =
-            new SandboxPrivate(principal, sandbox);
-
-        // Pass on ownership of sbp to |sandbox|.
-        JS_SetPrivate(sandbox, sbp.forget().take());
+        // This creates a SandboxPrivate and passes ownership of it to |sandbox|.
+        SandboxPrivate::Create(principal, sandbox);
 
         // Ensure |Object.prototype| is instantiated before prototype-
         // splicing below.
         if (!JS_GetObjectPrototype(cx, sandbox))
             return NS_ERROR_XPC_UNEXPECTED;
 
         if (options.proto) {
             bool ok = JS_WrapObject(cx, &options.proto);
--- a/js/xpconnect/src/SandboxPrivate.h
+++ b/js/xpconnect/src/SandboxPrivate.h
@@ -17,27 +17,38 @@
 
 
 class SandboxPrivate : public nsIGlobalObject,
                        public nsIScriptObjectPrincipal,
                        public nsSupportsWeakReference,
                        public nsWrapperCache
 {
 public:
-    SandboxPrivate(nsIPrincipal* principal, JSObject* global)
-        : mPrincipal(principal)
-    {
-        SetIsNotDOMBinding();
-        SetWrapper(global);
-    }
-
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(SandboxPrivate,
                                                            nsIGlobalObject)
 
+    static void Create(nsIPrincipal* principal, JS::Handle<JSObject*> global)
+    {
+        RefPtr<SandboxPrivate> sbp = new SandboxPrivate(principal);
+        sbp->SetWrapper(global);
+        sbp->PreserveWrapper(ToSupports(sbp.get()));
+
+        // Pass on ownership of sbp to |global|.
+        // The type used to cast to void needs to match the one in GetPrivate.
+        JS_SetPrivate(global, static_cast<nsIScriptObjectPrincipal*>(sbp.forget().take()));
+    }
+
+    static SandboxPrivate* GetPrivate(JSObject* obj)
+    {
+        // The type used to cast to void needs to match the one in Create.
+        return static_cast<SandboxPrivate*>(
+            static_cast<nsIScriptObjectPrincipal*>(JS_GetPrivate(obj)));
+    }
+
     nsIPrincipal* GetPrincipal() override
     {
         return mPrincipal;
     }
 
     JSObject* GetGlobalJSObject() override
     {
         return GetWrapper();
@@ -55,14 +66,19 @@ public:
 
     size_t ObjectMoved(JSObject* obj, JSObject* old)
     {
         UpdateWrapper(obj, old);
         return 0;
     }
 
 private:
-    virtual ~SandboxPrivate() { }
+    explicit SandboxPrivate(nsIPrincipal* principal)
+        : mPrincipal(principal)
+    { }
+
+    virtual ~SandboxPrivate()
+    { }
 
     nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
 #endif // __SANDBOXPRIVATE_H__
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -863,26 +863,23 @@ XPCConvert::NativeInterface2JSObject(Mut
     // (that means an XPCWrappedNative around an nsXPCWrappedJS). This isn't
     // optimal -- we could detect this and roll the functionality into a
     // single wrapper, but the current solution is good enough for now.
     AutoJSContext cx;
     XPCWrappedNativeScope* xpcscope = ObjectScope(JS::CurrentGlobalOrNull(cx));
     if (!xpcscope)
         return false;
 
-    // First, see if this object supports the wrapper cache.
-    // Note: If |cache->IsDOMBinding()| is true, then it means that the object
-    // implementing it doesn't want a wrapped native as its JS Object, but
-    // instead it provides its own proxy object. In that case, the object
-    // to use is found as cache->GetWrapper(). If that is null, then the
-    // object will create (and fill the cache) from its WrapObject call.
+    // First, see if this object supports the wrapper cache. In that case, the object
+    // to use is found as cache->GetWrapper(). If that is null, then the object will
+    // create (and fill the cache) from its WrapObject call.
     nsWrapperCache* cache = aHelper.GetWrapperCache();
 
     RootedObject flat(cx, cache ? cache->GetWrapper() : nullptr);
-    if (!flat && cache && cache->IsDOMBinding()) {
+    if (!flat && cache) {
         RootedObject global(cx, xpcscope->GetGlobalJSObject());
         js::AssertSameCompartment(cx, global);
         flat = cache->WrapObject(cx, nullptr);
         if (!flat)
             return false;
     }
     if (flat) {
         if (allowNativeWrapper && !JS_WrapObject(cx, &flat))
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -3091,12 +3091,20 @@ XPCJSRuntime::InitSingletonScopes()
     rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, compilationScopeOptions);
     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     mCompilationScope = js::UncheckedUnwrap(&v.toObject());
 }
 
 void
 XPCJSRuntime::DeleteSingletonScopes()
 {
+    // We're pretty late in shutdown, so we call ReleaseWrapper on the scopes. This way
+    // the GC can collect them immediately, and we don't rely on the CC to clean up.
+    RefPtr<SandboxPrivate> sandbox = SandboxPrivate::GetPrivate(mUnprivilegedJunkScope);
+    sandbox->ReleaseWrapper(sandbox);
     mUnprivilegedJunkScope = nullptr;
+    sandbox = SandboxPrivate::GetPrivate(mPrivilegedJunkScope);
+    sandbox->ReleaseWrapper(sandbox);
     mPrivilegedJunkScope = nullptr;
+    sandbox = SandboxPrivate::GetPrivate(mCompilationScope);
+    sandbox->ReleaseWrapper(sandbox);
     mCompilationScope = nullptr;
 }
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -139,16 +139,17 @@ nsLayoutStatics::Initialize()
   nsCSSPseudoElements::RegisterStaticAtoms();
   nsCSSKeywords::AddRefTable();
   nsCSSProps::AddRefTable();
   nsColorNames::AddRefTable();
 
   NS_SetStaticAtomsDone();
 
   StartupJSEnvironment();
+  nsJSContext::EnsureStatics();
 
   nsGlobalWindowInner::Init();
   nsGlobalWindowOuter::Init();
   Navigator::Init();
   nsXBLService::Init();
 
   rv = nsContentUtils::Init();
   if (NS_FAILED(rv)) {
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -3022,20 +3022,26 @@ locked_profiler_stop(PSLockRef aLock)
   // Stop sampling live threads.
   int tid = Thread::GetCurrentId();
   const nsTArray<LiveProfiledThreadData>& liveProfiledThreads =
     ActivePS::LiveProfiledThreads(aLock);
   for (auto& thread : liveProfiledThreads) {
     RegisteredThread* registeredThread = thread.mRegisteredThread;
     if (ActivePS::FeatureJS(aLock)) {
       registeredThread->StopJSSampling();
-      if (registeredThread->Info()->ThreadId() == tid) {
+      RefPtr<ThreadInfo> info = registeredThread->Info();
+      if (info->ThreadId() == tid) {
         // We can manually poll the current thread so it stops profiling
         // immediately.
         registeredThread->PollJSSampling();
+      } else if (info->IsMainThread()) {
+        // Dispatch a runnable to the main thread to call PollJSSampling(),
+        // so that we don't have wait for the next JS interrupt callback in
+        // order to start profiling JS.
+        TriggerPollJSSamplingOnMainThread();
       }
     }
   }
 
   // The Stop() call doesn't actually stop Run(); that happens in this
   // function's caller when the sampler thread is destroyed. Stop() just gives
   // the SamplerThread a chance to do some cleanup with gPSMutex locked.
   SamplerThread* samplerThread = ActivePS::Destroy(aLock);