Bug 996785 - Move CPOW wrapper answering code (r=mrbkap)
authorBill McCloskey <wmccloskey@mozilla.com>
Fri, 16 May 2014 16:40:36 -0700
changeset 183624 05fa4a69cb9818df96d82c9fc37fdad57ed85f68
parent 183623 15ecb06251c8b7e886dec794e42c923fa8ab6217
child 183625 1e495c6dba19835297494eb2a128301fe9e55848
push id43608
push userwmccloskey@mozilla.com
push dateFri, 16 May 2014 23:41:10 +0000
treeherdermozilla-inbound@4f0a7452db01 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs996785
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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',
 ]