Backed out changeset 5937ad352b2d (bug 1542024) for perma failing 406106-1.html CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Tue, 09 Apr 2019 00:12:32 +0300
changeset 468430 5da5b22bc3a74a98ec3cc23712199a0fd0f2ffdc
parent 468429 c87d5720c98201317c94bf8655a07f32fb188e00
child 468431 b34795658d239ba6415a9ad5e72fe10d7de38265
push id35837
push userrmaries@mozilla.com
push dateTue, 09 Apr 2019 03:43:40 +0000
treeherdermozilla-central@9eb55c9bf557 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1542024, 406106
milestone68.0a1
backs out5937ad352b2dd7bd092ea73348bb7f142db73050
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 5937ad352b2d (bug 1542024) for perma failing 406106-1.html CLOSED TREE
devtools/server/tests/unit/head_dbg.js
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCWrappedJS.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/xpcprivate.h
toolkit/crashreporter/test/browser/head.js
--- a/devtools/server/tests/unit/head_dbg.js
+++ b/devtools/server/tests/unit/head_dbg.js
@@ -318,20 +318,20 @@ var listener = {
       // Make sure we exit all nested event loops so that the test can finish.
       while (DebuggerServer
              && DebuggerServer.xpcInspector
              && DebuggerServer.xpcInspector.eventLoopNestLevel > 0) {
         DebuggerServer.xpcInspector.exitNestedEventLoop();
       }
 
       // In the world before bug 997440, exceptions were getting lost because of
-      // the arbitrary JSContext being used in nsXPCWrappedJS::CallMethod.
+      // the arbitrary JSContext being used in nsXPCWrappedJSClass::CallMethod.
       // In the new world, the wanderers have returned. However, because of the,
       // currently very-broken, exception reporting machinery in
-      // nsXPCWrappedJS these get reported as errors to the console, even if
+      // XPCWrappedJSClass these get reported as errors to the console, even if
       // there's actually JS on the stack above that will catch them.  If we
       // throw an error here because of them our tests start failing.  So, we'll
       // just dump the message to the logs instead, to make sure the information
       // isn't lost.
       dumpn("head_dbg.js observed a console message: " + string);
     } catch (_) {
       // Swallow everything to avoid console reentrancy errors. We did our best
       // to log above, but apparently that didn't cut it.
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -2221,17 +2221,17 @@ class WrappedJSHolder : public nsISuppor
 
  private:
   virtual ~WrappedJSHolder() {}
 };
 
 NS_IMPL_ADDREF(WrappedJSHolder)
 NS_IMPL_RELEASE(WrappedJSHolder)
 
-// nsINamed is always supported by nsXPCWrappedJS::DelegatedQueryInterface().
+// nsINamed is always supported by nsXPCWrappedJSClass.
 // We expose this interface only for the identity in telemetry analysis.
 NS_INTERFACE_TABLE_HEAD(WrappedJSHolder)
   if (aIID.Equals(NS_GET_IID(nsINamed))) {
     return mWrappedJS->QueryInterface(aIID, aInstancePtr);
   }
   NS_INTERFACE_TABLE0(WrappedJSHolder)
 NS_INTERFACE_TABLE_TAIL
 
--- a/js/xpconnect/src/XPCWrappedJS.cpp
+++ b/js/xpconnect/src/XPCWrappedJS.cpp
@@ -171,17 +171,17 @@ nsXPCWrappedJS::AggregatedQueryInterface
   // we don't want to do this check twice in one call in the normal case:
   // once in QueryInterface and once in DelegatedQueryInterface.
   if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
     NS_ADDREF(this);
     *aInstancePtr = (void*)static_cast<nsIXPConnectWrappedJS*>(this);
     return NS_OK;
   }
 
