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