Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/js/ipc/WrapperOwner.cpp
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=4 sw=4 et tw=80:
3
 *
4
 * This Source Code Form is subject to the terms of the Mozilla Public
5
 * License, v. 2.0. If a copy of the MPL was not distributed with this
6
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8
#include "WrapperOwner.h"
9
#include "JavaScriptLogging.h"
10
#include "mozilla/Unused.h"
11
#include "mozilla/dom/BindingUtils.h"
12
#include "jsfriendapi.h"
13
#include "js/CharacterEncoding.h"
14
#include "xpcprivate.h"
15
#include "CPOWTimer.h"
16
#include "WrapperFactory.h"
17
18
#include "nsIDocShellTreeItem.h"
19
#include "nsIDocument.h"
20
21
using namespace js;
22
using namespace JS;
23
using namespace mozilla;
24
using namespace mozilla::jsipc;
25
26
struct AuxCPOWData
27
{
28
    ObjectId id;
29
    bool isCallable;
30
    bool isConstructor;
31
    bool isDOMObject;
32
33
    // The object tag is just some auxilliary information that clients can use
34
    // however they see fit.
35
    nsCString objectTag;
36
37
    // The class name for WrapperOwner::className, below.
38
    nsCString className;
39
40
    AuxCPOWData(ObjectId id,
41
                bool isCallable,
42
                bool isConstructor,
43
                bool isDOMObject,
44
                const nsACString& objectTag)
45
      : id(id),
46
        isCallable(isCallable),
47
        isConstructor(isConstructor),
48
        isDOMObject(isDOMObject),
49
        objectTag(objectTag)
50
0
    {}
51
};
52
53
WrapperOwner::WrapperOwner()
54
  : inactive_(false)
55
0
{
56
0
}
57
58
static inline AuxCPOWData*
59
AuxCPOWDataOf(JSObject* obj)
60
0
{
61
0
    MOZ_ASSERT(IsCPOW(obj));
62
0
    return static_cast<AuxCPOWData*>(GetProxyReservedSlot(obj, 1).toPrivate());
63
0
}
64
65
static inline WrapperOwner*
66
OwnerOf(JSObject* obj)
67
0
{
68
0
    MOZ_ASSERT(IsCPOW(obj));
69
0
    return reinterpret_cast<WrapperOwner*>(GetProxyReservedSlot(obj, 0).toPrivate());
70
0
}
71
72
ObjectId
73
WrapperOwner::idOfUnchecked(JSObject* obj)
74
0
{
75
0
    MOZ_ASSERT(IsCPOW(obj));
76
0
77
0
    AuxCPOWData* aux = AuxCPOWDataOf(obj);
78
0
    MOZ_ASSERT(!aux->id.isNull());
79
0
    return aux->id;
80
0
}
81
82
ObjectId
83
WrapperOwner::idOf(JSObject* obj)
84
0
{
85
0
    ObjectId objId = idOfUnchecked(obj);
86
0
    MOZ_ASSERT(hasCPOW(objId, obj));
87
0
    return objId;
88
0
}
89
90
class CPOWProxyHandler : public BaseProxyHandler
91
{
92
  public:
93
    constexpr CPOWProxyHandler()
94
0
      : BaseProxyHandler(&family) {}
95
96
0
    virtual bool finalizeInBackground(const Value& priv) const override {
97
0
        return false;
98
0
    }
99
100
    virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
101
                                          MutableHandle<PropertyDescriptor> desc) const override;
102
    virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
103
                                Handle<PropertyDescriptor> desc,
104
                                ObjectOpResult& result) const override;
105
    virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
106
                                 AutoIdVector& props) const override;
107
    virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
108
                         ObjectOpResult& result) const override;
109
    virtual JSObject* enumerate(JSContext* cx, HandleObject proxy) const override;
110
    virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
111
                                   ObjectOpResult& result) const override;
112
    virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override;
113
    virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
114
    virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
115
                     HandleId id, MutableHandleValue vp) const override;
116
    virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
117
                     JS::HandleValue receiver, JS::ObjectOpResult& result) const override;
118
    virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
119
    virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
120
121
    virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
122
    virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
123
                                              AutoIdVector& props) const override;
124
    virtual bool hasInstance(JSContext* cx, HandleObject proxy,
125
                             MutableHandleValue v, bool* bp) const override;
126
    virtual bool getBuiltinClass(JSContext* cx, HandleObject obj, js::ESClass* cls) const override;
127
    virtual bool isArray(JSContext* cx, HandleObject obj,
128
                         IsArrayAnswer* answer) const override;
129
    virtual const char* className(JSContext* cx, HandleObject proxy) const override;
130
    virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
131
    virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
132
    virtual size_t objectMoved(JSObject* proxy, JSObject* old) const override;
133
    virtual bool isCallable(JSObject* obj) const override;
134
    virtual bool isConstructor(JSObject* obj) const override;
135
    virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override;
136
    virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
137
                                        MutableHandleObject protop) const override;
138
139
    static const char family;
140
    static const CPOWProxyHandler singleton;
141
};
142
143
const char CPOWProxyHandler::family = 0;
144
const CPOWProxyHandler CPOWProxyHandler::singleton;
145
146
#define FORWARD(call, args, failRetVal)                                 \
147
0
    AUTO_PROFILER_LABEL(__func__, JS);                                  \
148
0
    WrapperOwner* owner = OwnerOf(proxy);                               \
149
0
    if (!owner->active()) {                                             \
150
0
        JS_ReportErrorASCII(cx, "cannot use a CPOW whose process is gone"); \
151
0
        return failRetVal;                                              \
152
0
    }                                                                   \
153
0
    if (!owner->allowMessage(cx)) {                                     \
154
0
        return failRetVal;                                              \
155
0
    }                                                                   \
