1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 #include "mozilla/Maybe.h"
12 #include "WrapperFactory.h"
15 #include "jsfriendapi.h"
16 #include "js/friend/XrayJitInfo.h" // JS::XrayJitInfo
17 #include "js/Object.h" // JS::GetReservedSlot
19 #include "js/Wrapper.h"
20 #include "mozilla/dom/ScriptSettings.h"
22 // Slot where Xray functions for Web IDL methods store a pointer to
23 // the Xray wrapper they're associated with.
24 #define XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT 0
25 // Slot where in debug builds Xray functions for Web IDL methods store
26 // a pointer to their themselves, just so we can assert that they're the
27 // sort of functions we expect.
28 #define XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF 1
30 // Xray wrappers re-resolve the original native properties on the native
31 // object and always directly access to those properties.
32 // Because they work so differently from the rest of the wrapper hierarchy,
33 // we pull them out of the Wrapper inheritance hierarchy and create a
34 // little world around them.
49 constexpr XrayTraits() = default;
51 static JSObject
* getTargetObject(JSObject
* wrapper
) {
53 js::UncheckedUnwrap(wrapper
, /* stopAtWindowProxy = */ false);
55 JS::ExposeObjectToActiveJS(target
);
60 // NB: resolveOwnProperty may decide whether or not to cache what it finds
61 // on the holder. If the result is not cached, the lookup will happen afresh
62 // for each access, which is the right thing for things like dynamic NodeList
64 virtual bool resolveOwnProperty(
65 JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleObject target
,
66 JS::HandleObject holder
, JS::HandleId id
,
67 JS::MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
);
69 bool delete_(JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleId id
,
70 JS::ObjectOpResult
& result
) {
71 return result
.succeed();
74 static bool getBuiltinClass(JSContext
* cx
, JS::HandleObject wrapper
,
75 const js::Wrapper
& baseInstance
,
77 return baseInstance
.getBuiltinClass(cx
, wrapper
, cls
);
80 static const char* className(JSContext
* cx
, JS::HandleObject wrapper
,
81 const js::Wrapper
& baseInstance
) {
82 return baseInstance
.className(cx
, wrapper
);
85 virtual void preserveWrapper(JSObject
* target
) = 0;
87 bool getExpandoObject(JSContext
* cx
, JS::HandleObject target
,
88 JS::HandleObject consumer
,
89 JS::MutableHandleObject expandObject
);
90 JSObject
* ensureExpandoObject(JSContext
* cx
, JS::HandleObject wrapper
,
91 JS::HandleObject target
);
93 // Slots for holder objects.
95 HOLDER_SLOT_CACHED_PROTO
= 0,
96 HOLDER_SLOT_EXPANDO
= 1,
97 HOLDER_SHARED_SLOT_COUNT
100 static JSObject
* getHolder(JSObject
* wrapper
);
101 JSObject
* ensureHolder(JSContext
* cx
, JS::HandleObject wrapper
);
102 virtual JSObject
* createHolder(JSContext
* cx
, JSObject
* wrapper
) = 0;
104 JSObject
* getExpandoChain(JS::HandleObject obj
);
105 JSObject
* detachExpandoChain(JS::HandleObject obj
);
106 bool setExpandoChain(JSContext
* cx
, JS::HandleObject obj
,
107 JS::HandleObject chain
);
108 bool cloneExpandoChain(JSContext
* cx
, JS::HandleObject dst
,
109 JS::HandleObject srcChain
);
112 static const JSClass HolderClass
;
114 // Get the JSClass we should use for our expando object.
115 virtual const JSClass
* getExpandoClass(JSContext
* cx
,
116 JS::HandleObject target
) const;
119 bool expandoObjectMatchesConsumer(JSContext
* cx
,
120 JS::HandleObject expandoObject
,
121 nsIPrincipal
* consumerOrigin
);
123 // |expandoChain| is the expando chain in the wrapped object's compartment.
124 // |exclusiveWrapper| is any xray that has exclusive use of the expando.
125 // |cx| may be in any compartment.
126 bool getExpandoObjectInternal(JSContext
* cx
, JSObject
* expandoChain
,
127 JS::HandleObject exclusiveWrapper
,
128 nsIPrincipal
* origin
,
129 JS::MutableHandleObject expandoObject
);
131 // |cx| is in the target's compartment, and |exclusiveWrapper| is any xray
132 // that has exclusive use of the expando. |exclusiveWrapperGlobal| is the
133 // caller's global and must be same-compartment with |exclusiveWrapper|.
134 JSObject
* attachExpandoObject(JSContext
* cx
, JS::HandleObject target
,
135 JS::HandleObject exclusiveWrapper
,
136 JS::HandleObject exclusiveWrapperGlobal
,
137 nsIPrincipal
* origin
);
139 XrayTraits(XrayTraits
&) = delete;
140 const XrayTraits
& operator=(XrayTraits
&) = delete;
143 void ExpandoObjectFinalize(JS::GCContext
* gcx
, JSObject
* obj
);
145 class DOMXrayTraits
: public XrayTraits
{
147 constexpr DOMXrayTraits() = default;
149 static const XrayType Type
= XrayForDOMObject
;
151 virtual bool resolveOwnProperty(
152 JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleObject target
,
153 JS::HandleObject holder
, JS::HandleId id
,
154 JS::MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
) override
;
156 bool delete_(JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleId id
,
157 JS::ObjectOpResult
& result
);
160 JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleId id
,
161 JS::Handle
<JS::PropertyDescriptor
> desc
,
162 JS::Handle
<mozilla::Maybe
<JS::PropertyDescriptor
>> existingDesc
,
163 JS::Handle
<JSObject
*> existingHolder
, JS::ObjectOpResult
& result
,
165 virtual bool enumerateNames(JSContext
* cx
, JS::HandleObject wrapper
,
166 unsigned flags
, JS::MutableHandleIdVector props
);
167 static bool call(JSContext
* cx
, JS::HandleObject wrapper
,
168 const JS::CallArgs
& args
, const js::Wrapper
& baseInstance
);
169 static bool construct(JSContext
* cx
, JS::HandleObject wrapper
,
170 const JS::CallArgs
& args
,
171 const js::Wrapper
& baseInstance
);
173 static bool getPrototype(JSContext
* cx
, JS::HandleObject wrapper
,
174 JS::HandleObject target
,
175 JS::MutableHandleObject protop
);
177 virtual void preserveWrapper(JSObject
* target
) override
;
179 virtual JSObject
* createHolder(JSContext
* cx
, JSObject
* wrapper
) override
;
181 static DOMXrayTraits singleton
;
184 virtual const JSClass
* getExpandoClass(
185 JSContext
* cx
, JS::HandleObject target
) const override
;
188 class JSXrayTraits
: public XrayTraits
{
190 static const XrayType Type
= XrayForJSObject
;
192 virtual bool resolveOwnProperty(
193 JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleObject target
,
194 JS::HandleObject holder
, JS::HandleId id
,
195 JS::MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
) override
;
197 bool delete_(JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleId id
,
198 JS::ObjectOpResult
& result
);
201 JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleId id
,
202 JS::Handle
<JS::PropertyDescriptor
> desc
,
203 JS::Handle
<mozilla::Maybe
<JS::PropertyDescriptor
>> existingDesc
,
204 JS::Handle
<JSObject
*> existingHolder
, JS::ObjectOpResult
& result
,
207 virtual bool enumerateNames(JSContext
* cx
, JS::HandleObject wrapper
,
208 unsigned flags
, JS::MutableHandleIdVector props
);
210 static bool call(JSContext
* cx
, JS::HandleObject wrapper
,
211 const JS::CallArgs
& args
, const js::Wrapper
& baseInstance
) {
212 JSXrayTraits
& self
= JSXrayTraits::singleton
;
213 JS::RootedObject
holder(cx
, self
.ensureHolder(cx
, wrapper
));
217 JSProtoKey key
= xpc::JSXrayTraits::getProtoKey(holder
);
218 if (key
== JSProto_Function
|| key
== JSProto_BoundFunction
) {
219 return baseInstance
.call(cx
, wrapper
, args
);
222 JS::RootedValue
v(cx
, JS::ObjectValue(*wrapper
));
223 js::ReportIsNotFunction(cx
, v
);
227 static bool construct(JSContext
* cx
, JS::HandleObject wrapper
,
228 const JS::CallArgs
& args
,
229 const js::Wrapper
& baseInstance
);
231 bool getPrototype(JSContext
* cx
, JS::HandleObject wrapper
,
232 JS::HandleObject target
, JS::MutableHandleObject protop
) {
233 JS::RootedObject
holder(cx
, ensureHolder(cx
, wrapper
));
237 JSProtoKey key
= getProtoKey(holder
);
238 if (isPrototype(holder
)) {
239 JSProtoKey protoKey
= js::InheritanceProtoKeyForStandardClass(key
);
240 if (protoKey
== JSProto_Null
) {
248 JSAutoRealm
ar(cx
, target
);
249 if (!JS_GetClassPrototype(cx
, key
, protop
)) {
253 return JS_WrapObject(cx
, protop
);
256 virtual void preserveWrapper(JSObject
* target
) override
{
257 // In the case of pure JS objects, there is no underlying object, and
258 // the target is the canonical representation of state. If it gets
259 // collected, then expandos and such should be collected too. So there's
260 // nothing to do here.
264 SLOT_PROTOKEY
= HOLDER_SHARED_SLOT_COUNT
,
266 SLOT_CONSTRUCTOR_FOR
,
269 virtual JSObject
* createHolder(JSContext
* cx
, JSObject
* wrapper
) override
;
271 static JSProtoKey
getProtoKey(JSObject
* holder
) {
272 int32_t key
= JS::GetReservedSlot(holder
, SLOT_PROTOKEY
).toInt32();
273 return static_cast<JSProtoKey
>(key
);
276 static bool isPrototype(JSObject
* holder
) {
277 return JS::GetReservedSlot(holder
, SLOT_ISPROTOTYPE
).toBoolean();
280 static JSProtoKey
constructorFor(JSObject
* holder
) {
281 int32_t key
= JS::GetReservedSlot(holder
, SLOT_CONSTRUCTOR_FOR
).toInt32();
282 return static_cast<JSProtoKey
>(key
);
285 // Operates in the wrapper compartment.
286 static bool getOwnPropertyFromWrapperIfSafe(
287 JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleId id
,
288 JS::MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
);
290 // Like the above, but operates in the target compartment. wrapperGlobal is
291 // the caller's global (must be in the wrapper compartment).
292 static bool getOwnPropertyFromTargetIfSafe(
293 JSContext
* cx
, JS::HandleObject target
, JS::HandleObject wrapper
,
294 JS::HandleObject wrapperGlobal
, JS::HandleId id
,
295 JS::MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
);
297 static const JSClass HolderClass
;
298 static JSXrayTraits singleton
;
301 // These traits are used when the target is not Xrayable and we therefore want
302 // to make it opaque modulo the usual Xray machinery (like expandos and
303 // .wrappedJSObject).
304 class OpaqueXrayTraits
: public XrayTraits
{
306 static const XrayType Type
= XrayForOpaqueObject
;
308 virtual bool resolveOwnProperty(
309 JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleObject target
,
310 JS::HandleObject holder
, JS::HandleId id
,
311 JS::MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
) override
;
314 JSContext
* cx
, JS::HandleObject wrapper
, JS::HandleId id
,
315 JS::Handle
<JS::PropertyDescriptor
> desc
,
316 JS::Handle
<mozilla::Maybe
<JS::PropertyDescriptor
>> existingDesc
,
317 JS::Handle
<JSObject
*> existingHolder
, JS::ObjectOpResult
& result
,
323 virtual bool enumerateNames(JSContext
* cx
, JS::HandleObject wrapper
,
324 unsigned flags
, JS::MutableHandleIdVector props
) {
328 static bool call(JSContext
* cx
, JS::HandleObject wrapper
,
329 const JS::CallArgs
& args
, const js::Wrapper
& baseInstance
) {
330 JS::RootedValue
v(cx
, JS::ObjectValue(*wrapper
));
331 js::ReportIsNotFunction(cx
, v
);
335 static bool construct(JSContext
* cx
, JS::HandleObject wrapper
,
336 const JS::CallArgs
& args
,
337 const js::Wrapper
& baseInstance
) {
338 JS::RootedValue
v(cx
, JS::ObjectValue(*wrapper
));
339 js::ReportIsNotFunction(cx
, v
);
343 bool getPrototype(JSContext
* cx
, JS::HandleObject wrapper
,
344 JS::HandleObject target
, JS::MutableHandleObject protop
) {
345 // Opaque wrappers just get targetGlobal.Object.prototype as their
346 // prototype. This is preferable to using a null prototype because it
347 // lets things like |toString| and |__proto__| work.
349 JSAutoRealm
ar(cx
, target
);
350 if (!JS_GetClassPrototype(cx
, JSProto_Object
, protop
)) {
354 return JS_WrapObject(cx
, protop
);
357 static bool getBuiltinClass(JSContext
* cx
, JS::HandleObject wrapper
,
358 const js::Wrapper
& baseInstance
,
360 *cls
= js::ESClass::Other
;
364 static const char* className(JSContext
* cx
, JS::HandleObject wrapper
,
365 const js::Wrapper
& baseInstance
) {
369 virtual void preserveWrapper(JSObject
* target
) override
{}
371 virtual JSObject
* createHolder(JSContext
* cx
, JSObject
* wrapper
) override
{
372 return JS_NewObjectWithGivenProto(cx
, &HolderClass
, nullptr);
375 static OpaqueXrayTraits singleton
;
378 XrayType
GetXrayType(JSObject
* obj
);
379 XrayTraits
* GetXrayTraits(JSObject
* obj
);
381 template <typename Base
, typename Traits
>
382 class XrayWrapper
: public Base
{
383 static_assert(std::is_base_of_v
<js::BaseProxyHandler
, Base
>,
384 "Base *must* derive from js::BaseProxyHandler");
387 constexpr explicit XrayWrapper(unsigned flags
)
388 : Base(flags
| WrapperFactory::IS_XRAY_WRAPPER_FLAG
,
389 /* aHasPrototype = */ true) {};
391 /* Standard internal methods. */
392 virtual bool getOwnPropertyDescriptor(
393 JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
, JS::Handle
<jsid
> id
,
394 JS::MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
)
396 virtual bool defineProperty(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
398 JS::Handle
<JS::PropertyDescriptor
> desc
,
399 JS::ObjectOpResult
& result
) const override
;
400 virtual bool ownPropertyKeys(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
401 JS::MutableHandleIdVector props
) const override
;
402 virtual bool delete_(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
404 JS::ObjectOpResult
& result
) const override
;
405 virtual bool enumerate(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
406 JS::MutableHandleIdVector props
) const override
;
407 virtual bool getPrototype(JSContext
* cx
, JS::HandleObject wrapper
,
408 JS::MutableHandleObject protop
) const override
;
409 virtual bool setPrototype(JSContext
* cx
, JS::HandleObject wrapper
,
410 JS::HandleObject proto
,
411 JS::ObjectOpResult
& result
) const override
;
412 virtual bool getPrototypeIfOrdinary(
413 JSContext
* cx
, JS::HandleObject wrapper
, bool* isOrdinary
,
414 JS::MutableHandleObject protop
) const override
;
415 virtual bool setImmutablePrototype(JSContext
* cx
, JS::HandleObject wrapper
,
416 bool* succeeded
) const override
;
417 virtual bool preventExtensions(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
418 JS::ObjectOpResult
& result
) const override
;
419 virtual bool isExtensible(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
420 bool* extensible
) const override
;
421 virtual bool has(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
422 JS::Handle
<jsid
> id
, bool* bp
) const override
;
423 virtual bool get(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
424 JS::HandleValue receiver
, JS::Handle
<jsid
> id
,
425 JS::MutableHandle
<JS::Value
> vp
) const override
;
426 virtual bool set(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
427 JS::Handle
<jsid
> id
, JS::Handle
<JS::Value
> v
,
428 JS::Handle
<JS::Value
> receiver
,
429 JS::ObjectOpResult
& result
) const override
;
430 virtual bool call(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
431 const JS::CallArgs
& args
) const override
;
432 virtual bool construct(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
433 const JS::CallArgs
& args
) const override
;
435 /* SpiderMonkey extensions. */
436 virtual bool hasOwn(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
437 JS::Handle
<jsid
> id
, bool* bp
) const override
;
438 virtual bool getOwnEnumerablePropertyKeys(
439 JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
440 JS::MutableHandleIdVector props
) const override
;
442 virtual bool getBuiltinClass(JSContext
* cx
, JS::HandleObject wapper
,
443 js::ESClass
* cls
) const override
;
444 virtual const char* className(JSContext
* cx
,
445 JS::HandleObject proxy
) const override
;
447 static const XrayWrapper singleton
;
450 bool getPropertyKeys(JSContext
* cx
, JS::Handle
<JSObject
*> wrapper
,
451 unsigned flags
, JS::MutableHandleIdVector props
) const;
454 #define PermissiveXrayDOM \
455 xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>
456 #define PermissiveXrayJS \
457 xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>
458 #define PermissiveXrayOpaque \
459 xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>
461 extern template class PermissiveXrayDOM
;
462 extern template class PermissiveXrayJS
;
463 extern template class PermissiveXrayOpaque
;
466 * Slots for Xray expando objects. See comments in XrayWrapper.cpp for details
467 * of how these get used; we mostly want the value of JSSLOT_EXPANDO_COUNT here.
470 JSSLOT_EXPANDO_NEXT
= 0,
471 JSSLOT_EXPANDO_ORIGIN
,
472 JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER
,
473 JSSLOT_EXPANDO_PROTOTYPE
,
477 extern const JSClassOps XrayExpandoObjectClassOps
;
480 * Call aFunc on all Xray expandos for the given object. aFunc will be passed a
481 * JSObject pointer to an Xray expando object.
483 * No-op when called on non-main threads (where Xrays don't exist).
485 template <typename F
>
486 void ForEachXrayExpandoObject(JS::RootingContext
* aCx
, JSObject
* aTarget
,
488 if (!NS_IsMainThread()) {
493 MOZ_ASSERT(GetXrayTraits(aTarget
) == &DOMXrayTraits::singleton
);
494 JS::RootedObject
rootedTarget(aCx
, aTarget
);
495 JS::RootedObject
head(aCx
,
496 DOMXrayTraits::singleton
.getExpandoChain(rootedTarget
));
499 head
= JS::GetReservedSlot(head
, JSSLOT_EXPANDO_NEXT
).toObjectOrNull();
504 * Ensure the given wrapper has an expando object and return it. This can
505 * return null on failure. Will only be called when "wrapper" is an Xray for a
508 JSObject
* EnsureXrayExpandoObject(JSContext
* cx
, JS::HandleObject wrapper
);
510 // Information about xrays for use by the JITs.
511 extern JS::XrayJitInfo gXrayJitInfo
;