Bug 1477432 - Part 9: Switch to using plain JS objects for nsIJS[IC]ID, r=mccr8
authorNika Layzell <nika@thelayzells.com>
Fri, 20 Jul 2018 18:53:40 -0400
changeset 446904 3439b17bdc2a1bf5626355d529c9a3decf3843be
parent 446903 1ddcdfc06526ffa9a997987ae7a2cfff320c00e9
child 446905 29c30b62c301eacd5fefd9a78f19cf63fafd889a
push id35053
push userapavel@mozilla.com
push dateSat, 17 Nov 2018 11:27:54 +0000
treeherdermozilla-central@e4deec61fc8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1477432
milestone65.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 1477432 - Part 9: Switch to using plain JS objects for nsIJS[IC]ID, r=mccr8 This is a complete rewrite of the interface while maintaining the same APIs. Each ID is fully-contained within a single object, does not require a finalizer, and is cheap to create. Beyond using reserved slots, this code avoids using custom ClassOps, instead preferring Symbol.hasInstance and eager constants. One major change which occurred in this patch was the move from storing a nsCID to storing the ContractID for JSCID objects. This eliminates the need for the 'refreshCID' method, and hopefully shouldn't have performance implications. If we discover that there are performance problems there, we can look into stashing the CID, and re-introduce 'refreshCID', despite its surprising behaviour. Differential Revision: https://phabricator.services.mozilla.com/D2286
dom/base/MozQueryInterface.cpp
dom/xhr/XMLHttpRequestMainThread.cpp
js/xpconnect/idl/moz.build
js/xpconnect/idl/xpcjsid.idl
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCJSID.cpp
js/xpconnect/src/XPCModule.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
testing/modules/AppInfo.jsm
--- a/dom/base/MozQueryInterface.cpp
+++ b/dom/base/MozQueryInterface.cpp
@@ -8,17 +8,16 @@
 #include "MozQueryInterface.h"
 #include "nsIException.h"
 
 #include <string.h>
 
 #include "jsapi.h"
 
 #include "xpcpublic.h"
