js/ipc/JavaScriptChild.cpp

branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
equal deleted inserted replaced
-1:000000000000 0:b02161c8724c
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 "JavaScriptChild.h"
9 #include "mozilla/dom/ContentChild.h"
10 #include "mozilla/dom/BindingUtils.h"
11 #include "nsContentUtils.h"
12 #include "xpcprivate.h"
13 #include "jsfriendapi.h"
14 #include "nsCxPusher.h"
15
16 using namespace JS;
17 using namespace mozilla;
18 using namespace mozilla::jsipc;
19
20 using mozilla::AutoSafeJSContext;
21
22 JavaScriptChild::JavaScriptChild(JSRuntime *rt)
23 : lastId_(0),
24 rt_(rt)
25 {
26 }
27
28 static void
29 Trace(JSTracer *trc, void *data)
30 {
31 reinterpret_cast<JavaScriptChild *>(data)->trace(trc);
32 }
33
34 JavaScriptChild::~JavaScriptChild()
35 {
36 JS_RemoveExtraGCRootsTracer(rt_, Trace, this);
37 }
38
39 void
40 JavaScriptChild::trace(JSTracer *trc)
41 {
42 objects_.trace(trc);
43 ids_.trace(trc);
44 }
45
46 bool
47 JavaScriptChild::init()
48 {
49 if (!JavaScriptShared::init())
50 return false;
51 if (!ids_.init())
52 return false;
53
54 JS_AddExtraGCRootsTracer(rt_, Trace, this);
55 return true;
56 }
57
58 bool
59 JavaScriptChild::RecvDropObject(const ObjectId &objId)
60 {
61 JSObject *obj = findObject(objId);
62 if (obj) {
63 ids_.remove(obj);
64 objects_.remove(objId);
65 }
66 return true;
67 }
68
69 bool
70 JavaScriptChild::makeId(JSContext *cx, JSObject *obj, ObjectId *idp)
71 {
72 if (!obj) {
73 *idp = 0;
74 return true;
75 }
76
77 ObjectId id = ids_.find(obj);
78 if (id) {
79 *idp = id;
80 return true;
81 }
82
83 id = ++lastId_;
84 if (id > MAX_CPOW_IDS) {
85 JS_ReportError(cx, "CPOW id limit reached");
86 return false;
87 }
88
89 id <<= OBJECT_EXTRA_BITS;
90 if (JS_ObjectIsCallable(cx, obj))
91 id |= OBJECT_IS_CALLABLE;
92
93 if (!objects_.add(id, obj))
94 return false;
95 if (!ids_.add(cx, obj, id))
96 return false;
97
98 *idp = id;
99 return true;
100 }
101
102 JSObject *
103 JavaScriptChild::unwrap(JSContext *cx, ObjectId id)
104 {
105 JSObject *obj = findObject(id);
106 MOZ_ASSERT(obj);
107 return obj;
108 }
109
110 bool
111 JavaScriptChild::fail(JSContext *cx, ReturnStatus *rs)
112 {
113 // By default, we set |undefined| unless we can get a more meaningful
114 // exception.
115 *rs = ReturnStatus(ReturnException(JSVariant(void_t())));
116
117 // Note we always return true from this function, since this propagates
118 // to the IPC code, and we don't want a JS failure to cause the death
119 // of the child process.
120
121 RootedValue exn(cx);
122 if (!JS_GetPendingException(cx, &exn))
123 return true;
124
125 // If we don't clear the pending exception, JS will try to wrap it as it
126 // leaves the current compartment. Since there is no previous compartment,
127 // that would crash.
128 JS_ClearPendingException(cx);
129
130 if (JS_IsStopIteration(exn)) {
131 *rs = ReturnStatus(ReturnStopIteration());
132 return true;
133 }
134
135 // If this fails, we still don't want to exit. Just return an invalid
136 // exception.
137 (void) toVariant(cx, exn, &rs->get_ReturnException().exn());
138 return true;
139 }
140
141 bool
142 JavaScriptChild::ok(ReturnStatus *rs)
143 {
144 *rs = ReturnStatus(ReturnSuccess());
145 return true;
146 }
147
148 bool
149 JavaScriptChild::AnswerPreventExtensions(const ObjectId &objId, ReturnStatus *rs)
150 {
151 AutoSafeJSContext cx;
152 JSAutoRequest request(cx);
153
154 RootedObject obj(cx, findObject(objId));
155 if (!obj)
156 return false;
157
158 JSAutoCompartment comp(cx, obj);
159 if (!JS_PreventExtensions(cx, obj))
160 return fail(cx, rs);
161
162 return ok(rs);
163 }
164
165 static void
166 EmptyDesc(PPropertyDescriptor *desc)
167 {
168 desc->objId() = 0;
169 desc->attrs() = 0;
170 desc->value() = void_t();
171 desc->getter() = 0;
172 desc->setter() = 0;
173 }
174
175 bool
176 JavaScriptChild::AnswerGetPropertyDescriptor(const ObjectId &objId, const nsString &id,
177 ReturnStatus *rs, PPropertyDescriptor *out)
178 {
179 AutoSafeJSContext cx;
180 JSAutoRequest request(cx);
181
182 EmptyDesc(out);
183
184 RootedObject obj(cx, findObject(objId));
185 if (!obj)
186 return false;
187
188 JSAutoCompartment comp(cx, obj);
189
190 RootedId internedId(cx);
191 if (!convertGeckoStringToId(cx, id, &internedId))
192 return fail(cx, rs);
193
194 Rooted<JSPropertyDescriptor> desc(cx);
195 if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
196 return fail(cx, rs);
197
198 if (!desc.object())
199 return ok(rs);
200
201 if (!fromDescriptor(cx, desc, out))
202 return fail(cx, rs);
203
204 return ok(rs);
205 }
206
207 bool
208 JavaScriptChild::AnswerGetOwnPropertyDescriptor(const ObjectId &objId, const nsString &id,
209 ReturnStatus *rs, PPropertyDescriptor *out)
210 {
211 AutoSafeJSContext cx;
212 JSAutoRequest request(cx);
213
214 EmptyDesc(out);
215
216 RootedObject obj(cx, findObject(objId));
217 if (!obj)
218 return false;
219
220 JSAutoCompartment comp(cx, obj);
221
222 RootedId internedId(cx);
223 if (!convertGeckoStringToId(cx, id, &internedId))
224 return fail(cx, rs);
225
226 Rooted<JSPropertyDescriptor> desc(cx);
227 if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
228 return fail(cx, rs);
229
230 if (desc.object() != obj)
231 return ok(rs);
232
233 if (!fromDescriptor(cx, desc, out))
234 return fail(cx, rs);
235
236 return ok(rs);
237 }
238
239 bool
240 JavaScriptChild::AnswerDefineProperty(const ObjectId &objId, const nsString &id,
241 const PPropertyDescriptor &descriptor, ReturnStatus *rs)
242 {
243 AutoSafeJSContext cx;
244 JSAutoRequest request(cx);
245
246 RootedObject obj(cx, findObject(objId));
247 if (!obj)
248 return false;
249
250 JSAutoCompartment comp(cx, obj);
251
252 RootedId internedId(cx);
253 if (!convertGeckoStringToId(cx, id, &internedId))
254 return fail(cx, rs);
255
256 Rooted<JSPropertyDescriptor> desc(cx);
257 if (!toDescriptor(cx, descriptor, &desc))
258 return false;
259
260 if (!js::CheckDefineProperty(cx, obj, internedId, desc.value(), desc.getter(),
261 desc.setter(), desc.attributes()))
262 {
263 return fail(cx, rs);
264 }
265
266 if (!JS_DefinePropertyById(cx, obj, internedId, desc.value(), desc.getter(),
267 desc.setter(), desc.attributes()))
268 {
269 return fail(cx, rs);
270 }
271
272 return ok(rs);
273 }
274
275 bool
276 JavaScriptChild::AnswerDelete(const ObjectId &objId, const nsString &id, ReturnStatus *rs,
277 bool *success)
278 {
279 AutoSafeJSContext cx;
280 JSAutoRequest request(cx);
281
282 *success = false;
283
284 RootedObject obj(cx, findObject(objId));
285 if (!obj)
286 return false;
287
288 JSAutoCompartment comp(cx, obj);
289
290 RootedId internedId(cx);
291 if (!convertGeckoStringToId(cx, id, &internedId))
292 return fail(cx, rs);
293
294 if (!JS_DeletePropertyById2(cx, obj, internedId, success))
295 return fail(cx, rs);
296
297 return ok(rs);
298 }
299
300 bool
301 JavaScriptChild::AnswerHas(const ObjectId &objId, const nsString &id, ReturnStatus *rs, bool *bp)
302 {
303 AutoSafeJSContext cx;
304 JSAutoRequest request(cx);
305
306 *bp = false;
307
308 RootedObject obj(cx, findObject(objId));
309 if (!obj)
310 return false;
311
312 JSAutoCompartment comp(cx, obj);
313
314 RootedId internedId(cx);
315 if (!convertGeckoStringToId(cx, id, &internedId))
316 return fail(cx, rs);
317
318 bool found;
319 if (!JS_HasPropertyById(cx, obj, internedId, &found))
320 return fail(cx, rs);
321 *bp = !!found;
322
323 return ok(rs);
324 }
325
326 bool
327 JavaScriptChild::AnswerHasOwn(const ObjectId &objId, const nsString &id, ReturnStatus *rs, bool *bp)
328 {
329 AutoSafeJSContext cx;
330 JSAutoRequest request(cx);
331
332 *bp = false;
333
334 RootedObject obj(cx, findObject(objId));
335 if (!obj)
336 return false;
337
338 JSAutoCompartment comp(cx, obj);
339
340 RootedId internedId(cx);
341 if (!convertGeckoStringToId(cx, id, &internedId))
342 return fail(cx, rs);
343
344 Rooted<JSPropertyDescriptor> desc(cx);
345 if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
346 return fail(cx, rs);
347 *bp = (desc.object() == obj);
348
349 return ok(rs);
350 }
351
352 bool
353 JavaScriptChild::AnswerGet(const ObjectId &objId, const ObjectId &receiverId, const nsString &id,
354 ReturnStatus *rs, JSVariant *result)
355 {
356 AutoSafeJSContext cx;
357 JSAutoRequest request(cx);
358
359 // The outparam will be written to the buffer, so it must be set even if
360 // the parent won't read it.
361 *result = void_t();
362
363 RootedObject obj(cx, findObject(objId));
364 if (!obj)
365 return false;
366
367 RootedObject receiver(cx, findObject(receiverId));
368 if (!receiver)
369 return false;
370
371 JSAutoCompartment comp(cx, obj);
372
373 RootedId internedId(cx);
374 if (!convertGeckoStringToId(cx, id, &internedId))
375 return fail(cx, rs);
376
377 JS::RootedValue val(cx);
378 if (!JS_ForwardGetPropertyTo(cx, obj, internedId, receiver, &val))
379 return fail(cx, rs);
380
381 if (!toVariant(cx, val, result))
382 return fail(cx, rs);
383
384 return ok(rs);
385 }
386
387 bool
388 JavaScriptChild::AnswerSet(const ObjectId &objId, const ObjectId &receiverId, const nsString &id,
389 const bool &strict, const JSVariant &value, ReturnStatus *rs,
390 JSVariant *result)
391 {
392 AutoSafeJSContext cx;
393 JSAutoRequest request(cx);
394
395 // The outparam will be written to the buffer, so it must be set even if
396 // the parent won't read it.
397 *result = void_t();
398
399 RootedObject obj(cx, findObject(objId));
400 if (!obj)
401 return false;
402
403 RootedObject receiver(cx, findObject(receiverId));
404 if (!receiver)
405 return false;
406
407 JSAutoCompartment comp(cx, obj);
408
409 RootedId internedId(cx);
410 if (!convertGeckoStringToId(cx, id, &internedId))
411 return fail(cx, rs);
412
413 MOZ_ASSERT(obj == receiver);
414
415 RootedValue val(cx);
416 if (!toValue(cx, value, &val))
417 return fail(cx, rs);
418
419 if (!JS_SetPropertyById(cx, obj, internedId, val))
420 return fail(cx, rs);
421
422 if (!toVariant(cx, val, result))
423 return fail(cx, rs);
424
425 return ok(rs);
426 }
427
428 bool
429 JavaScriptChild::AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs, bool *result)
430 {
431 AutoSafeJSContext cx;
432 JSAutoRequest request(cx);
433
434 *result = false;
435
436 RootedObject obj(cx, findObject(objId));
437 if (!obj)
438 return false;
439
440 JSAutoCompartment comp(cx, obj);
441
442 bool extensible;
443 if (!JS_IsExtensible(cx, obj, &extensible))
444 return fail(cx, rs);
445
446 *result = !!extensible;
447 return ok(rs);
448 }
449
450 bool
451 JavaScriptChild::AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv, ReturnStatus *rs,
452 JSVariant *result, nsTArray<JSParam> *outparams)
453 {
454 AutoSafeJSContext cx;
455 JSAutoRequest request(cx);
456
457 // The outparam will be written to the buffer, so it must be set even if
458 // the parent won't read it.
459 *result = void_t();
460
461 RootedObject obj(cx, findObject(objId));
462 if (!obj)
463 return false;
464
465 MOZ_ASSERT(argv.Length() >= 2);
466
467 RootedValue objv(cx);
468 if (!toValue(cx, argv[0], &objv))
469 return fail(cx, rs);
470
471 JSAutoCompartment comp(cx, &objv.toObject());
472
473 *result = JSVariant(void_t());
474
475 AutoValueVector vals(cx);
476 AutoValueVector outobjects(cx);
477 for (size_t i = 0; i < argv.Length(); i++) {
478 if (argv[i].type() == JSParam::Tvoid_t) {
479 // This is an outparam.
480 JSCompartment *compartment = js::GetContextCompartment(cx);
481 RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, compartment));
482 RootedObject obj(cx, xpc::NewOutObject(cx, global));
483 if (!obj)
484 return fail(cx, rs);
485 if (!outobjects.append(ObjectValue(*obj)))
486 return fail(cx, rs);
487 if (!vals.append(ObjectValue(*obj)))
488 return fail(cx, rs);
489 } else {
490 RootedValue v(cx);
491 if (!toValue(cx, argv[i].get_JSVariant(), &v))
492 return fail(cx, rs);
493 if (!vals.append(v))
494 return fail(cx, rs);
495 }
496 }
497
498 RootedValue rval(cx);
499 {
500 AutoSaveContextOptions asco(cx);
501 ContextOptionsRef(cx).setDontReportUncaught(true);
502
503 HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2);
504 bool success = JS::Call(cx, vals.handleAt(1), vals.handleAt(0), args, &rval);
505 if (!success)
506 return fail(cx, rs);
507 }
508
509 if (!toVariant(cx, rval, result))
510 return fail(cx, rs);
511
512 // Prefill everything with a dummy jsval.
513 for (size_t i = 0; i < outobjects.length(); i++)
514 outparams->AppendElement(JSParam(void_t()));
515
516 // Go through each argument that was an outparam, retrieve the "value"
517 // field, and add it to a temporary list. We need to do this separately
518 // because the outparams vector is not rooted.
519 vals.clear();
520 for (size_t i = 0; i < outobjects.length(); i++) {
521 RootedObject obj(cx, &outobjects[i].toObject());
522
523 RootedValue v(cx);
524 bool found;
525 if (JS_HasProperty(cx, obj, "value", &found)) {
526 if (!JS_GetProperty(cx, obj, "value", &v))
527 return fail(cx, rs);
528 } else {
529 v = UndefinedValue();
530 }
531 if (!vals.append(v))
532 return fail(cx, rs);
533 }
534
535 // Copy the outparams. If any outparam is already set to a void_t, we
536 // treat this as the outparam never having been set.
537 for (size_t i = 0; i < vals.length(); i++) {
538 JSVariant variant;
539 if (!toVariant(cx, vals.handleAt(i), &variant))
540 return fail(cx, rs);
541 outparams->ReplaceElementAt(i, JSParam(variant));
542 }
543
544 return ok(rs);
545 }
546
547 bool
548 JavaScriptChild::AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
549 bool *result)
550 {
551 AutoSafeJSContext cx;
552 JSAutoRequest request(cx);
553
554 RootedObject obj(cx, findObject(objId));
555 if (!obj)
556 return false;
557
558 JSAutoCompartment comp(cx, obj);
559
560 *result = js_ObjectClassIs(cx, obj, (js::ESClassValue)classValue);
561 return true;
562 }
563
564 bool
565 JavaScriptChild::AnswerClassName(const ObjectId &objId, nsString *name)
566 {
567 AutoSafeJSContext cx;
568 JSAutoRequest request(cx);
569
570 RootedObject obj(cx, findObject(objId));
571 if (!obj)
572 return false;
573
574 JSAutoCompartment comp(cx, obj);
575
576 *name = NS_ConvertASCIItoUTF16(js_ObjectClassName(cx, obj));
577 return true;
578 }
579
580 bool
581 JavaScriptChild::AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
582 ReturnStatus *rs, nsTArray<nsString> *names)
583 {
584 AutoSafeJSContext cx;
585 JSAutoRequest request(cx);
586
587 RootedObject obj(cx, findObject(objId));
588 if (!obj)
589 return false;
590
591 JSAutoCompartment comp(cx, obj);
592
593 AutoIdVector props(cx);
594 if (!js::GetPropertyNames(cx, obj, flags, &props))
595 return fail(cx, rs);
596
597 for (size_t i = 0; i < props.length(); i++) {
598 nsString name;
599 if (!convertIdToGeckoString(cx, props.handleAt(i), &name))
600 return fail(cx, rs);
601
602 names->AppendElement(name);
603 }
604
605 return ok(rs);
606 }
607
608 bool
609 JavaScriptChild::AnswerInstanceOf(const ObjectId &objId, const JSIID &iid, ReturnStatus *rs,
610 bool *instanceof)
611 {
612 AutoSafeJSContext cx;
613 JSAutoRequest request(cx);
614
615 *instanceof = false;
616
617 RootedObject obj(cx, findObject(objId));
618 if (!obj)
619 return false;
620
621 JSAutoCompartment comp(cx, obj);
622
623 nsID nsiid;
624 ConvertID(iid, &nsiid);
625
626 nsresult rv = xpc::HasInstance(cx, obj, &nsiid, instanceof);
627 if (rv != NS_OK)
628 return fail(cx, rs);
629
630 return ok(rs);
631 }
632
633 bool
634 JavaScriptChild::AnswerDOMInstanceOf(const ObjectId &objId, const int &prototypeID,
635 const int &depth,
636 ReturnStatus *rs, bool *instanceof)
637 {
638 AutoSafeJSContext cx;
639 JSAutoRequest request(cx);
640
641 *instanceof = false;
642
643 RootedObject obj(cx, findObject(objId));
644 if (!obj)
645 return false;
646
647 JSAutoCompartment comp(cx, obj);
648
649 bool tmp;
650 if (!mozilla::dom::InterfaceHasInstance(cx, prototypeID, depth, obj, &tmp))
651 return fail(cx, rs);
652 *instanceof = tmp;
653
654 return ok(rs);
655 }

mercurial