Bug 996785 - Move CPOW wrapper answering code (r=mrbkap)
authorBill McCloskey <wmccloskey@mozilla.com>
Fri, 16 May 2014 16:40:36 -0700
changeset 183647 05fa4a69cb9818df96d82c9fc37fdad57ed85f68
parent 183646 15ecb06251c8b7e886dec794e42c923fa8ab6217
child 183648 1e495c6dba19835297494eb2a128301fe9e55848
push idunknown
push userunknown
push dateunknown
reviewersmrbkap
bugs996785
milestone32.0a1
Bug 996785 - Move CPOW wrapper answering code (r=mrbkap)
dom/ipc/ContentParent.cpp
js/ipc/JavaScriptBase.h
js/ipc/JavaScriptChild.cpp
js/ipc/JavaScriptChild.h
js/ipc/JavaScriptShared.cpp
js/ipc/PJavaScript.ipdl
js/ipc/WrapperAnswer.cpp
js/ipc/WrapperAnswer.h
js/ipc/WrapperOwner.h
js/ipc/moz.build
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3344,17 +3344,17 @@ ContentParent::DoSendAsyncMessage(JSCont
                                   JS::Handle<JSObject *> aCpows,
                                   nsIPrincipal* aPrincipal)
 {
   ClonedMessageData data;
   if (!BuildClonedMessageDataForParent(this, aData, data)) {
     return false;
   }
   InfallibleTArray<CpowEntry> cpows;
-  if (!GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
+  if (aCpows && !GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
     return false;
   }
   return SendAsyncMessage(nsString(aMessage), data, cpows, aPrincipal);
 }
 
 bool
 ContentParent::CheckPermission(const nsAString& aPermission)
 {
--- a/js/ipc/JavaScriptBase.h
+++ b/js/ipc/JavaScriptBase.h
@@ -3,29 +3,109 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_jsipc_JavaScriptBase_h__
 #define mozilla_jsipc_JavaScriptBase_h__
 
+#include "WrapperAnswer.h"
 #include "WrapperOwner.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/jsipc/PJavaScript.h"
 
 namespace mozilla {
 namespace jsipc {
 
 template<class Base>
-class JavaScriptBase : public WrapperOwner, public Base
+class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
 {
-    typedef WrapperOwner Shared;
+    typedef WrapperAnswer Answer;
 
   public:
+    virtual void ActorDestroy(WrapperOwner::ActorDestroyReason why) {
+        WrapperOwner::ActorDestroy(why);
+    }
+
+    /*** IPC handlers ***/
+
+    bool AnswerPreventExtensions(const ObjectId &objId, ReturnStatus *rs) {
+        return Answer::AnswerPreventExtensions(objId, rs);
+    }
+    bool AnswerGetPropertyDescriptor(const ObjectId &objId, const nsString &id,
+                                     ReturnStatus *rs,
+                                     PPropertyDescriptor *out) {
+        return Answer::AnswerGetPropertyDescriptor(objId, id, rs, out);
+    }
+    bool AnswerGetOwnPropertyDescriptor(const ObjectId &objId,
+                                        const nsString &id,
+                                        ReturnStatus *rs,
+                                        PPropertyDescriptor *out) {
+        return Answer::AnswerGetOwnPropertyDescriptor(objId, id, rs, out);
+    }
+    bool AnswerDefineProperty(const ObjectId &objId, const nsString &id,
+                              const PPropertyDescriptor &flags,
+                              ReturnStatus *rs) {
+        return Answer::AnswerDefineProperty(objId, id, flags, rs);
+    }
+    bool AnswerDelete(const ObjectId &objId, const nsString &id,
+                      ReturnStatus *rs, bool *success) {
+        return Answer::AnswerDelete(objId, id, rs, success);
+    }
+
+    bool AnswerHas(const ObjectId &objId, const nsString &id,
+                   ReturnStatus *rs, bool *bp) {
+        return Answer::AnswerHas(objId, id, rs, bp);
+    }
+    bool AnswerHasOwn(const ObjectId &objId, const nsString &id,
+                      ReturnStatus *rs, bool *bp) {
+        return Answer::AnswerHasOwn(objId, id, rs, bp);
+    }
+    bool AnswerGet(const ObjectId &objId, const ObjectId &receiverId,
+                   const nsString &id,
+                   ReturnStatus *rs, JSVariant *result) {
+        return Answer::AnswerGet(objId, receiverId, id, rs, result);
+    }
+    bool AnswerSet(const ObjectId &objId, const ObjectId &receiverId,
+                   const nsString &id, const bool &strict,
+                   const JSVariant &value, ReturnStatus *rs, JSVariant *result) {
+        return Answer::AnswerSet(objId, receiverId, id, strict, value, rs, result);
+    }
+
+    bool AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs,
+                            bool *result) {
+        return Answer::AnswerIsExtensible(objId, rs, result);
+    }
+    bool AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv,
+                    ReturnStatus *rs, JSVariant *result,
+                    nsTArray<JSParam> *outparams) {
+        return Answer::AnswerCall(objId, argv, rs, result, outparams);
+    }
+    bool AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
+                             bool *result) {
+        return Answer::AnswerObjectClassIs(objId, classValue, result);
+    }
+    bool AnswerClassName(const ObjectId &objId, nsString *result) {
+        return Answer::AnswerClassName(objId, result);
+    }
+
+    bool AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
+                                ReturnStatus *rs, nsTArray<nsString> *names) {
+        return Answer::AnswerGetPropertyNames(objId, flags, rs, names);
+    }
+    bool AnswerInstanceOf(const ObjectId &objId, const JSIID &iid,
+                          ReturnStatus *rs, bool *instanceof) {
+        return Answer::AnswerInstanceOf(objId, iid, rs, instanceof);
+    }
+    bool AnswerDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth,
+                             ReturnStatus *rs, bool *instanceof) {
+        return Answer::AnswerDOMInstanceOf(objId, prototypeID, depth, rs, instanceof);
+    }
+
     /*** Dummy call handlers ***/
 
     bool CallPreventExtensions(const ObjectId &objId, ReturnStatus *rs) {
         return Base::CallPreventExtensions(objId, rs);
     }
     bool CallGetPropertyDescriptor(const ObjectId &objId, const nsString &id,
                                      ReturnStatus *rs,
                                      PPropertyDescriptor *out) {
--- a/js/ipc/JavaScriptChild.cpp
+++ b/js/ipc/JavaScriptChild.cpp
@@ -42,18 +42,21 @@ JavaScriptChild::trace(JSTracer *trc)
     objects_.trace(trc);
     cpows_.trace(trc);
     ids_.trace(trc);
 }
 
 bool
 JavaScriptChild::init()
 {
-    if (!JavaScriptShared::init())
+    if (!WrapperOwner::init())
         return false;
+    if (!WrapperAnswer::init())
+        return false;
+
     if (!ids_.init())
         return false;
 
     JS_AddExtraGCRootsTracer(rt_, Trace, this);
     return true;
 }
 
 bool