-#include "xpcjsid.h"
 
 namespace mozilla {
 namespace dom {
 
 constexpr size_t IID_SIZE = sizeof(nsIID);
 
 static_assert(IID_SIZE == 16,
               "Size of nsID struct changed. Please ensure this code is still valid.");
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -82,17 +82,16 @@
 #include "mozilla/Attributes.h"
 #include "MultipartBlobImpl.h"
 #include "nsIPermissionManager.h"
 #include "nsMimeTypes.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIClassOfService.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsStreamListenerWrapper.h"
-#include "xpcjsid.h"
 #include "nsITimedChannel.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsZipArchive.h"
 #include "mozilla/Preferences.h"
 #include "private/pprio.h"
 #include "XMLHttpRequestUpload.h"
 
 // Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
--- a/js/xpconnect/idl/moz.build
+++ b/js/xpconnect/idl/moz.build
@@ -6,13 +6,12 @@
 
 XPIDL_SOURCES += [
     'mozIJSSubScriptLoader.idl',
     'nsIXPConnect.idl',
     'nsIXPCScriptable.idl',
     'xpccomponents.idl',
     'xpcIJSGetFactory.idl',
     'xpcIJSWeakReference.idl',
-    'xpcjsid.idl',
 ]
 
 XPIDL_MODULE = 'xpconnect'
 
deleted file mode 100644
--- a/js/xpconnect/idl/xpcjsid.idl
+++ /dev/null
@@ -1,41 +0,0 @@
-/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-[ptr] native const_nsID_ptr(const nsID);
-
-[builtinclass, scriptable, uuid(62883d14-4146-4039-94f5-a5e1e1a51a15)]
-interface nsIJSID : nsISupports
-{
-    readonly attribute string  name;
-    readonly attribute string  number;
-    readonly attribute boolean valid;
-
-    boolean equals(in nsIJSID other);
-    string toString();
-
-    [noscript] void initialize(in string idString);
-
-    // returns a pointer to the internal nsID. this pointer is only valid
-    // while the nsIJSID object remains alive!
-    [notxpcom] const_nsID_ptr getID();
-};
-
-[builtinclass, scriptable, uuid(e76ec564-a080-4705-8609-384c755ec91e)]
-interface nsIJSIID : nsIJSID
-{
-};
-
-[builtinclass, scriptable, uuid(bf5eb086-9eaa-4694-aec3-fe4aac6119bd)]
-interface nsIJSCID : nsIJSID
-{
-    // re-generate the internal CID value from this nsIJSID's ContractID.
-    void refreshCID();
-
-    [implicit_jscontext,optional_argc] jsval createInstance([optional] in jsval iid);
-    [implicit_jscontext,optional_argc] jsval getService([optional] in jsval iid);
-};
-
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -438,30 +438,25 @@ nsXPCComponents_Classes::Resolve(nsIXPCo
                                  JSContext* cx, JSObject* objArg,
                                  jsid idArg, bool* resolvedp,
                                  bool* _retval)
 
 {
     RootedId id(cx, idArg);
     RootedObject obj(cx, objArg);
 
-    JSAutoByteString name;
+    RootedValue cidv(cx);
     if (JSID_IS_STRING(id) &&
-        name.encodeLatin1(cx, JSID_TO_STRING(id)) &&
-        name.ptr()[0] != '{') { // we only allow contractids here
-        RootedValue cidv(cx);
-        if (xpc::ContractID2JSValue(cx, nsDependentCString(name.ptr()),
-                                    &cidv)) {
-            *resolvedp = true;
-            *_retval = JS_DefinePropertyById(cx, obj, id, cidv,
-                                             JSPROP_ENUMERATE |
-                                             JSPROP_READONLY |
-                                             JSPROP_PERMANENT |
-                                             JSPROP_RESOLVING);
-        }
+        xpc::ContractID2JSValue(cx, JSID_TO_STRING(id), &cidv)) {
+        *resolvedp = true;
+        *_retval = JS_DefinePropertyById(cx, obj, id, cidv,
+                                         JSPROP_ENUMERATE |
+                                         JSPROP_READONLY |
+                                         JSPROP_PERMANENT |
+                                         JSPROP_RESOLVING);
     }
     return NS_OK;
 }
 
 
 /***************************************************************************/
 /***************************************************************************/
 /***************************************************************************/
--- a/js/xpconnect/src/XPCJSID.cpp
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -2,441 +2,375 @@
 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* An xpcom implementation of the JavaScript nsIID and nsCID objects. */
 
 #include "xpcprivate.h"
-#include "xpc_make_class.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
-#include "mozilla/StaticPtr.h"
 
 using namespace mozilla::dom;
 using namespace JS;
 
-/***************************************************************************/
-// nsJSID
-
-#define NULL_CID                                                              \
-{ 0x00000000, 0x0000, 0x0000,                                                 \
-  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+namespace xpc {
 
-NS_IMPL_CLASSINFO(nsJSID, nullptr, 0, NULL_CID)
-NS_IMPL_ISUPPORTS_CI(nsJSID, nsIJSID)
-
-const char nsJSID::gNoString[] = "";
-
-nsJSID::nsJSID()
-    : mID(GetInvalidIID()),
-      mNumber(const_cast<char*>(gNoString)),
-      mName(const_cast<char*>(gNoString))
-{
-}
+/******************************************************************************
+ * # Generic IDs #
+ *
+ * Generic IDs are the type of JS object created by most code which passes nsID
+ * objects to JavaScript code. They provide no special methods, and only store
+ * the raw nsID value.
+ *
+ * The nsID value is stored in 4 reserved slots, with 32 bits of the 128-bit
+ * value stored in each slot. Getter code extracts this data, and combines them
+ * back into the nsID value.
+ */
+static bool ID_Equals(JSContext* aCx, unsigned aArgc, Value* aVp);
+static bool ID_GetNumber(JSContext* aCx, unsigned aArgc, Value* aVp);
 
-nsJSID::~nsJSID()
-{
-    if (mNumber && mNumber != gNoString) {
-        free(mNumber);
-    }
-    if (mName && mName != gNoString) {
-        free(mName);
-    }
-}
+// Generic ID objects contain 4 reserved slots, each containing a uint32_t with
+// 1/4 of the representation of the nsID value. This allows us to avoid an extra
+// allocation for the nsID object, and eliminates the need for a finalizer.
+enum { kID_Slot0, kID_Slot1, kID_Slot2, kID_Slot3, kID_SlotCount };
+static const js::Class sID_Class = {
+    "nsJSID",
+    JSCLASS_HAS_RESERVED_SLOTS(kID_SlotCount),
+    JS_NULL_CLASS_OPS
+};
 
-void nsJSID::Reset()
-{
-    mID = GetInvalidIID();
 
-    if (mNumber && mNumber != gNoString) {
-        free(mNumber);
-    }
-    if (mName && mName != gNoString) {
-        free(mName);
-    }
-
-    mNumber = mName = nullptr;
-}
+/******************************************************************************
+ * # Interface IDs #
+ *
+ * In addition to the properties exposed by Generic ID objects, IID supports
+ * 'instanceof', exposes constant properties defined on the class, and exposes
+ * the interface name as the 'name' and 'toString()' values.
+ */
+static bool IID_HasInstance(JSContext* aCx, unsigned aArgc, Value* aVp);
+static bool IID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp);
 
-void
-nsJSID::SetName(const char* name)
-{
-    MOZ_ASSERT(!mName || mName == gNoString, "name already set");
-    MOZ_ASSERT(name, "null name");
-    mName = NS_xstrdup(name);
-}
+// Interface ID objects use a single reserved slot containing a pointer to the
+// nsXPTInterfaceInfo object for the interface in question.
+enum { kIID_InfoSlot, kIID_SlotCount };
+static const js::Class sIID_Class = {
+    "nsJSIID",
+    JSCLASS_HAS_RESERVED_SLOTS(kIID_SlotCount),
+    JS_NULL_CLASS_OPS
+};
 
-NS_IMETHODIMP
-nsJSID::GetName(char * *aName)
-{
-    if (!aName) {
-        return NS_ERROR_NULL_POINTER;
-    }
 
-    if (!NameIsSet()) {
-        SetNameToNoString();
-    }
-    MOZ_ASSERT(mName, "name not set");
-    *aName = NS_xstrdup(mName);
-    return NS_OK;
-}
+/******************************************************************************
+ * # Contract IDs #
+ *
+ * In addition to the properties exposed by Generic ID objects, Contract IDs
+ * expose 'getService' and 'createInstance' methods, and expose the contractID
+ * string as '.name' and '.toString()'.
+ */
+static bool CID_CreateInstance(JSContext* aCx, unsigned aArgc, Value* aVp);
+static bool CID_GetService(JSContext* aCx, unsigned aArgc, Value* aVp);
+static bool CID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp);
 
-NS_IMETHODIMP
-nsJSID::GetNumber(char * *aNumber)
-{
-    if (!aNumber) {
-        return NS_ERROR_NULL_POINTER;
-    }
+// ContractID objects use a single reserved slot, containing the ContractID. The
+// nsCID value for this object is looked up when the object is being unwrapped.
+enum { kCID_ContractSlot, kCID_SlotCount };
+static const js::Class sCID_Class = {
+    "nsJSCID",
+    JSCLASS_HAS_RESERVED_SLOTS(kCID_SlotCount),
+    JS_NULL_CLASS_OPS
+};
 
-    if (!mNumber) {
-        if (!(mNumber = mID.ToString())) {
-            mNumber = const_cast<char*>(gNoString);
-        }
-    }
 
-    *aNumber = NS_xstrdup(mNumber);
-    return NS_OK;
-}
-
-NS_IMETHODIMP_(const nsID*)
-nsJSID::GetID()
+/**
+ * Ensure that the nsID prototype objects have been created for the current
+ * global, and extract the prototype values.
+ */
+static JSObject*
+GetIDPrototype(JSContext* aCx, const js::Class* aClass)
 {
-    return &mID;
-}
-
-NS_IMETHODIMP
-nsJSID::GetValid(bool* aValid)
-{
-    if (!aValid) {
-        return NS_ERROR_NULL_POINTER;
-    }
-
-    *aValid = IsValid();
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsJSID::Equals(nsIJSID* other, bool* _retval)
-{
-    if (!_retval) {
-        return NS_ERROR_NULL_POINTER;
-    }
-
-    if (!other || mID.Equals(GetInvalidIID())) {
-        *_retval = false;
-        return NS_OK;
-    }
-
-    *_retval = other->GetID()->Equals(mID);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsJSID::Initialize(const char* idString)
-{
-    if (!idString) {
-        return NS_ERROR_NULL_POINTER;
-    }
-
-    if (*idString != '\0' && mID.Equals(GetInvalidIID())) {
-        Reset();
-
-        if (idString[0] == '{') {
-            if (mID.Parse(idString)) {
-                return NS_OK;
-            }
-
-            // error - reset to invalid state
-            mID = GetInvalidIID();
-        }
-    }
-    return NS_ERROR_FAILURE;
-}
-
-void
-nsJSID::InitWithName(const nsID& id, const char* nameString)
-{
-    MOZ_ASSERT(nameString, "no name");
-    Reset();
-    mID = id;
-    SetName(nameString);
-}
-
-// try to use the name, if no name, then use the number
-NS_IMETHODIMP
-nsJSID::ToString(char** _retval)
-{
-    if (mName && mName != gNoString) {
-        return GetName(_retval);
-    }
-
-    return GetNumber(_retval);
-}
-
-const nsID&
-nsJSID::GetInvalidIID() const
-{
-    // {BB1F47B0-D137-11d2-9841-006008962422}
-    static const nsID invalid = {0xbb1f47b0, 0xd137, 0x11d2,
-                                  {0x98, 0x41, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22}};
-    return invalid;
-}
-
-//static
-already_AddRefed<nsJSID>
-nsJSID::NewID(const char* str)
-{
-    if (!str) {
-        NS_ERROR("no string");
+    XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(aCx));
+    if (NS_WARN_IF(!scope)) {
         return nullptr;
     }
 
-    RefPtr<nsJSID> idObj = new nsJSID();
-    NS_ENSURE_SUCCESS(idObj->Initialize(str), nullptr);
-    return idObj.forget();
+    // Create prototype objects for the JSID objects if they haven't been
+    // created for this scope yet.
+    if (!scope->mIDProto) {
+        MOZ_ASSERT(!scope->mIIDProto && !scope->mCIDProto);
+
+        RootedObject idProto(aCx, JS_NewPlainObject(aCx));
+        RootedObject iidProto(aCx, JS_NewObjectWithGivenProto(aCx, nullptr, idProto));
+        RootedObject cidProto(aCx, JS_NewObjectWithGivenProto(aCx, nullptr, idProto));
+        RootedId hasInstance(aCx, SYMBOL_TO_JSID(GetWellKnownSymbol(aCx, SymbolCode::hasInstance)));
+
+        const uint32_t kFlags = JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT;
+        const uint32_t kNoEnum = JSPROP_READONLY | JSPROP_PERMANENT;
+
+        bool ok = idProto && iidProto && cidProto &&
+            // Methods and properties on all ID Objects:
+            JS_DefineFunction(aCx, idProto, "equals", ID_Equals, 1, kFlags) &&
+            JS_DefineProperty(aCx, idProto, "number", ID_GetNumber, nullptr, kFlags) &&
+
+            // Methods for IfaceID objects, which also inherit ID properties:
+            JS_DefineFunctionById(aCx, iidProto, hasInstance, IID_HasInstance, 1, kNoEnum) &&
+            JS_DefineProperty(aCx, iidProto, "name", IID_GetName, nullptr, kFlags) &&
+
+            // Methods for ContractID objects, which also inherit ID properties:
+            JS_DefineFunction(aCx, cidProto, "createInstance", CID_CreateInstance, 1, kFlags) &&
+            JS_DefineFunction(aCx, cidProto, "getService", CID_GetService, 1, kFlags) &&
+            JS_DefineProperty(aCx, cidProto, "name", CID_GetName, nullptr, kFlags) &&
+
+            // ToString returns '.number' on generic IDs, while returning
+            // '.name' on other ID types.
+            JS_DefineFunction(aCx, idProto, "toString", ID_GetNumber, 0, kFlags) &&
+            JS_DefineFunction(aCx, iidProto, "toString", IID_GetName, 0, kFlags) &&
+            JS_DefineFunction(aCx, cidProto, "toString", CID_GetName, 0, kFlags);
+        if (!ok) {
+            return nullptr;
+        }
+
+        scope->mIDProto = idProto;
+        scope->mIIDProto = iidProto;
+        scope->mCIDProto = cidProto;
+    }
+
+    if (aClass == &sID_Class) {
+        return scope->mIDProto;
+    } else if (aClass == &sIID_Class) {
+        return scope->mIIDProto;
+    } else if (aClass == &sCID_Class) {
+        return scope->mCIDProto;
+    }
+
+    MOZ_CRASH("Unrecognized ID Object Class");
 }
 
-//static
-already_AddRefed<nsJSID>
-nsJSID::NewID(const nsID& id)
+// Unwrap the given value to an object with the correct class, or nullptr.
+static JSObject*
+GetIDObject(HandleValue aVal, const Class* aClass)
 {
-    RefPtr<nsJSID> idObj = new nsJSID();
-    idObj->mID = id;
-    idObj->mName = nullptr;
-    idObj->mNumber = nullptr;
-    return idObj.forget();
+    if (aVal.isObject()) {
+        JSObject* obj = js::CheckedUnwrap(&aVal.toObject());
+        if (obj && js::GetObjectClass(obj) == aClass) {
+            return obj;
+        }
+    }
+    return nullptr;
 }
 
 
-/***************************************************************************/
-// Class object support so that we can share prototypes of wrapper
-
-// This class exists just so we can have a shared scriptable helper for
-// the nsJSIID class. The instances implement their own helpers. But we
-// needed to be able to indicate to the shared prototypes this single flag:
-// XPC_SCRIPTABLE_DONT_ENUM_STATIC_PROPS. And having a class to do it is
-// the only means we have. Setting this flag on any given instance scriptable
-// helper is not sufficient to convey the information that we don't want
-// static properties enumerated on the shared proto.
-
-class SharedScriptableHelperForJSIID final : public nsIXPCScriptable
-{
-    ~SharedScriptableHelperForJSIID() {}
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIXPCSCRIPTABLE
-    SharedScriptableHelperForJSIID() {}
-};
-
-NS_IMPL_ISUPPORTS(SharedScriptableHelperForJSIID,
-                  nsIXPCScriptable)
-
-// The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME SharedScriptableHelperForJSIID
-#define XPC_MAP_QUOTED_CLASSNAME "JSIID"
-#define XPC_MAP_FLAGS XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE
-#include "xpc_map_end.h" /* This will #undef the above */
-
-static mozilla::StaticRefPtr<nsIXPCScriptable> gSharedScriptableHelperForJSIID;
-static bool gClassObjectsWereInited = false;
-
-static void EnsureClassObjectsInitialized()
-{
-    if (!gClassObjectsWereInited) {
-        gSharedScriptableHelperForJSIID = new SharedScriptableHelperForJSIID();
-
-        gClassObjectsWereInited = true;
-    }
-}
-
-static nsresult GetSharedScriptableHelperForJSIID(nsIXPCScriptable** helper)
-{
-    EnsureClassObjectsInitialized();
-    nsCOMPtr<nsIXPCScriptable> temp = gSharedScriptableHelperForJSIID.get();
-    temp.forget(helper);
-    return NS_OK;
-}
-
-/******************************************************/
-
-NS_DECL_CI_INTERFACE_GETTER(nsJSIID)
-NS_IMPL_CLASSINFO(nsJSIID, GetSharedScriptableHelperForJSIID, 0, NULL_CID)
-
-NS_DECL_CI_INTERFACE_GETTER(nsJSCID)
-NS_IMPL_CLASSINFO(nsJSCID, nullptr, 0, NULL_CID)
-
-void xpc_DestroyJSxIDClassObjects()
+/**
+ * Unwrap an nsID object from a JSValue.
+ *
+ * For Generic ID objects, this function will extract the nsID from reserved
+ * slots. For IfaceID objects, it will be extracted from the nsXPTInterfaceInfo,
+ * and for ContractID objects, the ContractID's corresponding CID will be looked
+ * up.
+ */
+Maybe<nsID>
+JSValue2ID(JSContext* aCx, HandleValue aVal)
 {
-    if (gClassObjectsWereInited) {
-        NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSIID));
-        NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSCID));
-        gSharedScriptableHelperForJSIID = nullptr;
-
-        gClassObjectsWereInited = false;
-    }
-}
-
-/***************************************************************************/
-
-NS_IMPL_QUERY_INTERFACE_CI(nsJSIID,
-                           nsIJSID,
-                           nsIJSIID,
-                           nsIXPCScriptable)
-
-NS_IMPL_ADDREF(nsJSIID)
-NS_IMPL_RELEASE(nsJSIID)
-NS_IMPL_CI_INTERFACE_GETTER(nsJSIID, nsIJSID, nsIJSIID)
-
-// The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME         nsJSIID
-#define XPC_MAP_QUOTED_CLASSNAME "nsJSIID"
-#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
-                       XPC_SCRIPTABLE_WANT_ENUMERATE | \
-                       XPC_SCRIPTABLE_WANT_HASINSTANCE | \
-                       XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
-#include "xpc_map_end.h" /* This will #undef the above */
-
-
-nsJSIID::nsJSIID(const nsXPTInterfaceInfo* aInfo)
-    : mInfo(aInfo)
-{
-}
-
-nsJSIID::~nsJSIID() {}
-
-// If mInfo is present we use it and ignore mDetails, else we use mDetails.
-
-NS_IMETHODIMP nsJSIID::GetName(char * *aName)
-{
-    *aName = moz_xstrdup(mInfo->Name());
-    return NS_OK;
-}
-
-NS_IMETHODIMP nsJSIID::GetNumber(char * *aNumber)
-{
-    char str[NSID_LENGTH];
-    mInfo->IID().ToProvidedString(str);
-    *aNumber = (char*) moz_xmemdup(str, NSID_LENGTH);
-    return NS_OK;
-}
-
-NS_IMETHODIMP_(const nsID*) nsJSIID::GetID()
-{
-    return &mInfo->IID();
-}
-
-NS_IMETHODIMP nsJSIID::GetValid(bool* aValid)
-{
-    *aValid = true;
-    return NS_OK;
-}
-
-NS_IMETHODIMP nsJSIID::Equals(nsIJSID* other, bool* _retval)
-{
-    if (!_retval) {
-        return NS_ERROR_NULL_POINTER;
+    if (!aVal.isObject()) {
+        return Nothing();
     }
 
-    if (!other) {
-        *_retval = false;
-        return NS_OK;
-    }
-
-    *_retval = mInfo->IID() == *other->GetID();
-    return NS_OK;
-}
-
-NS_IMETHODIMP nsJSIID::Initialize(const char* idString)
-{
-    return NS_ERROR_FAILURE;
-}
+    mozilla::Maybe<nsID> id;
+    RootedObject obj(aCx, js::CheckedUnwrap(&aVal.toObject()));
+    if (js::GetObjectClass(obj) == &sID_Class) {
+        // Extract the raw bytes of the nsID from reserved slots.
+        uint32_t rawid[] = {
+            js::GetReservedSlot(obj, kID_Slot0).toPrivateUint32(),
+            js::GetReservedSlot(obj, kID_Slot1).toPrivateUint32(),
+            js::GetReservedSlot(obj, kID_Slot2).toPrivateUint32(),
+            js::GetReservedSlot(obj, kID_Slot3).toPrivateUint32()
+        };
 
-NS_IMETHODIMP nsJSIID::ToString(char** _retval)
-{
-    *_retval = moz_xstrdup(mInfo->Name());
-    return NS_OK;
-}
+        // Construct a nsID inside the Maybe, and copy the rawid into it.
+        id.emplace();
+        memcpy(id.ptr(), &rawid, sizeof(nsID));
+    } else if (js::GetObjectClass(obj) == &sIID_Class) {
+        // IfaceID objects store a nsXPTInterfaceInfo* pointer.
+        auto* info = static_cast<const nsXPTInterfaceInfo*>(
+            js::GetReservedSlot(obj, kIID_InfoSlot).toPrivate());
+        id.emplace(info->IID());
+    } else if (js::GetObjectClass(obj) == &sCID_Class) {
+        // ContractID objects store a ContractID string.
+        JS::UniqueChars contractId = JS_EncodeStringToLatin1(aCx,
+            js::GetReservedSlot(obj, kCID_ContractSlot).toString());
 
-// static
-already_AddRefed<nsJSIID>
-nsJSIID::NewID(const nsXPTInterfaceInfo* aInfo)
-{
-    if (!aInfo) {
-        NS_ERROR("no info");
-        return nullptr;
+        // NOTE(nika): If we directly access the nsComponentManager, we can do
+        // this with a more-basic pointer lookup:
+        //     nsFactoryEntry* entry = nsComponentManagerImpl::gComponentManager->
+        //         GetFactoryEntry(contractId.ptr(), contractId.length());
+        //     if (entry) id.emplace(entry->mCIDEntry->cid);
+
+        nsCOMPtr<nsIComponentRegistrar> registrar;
+        nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
+        if (NS_FAILED(rv) || !registrar) {
+            return Nothing();
+        }
+
+        nsCID* cid = nullptr;
+        if (NS_SUCCEEDED(registrar->ContractIDToCID(contractId.get(), &cid))) {
+            id.emplace(*cid);
+            free(cid);
+        }
     }
-
-    RefPtr<nsJSIID> idObj = new nsJSIID(aInfo);
-    return idObj.forget();
+    return id;
 }
 
 
-NS_IMETHODIMP
-nsJSIID::Resolve(nsIXPConnectWrappedNative* wrapper,
-                 JSContext * cx, JSObject * objArg,
-                 jsid idArg, bool* resolvedp,
-                 bool* _retval)
+/**
+ * Public ID Object Constructor Methods
+ */
+static JSObject*
+NewIDObjectHelper(JSContext* aCx, const js::Class* aClass)
 {
-    RootedObject obj(cx, objArg);
-    RootedId id(cx, idArg);
-    XPCCallContext ccx(cx);
+    RootedObject proto(aCx, GetIDPrototype(aCx, aClass));
+    if (proto) {
+        return JS_NewObjectWithGivenProto(aCx, Jsvalify(aClass), proto);
+    }
+    return nullptr;
+}
 
-    RefPtr<XPCNativeInterface> iface =
-        XPCNativeInterface::GetNewOrUsed(mInfo);
-
-    if (!iface) {
-        return NS_OK;
+bool
+ID2JSValue(JSContext* aCx, const nsID& aId, MutableHandleValue aVal)
+{
+    RootedObject obj(aCx, NewIDObjectHelper(aCx, &sID_Class));
+    if (!obj) {
+        return false;
     }
 
-    XPCNativeMember* member = iface->FindMember(id);
-    if (member && member->IsConstant()) {
-        RootedValue val(cx);
-        if (!member->GetConstantValue(ccx, iface, val.address())) {
-            return NS_ERROR_OUT_OF_MEMORY;
+    // Get the data in nsID as 4 uint32_ts, and store them in slots.
+    uint32_t rawid[4];
+    memcpy(&rawid, &aId, sizeof(nsID));
+    static_assert(sizeof(nsID) == sizeof(rawid), "Wrong size of nsID");
+    js::SetReservedSlot(obj, kID_Slot0, PrivateUint32Value(rawid[0]));
+    js::SetReservedSlot(obj, kID_Slot1, PrivateUint32Value(rawid[1]));
+    js::SetReservedSlot(obj, kID_Slot2, PrivateUint32Value(rawid[2]));
+    js::SetReservedSlot(obj, kID_Slot3, PrivateUint32Value(rawid[3]));
+
+    aVal.setObject(*obj);
+    return true;
+}
+
+bool
+IfaceID2JSValue(JSContext* aCx, const nsXPTInterfaceInfo& aInfo,
+                MutableHandleValue aVal)
+{
+    RootedObject obj(aCx, NewIDObjectHelper(aCx, &sIID_Class));
+    if (!obj) {
+        return false;
+    }
+
+    // Define any constants defined on the interface on the ID object.
+    //
+    // NOTE: When InterfaceIDs were implemented using nsIXPCScriptable and
+    // XPConnect, this was implemented using a 'resolve' hook. It has been
+    // changed to happen at creation-time as most interfaces shouldn't have many
+    // constants, and this is likely to turn out cheaper.
+    RootedValue constant(aCx);
+    for (uint16_t i = 0; i < aInfo.ConstantCount(); ++i) {
+        constant.set(aInfo.Constant(i).JSValue());
+        if (!JS_DefineProperty(aCx, obj, aInfo.Constant(i).Name(), constant,
+                               JSPROP_READONLY | JSPROP_ENUMERATE |
+                               JSPROP_PERMANENT)) {
+            return false;
         }
-
-        *resolvedp = true;
-        *_retval = JS_DefinePropertyById(cx, obj, id, val,
-                                         JSPROP_ENUMERATE | JSPROP_READONLY |
-                                         JSPROP_PERMANENT | JSPROP_RESOLVING);
     }
 
-    return NS_OK;
+    // The InterfaceInfo is stored in a reserved slot.
+    js::SetReservedSlot(obj, kIID_InfoSlot, PrivateValue((void*)&aInfo));
+    aVal.setObject(*obj);
+    return true;
+}
+
+bool
+ContractID2JSValue(JSContext* aCx, JSString* aContract, MutableHandleValue aVal)
+{
+    RootedString jsContract(aCx, aContract);
+
+    {
+        // It is perfectly safe to have a ContractID object with an invalid
+        // ContractID, but is usually a bug.
+        nsCOMPtr<nsIComponentRegistrar> registrar;
+        NS_GetComponentRegistrar(getter_AddRefs(registrar));
+        if (!registrar) {
+            return false;
+        }
+
+        bool registered = false;
+        JS::UniqueChars contract = JS_EncodeStringToLatin1(aCx, jsContract);
+        registrar->IsContractIDRegistered(contract.get(), &registered);
+        if (!registered) {
+            return false;
+        }
+    }
+
+    RootedObject obj(aCx, NewIDObjectHelper(aCx, &sCID_Class));
+    if (!obj) {
+        return false;
+    }
+
+    // The Contract is stored in a reserved slot.
+    js::SetReservedSlot(obj, kCID_ContractSlot, StringValue(jsContract));
+    aVal.setObject(*obj);
+    return true;
 }
 
-NS_IMETHODIMP
-nsJSIID::Enumerate(nsIXPConnectWrappedNative* wrapper,
-                   JSContext * cx, JSObject * objArg, bool* _retval)
-{
-    // In this case, let's just eagerly resolve...
+
+/******************************************************************************
+ * # Method & Property Getter Implementations #
+ */
 
-    RootedObject obj(cx, objArg);
-    XPCCallContext ccx(cx);
+// NOTE: This method is used both for 'get ID.prototype.number' and
+// 'ID.prototype.toString'.
+static bool
+ID_GetNumber(JSContext* aCx, unsigned aArgc, Value* aVp)
+{
+    CallArgs args = CallArgsFromVp(aArgc, aVp);
 
-    RefPtr<XPCNativeInterface> iface =
-        XPCNativeInterface::GetNewOrUsed(mInfo);
-
-    if (!iface) {
-        return NS_OK;
+    Maybe<nsID> id = JSValue2ID(aCx, args.thisv());
+    if (!id) {
+        return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
     }
 
-    uint16_t count = iface->GetMemberCount();
-    for (uint16_t i = 0; i < count; i++) {
-        XPCNativeMember* member = iface->GetMemberAt(i);
-        if (member && member->IsConstant() &&
-            !xpc_ForcePropertyResolve(cx, obj, member->GetName())) {
-            return NS_ERROR_UNEXPECTED;
-        }
+    char buf[NSID_LENGTH];
+    id->ToProvidedString(buf);
+    JSString* jsnum = JS_NewStringCopyZ(aCx, buf);
+    if (!jsnum) {
+        return Throw(aCx, NS_ERROR_OUT_OF_MEMORY);
     }
-    return NS_OK;
+
+    args.rval().setString(jsnum);
+    return true;
 }
 
+static bool
+ID_Equals(JSContext* aCx, unsigned aArgc, Value* aVp)
+{
+    CallArgs args = CallArgsFromVp(aArgc, aVp);
+    if (!args.requireAtLeast(aCx, "nsID.equals", 1)) {
+        return false;
+    }
+
+    Maybe<nsID> id = JSValue2ID(aCx, args.thisv());
+    Maybe<nsID> id2 = JSValue2ID(aCx, args[0]);
+    if (!id || !id2) {
+        return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+    }
+
+    args.rval().setBoolean(id->Equals(*id2));
+    return true;
+}
+
+
 /*
  * HasInstance hooks need to find an appropriate reflector in order to function
  * properly. There are two complexities that we need to handle:
  *
  * 1 - Cross-compartment wrappers. Chrome uses over 100 compartments, all with
  *     system principal. The success of an instanceof check should not depend
  *     on which compartment an object comes from. At the same time, we want to
  *     make sure we don't unwrap important security wrappers.
@@ -473,17 +407,17 @@ FindObjectForHasInstance(JSContext* cx, 
             target.set(nullptr);
             return NS_OK;
         }
         obj = proto;
     }
 }
 
 nsresult
-xpc::HasInstance(JSContext* cx, HandleObject objArg, const nsID* iid, bool* bp)
+HasInstance(JSContext* cx, HandleObject objArg, const nsID* iid, bool* bp)
 {
     *bp = false;
 
     RootedObject obj(cx);
     nsresult rv = FindObjectForHasInstance(cx, objArg, &obj);
     if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
     }
@@ -512,336 +446,135 @@ xpc::HasInstance(JSContext* cx, HandleOb
     // really care if it fails.
     if (IS_WN_REFLECTOR(obj)) {
         (void) XPCWrappedNative::Get(obj)->FindTearOff(*iid);
     }
 
     return NS_OK;
 }
 
-NS_IMETHODIMP
-nsJSIID::HasInstance(nsIXPConnectWrappedNative* wrapper,
-                     JSContext* cx, JSObject * /* unused */,
-                     HandleValue val, bool* bp, bool* _retval)
+static bool
+IID_HasInstance(JSContext* aCx, unsigned aArgc, Value* aVp)
 {
-    *bp = false;
-
-    if (val.isPrimitive()) {
-        return NS_OK;
+    CallArgs args = CallArgsFromVp(aArgc, aVp);
+    if (!args.requireAtLeast(aCx, "nsIID[Symbol.hasInstance]", 1)) {
+        return false;
     }
 
-    // we have a JSObject
-    RootedObject obj(cx, &val.toObject());
+    Maybe<nsID> id = JSValue2ID(aCx, args.thisv());
+    if (!id) {
+        return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+    }
 
-    const nsIID* iid = &mInfo->IID();
-    return xpc::HasInstance(cx, obj, iid, bp);
+    bool hasInstance = false;
+    if (args[0].isObject()) {
+        RootedObject target(aCx, &args[0].toObject());
+        nsresult rv = HasInstance(aCx, target, id.ptr(), &hasInstance);
+        if (NS_FAILED(rv)) {
+            return Throw(aCx, rv);
+        }
+    }
+    args.rval().setBoolean(hasInstance);
+    return true;
 }
 
-/***************************************************************************/
-
-NS_IMPL_QUERY_INTERFACE_CI(nsJSCID,
-                           nsIJSID,
-                           nsIJSCID,
-                           nsIXPCScriptable)
-
-NS_IMPL_ADDREF(nsJSCID)
-NS_IMPL_RELEASE(nsJSCID)
-NS_IMPL_CI_INTERFACE_GETTER(nsJSCID, nsIJSID, nsIJSCID)
-
-// The nsIXPCScriptable map declaration that will generate stubs for us...
-#define XPC_MAP_CLASSNAME         nsJSCID
-#define XPC_MAP_QUOTED_CLASSNAME "nsJSCID"
-#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CONSTRUCT | \
-                       XPC_SCRIPTABLE_WANT_HASINSTANCE)
-#include "xpc_map_end.h" /* This will #undef the above */
-
-nsJSCID::nsJSCID()  { mDetails = new nsJSID(); }
-nsJSCID::~nsJSCID() {}
-
-NS_IMETHODIMP nsJSCID::GetName(char * *aName)
-    {ResolveName(); return mDetails->GetName(aName);}
-
-NS_IMETHODIMP nsJSCID::GetNumber(char * *aNumber)
-    {return mDetails->GetNumber(aNumber);}
-
-NS_IMETHODIMP_(const nsID*) nsJSCID::GetID()
-    {return &mDetails->ID();}
-
-NS_IMETHODIMP nsJSCID::GetValid(bool* aValid)
-    {return mDetails->GetValid(aValid);}
-
-NS_IMETHODIMP nsJSCID::Equals(nsIJSID* other, bool* _retval)
-    {return mDetails->Equals(other, _retval);}
-
-NS_IMETHODIMP
-nsJSCID::Initialize(const char* str)
+// NOTE: This method is used both for 'get IID.prototype.name' and
+// 'IID.prototype.toString'.
+static bool
+IID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp)
 {
-    if (str[0] == '{') {
-        NS_ENSURE_SUCCESS(mDetails->Initialize(str), NS_ERROR_FAILURE);
-    } else {
-        nsCOMPtr<nsIComponentRegistrar> registrar;
-        NS_GetComponentRegistrar(getter_AddRefs(registrar));
-        NS_ENSURE_TRUE(registrar, NS_ERROR_FAILURE);
-
-        nsCID* cid;
-        if (NS_FAILED(registrar->ContractIDToCID(str, &cid))) {
-            return NS_ERROR_FAILURE;
-        }
-        mDetails->InitWithName(*cid, str);
-        free(cid);
-    }
-    return NS_OK;
-}
+    CallArgs args = CallArgsFromVp(aArgc, aVp);
 
-NS_IMETHODIMP nsJSCID::ToString(char** _retval)
-    {ResolveName(); return mDetails->ToString(_retval);}
-
-void
-nsJSCID::ResolveName()
-{
-    if (!mDetails->NameIsSet()) {
-        mDetails->SetNameToNoString();
-    }
-}
-
-//static
-already_AddRefed<nsJSCID>
-nsJSCID::NewID(const char* str)
-{
-    if (!str) {
-        NS_ERROR("no string");
-        return nullptr;
-    }
-
-    RefPtr<nsJSCID> idObj = new nsJSCID();
-    if (NS_FAILED(idObj->Initialize(str))) {
-        return nullptr;
-    }
-    return idObj.forget();
-}
-
-NS_IMETHODIMP
-nsJSCID::RefreshCID()
-{
-    char* name = nullptr;
-    nsresult rv = mDetails->GetName(&name);
-    if (NS_SUCCEEDED(rv)) {
-        rv = *name ? Initialize(name) : NS_ERROR_UNEXPECTED;
-    }
-    free(name);
-    return rv;
-}
-
-static Maybe<nsID>
-GetIIDArg(uint32_t argc, JS::HandleValue val, JSContext* cx)
-{
-    // If an IID was passed in then use it
-    if (argc) {
-        return xpc::JSValue2ID(cx, val);
-    }
-    return Some(NS_GET_IID(nsISupports));
-}
-
-NS_IMETHODIMP
-nsJSCID::CreateInstance(HandleValue iidval, JSContext* cx,
-                        uint8_t optionalArgc, MutableHandleValue retval)
-{
-    if (!mDetails->IsValid()) {
-        return NS_ERROR_XPC_BAD_CID;
+    RootedObject obj(aCx, GetIDObject(args.thisv(), &sIID_Class));
+    if (!obj) {
+        return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
     }
 
-    if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, mDetails->ID()))) {
-        NS_ERROR("how are we not being called from chrome here?");
-        return NS_OK;
-    }
-
-    // If an IID was passed in then use it
-    Maybe<nsID> iid = GetIIDArg(optionalArgc, iidval, cx);
-    if (!iid) {
-        return NS_ERROR_XPC_BAD_IID;
-    }
-
-    nsCOMPtr<nsIComponentManager> compMgr;
-    nsresult rv = NS_GetComponentManager(getter_AddRefs(compMgr));
-    if (NS_FAILED(rv)) {
-        return NS_ERROR_UNEXPECTED;
-    }
-
-    nsCOMPtr<nsISupports> inst;
-    rv = compMgr->CreateInstance(mDetails->ID(), nullptr, iid.ref(), getter_AddRefs(inst));
-    MOZ_ASSERT(NS_FAILED(rv) || inst, "component manager returned success, but instance is null!");
-
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_XPC_CI_RETURNED_FAILURE);
-    if (!inst) {
-      return NS_ERROR_XPC_CI_RETURNED_FAILURE;
-    }
+    auto* info = (const nsXPTInterfaceInfo*)
+        js::GetReservedSlot(obj, kIID_InfoSlot).toPrivate();
 
-    rv = nsContentUtils::WrapNative(cx, inst, iid.ptr(), retval);
-    if (NS_FAILED(rv) || retval.isPrimitive()) {
-        return NS_ERROR_XPC_CANT_CREATE_WN;
-    }
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsJSCID::GetService(HandleValue iidval, JSContext* cx, uint8_t optionalArgc,
-                    MutableHandleValue retval)
-{
-    if (!mDetails->IsValid()) {
-        return NS_ERROR_XPC_BAD_CID;
-    }
-
-    if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, mDetails->ID()))) {
-        MOZ_ASSERT(JS_IsExceptionPending(cx),
-                   "security manager vetoed GetService without setting exception");
-        return NS_OK;
-    }
-
-    // If an IID was passed in then use it
-    Maybe<nsID> iid = GetIIDArg(optionalArgc, iidval, cx);
-    if (!iid) {
-        return NS_ERROR_XPC_BAD_IID;
-    }
-
-    nsCOMPtr<nsIServiceManager> svcMgr;
-    nsresult rv = NS_GetServiceManager(getter_AddRefs(svcMgr));
-    if (NS_FAILED(rv)) {
-        return rv;
+    // Name property is the name of the interface this nsIID was created from.
+    JSString* name = JS_NewStringCopyZ(aCx, info->Name());
+    if (!name) {
+        return Throw(aCx, NS_ERROR_OUT_OF_MEMORY);
     }
 
-    nsCOMPtr<nsISupports> srvc;
-    rv = svcMgr->GetService(mDetails->ID(), iid.ref(), getter_AddRefs(srvc));
-    MOZ_ASSERT(NS_FAILED(rv) || srvc, "service manager returned success, but service is null!");
-
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_XPC_GS_RETURNED_FAILURE);
-    if (!srvc) {
-        return NS_ERROR_XPC_GS_RETURNED_FAILURE;
-    }
-
-    RootedValue v(cx);
-    rv = nsContentUtils::WrapNative(cx, srvc, iid.ptr(), &v);
-    if (NS_FAILED(rv) || !v.isObject()) {
-        return NS_ERROR_XPC_CANT_CREATE_WN;
-    }
-
-    retval.set(v);
-    return NS_OK;
+    args.rval().setString(name);
+    return true;
 }
 
-NS_IMETHODIMP
-nsJSCID::Construct(nsIXPConnectWrappedNative* wrapper,
-                   JSContext* cx, JSObject* objArg,
-                   const CallArgs& args, bool* _retval)
+
+// Common code for CID_CreateInstance and CID_GetService
+static bool
+CIGSHelper(JSContext* aCx, unsigned aArgc, Value* aVp, bool aGetService)
 {
-    RootedObject obj(cx, objArg);
-    XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
-    if (!xpcrt) {
-        return NS_ERROR_FAILURE;
+    CallArgs args = CallArgsFromVp(aArgc, aVp);
+
+    // Extract the ContractID string from our reserved slot. Don't use
+    // JSValue2ID as this method should only be defined on Contract ID objects,
+    // and it allows us to avoid a duplicate hashtable lookup.
+    RootedObject obj(aCx, GetIDObject(args.thisv(), &sCID_Class));
+    if (!obj) {
+        return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
+    }
+    JS::UniqueChars contractID = JS_EncodeStringToLatin1(aCx,
+        js::GetReservedSlot(obj, kCID_ContractSlot).toString());
+
+    // Extract the IID from the first argument, if passed. Default: nsISupports.
+    Maybe<nsIID> iid = args.length() >= 1 ? JSValue2ID(aCx, args[0])
+                                          : Some(NS_GET_IID(nsISupports));
+    if (!iid) {
+        return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
     }
 
-    // 'push' a call context and call on it
-    RootedId name(cx, xpcrt->GetStringID(XPCJSContext::IDX_CREATE_INSTANCE));
-    XPCCallContext ccx(cx, obj, nullptr, name, args.length(), args.array(),
-                       args.rval().address());
-
-    *_retval = XPCWrappedNative::CallMethod(ccx);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsJSCID::HasInstance(nsIXPConnectWrappedNative* wrapper,
-                     JSContext* cx, JSObject * /* unused */,
-                     HandleValue val, bool* bp, bool* _retval)
-{
-    *bp = false;
-
-    if (!val.isObject()) {
-        return NS_OK;
-    }
-
-    RootedObject obj(cx, &val.toObject());
-
-    // is this really a native xpcom object with a wrapper?
-    RootedObject target(cx);
-    nsresult rv = FindObjectForHasInstance(cx, obj, &target);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-    }
-
-    if (!target || !IS_WN_REFLECTOR(target)) {
-        return NS_OK;
-    }
-
-    if (XPCWrappedNative* other_wrapper = XPCWrappedNative::Get(target)) {
-        if (nsIClassInfo* ci = other_wrapper->GetClassInfo()) {
-            // We consider CID equality to be the thing that matters here.
-            // This is perhaps debatable.
-            nsID cid;
-            if (NS_SUCCEEDED(ci->GetClassIDNoAlloc(&cid))) {
-                *bp = cid.Equals(mDetails->ID());
-            }
+    // Invoke CreateInstance or GetService with our ContractID.
+    nsresult rv;
+    nsCOMPtr<nsISupports> result;
+    if (aGetService) {
+        rv = CallGetService(contractID.get(), *iid, getter_AddRefs(result));
+        if (NS_FAILED(rv) || !result) {
+            return Throw(aCx, NS_ERROR_XPC_GS_RETURNED_FAILURE);
+        }
+    } else {
+        rv = CallCreateInstance(contractID.get(), nullptr, *iid,
+                                getter_AddRefs(result));
+        if (NS_FAILED(rv) || !result) {
+            return Throw(aCx, NS_ERROR_XPC_CI_RETURNED_FAILURE);
         }
     }
 
-    return NS_OK;
+    // Wrap the created object and return it.
+    rv = nsContentUtils::WrapNative(aCx, result, iid.ptr(), args.rval());
+    if (NS_FAILED(rv) || args.rval().isPrimitive()) {
+        return Throw(aCx, NS_ERROR_XPC_CANT_CREATE_WN);
+    }
+    return true;
+}
+
+static bool
+CID_CreateInstance(JSContext* aCx, unsigned aArgc, Value* aVp)
+{
+    return CIGSHelper(aCx, aArgc, aVp, /* aGetService = */ false);
 }
 
-/***************************************************************************/
-// Public Methods
-
-namespace xpc {
-
-Maybe<nsID>
-JSValue2ID(JSContext* aCx, JS::HandleValue aVal)
+static bool
+CID_GetService(JSContext* aCx, unsigned aArgc, Value* aVp)
 {
-    if (!aVal.isObject()) {
-        return Nothing();
-    }
+    return CIGSHelper(aCx, aArgc, aVp, /* aGetService = */ true);
+}
 
-    JS::RootedObject obj(aCx, js::CheckedUnwrap(&aVal.toObject()));
-    if (!obj || !IS_WN_REFLECTOR(obj)) {
-        return Nothing();
-    }
-
-    XPCWrappedNative* wrapper = XPCWrappedNative::Get(obj);
-    if (!wrapper || !(wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID)) ||
-                      wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) ||
-                      wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID)))) {
-        return Nothing();
+// NOTE: This method is used both for 'get CID.prototype.name' and
+// 'CID.prototype.toString'.
+static bool
+CID_GetName(JSContext* aCx, unsigned aArgc, Value* aVp)
+{
+    CallArgs args = CallArgsFromVp(aArgc, aVp);
+    RootedObject obj(aCx, GetIDObject(args.thisv(), &sCID_Class));
+    if (!obj) {
+        return Throw(aCx, NS_ERROR_XPC_BAD_CONVERT_JS);
     }
 
-    nsIJSID* jsid = static_cast<nsIJSID*>(wrapper->GetIdentityObject());
-    return Some(*jsid->GetID());
-}
-
-bool
-ID2JSValue(JSContext* aCx, const nsID& aId, JS::MutableHandleValue aVal)
-{
-    nsCOMPtr<nsIJSID> jsid = nsJSID::NewID(aId);
-    return jsid && NS_SUCCEEDED(
-        nsContentUtils::WrapNative(aCx, jsid, &NS_GET_IID(nsIJSID), aVal));
-}
-
-bool
-IfaceID2JSValue(JSContext* aCx, const nsXPTInterfaceInfo& aInfo,
-                JS::MutableHandleValue aVal)
-{
-    nsCOMPtr<nsIJSIID> jsid = nsJSIID::NewID(&aInfo);
-    return jsid && NS_SUCCEEDED(
-        nsContentUtils::WrapNative(aCx, jsid, &NS_GET_IID(nsIJSIID), aVal));
-}
-
-bool
-ContractID2JSValue(JSContext* aCx, const nsACString& aContract,
-                   JS::MutableHandleValue aVal)
-{
-    if (aContract.IsEmpty() || aContract.First() == '{') {
-        return false;
-    }
-
-    nsCOMPtr<nsIJSCID> jsid =
-        nsJSCID::NewID(PromiseFlatCString(aContract).get());
-    return jsid && NS_SUCCEEDED(
-        nsContentUtils::WrapNative(aCx, jsid, &NS_GET_IID(nsIJSCID), aVal));
+    // Return the string stored in our reserved ContractID slot.
+    args.rval().set(js::GetReservedSlot(obj, kCID_ContractSlot));
+    return true;
 }
 
 } // namespace xpc