156
0
    {                                                                   \
157
0
        CPOWTimer timer(cx);                                            \
158
0
        return owner->call args;                                        \
159
0
    }
160
161
bool
162
CPOWProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
163
                                           MutableHandle<PropertyDescriptor> desc) const
164
0
{
165
0
    FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc), false);
166
0
}
167
168
bool
169
WrapperOwner::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
170
                                       MutableHandle<PropertyDescriptor> desc)
171
0
{
172
0
    ObjectId objId = idOf(proxy);
173
0
174
0
    JSIDVariant idVar;
175
0
    if (!toJSIDVariant(cx, id, &idVar)) {
176
0
        return false;
177
0
    }
178
0
179
0
    ReturnStatus status;
180
0
    PPropertyDescriptor result;
181
0
    if (!SendGetOwnPropertyDescriptor(objId, idVar, &status, &result)) {
182
0
        return ipcfail(cx);
183
0
    }
184
0
185
0
    LOG_STACK();
186
0
187
0
    if (!ok(cx, status)) {
188
0
        return false;
189
0
    }
190
0
191
0
    return toDescriptor(cx, result, desc);
192
0
}
193
194
bool
195
CPOWProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
196
                                 Handle<PropertyDescriptor> desc,
197
                                 ObjectOpResult& result) const
198
0
{
199
0
    FORWARD(defineProperty, (cx, proxy, id, desc, result), false);
200
0
}
201
202
bool
203
WrapperOwner::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
204
                             Handle<PropertyDescriptor> desc,
205
                             ObjectOpResult& result)
206
0
{
207
0
    ObjectId objId = idOf(proxy);
208
0
209
0
    JSIDVariant idVar;
210
0
    if (!toJSIDVariant(cx, id, &idVar)) {
211
0
        return false;
212
0
    }
213
0
214
0
    PPropertyDescriptor descriptor;
215
0
    if (!fromDescriptor(cx, desc, &descriptor)) {
216
0
        return false;
217
0
    }
218
0
219
0
    ReturnStatus status;
220
0
    if (!SendDefineProperty(objId, idVar, descriptor, &status)) {
221
0
        return ipcfail(cx);
222
0
    }
223
0
224
0
    LOG_STACK();
225
0
226
0
    return ok(cx, status, result);
227
0
}
228
229
bool
230
CPOWProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy,
231
                                  AutoIdVector& props) const
232
0
{
233
0
    FORWARD(ownPropertyKeys, (cx, proxy, props), false);
234
0
}
235
236
bool
237
WrapperOwner::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
238
0
{
239
0
    return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
240
0
}
241
242
bool
243
CPOWProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id,
244
                          ObjectOpResult& result) const
245
0
{
246
0
    FORWARD(delete_, (cx, proxy, id, result), false);
247
0
}
248
249
bool
250
WrapperOwner::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result)
251
0
{
252
0
    ObjectId objId = idOf(proxy);
253
0
254
0
    JSIDVariant idVar;
255
0
    if (!toJSIDVariant(cx, id, &idVar)) {
256
0
        return false;
257
0
    }
258
0
259
0
    ReturnStatus status;
260
0
    if (!SendDelete(objId, idVar, &status)) {
261
0
        return ipcfail(cx);
262
0
    }
263
0
264
0
    LOG_STACK();
265
0
266
0
    return ok(cx, status, result);
267
0
}
268
269
JSObject*
270
CPOWProxyHandler::enumerate(JSContext* cx, HandleObject proxy) const
271
0
{
272
0
    // Using a CPOW for the Iterator would slow down for .. in performance, instead
273
0
    // call the base hook, that will use our implementation of getOwnEnumerablePropertyKeys
274
0
    // and follow the proto chain.
275
0
    return BaseProxyHandler::enumerate(cx, proxy);
276
0
}
277
278
bool
279
CPOWProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
280
0
{
281
0
    FORWARD(has, (cx, proxy, id, bp), false);
282
0
}
283
284
bool
285
WrapperOwner::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
286
0
{
287
0
    ObjectId objId = idOf(proxy);
288
0
289
0
    JSIDVariant idVar;
290
0
    if (!toJSIDVariant(cx, id, &idVar)) {
291
0
        return false;
292
0
    }
293
0
294
0
    ReturnStatus status;
295
0
    if (!SendHas(objId, idVar, &status, bp)) {
296
0
        return ipcfail(cx);
297
0
    }
298
0
299
0
    LOG_STACK();
300
0
301
0
    return ok(cx, status);
302
0
}
303
304
bool
305
CPOWProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
306
0
{
307
0
    FORWARD(hasOwn, (cx, proxy, id, bp), false);
308
0
}
309
310
bool
311
WrapperOwner::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
312
0
{
313
0
    ObjectId objId = idOf(proxy);
314
0
315
0
    JSIDVariant idVar;
316
0
    if (!toJSIDVariant(cx, id, &idVar)) {
317
0
        return false;
318
0
    }
319
0
320
0
    ReturnStatus status;
321
0
    if (!SendHasOwn(objId, idVar, &status, bp)) {
322
0
        return ipcfail(cx);
323
0
    }
324
0
325
0
    LOG_STACK();
326
0
327
0
    return !!ok(cx, status);
328
0
}
329
330
bool
331
CPOWProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
332
                      HandleId id, MutableHandleValue vp) const
