Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/js/ipc/WrapperAnswer.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 "WrapperAnswer.h"
9
#include "JavaScriptLogging.h"
10
#include "mozilla/dom/ContentChild.h"
11
#include "mozilla/dom/BindingUtils.h"
12
#include "mozilla/dom/ScriptSettings.h"
13
#include "xpcprivate.h"
14
#include "js/Class.h"
15
#include "jsfriendapi.h"
16
17
using namespace JS;
18
using namespace mozilla;
19
using namespace mozilla::jsipc;
20
21
// Note - Using AutoJSAPI (rather than AutoEntryScript) for a trap means
22
// that we don't expect it to run script. For most of these traps that will only
23
// happen if the target is a scripted proxy, which is probably something that we
24
// don't want to support over CPOWs. When enough code is fixed up, the long-term
25
// plan is to have the JS engine throw if it encounters script when it isn't
26
// expecting it.
27
using mozilla::dom::AutoJSAPI;
28
using mozilla::dom::AutoEntryScript;
29
30
using xpc::IsInAutomation;
31
32
static void
33
MaybeForceDebugGC()
34
0
{
35
0
    static bool sEnvVarInitialized = false;
36
0
    static bool sDebugGCs = false;
37
0
38
0
    if (!sEnvVarInitialized) {
39
0
        sDebugGCs = !!PR_GetEnv("MOZ_DEBUG_DEAD_CPOWS");
40
0
    }
41
0
42
0
    if (sDebugGCs) {
43
0
        JSContext* cx = XPCJSContext::Get()->Context();
44
0
        PrepareForFullGC(cx);
45
0
        NonIncrementalGC(cx, GC_NORMAL, gcreason::COMPONENT_UTILS);
46
0
    }
47
0
}
48
49
bool
50
WrapperAnswer::fail(AutoJSAPI& jsapi, ReturnStatus* rs)
51
0
{
52
0
    // By default, we set |undefined| unless we can get a more meaningful
53
0
    // exception.
54
0
    *rs = ReturnStatus(ReturnException(JSVariant(UndefinedVariant())));
55
0
56
0
    // Note we always return true from this function, since this propagates
57
0
    // to the IPC code, and we don't want a JS failure to cause the death
58
0
    // of the child process.
59
0
60
0
    JSContext* cx = jsapi.cx();
61
0
    RootedValue exn(cx);
62
0
    if (!jsapi.HasException()) {
63
0
        return true;
64
0
    }
65
0
66
0
    if (!jsapi.StealException(&exn)) {
67
0
        return true;
68
0
    }
69
0
70
0
    // If this fails, we still don't want to exit. Just return an invalid
71
0
    // exception.
72
0
    (void) toVariant(cx, exn, &rs->get_ReturnException().exn());
73
0
    return true;
74
0
}
75
76
bool
77
WrapperAnswer::ok(ReturnStatus* rs)
78
0
{
79
0
    *rs = ReturnStatus(ReturnSuccess());
80
0
    return true;
81
0
}
82
83
bool
84
WrapperAnswer::ok(ReturnStatus* rs, const JS::ObjectOpResult& result)
85
0
{
86
0
    *rs = result
87
0
          ? ReturnStatus(ReturnSuccess())
88
0
          : ReturnStatus(ReturnObjectOpResult(result.failureCode()));
89
0
    return true;
90
0
}
91
92
bool
93
WrapperAnswer::deadCPOW(AutoJSAPI& jsapi, ReturnStatus* rs)
94
0
{
95
0
    JSContext* cx = jsapi.cx();
96
0
    JS_ClearPendingException(cx);
97
0
    *rs = ReturnStatus(ReturnDeadCPOW());
98
0
    return true;
99
0
}
100
101
bool
102
WrapperAnswer::RecvPreventExtensions(const ObjectId& objId, ReturnStatus* rs)
103
0
{
104
0
    if (!IsInAutomation()) {
105
0
        return false;
106
0
    }
107
0
108
0
    MaybeForceDebugGC();
109
0
110
0
    AutoJSAPI jsapi;
111
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
112
0
        return false;
113
0
    }
114
0
    JSContext* cx = jsapi.cx();
