diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp
index 3b31ae94a..027869e6b 100644
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -162,9 +162,12 @@ XPCWrappedNativeXrayTraits::getWN(JSObject* wrapper)
}
const JSClass XPCWrappedNativeXrayTraits::HolderClass = {
- "NativePropertyHolder", JSCLASS_HAS_RESERVED_SLOTS(2)
+ "NativePropertyHolder", JSCLASS_HAS_RESERVED_SLOTS(HOLDER_SHARED_SLOT_COUNT)
};
+const JSClass XrayTraits::HolderClass = {
+ "XrayHolder", JSCLASS_HAS_RESERVED_SLOTS(HOLDER_SHARED_SLOT_COUNT)
+};
const JSClass JSXrayTraits::HolderClass = {
"JSXrayHolder", JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT)
@@ -1746,7 +1749,7 @@ DOMXrayTraits::preserveWrapper(JSObject* target)
JSObject*
DOMXrayTraits::createHolder(JSContext* cx, JSObject* wrapper)
{
- return JS_NewObjectWithGivenProto(cx, nullptr, nullptr);
+ return JS_NewObjectWithGivenProto(cx, &HolderClass, nullptr);
}
namespace XrayUtils {
@@ -2245,16 +2248,35 @@ XrayWrapper::getPrototype(JSContext* cx, JS::HandleObject wrapper,
// only if there's been a set. If there's not an expando, or the expando
// slot is |undefined|, hand back the default proto, appropriately wrapped.
- RootedValue v(cx);
if (expando) {
- JSAutoCompartment ac(cx, expando);
- v = JS_GetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE);
+ RootedValue v(cx);
+ { // Scope for JSAutoCompartment
+ JSAutoCompartment ac(cx, expando);
+ v = JS_GetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE);
+ }
+ if (!v.isUndefined()) {
+ protop.set(v.toObjectOrNull());
+ return JS_WrapObject(cx, protop);
+ }
}
- if (v.isUndefined())
- return getPrototypeHelper(cx, wrapper, target, protop);
- protop.set(v.toObjectOrNull());
- return JS_WrapObject(cx, protop);
+ // Check our holder, and cache there if we don't have it cached already.
+ RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper));
+ if (!holder)
+ return false;
+
+ Value cached = js::GetReservedSlot(holder,
+ Traits::HOLDER_SLOT_CACHED_PROTO);
+ if (cached.isUndefined()) {
+ if (!getPrototypeHelper(cx, wrapper, target, protop))
+ return false;
+
+ js::SetReservedSlot(holder, Traits::HOLDER_SLOT_CACHED_PROTO,
+ ObjectOrNullValue(protop));
+ } else {
+ protop.set(cached.toObjectOrNull());
+ }
+ return true;
}
template
diff --git a/js/xpconnect/wrappers/XrayWrapper.h b/js/xpconnect/wrappers/XrayWrapper.h
index a35f08315..53d6eb13e 100644
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -94,6 +94,12 @@ public:
JSObject* ensureExpandoObject(JSContext* cx, JS::HandleObject wrapper,
JS::HandleObject target);
+ // Slots for holder objects.
+ enum {
+ HOLDER_SLOT_CACHED_PROTO = 0,
+ HOLDER_SHARED_SLOT_COUNT
+ };
+
JSObject* getHolder(JSObject* wrapper);
JSObject* ensureHolder(JSContext* cx, JS::HandleObject wrapper);
virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) = 0;
@@ -102,6 +108,9 @@ public:
bool setExpandoChain(JSContext* cx, JS::HandleObject obj, JS::HandleObject chain);
bool cloneExpandoChain(JSContext* cx, JS::HandleObject dst, JS::HandleObject src);
+protected:
+ static const JSClass HolderClass;
+
private:
bool expandoObjectMatchesConsumer(JSContext* cx, JS::HandleObject expandoObject,
nsIPrincipal* consumerOrigin,
@@ -292,7 +301,7 @@ public:
}
enum {
- SLOT_PROTOKEY = 0,
+ SLOT_PROTOKEY = HOLDER_SHARED_SLOT_COUNT,
SLOT_ISPROTOTYPE,
SLOT_CONSTRUCTOR_FOR,
SLOT_COUNT
@@ -406,7 +415,7 @@ public:
virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override
{
- return JS_NewObjectWithGivenProto(cx, nullptr, nullptr);
+ return JS_NewObjectWithGivenProto(cx, &HolderClass, nullptr);
}
static OpaqueXrayTraits singleton;