333
0
{
334
0
    FORWARD(get, (cx, proxy, receiver, id, vp), false);
335
0
}
336
337
static bool
338
CPOWDOMQI(JSContext* cx, unsigned argc, Value* vp)
339
0
{
340
0
    CallArgs args = CallArgsFromVp(argc, vp);
341
0
    if (!args.thisv().isObject() || !IsCPOW(&args.thisv().toObject())) {
342
0
        JS_ReportErrorASCII(cx, "bad this object passed to special QI");
343
0
        return false;
344
0
    }
345
0
346
0
    RootedObject proxy(cx, &args.thisv().toObject());
347
0
    FORWARD(DOMQI, (cx, proxy, args), false);
348
0
}
349
350
static bool
351
CPOWToString(JSContext* cx, unsigned argc, Value* vp)
352
0
{
353
0
    CallArgs args = CallArgsFromVp(argc, vp);
354
0
    RootedObject callee(cx, &args.callee());
355
0
    RootedValue cpowValue(cx);
356
0
    if (!JS_GetProperty(cx, callee, "__cpow__", &cpowValue)) {
357
0
        return false;
358
0
    }
359
0
360
0
    if (!cpowValue.isObject() || !IsCPOW(&cpowValue.toObject())) {
361
0
        JS_ReportErrorASCII(cx, "CPOWToString called on an incompatible object");
362
0
        return false;
363
0
    }
364
0
365
0
    RootedObject proxy(cx, &cpowValue.toObject());
366
0
    FORWARD(toString, (cx, proxy, args), false);
367
0
}
368
369
bool
370
WrapperOwner::toString(JSContext* cx, HandleObject cpow, JS::CallArgs& args)
371
0
{
372
0
    // Ask the other side to call its toString method. Update the callee so that
373
0
    // it points to the CPOW and not to the synthesized CPOWToString function.
374
0
    args.setCallee(ObjectValue(*cpow));
375
0
    if (!callOrConstruct(cx, cpow, args, false)) {
376
0
        return false;
377
0
    }
378
0
379
0
    if (!args.rval().isString()) {
380
0
        return true;
381
0
    }
382
0
383
0
    RootedString cpowResult(cx, args.rval().toString());
384
0
    nsAutoJSString toStringResult;
385
0
    if (!toStringResult.init(cx, cpowResult)) {
386
0
        return false;
387
0
    }
388
0
389
0
    // We don't want to wrap toString() results for things like the location
390
0
    // object, where toString() is supposed to return a URL and nothing else.
391
0
    nsAutoString result;
392
0
    if (toStringResult[0] == '[') {
393
0
        result.AppendLiteral("[object CPOW ");
394
0
        result += toStringResult;
395
0
        result.AppendLiteral("]");
396
0
    } else {
397
0
        result += toStringResult;
398
0
    }
399
0
400
0
    JSString* str = JS_NewUCStringCopyN(cx, result.get(), result.Length());
401
0
    if (!str) {
402
0
        return false;
403
0
    }
404
0
405
0
    args.rval().setString(str);
406
0
    return true;
407
0
}
408
409
bool
410
WrapperOwner::DOMQI(JSContext* cx, JS::HandleObject proxy, JS::CallArgs& args)
411
0
{
412
0
    // Someone's calling us, handle nsISupports specially to avoid unnecessary
413
0
    // CPOW traffic.
414
0
    HandleValue id = args[0];
415
0
    if (id.isObject()) {
416
0
        RootedObject idobj(cx, &id.toObject());
417
0
        nsCOMPtr<nsIJSID> jsid;
418
0
419
0
        nsresult rv = UnwrapArg<nsIJSID>(cx, idobj, getter_AddRefs(jsid));
420
0
        if (NS_SUCCEEDED(rv)) {
421
0
            MOZ_ASSERT(jsid, "bad wrapJS");
422
0
            const nsID* idptr = jsid->GetID();
423
0
            if (idptr->Equals(NS_GET_IID(nsISupports))) {
424
0
                args.rval().set(args.thisv());
425
0
                return true;
426
0
            }
427
0
428
0
            // Webidl-implemented DOM objects never have nsIClassInfo.
429
0
            if (idptr->Equals(NS_GET_IID(nsIClassInfo))) {
430
0
                return Throw(cx, NS_ERROR_NO_INTERFACE);
431
0
            }
432
0
        }
433
0
    }
434
0
435
0
    // It wasn't nsISupports, call into the other process to do the QI for us
436
0
    // (since we don't know what other interfaces our object supports). Note
437
0
    // that we have to use JS_GetPropertyDescriptor here to avoid infinite
438
0
    // recursion back into CPOWDOMQI via WrapperOwner::get().
439
0
    // We could stash the actual QI function on our own function object to avoid
440
0
    // if we're called multiple times, but since we're transient, there's no
441
0
    // point right now.
442
0
    JS::Rooted<PropertyDescriptor> propDesc(cx);
443
0
    if (!JS_GetPropertyDescriptor(cx, proxy, "QueryInterface", &propDesc)) {
444
0
        return false;
445
0
    }
446
0
447
0
    if (!propDesc.value().isObject()) {
448
0
        MOZ_ASSERT_UNREACHABLE("We didn't get QueryInterface off a node");
449
0
        return Throw(cx, NS_ERROR_UNEXPECTED);
450
0
    }
451
0
    return JS_CallFunctionValue(cx, proxy, propDesc.value(), args, args.rval());
452
0
}
453
454
bool
455
WrapperOwner::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
456
                  HandleId id, MutableHandleValue vp)