115
0
116
0
    RootedObject obj(cx, findObjectById(cx, objId));
117
0
    if (!obj) {
118
0
        return deadCPOW(jsapi, rs);
119
0
    }
120
0
121
0
    ObjectOpResult success;
122
0
    if (!JS_PreventExtensions(cx, obj, success)) {
123
0
        return fail(jsapi, rs);
124
0
    }
125
0
126
0
    LOG("%s.preventExtensions()", ReceiverObj(objId));
127
0
    return ok(rs, success);
128
0
}
129
130
static void
131
EmptyDesc(PPropertyDescriptor* desc)
132
0
{
133
0
    desc->obj() = LocalObject(0);
134
0
    desc->attrs() = 0;
135
0
    desc->value() = UndefinedVariant();
136
0
    desc->getter() = 0;
137
0
    desc->setter() = 0;
138
0
}
139
140
bool
141
WrapperAnswer::RecvGetOwnPropertyDescriptor(const ObjectId& objId, const JSIDVariant& idVar,
142
                                            ReturnStatus* rs, PPropertyDescriptor* out)
143
0
{
144
0
    if (!IsInAutomation()) {
145
0
        return false;
146
0
    }
147
0
148
0
    MaybeForceDebugGC();
149
0
150
0
    AutoJSAPI jsapi;
151
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
152
0
        return false;
153
0
    }
154
0
    JSContext* cx = jsapi.cx();
155
0
    EmptyDesc(out);
156
0
157
0
    RootedObject obj(cx, findObjectById(cx, objId));
158
0
    if (!obj) {
159
0
        return deadCPOW(jsapi, rs);
160
0
    }
161
0
162
0
    LOG("%s.getOwnPropertyDescriptor(%s)", ReceiverObj(objId), Identifier(idVar));
163
0
164
0
    RootedId id(cx);
165
0
    if (!fromJSIDVariant(cx, idVar, &id)) {
166
0
        return fail(jsapi, rs);
167
0
    }
168
0
169
0
    Rooted<PropertyDescriptor> desc(cx);
170
0
    if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) {
171
0
        return fail(jsapi, rs);
172
0
    }
173
0
174
0
    if (!fromDescriptor(cx, desc, out)) {
175
0
        return fail(jsapi, rs);
176
0
    }
177
0
178
0
    return ok(rs);
179
0
}
180
181
bool
182
WrapperAnswer::RecvDefineProperty(const ObjectId& objId, const JSIDVariant& idVar,
183
                                  const PPropertyDescriptor& descriptor, ReturnStatus* rs)
184
0
{
185
0
    if (!IsInAutomation()) {
186
0
        return false;
187
0
    }
188
0
189
0
    MaybeForceDebugGC();
190
0
191
0
    AutoJSAPI jsapi;
192
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
193
0
        return false;
194
0
    }
195
0
    JSContext* cx = jsapi.cx();
196
0
197
0
    RootedObject obj(cx, findObjectById(cx, objId));
198
0
    if (!obj) {
199
0
        return deadCPOW(jsapi, rs);
200
0
    }
201
0
202
0
    LOG("define %s[%s]", ReceiverObj(objId), Identifier(idVar));
203
0
204
0
    RootedId id(cx);
205
0
    if (!fromJSIDVariant(cx, idVar, &id)) {
206
0
        return fail(jsapi, rs);
207
0
    }
208
0
209
0
    Rooted<PropertyDescriptor> desc(cx);
210
0
    if (!toDescriptor(cx, descriptor, &desc)) {
211
0
        return fail(jsapi, rs);
212
0
    }
213
0
214
0
    ObjectOpResult success;
215
0
    if (!JS_DefinePropertyById(cx, obj, id, desc, success)) {
216
0
        return fail(jsapi, rs);
217
0
    }
218
0
    return ok(rs, success);
219
0
}
220
221
bool
222
WrapperAnswer::RecvDelete(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs)
223
0
{
224
0
    if (!IsInAutomation()) {
225
0
        return false;
226
0
    }
227
0
228
0
    MaybeForceDebugGC();
229
0
230
0
    AutoJSAPI jsapi;
231
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
232
0
        return false;
233
0
    }
