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 XrayWrapper_h
8 : #define XrayWrapper_h
9 :
10 : #include "mozilla/Attributes.h"
11 :
12 : #include "WrapperFactory.h"
13 :
14 : #include "js/Proxy.h"
15 : #include "js/Wrapper.h"
16 :
17 : // Slot where Xray functions for Web IDL methods store a pointer to
18 : // the Xray wrapper they're associated with.
19 : #define XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT 0
20 : // Slot where in debug builds Xray functions for Web IDL methods store
21 : // a pointer to their themselves, just so we can assert that they're the
22 : // sort of functions we expect.
23 : #define XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF 1
24 :
25 : // Xray wrappers re-resolve the original native properties on the native
26 : // object and always directly access to those properties.
27 : // Because they work so differently from the rest of the wrapper hierarchy,
28 : // we pull them out of the Wrapper inheritance hierarchy and create a
29 : // little world around them.
30 :
31 : class nsIPrincipal;
32 :
33 : namespace xpc {
34 :
35 : namespace XrayUtils {
36 :
37 : bool
38 : IsTransparent(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id);
39 :
40 : bool
41 : HasNativeProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
42 : bool* hasProp);
43 : } // namespace XrayUtils
44 :
45 : enum XrayType {
46 : XrayForDOMObject,
47 : XrayForJSObject,
48 : XrayForOpaqueObject,
49 : NotXray
50 : };
51 :
52 : class XrayTraits
53 : {
54 : public:
55 : constexpr XrayTraits() {}
56 :
57 1113 : static JSObject* getTargetObject(JSObject* wrapper) {
58 1113 : JSObject* target = js::UncheckedUnwrap(wrapper, /* stopAtWindowProxy = */ false);
59 1113 : if (target)
60 1113 : JS::ExposeObjectToActiveJS(target);
61 1113 : return target;
62 : }
63 :
64 : // NB: resolveOwnProperty may decide whether or not to cache what it finds
65 : // on the holder. If the result is not cached, the lookup will happen afresh
66 : // for each access, which is the right thing for things like dynamic NodeList
67 : // properties.
68 : virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper,
69 : JS::HandleObject target, JS::HandleObject holder,
70 : JS::HandleId id, JS::MutableHandle<JS::PropertyDescriptor> desc);
71 :
72 : bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
73 : JS::ObjectOpResult& result) {
74 0 : return result.succeed();
75 : }
76 :
77 0 : static bool getBuiltinClass(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance,
78 : js::ESClass* cls) {
79 0 : return baseInstance.getBuiltinClass(cx, wrapper, cls);
80 : }
81 :
82 0 : static const char* className(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance) {
83 0 : return baseInstance.className(cx, wrapper);
84 : }
85 :
86 : virtual void preserveWrapper(JSObject* target) = 0;
87 :
88 : bool getExpandoObject(JSContext* cx, JS::HandleObject target,
89 : JS::HandleObject consumer, JS::MutableHandleObject expandObject);
90 : JSObject* ensureExpandoObject(JSContext* cx, JS::HandleObject wrapper,
91 : JS::HandleObject target);
92 :
93 : // Slots for holder objects.
94 : enum {
95 : HOLDER_SLOT_CACHED_PROTO = 0,
96 : HOLDER_SLOT_EXPANDO = 1,
97 : HOLDER_SHARED_SLOT_COUNT
98 : };
99 :
100 : static JSObject* getHolder(JSObject* wrapper);
101 : JSObject* ensureHolder(JSContext* cx, JS::HandleObject wrapper);
102 : virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) = 0;
103 :
104 : JSObject* getExpandoChain(JS::HandleObject obj);
105 : JSObject* detachExpandoChain(JS::HandleObject obj);
106 : bool setExpandoChain(JSContext* cx, JS::HandleObject obj, JS::HandleObject chain);
107 : bool cloneExpandoChain(JSContext* cx, JS::HandleObject dst, JS::HandleObject srcChain);
108 :
109 : protected:
110 : static const JSClass HolderClass;
111 :
112 : // Get the JSClass we should use for our expando object.
113 : virtual const JSClass* getExpandoClass(JSContext* cx,
114 : JS::HandleObject target) const;
115 :
116 : private:
117 : bool expandoObjectMatchesConsumer(JSContext* cx, JS::HandleObject expandoObject,
118 : nsIPrincipal* consumerOrigin);
119 :
120 : // |expandoChain| is the expando chain in the wrapped object's compartment.
121 : // |exclusiveWrapper| is any xray that has exclusive use of the expando.
122 : // |cx| may be in any compartment.
123 : bool getExpandoObjectInternal(JSContext* cx, JSObject* expandoChain,
124 : JS::HandleObject exclusiveWrapper,
125 : nsIPrincipal* origin,
126 : JS::MutableHandleObject expandoObject);
127 :
128 : // |cx| is in the target's compartment, and |exclusiveWrapper| is any xray
129 : // that has exclusive use of the expando.
130 : JSObject* attachExpandoObject(JSContext* cx, JS::HandleObject target,
131 : JS::HandleObject exclusiveWrapper,
132 : nsIPrincipal* origin);
133 :
134 : XrayTraits(XrayTraits&) = delete;
135 : const XrayTraits& operator=(XrayTraits&) = delete;
136 : };
137 :
138 : class DOMXrayTraits : public XrayTraits
139 : {
140 : public:
141 : constexpr DOMXrayTraits() = default;
142 :
143 : static const XrayType Type = XrayForDOMObject;
144 :
145 : virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
146 : JS::HandleObject holder, JS::HandleId id,
147 : JS::MutableHandle<JS::PropertyDescriptor> desc) override;
148 :
149 : bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::ObjectOpResult& result);
150 :
151 : bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
152 : JS::Handle<JS::PropertyDescriptor> desc,
153 : JS::Handle<JS::PropertyDescriptor> existingDesc,
154 : JS::ObjectOpResult& result, bool* defined);
155 : virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper, unsigned flags,
156 : JS::AutoIdVector& props);
157 : static bool call(JSContext* cx, JS::HandleObject wrapper,
158 : const JS::CallArgs& args, const js::Wrapper& baseInstance);
159 : static bool construct(JSContext* cx, JS::HandleObject wrapper,
160 : const JS::CallArgs& args, const js::Wrapper& baseInstance);
161 :
162 : static bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
163 : JS::HandleObject target,
164 : JS::MutableHandleObject protop);
165 :
166 : virtual void preserveWrapper(JSObject* target) override;
167 :
168 : virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override;
169 :
170 : static DOMXrayTraits singleton;
171 :
172 : protected:
173 : virtual const JSClass* getExpandoClass(JSContext* cx,
174 : JS::HandleObject target) const override;
175 : };
176 :
177 : class JSXrayTraits : public XrayTraits
178 : {
179 : public:
180 : static const XrayType Type = XrayForJSObject;
181 :
182 : virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
183 : JS::HandleObject holder, JS::HandleId id,
184 : JS::MutableHandle<JS::PropertyDescriptor> desc) override;
185 :
186 : bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::ObjectOpResult& result);
187 :
188 : bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
189 : JS::Handle<JS::PropertyDescriptor> desc,
190 : JS::Handle<JS::PropertyDescriptor> existingDesc,
191 : JS::ObjectOpResult& result, bool* defined);
192 :
193 : virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper, unsigned flags,
194 : JS::AutoIdVector& props);
195 :
196 0 : static bool call(JSContext* cx, JS::HandleObject wrapper,
197 : const JS::CallArgs& args, const js::Wrapper& baseInstance)
198 : {
199 0 : JSXrayTraits& self = JSXrayTraits::singleton;
200 0 : JS::RootedObject holder(cx, self.ensureHolder(cx, wrapper));
201 0 : if (xpc::JSXrayTraits::getProtoKey(holder) == JSProto_Function)
202 0 : return baseInstance.call(cx, wrapper, args);
203 :
204 0 : JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
205 0 : js::ReportIsNotFunction(cx, v);
206 : return false;
207 : }
208 :
209 : static bool construct(JSContext* cx, JS::HandleObject wrapper,
210 : const JS::CallArgs& args, const js::Wrapper& baseInstance);
211 :
212 18 : bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
213 : JS::HandleObject target,
214 : JS::MutableHandleObject protop)
215 : {
216 36 : JS::RootedObject holder(cx, ensureHolder(cx, wrapper));
217 18 : JSProtoKey key = getProtoKey(holder);
218 18 : if (isPrototype(holder)) {
219 5 : JSProtoKey protoKey = js::InheritanceProtoKeyForStandardClass(key);
220 5 : if (protoKey == JSProto_Null) {
221 3 : protop.set(nullptr);
222 3 : return true;
223 : }
224 : key = protoKey;
225 : }
226 :
227 : {
228 45 : JSAutoRealm ar(cx, target);
229 15 : if (!JS_GetClassPrototype(cx, key, protop))
230 0 : return false;
231 : }
232 15 : return JS_WrapObject(cx, protop);
233 : }
234 :
235 0 : virtual void preserveWrapper(JSObject* target) override {
236 : // In the case of pure JS objects, there is no underlying object, and
237 : // the target is the canonical representation of state. If it gets
238 : // collected, then expandos and such should be collected too. So there's
239 : // nothing to do here.
240 0 : }
241 :
242 : enum {
243 : SLOT_PROTOKEY = HOLDER_SHARED_SLOT_COUNT,
244 : SLOT_ISPROTOTYPE,
245 : SLOT_CONSTRUCTOR_FOR,
246 : SLOT_COUNT
247 : };
248 : virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override;
249 :
250 180 : static JSProtoKey getProtoKey(JSObject* holder) {
251 180 : int32_t key = js::GetReservedSlot(holder, SLOT_PROTOKEY).toInt32();
252 180 : return static_cast<JSProtoKey>(key);
253 : }
254 :
255 1 : static bool isPrototype(JSObject* holder) {
256 172 : return js::GetReservedSlot(holder, SLOT_ISPROTOTYPE).toBoolean();
257 : }
258 :
259 1 : static JSProtoKey constructorFor(JSObject* holder) {
260 12 : int32_t key = js::GetReservedSlot(holder, SLOT_CONSTRUCTOR_FOR).toInt32();
261 12 : return static_cast<JSProtoKey>(key);
262 : }
263 :
264 : // Operates in the wrapper compartment.
265 : static bool getOwnPropertyFromWrapperIfSafe(JSContext* cx,
266 : JS::HandleObject wrapper,
267 : JS::HandleId id,
268 : JS::MutableHandle<JS::PropertyDescriptor> desc);
269 :
270 : // Like the above, but operates in the target compartment.
271 : static bool getOwnPropertyFromTargetIfSafe(JSContext* cx,
272 : JS::HandleObject target,
273 : JS::HandleObject wrapper,
274 : JS::HandleId id,
275 : JS::MutableHandle<JS::PropertyDescriptor> desc);
276 :
277 : static const JSClass HolderClass;
278 : static JSXrayTraits singleton;
279 : };
280 :
281 : // These traits are used when the target is not Xrayable and we therefore want
282 : // to make it opaque modulo the usual Xray machinery (like expandos and
283 : // .wrappedJSObject).
284 : class OpaqueXrayTraits : public XrayTraits
285 : {
286 : public:
287 : static const XrayType Type = XrayForOpaqueObject;
288 :
289 : virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
290 : JS::HandleObject holder, JS::HandleId id,
291 : JS::MutableHandle<JS::PropertyDescriptor> desc) override;
292 :
293 : bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
294 : JS::Handle<JS::PropertyDescriptor> desc,
295 : JS::Handle<JS::PropertyDescriptor> existingDesc,
296 : JS::ObjectOpResult& result, bool* defined)
297 : {
298 0 : *defined = false;
299 : return true;
300 : }
301 :
302 0 : virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper, unsigned flags,
303 : JS::AutoIdVector& props)
304 : {
305 0 : return true;
306 : }
307 :
308 0 : static bool call(JSContext* cx, JS::HandleObject wrapper,
309 : const JS::CallArgs& args, const js::Wrapper& baseInstance)
310 : {
311 0 : JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
312 0 : js::ReportIsNotFunction(cx, v);
313 0 : return false;
314 : }
315 :
316 0 : static bool construct(JSContext* cx, JS::HandleObject wrapper,
317 : const JS::CallArgs& args, const js::Wrapper& baseInstance)
318 : {
319 0 : JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
320 0 : js::ReportIsNotFunction(cx, v);
321 0 : return false;
322 : }
323 :
324 0 : bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
325 : JS::HandleObject target,
326 : JS::MutableHandleObject protop)
327 : {
328 : // Opaque wrappers just get targetGlobal.Object.prototype as their
329 : // prototype. This is preferable to using a null prototype because it
330 : // lets things like |toString| and |__proto__| work.
331 : {
332 0 : JSAutoRealm ar(cx, target);
333 0 : if (!JS_GetClassPrototype(cx, JSProto_Object, protop))
334 0 : return false;
335 : }
336 0 : return JS_WrapObject(cx, protop);
337 : }
338 :
339 : static bool getBuiltinClass(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance,
340 : js::ESClass* cls) {
341 0 : *cls = js::ESClass::Other;
342 : return true;
343 : }
344 :
345 : static const char* className(JSContext* cx, JS::HandleObject wrapper, const js::Wrapper& baseInstance) {
346 : return "Opaque";
347 : }
348 :
349 0 : virtual void preserveWrapper(JSObject* target) override { }
350 :
351 0 : virtual JSObject* createHolder(JSContext* cx, JSObject* wrapper) override
352 : {
353 0 : return JS_NewObjectWithGivenProto(cx, &HolderClass, nullptr);
354 : }
355 :
356 : static OpaqueXrayTraits singleton;
357 : };
358 :
359 : XrayType GetXrayType(JSObject* obj);
360 : XrayTraits* GetXrayTraits(JSObject* obj);
361 :
362 : template <typename Base, typename Traits>
363 : class XrayWrapper : public Base {
364 : static_assert(mozilla::IsBaseOf<js::BaseProxyHandler, Base>::value,
365 : "Base *must* derive from js::BaseProxyHandler");
366 : public:
367 0 : constexpr explicit XrayWrapper(unsigned flags)
368 0 : : Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG, /* aHasPrototype = */ true)
369 0 : { };
370 :
371 : /* Standard internal methods. */
372 : virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
373 : JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
374 : virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
375 : JS::Handle<JS::PropertyDescriptor> desc,
376 : JS::ObjectOpResult& result) const override;
377 : virtual bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
378 : JS::AutoIdVector& props) const override;
379 : virtual bool delete_(JSContext* cx, JS::Handle<JSObject*> wrapper,
380 : JS::Handle<jsid> id, JS::ObjectOpResult& result) const override;
381 : virtual JSObject* enumerate(JSContext* cx, JS::Handle<JSObject*> wrapper) const override;
382 : virtual bool getPrototype(JSContext* cx, JS::HandleObject wrapper,
383 : JS::MutableHandleObject protop) const override;
384 : virtual bool setPrototype(JSContext* cx, JS::HandleObject wrapper,
385 : JS::HandleObject proto, JS::ObjectOpResult& result) const override;
386 : virtual bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject wrapper, bool* isOrdinary,
387 : JS::MutableHandleObject protop) const override;
388 : virtual bool setImmutablePrototype(JSContext* cx, JS::HandleObject wrapper,
389 : bool* succeeded) const override;
390 : virtual bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> wrapper,
391 : JS::ObjectOpResult& result) const override;
392 : virtual bool isExtensible(JSContext* cx, JS::Handle<JSObject*> wrapper, bool* extensible) const override;
393 : virtual bool has(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
394 : bool* bp) const override;
395 : virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::HandleValue receiver,
396 : JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const override;
397 : virtual bool set(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
398 : JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
399 : JS::ObjectOpResult& result) const override;
400 : virtual bool call(JSContext* cx, JS::Handle<JSObject*> wrapper,
401 : const JS::CallArgs& args) const override;
402 : virtual bool construct(JSContext* cx, JS::Handle<JSObject*> wrapper,
403 : const JS::CallArgs& args) const override;
404 :
405 : /* SpiderMonkey extensions. */
406 : virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
407 : JS::MutableHandle<JS::PropertyDescriptor> desc) const override;
408 : virtual bool hasOwn(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
409 : bool* bp) const override;
410 : virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
411 : JS::AutoIdVector& props) const override;
412 :
413 : virtual bool getBuiltinClass(JSContext* cx, JS::HandleObject wapper, js::ESClass* cls) const override;
414 : virtual const char* className(JSContext* cx, JS::HandleObject proxy) const override;
415 :
416 : static const XrayWrapper singleton;
417 :
418 : protected:
419 : bool getPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper, unsigned flags,
420 : JS::AutoIdVector& props) const;
421 : };
422 :
423 : #define PermissiveXrayDOM xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::DOMXrayTraits>
424 : #define SecurityXrayDOM xpc::XrayWrapper<js::CrossCompartmentSecurityWrapper, xpc::DOMXrayTraits>
425 : #define PermissiveXrayJS xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::JSXrayTraits>
426 : #define PermissiveXrayOpaque xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::OpaqueXrayTraits>
427 :
428 : extern template class PermissiveXrayDOM;
429 : extern template class SecurityXrayDOM;
430 : extern template class PermissiveXrayJS;
431 : extern template class PermissiveXrayOpaque;
432 :
433 : /*
434 : * Slots for Xray expando objects. See comments in XrayWrapper.cpp for details
435 : * of how these get used; we mostly want the value of JSSLOT_EXPANDO_COUNT here.
436 : */
437 : enum ExpandoSlots {
438 : JSSLOT_EXPANDO_NEXT = 0,
439 : JSSLOT_EXPANDO_ORIGIN,
440 : JSSLOT_EXPANDO_EXCLUSIVE_WRAPPER_HOLDER,
441 : JSSLOT_EXPANDO_PROTOTYPE,
442 : JSSLOT_EXPANDO_COUNT
443 : };
444 :
445 : extern const JSClassOps XrayExpandoObjectClassOps;
446 :
447 : /*
448 : * Clear the given slot on all Xray expandos for the given object.
449 : *
450 : * No-op when called on non-main threads (where Xrays don't exist).
451 : */
452 : void
453 : ClearXrayExpandoSlots(JSObject* target, size_t slotIndex);
454 :
455 : /*
456 : * Ensure the given wrapper has an expando object and return it. This can
457 : * return null on failure. Will only be called when "wrapper" is an Xray for a
458 : * DOM object.
459 : */
460 : JSObject*
461 : EnsureXrayExpandoObject(JSContext* cx, JS::HandleObject wrapper);
462 :
463 : // Information about xrays for use by the JITs.
464 : extern js::XrayJitInfo gXrayJitInfo;
465 :
466 : } // namespace xpc
467 :
468 : #endif
|