457
0
{
458
0
    ObjectId objId = idOf(proxy);
459
0
460
0
    JSVariant receiverVar;
461
0
    if (!toVariant(cx, receiver, &receiverVar)) {
462
0
        return false;
463
0
    }
464
0
465
0
    JSIDVariant idVar;
466
0
    if (!toJSIDVariant(cx, id, &idVar)) {
467
0
        return false;
468
0
    }
469
0
470
0
    AuxCPOWData* data = AuxCPOWDataOf(proxy);
471
0
    if (data->isDOMObject &&
472
0
        idVar.type() == JSIDVariant::TnsString &&
473
0
        idVar.get_nsString().EqualsLiteral("QueryInterface"))
474
0
    {
475
0
        // Handle QueryInterface on DOM Objects specially since we can assume
476
0
        // certain things about their implementation.
477
0
        RootedFunction qi(cx, JS_NewFunction(cx, CPOWDOMQI, 1, 0,
478
0
                                             "QueryInterface"));
479
0
        if (!qi) {
480
0
            return false;
481
0
        }
482
0
483
0
        vp.set(ObjectValue(*JS_GetFunctionObject(qi)));
484
0
        return true;
485
0
    }
486
0
487
0
    JSVariant val;
488
0
    ReturnStatus status;
489
0
    if (!SendGet(objId, receiverVar, idVar, &status, &val)) {
490
0
        return ipcfail(cx);
491
0
    }
492
0
493
0
    LOG_STACK();
494
0
495
0
    if (!ok(cx, status)) {
496
0
        return false;
497
0
    }
498
0
499
0
    if (!fromVariant(cx, val, vp)) {
500
0
        return false;
501
0
    }
502
0
503
0
    if (idVar.type() == JSIDVariant::TnsString &&
504
0
        idVar.get_nsString().EqualsLiteral("toString")) {
505
0
        RootedFunction toString(cx, JS_NewFunction(cx, CPOWToString, 0, 0,
506
0
                                                   "toString"));
507
0
        if (!toString) {
508
0
            return false;
509
0
        }
510
0
511
0
        RootedObject toStringObj(cx, JS_GetFunctionObject(toString));
512
0
513
0
        if (!JS_DefineProperty(cx, toStringObj, "__cpow__", vp, JSPROP_PERMANENT | JSPROP_READONLY)) {
514
0
            return false;
515
0
        }
516
0
517
0
        vp.set(ObjectValue(*toStringObj));
518
0
    }
519
0
520
0
    return true;
521
0
}
522
523
bool
524
CPOWProxyHandler::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
525
                      JS::HandleValue receiver, JS::ObjectOpResult& result) const
526
0
{
527
0
    FORWARD(set, (cx, proxy, id, v, receiver, result), false);
528
0
}
529
530
bool
531
WrapperOwner::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
532
                  JS::HandleValue receiver, JS::ObjectOpResult& result)
533
0
{
534
0
    ObjectId objId = idOf(proxy);
535
0
536
0
    JSIDVariant idVar;
537
0
    if (!toJSIDVariant(cx, id, &idVar)) {
538
0
        return false;
539
0
    }
540
0
541
0
    JSVariant val;
542
0
    if (!toVariant(cx, v, &val)) {
543
0
        return false;
544
0
    }
545
0
546
0
    JSVariant receiverVar;
547
0
    if (!toVariant(cx, receiver, &receiverVar)) {
548
0
        return false;
549
0
    }
550
0
551
0
    ReturnStatus status;
552
0
    if (!SendSet(objId, idVar, val, receiverVar, &status)) {
553
0
        return ipcfail(cx);
554
0
    }
555
0
556
0
    LOG_STACK();
557
0
558
0
    return ok(cx, status, result);
559
0
}
560
561
bool
562
CPOWProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
563
                                               AutoIdVector& props) const
564
0
{
565
0
    FORWARD(getOwnEnumerablePropertyKeys, (cx, proxy, props), false);
566
0
}
567
568
bool
569
WrapperOwner::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
570
0
{
571
0
    return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props);
572
0
}
573
574
bool
575
CPOWProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) const
576
0
{
577
0
    FORWARD(preventExtensions, (cx, proxy, result), false);
578
0
}
579
580
bool
581
WrapperOwner::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result)
582
0
{
583
0
    ObjectId objId = idOf(proxy);
584
0
585
0
    ReturnStatus status;
586
0
    if (!SendPreventExtensions(objId, &status)) {
587
0
        return ipcfail(cx);
588
0
    }
589
0
590
0
    LOG_STACK();
591
0
592
0
    return ok(cx, status, result);
593
0
}
594
595
bool
596
CPOWProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const
597
0
{
598
0
    FORWARD(isExtensible, (cx, proxy, extensible), false);
599
0
}
600
601
bool
602
WrapperOwner::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible)
603
0
{
604
0
    ObjectId objId = idOf(proxy);
605
0
606
0
    ReturnStatus status;
607
0
    if (!SendIsExtensible(objId, &status, extensible)) {
608
0
        return ipcfail(cx);
609
0
    }
610
0
611
0
    LOG_STACK();
612
0
613
0
    return ok(cx, status);
614
0
}
615
616
bool
617
CPOWProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const
618
0
{
619
0
    FORWARD(callOrConstruct, (cx, proxy, args, false), false);
620
0
}
621
622
bool
623
CPOWProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const
624
0
{
625
0
    FORWARD(callOrConstruct, (cx, proxy, args, true), false);
626
0
}
627
628
bool
629
WrapperOwner::callOrConstruct(JSContext* cx, HandleObject proxy, const CallArgs& args,
630
                              bool construct)