234
0
    JSContext* cx = jsapi.cx();
235
0
236
0
    RootedObject obj(cx, findObjectById(cx, objId));
237
0
    if (!obj) {
238
0
        return deadCPOW(jsapi, rs);
239
0
    }
240
0
241
0
    LOG("delete %s[%s]", ReceiverObj(objId), Identifier(idVar));
242
0
243
0
    RootedId id(cx);
244
0
    if (!fromJSIDVariant(cx, idVar, &id)) {
245
0
        return fail(jsapi, rs);
246
0
    }
247
0
248
0
    ObjectOpResult success;
249
0
    if (!JS_DeletePropertyById(cx, obj, id, success)) {
250
0
        return fail(jsapi, rs);
251
0
    }
252
0
    return ok(rs, success);
253
0
}
254
255
bool
256
WrapperAnswer::RecvHas(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs,
257
                       bool* foundp)
258
0
{
259
0
    if (!IsInAutomation()) {
260
0
        return false;
261
0
    }
262
0
263
0
    MaybeForceDebugGC();
264
0
265
0
    AutoJSAPI jsapi;
266
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
267
0
        return false;
268
0
    }
269
0
    JSContext* cx = jsapi.cx();
270
0
    *foundp = false;
271
0
272
0
    RootedObject obj(cx, findObjectById(cx, objId));
273
0
    if (!obj) {
274
0
        return deadCPOW(jsapi, rs);
275
0
    }
276
0
277
0
    LOG("%s.has(%s)", ReceiverObj(objId), Identifier(idVar));
278
0
279
0
    RootedId id(cx);
280
0
    if (!fromJSIDVariant(cx, idVar, &id)) {
281
0
        return fail(jsapi, rs);
282
0
    }
283
0
284
0
    if (!JS_HasPropertyById(cx, obj, id, foundp)) {
285
0
        return fail(jsapi, rs);
286
0
    }
287
0
    return ok(rs);
288
0
}
289
290
bool
291
WrapperAnswer::RecvHasOwn(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs,
292
                          bool* foundp)
293
0
{
294
0
    if (!IsInAutomation()) {
295
0
        return false;
296
0
    }
297
0
298
0
    MaybeForceDebugGC();
299
0
300
0
    AutoJSAPI jsapi;
301
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
302
0
        return false;
303
0
    }
304
0
    JSContext* cx = jsapi.cx();
305
0
    *foundp = false;
306
0
307
0
    RootedObject obj(cx, findObjectById(cx, objId));
308
0
    if (!obj) {
309
0
        return deadCPOW(jsapi, rs);
310
0
    }
311
0
312
0
    LOG("%s.hasOwn(%s)", ReceiverObj(objId), Identifier(idVar));
313
0
314
0
    RootedId id(cx);
315
0
    if (!fromJSIDVariant(cx, idVar, &id)) {
316
0
        return fail(jsapi, rs);
317
0
    }
318
0
319
0
    if (!JS_HasOwnPropertyById(cx, obj, id, foundp)) {
320
0
        return fail(jsapi, rs);
321
0
    }
322
0
    return ok(rs);
323
0
}
324
325
bool
326
WrapperAnswer::RecvGet(const ObjectId& objId, const JSVariant& receiverVar,
327
                       const JSIDVariant& idVar, ReturnStatus* rs, JSVariant* result)
