Bumping manifests a=b2g-bump
[gecko.git] / js / ipc / WrapperOwner.cpp
blobc8f17d6bd5ee42474749d591d6b3ebf9e260e460
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=80:
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/. */
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 "xpcprivate.h"
14 #include "CPOWTimer.h"
15 #include "WrapperFactory.h"
17 #include "nsIRemoteTagService.h"
19 using namespace js;
20 using namespace JS;
21 using namespace mozilla;
22 using namespace mozilla::jsipc;
24 struct AuxCPOWData
26 ObjectId id;
27 bool isCallable;
28 bool isConstructor;
30 // The object tag is just some auxilliary information that clients can use
31 // however they see fit.
32 nsCString objectTag;
34 AuxCPOWData(ObjectId id, bool isCallable, bool isConstructor, const nsACString& objectTag)
35 : id(id),
36 isCallable(isCallable),
37 isConstructor(isConstructor),
38 objectTag(objectTag)
42 WrapperOwner::WrapperOwner(JSRuntime* rt)
43 : JavaScriptShared(rt),
44 inactive_(false)
48 static inline AuxCPOWData*
49 AuxCPOWDataOf(JSObject* obj)
51 MOZ_ASSERT(IsCPOW(obj));
52 return static_cast<AuxCPOWData*>(GetProxyExtra(obj, 1).toPrivate());
55 static inline WrapperOwner*
56 OwnerOf(JSObject* obj)
58 MOZ_ASSERT(IsCPOW(obj));
59 return reinterpret_cast<WrapperOwner*>(GetProxyExtra(obj, 0).toPrivate());
62 ObjectId
63 WrapperOwner::idOfUnchecked(JSObject* obj)
65 MOZ_ASSERT(IsCPOW(obj));
67 AuxCPOWData* aux = AuxCPOWDataOf(obj);
68 MOZ_ASSERT(!aux->id.isNull());
69 return aux->id;
72 ObjectId
73 WrapperOwner::idOf(JSObject* obj)
75 ObjectId objId = idOfUnchecked(obj);
76 MOZ_ASSERT(findCPOWById(objId) == obj);
77 return objId;
80 class CPOWProxyHandler : public BaseProxyHandler
82 public:
83 MOZ_CONSTEXPR CPOWProxyHandler()
84 : BaseProxyHandler(&family) {}
86 virtual bool finalizeInBackground(Value priv) const MOZ_OVERRIDE {
87 return false;
90 virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
91 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
92 virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
93 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
94 virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
95 AutoIdVector& props) const MOZ_OVERRIDE;
96 virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const MOZ_OVERRIDE;
97 virtual bool enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const MOZ_OVERRIDE;
98 virtual bool preventExtensions(JSContext* cx, HandleObject proxy, bool* succeeded) const MOZ_OVERRIDE;
99 virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const MOZ_OVERRIDE;
100 virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const MOZ_OVERRIDE;
101 virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver,
102 HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
103 virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleObject receiver,
104 JS::HandleId id, bool strict, JS::MutableHandleValue vp) const MOZ_OVERRIDE;
105 virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const MOZ_OVERRIDE;
106 virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const MOZ_OVERRIDE;
108 virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
109 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
110 virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const MOZ_OVERRIDE;
111 virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
112 AutoIdVector& props) const MOZ_OVERRIDE;
113 virtual bool hasInstance(JSContext* cx, HandleObject proxy,
114 MutableHandleValue v, bool* bp) const MOZ_OVERRIDE;
115 virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue,
116 JSContext* cx) const MOZ_OVERRIDE;
117 virtual const char* className(JSContext* cx, HandleObject proxy) const MOZ_OVERRIDE;
118 virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const MOZ_OVERRIDE;
119 virtual void finalize(JSFreeOp* fop, JSObject* proxy) const MOZ_OVERRIDE;
120 virtual void objectMoved(JSObject* proxy, const JSObject* old) const MOZ_OVERRIDE;
121 virtual bool isCallable(JSObject* obj) const MOZ_OVERRIDE;
122 virtual bool isConstructor(JSObject* obj) const MOZ_OVERRIDE;
124 static const char family;
125 static const CPOWProxyHandler singleton;
128 const char CPOWProxyHandler::family = 0;
129 const CPOWProxyHandler CPOWProxyHandler::singleton;
131 #define FORWARD(call, args) \
132 WrapperOwner* owner = OwnerOf(proxy); \
133 if (!owner->active()) { \
134 JS_ReportError(cx, "cannot use a CPOW whose process is gone"); \
135 return false; \
138 CPOWTimer timer; \
139 return owner->call args; \
142 bool
143 CPOWProxyHandler::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
144 MutableHandle<JSPropertyDescriptor> desc) const
146 FORWARD(getPropertyDescriptor, (cx, proxy, id, desc));
149 bool
150 WrapperOwner::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
151 MutableHandle<JSPropertyDescriptor> desc)
153 ObjectId objId = idOf(proxy);
155 JSIDVariant idVar;
156 if (!toJSIDVariant(cx, id, &idVar))
157 return false;
159 ReturnStatus status;
160 PPropertyDescriptor result;
161 if (!SendGetPropertyDescriptor(objId, idVar, &status, &result))
162 return ipcfail(cx);
164 LOG_STACK();
166 if (!ok(cx, status))
167 return false;
169 return toDescriptor(cx, result, desc);
172 bool
173 CPOWProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
174 MutableHandle<JSPropertyDescriptor> desc) const
176 FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc));
179 bool
180 WrapperOwner::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
181 MutableHandle<JSPropertyDescriptor> desc)
183 ObjectId objId = idOf(proxy);
185 JSIDVariant idVar;
186 if (!toJSIDVariant(cx, id, &idVar))
187 return false;
189 ReturnStatus status;
190 PPropertyDescriptor result;
191 if (!SendGetOwnPropertyDescriptor(objId, idVar, &status, &result))
192 return ipcfail(cx);
194 LOG_STACK();
196 if (!ok(cx, status))
197 return false;
199 return toDescriptor(cx, result, desc);
202 bool
203 CPOWProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
204 MutableHandle<JSPropertyDescriptor> desc) const
206 FORWARD(defineProperty, (cx, proxy, id, desc));
209 bool
210 WrapperOwner::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
211 MutableHandle<JSPropertyDescriptor> desc)
213 ObjectId objId = idOf(proxy);
215 JSIDVariant idVar;
216 if (!toJSIDVariant(cx, id, &idVar))
217 return false;
219 PPropertyDescriptor descriptor;
220 if (!fromDescriptor(cx, desc, &descriptor))
221 return false;
223 ReturnStatus status;
224 if (!SendDefineProperty(objId, idVar, descriptor, &status))
225 return ipcfail(cx);
227 LOG_STACK();
229 return ok(cx, status);
232 bool
233 CPOWProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy,
234 AutoIdVector& props) const
236 FORWARD(ownPropertyKeys, (cx, proxy, props));
239 bool
240 WrapperOwner::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
242 return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
245 bool
246 CPOWProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
248 FORWARD(delete_, (cx, proxy, id, bp));
251 bool
252 WrapperOwner::delete_(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
254 ObjectId objId = idOf(proxy);
256 JSIDVariant idVar;
257 if (!toJSIDVariant(cx, id, &idVar))
258 return false;
260 ReturnStatus status;
261 if (!SendDelete(objId, idVar, &status, bp))
262 return ipcfail(cx);
264 LOG_STACK();
266 return ok(cx, status);
269 bool
270 CPOWProxyHandler::enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const
272 // Using a CPOW for the Iterator would slow down for .. in performance, instead
273 // call the base hook, that will use our implementation of getOwnEnumerablePropertyKeys
274 // and follow the proto chain.
275 return BaseProxyHandler::enumerate(cx, proxy, objp);
278 bool
279 CPOWProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
281 FORWARD(has, (cx, proxy, id, bp));
284 bool
285 WrapperOwner::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
287 ObjectId objId = idOf(proxy);
289 JSIDVariant idVar;
290 if (!toJSIDVariant(cx, id, &idVar))
291 return false;
293 ReturnStatus status;
294 if (!SendHas(objId, idVar, &status, bp))
295 return ipcfail(cx);
297 LOG_STACK();
299 return ok(cx, status);
302 bool
303 CPOWProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
305 FORWARD(hasOwn, (cx, proxy, id, bp));
308 bool
309 WrapperOwner::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
311 ObjectId objId = idOf(proxy);
313 JSIDVariant idVar;
314 if (!toJSIDVariant(cx, id, &idVar))
315 return false;
317 ReturnStatus status;
318 if (!SendHasOwn(objId, idVar, &status, bp))
319 return ipcfail(cx);
321 LOG_STACK();
323 return !!ok(cx, status);
326 bool
327 CPOWProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver,
328 HandleId id, MutableHandleValue vp) const
330 FORWARD(get, (cx, proxy, receiver, id, vp));
333 static bool
334 CPOWToString(JSContext* cx, unsigned argc, Value* vp)
336 CallArgs args = CallArgsFromVp(argc, vp);
337 RootedObject callee(cx, &args.callee());
338 RootedValue cpowValue(cx);
339 if (!JS_GetProperty(cx, callee, "__cpow__", &cpowValue))
340 return false;
342 if (!cpowValue.isObject() || !IsCPOW(&cpowValue.toObject())) {
343 JS_ReportError(cx, "CPOWToString called on an incompatible object");
344 return false;
347 RootedObject proxy(cx, &cpowValue.toObject());
348 FORWARD(toString, (cx, proxy, args));
351 bool
352 WrapperOwner::toString(JSContext* cx, HandleObject cpow, JS::CallArgs& args)
354 // Ask the other side to call its toString method. Update the callee so that
355 // it points to the CPOW and not to the synthesized CPOWToString function.
356 args.setCallee(ObjectValue(*cpow));
357 if (!callOrConstruct(cx, cpow, args, false))
358 return false;
360 if (!args.rval().isString())
361 return true;
363 RootedString cpowResult(cx, args.rval().toString());
364 nsAutoJSString toStringResult;
365 if (!toStringResult.init(cx, cpowResult))
366 return false;
368 // We don't want to wrap toString() results for things like the location
369 // object, where toString() is supposed to return a URL and nothing else.
370 nsAutoString result;
371 if (toStringResult[0] == '[') {
372 result.AppendLiteral("[object CPOW ");
373 result += toStringResult;
374 result.AppendLiteral("]");
375 } else {
376 result += toStringResult;
379 JSString* str = JS_NewUCStringCopyN(cx, result.get(), result.Length());
380 if (!str)
381 return false;
383 args.rval().setString(str);
384 return true;
387 bool
388 WrapperOwner::get(JSContext* cx, HandleObject proxy, HandleObject receiver,
389 HandleId id, MutableHandleValue vp)
391 ObjectId objId = idOf(proxy);
393 ObjectVariant receiverVar;
394 if (!toObjectVariant(cx, receiver, &receiverVar))
395 return false;
397 JSIDVariant idVar;
398 if (!toJSIDVariant(cx, id, &idVar))
399 return false;
401 JSVariant val;
402 ReturnStatus status;
403 if (!SendGet(objId, receiverVar, idVar, &status, &val))
404 return ipcfail(cx);
406 LOG_STACK();
408 if (!ok(cx, status))
409 return false;
411 if (!fromVariant(cx, val, vp))
412 return false;
414 if (idVar.type() == JSIDVariant::TnsString &&
415 idVar.get_nsString().EqualsLiteral("toString")) {
416 RootedFunction toString(cx, JS_NewFunction(cx, CPOWToString, 0, 0, proxy, "toString"));
417 if (!toString)
418 return false;
420 RootedObject toStringObj(cx, JS_GetFunctionObject(toString));
422 if (!JS_DefineProperty(cx, toStringObj, "__cpow__", vp, JSPROP_PERMANENT | JSPROP_READONLY))
423 return false;
425 vp.set(ObjectValue(*toStringObj));
428 return true;
431 bool
432 CPOWProxyHandler::set(JSContext* cx, JS::HandleObject proxy, JS::HandleObject receiver,
433 JS::HandleId id, bool strict, JS::MutableHandleValue vp) const
435 FORWARD(set, (cx, proxy, receiver, id, strict, vp));
438 bool
439 WrapperOwner::set(JSContext* cx, JS::HandleObject proxy, JS::HandleObject receiver,
440 JS::HandleId id, bool strict, JS::MutableHandleValue vp)
442 ObjectId objId = idOf(proxy);
444 ObjectVariant receiverVar;
445 if (!toObjectVariant(cx, receiver, &receiverVar))
446 return false;
448 JSIDVariant idVar;
449 if (!toJSIDVariant(cx, id, &idVar))
450 return false;
452 JSVariant val;
453 if (!toVariant(cx, vp, &val))
454 return false;
456 ReturnStatus status;
457 JSVariant result;
458 if (!SendSet(objId, receiverVar, idVar, strict, val, &status, &result))
459 return ipcfail(cx);
461 LOG_STACK();
463 if (!ok(cx, status))
464 return false;
466 return fromVariant(cx, result, vp);
469 bool
470 CPOWProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
471 AutoIdVector& props) const
473 FORWARD(getOwnEnumerablePropertyKeys, (cx, proxy, props));
476 bool
477 WrapperOwner::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
479 return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props);
482 bool
483 CPOWProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy, bool* succeeded) const
485 FORWARD(preventExtensions, (cx, proxy, succeeded));
488 bool
489 WrapperOwner::preventExtensions(JSContext* cx, HandleObject proxy, bool* succeeded)
491 ObjectId objId = idOf(proxy);
493 ReturnStatus status;
494 if (!SendPreventExtensions(objId, &status, succeeded))
495 return ipcfail(cx);
497 LOG_STACK();
499 return ok(cx, status);
502 bool
503 CPOWProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const
505 FORWARD(isExtensible, (cx, proxy, extensible));
508 bool
509 WrapperOwner::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible)
511 ObjectId objId = idOf(proxy);
513 ReturnStatus status;
514 if (!SendIsExtensible(objId, &status, extensible))
515 return ipcfail(cx);
517 LOG_STACK();
519 return ok(cx, status);
522 bool
523 CPOWProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const
525 FORWARD(callOrConstruct, (cx, proxy, args, false));
528 bool
529 CPOWProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const
531 FORWARD(callOrConstruct, (cx, proxy, args, true));
534 bool
535 WrapperOwner::callOrConstruct(JSContext* cx, HandleObject proxy, const CallArgs& args,
536 bool construct)
538 ObjectId objId = idOf(proxy);
540 InfallibleTArray<JSParam> vals;
541 AutoValueVector outobjects(cx);
543 RootedValue v(cx);
544 for (size_t i = 0; i < args.length() + 2; i++) {
545 // The |this| value for constructors is a magic value that we won't be
546 // able to convert, so skip it.
547 if (i == 1 && construct)
548 v = UndefinedValue();
549 else
550 v = args.base()[i];
551 if (v.isObject()) {
552 RootedObject obj(cx, &v.toObject());
553 if (xpc::IsOutObject(cx, obj)) {
554 // Make sure it is not an in-out object.
555 bool found;
556 if (!JS_HasProperty(cx, obj, "value", &found))
557 return false;
558 if (found) {
559 JS_ReportError(cx, "in-out objects cannot be sent via CPOWs yet");
560 return false;
563 vals.AppendElement(JSParam(void_t()));
564 if (!outobjects.append(ObjectValue(*obj)))
565 return false;
566 continue;
569 JSVariant val;
570 if (!toVariant(cx, v, &val))
571 return false;
572 vals.AppendElement(JSParam(val));
575 JSVariant result;
576 ReturnStatus status;
577 InfallibleTArray<JSParam> outparams;
578 if (!SendCallOrConstruct(objId, vals, construct, &status, &result, &outparams))
579 return ipcfail(cx);
581 LOG_STACK();
583 if (!ok(cx, status))
584 return false;
586 if (outparams.Length() != outobjects.length())
587 return ipcfail(cx);
589 RootedObject obj(cx);
590 for (size_t i = 0; i < outparams.Length(); i++) {
591 // Don't bother doing anything for outparams that weren't set.
592 if (outparams[i].type() == JSParam::Tvoid_t)
593 continue;
595 // Take the value the child process returned, and set it on the XPC
596 // object.
597 if (!fromVariant(cx, outparams[i], &v))
598 return false;
600 obj = &outobjects[i].toObject();
601 if (!JS_SetProperty(cx, obj, "value", v))
602 return false;
605 if (!fromVariant(cx, result, args.rval()))
606 return false;
608 return true;
611 bool
612 CPOWProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const
614 FORWARD(hasInstance, (cx, proxy, v, bp));
617 bool
618 WrapperOwner::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp)
620 ObjectId objId = idOf(proxy);
622 JSVariant vVar;
623 if (!toVariant(cx, v, &vVar))
624 return false;
626 ReturnStatus status;
627 JSVariant result;
628 if (!SendHasInstance(objId, vVar, &status, bp))
629 return ipcfail(cx);
631 LOG_STACK();
633 return ok(cx, status);
636 bool
637 CPOWProxyHandler::objectClassIs(HandleObject proxy, js::ESClassValue classValue, JSContext* cx) const
639 FORWARD(objectClassIs, (cx, proxy, classValue));
642 bool
643 WrapperOwner::objectClassIs(JSContext* cx, HandleObject proxy, js::ESClassValue classValue)
645 ObjectId objId = idOf(proxy);
647 // This function is assumed infallible, so we just return false if the IPC
648 // channel fails.
649 bool result;
650 if (!SendObjectClassIs(objId, classValue, &result))
651 return false;
653 LOG_STACK();
655 return result;
658 const char*
659 CPOWProxyHandler::className(JSContext* cx, HandleObject proxy) const
661 WrapperOwner* parent = OwnerOf(proxy);
662 if (!parent->active())
663 return "<dead CPOW>";
664 return parent->className(cx, proxy);
667 const char*
668 WrapperOwner::className(JSContext* cx, HandleObject proxy)
670 ObjectId objId = idOf(proxy);
672 nsString name;
673 if (!SendClassName(objId, &name))
674 return "<error>";
676 LOG_STACK();
678 return ToNewCString(name);
681 bool
682 CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
684 FORWARD(regexp_toShared, (cx, proxy, g));
687 bool
688 WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
690 ObjectId objId = idOf(proxy);
692 ReturnStatus status;
693 nsString source;
694 unsigned flags = 0;
695 if (!SendRegExpToShared(objId, &status, &source, &flags))
696 return ipcfail(cx);
698 LOG_STACK();
700 if (!ok(cx, status))
701 return false;
703 RootedObject regexp(cx);
704 RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
705 regexp = JS_NewUCRegExpObject(cx, global, source.get(), source.Length(), flags);
706 if (!regexp)
707 return false;
709 return js::RegExpToSharedNonInline(cx, regexp, g);
712 void
713 CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const
715 AuxCPOWData* aux = AuxCPOWDataOf(proxy);
717 OwnerOf(proxy)->drop(proxy);
719 if (aux)
720 delete aux;
723 void
724 CPOWProxyHandler::objectMoved(JSObject* proxy, const JSObject* old) const
726 OwnerOf(proxy)->updatePointer(proxy, old);
729 bool
730 CPOWProxyHandler::isCallable(JSObject* proxy) const
732 AuxCPOWData* aux = AuxCPOWDataOf(proxy);
733 return aux->isCallable;
736 bool
737 CPOWProxyHandler::isConstructor(JSObject* proxy) const
739 AuxCPOWData* aux = AuxCPOWDataOf(proxy);
740 return aux->isConstructor;
743 void
744 WrapperOwner::drop(JSObject* obj)
746 ObjectId objId = idOf(obj);
748 cpows_.remove(objId);
749 if (active())
750 unused << SendDropObject(objId);
751 decref();
754 void
755 WrapperOwner::updatePointer(JSObject* obj, const JSObject* old)
757 ObjectId objId = idOfUnchecked(obj);
758 MOZ_ASSERT(findCPOWById(objId) == old);
759 cpows_.add(objId, obj);
762 bool
763 WrapperOwner::init()
765 if (!JavaScriptShared::init())
766 return false;
768 return true;
771 bool
772 WrapperOwner::getPropertyKeys(JSContext* cx, HandleObject proxy, uint32_t flags, AutoIdVector& props)
774 ObjectId objId = idOf(proxy);
776 ReturnStatus status;
777 InfallibleTArray<JSIDVariant> ids;
778 if (!SendGetPropertyKeys(objId, flags, &status, &ids))
779 return ipcfail(cx);
781 LOG_STACK();
783 if (!ok(cx, status))
784 return false;
786 for (size_t i = 0; i < ids.Length(); i++) {
787 RootedId id(cx);
788 if (!fromJSIDVariant(cx, ids[i], &id))
789 return false;
790 if (!props.append(id))
791 return false;
794 return true;
797 namespace mozilla {
798 namespace jsipc {
800 bool
801 IsCPOW(JSObject* obj)
803 return IsProxy(obj) && GetProxyHandler(obj) == &CPOWProxyHandler::singleton;
806 bool
807 IsWrappedCPOW(JSObject* obj)
809 JSObject* unwrapped = js::UncheckedUnwrap(obj, true);
810 if (!unwrapped)
811 return false;
812 return IsCPOW(unwrapped);
815 void
816 GetWrappedCPOWTag(JSObject* obj, nsACString& out)
818 JSObject* unwrapped = js::UncheckedUnwrap(obj, true);
819 MOZ_ASSERT(IsCPOW(unwrapped));
821 AuxCPOWData* aux = AuxCPOWDataOf(unwrapped);
822 if (aux)
823 out = aux->objectTag;
826 nsresult
827 InstanceOf(JSObject* proxy, const nsID* id, bool* bp)
829 WrapperOwner* parent = OwnerOf(proxy);
830 if (!parent->active())
831 return NS_ERROR_UNEXPECTED;
832 return parent->instanceOf(proxy, id, bp);
835 bool
836 DOMInstanceOf(JSContext* cx, JSObject* proxy, int prototypeID, int depth, bool* bp)
838 FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp));
841 } /* namespace jsipc */
842 } /* namespace mozilla */
844 nsresult
845 WrapperOwner::instanceOf(JSObject* obj, const nsID* id, bool* bp)
847 ObjectId objId = idOf(obj);
849 JSIID iid;
850 ConvertID(*id, &iid);
852 ReturnStatus status;
853 if (!SendInstanceOf(objId, iid, &status, bp))
854 return NS_ERROR_UNEXPECTED;
856 if (status.type() != ReturnStatus::TReturnSuccess)
857 return NS_ERROR_UNEXPECTED;
859 return NS_OK;
862 bool
863 WrapperOwner::domInstanceOf(JSContext* cx, JSObject* obj, int prototypeID, int depth, bool* bp)
865 ObjectId objId = idOf(obj);
867 ReturnStatus status;
868 if (!SendDOMInstanceOf(objId, prototypeID, depth, &status, bp))
869 return ipcfail(cx);
871 LOG_STACK();
873 return ok(cx, status);
876 void
877 WrapperOwner::ActorDestroy(ActorDestroyReason why)
879 inactive_ = true;
881 objects_.clear();
882 unwaivedObjectIds_.clear();
883 waivedObjectIds_.clear();
886 bool
887 WrapperOwner::ipcfail(JSContext* cx)
889 JS_ReportError(cx, "child process crashed or timedout");
890 return false;
893 bool
894 WrapperOwner::ok(JSContext* cx, const ReturnStatus& status)
896 if (status.type() == ReturnStatus::TReturnSuccess)
897 return true;
899 if (status.type() == ReturnStatus::TReturnStopIteration)
900 return JS_ThrowStopIteration(cx);
902 RootedValue exn(cx);
903 if (!fromVariant(cx, status.get_ReturnException().exn(), &exn))
904 return false;
906 JS_SetPendingException(cx, exn);
907 return false;
910 static RemoteObject
911 MakeRemoteObject(JSContext* cx, ObjectId id, HandleObject obj)
913 nsCString objectTag;
915 nsCOMPtr<nsIRemoteTagService> service =
916 do_GetService("@mozilla.org/addons/remote-tag-service;1");
917 if (service) {
918 RootedValue objVal(cx, ObjectValue(*obj));
919 service->GetRemoteObjectTag(objVal, objectTag);
922 return RemoteObject(id.serialize(),
923 JS::IsCallable(obj),
924 JS::IsConstructor(obj),
925 objectTag);
928 bool
929 WrapperOwner::toObjectVariant(JSContext* cx, JSObject* objArg, ObjectVariant* objVarp)
931 RootedObject obj(cx, objArg);
932 MOZ_ASSERT(obj);
934 // We always save objects unwrapped in the CPOW table. If we stored
935 // wrappers, then the wrapper might be GCed while the target remained alive.
936 // Whenever operating on an object that comes from the table, we wrap it
937 // in findObjectById.
938 unsigned wrapperFlags = 0;
939 obj = js::UncheckedUnwrap(obj, true, &wrapperFlags);
940 if (obj && IsCPOW(obj) && OwnerOf(obj) == this) {
941 *objVarp = LocalObject(idOf(obj).serialize());
942 return true;
944 bool waiveXray = wrapperFlags & xpc::WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG;
946 ObjectId id = objectIdMap(waiveXray).find(obj);
947 if (!id.isNull()) {
948 MOZ_ASSERT(id.hasXrayWaiver() == waiveXray);
949 *objVarp = MakeRemoteObject(cx, id, obj);
950 return true;
953 // Need to call PreserveWrapper on |obj| in case it's a reflector.
954 // FIXME: What if it's an XPCWrappedNative?
955 if (mozilla::dom::IsDOMObject(obj))
956 mozilla::dom::TryPreserveWrapper(obj);
958 id = ObjectId(nextSerialNumber_++, waiveXray);
959 if (!objects_.add(id, obj))
960 return false;
961 if (!objectIdMap(waiveXray).add(cx, obj, id))
962 return false;
964 *objVarp = MakeRemoteObject(cx, id, obj);
965 return true;
968 JSObject*
969 WrapperOwner::fromObjectVariant(JSContext* cx, ObjectVariant objVar)
971 if (objVar.type() == ObjectVariant::TRemoteObject) {
972 return fromRemoteObjectVariant(cx, objVar.get_RemoteObject());
973 } else {
974 return fromLocalObjectVariant(cx, objVar.get_LocalObject());
978 JSObject*
979 WrapperOwner::fromRemoteObjectVariant(JSContext* cx, RemoteObject objVar)
981 ObjectId objId = ObjectId::deserialize(objVar.serializedId());
982 RootedObject obj(cx, findCPOWById(objId));
983 if (!obj) {
985 // All CPOWs live in the privileged junk scope.
986 RootedObject junkScope(cx, xpc::PrivilegedJunkScope());
987 JSAutoCompartment ac(cx, junkScope);
988 RootedValue v(cx, UndefinedValue());
989 obj = NewProxyObject(cx,
990 &CPOWProxyHandler::singleton,
992 nullptr,
993 junkScope);
994 if (!obj)
995 return nullptr;
997 if (!cpows_.add(objId, obj))
998 return nullptr;
1000 // Incref once we know the decref will be called.
1001 incref();
1003 AuxCPOWData* aux = new AuxCPOWData(objId,
1004 objVar.isCallable(),
1005 objVar.isConstructor(),
1006 objVar.objectTag());
1008 SetProxyExtra(obj, 0, PrivateValue(this));
1009 SetProxyExtra(obj, 1, PrivateValue(aux));
1012 if (!JS_WrapObject(cx, &obj))
1013 return nullptr;
1014 return obj;
1017 JSObject*
1018 WrapperOwner::fromLocalObjectVariant(JSContext* cx, LocalObject objVar)
1020 ObjectId id = ObjectId::deserialize(objVar.serializedId());
1021 Rooted<JSObject*> obj(cx, findObjectById(cx, id));
1022 if (!obj)
1023 return nullptr;
1024 if (!JS_WrapObject(cx, &obj))
1025 return nullptr;
1026 return obj;