@@ -103,554 +106,8 @@ JavaScriptChild::toId(JSContext *cx, JSO
 JSObject *
 JavaScriptChild::fromId(JSContext *cx, ObjectId id)
 {
     JSObject *obj = findObjectById(id);
     MOZ_ASSERT(obj);
     return obj;
 }
 
-bool
-JavaScriptChild::fail(JSContext *cx, ReturnStatus *rs)
-{
-    // By default, we set |undefined| unless we can get a more meaningful
-    // exception.
-    *rs = ReturnStatus(ReturnException(JSVariant(void_t())));
-
-    // Note we always return true from this function, since this propagates
-    // to the IPC code, and we don't want a JS failure to cause the death
-    // of the child process.
-
-    RootedValue exn(cx);
-    if (!JS_GetPendingException(cx, &exn))
-        return true;
-
-    // If we don't clear the pending exception, JS will try to wrap it as it
-    // leaves the current compartment. Since there is no previous compartment,
-    // that would crash.
-    JS_ClearPendingException(cx);
-
-    if (JS_IsStopIteration(exn)) {
-        *rs = ReturnStatus(ReturnStopIteration());
-        return true;
-    }
-
-    // If this fails, we still don't want to exit. Just return an invalid
-    // exception.
-    (void) toVariant(cx, exn, &rs->get_ReturnException().exn());
-    return true;
-}
-
-bool
-JavaScriptChild::ok(ReturnStatus *rs)
-{
-    *rs = ReturnStatus(ReturnSuccess());
-    return true;
-}
-
-bool
-JavaScriptChild::AnswerPreventExtensions(const ObjectId &objId, ReturnStatus *rs)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-    if (!JS_PreventExtensions(cx, obj))
-        return fail(cx, rs);
-
-    return ok(rs);
-}
-
-static void
-EmptyDesc(PPropertyDescriptor *desc)
-{
-    desc->objId() = 0;
-    desc->attrs() = 0;
-    desc->value() = void_t();
-    desc->getter() = 0;
-    desc->setter() = 0;
-}
-
-bool
-JavaScriptChild::AnswerGetPropertyDescriptor(const ObjectId &objId, const nsString &id,
-                                             ReturnStatus *rs, PPropertyDescriptor *out)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    EmptyDesc(out);
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    RootedId internedId(cx);
-    if (!convertGeckoStringToId(cx, id, &internedId))
-        return fail(cx, rs);
-
-    Rooted<JSPropertyDescriptor> desc(cx);
-    if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
-        return fail(cx, rs);
-
-    if (!desc.object())
-        return ok(rs);
-
-    if (!fromDescriptor(cx, desc, out))
-        return fail(cx, rs);
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerGetOwnPropertyDescriptor(const ObjectId &objId, const nsString &id,
-                                                ReturnStatus *rs, PPropertyDescriptor *out)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    EmptyDesc(out);
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    RootedId internedId(cx);
-    if (!convertGeckoStringToId(cx, id, &internedId))
-        return fail(cx, rs);
-
-    Rooted<JSPropertyDescriptor> desc(cx);
-    if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
-        return fail(cx, rs);
-
-    if (desc.object() != obj)
-        return ok(rs);
-
-    if (!fromDescriptor(cx, desc, out))
-        return fail(cx, rs);
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerDefineProperty(const ObjectId &objId, const nsString &id,
-                                      const PPropertyDescriptor &descriptor, ReturnStatus *rs)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    RootedId internedId(cx);
-    if (!convertGeckoStringToId(cx, id, &internedId))
-        return fail(cx, rs);
-
-    Rooted<JSPropertyDescriptor> desc(cx);
-    if (!toDescriptor(cx, descriptor, &desc))
-        return false;
-
-    if (!js::CheckDefineProperty(cx, obj, internedId, desc.value(), desc.attributes(),
-                                 desc.getter(), desc.setter()))
-    {
-        return fail(cx, rs);
-    }
-
-    if (!JS_DefinePropertyById(cx, obj, internedId, desc.value(), desc.attributes(),
-                               desc.getter(), desc.setter()))
-    {
-        return fail(cx, rs);
-    }
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerDelete(const ObjectId &objId, const nsString &id, ReturnStatus *rs,
-                              bool *success)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    *success = false;
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    RootedId internedId(cx);
-    if (!convertGeckoStringToId(cx, id, &internedId))
-        return fail(cx, rs);
-
-    if (!JS_DeletePropertyById2(cx, obj, internedId, success))
-        return fail(cx, rs);
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerHas(const ObjectId &objId, const nsString &id, ReturnStatus *rs, bool *bp)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    *bp = false;
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    RootedId internedId(cx);
-    if (!convertGeckoStringToId(cx, id, &internedId))
-        return fail(cx, rs);
-
-    bool found;
-    if (!JS_HasPropertyById(cx, obj, internedId, &found))
-        return fail(cx, rs);
-    *bp = !!found;
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerHasOwn(const ObjectId &objId, const nsString &id, ReturnStatus *rs, bool *bp)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    *bp = false;
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    RootedId internedId(cx);
-    if (!convertGeckoStringToId(cx, id, &internedId))
-        return fail(cx, rs);
-
-    Rooted<JSPropertyDescriptor> desc(cx);
-    if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
-        return fail(cx, rs);
-    *bp = (desc.object() == obj);
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerGet(const ObjectId &objId, const ObjectId &receiverId, const nsString &id,
-                           ReturnStatus *rs, JSVariant *result)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    // The outparam will be written to the buffer, so it must be set even if
-    // the parent won't read it.
-    *result = void_t();
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    RootedObject receiver(cx, findObjectById(receiverId));
-    if (!receiver)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    RootedId internedId(cx);
-    if (!convertGeckoStringToId(cx, id, &internedId))
-        return fail(cx, rs);
-
-    JS::RootedValue val(cx);
-    if (!JS_ForwardGetPropertyTo(cx, obj, internedId, receiver, &val))
-        return fail(cx, rs);
-
-    if (!toVariant(cx, val, result))
-        return fail(cx, rs);
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerSet(const ObjectId &objId, const ObjectId &receiverId, const nsString &id,
-                           const bool &strict, const JSVariant &value, ReturnStatus *rs,
-                           JSVariant *result)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    // The outparam will be written to the buffer, so it must be set even if
-    // the parent won't read it.
-    *result = void_t();
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    RootedObject receiver(cx, findObjectById(receiverId));
-    if (!receiver)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    RootedId internedId(cx);
-    if (!convertGeckoStringToId(cx, id, &internedId))
-        return fail(cx, rs);
-
-    MOZ_ASSERT(obj == receiver);
-
-    RootedValue val(cx);
-    if (!fromVariant(cx, value, &val))
-        return fail(cx, rs);
-
-    if (!JS_SetPropertyById(cx, obj, internedId, val))
-        return fail(cx, rs);
-
-    if (!toVariant(cx, val, result))
-        return fail(cx, rs);
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs, bool *result)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    *result = false;
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    bool extensible;
-    if (!JS_IsExtensible(cx, obj, &extensible))
-        return fail(cx, rs);
-
-    *result = !!extensible;
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv, ReturnStatus *rs,
-                            JSVariant *result, nsTArray<JSParam> *outparams)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    // The outparam will be written to the buffer, so it must be set even if
-    // the parent won't read it.
-    *result = void_t();
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    MOZ_ASSERT(argv.Length() >= 2);
-
-    RootedValue objv(cx);
-    if (!fromVariant(cx, argv[0], &objv))
-        return fail(cx, rs);
-
-    JSAutoCompartment comp(cx, &objv.toObject());
-
-    *result = JSVariant(void_t());
-
-    AutoValueVector vals(cx);
-    AutoValueVector outobjects(cx);
-    for (size_t i = 0; i < argv.Length(); i++) {
-        if (argv[i].type() == JSParam::Tvoid_t) {
-            // This is an outparam.
-            JSCompartment *compartment = js::GetContextCompartment(cx);
-            RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, compartment));
-            RootedObject obj(cx, xpc::NewOutObject(cx, global));
-            if (!obj)
-                return fail(cx, rs);
-            if (!outobjects.append(ObjectValue(*obj)))
-                return fail(cx, rs);
-            if (!vals.append(ObjectValue(*obj)))
-                return fail(cx, rs);
-        } else {
-            RootedValue v(cx);
-            if (!fromVariant(cx, argv[i].get_JSVariant(), &v))
-                return fail(cx, rs);
-            if (!vals.append(v))
-                return fail(cx, rs);
-        }
-    }
-
-    RootedValue rval(cx);
-    {
-        AutoSaveContextOptions asco(cx);
-        ContextOptionsRef(cx).setDontReportUncaught(true);
-
-        HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2);
-        bool success = JS::Call(cx, vals[1], vals[0], args, &rval);
-        if (!success)
-            return fail(cx, rs);
-    }
-
-    if (!toVariant(cx, rval, result))
-        return fail(cx, rs);
-
-    // Prefill everything with a dummy jsval.
-    for (size_t i = 0; i < outobjects.length(); i++)
-        outparams->AppendElement(JSParam(void_t()));
-
-    // Go through each argument that was an outparam, retrieve the "value"
-    // field, and add it to a temporary list. We need to do this separately
-    // because the outparams vector is not rooted.
-    vals.clear();
-    for (size_t i = 0; i < outobjects.length(); i++) {
-        RootedObject obj(cx, &outobjects[i].toObject());
-
-        RootedValue v(cx);
-        bool found;
-        if (JS_HasProperty(cx, obj, "value", &found)) {
-            if (!JS_GetProperty(cx, obj, "value", &v))
-                return fail(cx, rs);
-        } else {
-            v = UndefinedValue();
-        }
-        if (!vals.append(v))
-            return fail(cx, rs);
-    }
-
-    // Copy the outparams. If any outparam is already set to a void_t, we
-    // treat this as the outparam never having been set.
-    for (size_t i = 0; i < vals.length(); i++) {
-        JSVariant variant;
-        if (!toVariant(cx, vals[i], &variant))
-            return fail(cx, rs);
-        outparams->ReplaceElementAt(i, JSParam(variant));
-    }
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
-                                     bool *result)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    *result = js_ObjectClassIs(cx, obj, (js::ESClassValue)classValue);
-    return true;
-}
-
-bool
-JavaScriptChild::AnswerClassName(const ObjectId &objId, nsString *name)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    *name = NS_ConvertASCIItoUTF16(js_ObjectClassName(cx, obj));
-    return true;
-}
-
-bool
-JavaScriptChild::AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
-                                        ReturnStatus *rs, nsTArray<nsString> *names)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    AutoIdVector props(cx);
-    if (!js::GetPropertyNames(cx, obj, flags, &props))
-        return fail(cx, rs);
-
-    for (size_t i = 0; i < props.length(); i++) {
-        nsString name;
-        if (!convertIdToGeckoString(cx, props[i], &name))
-            return fail(cx, rs);
-
-        names->AppendElement(name);
-    }
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerInstanceOf(const ObjectId &objId, const JSIID &iid, ReturnStatus *rs,
-                                  bool *instanceof)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    *instanceof = false;
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    nsID nsiid;
-    ConvertID(iid, &nsiid);
-
-    nsresult rv = xpc::HasInstance(cx, obj, &nsiid, instanceof);
-    if (rv != NS_OK)
-        return fail(cx, rs);
-
-    return ok(rs);
-}
-
-bool
-JavaScriptChild::AnswerDOMInstanceOf(const ObjectId &objId, const int &prototypeID,
-                                     const int &depth,
-                                     ReturnStatus *rs, bool *instanceof)
-{
-    AutoSafeJSContext cx;
-    JSAutoRequest request(cx);
-
-    *instanceof = false;
-
-    RootedObject obj(cx, findObjectById(objId));
-    if (!obj)
-        return false;
-
-    JSAutoCompartment comp(cx, obj);
-
-    bool tmp;
-    if (!mozilla::dom::InterfaceHasInstance(cx, prototypeID, depth, obj, &tmp))
-        return fail(cx, rs);
-    *instanceof = tmp;
-
-    return ok(rs);
-}
--- a/js/ipc/JavaScriptChild.h
+++ b/js/ipc/JavaScriptChild.h
@@ -3,75 +3,34 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_jsipc_JavaScriptChild_h_
 #define mozilla_jsipc_JavaScriptChild_h_
 