328
0
{
329
0
    if (!IsInAutomation()) {
330
0
        return false;
331
0
    }
332
0
333
0
    MaybeForceDebugGC();
334
0
335
0
    // We may run scripted getters.
336
0
    AutoEntryScript aes(scopeForTargetObjects(),
337
0
                        "Cross-Process Object Wrapper 'get'");
338
0
    JSContext* cx = aes.cx();
339
0
340
0
    // The outparam will be written to the buffer, so it must be set even if
341
0
    // the parent won't read it.
342
0
    *result = UndefinedVariant();
343
0
344
0
    RootedObject obj(cx, findObjectById(cx, objId));
345
0
    if (!obj) {
346
0
        return deadCPOW(aes, rs);
347
0
    }
348
0
349
0
    RootedValue receiver(cx);
350
0
    if (!fromVariant(cx, receiverVar, &receiver)) {
351
0
        return fail(aes, rs);
352
0
    }
353
0
354
0
    RootedId id(cx);
355
0
    if (!fromJSIDVariant(cx, idVar, &id)) {
356
0
        return fail(aes, rs);
357
0
    }
358
0
359
0
    JS::RootedValue val(cx);
360
0
    if (!JS_ForwardGetPropertyTo(cx, obj, id, receiver, &val)) {
361
0
        return fail(aes, rs);
362
0
    }
363
0
364
0
    if (!toVariant(cx, val, result)) {
365
0
        return fail(aes, rs);
366
0
    }
367
0
368
0
    LOG("get %s.%s = %s", ReceiverObj(objId), Identifier(idVar), OutVariant(*result));
369
0
370
0
    return ok(rs);
371
0
}
372
373
bool
374
WrapperAnswer::RecvSet(const ObjectId& objId, const JSIDVariant& idVar, const JSVariant& value,
375
                       const JSVariant& receiverVar, ReturnStatus* rs)
376
0
{
377
0
    if (!IsInAutomation()) {
378
0
        return false;
379
0
    }
380
0
381
0
    MaybeForceDebugGC();
382
0
383
0
    // We may run scripted setters.
384
0
    AutoEntryScript aes(scopeForTargetObjects(),
385
0
                        "Cross-Process Object Wrapper 'set'");
386
0
    JSContext* cx = aes.cx();
387
0
388
0
    RootedObject obj(cx, findObjectById(cx, objId));
389
0
    if (!obj) {
390
0
        return deadCPOW(aes, rs);
391
0
    }
392
0
393
0
    LOG("set %s[%s] = %s", ReceiverObj(objId), Identifier(idVar), InVariant(value));
394
0
395
0
    RootedId id(cx);
396
0
    if (!fromJSIDVariant(cx, idVar, &id)) {
397
0
        return fail(aes, rs);
398
0
    }
399
0
400
0
    RootedValue val(cx);
401
0
    if (!fromVariant(cx, value, &val)) {
402
0
        return fail(aes, rs);
403
0
    }
404
0
405
0
    RootedValue receiver(cx);
406
0
    if (!fromVariant(cx, receiverVar, &receiver)) {
407
0
        return fail(aes, rs);
408
0
    }
409
0
410
0
    ObjectOpResult result;
411
0
    if (!JS_ForwardSetPropertyTo(cx, obj, id, val, receiver, result)) {
412
0
        return fail(aes, rs);
413
0
    }
414
0
415
0
    return ok(rs, result);
416
0
}
417
418
bool
419
WrapperAnswer::RecvIsExtensible(const ObjectId& objId, ReturnStatus* rs, bool* result)
420
0
{
421
0
    if (!IsInAutomation()) {
422
0
        return false;
423
0
    }
424
0
425
0
    MaybeForceDebugGC();
426
0
427
0
    AutoJSAPI jsapi;
428
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
429
0
        return false;
430
0
    }
431
0
    JSContext* cx = jsapi.cx();
432
0
    *result = false;
433
0
434
0
    RootedObject obj(cx, findObjectById(cx, objId));
435
0
    if (!obj) {
436
0
        return deadCPOW(jsapi, rs);
437
0
    }
438
0
439
0
    LOG("%s.isExtensible()", ReceiverObj(objId));
440
0
441
0
    bool extensible;
442
0
    if (!JS_IsExtensible(cx, obj, &extensible)) {
443
0
        return fail(jsapi, rs);
444
0
    }
445
0
446
0
    *result = !!extensible;
447
0
    return ok(rs);
448
0
}
449
450
bool
451
WrapperAnswer::RecvCallOrConstruct(const ObjectId& objId,
452
                                   InfallibleTArray<JSParam>&& argv,
453
                                   const bool& construct,
454
                                   ReturnStatus* rs,
455
                                   JSVariant* result,
456
                                   nsTArray<JSParam>* outparams)