-
--- a/js/xpconnect/src/XPCModule.cpp
+++ b/js/xpconnect/src/XPCModule.cpp
@@ -15,10 +15,9 @@ xpcModuleCtor()
     return NS_OK;
 }
 
 void
 xpcModuleDtor()
 {
     // Release our singletons
     nsXPConnect::ReleaseXPConnectSingleton();
-    xpc_DestroyJSxIDClassObjects();
 }
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -390,16 +390,19 @@ XPCWrappedNativeScope::~XPCWrappedNative
     // XXX might not want to do this at xpconnect shutdown time???
     mComponents = nullptr;
 
     if (mXrayExpandos.initialized()) {
         mXrayExpandos.destroy();
     }
 
     JSContext* cx = dom::danger::GetJSContext();
+    mIDProto.finalize(cx);
+    mIIDProto.finalize(cx);
+    mCIDProto.finalize(cx);
     mGlobalJSObject.finalize(cx);
 }
 
 // static
 void
 XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(JSTracer* trc)
 {
     // Do JS::TraceEdge for all wrapped natives with external references, as
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -115,17 +115,16 @@
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsXPTCUtils.h"
 #include "xptinfo.h"
 #include "XPCForwards.h"
 #include "XPCLog.h"
 #include "xpccomponents.h"
-#include "xpcjsid.h"
 #include "prenv.h"
 #include "prcvar.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 
 #include "MainThreadUtils.h"
 
 #include "nsIConsoleService.h"
@@ -886,16 +885,25 @@ public:
 
     void TraceInside(JSTracer* trc) {
         if (mContentXBLScope) {
             mContentXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
         }
         if (mXrayExpandos.initialized()) {
             mXrayExpandos.trace(trc);
         }
+        if (mIDProto) {
+            mIDProto.trace(trc, "XPCWrappedNativeScope::mIDProto");
+        }
+        if (mIIDProto) {
+            mIIDProto.trace(trc, "XPCWrappedNativeScope::mIIDProto");
+        }
+        if (mCIDProto) {
+            mCIDProto.trace(trc, "XPCWrappedNativeScope::mCIDProto");
+        }
     }
 
     static void
     SuspectAllWrappers(nsCycleCollectionNoteRootCallback& cb);
 
     static void
     SweepAllWrappedNativeTearOffs();
 
@@ -947,16 +955,21 @@ public:
 
     JS::Compartment* Compartment() const { return js::GetObjectCompartment(mGlobalJSObject); }
 
     bool IsContentXBLScope() { return xpc::IsContentXBLCompartment(Compartment()); }
     bool AllowContentXBLScope();
     bool UseContentXBLScope() { return mUseContentXBLScope; }
     void ClearContentXBLScope() { mContentXBLScope = nullptr; }
 
+    // ID Object prototype caches.
+    JS::ObjectPtr mIDProto;
+    JS::ObjectPtr mIIDProto;
+    JS::ObjectPtr mCIDProto;
+
 protected:
     virtual ~XPCWrappedNativeScope();
 
     XPCWrappedNativeScope() = delete;
 
 private:
     static XPCWrappedNativeScope* gScopes;
     static XPCWrappedNativeScope* gDyingScopes;
@@ -2132,110 +2145,16 @@ public:
     static const void* IterateNSResults(nsresult* rv,
                                         const char** name,
                                         const char** format,
                                         const void** iterp);
 
     static uint32_t GetNSResultCount();
 };
 