-#include "JavaScriptShared.h"
+#include "JavaScriptBase.h"
 #include "mozilla/jsipc/PJavaScriptChild.h"
 
 namespace mozilla {
 namespace jsipc {
 
-class JavaScriptChild
-  : public PJavaScriptChild,
-    public JavaScriptShared
+class JavaScriptChild : public JavaScriptBase<PJavaScriptChild>
 {
   public:
     JavaScriptChild(JSRuntime *rt);
     ~JavaScriptChild();
 
     bool init();
     void trace(JSTracer *trc);
 
     bool RecvDropObject(const ObjectId &objId) MOZ_OVERRIDE;
 
-    bool AnswerPreventExtensions(const ObjectId &objId, ReturnStatus *rs) MOZ_OVERRIDE;
-    bool AnswerGetPropertyDescriptor(const ObjectId &objId, const nsString &id,
-                                     ReturnStatus *rs,
-                                     PPropertyDescriptor *out) MOZ_OVERRIDE;
-    bool AnswerGetOwnPropertyDescriptor(const ObjectId &objId,
-                                        const nsString &id,
-                                        ReturnStatus *rs,
-                                        PPropertyDescriptor *out) MOZ_OVERRIDE;
-    bool AnswerDefineProperty(const ObjectId &objId, const nsString &id,
-                              const PPropertyDescriptor &flags,
-                              ReturnStatus *rs) MOZ_OVERRIDE;
-    bool AnswerDelete(const ObjectId &objId, const nsString &id,
-                      ReturnStatus *rs, bool *success) MOZ_OVERRIDE;
-
-    bool AnswerHas(const ObjectId &objId, const nsString &id,
-                       ReturnStatus *rs, bool *bp) MOZ_OVERRIDE;
-    bool AnswerHasOwn(const ObjectId &objId, const nsString &id,
-                          ReturnStatus *rs, bool *bp) MOZ_OVERRIDE;
-    bool AnswerGet(const ObjectId &objId, const ObjectId &receiverId,
-                       const nsString &id,
-                       ReturnStatus *rs, JSVariant *result) MOZ_OVERRIDE;
-    bool AnswerSet(const ObjectId &objId, const ObjectId &receiverId,
-                   const nsString &id, const bool &strict,
-                   const JSVariant &value, ReturnStatus *rs, JSVariant *result) MOZ_OVERRIDE;
-
-    bool AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs,
-                            bool *result) MOZ_OVERRIDE;
-    bool AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv,
-                    ReturnStatus *rs, JSVariant *result,
-                    nsTArray<JSParam> *outparams) MOZ_OVERRIDE;
-    bool AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
-                             bool *result) MOZ_OVERRIDE;
-    bool AnswerClassName(const ObjectId &objId, nsString *result) MOZ_OVERRIDE;
-
-    bool AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
-                                ReturnStatus *rs, nsTArray<nsString> *names) MOZ_OVERRIDE;
-    bool AnswerInstanceOf(const ObjectId &objId, const JSIID &iid,
-                          ReturnStatus *rs, bool *instanceof) MOZ_OVERRIDE;
-    bool AnswerDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth,
-                             ReturnStatus *rs, bool *instanceof) MOZ_OVERRIDE;
+    virtual void drop(JSObject *obj) { MOZ_CRASH(); }
 
   private:
     JSObject *fromId(JSContext *cx, ObjectId id);
     bool toId(JSContext *cx, JSObject *obj, ObjectId *idp);
 
     bool fail(JSContext *cx, ReturnStatus *rs);
     bool ok(ReturnStatus *rs);
 
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -18,16 +18,18 @@ using namespace mozilla::jsipc;
 IdToObjectMap::IdToObjectMap()
   : table_(SystemAllocPolicy())
 {
 }
 
 bool
 IdToObjectMap::init()
 {
+    if (table_.initialized())
+        return true;
     return table_.init(32);
 }
 
 void
 IdToObjectMap::trace(JSTracer *trc)
 {
     for (Table::Range r(table_.all()); !r.empty(); r.popFront()) {
         DebugOnly<JSObject *> prior = r.front().value().get();
@@ -68,17 +70,19 @@ ObjectToIdMap::~ObjectToIdMap()
         dom::AddForDeferredFinalization<Table, nsAutoPtr>(table_);
         table_ = nullptr;
     }
 }
 
 bool
 ObjectToIdMap::init()
 {
-    MOZ_ASSERT(!table_);
+    if (table_)
+        return true;
+
     table_ = new Table(SystemAllocPolicy());
     return table_ && table_->init(32);
 }
 
 void
 ObjectToIdMap::trace(JSTracer *trc)
 {
     for (Table::Range r(table_->all()); !r.empty(); r.popFront()) {
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -17,16 +17,17 @@ namespace jsipc {
 intr protocol PJavaScript
 {
     manager PContent;
 
 child:
     // The parent process no longer holds any references to the child object.
     async DropObject(uint64_t objId);
 
+both:
     // These roughly map to the ProxyHandler hooks that CPOWs need.
     rpc PreventExtensions(uint64_t objId) returns (ReturnStatus rs);
     rpc GetPropertyDescriptor(uint64_t objId, nsString id) returns (ReturnStatus rs, PPropertyDescriptor result);
     rpc GetOwnPropertyDescriptor(uint64_t objId, nsString id) returns (ReturnStatus rs, PPropertyDescriptor result);
     rpc DefineProperty(uint64_t objId, nsString id, PPropertyDescriptor descriptor) returns (ReturnStatus rs);
     rpc Delete(uint64_t objId, nsString id) returns (ReturnStatus rs, bool successful);
 
     rpc Has(uint64_t objId, nsString id) returns (ReturnStatus rs, bool has);
new file mode 100644
--- /dev/null
+++ b/js/ipc/WrapperAnswer.cpp
@@ -0,0 +1,578 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=80:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WrapperAnswer.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "nsContentUtils.h"
+#include "xpcprivate.h"
+#include "jsfriendapi.h"
+#include "nsCxPusher.h"
+
+using namespace JS;
+using namespace mozilla;
+using namespace mozilla::jsipc;
+
+using mozilla::AutoSafeJSContext;
+
+bool
+WrapperAnswer::fail(JSContext *cx, ReturnStatus *rs)
+{
+    // By default, we set |undefined| unless we can get a more meaningful
+    // exception.
+    *rs = ReturnStatus(ReturnException(JSVariant(UndefinedVariant())));
+
+    // Note we always return true from this function, since this propagates
+    // to the IPC code, and we don't want a JS failure to cause the death
+    // of the child process.
+
+    RootedValue exn(cx);
+    if (!JS_GetPendingException(cx, &exn))
+        return true;
+
+    // If we don't clear the pending exception, JS will try to wrap it as it
+    // leaves the current compartment. Since there is no previous compartment,
+    // that would crash.
+    JS_ClearPendingException(cx);
+
+    if (JS_IsStopIteration(exn)) {
+        *rs = ReturnStatus(ReturnStopIteration());
+        return true;
+    }
+
+    // If this fails, we still don't want to exit. Just return an invalid
+    // exception.
+    (void) toVariant(cx, exn, &rs->get_ReturnException().exn());
+    return true;
+}
+
+bool
+WrapperAnswer::ok(ReturnStatus *rs)
+{
+    *rs = ReturnStatus(ReturnSuccess());
+    return true;
+}
+
+bool
+WrapperAnswer::AnswerPreventExtensions(const ObjectId &objId, ReturnStatus *rs)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+    if (!JS_PreventExtensions(cx, obj))
+        return fail(cx, rs);
+
+    return ok(rs);
+}
+
+static void
+EmptyDesc(PPropertyDescriptor *desc)
+{
+    desc->obj() = LocalObject(0);
+    desc->attrs() = 0;
+    desc->value() = UndefinedVariant();
+    desc->getter() = 0;
+    desc->setter() = 0;
+}
+
+bool
+WrapperAnswer::AnswerGetPropertyDescriptor(const ObjectId &objId, const nsString &id,
+					   ReturnStatus *rs, PPropertyDescriptor *out)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    EmptyDesc(out);
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    RootedId internedId(cx);
+    if (!convertGeckoStringToId(cx, id, &internedId))
+        return fail(cx, rs);
+
+    Rooted<JSPropertyDescriptor> desc(cx);
+    if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
+        return fail(cx, rs);
+
+    if (!desc.object())
+        return ok(rs);
+
+    if (!fromDescriptor(cx, desc, out))
+        return fail(cx, rs);
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerGetOwnPropertyDescriptor(const ObjectId &objId, const nsString &id,
+					      ReturnStatus *rs, PPropertyDescriptor *out)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    EmptyDesc(out);
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    RootedId internedId(cx);
+    if (!convertGeckoStringToId(cx, id, &internedId))
+        return fail(cx, rs);
+
+    Rooted<JSPropertyDescriptor> desc(cx);
+    if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
+        return fail(cx, rs);
+
+    if (desc.object() != obj)
+        return ok(rs);
+
+    if (!fromDescriptor(cx, desc, out))
+        return fail(cx, rs);
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerDefineProperty(const ObjectId &objId, const nsString &id,
+				    const PPropertyDescriptor &descriptor, ReturnStatus *rs)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    RootedId internedId(cx);
+    if (!convertGeckoStringToId(cx, id, &internedId))
+        return fail(cx, rs);
+
+    Rooted<JSPropertyDescriptor> desc(cx);
+    if (!toDescriptor(cx, descriptor, &desc))
+        return false;
+
+    if (!js::CheckDefineProperty(cx, obj, internedId, desc.value(), desc.attributes(),
+                                 desc.getter(), desc.setter()))
+    {
+        return fail(cx, rs);
+    }
+
+    if (!JS_DefinePropertyById(cx, obj, internedId, desc.value(), desc.attributes(),
+                               desc.getter(), desc.setter()))
+    {
+        return fail(cx, rs);
+    }
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerDelete(const ObjectId &objId, const nsString &id, ReturnStatus *rs,
+			    bool *success)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    *success = false;
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    RootedId internedId(cx);
+    if (!convertGeckoStringToId(cx, id, &internedId))
+        return fail(cx, rs);
+
+    if (!JS_DeletePropertyById2(cx, obj, internedId, success))
+        return fail(cx, rs);
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerHas(const ObjectId &objId, const nsString &id, ReturnStatus *rs, bool *bp)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    *bp = false;
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    RootedId internedId(cx);
+    if (!convertGeckoStringToId(cx, id, &internedId))
+        return fail(cx, rs);
+
+    bool found;
+    if (!JS_HasPropertyById(cx, obj, internedId, &found))
+        return fail(cx, rs);
+    *bp = !!found;
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerHasOwn(const ObjectId &objId, const nsString &id, ReturnStatus *rs, bool *bp)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    *bp = false;
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    RootedId internedId(cx);
+    if (!convertGeckoStringToId(cx, id, &internedId))
+        return fail(cx, rs);
+
+    Rooted<JSPropertyDescriptor> desc(cx);
+    if (!JS_GetPropertyDescriptorById(cx, obj, internedId, &desc))
+        return fail(cx, rs);
+    *bp = (desc.object() == obj);
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerGet(const ObjectId &objId, const ObjectId &receiverId, const nsString &id,
+			 ReturnStatus *rs, JSVariant *result)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    // The outparam will be written to the buffer, so it must be set even if
+    // the parent won't read it.
+    *result = UndefinedVariant();
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    RootedObject receiver(cx, findObjectById(receiverId));
+    if (!receiver)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    RootedId internedId(cx);
+    if (!convertGeckoStringToId(cx, id, &internedId))
+        return fail(cx, rs);
+
+    JS::RootedValue val(cx);
+    if (!JS_ForwardGetPropertyTo(cx, obj, internedId, receiver, &val))
+        return fail(cx, rs);
+
+    if (!toVariant(cx, val, result))
+        return fail(cx, rs);
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerSet(const ObjectId &objId, const ObjectId &receiverId, const nsString &id,
+			 const bool &strict, const JSVariant &value, ReturnStatus *rs,
+			 JSVariant *result)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    // The outparam will be written to the buffer, so it must be set even if
+    // the parent won't read it.
+    *result = UndefinedVariant();
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    RootedObject receiver(cx, findObjectById(receiverId));
+    if (!receiver)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    RootedId internedId(cx);
+    if (!convertGeckoStringToId(cx, id, &internedId))
+        return fail(cx, rs);
+
+    MOZ_ASSERT(obj == receiver);
+
+    RootedValue val(cx);
+    if (!fromVariant(cx, value, &val))
+        return fail(cx, rs);
+
+    if (!JS_SetPropertyById(cx, obj, internedId, val))
+        return fail(cx, rs);
+
+    if (!toVariant(cx, val, result))
+        return fail(cx, rs);
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs, bool *result)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    *result = false;
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    bool extensible;
+    if (!JS_IsExtensible(cx, obj, &extensible))
+        return fail(cx, rs);
+
+    *result = !!extensible;
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv, ReturnStatus *rs,
+			  JSVariant *result, nsTArray<JSParam> *outparams)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    // The outparam will be written to the buffer, so it must be set even if
+    // the parent won't read it.
+    *result = UndefinedVariant();
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    MOZ_ASSERT(argv.Length() >= 2);
+
+    RootedValue objv(cx);
+    if (!fromVariant(cx, argv[0], &objv))
+        return fail(cx, rs);
+
+    *result = JSVariant(UndefinedVariant());
+
+    AutoValueVector vals(cx);
+    AutoValueVector outobjects(cx);
+    for (size_t i = 0; i < argv.Length(); i++) {
+        if (argv[i].type() == JSParam::Tvoid_t) {
+            // This is an outparam.
+            JSCompartment *compartment = js::GetContextCompartment(cx);
+            RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, compartment));
+            RootedObject obj(cx, xpc::NewOutObject(cx, global));
+            if (!obj)
+                return fail(cx, rs);
+            if (!outobjects.append(ObjectValue(*obj)))
+                return fail(cx, rs);
+            if (!vals.append(ObjectValue(*obj)))
+                return fail(cx, rs);
+        } else {
+            RootedValue v(cx);
+            if (!fromVariant(cx, argv[i].get_JSVariant(), &v))
+                return fail(cx, rs);
+            if (!vals.append(v))
+                return fail(cx, rs);
+        }
+    }
+
+    RootedValue rval(cx);
+    {
+        AutoSaveContextOptions asco(cx);
+        ContextOptionsRef(cx).setDontReportUncaught(true);
+
+        HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2);
+        bool success = JS::Call(cx, vals[1], vals[0], args, &rval);
+        if (!success)
+            return fail(cx, rs);
+    }
+
+    if (!toVariant(cx, rval, result))
+        return fail(cx, rs);
+
+    // Prefill everything with a dummy jsval.
+    for (size_t i = 0; i < outobjects.length(); i++)
+        outparams->AppendElement(JSParam(void_t()));
+
+    // Go through each argument that was an outparam, retrieve the "value"
+    // field, and add it to a temporary list. We need to do this separately
+    // because the outparams vector is not rooted.
+    vals.clear();
+    for (size_t i = 0; i < outobjects.length(); i++) {
+        RootedObject obj(cx, &outobjects[i].toObject());
+
+        RootedValue v(cx);
+        bool found;
+        if (JS_HasProperty(cx, obj, "value", &found)) {
+            if (!JS_GetProperty(cx, obj, "value", &v))
+                return fail(cx, rs);
+        } else {
+            v = UndefinedValue();
+        }
+        if (!vals.append(v))
+            return fail(cx, rs);
+    }
+
+    // Copy the outparams. If any outparam is already set to a void_t, we
+    // treat this as the outparam never having been set.
+    for (size_t i = 0; i < vals.length(); i++) {
+        JSVariant variant;
+        if (!toVariant(cx, vals[i], &variant))
+            return fail(cx, rs);
+        outparams->ReplaceElementAt(i, JSParam(variant));
+    }
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
+				   bool *result)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    *result = js_ObjectClassIs(cx, obj, (js::ESClassValue)classValue);
+    return true;
+}
+
+bool
+WrapperAnswer::AnswerClassName(const ObjectId &objId, nsString *name)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    *name = NS_ConvertASCIItoUTF16(js_ObjectClassName(cx, obj));
+    return true;
+}
+
+bool
+WrapperAnswer::AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
+				      ReturnStatus *rs, nsTArray<nsString> *names)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    AutoIdVector props(cx);
+    if (!js::GetPropertyNames(cx, obj, flags, &props))
+        return fail(cx, rs);
+
+    for (size_t i = 0; i < props.length(); i++) {
+        nsString name;
+        if (!convertIdToGeckoString(cx, props[i], &name))
+            return fail(cx, rs);
+
+        names->AppendElement(name);
+    }
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerInstanceOf(const ObjectId &objId, const JSIID &iid, ReturnStatus *rs,
+				bool *instanceof)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    *instanceof = false;
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    nsID nsiid;
+    ConvertID(iid, &nsiid);
+
+    nsresult rv = xpc::HasInstance(cx, obj, &nsiid, instanceof);
+    if (rv != NS_OK)
+        return fail(cx, rs);
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::AnswerDOMInstanceOf(const ObjectId &objId, const int &prototypeID,
+				   const int &depth,
+				   ReturnStatus *rs, bool *instanceof)
+{
+    AutoSafeJSContext cx;
+    JSAutoRequest request(cx);
+
+    *instanceof = false;
+
+    RootedObject obj(cx, findObjectById(objId));
+    if (!obj)
+        return false;
+
+    JSAutoCompartment comp(cx, obj);
+
+    bool tmp;
+    if (!mozilla::dom::InterfaceHasInstance(cx, prototypeID, depth, obj, &tmp))
+        return fail(cx, rs);
+    *instanceof = tmp;
+
+    return ok(rs);
+}
+
+bool
+WrapperAnswer::RecvDropObject(const ObjectId &objId)
+{
+    JSObject *obj = findObjectById(objId);
+    if (obj) {
+        objectIds_.remove(obj);
+        objects_.remove(objId);
+    }
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/js/ipc/WrapperAnswer.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=80:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_jsipc_WrapperAnswer_h_
+#define mozilla_jsipc_WrapperAnswer_h_
+
+#include "JavaScriptShared.h"
+
+namespace mozilla {
+namespace jsipc {
+
+class WrapperAnswer : public virtual JavaScriptShared
+{
+  public:
+    WrapperAnswer(JSRuntime *rt) : JavaScriptShared(rt) {}
+
+    bool AnswerPreventExtensions(const ObjectId &objId, ReturnStatus *rs);
+    bool AnswerGetPropertyDescriptor(const ObjectId &objId, const nsString &id,
+                                     ReturnStatus *rs,
+                                     PPropertyDescriptor *out);
+    bool AnswerGetOwnPropertyDescriptor(const ObjectId &objId,
+                                        const nsString &id,
+                                        ReturnStatus *rs,
+                                        PPropertyDescriptor *out);
+    bool AnswerDefineProperty(const ObjectId &objId, const nsString &id,
+                              const PPropertyDescriptor &flags,
+                              ReturnStatus *rs);
+    bool AnswerDelete(const ObjectId &objId, const nsString &id,
+                      ReturnStatus *rs, bool *success);
+
+    bool AnswerHas(const ObjectId &objId, const nsString &id,
+                       ReturnStatus *rs, bool *bp);
+    bool AnswerHasOwn(const ObjectId &objId, const nsString &id,
+                          ReturnStatus *rs, bool *bp);
+    bool AnswerGet(const ObjectId &objId, const ObjectId &receiverId,
+                       const nsString &id,
+                       ReturnStatus *rs, JSVariant *result);
+    bool AnswerSet(const ObjectId &objId, const ObjectId &receiverId,
+                   const nsString &id, const bool &strict,
+                   const JSVariant &value, ReturnStatus *rs, JSVariant *result);
+
+    bool AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs,
+                            bool *result);
+    bool AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv,
+                    ReturnStatus *rs, JSVariant *result,
+                    nsTArray<JSParam> *outparams);
+    bool AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
+                             bool *result);
+    bool AnswerClassName(const ObjectId &objId, nsString *result);
+
+    bool AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
+                                ReturnStatus *rs, nsTArray<nsString> *names);
+    bool AnswerInstanceOf(const ObjectId &objId, const JSIID &iid,
+                          ReturnStatus *rs, bool *instanceof);
+    bool AnswerDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth,
+                             ReturnStatus *rs, bool *instanceof);
+
+    bool RecvDropObject(const ObjectId &objId);
+
+  private:
+    bool fail(JSContext *cx, ReturnStatus *rs);
+    bool ok(ReturnStatus *rs);
+};
+
+} // mozilla
+} // jsipc
+
+#endif
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -19,17 +19,17 @@
 
 namespace js {
 class BaseProxyHandler;
 } // js
 
 namespace mozilla {
 namespace jsipc {
 
-class WrapperOwner : public JavaScriptShared
+class WrapperOwner : public virtual JavaScriptShared
 {
   public:
     typedef mozilla::ipc::IProtocolManager<
                        mozilla::ipc::IProtocol>::ActorDestroyReason
            ActorDestroyReason;
 
     WrapperOwner();
     bool init();
--- a/js/ipc/moz.build
+++ b/js/ipc/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'JavaScriptChild.cpp',
     'JavaScriptParent.cpp',
     'JavaScriptShared.cpp',
+    'WrapperAnswer.cpp',
     'WrapperOwner.cpp',
 ]
 
 IPDL_SOURCES += [
     'JavaScriptTypes.ipdlh',
     'PJavaScript.ipdl',
 ]