457
0
{
458
0
    if (!IsInAutomation()) {
459
0
        return false;
460
0
    }
461
0
462
0
    MaybeForceDebugGC();
463
0
464
0
    AutoEntryScript aes(scopeForTargetObjects(),
465
0
                        "Cross-Process Object Wrapper call/construct");
466
0
    JSContext* cx = aes.cx();
467
0
468
0
    // The outparam will be written to the buffer, so it must be set even if
469
0
    // the parent won't read it.
470
0
    *result = UndefinedVariant();
471
0
472
0
    RootedObject obj(cx, findObjectById(cx, objId));
473
0
    if (!obj) {
474
0
        return deadCPOW(aes, rs);
475
0
    }
476
0
477
0
    MOZ_ASSERT(argv.Length() >= 2);
478
0
479
0
    RootedValue objv(cx);
480
0
    if (!fromVariant(cx, argv[0], &objv)) {
481
0
        return fail(aes, rs);
482
0
    }
483
0
484
0
    *result = JSVariant(UndefinedVariant());
485
0
486
0
    AutoValueVector vals(cx);
487
0
    AutoValueVector outobjects(cx);
488
0
    for (size_t i = 0; i < argv.Length(); i++) {
489
0
        if (argv[i].type() == JSParam::Tvoid_t) {
490
0
            // This is an outparam.
491
0
            RootedObject obj(cx, xpc::NewOutObject(cx));
492
0
            if (!obj) {
493
0
                return fail(aes, rs);
494
0
            }
495
0
            if (!outobjects.append(ObjectValue(*obj))) {
496
0
                return fail(aes, rs);
497
0
            }
498
0
            if (!vals.append(ObjectValue(*obj))) {
499
0
                return fail(aes, rs);
500
0
            }
501
0
        } else {
502
0
            RootedValue v(cx);
503
0
            if (!fromVariant(cx, argv[i].get_JSVariant(), &v)) {
504
0
                return fail(aes, rs);
505
0
            }
506
0
            if (!vals.append(v)) {
507
0
                return fail(aes, rs);
508
0
            }
509
0
        }
510
0
    }
511
0
512
0
    RootedValue rval(cx);
513
0
    {
514
0
        HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2);
515
0
        if (construct) {
516
0
            RootedObject obj(cx);
517
0
            if (!JS::Construct(cx, vals[0], args, &obj)) {
518
0
                return fail(aes, rs);
519
0
            }
520
0
            rval.setObject(*obj);
521
0
        } else {
522
0
            if(!JS::Call(cx, vals[1], vals[0], args, &rval))
523
0
                return fail(aes, rs);
524
0
        }
525
0
    }
526
0
527
0
    if (!toVariant(cx, rval, result)) {
528
0
        return fail(aes, rs);
529
0
    }
530
0
531
0
    // Prefill everything with a dummy jsval.
532
0
    for (size_t i = 0; i < outobjects.length(); i++) {
533
0
        outparams->AppendElement(JSParam(void_t()));
534
0
    }
535
0
536
0
    // Go through each argument that was an outparam, retrieve the "value"
537
0
    // field, and add it to a temporary list. We need to do this separately
538
0
    // because the outparams vector is not rooted.
539
0
    vals.clear();
540
0
    for (size_t i = 0; i < outobjects.length(); i++) {
541
0
        RootedObject obj(cx, &outobjects[i].toObject());
542
0
543
0
        RootedValue v(cx);
544
0
        bool found;
545
0
        if (JS_HasProperty(cx, obj, "value", &found)) {
546
0
            if (!JS_GetProperty(cx, obj, "value", &v)) {
547
0
                return fail(aes, rs);
548
0
            }
549
0
        } else {
550
0
            v = UndefinedValue();
551
0
        }
552
0
        if (!vals.append(v)) {
553
0
            return fail(aes, rs);
554
0
        }
555
0
    }
556
0
557
0
    // Copy the outparams. If any outparam is already set to a void_t, we
558
0
    // treat this as the outparam never having been set.
559
0
    for (size_t i = 0; i < vals.length(); i++) {
560
0
        JSVariant variant;
561
0
        if (!toVariant(cx, vals[i], &variant)) {
562
0
            return fail(aes, rs);
563
0
        }
564
0
        outparams->ReplaceElementAt(i, JSParam(variant));
565
0
    }