-  return DelegatedQueryInterface(aIID, aInstancePtr);
+  return nsXPCWrappedJSClass::DelegatedQueryInterface(this, aIID, aInstancePtr);
 }
 
 NS_IMETHODIMP
 nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr) {
   if (nullptr == aInstancePtr) {
     MOZ_ASSERT(false, "null pointer");
     return NS_ERROR_NULL_POINTER;
   }
@@ -222,17 +222,17 @@ nsXPCWrappedJS::QueryInterface(REFNSIID 
 
   nsISupports* outer = GetAggregatedNativeObject();
   if (outer) {
     return outer->QueryInterface(aIID, aInstancePtr);
   }
 
   // else...
 
-  return DelegatedQueryInterface(aIID, aInstancePtr);
+  return nsXPCWrappedJSClass::DelegatedQueryInterface(this, aIID, aInstancePtr);
 }
 
 // For a description of nsXPCWrappedJS lifetime and reference counting, see
 // the comment at the top of this file.
 
 MozExternalRefCountType nsXPCWrappedJS::AddRef(void) {
   MOZ_RELEASE_ASSERT(NS_IsMainThread(),
                      "nsXPCWrappedJS::AddRef called off main thread");
@@ -324,22 +324,23 @@ nsresult nsXPCWrappedJS::GetNewOrUsed(JS
                                       nsXPCWrappedJS** wrapperResult) {
   // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
   MOZ_RELEASE_ASSERT(NS_IsMainThread(),
                      "nsXPCWrappedJS::GetNewOrUsed called off main thread");
 
   MOZ_RELEASE_ASSERT(js::GetContextCompartment(cx) ==
                      js::GetObjectCompartment(jsObj));
 
-  const nsXPTInterfaceInfo* info = GetInterfaceInfo(aIID);
+  const nsXPTInterfaceInfo* info = nsXPCWrappedJSClass::GetInterfaceInfo(aIID);
   if (!info) {
     return NS_ERROR_FAILURE;
   }
 
-  JS::RootedObject rootJSObj(cx, GetRootJSObject(cx, jsObj));
+  JS::RootedObject rootJSObj(cx,
+                             nsXPCWrappedJSClass::GetRootJSObject(cx, jsObj));
   if (!rootJSObj) {
     return NS_ERROR_FAILURE;
   }
 
   xpc::CompartmentPrivate* rootComp = xpc::CompartmentPrivate::Get(rootJSObj);
   MOZ_ASSERT(rootComp);
 
   // Find any existing wrapper.
@@ -360,17 +361,17 @@ nsresult nsXPCWrappedJS::GetNewOrUsed(JS
       wrapper.forget(wrapperResult);
       return NS_OK;
     }
   } else if (rootJSObj != jsObj) {
     // Make a new root wrapper, because there is no existing
     // root wrapper, and the wrapper we are trying to make isn't
     // a root.
     const nsXPTInterfaceInfo* rootInfo =
-        GetInterfaceInfo(NS_GET_IID(nsISupports));
+        nsXPCWrappedJSClass::GetInterfaceInfo(NS_GET_IID(nsISupports));
     if (!rootInfo) {
       return NS_ERROR_FAILURE;
     }
 
     root = new nsXPCWrappedJS(cx, rootJSObj, rootInfo, nullptr, &rv);
     if (NS_FAILED(rv)) {
       return rv;
     }
@@ -383,17 +384,20 @@ nsresult nsXPCWrappedJS::GetNewOrUsed(JS
   }
   wrapper.forget(wrapperResult);
   return NS_OK;
 }
 
 nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx, JSObject* aJSObj,
                                const nsXPTInterfaceInfo* aInfo,
                                nsXPCWrappedJS* root, nsresult* rv)
-    : mJSObj(aJSObj), mInfo(aInfo), mRoot(root ? root : this), mNext(nullptr) {
+    : mJSObj(aJSObj),
+      mInfo(aInfo),
+      mRoot(root ? root : this),
+      mNext(nullptr) {
   *rv = InitStub(mInfo->IID());
   // Continue even in the failure case, so that our refcounting/Destroy
   // behavior works correctly.
 
   // There is an extra AddRef to support weak references to wrappers
   // that are subject to finalization. See the top of the file for more
   // details.
   NS_ADDREF_THIS();
@@ -572,16 +576,29 @@ nsXPCWrappedJS* nsXPCWrappedJS::FindInhe
       return cur;
     }
   }
 
   return nullptr;
 }
 
 NS_IMETHODIMP
