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;