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 class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions {
23 public:
WrapperOptions()24 WrapperOptions() : ProxyOptions(false),
25 proto_()
26 {}
27
WrapperOptions(JSContext * cx)28 explicit WrapperOptions(JSContext* cx) : ProxyOptions(false),
29 proto_()
30 {
31 proto_.emplace(cx);
32 }
33
34 inline JSObject* proto() const;
setProto(JSObject * protoArg)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 instrument the
48 * methods in various ways. A wrapper is distinct from a Direct Proxy Handler
49 * in the sense that it can be "unwrapped" in C++, exposing the underlying
50 * object (Direct Proxy Handlers have an underlying target object, but don't
51 * expect to expose this object via any kind of unwrapping operation). Callers
52 * should be careful to avoid unwrapping security wrappers in the wrong
53 * context.
54 */
JS_FRIEND_API(Wrapper)55 class JS_FRIEND_API(Wrapper) : public DirectProxyHandler
56 {
57 unsigned mFlags;
58
59 public:
60 using BaseProxyHandler::Action;
61
62 enum Flags {
63 CROSS_COMPARTMENT = 1 << 0,
64 LAST_USED_FLAG = CROSS_COMPARTMENT
65 };
66
67 static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler,
68 const WrapperOptions& options = WrapperOptions());
69
70 static JSObject* Renew(JSContext* cx, JSObject* existing, JSObject* obj, const Wrapper* handler);
71
72 static const Wrapper* wrapperHandler(JSObject* wrapper);
73
74 static JSObject* wrappedObject(JSObject* wrapper);
75
76 unsigned flags() const {
77 return mFlags;
78 }
79
80 explicit MOZ_CONSTEXPR Wrapper(unsigned aFlags, bool aHasPrototype = false,
81 bool aHasSecurityPolicy = false)
82 : DirectProxyHandler(&family, aHasPrototype, aHasSecurityPolicy),
83 mFlags(aFlags)
84 { }
85
86 virtual bool finalizeInBackground(Value priv) const override;
87 virtual bool isConstructor(JSObject* obj) const override;
88
89 static const char family;
90 static const Wrapper singleton;
91 static const Wrapper singletonWithPrototype;
92
93 static JSObject* defaultProto;
94 };
95
96 inline JSObject*
proto()97 WrapperOptions::proto() const
98 {
99 return proto_ ? *proto_ : Wrapper::defaultProto;
100 }
101
102 /* Base class for all cross compartment wrapper handlers. */
JS_FRIEND_API(CrossCompartmentWrapper)103 class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
104 {
105 public:
106 explicit MOZ_CONSTEXPR CrossCompartmentWrapper(unsigned aFlags, bool aHasPrototype = false,
107 bool aHasSecurityPolicy = false)
108 : Wrapper(CROSS_COMPARTMENT | aFlags, aHasPrototype, aHasSecurityPolicy)
109 { }
110
111 /* Standard internal methods. */
112 virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
113 MutableHandle<JSPropertyDescriptor> desc) const override;
114 virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
115 Handle<JSPropertyDescriptor> desc,
116 ObjectOpResult& result) const override;
117 virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper,
118 AutoIdVector& props) const override;
119 virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id,
120 ObjectOpResult& result) const override;
121 virtual bool enumerate(JSContext* cx, HandleObject wrapper, MutableHandleObject objp) const override;
122 virtual bool getPrototype(JSContext* cx, HandleObject proxy,
123 MutableHandleObject protop) const override;
124 virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
125 ObjectOpResult& result) const override;
126
127 virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy,
128 bool* succeeded) const override;
129 virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
130 ObjectOpResult& result) const override;
131 virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
132 virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override;
133 virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
134 HandleId id, MutableHandleValue vp) const override;
135 virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
136 HandleValue receiver, ObjectOpResult& result) const override;
137 virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
138 virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
139
140 /* SpiderMonkey extensions. */
141 virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
142 MutableHandle<JSPropertyDescriptor> desc) const override;
143 virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override;
144 virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
145 AutoIdVector& props) const override;
146 virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
147 const CallArgs& args) const override;
148 virtual bool hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
149 bool* bp) const override;
150 virtual const char* className(JSContext* cx, HandleObject proxy) const override;
151 virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
152 unsigned indent) const override;
153 virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
154 virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
155
156 static const CrossCompartmentWrapper singleton;
157 static const CrossCompartmentWrapper singletonWithPrototype;
158 };
159
JS_FRIEND_API(OpaqueCrossCompartmentWrapper)160 class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrapper
161 {
162 public:
163 explicit MOZ_CONSTEXPR OpaqueCrossCompartmentWrapper() : CrossCompartmentWrapper(0)
164 { }
165
166 /* Standard internal methods. */
167 virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
168 MutableHandle<JSPropertyDescriptor> desc) const override;
169 virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
170 Handle<JSPropertyDescriptor> desc,
171 ObjectOpResult& result) const override;
172 virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper,
173 AutoIdVector& props) const override;
174 virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id,
175 ObjectOpResult& result) const override;
176 virtual bool enumerate(JSContext* cx, HandleObject wrapper,
177 MutableHandleObject objp) const override;
178 virtual bool getPrototype(JSContext* cx, HandleObject wrapper,
179 MutableHandleObject protop) const override;
180 virtual bool setPrototype(JSContext* cx, HandleObject wrapper, HandleObject proto,
181 ObjectOpResult& result) const override;
182 virtual bool setImmutablePrototype(JSContext* cx, HandleObject wrapper,
183 bool* succeeded) const override;
184 virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
185 ObjectOpResult& result) const override;
186 virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
187 virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id,
188 bool* bp) const override;
189 virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
190 HandleId id, MutableHandleValue vp) const override;
191 virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
192 HandleValue receiver, ObjectOpResult& result) const override;
193 virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
194 virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
195
196 /* SpiderMonkey extensions. */
197 virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
198 MutableHandle<JSPropertyDescriptor> desc) const override;
199 virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id,
200 bool* bp) const override;
201 virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
202 AutoIdVector& props) const override;
203 virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper,
204 ESClassValue* classValue) const override;
205 virtual bool isArray(JSContext* cx, HandleObject obj,
206 JS::IsArrayAnswer* answer) const override;
207 virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
208 virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
209
210 static const OpaqueCrossCompartmentWrapper singleton;
211 };
212
213 /*
214 * Base class for security wrappers. A security wrapper is potentially hiding
215 * all or part of some wrapped object thus SecurityWrapper defaults to denying
216 * access to the wrappee. This is the opposite of Wrapper which tries to be
217 * completely transparent.
218 *
219 * NB: Currently, only a few ProxyHandler operations are overridden to deny
220 * access, relying on derived SecurityWrapper to block access when necessary.
221 */
222 template <class Base>
JS_FRIEND_API(SecurityWrapper)223 class JS_FRIEND_API(SecurityWrapper) : public Base
224 {
225 public:
226 explicit MOZ_CONSTEXPR SecurityWrapper(unsigned flags, bool hasPrototype = false)
227 : Base(flags, hasPrototype, /* hasSecurityPolicy = */ true)
228 { }
229
230 virtual bool enter(JSContext* cx, HandleObject wrapper, HandleId id, Wrapper::Action act,
231 bool* bp) const override;
232
233 virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
234 Handle<JSPropertyDescriptor> desc,
235 ObjectOpResult& result) const override;
236 virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
237 virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
238 ObjectOpResult& result) const override;
239 virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
240 ObjectOpResult& result) const override;
241 virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const override;
242
243 virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
244 const CallArgs& args) const override;
245 virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper,
246 ESClassValue* classValue) const override;
247 virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
248 virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
249 virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
250
251 // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
252 // against.
253
254 virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
255 JS::HandleObject callable) const override;
256 virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override;
257
258 /*
259 * Allow our subclasses to select the superclass behavior they want without
260 * needing to specify an exact superclass.
261 */
262 typedef Base Permissive;
263 typedef SecurityWrapper<Base> Restrictive;
264 };
265
266 typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper;
267 typedef SecurityWrapper<CrossCompartmentWrapper> CrossCompartmentSecurityWrapper;
268
269 extern JSObject*
270 TransparentObjectWrapper(JSContext* cx, HandleObject existing, HandleObject obj);
271
272 inline bool
IsWrapper(JSObject * obj)273 IsWrapper(JSObject* obj)
274 {
275 return IsProxy(obj) && GetProxyHandler(obj)->family() == &Wrapper::family;
276 }
277
278 // Given a JSObject, returns that object stripped of wrappers. If
279 // stopAtWindowProxy is true, then this returns the WindowProxy if it was
280 // previously wrapped. Otherwise, this returns the first object for
281 // which JSObject::isWrapper returns false.
282 JS_FRIEND_API(JSObject*)
283 UncheckedUnwrap(JSObject* obj, bool stopAtWindowProxy = true, unsigned* flagsp = nullptr);
284
285 // Given a JSObject, returns that object stripped of wrappers. At each stage,
286 // the security wrapper has the opportunity to veto the unwrap. If
287 // stopAtWindowProxy is true, then this returns the WindowProxy if it was
288 // previously wrapped.
289 JS_FRIEND_API(JSObject*)
290 CheckedUnwrap(JSObject* obj, bool stopAtWindowProxy = true);
291
292 // Unwrap only the outermost security wrapper, with the same semantics as
293 // above. This is the checked version of Wrapper::wrappedObject.
294 JS_FRIEND_API(JSObject*)
295 UnwrapOneChecked(JSObject* obj, bool stopAtWindowProxy = true);
296
297 JS_FRIEND_API(bool)
298 IsCrossCompartmentWrapper(JSObject* obj);
299
300 void
301 NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper);
302
303 bool
304 RemapWrapper(JSContext* cx, JSObject* wobj, JSObject* newTarget);
305
306 JS_FRIEND_API(bool)
307 RemapAllWrappersForObject(JSContext* cx, JSObject* oldTarget,
308 JSObject* newTarget);
309
310 // API to recompute all cross-compartment wrappers whose source and target
311 // match the given filters.
312 JS_FRIEND_API(bool)
313 RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
314 const CompartmentFilter& targetFilter);
315
316 } /* namespace js */
317
318 #endif /* jswrapper_h */
319