566
0
567
0
    LOG("%s.call(%s) = %s", ReceiverObj(objId), argv, OutVariant(*result));
568
0
569
0
    return ok(rs);
570
0
}
571
572
bool
573
WrapperAnswer::RecvHasInstance(const ObjectId& objId, const JSVariant& vVar, ReturnStatus* rs, bool* bp)
574
0
{
575
0
    if (!IsInAutomation()) {
576
0
        return false;
577
0
    }
578
0
579
0
    MaybeForceDebugGC();
580
0
581
0
    AutoJSAPI jsapi;
582
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
583
0
        return false;
584
0
    }
585
0
    JSContext* cx = jsapi.cx();
586
0
587
0
    RootedObject obj(cx, findObjectById(cx, objId));
588
0
    if (!obj) {
589
0
        return deadCPOW(jsapi, rs);
590
0
    }
591
0
592
0
    LOG("%s.hasInstance(%s)", ReceiverObj(objId), InVariant(vVar));
593
0
594
0
    RootedValue val(cx);
595
0
    if (!fromVariant(cx, vVar, &val)) {
596
0
        return fail(jsapi, rs);
597
0
    }
598
0
599
0
    if (!JS_HasInstance(cx, obj, val, bp)) {
600
0
        return fail(jsapi, rs);
601
0
    }
602
0
603
0
    return ok(rs);
604
0
}
605
606
bool
607
WrapperAnswer::RecvGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs,
608
                                   uint32_t* classValue)
609
0
{
610
0
    if (!IsInAutomation()) {
611
0
        return false;
612
0
    }
613
0
614
0
    MaybeForceDebugGC();
615
0
616
0
    *classValue = uint32_t(js::ESClass::Other);
617
0
618
0
    AutoJSAPI jsapi;
619
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
620
0
        return false;
621
0
    }
622
0
    JSContext* cx = jsapi.cx();
623
0
624
0
    RootedObject obj(cx, findObjectById(cx, objId));
625
0
    if (!obj) {
626
0
        return deadCPOW(jsapi, rs);
627
0
    }
628
0
629
0
    LOG("%s.getBuiltinClass()", ReceiverObj(objId));
630
0
631
0
    js::ESClass cls;
632
0
    if (!js::GetBuiltinClass(cx, obj, &cls)) {
633
0
        return fail(jsapi, rs);
634
0
    }
635
0
636
0
    *classValue = uint32_t(cls);
637
0
    return ok(rs);
638
0
}
639
640
bool
641
WrapperAnswer::RecvIsArray(const ObjectId& objId, ReturnStatus* rs,
642
                           uint32_t* ans)
643
0
{
644
0
    if (!IsInAutomation()) {
645
0
        return false;
646
0
    }
647
0
648
0
    MaybeForceDebugGC();
649
0
650
0
    *ans = uint32_t(IsArrayAnswer::NotArray);
651
0
652
0
    AutoJSAPI jsapi;
653
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
654
0
        return false;
655
0
    }
656
0
    JSContext* cx = jsapi.cx();
657
0
658
0
    RootedObject obj(cx, findObjectById(cx, objId));
659
0
    if (!obj) {
660
0
        return deadCPOW(jsapi, rs);
661
0
    }
662
0
663
0
    LOG("%s.isArray()", ReceiverObj(objId));
664
0
665
0
    IsArrayAnswer answer;
666
0
    if (!JS::IsArray(cx, obj, &answer)) {
667
0
        return fail(jsapi, rs);
668
0
    }
669
0
670
0
    *ans = uint32_t(answer);
671
0
    return ok(rs);
672
0
}
673
674
bool
675
WrapperAnswer::RecvClassName(const ObjectId& objId, nsCString* name)
676
0
{
677
0
    if (!IsInAutomation()) {
678
0
        return false;
679
0
    }
680
0
681
0
    MaybeForceDebugGC();
682
0
683
0
    AutoJSAPI jsapi;
684
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
685
0
        return false;
686
0
    }
687
0
    JSContext* cx = jsapi.cx();
