Bug 1329846 (part 1) - Remove XPCNativeScriptableInfo. r=mccr8.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 10 Jan 2017 12:47:57 +1100
changeset 376502 105d7762f4b06db69cef6fffd2129890dd9fbbb0
parent 376501 ea577d0f972a1c324810cbc490a56bd341b1d20d
child 376503 18ff0b6063121d502dd610560512ff91f46c97e9
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1329846
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1329846 (part 1) - Remove XPCNativeScriptableInfo. r=mccr8. XPCNativeScriptableInfo is now a very thin wrapper around nsIXPCScriptable, and it uses manual memory management. Removing it simplifies things quite a bit. In particular, when setting XPCWrappedNative::mScriptable in XPCWrappedNative::WrapNewGlobal() and XPCWrappedNative::Init() we no longer have to worry about sharing the XPCNativeScriptableInfo object with the proto. And XPCWrappedNative::{Init,Destroy}() have similar simplifications.
dom/base/nsDOMClassInfo.cpp
js/xpconnect/idl/nsIXPCScriptable.idl
js/xpconnect/public/xpc_map_end.h
js/xpconnect/src/XPCCallContext.cpp
js/xpconnect/src/XPCForwards.h
js/xpconnect/src/XPCInlines.h
js/xpconnect/src/XPCJSContext.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/src/XPCWrappedNativeProto.cpp
js/xpconnect/src/XPCWrapper.h
js/xpconnect/src/xpcprivate.h
js/xpconnect/wrappers/WrapperFactory.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -788,16 +788,23 @@ nsDOMClassInfo::GetScriptableFlags()
 
 // virtual
 const js::Class*
 nsDOMClassInfo::GetClass()
 {
     return &mData->mClass;
 }
 
+// virtual
+const JSClass*
+nsDOMClassInfo::GetJSClass()
+{
+    return Jsvalify(&mData->mClass);
+}
+
 NS_IMETHODIMP
 nsDOMClassInfo::PreCreate(nsISupports *nativeObj, JSContext *cx,
                           JSObject *globalObj, JSObject **parentObj)
 {
   *parentObj = globalObj;
   return NS_OK;
 }
 
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -24,16 +24,17 @@ interface nsIXPConnectWrappedNative;
 
 [ptr] native JSContextPtr(JSContext);
 [ptr] native JSObjectPtr(JSObject);
 [ptr] native JSValPtr(JS::Value);
 [ptr] native JSFreeOpPtr(JSFreeOp);
 [ref] native JSCallArgsRef(const JS::CallArgs);
 [ref] native JSAutoIdVector(JS::AutoIdVector);
 [ptr] native jsClassPtr(const js::Class);
+[ptr] native JSClassPtr(const JSClass);
 
 /**
  * Note: This is not really an XPCOM interface.  For example, callers must
  * guarantee that they set the *_retval of the various methods that return a
  * boolean to PR_TRUE before making the call.  Implementations may skip writing
  * to *_retval unless they want to return PR_FALSE.
  */
 [uuid(19b70b26-7c3f-437f-a04a-2a8f9e28b617)]
@@ -60,16 +61,17 @@ interface nsIXPCScriptable : nsISupports
     const uint32_t ALLOW_PROP_MODS_DURING_RESOLVE   = 1 << 16;
     const uint32_t ALLOW_PROP_MODS_TO_PROTOTYPE     = 1 << 17;
     const uint32_t IS_GLOBAL_OBJECT                 = 1 << 18;
     const uint32_t DONT_REFLECT_INTERFACE_NAMES     = 1 << 19;
 
     readonly attribute string className;
     [notxpcom,nostdcall] uint32_t getScriptableFlags();
     [notxpcom,nostdcall] jsClassPtr getClass();
+    [notxpcom,nostdcall] JSClassPtr getJSClass();
 
     void   preCreate(in nsISupports nativeObj, in JSContextPtr cx,
                      in JSObjectPtr globalObj, out JSObjectPtr parentObj);
 
     boolean getProperty(in nsIXPConnectWrappedNative wrapper,
                        in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
                        in JSValPtr vp);
 
--- a/js/xpconnect/public/xpc_map_end.h
+++ b/js/xpconnect/public/xpc_map_end.h
@@ -80,16 +80,23 @@ XPC_MAP_CLASSNAME::GetClass()
     static const js::ClassOps classOps =
         XPC_MAKE_CLASS_OPS(GetScriptableFlags());
     static const js::Class klass =
         XPC_MAKE_CLASS(XPC_MAP_QUOTED_CLASSNAME, GetScriptableFlags(),
                        &classOps);
     return &klass;
 }
 
+// virtual
+const JSClass*
+XPC_MAP_CLASSNAME::GetJSClass()
+{
+    return Jsvalify(GetClass());
+}
+
 /**************************************************************/
 
 #ifndef XPC_MAP_WANT_PRECREATE
 NS_IMETHODIMP XPC_MAP_CLASSNAME::PreCreate(nsISupports* nativeObj, JSContext * cx, JSObject * globalObj, JSObject * *parentObj)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 #ifndef XPC_MAP_WANT_GETPROPERTY
