|
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 jsproxy_h |
|
8 #define jsproxy_h |
|
9 |
|
10 #include "mozilla/Maybe.h" |
|
11 |
|
12 #include "jsfriendapi.h" |
|
13 |
|
14 #include "js/CallNonGenericMethod.h" |
|
15 #include "js/Class.h" |
|
16 |
|
17 namespace js { |
|
18 |
|
19 using JS::AutoIdVector; |
|
20 using JS::CallArgs; |
|
21 using JS::HandleId; |
|
22 using JS::HandleObject; |
|
23 using JS::HandleValue; |
|
24 using JS::IsAcceptableThis; |
|
25 using JS::MutableHandle; |
|
26 using JS::MutableHandleObject; |
|
27 using JS::MutableHandleValue; |
|
28 using JS::NativeImpl; |
|
29 using JS::PrivateValue; |
|
30 using JS::Value; |
|
31 |
|
32 class RegExpGuard; |
|
33 class JS_FRIEND_API(Wrapper); |
|
34 |
|
35 /* |
|
36 * A proxy is a JSObject that implements generic behavior by providing custom |
|
37 * implementations for each object trap. The implementation for each trap is |
|
38 * provided by a C++ object stored on the proxy, known as its handler. |
|
39 * |
|
40 * A major use case for proxies is to forward each trap to another object, |
|
41 * known as its target. The target can be an arbitrary C++ object. Not every |
|
42 * proxy has the notion of a target, however. |
|
43 * |
|
44 * Proxy traps are grouped into fundamental and derived traps. Every proxy has |
|
45 * to at least provide implementations for the fundamental traps, but the |
|
46 * derived traps can be implemented in terms of the fundamental ones |
|
47 * BaseProxyHandler provides implementations of the derived traps in terms of |
|
48 * the (pure virtual) fundamental traps. |
|
49 * |
|
50 * In addition to the normal traps, there are two models for proxy prototype |
|
51 * chains. First, proxies may opt to use the standard prototype mechanism used |
|
52 * throughout the engine. To do so, simply pass a prototype to NewProxyObject() |
|
53 * at creation time. All prototype accesses will then "just work" to treat the |
|
54 * proxy as a "normal" object. Alternatively, if instead the proxy wishes to |
|
55 * implement more complicated prototype semantics (if, for example, it wants to |
|
56 * delegate the prototype lookup to a wrapped object), it may pass Proxy::LazyProto |
|
57 * as the prototype at create time and opt in to the trapped prototype system, |
|
58 * which guarantees that their trap will be called on any and every prototype |
|
59 * chain access of the object. |
|
60 * |
|
61 * This system is implemented with two traps: {get,set}PrototypeOf. The default |
|
62 * implementation of setPrototypeOf throws a TypeError. Since it is not possible |
|
63 * to create an object without a sense of prototype chain, handler implementors |
|
64 * must provide a getPrototypeOf trap if opting in to the dynamic prototype system. |
|
65 * |
|
66 * To minimize code duplication, a set of abstract proxy handler classes is |
|
67 * provided, from which other handlers may inherit. These abstract classes |
|
68 * are organized in the following hierarchy: |
|
69 * |
|
70 * BaseProxyHandler |
|
71 * | |
|
72 * DirectProxyHandler |
|
73 * | |
|
74 * Wrapper |
|
75 */ |
|
76 |
|
77 /* |
|
78 * BaseProxyHandler is the most generic kind of proxy handler. It does not make |
|
79 * any assumptions about the target. Consequently, it does not provide any |
|
80 * default implementation for the fundamental traps. It does, however, implement |
|
81 * the derived traps in terms of the fundamental ones. This allows consumers of |
|
82 * this class to define any custom behavior they want. |
|
83 * |
|
84 * Important: If you add a trap here, you should probably also add a Proxy::foo |
|
85 * entry point with an AutoEnterPolicy. If you don't, you need an explicit |
|
86 * override for the trap in SecurityWrapper. See bug 945826 comment 0. |
|
87 */ |
|
88 class JS_FRIEND_API(BaseProxyHandler) |
|
89 { |
|
90 const void *mFamily; |
|
91 |
|
92 /* |
|
93 * Proxy handlers can use mHasPrototype to request the following special |
|
94 * treatment from the JS engine: |
|
95 * |
|
96 * - When mHasPrototype is true, the engine never calls these methods: |
|
97 * getPropertyDescriptor, has, set, enumerate, iterate. Instead, for |
|
98 * these operations, it calls the "own" traps like |
|
99 * getOwnPropertyDescriptor, hasOwn, defineProperty, keys, etc., and |
|
100 * consults the prototype chain if needed. |
|
101 * |
|
102 * - When mHasPrototype is true, the engine calls handler->get() only if |
|
103 * handler->hasOwn() says an own property exists on the proxy. If not, |
|
104 * it consults the prototype chain. |
|
105 * |
|
106 * This is useful because it frees the ProxyHandler from having to implement |
|
107 * any behavior having to do with the prototype chain. |
|
108 */ |
|
109 bool mHasPrototype; |
|
110 |
|
111 /* |
|
112 * All proxies indicate whether they have any sort of interesting security |
|
113 * policy that might prevent the caller from doing something it wants to |
|
114 * the object. In the case of wrappers, this distinction is used to |
|
115 * determine whether the caller may strip off the wrapper if it so desires. |
|
116 */ |
|
117 bool mHasSecurityPolicy; |
|
118 |
|
119 protected: |
|
120 // Subclasses may set this in their constructor. |
|
121 void setHasPrototype(bool aHasPrototype) { mHasPrototype = aHasPrototype; } |
|
122 void setHasSecurityPolicy(bool aHasPolicy) { mHasSecurityPolicy = aHasPolicy; } |
|
123 |
|
124 public: |
|
125 explicit BaseProxyHandler(const void *family); |
|
126 virtual ~BaseProxyHandler(); |
|
127 |
|
128 bool hasPrototype() { |
|
129 return mHasPrototype; |
|
130 } |
|
131 |
|
132 bool hasSecurityPolicy() { |
|
133 return mHasSecurityPolicy; |
|
134 } |
|
135 |
|
136 inline const void *family() { |
|
137 return mFamily; |
|
138 } |
|
139 static size_t offsetOfFamily() { |
|
140 return offsetof(BaseProxyHandler, mFamily); |
|
141 } |
|
142 |
|
143 virtual bool finalizeInBackground(Value priv) { |
|
144 /* |
|
145 * Called on creation of a proxy to determine whether its finalize |
|
146 * method can be finalized on the background thread. |
|
147 */ |
|
148 return true; |
|
149 } |
|
150 |
|
151 /* Policy enforcement traps. |
|
152 * |
|
153 * enter() allows the policy to specify whether the caller may perform |act| |
|
154 * on the proxy's |id| property. In the case when |act| is CALL, |id| is |
|
155 * generally JSID_VOID. |
|
156 * |
|
157 * The |act| parameter to enter() specifies the action being performed. |
|
158 * If |bp| is false, the trap suggests that the caller throw (though it |
|
159 * may still decide to squelch the error). |
|
160 * |
|
161 * We make these OR-able so that assertEnteredPolicy can pass a union of them. |
|
162 * For example, get{,Own}PropertyDescriptor is invoked by both calls to ::get() |
|
163 * and ::set() (since we need to look up the accessor), so its |
|
164 * assertEnteredPolicy would pass GET | SET. |
|
165 */ |
|
166 typedef uint32_t Action; |
|
167 enum { |
|
168 NONE = 0x00, |
|
169 GET = 0x01, |
|
170 SET = 0x02, |
|
171 CALL = 0x04, |
|
172 ENUMERATE = 0x08 |
|
173 }; |
|
174 |
|
175 virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act, |
|
176 bool *bp); |
|
177 |
|
178 /* ES5 Harmony fundamental proxy traps. */ |
|
179 virtual bool preventExtensions(JSContext *cx, HandleObject proxy) = 0; |
|
180 virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
|
181 MutableHandle<JSPropertyDescriptor> desc) = 0; |
|
182 virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, |
|
183 HandleId id, MutableHandle<JSPropertyDescriptor> desc) = 0; |
|
184 virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, |
|
185 MutableHandle<JSPropertyDescriptor> desc) = 0; |
|
186 virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, |
|
187 AutoIdVector &props) = 0; |
|
188 virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) = 0; |
|
189 virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) = 0; |
|
190 |
|
191 /* ES5 Harmony derived proxy traps. */ |
|
192 virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); |
|
193 virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); |
|
194 virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, |
|
195 HandleId id, MutableHandleValue vp); |
|
196 virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, |
|
197 HandleId id, bool strict, MutableHandleValue vp); |
|
198 virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props); |
|
199 virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, |
|
200 MutableHandleValue vp); |
|
201 |
|
202 /* Spidermonkey extensions. */ |
|
203 virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) = 0; |
|
204 virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args); |
|
205 virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args); |
|
206 virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args); |
|
207 virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp); |
|
208 virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx); |
|
209 virtual const char *className(JSContext *cx, HandleObject proxy); |
|
210 virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent); |
|
211 virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g); |
|
212 virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp); |
|
213 virtual void finalize(JSFreeOp *fop, JSObject *proxy); |
|
214 virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); |
|
215 virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp); |
|
216 |
|
217 // These two hooks must be overridden, or not overridden, in tandem -- no |
|
218 // overriding just one! |
|
219 virtual bool watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, |
|
220 JS::HandleObject callable); |
|
221 virtual bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id); |
|
222 |
|
223 virtual bool slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end, |
|
224 HandleObject result); |
|
225 |
|
226 /* See comment for weakmapKeyDelegateOp in js/Class.h. */ |
|
227 virtual JSObject *weakmapKeyDelegate(JSObject *proxy); |
|
228 virtual bool isScripted() { return false; } |
|
229 }; |
|
230 |
|
231 /* |
|
232 * DirectProxyHandler includes a notion of a target object. All traps are |
|
233 * reimplemented such that they forward their behavior to the target. This |
|
234 * allows consumers of this class to forward to another object as transparently |
|
235 * and efficiently as possible. |
|
236 * |
|
237 * Important: If you add a trap implementation here, you probably also need to |
|
238 * add an override in CrossCompartmentWrapper. If you don't, you risk |
|
239 * compartment mismatches. See bug 945826 comment 0. |
|
240 */ |
|
241 class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler |
|
242 { |
|
243 public: |
|
244 explicit DirectProxyHandler(const void *family); |
|
245 |
|
246 /* ES5 Harmony fundamental proxy traps. */ |
|
247 virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; |
|
248 virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
|
249 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
250 virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
|
251 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
252 virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, |
|
253 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
254 virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, |
|
255 AutoIdVector &props) MOZ_OVERRIDE; |
|
256 virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, |
|
257 bool *bp) MOZ_OVERRIDE; |
|
258 virtual bool enumerate(JSContext *cx, HandleObject proxy, |
|
259 AutoIdVector &props) MOZ_OVERRIDE; |
|
260 |
|
261 /* ES5 Harmony derived proxy traps. */ |
|
262 virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, |
|
263 bool *bp) MOZ_OVERRIDE; |
|
264 virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, |
|
265 bool *bp) MOZ_OVERRIDE; |
|
266 virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, |
|
267 HandleId id, MutableHandleValue vp) MOZ_OVERRIDE; |
|
268 virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, |
|
269 HandleId id, bool strict, MutableHandleValue vp) MOZ_OVERRIDE; |
|
270 virtual bool keys(JSContext *cx, HandleObject proxy, |
|
271 AutoIdVector &props) MOZ_OVERRIDE; |
|
272 virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, |
|
273 MutableHandleValue vp) MOZ_OVERRIDE; |
|
274 |
|
275 /* Spidermonkey extensions. */ |
|
276 virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE; |
|
277 virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; |
|
278 virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; |
|
279 virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, |
|
280 CallArgs args) MOZ_OVERRIDE; |
|
281 virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, |
|
282 bool *bp) MOZ_OVERRIDE; |
|
283 virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); |
|
284 virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp); |
|
285 virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, |
|
286 JSContext *cx) MOZ_OVERRIDE; |
|
287 virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; |
|
288 virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, |
|
289 unsigned indent) MOZ_OVERRIDE; |
|
290 virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, |
|
291 RegExpGuard *g) MOZ_OVERRIDE; |
|
292 virtual JSObject *weakmapKeyDelegate(JSObject *proxy); |
|
293 }; |
|
294 |
|
295 /* |
|
296 * Dispatch point for handlers that executes the appropriate C++ or scripted traps. |
|
297 * |
|
298 * Important: All proxy traps need either (a) an AutoEnterPolicy in their |
|
299 * Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug |
|
300 * 945826 comment 0. |
|
301 */ |
|
302 class Proxy |
|
303 { |
|
304 public: |
|
305 /* ES5 Harmony fundamental proxy traps. */ |
|
306 static bool preventExtensions(JSContext *cx, HandleObject proxy); |
|
307 static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
|
308 MutableHandle<JSPropertyDescriptor> desc); |
|
309 static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
|
310 MutableHandleValue vp); |
|
311 static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
|
312 MutableHandle<JSPropertyDescriptor> desc); |
|
313 static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
|
314 MutableHandleValue vp); |
|
315 static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, |
|
316 MutableHandle<JSPropertyDescriptor> desc); |
|
317 static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, HandleValue v); |
|
318 static bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props); |
|
319 static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); |
|
320 static bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props); |
|
321 |
|
322 /* ES5 Harmony derived proxy traps. */ |
|
323 static bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); |
|
324 static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp); |
|
325 static bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, |
|
326 MutableHandleValue vp); |
|
327 static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, |
|
328 bool strict, MutableHandleValue vp); |
|
329 static bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props); |
|
330 static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp); |
|
331 |
|
332 /* Spidermonkey extensions. */ |
|
333 static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible); |
|
334 static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args); |
|
335 static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args); |
|
336 static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args); |
|
337 static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp); |
|
338 static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx); |
|
339 static const char *className(JSContext *cx, HandleObject proxy); |
|
340 static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent); |
|
341 static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g); |
|
342 static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp); |
|
343 static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop); |
|
344 static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp); |
|
345 |
|
346 static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable); |
|
347 static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id); |
|
348 |
|
349 static bool slice(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end, |
|
350 HandleObject result); |
|
351 |
|
352 /* IC entry path for handling __noSuchMethod__ on access. */ |
|
353 static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id, |
|
354 MutableHandleValue vp); |
|
355 }; |
|
356 |
|
357 // Use these in places where you don't want to #include vm/ProxyObject.h. |
|
358 extern JS_FRIEND_DATA(const js::Class* const) CallableProxyClassPtr; |
|
359 extern JS_FRIEND_DATA(const js::Class* const) UncallableProxyClassPtr; |
|
360 |
|
361 inline bool IsProxy(JSObject *obj) |
|
362 { |
|
363 return GetObjectClass(obj)->isProxy(); |
|
364 } |
|
365 |
|
366 /* |
|
367 * These are part of the API. |
|
368 * |
|
369 * NOTE: PROXY_PRIVATE_SLOT is 0 because that way slot 0 is usable by API |
|
370 * clients for both proxy and non-proxy objects. So an API client that only |
|
371 * needs to store one slot's worth of data doesn't need to branch on what sort |
|
372 * of object it has. |
|
373 */ |
|
374 const uint32_t PROXY_PRIVATE_SLOT = 0; |
|
375 const uint32_t PROXY_HANDLER_SLOT = 1; |
|
376 const uint32_t PROXY_EXTRA_SLOT = 2; |
|
377 const uint32_t PROXY_MINIMUM_SLOTS = 4; |
|
378 |
|
379 inline BaseProxyHandler * |
|
380 GetProxyHandler(JSObject *obj) |
|
381 { |
|
382 JS_ASSERT(IsProxy(obj)); |
|
383 return (BaseProxyHandler *) GetReservedSlot(obj, PROXY_HANDLER_SLOT).toPrivate(); |
|
384 } |
|
385 |
|
386 inline const Value & |
|
387 GetProxyPrivate(JSObject *obj) |
|
388 { |
|
389 JS_ASSERT(IsProxy(obj)); |
|
390 return GetReservedSlot(obj, PROXY_PRIVATE_SLOT); |
|
391 } |
|
392 |
|
393 inline JSObject * |
|
394 GetProxyTargetObject(JSObject *obj) |
|
395 { |
|
396 JS_ASSERT(IsProxy(obj)); |
|
397 return GetProxyPrivate(obj).toObjectOrNull(); |
|
398 } |
|
399 |
|
400 inline const Value & |
|
401 GetProxyExtra(JSObject *obj, size_t n) |
|
402 { |
|
403 JS_ASSERT(IsProxy(obj)); |
|
404 return GetReservedSlot(obj, PROXY_EXTRA_SLOT + n); |
|
405 } |
|
406 |
|
407 inline void |
|
408 SetProxyHandler(JSObject *obj, BaseProxyHandler *handler) |
|
409 { |
|
410 JS_ASSERT(IsProxy(obj)); |
|
411 SetReservedSlot(obj, PROXY_HANDLER_SLOT, PrivateValue(handler)); |
|
412 } |
|
413 |
|
414 inline void |
|
415 SetProxyExtra(JSObject *obj, size_t n, const Value &extra) |
|
416 { |
|
417 JS_ASSERT(IsProxy(obj)); |
|
418 JS_ASSERT(n <= 1); |
|
419 SetReservedSlot(obj, PROXY_EXTRA_SLOT + n, extra); |
|
420 } |
|
421 |
|
422 inline bool |
|
423 IsScriptedProxy(JSObject *obj) |
|
424 { |
|
425 return IsProxy(obj) && GetProxyHandler(obj)->isScripted(); |
|
426 } |
|
427 |
|
428 class MOZ_STACK_CLASS ProxyOptions { |
|
429 protected: |
|
430 /* protected constructor for subclass */ |
|
431 ProxyOptions(bool singletonArg, const Class *claspArg) |
|
432 : singleton_(singletonArg), |
|
433 clasp_(claspArg) |
|
434 {} |
|
435 |
|
436 public: |
|
437 ProxyOptions() : singleton_(false), |
|
438 clasp_(UncallableProxyClassPtr) |
|
439 {} |
|
440 |
|
441 bool singleton() const { return singleton_; } |
|
442 ProxyOptions &setSingleton(bool flag) { |
|
443 singleton_ = flag; |
|
444 return *this; |
|
445 } |
|
446 |
|
447 const Class *clasp() const { |
|
448 return clasp_; |
|
449 } |
|
450 ProxyOptions &setClass(const Class *claspArg) { |
|
451 clasp_ = claspArg; |
|
452 return *this; |
|
453 } |
|
454 ProxyOptions &selectDefaultClass(bool callable) { |
|
455 const Class *classp = callable? CallableProxyClassPtr : |
|
456 UncallableProxyClassPtr; |
|
457 return setClass(classp); |
|
458 } |
|
459 |
|
460 private: |
|
461 bool singleton_; |
|
462 const Class *clasp_; |
|
463 }; |
|
464 |
|
465 JS_FRIEND_API(JSObject *) |
|
466 NewProxyObject(JSContext *cx, BaseProxyHandler *handler, HandleValue priv, |
|
467 JSObject *proto, JSObject *parent, const ProxyOptions &options = ProxyOptions()); |
|
468 |
|
469 JSObject * |
|
470 RenewProxyObject(JSContext *cx, JSObject *obj, BaseProxyHandler *handler, Value priv); |
|
471 |
|
472 class JS_FRIEND_API(AutoEnterPolicy) |
|
473 { |
|
474 public: |
|
475 typedef BaseProxyHandler::Action Action; |
|
476 AutoEnterPolicy(JSContext *cx, BaseProxyHandler *handler, |
|
477 HandleObject wrapper, HandleId id, Action act, bool mayThrow) |
|
478 #ifdef JS_DEBUG |
|
479 : context(nullptr) |
|
480 #endif |
|
481 { |
|
482 allow = handler->hasSecurityPolicy() ? handler->enter(cx, wrapper, id, act, &rv) |
|
483 : true; |
|
484 recordEnter(cx, wrapper, id, act); |
|
485 // We want to throw an exception if all of the following are true: |
|
486 // * The policy disallowed access. |
|
487 // * The policy set rv to false, indicating that we should throw. |
|
488 // * The caller did not instruct us to ignore exceptions. |
|
489 // * The policy did not throw itself. |
|
490 if (!allow && !rv && mayThrow) |
|
491 reportErrorIfExceptionIsNotPending(cx, id); |
|
492 } |
|
493 |
|
494 virtual ~AutoEnterPolicy() { recordLeave(); } |
|
495 inline bool allowed() { return allow; } |
|
496 inline bool returnValue() { JS_ASSERT(!allowed()); return rv; } |
|
497 |
|
498 protected: |
|
499 // no-op constructor for subclass |
|
500 AutoEnterPolicy() |
|
501 #ifdef JS_DEBUG |
|
502 : context(nullptr) |
|
503 , enteredAction(BaseProxyHandler::NONE) |
|
504 #endif |
|
505 {}; |
|
506 void reportErrorIfExceptionIsNotPending(JSContext *cx, jsid id); |
|
507 bool allow; |
|
508 bool rv; |
|
509 |
|
510 #ifdef JS_DEBUG |
|
511 JSContext *context; |
|
512 mozilla::Maybe<HandleObject> enteredProxy; |
|
513 mozilla::Maybe<HandleId> enteredId; |
|
514 Action enteredAction; |
|
515 |
|
516 // NB: We explicitly don't track the entered action here, because sometimes |
|
517 // SET traps do an implicit GET during their implementation, leading to |
|
518 // spurious assertions. |
|
519 AutoEnterPolicy *prev; |
|
520 void recordEnter(JSContext *cx, HandleObject proxy, HandleId id, Action act); |
|
521 void recordLeave(); |
|
522 |
|
523 friend JS_FRIEND_API(void) assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id, Action act); |
|
524 #else |
|
525 inline void recordEnter(JSContext *cx, JSObject *proxy, jsid id, Action act) {} |
|
526 inline void recordLeave() {} |
|
527 #endif |
|
528 |
|
529 }; |
|
530 |
|
531 #ifdef JS_DEBUG |
|
532 class JS_FRIEND_API(AutoWaivePolicy) : public AutoEnterPolicy { |
|
533 public: |
|
534 AutoWaivePolicy(JSContext *cx, HandleObject proxy, HandleId id, |
|
535 BaseProxyHandler::Action act) |
|
536 { |
|
537 allow = true; |
|
538 recordEnter(cx, proxy, id, act); |
|
539 } |
|
540 }; |
|
541 #else |
|
542 class JS_FRIEND_API(AutoWaivePolicy) { |
|
543 public: |
|
544 AutoWaivePolicy(JSContext *cx, HandleObject proxy, HandleId id, |
|
545 BaseProxyHandler::Action act) |
|
546 {} |
|
547 }; |
|
548 #endif |
|
549 |
|
550 #ifdef JS_DEBUG |
|
551 extern JS_FRIEND_API(void) |
|
552 assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id, |
|
553 BaseProxyHandler::Action act); |
|
554 #else |
|
555 inline void assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id, |
|
556 BaseProxyHandler::Action act) |
|
557 {}; |
|
558 #endif |
|
559 |
|
560 } /* namespace js */ |
|
561 |
|
562 extern JS_FRIEND_API(JSObject *) |
|
563 js_InitProxyClass(JSContext *cx, JS::HandleObject obj); |
|
564 |
|
565 #endif /* jsproxy_h */ |