631
0
{
632
0
    ObjectId objId = idOf(proxy);
633
0
634
0
    InfallibleTArray<JSParam> vals;
635
0
    AutoValueVector outobjects(cx);
636
0
637
0
    RootedValue v(cx);
638
0
    for (size_t i = 0; i < args.length() + 2; i++) {
639
0
        // The |this| value for constructors is a magic value that we won't be
640
0
        // able to convert, so skip it.
641
0
        if (i == 1 && construct) {
642
0
            v = UndefinedValue();
643
0
        } else {
644
0
            v = args.base()[i];
645
0
        }
646
0
        if (v.isObject()) {
647
0
            RootedObject obj(cx, &v.toObject());
648
0
            if (xpc::IsOutObject(cx, obj)) {
649
0
                // Make sure it is not an in-out object.
650
0
                bool found;
651
0
                if (!JS_HasProperty(cx, obj, "value", &found)) {
652
0
                    return false;
653
0
                }
654
0
                if (found) {
655
0
                    JS_ReportErrorASCII(cx, "in-out objects cannot be sent via CPOWs yet");
656
0
                    return false;
657
0
                }
658
0
659
0
                vals.AppendElement(JSParam(void_t()));
660
0
                if (!outobjects.append(ObjectValue(*obj))) {
661
0
                    return false;
662
0
                }
663
0
                continue;
664
0
            }
665
0
        }
666
0
        JSVariant val;
667
0
        if (!toVariant(cx, v, &val)) {
668
0
            return false;
669
0
        }
670
0
        vals.AppendElement(JSParam(val));
671
0
    }
672
0
673
0
    JSVariant result;
674
0
    ReturnStatus status;
675
0
    InfallibleTArray<JSParam> outparams;
676
0
    if (!SendCallOrConstruct(objId, vals, construct, &status, &result, &outparams)) {
677
0
        return ipcfail(cx);
678
0
    }
679
0
680
0
    LOG_STACK();
681
0
682
0
    if (!ok(cx, status)) {
683
0
        return false;
684
0
    }
685
0
686
0
    if (outparams.Length() != outobjects.length()) {
687
0
        return ipcfail(cx);
688
0
    }
689
0
690
0
    RootedObject obj(cx);
691
0
    for (size_t i = 0; i < outparams.Length(); i++) {
692
0
        // Don't bother doing anything for outparams that weren't set.
693
0
        if (outparams[i].type() == JSParam::Tvoid_t) {
694
0
            continue;
695
0
        }
696
0
697
0
        // Take the value the child process returned, and set it on the XPC
698
0
        // object.
699
0
        if (!fromVariant(cx, outparams[i], &v)) {
700
0
            return false;
701
0
        }
702
0
703
0
        obj = &outobjects[i].toObject();
704
0
        if (!JS_SetProperty(cx, obj, "value", v)) {
705
0
            return false;
706
0
        }
707
0
    }
708
0
709
0
    if (!fromVariant(cx, result, args.rval())) {
710
0
        return false;
711
0
    }
712
0
713
0
    return true;
714
0
}
715
716
bool
717
CPOWProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const
718
0
{
719
0
    FORWARD(hasInstance, (cx, proxy, v, bp), false);
720
0
}
721
722
bool
723
WrapperOwner::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp)
724
0
{
725
0
    ObjectId objId = idOf(proxy);
726
0
727
0
    JSVariant vVar;
728
0
    if (!toVariant(cx, v, &vVar)) {
729
0
        return false;
730
0
    }
731
0
732
0
    ReturnStatus status;
733
0
    if (!SendHasInstance(objId, vVar, &status, bp)) {
734
0
        return ipcfail(cx);
735
0
    }
736
0
737
0
    LOG_STACK();
738
0
739
0
    return ok(cx, status);
740
0
}
741
742
bool
743
CPOWProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const
744
0
{
745
0
    FORWARD(getBuiltinClass, (cx, proxy, cls), false);
746
0
}
747
748
bool
749
WrapperOwner::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls)
750
0
{
751
0
    ObjectId objId = idOf(proxy);
752
0
753
0
    uint32_t classValue = uint32_t(ESClass::Other);
754
0
    ReturnStatus status;
755
0
    if (!SendGetBuiltinClass(objId, &status, &classValue)) {
756
0
        return ipcfail(cx);
757
0
    }
758
0
    *cls = ESClass(classValue);
759
0
760
0
    LOG_STACK();
761
0
762
0
    return ok(cx, status);
763
0
}
764
765
bool
766
CPOWProxyHandler::isArray(JSContext* cx, HandleObject proxy,
767
                          IsArrayAnswer* answer) const
768
0
{
769
0
    FORWARD(isArray, (cx, proxy, answer), false);
770
0
}
771
772
bool
773
WrapperOwner::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer)
774
0
{
775
0
    ObjectId objId = idOf(proxy);
776
0
777
0
    uint32_t ans;
778
0
    ReturnStatus status;
779
0
    if (!SendIsArray(objId, &status, &ans)) {
780
0
        return ipcfail(cx);
781
0
    }
782
0
783
0
    LOG_STACK();
784
0
785
0
    *answer = IsArrayAnswer(ans);
786
0
    MOZ_ASSERT(*answer == IsArrayAnswer::Array ||
787
0
               *answer == IsArrayAnswer::NotArray ||
788
0
               *answer == IsArrayAnswer::RevokedProxy);
789
0
790
0
    return ok(cx, status);
791
0
}
792
793
const char*
794
CPOWProxyHandler::className(JSContext* cx, HandleObject proxy) const
795
0
{
796
0
    WrapperOwner* parent = OwnerOf(proxy);
797
0
    if (!parent->active()) {
798
0
        return "<dead CPOW>";
799
0
    }
800
0
    return parent->className(cx, proxy);
801
0
}
802
803
const char*
804
WrapperOwner::className(JSContext* cx, HandleObject proxy)
805
0
{
806
0
    AuxCPOWData* data = AuxCPOWDataOf(proxy);
807
0
    if (data->className.IsEmpty()) {
808
0
        ObjectId objId = idOf(proxy);
809
0
810
0
        if (!SendClassName(objId, &data->className)) {
811
0
            return "<error>";
812
0
        }
813
0
814
0
        LOG_STACK();
815
0
    }
816
0
817
0
    return data->className.get();
818
0
}
819
820
bool
821
CPOWProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const
822
0
{
823
0
    FORWARD(getPrototype, (cx, proxy, objp), false);
824
0
}
825
826
bool
827
WrapperOwner::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp)
828
0
{
829
0
    ObjectId objId = idOf(proxy);
830
0
831
0
    ObjectOrNullVariant val;
832
0
    ReturnStatus status;
833
0
    if (!SendGetPrototype(objId, &status, &val)) {
834
0
        return ipcfail(cx);
835
0
    }
836
0
837
0
    LOG_STACK();
838
0
839
0
    if (!ok(cx, status)) {
840
0
        return false;
841
0
    }
842
0
843
0
    objp.set(fromObjectOrNullVariant(cx, val));
844
0
845
0
    return true;
846
0
}
847
848
bool
849
CPOWProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
850
                                         MutableHandleObject objp) const
