Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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/. */
6 :
7 : #ifndef jswrapper_h
8 : #define jswrapper_h
9 :
10 : #include "mozilla/Attributes.h"
11 :
12 : #include "js/Proxy.h"
13 :
14 : namespace js {
15 :
16 : /*
17 : * Helper for Wrapper::New default options.
18 : *
19 : * Callers of Wrapper::New() who wish to specify a prototype for the created
20 : * Wrapper, *MUST* construct a WrapperOptions with a JSContext.
21 : */
22 9308 : class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions {
23 : public:
24 9308 : WrapperOptions() : ProxyOptions(false),
25 9308 : proto_()
26 9308 : {}
27 :
28 : explicit WrapperOptions(JSContext* cx) : ProxyOptions(false),
29 : proto_()
30 : {
31 : proto_.emplace(cx);
32 : }
33 :
34 : inline JSObject* proto() const;
35 : WrapperOptions& setProto(JSObject* protoArg) {
36 : MOZ_ASSERT(proto_);
37 : *proto_ = protoArg;
38 : return *this;
39 : }
40 :
41 : private:
42 : mozilla::Maybe<JS::RootedObject> proto_;
43 : };
44 :
45 : /*
46 : * A wrapper is a proxy with a target object to which it generally forwards
47 : * operations, but may restrict access to certain operations or augment those
48 : * operations in various ways.
49 : *
50 : * A wrapper can be "unwrapped" in C++, exposing the underlying object.
51 : * Callers should be careful to avoid unwrapping security wrappers in the wrong
52 : * context.
53 : *
54 : * Important: If you add a method implementation here, you probably also need
55 : * to add an override in CrossCompartmentWrapper. If you don't, you risk
56 : * compartment mismatches. See bug 945826 comment 0.
57 : */
58 : class JS_FRIEND_API(Wrapper) : public BaseProxyHandler
59 : {
60 : unsigned mFlags;
61 :
62 : public:
63 0 : explicit constexpr Wrapper(unsigned aFlags, bool aHasPrototype = false,
64 : bool aHasSecurityPolicy = false)
65 0 : : BaseProxyHandler(&family, aHasPrototype, aHasSecurityPolicy),
66 0 : mFlags(aFlags)
67 0 : { }
68 :
69 : virtual bool finalizeInBackground(const Value& priv) const override;
70 :
71 : /* Standard internal methods. */
72 : virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
73 : MutableHandle<PropertyDescriptor> desc) const override;
74 : virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
75 : Handle<PropertyDescriptor> desc,
76 : ObjectOpResult& result) const override;
77 : virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
78 : AutoIdVector& props) const override;
79 : virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
80 : ObjectOpResult& result) const override;
81 : virtual JSObject* enumerate(JSContext* cx, HandleObject proxy) const override;
82 : virtual bool getPrototype(JSContext* cx, HandleObject proxy,
83 : MutableHandleObject protop) const override;
84 : virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
85 : ObjectOpResult& result) const override;
86 : virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
87 : MutableHandleObject protop) const override;
88 : virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy,
89 : bool* succeeded) const override;
90 : virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
91 : ObjectOpResult& result) const override;
92 : virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override;
93 : virtual bool has(JSContext* cx, HandleObject proxy, HandleId id,
94 : bool* bp) const override;
95 : virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
96 : HandleId id, MutableHandleValue vp) const override;
97 : virtual bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
98 : HandleValue receiver, ObjectOpResult& result) const override;
99 : virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
100 : virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
101 :
102 : /* SpiderMonkey extensions. */
103 : virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
104 : MutableHandle<PropertyDescriptor> desc) const override;
105 : virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id,
106 : bool* bp) const override;
107 : virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
108 : AutoIdVector& props) const override;
109 : virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
110 : const CallArgs& args) const override;
111 : virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
112 : bool* bp) const override;
113 : virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
114 : virtual bool isArray(JSContext* cx, HandleObject proxy,
115 : JS::IsArrayAnswer* answer) const override;
116 : virtual const char* className(JSContext* cx, HandleObject proxy) const override;
117 : virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
118 : unsigned indent) const override;
119 : virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
120 : virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
121 : MutableHandleValue vp) const override;
122 : virtual bool isCallable(JSObject* obj) const override;
123 : virtual bool isConstructor(JSObject* obj) const override;
124 : virtual JSObject* weakmapKeyDelegate(JSObject* proxy) const override;
125 :
126 : public:
127 : using BaseProxyHandler::Action;
128 :
129 : enum Flags {
130 : CROSS_COMPARTMENT = 1 << 0,
131 : LAST_USED_FLAG = CROSS_COMPARTMENT
132 : };
133 :
134 : static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler,
135 : const WrapperOptions& options = WrapperOptions());
136 :
137 : static JSObject* Renew(JSObject* existing, JSObject* obj, const Wrapper* handler);
138 :
139 : static const Wrapper* wrapperHandler(JSObject* wrapper);
140 :
141 : static JSObject* wrappedObject(JSObject* wrapper);
142 :
143 38635 : unsigned flags() const {
144 38635 : return mFlags;
145 : }
146 :
147 : static const char family;
148 : static const Wrapper singleton;
149 : static const Wrapper singletonWithPrototype;
150 :
151 : static JSObject* defaultProto;
152 : };
153 :
154 : inline JSObject*
155 9264 : WrapperOptions::proto() const
156 : {
157 9264 : return proto_ ? *proto_ : Wrapper::defaultProto;
158 : }
159 :
160 : /* Base class for all cross compartment wrapper handlers. */
161 : class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
162 : {
163 : public:
164 0 : explicit constexpr CrossCompartmentWrapper(unsigned aFlags, bool aHasPrototype = false,
165 : bool aHasSecurityPolicy = false)
166 0 : : Wrapper(CROSS_COMPARTMENT | aFlags, aHasPrototype, aHasSecurityPolicy)
167 0 : { }
168 :
169 : /* Standard internal methods. */
170 : virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
171 : MutableHandle<PropertyDescriptor> desc) const override;
172 : virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
173 : Handle<PropertyDescriptor> desc,
174 : ObjectOpResult& result) const override;
175 : virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper,
176 : AutoIdVector& props) const override;
177 : virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id,
178 : ObjectOpResult& result) const override;
179 : virtual JSObject* enumerate(JSContext* cx, HandleObject wrapper) const override;
180 : virtual bool getPrototype(JSContext* cx, HandleObject proxy,
181 : MutableHandleObject protop) const override;
182 : virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
183 : ObjectOpResult& result) const override;
184 :
185 : virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
186 : MutableHandleObject protop) const override;
187 : virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy,
188 : bool* succeeded) const override;
189 : virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
190 : ObjectOpResult& result) const override;
191 : virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
192 : virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override;
193 : virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
194 : HandleId id, MutableHandleValue vp) const override;
195 : virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
196 : HandleValue receiver, ObjectOpResult& result) const override;
197 : virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
198 : virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
199 :
200 : /* SpiderMonkey extensions. */
201 : virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
202 : MutableHandle<PropertyDescriptor> desc) const override;
203 : virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override;
204 : virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
205 : AutoIdVector& props) const override;
206 : virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
207 : const CallArgs& args) const override;
208 : virtual bool hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
209 : bool* bp) const override;
210 : virtual const char* className(JSContext* cx, HandleObject proxy) const override;
211 : virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
212 : unsigned indent) const override;
213 : virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
214 : virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
215 :
216 : // Allocate CrossCompartmentWrappers in the nursery.
217 3409 : virtual bool canNurseryAllocate() const override { return true; }
218 :
219 : static const CrossCompartmentWrapper singleton;
220 : static const CrossCompartmentWrapper singletonWithPrototype;
221 : };
222 :
223 : class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrapper
224 : {
225 : public:
226 : explicit constexpr OpaqueCrossCompartmentWrapper() : CrossCompartmentWrapper(0)
227 : { }
228 :
229 : /* Standard internal methods. */
230 : virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
231 : MutableHandle<PropertyDescriptor> desc) const override;
232 : virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
233 : Handle<PropertyDescriptor> desc,
234 : ObjectOpResult& result) const override;
235 : virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper,
236 : AutoIdVector& props) const override;
237 : virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id,
238 : ObjectOpResult& result) const override;
239 : virtual JSObject* enumerate(JSContext* cx, HandleObject wrapper) const override;
240 : virtual bool getPrototype(JSContext* cx, HandleObject wrapper,
241 : MutableHandleObject protop) const override;
242 : virtual bool setPrototype(JSContext* cx, HandleObject wrapper, HandleObject proto,
243 : ObjectOpResult& result) const override;
244 : virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject wrapper, bool* isOrdinary,
245 : MutableHandleObject protop) const override;
246 : virtual bool setImmutablePrototype(JSContext* cx, HandleObject wrapper,
247 : bool* succeeded) const override;
248 : virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
249 : ObjectOpResult& result) const override;
250 : virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
251 : virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id,
252 : bool* bp) const override;
253 : virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
254 : HandleId id, MutableHandleValue vp) const override;
255 : virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
256 : HandleValue receiver, ObjectOpResult& result) const override;
257 : virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
258 : virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
259 :
260 : /* SpiderMonkey extensions. */
261 : virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
262 : MutableHandle<PropertyDescriptor> desc) const override;
263 : virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id,
264 : bool* bp) const override;
265 : virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
266 : AutoIdVector& props) const override;
267 : virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override;
268 : virtual bool isArray(JSContext* cx, HandleObject obj,
269 : JS::IsArrayAnswer* answer) const override;
270 : virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
271 : virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
272 :
273 : static const OpaqueCrossCompartmentWrapper singleton;
274 : };
275 :
276 : /*
277 : * Base class for security wrappers. A security wrapper is potentially hiding
278 : * all or part of some wrapped object thus SecurityWrapper defaults to denying
279 : * access to the wrappee. This is the opposite of Wrapper which tries to be
280 : * completely transparent.
281 : *
282 : * NB: Currently, only a few ProxyHandler operations are overridden to deny
283 : * access, relying on derived SecurityWrapper to block access when necessary.
284 : */
285 : template <class Base>
286 : class JS_FRIEND_API(SecurityWrapper) : public Base
287 : {
288 : public:
289 0 : explicit constexpr SecurityWrapper(unsigned flags, bool hasPrototype = false)
290 0 : : Base(flags, hasPrototype, /* hasSecurityPolicy = */ true)
291 0 : { }
292 :
293 : virtual bool enter(JSContext* cx, HandleObject wrapper, HandleId id, Wrapper::Action act,
294 : bool mayThrow, bool* bp) const override;
295 :
296 : virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
297 : Handle<PropertyDescriptor> desc,
298 : ObjectOpResult& result) const override;
299 : virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
300 : virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
301 : ObjectOpResult& result) const override;
302 : virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
303 : ObjectOpResult& result) const override;
304 : virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const override;
305 :
306 : virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
307 : const CallArgs& args) const override;
308 : virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override;
309 : virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
310 : virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
311 : virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
312 :
313 : // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
314 : // against.
315 :
316 : virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
317 : JS::HandleObject callable) const override;
318 : virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override;
319 :
320 : /*
321 : * Allow our subclasses to select the superclass behavior they want without
322 : * needing to specify an exact superclass.
323 : */
324 : typedef Base Permissive;
325 : typedef SecurityWrapper<Base> Restrictive;
326 : };
327 :
328 : typedef SecurityWrapper<CrossCompartmentWrapper> CrossCompartmentSecurityWrapper;
329 :
330 : extern JSObject*
331 : TransparentObjectWrapper(JSContext* cx, HandleObject existing, HandleObject obj);
332 :
333 : inline bool
334 419207 : IsWrapper(JSObject* obj)
335 : {
336 419207 : return IsProxy(obj) && GetProxyHandler(obj)->family() == &Wrapper::family;
337 : }
338 :
339 : // Given a JSObject, returns that object stripped of wrappers. If
340 : // stopAtWindowProxy is true, then this returns the WindowProxy if it was
341 : // previously wrapped. Otherwise, this returns the first object for which
342 : // JSObject::isWrapper returns false.
343 : //
344 : // ExposeToActiveJS is called on wrapper targets to allow gray marking
345 : // assertions to work while an incremental GC is in progress, but this means
346 : // that this cannot be called from the GC or off the main thread.
347 : JS_FRIEND_API(JSObject*)
348 : UncheckedUnwrap(JSObject* obj, bool stopAtWindowProxy = true, unsigned* flagsp = nullptr);
349 :
350 : // Given a JSObject, returns that object stripped of wrappers. At each stage,
351 : // the security wrapper has the opportunity to veto the unwrap. If
352 : // stopAtWindowProxy is true, then this returns the WindowProxy if it was
353 : // previously wrapped.
354 : //
355 : // ExposeToActiveJS is called on wrapper targets to allow gray marking
356 : // assertions to work while an incremental GC is in progress, but this means
357 : // that this cannot be called from the GC or off the main thread.
358 : JS_FRIEND_API(JSObject*)
359 : CheckedUnwrap(JSObject* obj, bool stopAtWindowProxy = true);
360 :
361 : // Unwrap only the outermost security wrapper, with the same semantics as
362 : // above. This is the checked version of Wrapper::wrappedObject.
363 : JS_FRIEND_API(JSObject*)
364 : UnwrapOneChecked(JSObject* obj, bool stopAtWindowProxy = true);
365 :
366 : // Given a JSObject, returns that object stripped of wrappers. This returns the
367 : // WindowProxy if it was previously wrapped.
368 : //
369 : // ExposeToActiveJS is not called on wrapper targets so this can be called from
370 : // the GC or off the main thread.
371 : JS_FRIEND_API(JSObject*)
372 : UncheckedUnwrapWithoutExpose(JSObject* obj);
373 :
374 : void
375 : ReportAccessDenied(JSContext* cx);
376 :
377 : JS_FRIEND_API(bool)
378 : IsCrossCompartmentWrapper(JSObject* obj);
379 :
380 : JS_FRIEND_API(void)
381 : NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper);
382 :
383 : void
384 : RemapWrapper(JSContext* cx, JSObject* wobj, JSObject* newTarget);
385 :
386 : JS_FRIEND_API(bool)
387 : RemapAllWrappersForObject(JSContext* cx, JSObject* oldTarget,
388 : JSObject* newTarget);
389 :
390 : // API to recompute all cross-compartment wrappers whose source and target
391 : // match the given filters.
392 : JS_FRIEND_API(bool)
393 : RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
394 : const CompartmentFilter& targetFilter);
395 :
396 : } /* namespace js */
397 :
398 : #endif /* jswrapper_h */
|