-/***************************************************************************/
-/*
-* nsJSID implements nsIJSID. It is also used by nsJSIID and nsJSCID as a
-* member (as a hidden implementaion detail) to which they delegate many calls.
-*/
-
-// Initialization is done on demand, and calling the destructor below is always
-// safe.
-extern void xpc_DestroyJSxIDClassObjects();
-
-class nsJSID final : public nsIJSID
-{
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIJSID
-
-    void InitWithName(const nsID& id, const char* nameString);
-    void SetName(const char* name);
-    void   SetNameToNoString()
-        {MOZ_ASSERT(!mName, "name already set"); mName = const_cast<char*>(gNoString);}
-    bool NameIsSet() const {return nullptr != mName;}
-    const nsID& ID() const {return mID;}
-    bool IsValid() const {return !mID.Equals(GetInvalidIID());}
-
-    static already_AddRefed<nsJSID> NewID(const char* str);
-    static already_AddRefed<nsJSID> NewID(const nsID& id);
-
-    nsJSID();
-
-    void Reset();
-    const nsID& GetInvalidIID() const;
-
-protected:
-    virtual ~nsJSID();
-    static const char gNoString[];
-    nsID    mID;
-    char*   mNumber;
-    char*   mName;
-};
-
-
-// nsJSIID
-
-class nsJSIID : public nsIJSIID,
-                public nsIXPCScriptable
-{
-public:
-    NS_DECL_ISUPPORTS
-
-    // we manually delegate these to nsJSID
-    NS_DECL_NSIJSID
-
-    // we implement the rest...
-    NS_DECL_NSIJSIID
-    NS_DECL_NSIXPCSCRIPTABLE
-
-    static already_AddRefed<nsJSIID> NewID(const nsXPTInterfaceInfo* aInfo);
-
-    explicit nsJSIID(const nsXPTInterfaceInfo* aInfo);
-    nsJSIID() = delete;
-
-private:
-    virtual ~nsJSIID();
-
-    const nsXPTInterfaceInfo* mInfo;
-};
-
-// nsJSCID
-
-class nsJSCID : public nsIJSCID, public nsIXPCScriptable
-{
-public:
-    NS_DECL_ISUPPORTS
-
-    // we manually delegate these to nsJSID
-    NS_DECL_NSIJSID
-
-    // we implement the rest...
-    NS_DECL_NSIJSCID
-    NS_DECL_NSIXPCSCRIPTABLE
-
-    static already_AddRefed<nsJSCID> NewID(const char* str);
-
-    nsJSCID();
-
-private:
-    virtual ~nsJSCID();
-
-    void ResolveName();
-
-private:
-    RefPtr<nsJSID> mDetails;
-};
-
 
 /***************************************************************************/
 // 'Components' object implementations. nsXPCComponentsBase has the
 // less-privileged stuff that we're willing to expose to XBL.
 
 class nsXPCComponentsBase : public nsIXPCComponentsBase
 {
 public:
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -761,17 +761,17 @@ bool IfaceID2JSValue(JSContext* aCx, con
 /**
  * Reflect a ContractID into JS
  *
  * This object will expose 'getService' and 'createInstance' methods in addition
  * to the other methods available on nsID objects.
  *
  * Use 'xpc::JSValue2ID' to unwrap JS::Values created with this function.
  */
-bool ContractID2JSValue(JSContext* aCx, const nsACString& aContract,
+bool ContractID2JSValue(JSContext* aCx, JSString* aContract,
                         JS::MutableHandleValue aVal);
 
 } // namespace xpc
 
 namespace mozilla {
 namespace dom {
 
 /**
--- a/testing/modules/AppInfo.jsm
+++ b/testing/modules/AppInfo.jsm
@@ -127,14 +127,9 @@ var updateAppInfo = function(options) {
         throw Cr.NS_ERROR_NO_AGGREGATION;
       }
 
       return currentAppInfo.QueryInterface(iid);
     },
   };
 
   registrar.registerFactory(id, "XULAppInfo", contractid, factory);
-
-  // Ensure that Cc actually maps contractid to the new shim AppInfo. This is
-  // needed when JSM global sharing is enabled, because some prior code may
-  // already have looked up |Cc[contractid]|.
-  Cc[contractid].refreshCID();
 };