851
0
{
852
0
    FORWARD(getPrototypeIfOrdinary, (cx, proxy, isOrdinary, objp), false);
853
0
}
854
855
bool
856
WrapperOwner::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
857
                                     MutableHandleObject objp)
858
0
{
859
0
    ObjectId objId = idOf(proxy);
860
0
861
0
    ObjectOrNullVariant val;
862
0
    ReturnStatus status;
863
0
    if (!SendGetPrototypeIfOrdinary(objId, &status, isOrdinary, &val)) {
864
0
        return ipcfail(cx);
865
0
    }
866
0
867
0
    LOG_STACK();
868
0
869
0
    if (!ok(cx, status)) {
870
0
        return false;
871
0
    }
872
0
873
0
    objp.set(fromObjectOrNullVariant(cx, val));
874
0
875
0
    return true;
876
0
}
877
878
RegExpShared*
879
CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const
880
0
{
881
0
    FORWARD(regexp_toShared, (cx, proxy), nullptr);
882
0
}
883
884
RegExpShared*
885
WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy)
886
0
{
887
0
    ObjectId objId = idOf(proxy);
888
0
889
0
    ReturnStatus status;
890
0
    nsString source;
891
0
    unsigned flags = 0;
892
0
    if (!SendRegExpToShared(objId, &status, &source, &flags)) {
893
0
        MOZ_ALWAYS_FALSE(ipcfail(cx));
894
0
        return nullptr;
895
0
    }
896
0
    LOG_STACK();
897
0
898
0
    if (!ok(cx, status)) {
899
0
        return nullptr;
900
0
    }
901
0
902
0
    RootedObject regexp(cx);
903
0
    regexp = JS_NewUCRegExpObject(cx, source.get(), source.Length(), flags);
904
0
    if (!regexp) {
905
0
        return nullptr;
906
0
    }
907
0
908
0
    return js::RegExpToSharedNonInline(cx, regexp);
909
0
}
910
911
void
912
CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const
913
0
{
914
0
    AuxCPOWData* aux = AuxCPOWDataOf(proxy);
915
0
916
0
    OwnerOf(proxy)->drop(proxy);
917
0
918
0
    if (aux) {
919
0
        delete aux;
920
0
    }
921
0
}
922
923
size_t
924
CPOWProxyHandler::objectMoved(JSObject* proxy, JSObject* old) const
925
0
{
926
0
    OwnerOf(proxy)->updatePointer(proxy, old);
927
0
    return 0;
928
0
}
929
930
bool
931
CPOWProxyHandler::isCallable(JSObject* proxy) const
932
0
{
933
0
    AuxCPOWData* aux = AuxCPOWDataOf(proxy);
934
0
    return aux->isCallable;
935
0
}
936
937
bool
938
CPOWProxyHandler::isConstructor(JSObject* proxy) const
939
0
{
940
0
    AuxCPOWData* aux = AuxCPOWDataOf(proxy);
941
0
    return aux->isConstructor;
942
0
}
943
944
void
945
WrapperOwner::drop(JSObject* obj)
946
0
{
947
0
    // The association may have already been swept from the table but if it's
948
0
    // there then remove it.
949
0
    ObjectId objId = idOfUnchecked(obj);
950
0
    if (cpows_.findPreserveColor(objId) == obj) {
951
0
        cpows_.remove(objId);
952
0
    }
953
0
954
0
    if (active()) {
955
0
        Unused << SendDropObject(objId);
956
0
    }
957
0
    decref();
958
0
}
959
960
void
961
WrapperOwner::updatePointer(JSObject* obj, const JSObject* old)
962
0
{
963
0
    ObjectId objId = idOfUnchecked(obj);
964
0
    MOZ_ASSERT(hasCPOW(objId, old));
965
0
    cpows_.add(objId, obj);
966
0
}
967
968
bool
969
WrapperOwner::getPropertyKeys(JSContext* cx, HandleObject proxy, uint32_t flags, AutoIdVector& props)
970
0
{
971
0
    ObjectId objId = idOf(proxy);
972
0
973
0
    ReturnStatus status;
974
0
    InfallibleTArray<JSIDVariant> ids;
975
0
    if (!SendGetPropertyKeys(objId, flags, &status, &ids)) {
976
0
        return ipcfail(cx);
977
0
    }
978
0
979
0
    LOG_STACK();
980
0
981
0
    if (!ok(cx, status)) {
982
0
        return false;
983
0
    }
984
0
985
0
    for (size_t i = 0; i < ids.Length(); i++) {
986
0
        RootedId id(cx);
987
0
        if (!fromJSIDVariant(cx, ids[i], &id)) {
988
0
            return false;
989
0
        }
990
0
        if (!props.append(id)) {
991
0
            return false;
992
0
        }
993
0
    }
994
0
995
0
    return true;
996
0
}
997
998
namespace mozilla {
999
namespace jsipc {
1000
1001
bool
1002
IsCPOW(JSObject* obj)
1003
1.62M
{
1004
1.62M
    return IsProxy(obj) && GetProxyHandler(obj) == &CPOWProxyHandler::singleton;
1005
1.62M
}
1006
1007
bool
1008
IsWrappedCPOW(JSObject* obj)
1009
1.62M
{
1010
1.62M
    JSObject* unwrapped = js::UncheckedUnwrap(obj, true);
1011
1.62M
    if (!unwrapped) {
1012
0
        return false;
1013
0
    }
1014
1.62M
    return IsCPOW(unwrapped);
1015
1.62M
}
1016
1017
void
1018
GetWrappedCPOWTag(JSObject* obj, nsACString& out)
1019
0
{
1020
0
    JSObject* unwrapped = js::UncheckedUnwrap(obj, true);
1021
0
    MOZ_ASSERT(IsCPOW(unwrapped));
1022
0
1023
0
    AuxCPOWData* aux = AuxCPOWDataOf(unwrapped);
1024
0
    if (aux) {
1025
0
        out = aux->objectTag;
1026
0
    }
1027
0
}
1028
1029
nsresult
1030
InstanceOf(JSObject* proxy, const nsID* id, bool* bp)
1031
0
{
1032
0
    WrapperOwner* parent = OwnerOf(proxy);
1033
0
    if (!parent->active()) {
1034
0
        return NS_ERROR_UNEXPECTED;
1035
0
    }
1036
0
    return parent->instanceOf(proxy, id, bp);
1037
0
}
1038
1039
bool
1040
DOMInstanceOf(JSContext* cx, JSObject* proxyArg, int prototypeID, int depth, bool* bp)
1041
0
{
1042
0
    RootedObject proxy(cx, proxyArg);
1043
0
    FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp), false);
1044
0
}
1045
1046
} /* namespace jsipc */
1047
} /* namespace mozilla */
1048
1049
nsresult
1050
WrapperOwner::instanceOf(JSObject* obj, const nsID* id, bool* bp)
1051
0
{
1052
0
    ObjectId objId = idOf(obj);
1053
0
1054
0
    JSIID iid;
1055
0
    ConvertID(*id, &iid);
1056
0
1057
0
    ReturnStatus status;
1058
0
    if (!SendInstanceOf(objId, iid, &status, bp)) {
1059
0
        return NS_ERROR_UNEXPECTED;
1060
0
    }
1061
0
1062
0
    if (status.type() != ReturnStatus::TReturnSuccess) {
1063
0
        return NS_ERROR_UNEXPECTED;
1064
0
    }
1065
0
1066
0
    return NS_OK;
1067
0
}
1068
1069
bool
1070
WrapperOwner::domInstanceOf(JSContext* cx, JSObject* obj, int prototypeID, int depth, bool* bp)
1071
0
{
1072
0
    ObjectId objId = idOf(obj);
1073
0
1074
0
    ReturnStatus status;
1075
0
    if (!SendDOMInstanceOf(objId, prototypeID, depth, &status, bp)) {
1076
0
        return ipcfail(cx);
1077
0
    }
1078
0
1079
0
    LOG_STACK();
1080
0
1081
0
    return ok(cx, status);
1082
0
}
1083
1084
void
1085
WrapperOwner::ActorDestroy(ActorDestroyReason why)
1086
0
{
1087
0
    inactive_ = true;
1088
0
1089
0
    objects_.clear();
1090
0
    unwaivedObjectIds_.clear();
1091
0
    waivedObjectIds_.clear();
1092
0
}
1093
1094
bool
1095
WrapperOwner::ipcfail(JSContext* cx)
1096
0
{
1097
0
    JS_ReportErrorASCII(cx, "cross-process JS call failed");
1098
0
    return false;
1099
0
}
1100
1101
bool
1102
WrapperOwner::ok(JSContext* cx, const ReturnStatus& status)
1103
0
{
1104
0
    if (status.type() == ReturnStatus::TReturnSuccess) {
1105
0
        return true;
1106
0
    }
1107
0
1108
0
    if (status.type() == ReturnStatus::TReturnDeadCPOW) {
1109
0
        JS_ReportErrorASCII(cx, "operation not possible on dead CPOW");
1110
0
        return false;
1111
0
    }
1112
0
1113
0
    RootedValue exn(cx);
1114
0
    if (!fromVariant(cx, status.get_ReturnException().exn(), &exn)) {
1115
0
        return false;
1116
0
    }
1117
0
1118
0
    JS_SetPendingException(cx, exn);
1119
0
    return false;
1120
0
}
1121
1122
bool
1123
WrapperOwner::ok(JSContext* cx, const ReturnStatus& status, ObjectOpResult& result)
1124
0
{
1125
0
    if (status.type() == ReturnStatus::TReturnObjectOpResult) {
1126
0
        return result.fail(status.get_ReturnObjectOpResult().code());
1127
0
    }
1128
0
    if (!ok(cx, status)) {
1129
0
        return false;
1130
0
    }
1131
0
    return result.succeed();
1132
0
}
1133
1134
// CPOWs can have a tag string attached to them, originating in the local
1135
// process from this function.  It's sent with the CPOW to the remote process,
1136
// where it can be fetched with Components.utils.getCrossProcessWrapperTag.
1137
static nsCString
1138
GetRemoteObjectTag(JS::Handle<JSObject*> obj)
1139
0
{
1140
0
    if (nsCOMPtr<nsISupports> supports = xpc::UnwrapReflectorToISupports(obj)) {
1141
0
        nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(supports));
1142
0
        if (treeItem) {
1143
0
            return NS_LITERAL_CSTRING("ContentDocShellTreeItem");
1144
0
        }
1145
0
1146
0
        nsCOMPtr<nsIDocument> doc(do_QueryInterface(supports));
1147
0
        if (doc) {
1148
0
            return NS_LITERAL_CSTRING("ContentDocument");
1149
0
        }
1150
0
    }