688
0
689
0
    RootedObject obj(cx, findObjectById(cx, objId));
690
0
    if (!obj) {
691
0
        // This is very unfortunate, but we have no choice.
692
0
        *name = "<dead CPOW>";
693
0
        return true;
694
0
    }
695
0
696
0
    LOG("%s.className()", ReceiverObj(objId));
697
0
698
0
    *name = js::ObjectClassName(cx, obj);
699
0
    return true;
700
0
}
701
702
bool
703
WrapperAnswer::RecvGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result)
704
0
{
705
0
    if (!IsInAutomation()) {
706
0
        return false;
707
0
    }
708
0
709
0
    MaybeForceDebugGC();
710
0
711
0
    *result = NullVariant();
712
0
713
0
    AutoJSAPI jsapi;
714
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
715
0
        return false;
716
0
    }
717
0
    JSContext* cx = jsapi.cx();
718
0
719
0
    RootedObject obj(cx, findObjectById(cx, objId));
720
0
    if (!obj) {
721
0
        return deadCPOW(jsapi, rs);
722
0
    }
723
0
724
0
    JS::RootedObject proto(cx);
725
0
    if (!JS_GetPrototype(cx, obj, &proto)) {
726
0
        return fail(jsapi, rs);
727
0
    }
728
0
729
0
    if (!toObjectOrNullVariant(cx, proto, result)) {
730
0
        return fail(jsapi, rs);
731
0
    }
732
0
733
0
    LOG("getPrototype(%s)", ReceiverObj(objId));
734
0
735
0
    return ok(rs);
736
0
}
737
738
bool
739
WrapperAnswer::RecvGetPrototypeIfOrdinary(const ObjectId& objId, ReturnStatus* rs, bool* isOrdinary,
740
                                          ObjectOrNullVariant* result)
741
0
{
742
0
    if (!IsInAutomation()) {
743
0
        return false;
744
0
    }
745
0
746
0
    MaybeForceDebugGC();
747
0
748
0
    *result = NullVariant();
749
0
    *isOrdinary = false;
750
0
751
0
    AutoJSAPI jsapi;
752
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
753
0
        return false;
754
0
    }
755
0
    JSContext* cx = jsapi.cx();
756
0
757
0
    RootedObject obj(cx, findObjectById(cx, objId));
758
0
    if (!obj) {
759
0
        return deadCPOW(jsapi, rs);
760
0
    }
761
0
762
0
    JS::RootedObject proto(cx);
763
0
    if (!JS_GetPrototypeIfOrdinary(cx, obj, isOrdinary, &proto)) {
764
0
        return fail(jsapi, rs);
765
0
    }
766
0
767
0
    if (!toObjectOrNullVariant(cx, proto, result)) {
768
0
        return fail(jsapi, rs);
769
0
    }
770
0
771
0
    LOG("getPrototypeIfOrdinary(%s)", ReceiverObj(objId));
772
0
773
0
    return ok(rs);
774
0
}
775
776
bool
777
WrapperAnswer::RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs,
778
                                  nsString* source, uint32_t* flags)
779
0
{
780
0
    if (!IsInAutomation()) {
781
0
        return false;
782
0
    }
783
0
784
0
    MaybeForceDebugGC();
785
0
786
0
    AutoJSAPI jsapi;
787
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
788
0
        return false;
789
0
    }
790
0
    JSContext* cx = jsapi.cx();
791
0
792
0
    RootedObject obj(cx, findObjectById(cx, objId));
793
0
    if (!obj) {
794
0
        return deadCPOW(jsapi, rs);
795
0
    }
796
0
797
0
    RootedString sourceJSStr(cx, JS_GetRegExpSource(cx, obj));
798
0
    if (!sourceJSStr) {
799
0
        return fail(jsapi, rs);
800
0
    }
801
0
    nsAutoJSString sourceStr;
802
0
    if (!sourceStr.init(cx, sourceJSStr)) {
803
0
        return fail(jsapi, rs);
804
0
    }
805
0
    source->Assign(sourceStr);
806
0
807
0
    *flags = JS_GetRegExpFlags(cx, obj);
808
0
809
0
    return ok(rs);
