js/ipc/JavaScriptParent.cpp

branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
equal deleted inserted replaced
-1:000000000000 0:18db85a9eef1
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 "JavaScriptParent.h"
9 #include "mozilla/dom/ContentParent.h"
10 #include "nsJSUtils.h"
11 #include "jsfriendapi.h"
12 #include "jsproxy.h"
13 #include "jswrapper.h"
14 #include "HeapAPI.h"
15 #include "xpcprivate.h"
16 #include "mozilla/Casting.h"
17
18 using namespace js;
19 using namespace JS;
20 using namespace mozilla;
21 using namespace mozilla::jsipc;
22 using namespace mozilla::dom;
23
24 JavaScriptParent::JavaScriptParent()
25 : refcount_(1),
26 inactive_(false)
27 {
28 }
29
30 static inline JavaScriptParent *
31 ParentOf(JSObject *obj)
32 {
33 MOZ_ASSERT(JavaScriptParent::IsCPOW(obj));
34 return reinterpret_cast<JavaScriptParent *>(GetProxyExtra(obj, 0).toPrivate());
35 }
36
37 ObjectId
38 JavaScriptParent::idOf(JSObject *obj)
39 {
40 MOZ_ASSERT(JavaScriptParent::IsCPOW(obj));
41
42 Value v = GetProxyExtra(obj, 1);
43 MOZ_ASSERT(v.isDouble());
44
45 ObjectId objId = BitwiseCast<uint64_t>(v.toDouble());
46 MOZ_ASSERT(findObject(objId) == obj);
47 MOZ_ASSERT(objId);
48
49 return objId;
50 }
51
52 int sCPOWProxyHandler;
53
54 class CPOWProxyHandler : public BaseProxyHandler
55 {
56 public:
57 CPOWProxyHandler()
58 : BaseProxyHandler(&sCPOWProxyHandler) {}
59 virtual ~CPOWProxyHandler() {}
60
61 virtual bool finalizeInBackground(Value priv) MOZ_OVERRIDE {
62 return false;
63 }
64
65 virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
66 virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
67 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
68 virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
69 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
70 virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
71 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
72 virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy,
73 AutoIdVector &props) MOZ_OVERRIDE;
74 virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
75 virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
76
77 virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
78 virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE;
79 virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
80 HandleId id, MutableHandleValue vp) MOZ_OVERRIDE;
81 virtual bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
82 JS::HandleId id, bool strict, JS::MutableHandleValue vp) MOZ_OVERRIDE;
83 virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
84
85 virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
86 virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
87 virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE;
88 virtual const char* className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
89 virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE;
90
91 static CPOWProxyHandler singleton;
92 };
93
94 CPOWProxyHandler CPOWProxyHandler::singleton;
95
96 #define FORWARD(call, args) \
97 JavaScriptParent *parent = ParentOf(proxy); \
98 if (!parent->active()) { \
99 JS_ReportError(cx, "cannot use a CPOW whose process is gone"); \
100 return false; \
101 } \
102 return parent->call args;
103
104 bool
105 CPOWProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy)
106 {
107 FORWARD(preventExtensions, (cx, proxy));
108 }
109
110 bool
111 JavaScriptParent::preventExtensions(JSContext *cx, HandleObject proxy)
112 {
113 ObjectId objId = idOf(proxy);
114
115 ReturnStatus status;
116 if (!CallPreventExtensions(objId, &status))
117 return ipcfail(cx);
118
119 return ok(cx, status);
120 }
121
122 bool
123 CPOWProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
124 MutableHandle<JSPropertyDescriptor> desc)
125 {
126 FORWARD(getPropertyDescriptor, (cx, proxy, id, desc));
127 }
128
129 bool
130 JavaScriptParent::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
131 MutableHandle<JSPropertyDescriptor> desc)
132 {
133 ObjectId objId = idOf(proxy);
134
135 nsString idstr;
136 if (!convertIdToGeckoString(cx, id, &idstr))
137 return false;
138
139 ReturnStatus status;
140 PPropertyDescriptor result;
141 if (!CallGetPropertyDescriptor(objId, idstr, &status, &result))
142 return ipcfail(cx);
143 if (!ok(cx, status))
144 return false;
145
146 return toDescriptor(cx, result, desc);
147 }
148
149 bool
150 CPOWProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy,
151 HandleId id, MutableHandle<JSPropertyDescriptor> desc)
152 {
153 FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc));
154 }
155
156 bool
157 JavaScriptParent::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
158 MutableHandle<JSPropertyDescriptor> desc)
159 {
160 ObjectId objId = idOf(proxy);
161
162 nsString idstr;
163 if (!convertIdToGeckoString(cx, id, &idstr))
164 return false;
165
166 ReturnStatus status;
167 PPropertyDescriptor result;
168 if (!CallGetOwnPropertyDescriptor(objId, idstr, &status, &result))
169 return ipcfail(cx);
170 if (!ok(cx, status))
171 return false;
172
173 return toDescriptor(cx, result, desc);
174 }
175
176 bool
177 CPOWProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
178 MutableHandle<JSPropertyDescriptor> desc)
179 {
180 FORWARD(defineProperty, (cx, proxy, id, desc));
181 }
182
183 bool
184 JavaScriptParent::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
185 MutableHandle<JSPropertyDescriptor> desc)
186 {
187 ObjectId objId = idOf(proxy);
188
189 nsString idstr;
190 if (!convertIdToGeckoString(cx, id, &idstr))
191 return false;
192
193 PPropertyDescriptor descriptor;
194 if (!fromDescriptor(cx, desc, &descriptor))
195 return false;
196
197 ReturnStatus status;
198 if (!CallDefineProperty(objId, idstr, descriptor, &status))
199 return ipcfail(cx);
200
201 return ok(cx, status);
202 }
203
204 bool
205 CPOWProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props)
206 {
207 FORWARD(getOwnPropertyNames, (cx, proxy, props));
208 }
209
210 bool
211 JavaScriptParent::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props)
212 {
213 return getPropertyNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN, props);
214 }
215
216 bool
217 CPOWProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
218 {
219 FORWARD(delete_, (cx, proxy, id, bp));
220 }
221
222 bool
223 JavaScriptParent::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
224 {
225 ObjectId objId = idOf(proxy);
226
227 nsString idstr;
228 if (!convertIdToGeckoString(cx, id, &idstr))
229 return false;
230
231 ReturnStatus status;
232 if (!CallDelete(objId, idstr, &status, bp))
233 return ipcfail(cx);
234
235 return ok(cx, status);
236 }
237
238 bool
239 CPOWProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
240 {
241 FORWARD(enumerate, (cx, proxy, props));
242 }
243
244 bool
245 JavaScriptParent::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
246 {
247 return getPropertyNames(cx, proxy, 0, props);
248 }
249
250 bool
251 CPOWProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
252 {
253 FORWARD(has, (cx, proxy, id, bp));
254 }
255
256 bool
257 JavaScriptParent::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
258 {
259 ObjectId objId = idOf(proxy);
260
261 nsString idstr;
262 if (!convertIdToGeckoString(cx, id, &idstr))
263 return false;
264
265 ReturnStatus status;
266 if (!CallHas(objId, idstr, &status, bp))
267 return ipcfail(cx);
268
269 return ok(cx, status);
270 }
271
272 bool
273 CPOWProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
274 {
275 FORWARD(hasOwn, (cx, proxy, id, bp));
276 }
277
278 bool
279 JavaScriptParent::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
280 {
281 ObjectId objId = idOf(proxy);
282
283 nsString idstr;
284 if (!convertIdToGeckoString(cx, id, &idstr))
285 return false;
286
287 ReturnStatus status;
288 if (!CallHasOwn(objId, idstr, &status, bp))
289 return ipcfail(cx);
290
291 return !!ok(cx, status);
292 }
293
294 bool
295 CPOWProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
296 HandleId id, MutableHandleValue vp)
297 {
298 FORWARD(get, (cx, proxy, receiver, id, vp));
299 }
300
301 bool
302 JavaScriptParent::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
303 HandleId id, MutableHandleValue vp)
304 {
305 ObjectId objId = idOf(proxy);
306 ObjectId receiverId = idOf(receiver);
307
308 nsString idstr;
309 if (!convertIdToGeckoString(cx, id, &idstr))
310 return false;
311
312 JSVariant val;
313 ReturnStatus status;
314 if (!CallGet(objId, receiverId, idstr, &status, &val))
315 return ipcfail(cx);
316
317 if (!ok(cx, status))
318 return false;
319
320 return toValue(cx, val, vp);
321 }
322
323 bool
324 CPOWProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
325 JS::HandleId id, bool strict, JS::MutableHandleValue vp)
326 {
327 FORWARD(set, (cx, proxy, receiver, id, strict, vp));
328 }
329
330 bool
331 JavaScriptParent::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
332 JS::HandleId id, bool strict, JS::MutableHandleValue vp)
333 {
334 ObjectId objId = idOf(proxy);
335 ObjectId receiverId = idOf(receiver);
336
337 nsString idstr;
338 if (!convertIdToGeckoString(cx, id, &idstr))
339 return false;
340
341 JSVariant val;
342 if (!toVariant(cx, vp, &val))
343 return false;
344
345 ReturnStatus status;
346 JSVariant result;
347 if (!CallSet(objId, receiverId, idstr, strict, val, &status, &result))
348 return ipcfail(cx);
349
350 if (!ok(cx, status))
351 return false;
352
353 return toValue(cx, result, vp);
354 }
355
356 bool
357 CPOWProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
358 {
359 FORWARD(keys, (cx, proxy, props));
360 }
361
362 bool
363 JavaScriptParent::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
364 {
365 return getPropertyNames(cx, proxy, JSITER_OWNONLY, props);
366 }
367
368 bool
369 CPOWProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
370 {
371 FORWARD(isExtensible, (cx, proxy, extensible));
372 }
373
374 bool
375 JavaScriptParent::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
376 {
377 ObjectId objId = idOf(proxy);
378
379 ReturnStatus status;
380 if (!CallIsExtensible(objId, &status, extensible))
381 return ipcfail(cx);
382
383 return ok(cx, status);
384 }
385
386 bool
387 CPOWProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
388 {
389 FORWARD(call, (cx, proxy, args));
390 }
391
392 bool
393 JavaScriptParent::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
394 {
395 ObjectId objId = idOf(proxy);
396
397 InfallibleTArray<JSParam> vals;
398 AutoValueVector outobjects(cx);
399
400 RootedValue v(cx);
401 for (size_t i = 0; i < args.length() + 2; i++) {
402 v = args.base()[i];
403 if (v.isObject()) {
404 RootedObject obj(cx, &v.toObject());
405 if (xpc::IsOutObject(cx, obj)) {
406 // Make sure it is not an in-out object.
407 bool found;
408 if (!JS_HasProperty(cx, obj, "value", &found))
409 return false;
410 if (found) {
411 JS_ReportError(cx, "in-out objects cannot be sent via CPOWs yet");
412 return false;
413 }
414
415 vals.AppendElement(JSParam(void_t()));
416 if (!outobjects.append(ObjectValue(*obj)))
417 return false;
418 continue;
419 }
420 }
421 JSVariant val;
422 if (!toVariant(cx, v, &val))
423 return false;
424 vals.AppendElement(JSParam(val));
425 }
426
427 JSVariant result;
428 ReturnStatus status;
429 InfallibleTArray<JSParam> outparams;
430 if (!CallCall(objId, vals, &status, &result, &outparams))
431 return ipcfail(cx);
432 if (!ok(cx, status))
433 return false;
434
435 if (outparams.Length() != outobjects.length())
436 return ipcfail(cx);
437
438 RootedObject obj(cx);
439 for (size_t i = 0; i < outparams.Length(); i++) {
440 // Don't bother doing anything for outparams that weren't set.
441 if (outparams[i].type() == JSParam::Tvoid_t)
442 continue;
443
444 // Take the value the child process returned, and set it on the XPC
445 // object.
446 if (!toValue(cx, outparams[i], &v))
447 return false;
448
449 obj = &outobjects[i].toObject();
450 if (!JS_SetProperty(cx, obj, "value", v))
451 return false;
452 }
453
454 if (!toValue(cx, result, args.rval()))
455 return false;
456
457 return true;
458 }
459
460
461 bool
462 CPOWProxyHandler::objectClassIs(HandleObject proxy, js::ESClassValue classValue, JSContext *cx)
463 {
464 FORWARD(objectClassIs, (cx, proxy, classValue));
465 }
466
467 bool
468 JavaScriptParent::objectClassIs(JSContext *cx, HandleObject proxy, js::ESClassValue classValue)
469 {
470 ObjectId objId = idOf(proxy);
471
472 // This function is assumed infallible, so we just return false if the IPC
473 // channel fails.
474 bool result;
475 if (!CallObjectClassIs(objId, classValue, &result))
476 return false;
477
478 return result;
479 }
480
481 const char *
482 CPOWProxyHandler::className(JSContext *cx, HandleObject proxy)
483 {
484 JavaScriptParent *parent = ParentOf(proxy);
485 if (!parent->active())
486 return "<dead CPOW>";
487 return parent->className(cx, proxy);
488 }
489
490 const char *
491 JavaScriptParent::className(JSContext *cx, HandleObject proxy)
492 {
493 ObjectId objId = idOf(proxy);
494
495 nsString name;
496 if (!CallClassName(objId, &name))
497 return "<error>";
498
499 return ToNewCString(name);
500 }
501
502 void
503 CPOWProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy)
504 {
505 ParentOf(proxy)->drop(proxy);
506 }
507
508 void
509 JavaScriptParent::drop(JSObject *obj)
510 {
511 ObjectId objId = idOf(obj);
512
513 objects_.remove(objId);
514 if (!inactive_ && !SendDropObject(objId))
515 (void)0;
516 decref();
517 }
518
519 bool
520 JavaScriptParent::init()
521 {
522 if (!JavaScriptShared::init())
523 return false;
524
525 return true;
526 }
527
528 bool
529 JavaScriptParent::makeId(JSContext *cx, JSObject *obj, ObjectId *idp)
530 {
531 obj = js::CheckedUnwrap(obj, false);
532 if (!obj || !IsProxy(obj) || GetProxyHandler(obj) != &CPOWProxyHandler::singleton) {
533 JS_ReportError(cx, "cannot ipc non-cpow object");
534 return false;
535 }
536
537 *idp = idOf(obj);
538 return true;
539 }
540
541 bool
542 JavaScriptParent::getPropertyNames(JSContext *cx, HandleObject proxy, uint32_t flags, AutoIdVector &props)
543 {
544 ObjectId objId = idOf(proxy);
545
546 ReturnStatus status;
547 InfallibleTArray<nsString> names;
548 if (!CallGetPropertyNames(objId, flags, &status, &names))
549 return ipcfail(cx);
550 if (!ok(cx, status))
551 return false;
552
553 for (size_t i = 0; i < names.Length(); i++) {
554 RootedId name(cx);
555 if (!convertGeckoStringToId(cx, names[i], &name))
556 return false;
557 if (!props.append(name))
558 return false;
559 }
560
561 return true;
562 }
563
564 JSObject *
565 JavaScriptParent::unwrap(JSContext *cx, ObjectId objId)
566 {
567 RootedObject obj(cx, findObject(objId));
568 if (obj) {
569 if (!JS_WrapObject(cx, &obj))
570 return nullptr;
571 return obj;
572 }
573
574 if (objId > MAX_CPOW_IDS) {
575 JS_ReportError(cx, "unusable CPOW id");
576 return nullptr;
577 }
578
579 bool callable = !!(objId & OBJECT_IS_CALLABLE);
580
581 RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
582
583 RootedValue v(cx, UndefinedValue());
584 ProxyOptions options;
585 options.selectDefaultClass(callable);
586 obj = NewProxyObject(cx,
587 &CPOWProxyHandler::singleton,
588 v,
589 nullptr,
590 global,
591 options);
592 if (!obj)
593 return nullptr;
594
595 if (!objects_.add(objId, obj))
596 return nullptr;
597
598 // Incref once we know the decref will be called.
599 incref();
600
601 SetProxyExtra(obj, 0, PrivateValue(this));
602 SetProxyExtra(obj, 1, DoubleValue(BitwiseCast<double>(objId)));
603 return obj;
604 }
605
606 bool
607 JavaScriptParent::ipcfail(JSContext *cx)
608 {
609 JS_ReportError(cx, "child process crashed or timedout");
610 return false;
611 }
612
613 bool
614 JavaScriptParent::ok(JSContext *cx, const ReturnStatus &status)
615 {
616 if (status.type() == ReturnStatus::TReturnSuccess)
617 return true;
618
619 if (status.type() == ReturnStatus::TReturnStopIteration)
620 return JS_ThrowStopIteration(cx);
621
622 RootedValue exn(cx);
623 if (!toValue(cx, status.get_ReturnException().exn(), &exn))
624 return false;
625
626 JS_SetPendingException(cx, exn);
627 return false;
628 }
629
630 void
631 JavaScriptParent::decref()
632 {
633 refcount_--;
634 if (!refcount_)
635 delete this;
636 }
637
638 void
639 JavaScriptParent::incref()
640 {
641 refcount_++;
642 }
643
644 void
645 JavaScriptParent::ActorDestroy(ActorDestroyReason why)
646 {
647 inactive_ = true;
648 }
649
650 /* static */ bool
651 JavaScriptParent::IsCPOW(JSObject *obj)
652 {
653 return IsProxy(obj) && GetProxyHandler(obj) == &CPOWProxyHandler::singleton;
654 }
655
656 /* static */ nsresult
657 JavaScriptParent::InstanceOf(JSObject *proxy, const nsID *id, bool *bp)
658 {
659 JavaScriptParent *parent = ParentOf(proxy);
660 if (!parent->active())
661 return NS_ERROR_UNEXPECTED;
662 return parent->instanceOf(proxy, id, bp);
663 }
664
665 nsresult
666 JavaScriptParent::instanceOf(JSObject *obj, const nsID *id, bool *bp)
667 {
668 ObjectId objId = idOf(obj);
669
670 JSIID iid;
671 ConvertID(*id, &iid);
672
673 ReturnStatus status;
674 if (!CallInstanceOf(objId, iid, &status, bp))
675 return NS_ERROR_UNEXPECTED;
676
677 if (status.type() != ReturnStatus::TReturnSuccess)
678 return NS_ERROR_UNEXPECTED;
679
680 return NS_OK;
681 }
682
683 /* static */ bool
684 JavaScriptParent::DOMInstanceOf(JSContext *cx, JSObject *proxy, int prototypeID, int depth, bool *bp)
685 {
686 FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp));
687 }
688
689 bool
690 JavaScriptParent::domInstanceOf(JSContext *cx, JSObject *obj, int prototypeID, int depth, bool *bp)
691 {
692 ObjectId objId = idOf(obj);
693
694 ReturnStatus status;
695 if (!CallDOMInstanceOf(objId, prototypeID, depth, &status, bp))
696 return ipcfail(cx);
697
698 return ok(cx, status);
699 }
700
701 mozilla::ipc::IProtocol*
702 JavaScriptParent::CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx)
703 {
704 ContentParent *contentParent = aCtx->GetContentParent();
705 nsAutoPtr<PJavaScriptParent> actor(contentParent->AllocPJavaScriptParent());
706 if (!actor || !contentParent->RecvPJavaScriptConstructor(actor)) {
707 return nullptr;
708 }
709 return actor.forget();
710 }

mercurial