+nsXPCWrappedJS::CallMethod(uint16_t methodIndex, const nsXPTMethodInfo* info,
+                           nsXPTCMiniVariant* params) {
+  // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(),
+                     "nsXPCWrappedJS::CallMethod called off main thread");
+
+  if (!IsValid()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+  return nsXPCWrappedJSClass::CallMethod(this, methodIndex, info, params);
+}
+
+NS_IMETHODIMP
 nsXPCWrappedJS::GetInterfaceIID(nsIID** iid) {
   MOZ_ASSERT(iid, "bad param");
 
   *iid = GetIID().Clone();
   return NS_OK;
 }
 
 void nsXPCWrappedJS::SystemIsBeingShutDown() {
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -91,34 +91,35 @@ class MOZ_STACK_CLASS AutoSavePendingRes
   ~AutoSavePendingResult() { mXPCContext->SetPendingResult(mSavedResult); }
 
  private:
   XPCJSContext* mXPCContext;
   nsresult mSavedResult;
 };
 
 // static
-const nsXPTInterfaceInfo* nsXPCWrappedJS::GetInterfaceInfo(REFNSIID aIID) {
+const nsXPTInterfaceInfo*
+nsXPCWrappedJSClass::GetInterfaceInfo(REFNSIID aIID) {
   const nsXPTInterfaceInfo* info = nsXPTInterfaceInfo::ByIID(aIID);
   if (!info) {
     return nullptr;
   }
 
   if (info->IsBuiltinClass() || !nsXPConnect::IsISupportsDescendant(info)) {
     return nullptr;
   }
 
   return info;
 }
 
 // static
-JSObject* nsXPCWrappedJS::CallQueryInterfaceOnJSObject(JSContext* cx,
-                                                       JSObject* jsobjArg,
-                                                       HandleObject scope,
-                                                       REFNSIID aIID) {
+JSObject* nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx,
+                                                            JSObject* jsobjArg,
+                                                            HandleObject scope,
+                                                            REFNSIID aIID) {
   js::AssertSameCompartment(scope, jsobjArg);
 
   RootedObject jsobj(cx, jsobjArg);
   RootedValue arg(cx);
   RootedValue retval(cx);
   RootedObject retObj(cx);
   RootedValue fun(cx);
 
@@ -299,59 +300,58 @@ nsCString GetFunctionName(JSContext* cx,
   return displayName;
 }
 
 }  // anonymous namespace
 
 /***************************************************************************/
 
 // static
-nsresult nsXPCWrappedJS::DelegatedQueryInterface(REFNSIID aIID,
-                                                 void** aInstancePtr) {
+nsresult nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
+                                                      REFNSIID aIID,
+                                                      void** aInstancePtr) {
   if (aIID.Equals(NS_GET_IID(nsIXPConnectJSObjectHolder))) {
-    nsCOMPtr<nsIXPConnectJSObjectHolder> rval(this);
-    rval.forget(aInstancePtr);
+    NS_ADDREF(self);
+    *aInstancePtr = (void*)static_cast<nsIXPConnectJSObjectHolder*>(self);
     return NS_OK;
   }
 
   // We can't have a cached wrapper.
   if (aIID.Equals(NS_GET_IID(nsWrapperCache))) {
     *aInstancePtr = nullptr;
     return NS_NOINTERFACE;
   }
 
   // QI on an XPCWrappedJS can run script, so we need an AutoEntryScript.
   // This is inherently Gecko-specific.
   // We check both nativeGlobal and nativeGlobal->GetGlobalJSObject() even
   // though we have derived nativeGlobal from the JS global, because we know
   // there are cases where this can happen. See bug 1094953.
-  RootedObject obj(RootingCx(), GetJSObject());
+  RootedObject obj(RootingCx(), self->GetJSObject());
   nsIGlobalObject* nativeGlobal = NativeGlobal(js::UncheckedUnwrap(obj));
   NS_ENSURE_TRUE(nativeGlobal, NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(nativeGlobal->GetGlobalJSObject(), NS_ERROR_FAILURE);
   AutoEntryScript aes(nativeGlobal, "XPCWrappedJS QueryInterface",
                       /* aIsMainThread = */ true);
   XPCCallContext ccx(aes.cx());
   if (!ccx.IsValid()) {
     *aInstancePtr = nullptr;
     return NS_NOINTERFACE;
   }
 
-  // We now need to enter the realm of the actual JSObject* we are pointing at.
-  // But that may be a cross-compartment wrapper and therefore not have a
-  // well-defined realm, so enter the realm of the global that we grabbed back
-  // when we started pointing to our JSObject*.
-  RootedObject objScope(RootingCx(), GetJSObjectGlobal());
+  // We passed the unwrapped object's global to AutoEntryScript so we now need
+  // to enter the realm corresponding with the (maybe wrapper) object.
+  RootedObject objScope(RootingCx(), self->GetJSObjectGlobal());
   JSAutoRealm ar(aes.cx(), objScope);
 
   // We support nsISupportsWeakReference iff the root wrapped JSObject
   // claims to support it in its QueryInterface implementation.
   if (aIID.Equals(NS_GET_IID(nsISupportsWeakReference))) {
     // We only want to expose one implementation from our aggregate.
-    nsXPCWrappedJS* root = GetRootWrapper();
+    nsXPCWrappedJS* root = self->GetRootWrapper();
     RootedObject rootScope(ccx, root->GetJSObjectGlobal());
 
     // Fail if JSObject doesn't claim support for nsISupportsWeakReference
     if (!root->IsValid() || !CallQueryInterfaceOnJSObject(
                                 ccx, root->GetJSObject(), rootScope, aIID)) {
       *aInstancePtr = nullptr;
       return NS_NOINTERFACE;
     }
@@ -380,21 +380,21 @@ nsresult nsXPCWrappedJS::DelegatedQueryI
       }
       nsCOMPtr<nsISimpleEnumerator> res = new XPCWrappedJSIterator(jsEnum);
       res.forget(aInstancePtr);
       return NS_OK;
     }
   }
 
   // Checks for any existing wrapper explicitly constructed for this iid.
-  // This includes the current wrapper. This also deals with the
+  // This includes the current 'self' wrapper. This also deals with the
   // nsISupports case (for which it returns mRoot).
   // Also check if asking for an interface from which one of our wrappers
   // inherits.
-  if (nsXPCWrappedJS* sibling = FindOrFindInherited(aIID)) {
+  if (nsXPCWrappedJS* sibling = self->FindOrFindInherited(aIID)) {
     NS_ADDREF(sibling);
     *aInstancePtr = sibling->GetXPTCStub();
     return NS_OK;
   }
 
   // Check if the desired interface is a function interface. If so, we don't
   // want to QI, because the function almost certainly doesn't have a
   // QueryInterface property, and doesn't need one.
@@ -452,32 +452,33 @@ nsresult nsXPCWrappedJS::DelegatedQueryI
 
   // else...
   // no can do
   *aInstancePtr = nullptr;
   return NS_NOINTERFACE;
 }
 
 // static
-JSObject* nsXPCWrappedJS::GetRootJSObject(JSContext* cx, JSObject* aJSObjArg) {
+JSObject* nsXPCWrappedJSClass::GetRootJSObject(JSContext* cx,
+                                               JSObject* aJSObjArg) {
   RootedObject aJSObj(cx, aJSObjArg);
   RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
   JSObject* result =
       CallQueryInterfaceOnJSObject(cx, aJSObj, global, NS_GET_IID(nsISupports));
   if (!result) {
     result = aJSObj;
   }
   return js::UncheckedUnwrap(result);
 }
 
 // static
-bool nsXPCWrappedJS::GetArraySizeFromParam(const nsXPTMethodInfo* method,
-                                           const nsXPTType& type,
-                                           nsXPTCMiniVariant* nativeParams,
-                                           uint32_t* result) {
+bool nsXPCWrappedJSClass::GetArraySizeFromParam(const nsXPTMethodInfo* method,
+                                                const nsXPTType& type,
+                                                nsXPTCMiniVariant* nativeParams,
+                                                uint32_t* result) {
   if (type.Tag() != nsXPTType::T_LEGACY_ARRAY &&
       type.Tag() != nsXPTType::T_PSTRING_SIZE_IS &&
       type.Tag() != nsXPTType::T_PWSTRING_SIZE_IS) {
     *result = 0;
     return true;
   }
 
   uint8_t argnum = type.ArgNum();
@@ -495,20 +496,19 @@ bool nsXPCWrappedJS::GetArraySizeFromPar
     *result = *(uint32_t*)nativeParams[argnum].val.p;
   } else {
     *result = nativeParams[argnum].val.u32;
   }
   return true;
 }
 
 // static
-bool nsXPCWrappedJS::GetInterfaceTypeFromParam(const nsXPTMethodInfo* method,
-                                               const nsXPTType& type,
-                                               nsXPTCMiniVariant* nativeParams,
-                                               nsID* result) {
+bool nsXPCWrappedJSClass::GetInterfaceTypeFromParam(
+    const nsXPTMethodInfo* method, const nsXPTType& type,
+    nsXPTCMiniVariant* nativeParams, nsID* result) {
   result->Clear();
 
   const nsXPTType& inner = type.InnermostType();
   if (inner.Tag() == nsXPTType::T_INTERFACE) {
     // Directly get IID from nsXPTInterfaceInfo.
     if (!inner.GetInterface()) {
       return false;
     }
@@ -536,19 +536,19 @@ bool nsXPCWrappedJS::GetInterfaceTypeFro
     }
 
     *result = *(nsID*)ptr;
   }
   return true;
 }
 
 // static
-void nsXPCWrappedJS::CleanupOutparams(const nsXPTMethodInfo* info,
-                                      nsXPTCMiniVariant* nativeParams,
-                                      bool inOutOnly, uint8_t count) {
+void nsXPCWrappedJSClass::CleanupOutparams(const nsXPTMethodInfo* info,
+                                           nsXPTCMiniVariant* nativeParams,
+                                           bool inOutOnly, uint8_t count) {
   // clean up any 'out' params handed in
   for (uint8_t i = 0; i < count; i++) {
     const nsXPTParamInfo& param = info->GetParam(i);
     if (!param.IsOut()) {
       continue;
     }
 
     MOZ_ASSERT(param.IsIndirect(), "Outparams are always indirect");
@@ -572,22 +572,20 @@ void nsXPCWrappedJS::CleanupOutparams(co
     // Ensure our parameters are in a clean state. Complex values are always
     // handled by CleanupValue, and others have a valid null representation.
     if (!param.Type().IsComplex()) {
       param.Type().ZeroValue(nativeParams[i].val.p);
     }
   }
 }
 
-nsresult nsXPCWrappedJS::CheckForException(XPCCallContext& ccx,
-                                           AutoEntryScript& aes,
-                                           HandleObject aObj,
-                                           const char* aPropertyName,
-                                           const char* anInterfaceName,
-                                           Exception* aSyntheticException) {
+nsresult nsXPCWrappedJSClass::CheckForException(
+    XPCCallContext& ccx, AutoEntryScript& aes, HandleObject aObj,
+    const char* aPropertyName, const char* anInterfaceName,
+    Exception* aSyntheticException) {
   JSContext* cx = ccx.GetJSContext();
   MOZ_ASSERT(cx == aes.cx());
   RefPtr<Exception> xpc_exception = aSyntheticException;
   /* this one would be set by our error reporter */
 
   XPCJSContext* xpccx = ccx.GetContext();
 
   // Get this right away in case we do something below to cause JS code
@@ -732,74 +730,65 @@ nsresult nsXPCWrappedJS::CheckForExcepti
     // see if JS code signaled failure result without throwing exception
     if (NS_FAILED(pending_result)) {
       return pending_result;
     }
   }
   return NS_ERROR_FAILURE;
 }
 
-NS_IMETHODIMP
-nsXPCWrappedJS::CallMethod(uint16_t methodIndex, const nsXPTMethodInfo* info,
-                           nsXPTCMiniVariant* nativeParams) {
-  // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
-  MOZ_RELEASE_ASSERT(NS_IsMainThread(),
-                     "nsXPCWrappedJS::CallMethod called off main thread");
-
-  if (!IsValid()) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
+nsresult nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper,
+                                         uint16_t methodIndex,
+                                         const nsXPTMethodInfo* info,
+                                         nsXPTCMiniVariant* nativeParams) {
   Value* sp = nullptr;
   Value* argv = nullptr;
   uint8_t i;
   nsresult retval = NS_ERROR_FAILURE;
   bool success;
   bool readyToDoTheCall = false;
   nsID param_iid;
   bool foundDependentParam;
 
   // We're about to call into script via an XPCWrappedJS, so we need an
   // AutoEntryScript. This is probably Gecko-specific at this point, and
   // definitely will be when we turn off XPConnect for the web.
-  RootedObject obj(RootingCx(), GetJSObject());
+  RootedObject obj(RootingCx(), wrapper->GetJSObject());
   nsIGlobalObject* nativeGlobal = NativeGlobal(js::UncheckedUnwrap(obj));
   AutoEntryScript aes(nativeGlobal, "XPCWrappedJS method call",
                       /* aIsMainThread = */ true);
   XPCCallContext ccx(aes.cx());
   if (!ccx.IsValid()) {
     return retval;
   }
 
   JSContext* cx = ccx.GetJSContext();
 
   if (!cx || !info->IsReflectable()) {
     return NS_ERROR_FAILURE;
   }
 
-  const nsXPTInterfaceInfo* interfaceInfo = GetInfo();
+  const nsXPTInterfaceInfo* interfaceInfo = wrapper->GetInfo();
   JS::RootedId id(cx);
   const char* name;
   nsAutoCString symbolName;
   if (info->IsSymbol()) {
     info->GetSymbolDescription(cx, symbolName);
     name = symbolName.get();
     id = SYMBOL_TO_JSID(info->GetSymbol(cx));
   } else {
     name = info->GetName();
     if (!AtomizeAndPinJSString(cx, id.get(), name)) {
       return NS_ERROR_FAILURE;
     }
   }
 
-  // We now need to enter the realm of the actual JSObject* we are pointing at.
-  // But that may be a cross-compartment wrapper and therefore not have a
-  // well-defined realm, so enter the realm of the global that we grabbed back
-  // when we started pointing to our JSObject*.
-  RootedObject scope(cx, GetJSObjectGlobal());
+  // We passed the unwrapped object's global to AutoEntryScript so we now need
+  // to enter the realm corresponding with the (maybe wrapper) object.
+  RootedObject scope(cx, wrapper->GetJSObjectGlobal());
   JSAutoRealm ar(cx, scope);
 
   // [optional_argc] has a different calling convention, which we don't
   // support for JS-implemented components.
   if (info->WantsOptArgc()) {
     const char* str =
         "IDL methods marked with [optional_argc] may not "
         "be implemented in JS";
@@ -1096,18 +1085,18 @@ bool xpc::IsOutObject(JSContext* cx, JSO
   return js::GetObjectJSClass(obj) == &XPCOutParamClass;
 }
 
 JSObject* xpc::NewOutObject(JSContext* cx) {
   return JS_NewObject(cx, &XPCOutParamClass);
 }
 
 // static
-void nsXPCWrappedJS::DebugDumpInterfaceInfo(const nsXPTInterfaceInfo* aInfo,
-                                            int16_t depth) {
+void nsXPCWrappedJSClass::DebugDump(const nsXPTInterfaceInfo* aInfo,
+                                    int16_t depth) {
 #ifdef DEBUG
   depth--;
   XPC_LOG_ALWAYS(("nsXPTInterfaceInfo @ %p = ", aInfo));
   XPC_LOG_INDENT();
   const char* name = aInfo->Name();
   XPC_LOG_ALWAYS(("interface name is %s", name));
   char* iid = aInfo->IID().ToString();
   XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1585,16 +1585,68 @@ class XPCWrappedNative final : public ns
 ****************************************************************************
 *
 * Core classes for wrapped JSObject for use from native code...
 *
 ****************************************************************************
 ***************************************************************************/
 
 /*************************/
+// nsXPCWrappedJSClass contains a number of helper methods for using
+// nsXPTInterfaceInfo and nsXPCWrappedJS.
+
+class nsXPCWrappedJSClass final {
+ public:
+  static const nsXPTInterfaceInfo* GetInterfaceInfo(REFNSIID aIID);
+
+  static void DebugDump(const nsXPTInterfaceInfo* aInfo, int16_t depth);
+
+  static nsresult DelegatedQueryInterface(nsXPCWrappedJS* self, REFNSIID aIID,
+                                          void** aInstancePtr);
+
+  static JSObject* GetRootJSObject(JSContext* cx, JSObject* aJSObj);
+
+  static nsresult CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
+                             const nsXPTMethodInfo* info,
+                             nsXPTCMiniVariant* params);
+
+  static JSObject* CallQueryInterfaceOnJSObject(JSContext* cx, JSObject* jsobj,
+                                                JS::HandleObject scope,
+                                                REFNSIID aIID);
+
+ private:
+  // aObj is the nsXPCWrappedJS's object. We used this as the callee (or |this|
+  // if getter or setter).
+  // aSyntheticException, if not null, is the exception we should be using.
+  // If null, look for an exception on the JSContext hanging off the
+  // XPCCallContext.
+  static nsresult CheckForException(
+      XPCCallContext& ccx, mozilla::dom::AutoEntryScript& aes,
+      JS::HandleObject aObj, const char* aPropertyName,
+      const char* anInterfaceName,
+      mozilla::dom::Exception* aSyntheticException = nullptr);
+  nsXPCWrappedJSClass() = delete;
+  ~nsXPCWrappedJSClass() = delete;
+
+  static bool GetArraySizeFromParam(const nsXPTMethodInfo* method,
+                                    const nsXPTType& type,
+                                    nsXPTCMiniVariant* params,
+                                    uint32_t* result);
+
+  static bool GetInterfaceTypeFromParam(const nsXPTMethodInfo* method,
+                                        const nsXPTType& type,
+                                        nsXPTCMiniVariant* params,
+                                        nsID* result);
+
+  static void CleanupOutparams(const nsXPTMethodInfo* info,
+                               nsXPTCMiniVariant* nativeParams, bool inOutOnly,
+                               uint8_t count);
+};
+
+/*************************/
 // nsXPCWrappedJS is a wrapper for a single JSObject for use from native code.
 // nsXPCWrappedJS objects are chained together to represent the various
 // interface on the single underlying (possibly aggregate) JSObject.
 
 class nsXPCWrappedJS final : protected nsAutoXPTCStub,
                              public nsIXPConnectWrappedJSUnmarkGray,
                              public nsSupportsWeakReference,
                              public XPCRootSetElem {
@@ -1603,19 +1655,18 @@ class nsXPCWrappedJS final : protected n
   NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
   NS_DECL_NSIXPCONNECTWRAPPEDJS
   NS_DECL_NSIXPCONNECTWRAPPEDJSUNMARKGRAY
   NS_DECL_NSISUPPORTSWEAKREFERENCE
 
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsXPCWrappedJS,
                                                      nsIXPConnectWrappedJS)
 
-  // This method is defined in XPCWrappedJSClass.cpp to preserve VCS blame.
   NS_IMETHOD CallMethod(uint16_t methodIndex, const nsXPTMethodInfo* info,
-                        nsXPTCMiniVariant* nativeParams) override;
+                        nsXPTCMiniVariant* params) override;
 
   /*
    * This is rarely called directly. Instead one usually calls
    * XPCConvert::JSObject2NativeInterface which will handles cases where the
    * JS object is already a wrapped native or a DOM object.
    */
 
   static nsresult GetNewOrUsed(JSContext* cx, JS::HandleObject aJSObj,
@@ -1679,75 +1730,34 @@ class nsXPCWrappedJS final : protected n
                  "Only one aggregated native can be set");
       return;
     }
     mRoot->mOuter = aNative;
   }
 
   void TraceJS(JSTracer* trc);
 
-  // This method is defined in XPCWrappedJSClass.cpp to preserve VCS blame.
-  static void DebugDumpInterfaceInfo(const nsXPTInterfaceInfo* aInfo,
-                                     int16_t depth);
-
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
   virtual ~nsXPCWrappedJS();
 
  protected:
   nsXPCWrappedJS() = delete;
-  nsXPCWrappedJS(JSContext* cx, JSObject* aJSObj,
-                 const nsXPTInterfaceInfo* aInfo, nsXPCWrappedJS* root,
-                 nsresult* rv);
+  nsXPCWrappedJS(JSContext* cx, JSObject* aJSObj, const nsXPTInterfaceInfo* aInfo,
+                 nsXPCWrappedJS* root, nsresult* rv);
 
   bool CanSkip();
   void Destroy();
   void Unlink();
 
  private:
   JS::Compartment* Compartment() const {
     return js::GetObjectCompartment(mJSObj.unbarrieredGet());
   }
 
-  // These methods are defined in XPCWrappedJSClass.cpp to preserve VCS blame.
-  static const nsXPTInterfaceInfo* GetInterfaceInfo(REFNSIID aIID);
-
-  nsresult DelegatedQueryInterface(REFNSIID aIID, void** aInstancePtr);
-
-  static JSObject* GetRootJSObject(JSContext* cx, JSObject* aJSObj);
-
-  static JSObject* CallQueryInterfaceOnJSObject(JSContext* cx, JSObject* jsobj,
-                                                JS::HandleObject scope,
-                                                REFNSIID aIID);
-
-  // aObj is the nsXPCWrappedJS's object. We used this as the callee (or |this|
-  // if getter or setter).
-  // aSyntheticException, if not null, is the exception we should be using.
-  // If null, look for an exception on the JSContext hanging off the
-  // XPCCallContext.
-  static nsresult CheckForException(
-      XPCCallContext& ccx, mozilla::dom::AutoEntryScript& aes,
-      JS::HandleObject aObj, const char* aPropertyName,
-      const char* anInterfaceName,
-      mozilla::dom::Exception* aSyntheticException = nullptr);
-
-  static bool GetArraySizeFromParam(const nsXPTMethodInfo* method,
-                                    const nsXPTType& type,
-                                    nsXPTCMiniVariant* params,
-                                    uint32_t* result);
-
-  static bool GetInterfaceTypeFromParam(const nsXPTMethodInfo* method,
-                                        const nsXPTType& type,
-                                        nsXPTCMiniVariant* params,
-                                        nsID* result);
-
-  static void CleanupOutparams(const nsXPTMethodInfo* info,
-                               nsXPTCMiniVariant* nativeParams, bool inOutOnly,
-                               uint8_t count);
-
   JS::Heap<JSObject*> mJSObj;
   const nsXPTInterfaceInfo* const mInfo;
   nsXPCWrappedJS* mRoot;  // If mRoot != this, it is an owning pointer.
   nsXPCWrappedJS* mNext;
   nsCOMPtr<nsISupports> mOuter;  // only set in root
 };
 
 /***************************************************************************
--- a/toolkit/crashreporter/test/browser/head.js
+++ b/toolkit/crashreporter/test/browser/head.js
@@ -28,17 +28,17 @@ function make_fake_appdir() {
   _provider = {
     getFile(prop, persistent) {
       persistent.value = true;
       if (prop == "UAppData") {
         return appD.clone();
       }
       // Depending on timing we can get requests for other files.
       // When we threw an exception here, in the world before bug 997440, this got lost
-      // because of the arbitrary JSContext being used in nsXPCWrappedJS::CallMethod.
+      // because of the arbitrary JSContext being used in XPCWrappedJSClass::CallMethod.
       // After bug 997440 this gets reported to our window and causes the tests to fail.
       // So, we'll just dump out a message to the logs.
       dump("WARNING: make_fake_appdir - fake nsIDirectoryServiceProvider - Unexpected getFile for: '" + prop + "'\n");
       return null;
     },
     QueryInterface: ChromeUtils.generateQI(["nsIDirectoryServiceProvider"]),
   };
   // register our new provider