1151
0
1152
0
    return NS_LITERAL_CSTRING("generic");
1153
0
}
1154
1155
static RemoteObject
1156
MakeRemoteObject(JSContext* cx, ObjectId id, HandleObject obj)
1157
0
{
1158
0
    return RemoteObject(id.serialize(),
1159
0
                        JS::IsCallable(obj),
1160
0
                        JS::IsConstructor(obj),
1161
0
                        dom::IsDOMObject(obj),
1162
0
                        GetRemoteObjectTag(obj));
1163
0
}
1164
1165
bool
1166
WrapperOwner::toObjectVariant(JSContext* cx, JSObject* objArg, ObjectVariant* objVarp)
1167
0
{
1168
0
    RootedObject obj(cx, objArg);
1169
0
    MOZ_ASSERT(obj);
1170
0
1171
0
    // We always save objects unwrapped in the CPOW table. If we stored
1172
0
    // wrappers, then the wrapper might be GCed while the target remained alive.
1173
0
    // Whenever operating on an object that comes from the table, we wrap it
1174
0
    // in findObjectById.
1175
0
    unsigned wrapperFlags = 0;
1176
0
    obj = js::UncheckedUnwrap(obj, true, &wrapperFlags);
1177
0
    if (obj && IsCPOW(obj) && OwnerOf(obj) == this) {
1178
0
        *objVarp = LocalObject(idOf(obj).serialize());
1179
0
        return true;
1180
0
    }
1181
0
    bool waiveXray = wrapperFlags & xpc::WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG;
1182
0
1183
0
    ObjectId id = objectIdMap(waiveXray).find(obj);
1184
0
    if (!id.isNull()) {
1185
0
        MOZ_ASSERT(id.hasXrayWaiver() == waiveXray);
1186
0
        *objVarp = MakeRemoteObject(cx, id, obj);
1187
0
        return true;
1188
0
    }
1189
0
1190
0
    // Need to call PreserveWrapper on |obj| in case it's a reflector.
1191
0
    // FIXME: What if it's an XPCWrappedNative?
1192
0
    if (mozilla::dom::IsDOMObject(obj)) {
1193
0
        mozilla::dom::TryPreserveWrapper(obj);
1194
0
    }
1195
0
1196
0
    id = ObjectId(nextSerialNumber_++, waiveXray);
1197
0
    if (!objects_.add(id, obj)) {
1198
0
        return false;
1199
0
    }
1200
0
    if (!objectIdMap(waiveXray).add(cx, obj, id)) {
1201
0
        return false;
1202
0
    }
1203
0
1204
0
    *objVarp = MakeRemoteObject(cx, id, obj);
1205
0
    return true;
1206
0
}
1207
1208
JSObject*
1209
WrapperOwner::fromObjectVariant(JSContext* cx, const ObjectVariant& objVar)
1210
0
{
1211
0
    if (objVar.type() == ObjectVariant::TRemoteObject) {
1212
0
        return fromRemoteObjectVariant(cx, objVar.get_RemoteObject());
1213
0
    } else {
1214
0
        return fromLocalObjectVariant(cx, objVar.get_LocalObject());
1215
0
    }
1216
0
}
1217
1218
JSObject*
1219
WrapperOwner::fromRemoteObjectVariant(JSContext* cx, const RemoteObject& objVar)
1220
0
{
1221
0
    Maybe<ObjectId> maybeObjId(ObjectId::deserialize(objVar.serializedId()));
1222
0
    if (maybeObjId.isNothing()) {
1223
0
        return nullptr;
1224
0
    }
1225
0
1226
0
    ObjectId objId = maybeObjId.value();
1227
0
    RootedObject obj(cx, findCPOWById(objId));
1228
0
    if (!obj) {
1229
0
1230
0
        // All CPOWs live in the privileged junk scope.
1231
0
        RootedObject junkScope(cx, xpc::PrivilegedJunkScope());
1232
0
        JSAutoRealm ar(cx, junkScope);
1233
0
        RootedValue v(cx, UndefinedValue());
1234
0
        // We need to setLazyProto for the getPrototype/getPrototypeIfOrdinary
1235
0
        // hooks.
1236
0
        ProxyOptions options;
1237
0
        options.setLazyProto(true);
1238
0
        obj = NewProxyObject(cx,
1239
0
                             &CPOWProxyHandler::singleton,
1240
0
                             v,
1241
0
                             nullptr,
1242
0
                             options);
1243
0
        if (!obj) {
1244
0
            return nullptr;
1245
0
        }
1246
0
1247
0
        if (!cpows_.add(objId, obj)) {
1248
0
            return nullptr;
1249
0
        }
1250
0
1251
0
        nextCPOWNumber_ = objId.serialNumber() + 1;
1252
0
1253
0
        // Incref once we know the decref will be called.
1254
0
        incref();
1255
0
1256
0
        AuxCPOWData* aux = new AuxCPOWData(objId,
1257
0
                                           objVar.isCallable(),
1258
0
                                           objVar.isConstructor(),
1259
0
                                           objVar.isDOMObject(),
1260
0
                                           objVar.objectTag());
1261
0
1262
0
        SetProxyReservedSlot(obj, 0, PrivateValue(this));
1263
0
        SetProxyReservedSlot(obj, 1, PrivateValue(aux));
1264
0
    }
1265
0
1266
0
    if (!JS_WrapObject(cx, &obj)) {
1267
0
        return nullptr;
1268
0
    }
1269
0
    return obj;
1270
0
}
1271
1272
JSObject*
1273
WrapperOwner::fromLocalObjectVariant(JSContext* cx, const LocalObject& objVar)
1274
0
{
1275
0
    Maybe<ObjectId> id(ObjectId::deserialize(objVar.serializedId()));
1276
0
    if (id.isNothing()) {
1277
0
        return nullptr;
1278
0
    }
1279
0
    Rooted<JSObject*> obj(cx, findObjectById(cx, id.value()));
1280
0
    if (!obj) {
1281
0
        return nullptr;
1282
0
    }
1283
0
    if (!JS_WrapObject(cx, &obj)) {
1284
0
        return nullptr;
1285
0
    }
1286
0
    return obj;
1287
0
}