Coverage Report

Created: 2018-09-25 14:53

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