810
0
}
811
812
bool
813
WrapperAnswer::RecvGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
814
                                   ReturnStatus* rs, nsTArray<JSIDVariant>* ids)
815
0
{
816
0
    if (!IsInAutomation()) {
817
0
        return false;
818
0
    }
819
0
820
0
    MaybeForceDebugGC();
821
0
822
0
    AutoJSAPI jsapi;
823
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
824
0
        return false;
825
0
    }
826
0
    JSContext* cx = jsapi.cx();
827
0
828
0
    RootedObject obj(cx, findObjectById(cx, objId));
829
0
    if (!obj) {
830
0
        return deadCPOW(jsapi, rs);
831
0
    }
832
0
833
0
    LOG("%s.getPropertyKeys()", ReceiverObj(objId));
834
0
835
0
    AutoIdVector props(cx);
836
0
    if (!js::GetPropertyKeys(cx, obj, flags, &props)) {
837
0
        return fail(jsapi, rs);
838
0
    }
839
0
840
0
    for (size_t i = 0; i < props.length(); i++) {
841
0
        JSIDVariant id;
842
0
        if (!toJSIDVariant(cx, props[i], &id)) {
843
0
            return fail(jsapi, rs);
844
0
        }
845
0
846
0
        ids->AppendElement(id);
847
0
    }
848
0
849
0
    return ok(rs);
850
0
}
851
852
bool
853
WrapperAnswer::RecvInstanceOf(const ObjectId& objId, const JSIID& iid, ReturnStatus* rs,
854
                              bool* instanceof)
855
0
{
856
0
    if (!IsInAutomation()) {
857
0
        return false;
858
0
    }
859
0
860
0
    MaybeForceDebugGC();
861
0
862
0
    AutoJSAPI jsapi;
863
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
864
0
        return false;
865
0
    }
866
0
    JSContext* cx = jsapi.cx();
867
0
868
0
    *instanceof = false;
869
0
870
0
    RootedObject obj(cx, findObjectById(cx, objId));
871
0
    if (!obj) {
872
0
        return deadCPOW(jsapi, rs);
873
0
    }
874
0
875
0
    LOG("%s.instanceOf()", ReceiverObj(objId));
876
0
877
0
    nsID nsiid;
878
0
    ConvertID(iid, &nsiid);
879
0
880
0
    nsresult rv = xpc::HasInstance(cx, obj, &nsiid, instanceof);
881
0
    if (rv != NS_OK) {
882
0
        return fail(jsapi, rs);
883
0
    }
884
0
885
0
    return ok(rs);
886
0
}
887
888
bool
889
WrapperAnswer::RecvDOMInstanceOf(const ObjectId& objId, const int& prototypeID,
890
                                 const int& depth, ReturnStatus* rs, bool* instanceof)
891
0
{
892
0
    if (!IsInAutomation()) {
893
0
        return false;
894
0
    }
895
0
896
0
    MaybeForceDebugGC();
897
0
898
0
    AutoJSAPI jsapi;
899
0
    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects()))) {
900
0
        return false;
901
0
    }
902
0
    JSContext* cx = jsapi.cx();
903
0
    *instanceof = false;
904
0
905
0
    RootedObject obj(cx, findObjectById(cx, objId));
906
0
    if (!obj) {
907
0
        return deadCPOW(jsapi, rs);
908
0
    }
909
0
910
0
    LOG("%s.domInstanceOf()", ReceiverObj(objId));
911
0
912
0
    bool tmp;
913
0
    if (!mozilla::dom::InterfaceHasInstance(cx, prototypeID, depth, obj, &tmp)) {
914
0
        return fail(jsapi, rs);
915
0
    }
916
0
    *instanceof = tmp;
917
0
918
0
    return ok(rs);
919
0
}
920
921
bool
922
WrapperAnswer::RecvDropObject(const ObjectId& objId)
923
0
{
924
0
    JSObject* obj = objects_.findPreserveColor(objId);
925
0
    if (obj) {
926
0
        objectIdMap(objId.hasXrayWaiver()).remove(obj);
927
0
        objects_.remove(objId);
928
0
    }
929
0
    return true;
930
0
}