|
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 "jsproxy.h" |
|
13 |
|
14 namespace js { |
|
15 |
|
16 class DummyFrameGuard; |
|
17 |
|
18 /* |
|
19 * Helper for Wrapper::New default options. |
|
20 * |
|
21 * Callers of Wrapper::New() who wish to specify a prototype for the created |
|
22 * Wrapper, *MUST* construct a WrapperOptions with a JSContext. |
|
23 */ |
|
24 class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions { |
|
25 public: |
|
26 WrapperOptions() : ProxyOptions(false, nullptr), |
|
27 proto_() |
|
28 {} |
|
29 |
|
30 WrapperOptions(JSContext *cx) : ProxyOptions(false, nullptr), |
|
31 proto_() |
|
32 { |
|
33 proto_.construct(cx); |
|
34 } |
|
35 |
|
36 inline JSObject *proto() const; |
|
37 WrapperOptions &setProto(JSObject *protoArg) { |
|
38 JS_ASSERT(!proto_.empty()); |
|
39 proto_.ref() = protoArg; |
|
40 return *this; |
|
41 } |
|
42 |
|
43 private: |
|
44 mozilla::Maybe<JS::RootedObject> proto_; |
|
45 }; |
|
46 |
|
47 /* |
|
48 * A wrapper is a proxy with a target object to which it generally forwards |
|
49 * operations, but may restrict access to certain operations or instrument |
|
50 * the trap operations in various ways. A wrapper is distinct from a Direct Proxy |
|
51 * Handler in the sense that it can be "unwrapped" in C++, exposing the underlying |
|
52 * object (Direct Proxy Handlers have an underlying target object, but don't |
|
53 * expect to expose this object via any kind of unwrapping operation). Callers |
|
54 * should be careful to avoid unwrapping security wrappers in the wrong context. |
|
55 */ |
|
56 class JS_FRIEND_API(Wrapper) : public DirectProxyHandler |
|
57 { |
|
58 unsigned mFlags; |
|
59 |
|
60 public: |
|
61 using BaseProxyHandler::Action; |
|
62 |
|
63 enum Flags { |
|
64 CROSS_COMPARTMENT = 1 << 0, |
|
65 LAST_USED_FLAG = CROSS_COMPARTMENT |
|
66 }; |
|
67 |
|
68 virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, |
|
69 MutableHandleValue vp) MOZ_OVERRIDE; |
|
70 |
|
71 static JSObject *New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler, |
|
72 const WrapperOptions *options = nullptr); |
|
73 |
|
74 static JSObject *Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler); |
|
75 |
|
76 static Wrapper *wrapperHandler(JSObject *wrapper); |
|
77 |
|
78 static JSObject *wrappedObject(JSObject *wrapper); |
|
79 |
|
80 unsigned flags() const { |
|
81 return mFlags; |
|
82 } |
|
83 |
|
84 explicit Wrapper(unsigned flags, bool hasPrototype = false); |
|
85 |
|
86 virtual ~Wrapper(); |
|
87 |
|
88 virtual bool finalizeInBackground(Value priv) MOZ_OVERRIDE; |
|
89 |
|
90 static Wrapper singleton; |
|
91 static Wrapper singletonWithPrototype; |
|
92 |
|
93 static JSObject *defaultProto; |
|
94 }; |
|
95 |
|
96 inline JSObject * |
|
97 WrapperOptions::proto() const |
|
98 { |
|
99 return proto_.empty() ? Wrapper::defaultProto : proto_.ref(); |
|
100 } |
|
101 |
|
102 /* Base class for all cross compartment wrapper handlers. */ |
|
103 class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper |
|
104 { |
|
105 public: |
|
106 CrossCompartmentWrapper(unsigned flags, bool hasPrototype = false); |
|
107 |
|
108 virtual ~CrossCompartmentWrapper(); |
|
109 |
|
110 /* ES5 Harmony fundamental wrapper traps. */ |
|
111 virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) MOZ_OVERRIDE; |
|
112 virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, |
|
113 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
114 virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, |
|
115 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
116 virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, |
|
117 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
118 virtual bool getOwnPropertyNames(JSContext *cx, HandleObject wrapper, |
|
119 AutoIdVector &props) MOZ_OVERRIDE; |
|
120 virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) MOZ_OVERRIDE; |
|
121 virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) MOZ_OVERRIDE; |
|
122 |
|
123 /* ES5 Harmony derived wrapper traps. */ |
|
124 virtual bool has(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) MOZ_OVERRIDE; |
|
125 virtual bool hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) MOZ_OVERRIDE; |
|
126 virtual bool get(JSContext *cx, HandleObject wrapper, HandleObject receiver, |
|
127 HandleId id, MutableHandleValue vp) MOZ_OVERRIDE; |
|
128 virtual bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, |
|
129 HandleId id, bool strict, MutableHandleValue vp) MOZ_OVERRIDE; |
|
130 virtual bool keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) MOZ_OVERRIDE; |
|
131 virtual bool iterate(JSContext *cx, HandleObject wrapper, unsigned flags, |
|
132 MutableHandleValue vp) MOZ_OVERRIDE; |
|
133 |
|
134 /* Spidermonkey extensions. */ |
|
135 virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) MOZ_OVERRIDE; |
|
136 virtual bool call(JSContext *cx, HandleObject wrapper, const CallArgs &args) MOZ_OVERRIDE; |
|
137 virtual bool construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) MOZ_OVERRIDE; |
|
138 virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, |
|
139 CallArgs args) MOZ_OVERRIDE; |
|
140 virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, |
|
141 bool *bp) MOZ_OVERRIDE; |
|
142 virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; |
|
143 virtual JSString *fun_toString(JSContext *cx, HandleObject wrapper, |
|
144 unsigned indent) MOZ_OVERRIDE; |
|
145 virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE; |
|
146 virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, |
|
147 MutableHandleValue vp) MOZ_OVERRIDE; |
|
148 virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, |
|
149 MutableHandleObject protop) MOZ_OVERRIDE; |
|
150 virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, |
|
151 bool *bp) MOZ_OVERRIDE; |
|
152 |
|
153 static CrossCompartmentWrapper singleton; |
|
154 static CrossCompartmentWrapper singletonWithPrototype; |
|
155 }; |
|
156 |
|
157 /* |
|
158 * Base class for security wrappers. A security wrapper is potentially hiding |
|
159 * all or part of some wrapped object thus SecurityWrapper defaults to denying |
|
160 * access to the wrappee. This is the opposite of Wrapper which tries to be |
|
161 * completely transparent. |
|
162 * |
|
163 * NB: Currently, only a few ProxyHandler operations are overridden to deny |
|
164 * access, relying on derived SecurityWrapper to block access when necessary. |
|
165 */ |
|
166 template <class Base> |
|
167 class JS_FRIEND_API(SecurityWrapper) : public Base |
|
168 { |
|
169 public: |
|
170 SecurityWrapper(unsigned flags); |
|
171 |
|
172 virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) MOZ_OVERRIDE; |
|
173 virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) MOZ_OVERRIDE; |
|
174 virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Wrapper::Action act, |
|
175 bool *bp) MOZ_OVERRIDE; |
|
176 virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, |
|
177 CallArgs args) MOZ_OVERRIDE; |
|
178 virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, |
|
179 MutableHandleValue vp) MOZ_OVERRIDE; |
|
180 virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, |
|
181 JSContext *cx) MOZ_OVERRIDE; |
|
182 virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE; |
|
183 virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, |
|
184 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
185 |
|
186 virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, |
|
187 bool *bp) MOZ_OVERRIDE; |
|
188 |
|
189 virtual bool watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, |
|
190 JS::HandleObject callable) MOZ_OVERRIDE; |
|
191 virtual bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id) MOZ_OVERRIDE; |
|
192 |
|
193 /* |
|
194 * Allow our subclasses to select the superclass behavior they want without |
|
195 * needing to specify an exact superclass. |
|
196 */ |
|
197 typedef Base Permissive; |
|
198 typedef SecurityWrapper<Base> Restrictive; |
|
199 }; |
|
200 |
|
201 typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper; |
|
202 typedef SecurityWrapper<CrossCompartmentWrapper> CrossCompartmentSecurityWrapper; |
|
203 |
|
204 class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler |
|
205 { |
|
206 public: |
|
207 // This variable exists solely to provide a unique address for use as an identifier. |
|
208 static const char sDeadObjectFamily; |
|
209 |
|
210 explicit DeadObjectProxy(); |
|
211 |
|
212 /* ES5 Harmony fundamental wrapper traps. */ |
|
213 virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; |
|
214 virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, |
|
215 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
216 virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, |
|
217 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
218 virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, |
|
219 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
220 virtual bool getOwnPropertyNames(JSContext *cx, HandleObject wrapper, |
|
221 AutoIdVector &props) MOZ_OVERRIDE; |
|
222 virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) MOZ_OVERRIDE; |
|
223 virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) MOZ_OVERRIDE; |
|
224 |
|
225 /* Spidermonkey extensions. */ |
|
226 virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE; |
|
227 virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; |
|
228 virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; |
|
229 virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, |
|
230 CallArgs args) MOZ_OVERRIDE; |
|
231 virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, |
|
232 bool *bp) MOZ_OVERRIDE; |
|
233 virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, |
|
234 JSContext *cx) MOZ_OVERRIDE; |
|
235 virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; |
|
236 virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) MOZ_OVERRIDE; |
|
237 virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE; |
|
238 virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, |
|
239 MutableHandleValue vp) MOZ_OVERRIDE; |
|
240 virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, |
|
241 MutableHandleObject protop) MOZ_OVERRIDE; |
|
242 |
|
243 static DeadObjectProxy singleton; |
|
244 }; |
|
245 |
|
246 extern JSObject * |
|
247 TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj, |
|
248 HandleObject wrappedProto, HandleObject parent, |
|
249 unsigned flags); |
|
250 |
|
251 // Proxy family for wrappers. Public so that IsWrapper() can be fully inlined by |
|
252 // jsfriendapi users. |
|
253 // This variable exists solely to provide a unique address for use as an identifier. |
|
254 extern JS_FRIEND_DATA(const char) sWrapperFamily; |
|
255 |
|
256 inline bool |
|
257 IsWrapper(JSObject *obj) |
|
258 { |
|
259 return IsProxy(obj) && GetProxyHandler(obj)->family() == &sWrapperFamily; |
|
260 } |
|
261 |
|
262 // Given a JSObject, returns that object stripped of wrappers. If |
|
263 // stopAtOuter is true, then this returns the outer window if it was |
|
264 // previously wrapped. Otherwise, this returns the first object for |
|
265 // which JSObject::isWrapper returns false. |
|
266 JS_FRIEND_API(JSObject *) |
|
267 UncheckedUnwrap(JSObject *obj, bool stopAtOuter = true, unsigned *flagsp = nullptr); |
|
268 |
|
269 // Given a JSObject, returns that object stripped of wrappers. At each stage, |
|
270 // the security wrapper has the opportunity to veto the unwrap. Since checked |
|
271 // code should never be unwrapping outer window wrappers, we always stop at |
|
272 // outer windows. |
|
273 JS_FRIEND_API(JSObject *) |
|
274 CheckedUnwrap(JSObject *obj, bool stopAtOuter = true); |
|
275 |
|
276 // Unwrap only the outermost security wrapper, with the same semantics as |
|
277 // above. This is the checked version of Wrapper::wrappedObject. |
|
278 JS_FRIEND_API(JSObject *) |
|
279 UnwrapOneChecked(JSObject *obj, bool stopAtOuter = true); |
|
280 |
|
281 JS_FRIEND_API(bool) |
|
282 IsCrossCompartmentWrapper(JSObject *obj); |
|
283 |
|
284 bool |
|
285 IsDeadProxyObject(JSObject *obj); |
|
286 |
|
287 JSObject * |
|
288 NewDeadProxyObject(JSContext *cx, JSObject *parent, |
|
289 const ProxyOptions &options = ProxyOptions()); |
|
290 |
|
291 void |
|
292 NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper); |
|
293 |
|
294 bool |
|
295 RemapWrapper(JSContext *cx, JSObject *wobj, JSObject *newTarget); |
|
296 |
|
297 JS_FRIEND_API(bool) |
|
298 RemapAllWrappersForObject(JSContext *cx, JSObject *oldTarget, |
|
299 JSObject *newTarget); |
|
300 |
|
301 // API to recompute all cross-compartment wrappers whose source and target |
|
302 // match the given filters. |
|
303 JS_FRIEND_API(bool) |
|
304 RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, |
|
305 const CompartmentFilter &targetFilter); |
|
306 |
|
307 /* |
|
308 * This auto class should be used around any code, such as brain transplants, |
|
309 * that may touch dead zones. Brain transplants can cause problems |
|
310 * because they operate on all compartments, whether live or dead. A brain |
|
311 * transplant can cause a formerly dead object to be "reanimated" by causing a |
|
312 * read or write barrier to be invoked on it during the transplant. In this way, |
|
313 * a zone becomes a zombie, kept alive by repeatedly consuming |
|
314 * (transplanted) brains. |
|
315 * |
|
316 * To work around this issue, we observe when mark bits are set on objects in |
|
317 * dead zones. If this happens during a brain transplant, we do a full, |
|
318 * non-incremental GC at the end of the brain transplant. This will clean up any |
|
319 * objects that were improperly marked. |
|
320 */ |
|
321 struct JS_FRIEND_API(AutoMaybeTouchDeadZones) |
|
322 { |
|
323 // The version that takes an object just uses it for its runtime. |
|
324 AutoMaybeTouchDeadZones(JSContext *cx); |
|
325 AutoMaybeTouchDeadZones(JSObject *obj); |
|
326 ~AutoMaybeTouchDeadZones(); |
|
327 |
|
328 private: |
|
329 JSRuntime *runtime; |
|
330 unsigned markCount; |
|
331 bool inIncremental; |
|
332 bool manipulatingDeadZones; |
|
333 }; |
|
334 |
|
335 } /* namespace js */ |
|
336 |
|
337 #endif /* jswrapper_h */ |