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/Attributes.h"
17 * Helper for Wrapper::New default options.
19 * Callers of Wrapper::New() who wish to specify a prototype for the created
20 * Wrapper, *MUST* construct a WrapperOptions with a JSContext.
22 class MOZ_STACK_CLASS WrapperOptions
: public ProxyOptions
{
24 WrapperOptions() : ProxyOptions(false), proto_() {}
26 explicit WrapperOptions(JSContext
* cx
) : ProxyOptions(false), proto_() {
30 inline JSObject
* proto() const;
31 WrapperOptions
& setProto(JSObject
* protoArg
) {
38 mozilla::Maybe
<JS::RootedObject
> proto_
;
41 // Base class for proxy handlers that want to forward all operations to an
42 // object stored in the proxy's private slot.
43 class JS_FRIEND_API ForwardingProxyHandler
: public BaseProxyHandler
{
45 using BaseProxyHandler::BaseProxyHandler
;
47 /* Standard internal methods. */
48 virtual bool getOwnPropertyDescriptor(
49 JSContext
* cx
, HandleObject proxy
, HandleId id
,
50 MutableHandle
<PropertyDescriptor
> desc
) const override
;
51 virtual bool defineProperty(JSContext
* cx
, HandleObject proxy
, HandleId id
,
52 Handle
<PropertyDescriptor
> desc
,
53 ObjectOpResult
& result
) const override
;
54 virtual bool ownPropertyKeys(JSContext
* cx
, HandleObject proxy
,
55 MutableHandleIdVector props
) const override
;
56 virtual bool delete_(JSContext
* cx
, HandleObject proxy
, HandleId id
,
57 ObjectOpResult
& result
) const override
;
58 virtual bool enumerate(JSContext
* cx
, HandleObject proxy
,
59 MutableHandleIdVector props
) const override
;
60 virtual bool getPrototype(JSContext
* cx
, HandleObject proxy
,
61 MutableHandleObject protop
) const override
;
62 virtual bool setPrototype(JSContext
* cx
, HandleObject proxy
,
64 ObjectOpResult
& result
) const override
;
65 virtual bool getPrototypeIfOrdinary(
66 JSContext
* cx
, HandleObject proxy
, bool* isOrdinary
,
67 MutableHandleObject protop
) const override
;
68 virtual bool setImmutablePrototype(JSContext
* cx
, HandleObject proxy
,
69 bool* succeeded
) const override
;
70 virtual bool preventExtensions(JSContext
* cx
, HandleObject proxy
,
71 ObjectOpResult
& result
) const override
;
72 virtual bool isExtensible(JSContext
* cx
, HandleObject proxy
,
73 bool* extensible
) const override
;
74 virtual bool has(JSContext
* cx
, HandleObject proxy
, HandleId id
,
75 bool* bp
) const override
;
76 virtual bool get(JSContext
* cx
, HandleObject proxy
, HandleValue receiver
,
77 HandleId id
, MutableHandleValue vp
) const override
;
78 virtual bool set(JSContext
* cx
, HandleObject proxy
, HandleId id
,
79 HandleValue v
, HandleValue receiver
,
80 ObjectOpResult
& result
) const override
;
81 virtual bool call(JSContext
* cx
, HandleObject proxy
,
82 const CallArgs
& args
) const override
;
83 virtual bool construct(JSContext
* cx
, HandleObject proxy
,
84 const CallArgs
& args
) const override
;
86 /* SpiderMonkey extensions. */
87 virtual bool hasOwn(JSContext
* cx
, HandleObject proxy
, HandleId id
,
88 bool* bp
) const override
;
89 virtual bool getOwnEnumerablePropertyKeys(
90 JSContext
* cx
, HandleObject proxy
,
91 MutableHandleIdVector props
) const override
;
92 virtual bool nativeCall(JSContext
* cx
, IsAcceptableThis test
, NativeImpl impl
,
93 const CallArgs
& args
) const override
;
94 virtual bool hasInstance(JSContext
* cx
, HandleObject proxy
,
95 MutableHandleValue v
, bool* bp
) const override
;
96 virtual bool getBuiltinClass(JSContext
* cx
, HandleObject proxy
,
97 ESClass
* cls
) const override
;
98 virtual bool isArray(JSContext
* cx
, HandleObject proxy
,
99 JS::IsArrayAnswer
* answer
) const override
;
100 virtual const char* className(JSContext
* cx
,
101 HandleObject proxy
) const override
;
102 virtual JSString
* fun_toString(JSContext
* cx
, HandleObject proxy
,
103 bool isToSource
) const override
;
104 virtual RegExpShared
* regexp_toShared(JSContext
* cx
,
105 HandleObject proxy
) const override
;
106 virtual bool boxedValue_unbox(JSContext
* cx
, HandleObject proxy
,
107 MutableHandleValue vp
) const override
;
108 virtual bool isCallable(JSObject
* obj
) const override
;
109 virtual bool isConstructor(JSObject
* obj
) const override
;
113 * A wrapper is a proxy with a target object to which it generally forwards
114 * operations, but may restrict access to certain operations or augment those
115 * operations in various ways.
117 * A wrapper can be "unwrapped" in C++, exposing the underlying object.
118 * Callers should be careful to avoid unwrapping security wrappers in the wrong
121 * Important: If you add a method implementation here, you probably also need
122 * to add an override in CrossCompartmentWrapper. If you don't, you risk
123 * compartment mismatches. See bug 945826 comment 0.
125 class JS_FRIEND_API Wrapper
: public ForwardingProxyHandler
{
129 explicit constexpr Wrapper(unsigned aFlags
, bool aHasPrototype
= false,
130 bool aHasSecurityPolicy
= false)
131 : ForwardingProxyHandler(&family
, aHasPrototype
, aHasSecurityPolicy
),
134 virtual bool finalizeInBackground(const Value
& priv
) const override
;
137 * A hook subclasses can override to implement CheckedUnwrapDynamic
138 * behavior. The JSContext represents the "who is trying to unwrap?" Realm.
139 * The JSObject is the wrapper that the caller is trying to unwrap.
141 virtual bool dynamicCheckedUnwrapAllowed(HandleObject obj
,
142 JSContext
* cx
) const {
143 MOZ_ASSERT(hasSecurityPolicy(), "Why are you asking?");
147 using BaseProxyHandler::Action
;
149 enum Flags
{ CROSS_COMPARTMENT
= 1 << 0, LAST_USED_FLAG
= CROSS_COMPARTMENT
};
151 static JSObject
* New(JSContext
* cx
, JSObject
* obj
, const Wrapper
* handler
,
152 const WrapperOptions
& options
= WrapperOptions());
154 static JSObject
* NewSingleton(
155 JSContext
* cx
, JSObject
* obj
, const Wrapper
* handler
,
156 const WrapperOptions
& options
= WrapperOptions());
158 static JSObject
* Renew(JSObject
* existing
, JSObject
* obj
,
159 const Wrapper
* handler
);
161 static inline const Wrapper
* wrapperHandler(const JSObject
* wrapper
);
163 static JSObject
* wrappedObject(JSObject
* wrapper
);
165 unsigned flags() const { return mFlags
; }
167 bool isCrossCompartmentWrapper() const {
168 return !!(mFlags
& CROSS_COMPARTMENT
);
171 static const char family
;
172 static const Wrapper singleton
;
173 static const Wrapper singletonWithPrototype
;
175 static JSObject
* const defaultProto
;
178 inline JSObject
* WrapperOptions::proto() const {
179 return proto_
? *proto_
: Wrapper::defaultProto
;
182 /* Base class for all cross compartment wrapper handlers. */
183 class JS_FRIEND_API CrossCompartmentWrapper
: public Wrapper
{
185 explicit constexpr CrossCompartmentWrapper(unsigned aFlags
,
186 bool aHasPrototype
= false,
187 bool aHasSecurityPolicy
= false)
188 : Wrapper(CROSS_COMPARTMENT
| aFlags
, aHasPrototype
, aHasSecurityPolicy
) {
191 /* Standard internal methods. */
192 virtual bool getOwnPropertyDescriptor(
193 JSContext
* cx
, HandleObject wrapper
, HandleId id
,
194 MutableHandle
<PropertyDescriptor
> desc
) const override
;
195 virtual bool defineProperty(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
196 Handle
<PropertyDescriptor
> desc
,
197 ObjectOpResult
& result
) const override
;
198 virtual bool ownPropertyKeys(JSContext
* cx
, HandleObject wrapper
,
199 MutableHandleIdVector props
) const override
;
200 virtual bool delete_(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
201 ObjectOpResult
& result
) const override
;
202 virtual bool enumerate(JSContext
* cx
, HandleObject proxy
,
203 MutableHandleIdVector props
) const override
;
204 virtual bool getPrototype(JSContext
* cx
, HandleObject proxy
,
205 MutableHandleObject protop
) const override
;
206 virtual bool setPrototype(JSContext
* cx
, HandleObject proxy
,
208 ObjectOpResult
& result
) const override
;
210 virtual bool getPrototypeIfOrdinary(
211 JSContext
* cx
, HandleObject proxy
, bool* isOrdinary
,
212 MutableHandleObject protop
) const override
;
213 virtual bool setImmutablePrototype(JSContext
* cx
, HandleObject proxy
,
214 bool* succeeded
) const override
;
215 virtual bool preventExtensions(JSContext
* cx
, HandleObject wrapper
,
216 ObjectOpResult
& result
) const override
;
217 virtual bool isExtensible(JSContext
* cx
, HandleObject wrapper
,
218 bool* extensible
) const override
;
219 virtual bool has(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
220 bool* bp
) const override
;
221 virtual bool get(JSContext
* cx
, HandleObject wrapper
, HandleValue receiver
,
222 HandleId id
, MutableHandleValue vp
) const override
;
223 virtual bool set(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
224 HandleValue v
, HandleValue receiver
,
225 ObjectOpResult
& result
) const override
;
226 virtual bool call(JSContext
* cx
, HandleObject wrapper
,
227 const CallArgs
& args
) const override
;
228 virtual bool construct(JSContext
* cx
, HandleObject wrapper
,
229 const CallArgs
& args
) const override
;
231 /* SpiderMonkey extensions. */
232 virtual bool hasOwn(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
233 bool* bp
) const override
;
234 virtual bool getOwnEnumerablePropertyKeys(
235 JSContext
* cx
, HandleObject wrapper
,
236 MutableHandleIdVector props
) const override
;
237 virtual bool nativeCall(JSContext
* cx
, IsAcceptableThis test
, NativeImpl impl
,
238 const CallArgs
& args
) const override
;
239 virtual bool hasInstance(JSContext
* cx
, HandleObject wrapper
,
240 MutableHandleValue v
, bool* bp
) const override
;
241 virtual const char* className(JSContext
* cx
,
242 HandleObject proxy
) const override
;
243 virtual JSString
* fun_toString(JSContext
* cx
, HandleObject wrapper
,
244 bool isToSource
) const override
;
245 virtual RegExpShared
* regexp_toShared(JSContext
* cx
,
246 HandleObject proxy
) const override
;
247 virtual bool boxedValue_unbox(JSContext
* cx
, HandleObject proxy
,
248 MutableHandleValue vp
) const override
;
250 // Allocate CrossCompartmentWrappers in the nursery.
251 virtual bool canNurseryAllocate() const override
{ return true; }
253 static const CrossCompartmentWrapper singleton
;
254 static const CrossCompartmentWrapper singletonWithPrototype
;
257 class JS_FRIEND_API OpaqueCrossCompartmentWrapper
258 : public CrossCompartmentWrapper
{
260 explicit constexpr OpaqueCrossCompartmentWrapper()
261 : CrossCompartmentWrapper(0) {}
263 /* Standard internal methods. */
264 virtual bool getOwnPropertyDescriptor(
265 JSContext
* cx
, HandleObject wrapper
, HandleId id
,
266 MutableHandle
<PropertyDescriptor
> desc
) const override
;
267 virtual bool defineProperty(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
268 Handle
<PropertyDescriptor
> desc
,
269 ObjectOpResult
& result
) const override
;
270 virtual bool ownPropertyKeys(JSContext
* cx
, HandleObject wrapper
,
271 MutableHandleIdVector props
) const override
;
272 virtual bool delete_(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
273 ObjectOpResult
& result
) const override
;
274 virtual bool enumerate(JSContext
* cx
, HandleObject proxy
,
275 MutableHandleIdVector props
) const override
;
276 virtual bool getPrototype(JSContext
* cx
, HandleObject wrapper
,
277 MutableHandleObject protop
) const override
;
278 virtual bool setPrototype(JSContext
* cx
, HandleObject wrapper
,
280 ObjectOpResult
& result
) const override
;
281 virtual bool getPrototypeIfOrdinary(
282 JSContext
* cx
, HandleObject wrapper
, bool* isOrdinary
,
283 MutableHandleObject protop
) const override
;
284 virtual bool setImmutablePrototype(JSContext
* cx
, HandleObject wrapper
,
285 bool* succeeded
) const override
;
286 virtual bool preventExtensions(JSContext
* cx
, HandleObject wrapper
,
287 ObjectOpResult
& result
) const override
;
288 virtual bool isExtensible(JSContext
* cx
, HandleObject wrapper
,
289 bool* extensible
) const override
;
290 virtual bool has(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
291 bool* bp
) const override
;
292 virtual bool get(JSContext
* cx
, HandleObject wrapper
, HandleValue receiver
,
293 HandleId id
, MutableHandleValue vp
) const override
;
294 virtual bool set(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
295 HandleValue v
, HandleValue receiver
,
296 ObjectOpResult
& result
) const override
;
297 virtual bool call(JSContext
* cx
, HandleObject wrapper
,
298 const CallArgs
& args
) const override
;
299 virtual bool construct(JSContext
* cx
, HandleObject wrapper
,
300 const CallArgs
& args
) const override
;
302 /* SpiderMonkey extensions. */
303 virtual bool hasOwn(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
304 bool* bp
) const override
;
305 virtual bool getOwnEnumerablePropertyKeys(
306 JSContext
* cx
, HandleObject wrapper
,
307 MutableHandleIdVector props
) const override
;
308 virtual bool getBuiltinClass(JSContext
* cx
, HandleObject wrapper
,
309 ESClass
* cls
) const override
;
310 virtual bool isArray(JSContext
* cx
, HandleObject obj
,
311 JS::IsArrayAnswer
* answer
) const override
;
312 virtual bool hasInstance(JSContext
* cx
, HandleObject wrapper
,
313 MutableHandleValue v
, bool* bp
) const override
;
314 virtual const char* className(JSContext
* cx
,
315 HandleObject wrapper
) const override
;
316 virtual JSString
* fun_toString(JSContext
* cx
, HandleObject proxy
,
317 bool isToSource
) const override
;
319 static const OpaqueCrossCompartmentWrapper singleton
;
323 * Base class for security wrappers. A security wrapper is potentially hiding
324 * all or part of some wrapped object thus SecurityWrapper defaults to denying
325 * access to the wrappee. This is the opposite of Wrapper which tries to be
326 * completely transparent.
328 * NB: Currently, only a few ProxyHandler operations are overridden to deny
329 * access, relying on derived SecurityWrapper to block access when necessary.
331 template <class Base
>
332 class JS_FRIEND_API SecurityWrapper
: public Base
{
334 explicit constexpr SecurityWrapper(unsigned flags
, bool hasPrototype
= false)
335 : Base(flags
, hasPrototype
, /* hasSecurityPolicy = */ true) {}
337 virtual bool enter(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
338 Wrapper::Action act
, bool mayThrow
,
339 bool* bp
) const override
;
341 virtual bool defineProperty(JSContext
* cx
, HandleObject wrapper
, HandleId id
,
342 Handle
<PropertyDescriptor
> desc
,
343 ObjectOpResult
& result
) const override
;
344 virtual bool isExtensible(JSContext
* cx
, HandleObject wrapper
,
345 bool* extensible
) const override
;
346 virtual bool preventExtensions(JSContext
* cx
, HandleObject wrapper
,
347 ObjectOpResult
& result
) const override
;
348 virtual bool setPrototype(JSContext
* cx
, HandleObject proxy
,
350 ObjectOpResult
& result
) const override
;
351 virtual bool setImmutablePrototype(JSContext
* cx
, HandleObject proxy
,
352 bool* succeeded
) const override
;
354 virtual bool nativeCall(JSContext
* cx
, IsAcceptableThis test
, NativeImpl impl
,
355 const CallArgs
& args
) const override
;
356 virtual bool getBuiltinClass(JSContext
* cx
, HandleObject wrapper
,
357 ESClass
* cls
) const override
;
358 virtual bool isArray(JSContext
* cx
, HandleObject wrapper
,
359 JS::IsArrayAnswer
* answer
) const override
;
360 virtual RegExpShared
* regexp_toShared(JSContext
* cx
,
361 HandleObject proxy
) const override
;
362 virtual bool boxedValue_unbox(JSContext
* cx
, HandleObject proxy
,
363 MutableHandleValue vp
) const override
;
365 // Allow isCallable and isConstructor. They used to be class-level, and so
366 // could not be guarded against.
369 * Allow our subclasses to select the superclass behavior they want without
370 * needing to specify an exact superclass.
372 typedef Base Permissive
;
373 typedef SecurityWrapper
<Base
> Restrictive
;
376 typedef SecurityWrapper
<CrossCompartmentWrapper
>
377 CrossCompartmentSecurityWrapper
;
379 extern JSObject
* TransparentObjectWrapper(JSContext
* cx
, HandleObject existing
,
382 inline bool IsWrapper(const JSObject
* obj
) {
383 return IsProxy(obj
) && GetProxyHandler(obj
)->family() == &Wrapper::family
;
386 inline bool IsCrossCompartmentWrapper(const JSObject
* obj
) {
387 return IsWrapper(obj
) &&
388 (Wrapper::wrapperHandler(obj
)->flags() & Wrapper::CROSS_COMPARTMENT
);
391 /* static */ inline const Wrapper
* Wrapper::wrapperHandler(
392 const JSObject
* wrapper
) {
393 MOZ_ASSERT(IsWrapper(wrapper
));
394 return static_cast<const Wrapper
*>(GetProxyHandler(wrapper
));
397 // Given a JSObject, returns that object stripped of wrappers. If
398 // stopAtWindowProxy is true, then this returns the WindowProxy if it was
399 // previously wrapped. Otherwise, this returns the first object for which
400 // JSObject::isWrapper returns false.
402 // ExposeToActiveJS is called on wrapper targets to allow gray marking
403 // assertions to work while an incremental GC is in progress, but this means
404 // that this cannot be called from the GC or off the main thread.
405 JS_FRIEND_API JSObject
* UncheckedUnwrap(JSObject
* obj
,
406 bool stopAtWindowProxy
= true,
407 unsigned* flagsp
= nullptr);
409 // Given a JSObject, returns that object stripped of wrappers, except
410 // WindowProxy wrappers. At each stage, the wrapper has the opportunity to veto
411 // the unwrap. Null is returned if there are security wrappers that can't be
414 // This does a static-only unwrap check: it basically checks whether _all_
415 // globals in the wrapper's source compartment should be able to access the
416 // wrapper target. This won't necessarily return the right thing for the HTML
417 // spec's cross-origin objects (WindowProxy and Location), but is fine to use
418 // when failure to unwrap one of those objects wouldn't be a problem. For
419 // example, if you want to test whether your target object is a specific class
420 // that's not WindowProxy or Location, you can use this.
422 // ExposeToActiveJS is called on wrapper targets to allow gray marking
423 // assertions to work while an incremental GC is in progress, but this means
424 // that this cannot be called from the GC or off the main thread.
425 JS_FRIEND_API JSObject
* CheckedUnwrapStatic(JSObject
* obj
);
427 // Unwrap only the outermost security wrapper, with the same semantics as
428 // above. This is the checked version of Wrapper::wrappedObject.
429 JS_FRIEND_API JSObject
* UnwrapOneCheckedStatic(JSObject
* obj
);
431 // Given a JSObject, returns that object stripped of wrappers. At each stage,
432 // the security wrapper has the opportunity to veto the unwrap. If
433 // stopAtWindowProxy is true, then this returns the WindowProxy if it was
434 // previously wrapped. Null is returned if there are security wrappers that
435 // can't be unwrapped.
437 // ExposeToActiveJS is called on wrapper targets to allow gray marking
438 // assertions to work while an incremental GC is in progress, but this means
439 // that this cannot be called from the GC or off the main thread.
441 // The JSContext argument will be used for dynamic checks (needed by WindowProxy
442 // and Location) and should represent the Realm doing the unwrapping. It is not
443 // used to throw exceptions; this function never throws.
445 // This function may be able to GC (and the static analysis definitely thinks it
446 // can), but it still takes a JSObject* argument, because some of its callers
447 // would actually have a bit of a hard time producing a Rooted. And it ends up
448 // having to root internally anyway, because it wants to use the value in a loop
449 // and you can't assign to a HandleObject. What this means is that callers who
450 // plan to use the argument object after they have called this function will
451 // need to root it to avoid hazard failures, even though this function doesn't
453 JS_FRIEND_API JSObject
* CheckedUnwrapDynamic(JSObject
* obj
, JSContext
* cx
,
454 bool stopAtWindowProxy
= true);
456 // Unwrap only the outermost security wrapper, with the same semantics as
457 // above. This is the checked version of Wrapper::wrappedObject.
458 JS_FRIEND_API JSObject
* UnwrapOneCheckedDynamic(HandleObject obj
, JSContext
* cx
,
459 bool stopAtWindowProxy
= true);
461 // Given a JSObject, returns that object stripped of wrappers. This returns the
462 // WindowProxy if it was previously wrapped.
464 // ExposeToActiveJS is not called on wrapper targets so this can be called from
465 // the GC or off the main thread.
466 JS_FRIEND_API JSObject
* UncheckedUnwrapWithoutExpose(JSObject
* obj
);
468 void ReportAccessDenied(JSContext
* cx
);
470 JS_FRIEND_API
void NukeCrossCompartmentWrapper(JSContext
* cx
,
473 // If a cross-compartment wrapper source => target exists, nuke it.
474 JS_FRIEND_API
void NukeCrossCompartmentWrapperIfExists(JSContext
* cx
,
475 JS::Compartment
* source
,
478 void RemapWrapper(JSContext
* cx
, JSObject
* wobj
, JSObject
* newTarget
);
479 void RemapDeadWrapper(JSContext
* cx
, HandleObject wobj
, HandleObject newTarget
);
481 JS_FRIEND_API
bool RemapAllWrappersForObject(JSContext
* cx
,
482 HandleObject oldTarget
,
483 HandleObject newTarget
);
485 // API to recompute all cross-compartment wrappers whose source and target
486 // match the given filters.
487 JS_FRIEND_API
bool RecomputeWrappers(JSContext
* cx
,
488 const CompartmentFilter
& sourceFilter
,
489 const CompartmentFilter
& targetFilter
);
493 #endif /* js_Wrapper_h */