--- a/js/xpconnect/src/XPCCallContext.cpp
+++ b/js/xpconnect/src/XPCCallContext.cpp
@@ -65,21 +65,18 @@ XPCCallContext::XPCCallContext(JSContext
     if (IS_WN_CLASS(clasp)) {
         mWrapper = XPCWrappedNative::Get(unwrapped);
     } else if (IS_TEAROFF_CLASS(clasp)) {
         mTearOff = (XPCWrappedNativeTearOff*)js::GetObjectPrivate(unwrapped);
         mWrapper = XPCWrappedNative::Get(
           &js::GetReservedSlot(unwrapped,
                                XPC_WN_TEAROFF_FLAT_OBJECT_SLOT).toObject());
     }
-    if (mWrapper) {
-        if (mTearOff)
-            mScriptableInfo = nullptr;
-        else
-            mScriptableInfo = mWrapper->GetScriptableInfo();
+    if (mWrapper && !mTearOff) {
+        mScriptable = mWrapper->GetScriptable();
     }
 
     if (!JSID_IS_VOID(name))
         SetName(name);
 
     if (argc != NO_ARGS)
         SetArgsAndResultPtr(argc, argv, rval);
 
--- a/js/xpconnect/src/XPCForwards.h
+++ b/js/xpconnect/src/XPCForwards.h
@@ -23,17 +23,16 @@ class nsXPCWrappedJSClass;
 
 class XPCNativeMember;
 class XPCNativeInterface;
 class XPCNativeSet;
 
 class XPCWrappedNative;
 class XPCWrappedNativeProto;
 class XPCWrappedNativeTearOff;
-class XPCNativeScriptableInfo;
 class XPCNativeScriptableCreateInfo;
 
 class XPCTraceableVariant;
 class XPCJSObjectHolder;
 
 class JSObject2WrappedJSMap;
 class Native2WrappedNativeMap;
 class IID2WrappedJSClassMap;
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -94,21 +94,21 @@ XPCCallContext::CanGetTearOff() const
 
 inline XPCWrappedNativeTearOff*
 XPCCallContext::GetTearOff() const
 {
     CHECK_STATE(HAVE_OBJECT);
     return mTearOff;
 }
 
-inline XPCNativeScriptableInfo*
-XPCCallContext::GetScriptableInfo() const
+inline nsIXPCScriptable*
+XPCCallContext::GetScriptable() const
 {
     CHECK_STATE(HAVE_OBJECT);
-    return mScriptableInfo;
+    return mScriptable;
 }
 
 inline bool
 XPCCallContext::CanGetSet() const
 {
     return mState >= HAVE_NAME;
 }
 
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -3526,31 +3526,30 @@ XPCJSContext::JSContextInitialized(JSCon
 
     return true;
 }
 
 bool
 XPCJSContext::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
                                     char (&name)[72]) const
 {
-    XPCNativeScriptableInfo* si = nullptr;
 
     if (!IS_PROTO_CLASS(clasp)) {
         return false;
     }
 
     XPCWrappedNativeProto* p =
         static_cast<XPCWrappedNativeProto*>(xpc_GetJSPrivate(obj));
-    si = p->GetScriptableInfo();
-
-    if (!si) {
+    nsCOMPtr<nsIXPCScriptable> scr = p->GetScriptable();
+    if (!scr) {
         return false;
     }
 
-    SprintfLiteral(name, "JS Object (%s - %s)", clasp->name, si->GetJSClass()->name);
+    SprintfLiteral(name, "JS Object (%s - %s)",
+                   clasp->name, scr->GetJSClass()->name);
     return true;
 }
 
 bool
 XPCJSContext::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* obj,
                                              nsCycleCollectionTraversalCallback& cb) const
 {
     if (clasp != &XPC_WN_Tearoff_JSClass) {
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -45,19 +45,20 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XP
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(XPCWrappedNative)
     if (!tmp->IsValid())
         return NS_OK;
 
     if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
         char name[72];
-        XPCNativeScriptableInfo* si = tmp->GetScriptableInfo();
-        if (si)
-            SprintfLiteral(name, "XPCWrappedNative (%s)", si->GetJSClass()->name);
+        nsCOMPtr<nsIXPCScriptable> scr = tmp->GetScriptable();
+        if (scr)
+            SprintfLiteral(name, "XPCWrappedNative (%s)",
+                           scr->GetJSClass()->name);
         else
             SprintfLiteral(name, "XPCWrappedNative");
 
         cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
     } else {
         NS_IMPL_CYCLE_COLLECTION_DESCRIBE(XPCWrappedNative, tmp->mRefCnt.get())
     }
 
@@ -165,23 +166,23 @@ XPCWrappedNative::WrapNewGlobal(xpcObjec
 
     // Put together the ScriptableCreateInfo...
     XPCNativeScriptableCreateInfo sciProto;
     XPCNativeScriptableCreateInfo sciMaybe;
     const XPCNativeScriptableCreateInfo& sciWrapper =
         GatherScriptableCreateInfo(identity, nativeHelper.GetClassInfo(),
                                    sciProto, sciMaybe);
 
-    // ...and then ScriptableInfo. We need all this stuff now because it's going
-    // to tell us the JSClass of the object we're going to create.
-    XPCNativeScriptableInfo* si = XPCNativeScriptableInfo::Construct(&sciWrapper);
-    MOZ_ASSERT(si);
+    // ...and then get the nsIXPCScriptable. This will tell us the JSClass of
+    // the object we're going to create.
+    nsCOMPtr<nsIXPCScriptable> scr = sciWrapper.GetCallback();
+    MOZ_ASSERT(scr);
 
     // Finally, we get to the JSClass.
-    const JSClass* clasp = si->GetJSClass();
+    const JSClass* clasp = scr->GetJSClass();
     MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
 
     // Create the global.
     aOptions.creationOptions().setTrace(XPCWrappedNative::Trace);
     if (xpc::SharedMemoryEnabled())
         aOptions.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
     RootedObject global(cx, xpc::CreateGlobalObject(cx, clasp, principal, aOptions));
     if (!global)
@@ -216,33 +217,17 @@ XPCWrappedNative::WrapNewGlobal(xpcObjec
     RefPtr<XPCWrappedNative> wrapper =
         new XPCWrappedNative(nativeHelper.forgetCanonical(), proto);
 
     //
     // We don't call ::Init() on this wrapper, because our setup requirements
     // are different for globals. We do our setup inline here, instead.
     //
 
-    // Share mScriptableInfo with the proto.
-    //
-    // This is probably more trouble than it's worth, since we've already
-    // created an XPCNativeScriptableInfo for ourselves. Nevertheless, this is
-    // what ::Init() does, and we want to be as consistent as possible with
-    // that code.
-    XPCNativeScriptableInfo* siProto = proto->GetScriptableInfo();
-    if (siProto && siProto->GetCallback() == sciWrapper.GetCallback()) {
-        wrapper->mScriptableInfo = siProto;
-        // XPCNativeScriptableInfo uses manual memory management. If we're
-        // switching over to that of the proto, we need to destroy the one
-        // we've allocated.
-        delete si;
-        si = nullptr;
-    } else {
-        wrapper->mScriptableInfo = si;
-    }
+    wrapper->mScriptable = scr;
 
     // Set the JS object to the global we already created.
     wrapper->mFlatJSObject = global;
     wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
 
     // Set the private to the XPCWrappedNative.
     JS_SetPrivate(global, wrapper);
 
@@ -545,36 +530,34 @@ XPCWrappedNative::GetUsedOnly(nsISupport
     wrapper.forget(resultWrapper);
     return NS_OK;
 }
 
 // This ctor is used if this object will have a proto.
 XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
                                    XPCWrappedNativeProto* aProto)
     : mMaybeProto(aProto),
-      mSet(aProto->GetSet()),
-      mScriptableInfo(nullptr)
+      mSet(aProto->GetSet())
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     mIdentity = aIdentity;
     mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
 
     MOZ_ASSERT(mMaybeProto, "bad ctor param");
     MOZ_ASSERT(mSet, "bad ctor param");
 }
 
 // This ctor is used if this object will NOT have a proto.
 XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
                                    XPCWrappedNativeScope* aScope,
                                    already_AddRefed<XPCNativeSet>&& aSet)
 
     : mMaybeScope(TagScope(aScope)),
-      mSet(aSet),
-      mScriptableInfo(nullptr)
+      mSet(aSet)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     mIdentity = aIdentity;
     mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
 
     MOZ_ASSERT(aScope, "bad ctor param");
     MOZ_ASSERT(mSet, "bad ctor param");
@@ -583,24 +566,17 @@ XPCWrappedNative::XPCWrappedNative(alrea
 XPCWrappedNative::~XPCWrappedNative()
 {
     Destroy();
 }
 
 void
 XPCWrappedNative::Destroy()
 {
-    XPCWrappedNativeProto* proto = GetProto();
-
-    if (mScriptableInfo &&
-        (!HasProto() ||
-         (proto && proto->GetScriptableInfo() != mScriptableInfo))) {
-        delete mScriptableInfo;
-        mScriptableInfo = nullptr;
-    }
+    mScriptable = nullptr;
 
     XPCWrappedNativeScope* scope = GetScope();
     if (scope) {
         Native2WrappedNativeMap* map = scope->GetWrappedNativeMap();
 
         // Post-1.9 we should not remove this wrapper from the map if it is
         // uninitialized.
         map->Remove(this);
@@ -724,40 +700,30 @@ XPCWrappedNative::GatherScriptableCreate
 
     return sciProto;
 }
 
 bool
 XPCWrappedNative::Init(const XPCNativeScriptableCreateInfo* sci)
 {
     AutoJSContext cx;
-    // setup our scriptable info...
 
-    if (sci->GetCallback()) {
-        if (HasProto()) {
-            XPCNativeScriptableInfo* siProto = GetProto()->GetScriptableInfo();
-            if (siProto && siProto->GetCallback() == sci->GetCallback())
-                mScriptableInfo = siProto;
-        }
-        if (!mScriptableInfo) {
-            mScriptableInfo = XPCNativeScriptableInfo::Construct(sci);
-
-            if (!mScriptableInfo)
-                return false;
-        }
-    }
-    XPCNativeScriptableInfo* si = mScriptableInfo;
+    // Setup our scriptable...
+    MOZ_ASSERT(!mScriptable);
+    mScriptable = sci->GetCallback();
 
     // create our flatJSObject
 
-    const JSClass* jsclazz = si ? si->GetJSClass() : Jsvalify(&XPC_WN_NoHelper_JSClass);
+    const JSClass* jsclazz = mScriptable
+                           ? mScriptable->GetJSClass()
+                           : Jsvalify(&XPC_WN_NoHelper_JSClass);
 
     // We should have the global jsclass flag if and only if we're a global.
-    MOZ_ASSERT_IF(si, !!si->GetCallback()->IsGlobalObject() ==
-                      !!(jsclazz->flags & JSCLASS_IS_GLOBAL));
+    MOZ_ASSERT_IF(mScriptable, !!mScriptable->IsGlobalObject() ==
+                               !!(jsclazz->flags & JSCLASS_IS_GLOBAL));
 
     MOZ_ASSERT(jsclazz &&
                jsclazz->name &&
                jsclazz->flags &&
                jsclazz->getResolve() &&
                jsclazz->hasFinalize(), "bad class");
 
     // XXXbz JS_GetObjectPrototype wants an object, even though it then asserts
@@ -954,17 +920,17 @@ XPCWrappedNative::SystemIsBeingShutDown(
     mFlatJSObject = nullptr;
     mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
 
     XPCWrappedNativeProto* proto = GetProto();
 
     if (HasProto())
         proto->SystemIsBeingShutDown();
 
-    // We don't destroy mScriptableInfo here. The destructor will do it.
+    // We don't clear mScriptable here. The destructor will do it.
 
     // Cleanup the tearoffs.
     for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
         if (JSObject* jso = to->GetJSObjectPreserveColor()) {
             JS_SetPrivate(jso, nullptr);
             to->SetJSObject(nullptr);
         }
         // We leak the tearoff mNative
@@ -1090,18 +1056,18 @@ XPCWrappedNative::InitTearOff(XPCWrapped
     nsISupports* identity = GetIdentityObject();
 
     // This is an nsRefPtr instead of an nsCOMPtr because it may not be the
     // canonical nsISupports for this object.
     RefPtr<nsISupports> qiResult;
 
     // If the scriptable helper forbids us from reflecting additional
     // interfaces, then don't even try the QI, just fail.
-    if (mScriptableInfo &&
-        mScriptableInfo->GetCallback()->ClassInfoInterfacesOnly() &&
+    if (mScriptable &&
+        mScriptable->ClassInfoInterfacesOnly() &&
         !mSet->HasInterface(aInterface) &&
         !mSet->HasInterfaceWithAncestor(aInterface)) {
         return NS_ERROR_NO_INTERFACE;
     }
 
     // We are about to call out to other code.
     // So protect our intended tearoff.
 
@@ -2147,24 +2113,22 @@ NS_IMETHODIMP XPCWrappedNative::DebugDum
 
         if (depth && mSet)
             mSet->DebugDump(depth);
         else
             XPC_LOG_ALWAYS(("mSet @ %x", mSet.get()));
 
         XPC_LOG_ALWAYS(("mFlatJSObject of %x", mFlatJSObject.unbarrieredGetPtr()));
         XPC_LOG_ALWAYS(("mIdentity of %x", mIdentity.get()));
-        XPC_LOG_ALWAYS(("mScriptableInfo @ %x", mScriptableInfo));
+        XPC_LOG_ALWAYS(("mScriptable @ %x", mScriptable.get()));
 
-        if (depth && mScriptableInfo) {
-            nsCOMPtr<nsIXPCScriptable> scr = mScriptableInfo->GetCallback();
+        if (depth && mScriptable) {
             XPC_LOG_INDENT();
-            XPC_LOG_ALWAYS(("mScriptable @ %x", scr.get()));
-            XPC_LOG_ALWAYS(("mFlags of %x", scr->GetScriptableFlags()));
-            XPC_LOG_ALWAYS(("mJSClass @ %x", mScriptableInfo->GetJSClass()));
+            XPC_LOG_ALWAYS(("mFlags of %x", mScriptable->GetScriptableFlags()));
+            XPC_LOG_ALWAYS(("mJSClass @ %x", mScriptable->GetJSClass()));
             XPC_LOG_OUTDENT();
         }
     XPC_LOG_OUTDENT();
 #endif
     return NS_OK;
 }
 
 /***************************************************************************/
@@ -2180,19 +2144,19 @@ XPCWrappedNative::ToString(XPCWrappedNat
 #  define FMT_ADDR ""
 #  define FMT_STR(str)
 #  define PARAM_ADDR(w)
 #endif
 
     char* sz = nullptr;
     char* name = nullptr;
 
-    XPCNativeScriptableInfo* si = GetScriptableInfo();
-    if (si)
-        name = JS_smprintf("%s", si->GetJSClass()->name);
+    nsCOMPtr<nsIXPCScriptable> scr = GetScriptable();
+    if (scr)
+        name = JS_smprintf("%s", scr->GetJSClass()->name);
     if (to) {
         const char* fmt = name ? " (%s)" : "%s";
         name = JS_sprintf_append(name, fmt,
                                  to->GetInterface()->GetNameString());
     } else if (!name) {
         XPCNativeSet* set = GetSet();
         XPCNativeInterface** array = set->GetInterfaceArray();
         RefPtr<XPCNativeInterface> isupp = XPCNativeInterface::GetISupports();
@@ -2213,17 +2177,17 @@ XPCWrappedNative::ToString(XPCWrappedNat
         }
     }
 
     if (!name) {
         return nullptr;
     }
     const char* fmt = "[xpconnect wrapped %s" FMT_ADDR FMT_STR(" (native")
         FMT_ADDR FMT_STR(")") "]";
-    if (si) {
+    if (scr) {
         fmt = "[object %s" FMT_ADDR FMT_STR(" (native") FMT_ADDR FMT_STR(")") "]";
     }
     sz = JS_smprintf(fmt, name PARAM_ADDR(this) PARAM_ADDR(mIdentity.get()));
 
     JS_smprintf_free(name);
 
 
     return sz;
@@ -2262,22 +2226,20 @@ static void DEBUG_CheckClassInfoClaims(X
         // Houston, We have a problem...
 
         char* className = nullptr;
         char* contractID = nullptr;
         const char* interfaceName;
 
         info->GetNameShared(&interfaceName);
         clsInfo->GetContractID(&contractID);
-        if (wrapper->GetScriptableInfo()) {
-            wrapper->GetScriptableInfo()->GetCallback()->
-                GetClassName(&className);
+        if (wrapper->GetScriptable()) {
+            wrapper->GetScriptable()->GetClassName(&className);
         }
 
-
         printf("\n!!! Object's nsIClassInfo lies about its interfaces!!!\n"
                "   classname: %s \n"
                "   contractid: %s \n"
                "   unimplemented interface name: %s\n\n",
                className ? className : "<unknown>",
                contractID ? contractID : "<unknown>",
                interfaceName);
 
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -224,17 +224,17 @@ DefinePropertyIfFound(XPCCallContext& cc
                       HandleId idArg,
                       XPCNativeSet* set,
                       XPCNativeInterface* ifaceArg,
                       XPCNativeMember* member,
                       XPCWrappedNativeScope* scope,
                       bool reflectToStringAndToSource,
                       XPCWrappedNative* wrapperToReflectInterfaceNames,
                       XPCWrappedNative* wrapperToReflectDoubleWrap,
-                      XPCNativeScriptableInfo* scriptableInfo,
+                      nsIXPCScriptable* scr,
                       unsigned propFlags,
                       bool* resolved)
 {
     RootedId id(ccx, idArg);
     RefPtr<XPCNativeInterface> iface = ifaceArg;
     XPCJSContext* xpccx = ccx.GetContext();
     bool found;
     const char* name;
@@ -249,19 +249,18 @@ DefinePropertyIfFound(XPCCallContext& cc
     } else
         found = (nullptr != (member = iface->FindMember(id)));
 
     if (!found) {
         if (reflectToStringAndToSource) {
             JSNative call;
             uint32_t flags = 0;
 
-            if (scriptableInfo) {
-                nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(
-                    scriptableInfo->GetCallback());
+            if (scr) {
+                nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(scr);
 
                 if (classInfo) {
                     nsresult rv = classInfo->GetFlags(&flags);
                     if (NS_FAILED(rv))
                         return Throw(rv, ccx);
                 }
             }
 
@@ -410,18 +409,18 @@ DefinePropertyIfFound(XPCCallContext& cc
                 *resolved = true;
             desc.attributesRef() |= JSPROP_RESOLVING;
             return JS_DefinePropertyById(ccx, obj, id, desc);
         }
     }
 
     if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING) ||
         id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE) ||
-        (scriptableInfo &&
-         scriptableInfo->GetCallback()->DontEnumQueryInterface() &&
+        (scr &&
+         scr->DontEnumQueryInterface() &&
          id == xpccx->GetStringID(XPCJSContext::IDX_QUERY_INTERFACE)))
         propFlags &= ~JSPROP_ENUMERATE;
 
     RootedValue funval(ccx);
     if (!member->NewFunctionObject(ccx, iface, obj, funval.address()))
         return false;
 
     if (member->IsMethod()) {
@@ -544,17 +543,17 @@ WrappedNativeFinalize(js::FreeOp* fop, J
         mozilla::dom::DestroyProtoAndIfaceCache(obj);
     }
     nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
     if (!p)
         return;
 
     XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
     if (helperType == WN_HELPER)
-        wrapper->GetScriptableCallback()->Finalize(wrapper, js::CastToJSFreeOp(fop), obj);
+        wrapper->GetScriptable()->Finalize(wrapper, js::CastToJSFreeOp(fop), obj);
     wrapper->FlatJSObjectFinalized();
 }
 
 static void
 WrappedNativeObjectMoved(JSObject* obj, const JSObject* old)
 {
     nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
     if (!p)
@@ -699,17 +698,17 @@ XPC_WN_MaybeResolvingDeletePropertyStub(
         return false;                                                         \
     }                                                                         \
     if (!IS_WN_REFLECTOR(unwrapped)) {                                        \
         return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);                    \
     }                                                                         \
     XPCWrappedNative* wrapper = XPCWrappedNative::Get(unwrapped);             \
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);                             \
     bool retval = true;                                                       \
-    nsresult rv = wrapper->GetScriptableCallback()->
+    nsresult rv = wrapper->GetScriptable()->
 
 #define POST_HELPER_STUB                                                      \
     if (NS_FAILED(rv))                                                        \
         return Throw(rv, cx);                                                 \
     return retval;
 
 #define POST_HELPER_STUB_WITH_OBJECTOPRESULT(failMethod)                      \
     if (NS_FAILED(rv))                                                        \
@@ -792,25 +791,25 @@ XPC_WN_Helper_Resolve(JSContext* cx, Han
     bool retval = true;
     bool resolved = false;
     XPCCallContext ccx(cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
     RootedId old(cx, ccx.SetResolveName(id));
 
-    XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
-    if (si && si->GetCallback()->WantResolve()) {
+    nsCOMPtr<nsIXPCScriptable> scr = wrapper->GetScriptable();
+    if (scr && scr->WantResolve()) {
         XPCWrappedNative* oldResolvingWrapper;
-        bool allowPropMods = si->GetCallback()->AllowPropModsDuringResolve();
+        bool allowPropMods = scr->AllowPropModsDuringResolve();
 
         if (allowPropMods)
             oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
 
-        rv = si->GetCallback()->Resolve(wrapper, cx, obj, id, &resolved, &retval);
+        rv = scr->Resolve(wrapper, cx, obj, id, &resolved, &retval);
 
         if (allowPropMods)
             (void)ccx.SetResolvingWrapper(oldResolvingWrapper);
     }
 
     old = ccx.SetResolveName(old);
     MOZ_ASSERT(old == id, "bad nest");
 
@@ -830,93 +829,81 @@ XPC_WN_Helper_Resolve(JSContext* cx, Han
         XPCNativeMember* member = nullptr;
         RefPtr<XPCNativeInterface> iface;
         bool IsLocal = false;
 
         if (set->FindMember(id, &member, &iface, protoSet, &IsLocal) &&
             IsLocal) {
 
             XPCWrappedNative* wrapperForInterfaceNames =
-                (si && si->GetCallback()->DontReflectInterfaceNames())
-                ? nullptr
-                : wrapper;
+                (scr && scr->DontReflectInterfaceNames()) ? nullptr : wrapper;
 
             XPCWrappedNative* oldResolvingWrapper =
                 ccx.SetResolvingWrapper(wrapper);
             retval = DefinePropertyIfFound(ccx, obj, id,
                                            set, iface, member,
                                            wrapper->GetScope(),
                                            false,
                                            wrapperForInterfaceNames,
-                                           nullptr, si,
+                                           nullptr, scr,
                                            JSPROP_ENUMERATE, resolvedp);
             (void)ccx.SetResolvingWrapper(oldResolvingWrapper);
         }
     }
 
     return retval;
 }
 
 bool
 XPC_WN_Helper_Enumerate(JSContext* cx, HandleObject obj)
 {
     XPCCallContext ccx(cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
-    XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
-    if (!si || !si->GetCallback()->WantEnumerate())
+    nsCOMPtr<nsIXPCScriptable> scr = wrapper->GetScriptable();
+    if (!scr || !scr->WantEnumerate())
         return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
 
     if (!XPC_WN_Shared_Enumerate(cx, obj))
         return false;
 
     bool retval = true;
-    nsresult rv = si->GetCallback()->Enumerate(wrapper, cx, obj, &retval);
+    nsresult rv = scr->Enumerate(wrapper, cx, obj, &retval);
     if (NS_FAILED(rv))
         return Throw(rv, cx);
     return retval;
 }
 
 /***************************************************************************/
 
 static bool
 XPC_WN_JSOp_Enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
                       bool enumerableOnly)
 {
     XPCCallContext ccx(cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
-    XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
-    if (!si || !si->GetCallback()->WantNewEnumerate())
+    nsCOMPtr<nsIXPCScriptable> scr = wrapper->GetScriptable();
+    if (!scr || !scr->WantNewEnumerate())
         return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
 
     if (!XPC_WN_Shared_Enumerate(cx, obj))
         return false;
 
     bool retval = true;
-    nsresult rv = si->GetCallback()->NewEnumerate(wrapper, cx, obj, properties, &retval);
+    nsresult rv = scr->NewEnumerate(wrapper, cx, obj, properties, &retval);
     if (NS_FAILED(rv))
         return Throw(rv, cx);
     return retval;
 }
 
 /***************************************************************************/
 
-// static
-XPCNativeScriptableInfo*
-XPCNativeScriptableInfo::Construct(const XPCNativeScriptableCreateInfo* sci)
-{
-    MOZ_ASSERT(sci, "bad param");
-    nsCOMPtr<nsIXPCScriptable> callback = sci->GetCallback();
-    MOZ_ASSERT(callback);
-    return new XPCNativeScriptableInfo(callback);
-}
-
 const js::ObjectOps XPC_WN_ObjectOpsWithEnumerate = {
     nullptr,  // lookupProperty
     nullptr,  // defineProperty
     nullptr,  // hasProperty
     nullptr,  // getProperty
     nullptr,  // setProperty
     nullptr,  // getOwnPropertyDescriptor
     nullptr,  // deleteProperty
@@ -1108,21 +1095,21 @@ XPC_WN_ModsAllowed_Proto_Resolve(JSConte
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if (!self)
         return false;
 
     XPCCallContext ccx(cx);
     if (!ccx.IsValid())
         return false;
 
-    XPCNativeScriptableInfo* si = self->GetScriptableInfo();
+    nsCOMPtr<nsIXPCScriptable> scr = self->GetScriptable();
     return DefinePropertyIfFound(ccx, obj, id,
                                  self->GetSet(), nullptr, nullptr,
                                  self->GetScope(),
-                                 true, nullptr, nullptr, si,
+                                 true, nullptr, nullptr, scr,
                                  JSPROP_ENUMERATE, resolvep);
 }
 
 static const js::ClassOps XPC_WN_ModsAllowed_Proto_JSClassOps = {
     nullptr,                            // addProperty
     nullptr,                            // delProperty
     nullptr,                            // getProperty
     nullptr,                            // setProperty
@@ -1185,22 +1172,22 @@ XPC_WN_NoMods_Proto_Resolve(JSContext* c
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if (!self)
         return false;
 
     XPCCallContext ccx(cx);
     if (!ccx.IsValid())
         return false;
 
-    XPCNativeScriptableInfo* si = self->GetScriptableInfo();
+    nsCOMPtr<nsIXPCScriptable> scr = self->GetScriptable();
 
     return DefinePropertyIfFound(ccx, obj, id,
                                  self->GetSet(), nullptr, nullptr,
                                  self->GetScope(),
-                                 true, nullptr, nullptr, si,
+                                 true, nullptr, nullptr, scr,
                                  JSPROP_READONLY |
                                  JSPROP_PERMANENT |
                                  JSPROP_ENUMERATE, resolvedp);
 }
 
 static const js::ClassOps XPC_WN_NoMods_Proto_JSClassOps = {
     XPC_WN_OnlyIWrite_Proto_AddPropertyStub,   // addProperty
     XPC_WN_CannotDeletePropertyStub,           // delProperty
--- a/js/xpconnect/src/XPCWrappedNativeProto.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -16,18 +16,17 @@ int32_t XPCWrappedNativeProto::gDEBUG_Li
 #endif
 
 XPCWrappedNativeProto::XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
                                              nsIClassInfo* ClassInfo,
                                              already_AddRefed<XPCNativeSet>&& Set)
     : mScope(Scope),
       mJSProtoObject(nullptr),
       mClassInfo(ClassInfo),
-      mSet(Set),
-      mScriptableInfo(nullptr)
+      mSet(Set)
 {
     // This native object lives as long as its associated JSObject - killed
     // by finalization of the JSObject (or explicitly if Init fails).
 
     MOZ_COUNT_CTOR(XPCWrappedNativeProto);
     MOZ_ASSERT(mScope);
 
 #ifdef DEBUG
@@ -43,38 +42,31 @@ XPCWrappedNativeProto::~XPCWrappedNative
 
 #ifdef DEBUG
     gDEBUG_LiveProtoCount--;
 #endif
 
     // Note that our weak ref to mScope is not to be trusted at this point.
 
     XPCNativeSet::ClearCacheEntryForClassInfo(mClassInfo);
-
-    delete mScriptableInfo;
 }
 
 bool
 XPCWrappedNativeProto::Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
                             bool callPostCreatePrototype)
 {
     AutoJSContext cx;
-    nsIXPCScriptable* callback = scriptableCreateInfo ?
-                                 scriptableCreateInfo->GetCallback() :
-                                 nullptr;
-    if (callback) {
-        mScriptableInfo =
-            XPCNativeScriptableInfo::Construct(scriptableCreateInfo);
-        if (!mScriptableInfo)
-            return false;
-    }
+    nsCOMPtr<nsIXPCScriptable> callback = scriptableCreateInfo
+                                        ? scriptableCreateInfo->GetCallback() :
+                                        : nullptr;
+    if (callback)
+        mScriptable = callback;
 
     const js::Class* jsclazz =
-        (mScriptableInfo &&
-         mScriptableInfo->GetCallback()->AllowPropModsToPrototype())
+        (mScriptable && mScriptable->AllowPropModsToPrototype())
         ? &XPC_WN_ModsAllowed_Proto_JSClass
         : &XPC_WN_NoMods_Proto_JSClass;
 
     JS::RootedObject global(cx, mScope->GetGlobalJSObject());
     JS::RootedObject proto(cx, JS_GetObjectPrototype(cx, global));
     mJSProtoObject = JS_NewObjectWithUniqueType(cx, js::Jsvalify(jsclazz),
                                                 proto);
 
@@ -89,24 +81,22 @@ XPCWrappedNativeProto::Init(const XPCNat
 }
 
 bool
 XPCWrappedNativeProto::CallPostCreatePrototype()
 {
     AutoJSContext cx;
 
     // Nothing to do if we don't have a scriptable callback.
-    nsIXPCScriptable* callback = mScriptableInfo ? mScriptableInfo->GetCallback()
-                                                 : nullptr;
-    if (!callback)
+    if (!mScriptable)
         return true;
 
     // Call the helper. This can handle being called if it's not implemented,
     // so we don't have to check any sort of "want" here. See xpc_map_end.h.
-    nsresult rv = callback->PostCreatePrototype(cx, mJSProtoObject);
+    nsresult rv = mScriptable->PostCreatePrototype(cx, mJSProtoObject);
     if (NS_FAILED(rv)) {
         JS_SetPrivate(mJSProtoObject, nullptr);
         mJSProtoObject = nullptr;
         XPCThrower::Throw(rv, cx);
         return false;
     }
 
     return true;
@@ -188,22 +178,20 @@ XPCWrappedNativeProto::DebugDump(int16_t
 #ifdef DEBUG
     depth-- ;
     XPC_LOG_ALWAYS(("XPCWrappedNativeProto @ %x", this));
     XPC_LOG_INDENT();
         XPC_LOG_ALWAYS(("gDEBUG_LiveProtoCount is %d", gDEBUG_LiveProtoCount));
         XPC_LOG_ALWAYS(("mScope @ %x", mScope));
         XPC_LOG_ALWAYS(("mJSProtoObject @ %x", mJSProtoObject.get()));
         XPC_LOG_ALWAYS(("mSet @ %x", mSet.get()));
-        XPC_LOG_ALWAYS(("mScriptableInfo @ %x", mScriptableInfo));
-        if (depth && mScriptableInfo) {
-            nsCOMPtr<nsIXPCScriptable> scr = mScriptableInfo->GetCallback();
+        XPC_LOG_ALWAYS(("mScriptable @ %x", mScriptable.get()));
+        if (depth && mScriptable) {
             XPC_LOG_INDENT();
-            XPC_LOG_ALWAYS(("mScriptable @ %x", scr.get()));
-            XPC_LOG_ALWAYS(("mFlags of %x", scr->GetScriptableFlags()));
-            XPC_LOG_ALWAYS(("mJSClass @ %x", mScriptableInfo->GetJSClass()));
+            XPC_LOG_ALWAYS(("mFlags of %x", mScriptable->GetScriptableFlags()));
+            XPC_LOG_ALWAYS(("mJSClass @ %x", mScriptable->GetJSClass()));
             XPC_LOG_OUTDENT();
         }
     XPC_LOG_OUTDENT();
 #endif
 }
 
 
--- a/js/xpconnect/src/XPCWrapper.h
+++ b/js/xpconnect/src/XPCWrapper.h
@@ -11,18 +11,18 @@
 
 namespace XPCNativeWrapper {
 
 // Given an XPCWrappedNative pointer and the name of a function on
 // nsIXPCScriptable corresponding with a flag, returns 'true' if the flag is
 // set.
 // XXX Convert to using GetFlags() and not a macro.
 #define NATIVE_HAS_FLAG(_wn, _flag)                                           \
-  ((_wn)->GetScriptableInfo() &&                                              \
-   (_wn)->GetScriptableInfo()->GetCallback()->_flag())
+  ((_wn)->GetScriptable() &&                                                  \
+   (_wn)->GetScriptable()->_flag())
 
 bool
 AttachNewConstructorObject(JSContext* aCx, JS::HandleObject aGlobalObject);
 
 } // namespace XPCNativeWrapper
 
 // This namespace wraps some common functionality between the three existing
 // wrappers. Its main purpose is to allow XPCCrossOriginWrapper to act both
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -53,18 +53,17 @@
  * outside of the existing set, the set will grow. All QueryInterface results
  * are cached in XPCWrappedNativeTearOff objects, which are linked off of the
  * XPCWrappedNative.
  *
  * Besides having class info, a C++ object may be "scriptable" (i.e., implement
  * nsIXPCScriptable). This allows it to implement a more DOM-like interface,
  * besides just exposing XPCOM methods and constants. An nsIXPCScriptable
  * instance has hooks that correspond to all the normal JSClass hooks. Each
- * nsIXPCScriptable instance is mirrored by an XPCNativeScriptableInfo in
- * XPConnect. These can have pointers from XPCWrappedNativeProto and
+ * nsIXPCScriptable instance can have pointers from XPCWrappedNativeProto and
  * XPCWrappedNative (since C++ objects can have scriptable info without having
  * class info).
  */
 
 /* All the XPConnect private declarations - only include locally. */
 
 #ifndef xpcprivate_h___
 #define xpcprivate_h___
@@ -698,17 +697,17 @@ public:
     inline JSObject*                    GetFlattenedJSObject() const ;
     inline nsISupports*                 GetIdentityObject() const ;
     inline XPCWrappedNative*            GetWrapper() const ;
     inline XPCWrappedNativeProto*       GetProto() const ;
 
     inline bool                         CanGetTearOff() const ;
     inline XPCWrappedNativeTearOff*     GetTearOff() const ;
 
-    inline XPCNativeScriptableInfo*     GetScriptableInfo() const ;
+    inline nsIXPCScriptable*            GetScriptable() const ;
     inline bool                         CanGetSet() const ;
     inline XPCNativeSet*                GetSet() const ;
     inline bool                         CanGetInterface() const ;
     inline XPCNativeInterface*          GetInterface() const ;
     inline XPCNativeMember*             GetMember() const ;
     inline bool                         HasInterfaceAndMember() const ;
     inline jsid                         GetName() const ;
     inline bool                         GetStaticMemberIsLocal() const ;
@@ -774,17 +773,17 @@ private:
 
     // ctor does not necessarily init the following. BEWARE!
 
     XPCCallContext*                 mPrevCallContext;
 
     XPCWrappedNative*               mWrapper;
     XPCWrappedNativeTearOff*        mTearOff;
 
-    XPCNativeScriptableInfo*        mScriptableInfo;
+    nsCOMPtr<nsIXPCScriptable>      mScriptable;
 
     RefPtr<XPCNativeSet>            mSet;
     RefPtr<XPCNativeInterface>      mInterface;
     XPCNativeMember*                mMember;
 
     JS::RootedId                    mName;
     bool                            mStaticMemberIsLocal;
 
@@ -1379,56 +1378,18 @@ class XPCNativeSet final
     uint16_t                mMemberCount;
     uint16_t                mInterfaceCount;
     // Always last - object sized for array.
     // These are strong references.
     XPCNativeInterface*     mInterfaces[1];
 };
 
 /***************************************************************************/
-// XPCNativeScriptableInfo is a trivial wrapper for nsIXPCScriptable which
-// should be removed eventually.
-
-class XPCNativeScriptableInfo final
-{
-public:
-    static XPCNativeScriptableInfo*
-    Construct(const XPCNativeScriptableCreateInfo* sci);
-
-    nsIXPCScriptable*
-    GetCallback() const { return mCallback; }
-
-    const JSClass*
-    GetJSClass() { return Jsvalify(mCallback->GetClass()); }
-
-protected:
-    explicit XPCNativeScriptableInfo(nsIXPCScriptable* aCallback)
-        : mCallback(aCallback)
-    {
-        MOZ_COUNT_CTOR(XPCNativeScriptableInfo);
-    }
-public:
-    ~XPCNativeScriptableInfo()
-    {
-        MOZ_COUNT_DTOR(XPCNativeScriptableInfo);
-    }
-private:
-
-    // disable copy ctor and assignment
-    XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r) = delete;
-    XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r) = delete;
-
-private:
-    nsCOMPtr<nsIXPCScriptable> mCallback;
-};
-
-/***************************************************************************/
 // XPCNativeScriptableCreateInfo is used in creating new wrapper and protos.
-// it abstracts out the scriptable interface pointer and the flags. After
-// creation these are factored differently using XPCNativeScriptableInfo.
+// It abstracts out the scriptable interface pointer and the flags.
 
 class MOZ_STACK_CLASS XPCNativeScriptableCreateInfo final
 {
 public:
     XPCNativeScriptableCreateInfo() {}
 
     nsIXPCScriptable*
     GetCallback() const {return mCallback;}
@@ -1464,18 +1425,18 @@ public:
     GetJSProtoObject() const { return mJSProtoObject; }
 
     nsIClassInfo*
     GetClassInfo()     const {return mClassInfo;}
 
     XPCNativeSet*
     GetSet()           const {return mSet;}
 
-    XPCNativeScriptableInfo*
-    GetScriptableInfo()   {return mScriptableInfo;}
+    nsIXPCScriptable*
+    GetScriptable() const { return mScriptable; }
 
     bool CallPostCreatePrototype();
     void JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj);
     void JSProtoObjectMoved(JSObject* obj, const JSObject* old);
 
     void SystemIsBeingShutDown();
 
     void DebugDump(int16_t depth);
@@ -1524,17 +1485,17 @@ private:
     static int32_t gDEBUG_LiveProtoCount;
 #endif
 
 private:
     XPCWrappedNativeScope*   mScope;
     JS::ObjectPtr            mJSProtoObject;
     nsCOMPtr<nsIClassInfo>   mClassInfo;
     RefPtr<XPCNativeSet>     mSet;
-    XPCNativeScriptableInfo* mScriptableInfo;
+    nsCOMPtr<nsIXPCScriptable> mScriptable;
 };
 
 /***********************************************/
 // XPCWrappedNativeTearOff represents the info needed to make calls to one
 // interface on the underlying native object of a XPCWrappedNative.
 
 class XPCWrappedNativeTearOff final
 {
@@ -1687,21 +1648,18 @@ public:
 private:
     inline void
     ExpireWrapper()
         {mMaybeScope = (XPCWrappedNativeScope*)
                        (XPC_SCOPE_WORD(mMaybeScope) | XPC_WRAPPER_EXPIRED);}
 
 public:
 
-    XPCNativeScriptableInfo*
-    GetScriptableInfo() const {return mScriptableInfo;}
-
-    nsIXPCScriptable*      // call this wrong and you deserve to crash
-    GetScriptableCallback() const  {return mScriptableInfo->GetCallback();}
+    nsIXPCScriptable*
+    GetScriptable() const { return mScriptable; }
 
     nsIClassInfo*
     GetClassInfo() const {return IsValid() && HasProto() ?
                             GetProto()->GetClassInfo() : nullptr;}
 
     bool
     HasMutatedSet() const {return IsValid() &&
                                   (!HasProto() ||
@@ -1839,17 +1797,17 @@ public:
 private:
     union
     {
         XPCWrappedNativeScope* mMaybeScope;
         XPCWrappedNativeProto* mMaybeProto;
     };
     RefPtr<XPCNativeSet> mSet;
     JS::TenuredHeap<JSObject*> mFlatJSObject;
-    XPCNativeScriptableInfo* mScriptableInfo;
+    nsCOMPtr<nsIXPCScriptable> mScriptable;
     XPCWrappedNativeTearOff mFirstTearOff;
 };
 
 /***************************************************************************
 ****************************************************************************
 *
 * Core classes for wrapped JSObject for use from native code...
 *
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -206,17 +206,17 @@ WrapperFactory::PrepareForWrapping(JSCon
         if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) {
             // We have a precreate hook. This object might enforce that we only
             // ever create JS object for it.
 
             // Note: this penalizes objects that only have one wrapper, but are
             // being accessed across compartments. We would really prefer to
             // replace the above code with a test that says "do you only have one
             // wrapper?"
-            nsresult rv = wn->GetScriptableInfo()->GetCallback()->
+            nsresult rv = wn->GetScriptable()->
                 PreCreate(wn->Native(), cx, scope, wrapScope.address());
             if (NS_FAILED(rv)) {
                 retObj.set(waive ? WaiveXray(cx, obj) : obj);
                 return;
             }
 
             // If the handed back scope differs from the passed-in scope and is in
             // a separate compartment, then this object is explicitly requesting
@@ -245,17 +245,17 @@ WrapperFactory::PrepareForWrapping(JSCon
                 // case, we want to return the existing wrapper.
                 //
                 // So we do a trick: call PreCreate _again_, but say that we're
                 // wrapping for the old scope, rather than the new one. If (1) is
                 // the case, then PreCreate will return the scope we pass to it
                 // (the old scope). If (2) is the case, PreCreate will return the
                 // scope of the document (the new scope).
                 RootedObject probe(cx);
-                rv = wn->GetScriptableInfo()->GetCallback()->
+                rv = wn->GetScriptable()->
                     PreCreate(wn->Native(), cx, currentScope, probe.address());
 
                 // Check for case (2).
                 if (probe != currentScope) {
                     MOZ_ASSERT(probe == wrapScope);
                     retObj.set(waive ? WaiveXray(cx, obj) : obj);
                     return;
                 }
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1574,18 +1574,17 @@ XPCWrappedNativeXrayTraits::call(JSConte
     // Run the call hook of the wrapped native.
     XPCWrappedNative* wn = getWN(wrapper);
     if (NATIVE_HAS_FLAG(wn, WantCall)) {
         XPCCallContext ccx(cx, wrapper, nullptr, JSID_VOIDHANDLE, args.length(),
                            args.array(), args.rval().address());
         if (!ccx.IsValid())
             return false;
         bool ok = true;
-        nsresult rv = wn->GetScriptableInfo()->GetCallback()->Call(
-            wn, cx, wrapper, args, &ok);
+        nsresult rv = wn->GetScriptable()->Call(wn, cx, wrapper, args, &ok);
         if (NS_FAILED(rv)) {
             if (ok)
                 XPCThrower::Throw(rv, cx);
             return false;
         }
     }
 
     return true;
@@ -1600,18 +1599,18 @@ XPCWrappedNativeXrayTraits::construct(JS
     // Run the construct hook of the wrapped native.
     XPCWrappedNative* wn = getWN(wrapper);
     if (NATIVE_HAS_FLAG(wn, WantConstruct)) {
         XPCCallContext ccx(cx, wrapper, nullptr, JSID_VOIDHANDLE, args.length(),
                            args.array(), args.rval().address());
         if (!ccx.IsValid())
             return false;
         bool ok = true;
-        nsresult rv = wn->GetScriptableInfo()->GetCallback()->Construct(
-            wn, cx, wrapper, args, &ok);
+        nsresult rv =
+            wn->GetScriptable()->Construct(wn, cx, wrapper, args, &ok);
         if (NS_FAILED(rv)) {
             if (ok)
                 XPCThrower::Throw(rv, cx);
             return false;
         }
     }
 
     return true;