summaryrefslogtreecommitdiffstats
path: root/js/ipc/WrapperOwner.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/ipc/WrapperOwner.cpp')
-rw-r--r--js/ipc/WrapperOwner.cpp1236
1 files changed, 1236 insertions, 0 deletions
diff --git a/js/ipc/WrapperOwner.cpp b/js/ipc/WrapperOwner.cpp
new file mode 100644
index 000000000..427ec6ab3
--- /dev/null
+++ b/js/ipc/WrapperOwner.cpp
@@ -0,0 +1,1236 @@
+/* -*- 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 "WrapperOwner.h"
+#include "JavaScriptLogging.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "jsfriendapi.h"
+#include "js/CharacterEncoding.h"
+#include "xpcprivate.h"
+#include "CPOWTimer.h"
+#include "WrapperFactory.h"
+
+#include "nsIDocShellTreeItem.h"
+#include "nsIDOMDocument.h"
+
+using namespace js;
+using namespace JS;
+using namespace mozilla;
+using namespace mozilla::jsipc;
+
+struct AuxCPOWData
+{
+ ObjectId id;
+ bool isCallable;
+ bool isConstructor;
+ bool isDOMObject;
+
+ // The object tag is just some auxilliary information that clients can use
+ // however they see fit.
+ nsCString objectTag;
+
+ // The class name for WrapperOwner::className, below.
+ nsCString className;
+
+ AuxCPOWData(ObjectId id,
+ bool isCallable,
+ bool isConstructor,
+ bool isDOMObject,
+ const nsACString& objectTag)
+ : id(id),
+ isCallable(isCallable),
+ isConstructor(isConstructor),
+ isDOMObject(isDOMObject),
+ objectTag(objectTag)
+ {}
+};
+
+WrapperOwner::WrapperOwner()
+ : inactive_(false)
+{
+}
+
+static inline AuxCPOWData*
+AuxCPOWDataOf(JSObject* obj)
+{
+ MOZ_ASSERT(IsCPOW(obj));
+ return static_cast<AuxCPOWData*>(GetProxyExtra(obj, 1).toPrivate());
+}
+
+static inline WrapperOwner*
+OwnerOf(JSObject* obj)
+{
+ MOZ_ASSERT(IsCPOW(obj));
+ return reinterpret_cast<WrapperOwner*>(GetProxyExtra(obj, 0).toPrivate());
+}
+
+ObjectId
+WrapperOwner::idOfUnchecked(JSObject* obj)
+{
+ MOZ_ASSERT(IsCPOW(obj));
+
+ AuxCPOWData* aux = AuxCPOWDataOf(obj);
+ MOZ_ASSERT(!aux->id.isNull());
+ return aux->id;
+}
+
+ObjectId
+WrapperOwner::idOf(JSObject* obj)
+{
+ ObjectId objId = idOfUnchecked(obj);
+ MOZ_ASSERT(hasCPOW(objId, obj));
+ return objId;
+}
+
+class CPOWProxyHandler : public BaseProxyHandler
+{
+ public:
+ constexpr CPOWProxyHandler()
+ : BaseProxyHandler(&family) {}
+
+ virtual bool finalizeInBackground(const Value& priv) const override {
+ return false;
+ }
+
+ virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
+ MutableHandle<PropertyDescriptor> desc) const override;
+ virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
+ Handle<PropertyDescriptor> desc,
+ ObjectOpResult& result) const override;
+ virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
+ AutoIdVector& props) const override;
+ virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
+ ObjectOpResult& result) const override;
+ virtual bool enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const override;
+ virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
+ ObjectOpResult& result) const override;
+ virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override;
+ virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
+ virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
+ HandleId id, MutableHandleValue vp) const override;
+ virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
+ JS::HandleValue receiver, JS::ObjectOpResult& result) const override;
+ virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
+ virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
+
+ virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
+ MutableHandle<PropertyDescriptor> desc) const override;
+ virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
+ virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
+ AutoIdVector& props) const override;
+ virtual bool hasInstance(JSContext* cx, HandleObject proxy,
+ MutableHandleValue v, bool* bp) const override;
+ virtual bool getBuiltinClass(JSContext* cx, HandleObject obj, js::ESClass* cls) const override;
+ virtual bool isArray(JSContext* cx, HandleObject obj,
+ IsArrayAnswer* answer) const override;
+ virtual const char* className(JSContext* cx, HandleObject proxy) const override;
+ virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
+ virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
+ virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
+ virtual bool isCallable(JSObject* obj) const override;
+ virtual bool isConstructor(JSObject* obj) const override;
+ virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override;
+ virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
+ MutableHandleObject protop) const override;
+
+ static const char family;
+ static const CPOWProxyHandler singleton;
+};
+
+const char CPOWProxyHandler::family = 0;
+const CPOWProxyHandler CPOWProxyHandler::singleton;
+
+#define FORWARD(call, args) \
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::JS); \
+ WrapperOwner* owner = OwnerOf(proxy); \
+ if (!owner->active()) { \
+ JS_ReportErrorASCII(cx, "cannot use a CPOW whose process is gone"); \
+ return false; \
+ } \
+ if (!owner->allowMessage(cx)) { \
+ return false; \
+ } \
+ { \
+ CPOWTimer timer(cx); \
+ return owner->call args; \
+ }
+
+bool
+CPOWProxyHandler::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
+ MutableHandle<PropertyDescriptor> desc) const
+{
+ FORWARD(getPropertyDescriptor, (cx, proxy, id, desc));
+}
+
+bool
+WrapperOwner::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
+ MutableHandle<PropertyDescriptor> desc)
+{
+ ObjectId objId = idOf(proxy);
+
+ JSIDVariant idVar;
+ if (!toJSIDVariant(cx, id, &idVar))
+ return false;
+
+ ReturnStatus status;
+ PPropertyDescriptor result;
+ if (!SendGetPropertyDescriptor(objId, idVar, &status, &result))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ if (!ok(cx, status))
+ return false;
+
+ return toDescriptor(cx, result, desc);
+}
+
+bool
+CPOWProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
+ MutableHandle<PropertyDescriptor> desc) const
+{
+ FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc));
+}
+
+bool
+WrapperOwner::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
+ MutableHandle<PropertyDescriptor> desc)
+{
+ ObjectId objId = idOf(proxy);
+
+ JSIDVariant idVar;
+ if (!toJSIDVariant(cx, id, &idVar))
+ return false;
+
+ ReturnStatus status;
+ PPropertyDescriptor result;
+ if (!SendGetOwnPropertyDescriptor(objId, idVar, &status, &result))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ if (!ok(cx, status))
+ return false;
+
+ return toDescriptor(cx, result, desc);
+}
+
+bool
+CPOWProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
+ Handle<PropertyDescriptor> desc,
+ ObjectOpResult& result) const
+{
+ FORWARD(defineProperty, (cx, proxy, id, desc, result));
+}
+
+bool
+WrapperOwner::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
+ Handle<PropertyDescriptor> desc,
+ ObjectOpResult& result)
+{
+ ObjectId objId = idOf(proxy);
+
+ JSIDVariant idVar;
+ if (!toJSIDVariant(cx, id, &idVar))
+ return false;
+
+ PPropertyDescriptor descriptor;
+ if (!fromDescriptor(cx, desc, &descriptor))
+ return false;
+
+ ReturnStatus status;
+ if (!SendDefineProperty(objId, idVar, descriptor, &status))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ return ok(cx, status, result);
+}
+
+bool
+CPOWProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy,
+ AutoIdVector& props) const
+{
+ FORWARD(ownPropertyKeys, (cx, proxy, props));
+}
+
+bool
+WrapperOwner::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
+{
+ return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
+}
+
+bool
+CPOWProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id,
+ ObjectOpResult& result) const
+{
+ FORWARD(delete_, (cx, proxy, id, result));
+}
+
+bool
+WrapperOwner::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result)
+{
+ ObjectId objId = idOf(proxy);
+
+ JSIDVariant idVar;
+ if (!toJSIDVariant(cx, id, &idVar))
+ return false;
+
+ ReturnStatus status;
+ if (!SendDelete(objId, idVar, &status))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ return ok(cx, status, result);
+}
+
+bool
+CPOWProxyHandler::enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const
+{
+ // Using a CPOW for the Iterator would slow down for .. in performance, instead
+ // call the base hook, that will use our implementation of getOwnEnumerablePropertyKeys
+ // and follow the proto chain.
+ return BaseProxyHandler::enumerate(cx, proxy, objp);
+}
+
+bool
+CPOWProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
+{
+ FORWARD(has, (cx, proxy, id, bp));
+}
+
+bool
+WrapperOwner::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
+{
+ ObjectId objId = idOf(proxy);
+
+ JSIDVariant idVar;
+ if (!toJSIDVariant(cx, id, &idVar))
+ return false;
+
+ ReturnStatus status;
+ if (!SendHas(objId, idVar, &status, bp))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ return ok(cx, status);
+}
+
+bool
+CPOWProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
+{
+ FORWARD(hasOwn, (cx, proxy, id, bp));
+}
+
+bool
+WrapperOwner::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
+{
+ ObjectId objId = idOf(proxy);
+
+ JSIDVariant idVar;
+ if (!toJSIDVariant(cx, id, &idVar))
+ return false;
+
+ ReturnStatus status;
+ if (!SendHasOwn(objId, idVar, &status, bp))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ return !!ok(cx, status);
+}
+
+bool
+CPOWProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
+ HandleId id, MutableHandleValue vp) const
+{
+ FORWARD(get, (cx, proxy, receiver, id, vp));
+}
+
+static bool
+CPOWDOMQI(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (!args.thisv().isObject() || !IsCPOW(&args.thisv().toObject())) {
+ JS_ReportErrorASCII(cx, "bad this object passed to special QI");
+ return false;
+ }
+
+ RootedObject proxy(cx, &args.thisv().toObject());
+ FORWARD(DOMQI, (cx, proxy, args));
+}
+
+static bool
+CPOWToString(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RootedObject callee(cx, &args.callee());
+ RootedValue cpowValue(cx);
+ if (!JS_GetProperty(cx, callee, "__cpow__", &cpowValue))
+ return false;
+
+ if (!cpowValue.isObject() || !IsCPOW(&cpowValue.toObject())) {
+ JS_ReportErrorASCII(cx, "CPOWToString called on an incompatible object");
+ return false;
+ }
+
+ RootedObject proxy(cx, &cpowValue.toObject());
+ FORWARD(toString, (cx, proxy, args));
+}
+
+bool
+WrapperOwner::toString(JSContext* cx, HandleObject cpow, JS::CallArgs& args)
+{
+ // Ask the other side to call its toString method. Update the callee so that
+ // it points to the CPOW and not to the synthesized CPOWToString function.
+ args.setCallee(ObjectValue(*cpow));
+ if (!callOrConstruct(cx, cpow, args, false))
+ return false;
+
+ if (!args.rval().isString())
+ return true;
+
+ RootedString cpowResult(cx, args.rval().toString());
+ nsAutoJSString toStringResult;
+ if (!toStringResult.init(cx, cpowResult))
+ return false;
+
+ // We don't want to wrap toString() results for things like the location
+ // object, where toString() is supposed to return a URL and nothing else.
+ nsAutoString result;
+ if (toStringResult[0] == '[') {
+ result.AppendLiteral("[object CPOW ");
+ result += toStringResult;
+ result.AppendLiteral("]");
+ } else {
+ result += toStringResult;
+ }
+
+ JSString* str = JS_NewUCStringCopyN(cx, result.get(), result.Length());
+ if (!str)
+ return false;
+
+ args.rval().setString(str);
+ return true;
+}
+
+bool
+WrapperOwner::DOMQI(JSContext* cx, JS::HandleObject proxy, JS::CallArgs& args)
+{
+ // Someone's calling us, handle nsISupports specially to avoid unnecessary
+ // CPOW traffic.
+ HandleValue id = args[0];
+ if (id.isObject()) {
+ RootedObject idobj(cx, &id.toObject());
+ nsCOMPtr<nsIJSID> jsid;
+
+ nsresult rv = UnwrapArg<nsIJSID>(idobj, getter_AddRefs(jsid));
+ if (NS_SUCCEEDED(rv)) {
+ MOZ_ASSERT(jsid, "bad wrapJS");
+ const nsID* idptr = jsid->GetID();
+ if (idptr->Equals(NS_GET_IID(nsISupports))) {
+ args.rval().set(args.thisv());
+ return true;
+ }
+
+ // Webidl-implemented DOM objects never have nsIClassInfo.
+ if (idptr->Equals(NS_GET_IID(nsIClassInfo)))
+ return Throw(cx, NS_ERROR_NO_INTERFACE);
+ }
+ }
+
+ // It wasn't nsISupports, call into the other process to do the QI for us
+ // (since we don't know what other interfaces our object supports). Note
+ // that we have to use JS_GetPropertyDescriptor here to avoid infinite
+ // recursion back into CPOWDOMQI via WrapperOwner::get().
+ // We could stash the actual QI function on our own function object to avoid
+ // if we're called multiple times, but since we're transient, there's no
+ // point right now.
+ JS::Rooted<PropertyDescriptor> propDesc(cx);
+ if (!JS_GetPropertyDescriptor(cx, proxy, "QueryInterface", &propDesc))
+ return false;
+
+ if (!propDesc.value().isObject()) {
+ MOZ_ASSERT_UNREACHABLE("We didn't get QueryInterface off a node");
+ return Throw(cx, NS_ERROR_UNEXPECTED);
+ }
+ return JS_CallFunctionValue(cx, proxy, propDesc.value(), args, args.rval());
+}
+
+bool
+WrapperOwner::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
+ HandleId id, MutableHandleValue vp)
+{
+ ObjectId objId = idOf(proxy);
+
+ JSVariant receiverVar;
+ if (!toVariant(cx, receiver, &receiverVar))
+ return false;
+
+ JSIDVariant idVar;
+ if (!toJSIDVariant(cx, id, &idVar))
+ return false;
+
+ AuxCPOWData* data = AuxCPOWDataOf(proxy);
+ if (data->isDOMObject &&
+ idVar.type() == JSIDVariant::TnsString &&
+ idVar.get_nsString().EqualsLiteral("QueryInterface"))
+ {
+ // Handle QueryInterface on DOM Objects specially since we can assume
+ // certain things about their implementation.
+ RootedFunction qi(cx, JS_NewFunction(cx, CPOWDOMQI, 1, 0,
+ "QueryInterface"));
+ if (!qi)
+ return false;
+
+ vp.set(ObjectValue(*JS_GetFunctionObject(qi)));
+ return true;
+ }
+
+ JSVariant val;
+ ReturnStatus status;
+ if (!SendGet(objId, receiverVar, idVar, &status, &val))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ if (!ok(cx, status))
+ return false;
+
+ if (!fromVariant(cx, val, vp))
+ return false;
+
+ if (idVar.type() == JSIDVariant::TnsString &&
+ idVar.get_nsString().EqualsLiteral("toString")) {
+ RootedFunction toString(cx, JS_NewFunction(cx, CPOWToString, 0, 0,
+ "toString"));
+ if (!toString)
+ return false;
+
+ RootedObject toStringObj(cx, JS_GetFunctionObject(toString));
+
+ if (!JS_DefineProperty(cx, toStringObj, "__cpow__", vp, JSPROP_PERMANENT | JSPROP_READONLY))
+ return false;
+
+ vp.set(ObjectValue(*toStringObj));
+ }
+
+ return true;
+}
+
+bool
+CPOWProxyHandler::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
+ JS::HandleValue receiver, JS::ObjectOpResult& result) const
+{
+ FORWARD(set, (cx, proxy, id, v, receiver, result));
+}
+
+bool
+WrapperOwner::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
+ JS::HandleValue receiver, JS::ObjectOpResult& result)
+{
+ ObjectId objId = idOf(proxy);
+
+ JSIDVariant idVar;
+ if (!toJSIDVariant(cx, id, &idVar))
+ return false;
+
+ JSVariant val;
+ if (!toVariant(cx, v, &val))
+ return false;
+
+ JSVariant receiverVar;
+ if (!toVariant(cx, receiver, &receiverVar))
+ return false;
+
+ ReturnStatus status;
+ if (!SendSet(objId, idVar, val, receiverVar, &status))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ return ok(cx, status, result);
+}
+
+bool
+CPOWProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
+ AutoIdVector& props) const
+{
+ FORWARD(getOwnEnumerablePropertyKeys, (cx, proxy, props));
+}
+
+bool
+WrapperOwner::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
+{
+ return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props);
+}
+
+bool
+CPOWProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) const
+{
+ FORWARD(preventExtensions, (cx, proxy, result));
+}
+
+bool
+WrapperOwner::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result)
+{
+ ObjectId objId = idOf(proxy);
+
+ ReturnStatus status;
+ if (!SendPreventExtensions(objId, &status))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ return ok(cx, status, result);
+}
+
+bool
+CPOWProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const
+{
+ FORWARD(isExtensible, (cx, proxy, extensible));
+}
+
+bool
+WrapperOwner::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible)
+{
+ ObjectId objId = idOf(proxy);
+
+ ReturnStatus status;
+ if (!SendIsExtensible(objId, &status, extensible))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ return ok(cx, status);
+}
+
+bool
+CPOWProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const
+{
+ FORWARD(callOrConstruct, (cx, proxy, args, false));
+}
+
+bool
+CPOWProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const
+{
+ FORWARD(callOrConstruct, (cx, proxy, args, true));
+}
+
+bool
+WrapperOwner::callOrConstruct(JSContext* cx, HandleObject proxy, const CallArgs& args,
+ bool construct)
+{
+ ObjectId objId = idOf(proxy);
+
+ InfallibleTArray<JSParam> vals;
+ AutoValueVector outobjects(cx);
+
+ RootedValue v(cx);
+ for (size_t i = 0; i < args.length() + 2; i++) {
+ // The |this| value for constructors is a magic value that we won't be
+ // able to convert, so skip it.
+ if (i == 1 && construct)
+ v = UndefinedValue();
+ else
+ v = args.base()[i];
+ if (v.isObject()) {
+ RootedObject obj(cx, &v.toObject());
+ if (xpc::IsOutObject(cx, obj)) {
+ // Make sure it is not an in-out object.
+ bool found;
+ if (!JS_HasProperty(cx, obj, "value", &found))
+ return false;
+ if (found) {
+ JS_ReportErrorASCII(cx, "in-out objects cannot be sent via CPOWs yet");
+ return false;
+ }
+
+ vals.AppendElement(JSParam(void_t()));
+ if (!outobjects.append(ObjectValue(*obj)))
+ return false;
+ continue;
+ }
+ }
+ JSVariant val;
+ if (!toVariant(cx, v, &val))
+ return false;
+ vals.AppendElement(JSParam(val));
+ }
+
+ JSVariant result;
+ ReturnStatus status;
+ InfallibleTArray<JSParam> outparams;
+ if (!SendCallOrConstruct(objId, vals, construct, &status, &result, &outparams))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ if (!ok(cx, status))
+ return false;
+
+ if (outparams.Length() != outobjects.length())
+ return ipcfail(cx);
+
+ RootedObject obj(cx);
+ for (size_t i = 0; i < outparams.Length(); i++) {
+ // Don't bother doing anything for outparams that weren't set.
+ if (outparams[i].type() == JSParam::Tvoid_t)
+ continue;
+
+ // Take the value the child process returned, and set it on the XPC
+ // object.
+ if (!fromVariant(cx, outparams[i], &v))
+ return false;
+
+ obj = &outobjects[i].toObject();
+ if (!JS_SetProperty(cx, obj, "value", v))
+ return false;
+ }
+
+ if (!fromVariant(cx, result, args.rval()))
+ return false;
+
+ return true;
+}
+
+bool
+CPOWProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const
+{
+ FORWARD(hasInstance, (cx, proxy, v, bp));
+}
+
+bool
+WrapperOwner::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp)
+{
+ ObjectId objId = idOf(proxy);
+
+ JSVariant vVar;
+ if (!toVariant(cx, v, &vVar))
+ return false;
+
+ ReturnStatus status;
+ JSVariant result;
+ if (!SendHasInstance(objId, vVar, &status, bp))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ return ok(cx, status);
+}
+
+bool
+CPOWProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const
+{
+ FORWARD(getBuiltinClass, (cx, proxy, cls));
+}
+
+bool
+WrapperOwner::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls)
+{
+ ObjectId objId = idOf(proxy);
+
+ uint32_t classValue = uint32_t(ESClass::Other);
+ ReturnStatus status;
+ if (!SendGetBuiltinClass(objId, &status, &classValue))
+ return ipcfail(cx);
+ *cls = ESClass(classValue);
+
+ LOG_STACK();
+
+ return ok(cx, status);
+}
+
+bool
+CPOWProxyHandler::isArray(JSContext* cx, HandleObject proxy,
+ IsArrayAnswer* answer) const
+{
+ FORWARD(isArray, (cx, proxy, answer));
+}
+
+bool
+WrapperOwner::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer)
+{
+ ObjectId objId = idOf(proxy);
+
+ uint32_t ans;
+ ReturnStatus status;
+ if (!SendIsArray(objId, &status, &ans))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ *answer = IsArrayAnswer(ans);
+ MOZ_ASSERT(*answer == IsArrayAnswer::Array ||
+ *answer == IsArrayAnswer::NotArray ||
+ *answer == IsArrayAnswer::RevokedProxy);
+
+ return ok(cx, status);
+}
+
+const char*
+CPOWProxyHandler::className(JSContext* cx, HandleObject proxy) const
+{
+ WrapperOwner* parent = OwnerOf(proxy);
+ if (!parent->active())
+ return "<dead CPOW>";
+ return parent->className(cx, proxy);
+}
+
+const char*
+WrapperOwner::className(JSContext* cx, HandleObject proxy)
+{
+ AuxCPOWData* data = AuxCPOWDataOf(proxy);
+ if (data->className.IsEmpty()) {
+ ObjectId objId = idOf(proxy);
+
+ if (!SendClassName(objId, &data->className))
+ return "<error>";
+
+ LOG_STACK();
+ }
+
+ return data->className.get();
+}
+
+bool
+CPOWProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const
+{
+ FORWARD(getPrototype, (cx, proxy, objp));
+}
+
+bool
+WrapperOwner::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp)
+{
+ ObjectId objId = idOf(proxy);
+
+ ObjectOrNullVariant val;
+ ReturnStatus status;
+ if (!SendGetPrototype(objId, &status, &val))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ if (!ok(cx, status))
+ return false;
+
+ objp.set(fromObjectOrNullVariant(cx, val));
+
+ return true;
+}
+
+bool
+CPOWProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
+ MutableHandleObject objp) const
+{
+ FORWARD(getPrototypeIfOrdinary, (cx, proxy, isOrdinary, objp));
+}
+
+bool
+WrapperOwner::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
+ MutableHandleObject objp)
+{
+ ObjectId objId = idOf(proxy);
+
+ ObjectOrNullVariant val;
+ ReturnStatus status;
+ if (!SendGetPrototypeIfOrdinary(objId, &status, isOrdinary, &val))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ if (!ok(cx, status))
+ return false;
+
+ objp.set(fromObjectOrNullVariant(cx, val));
+
+ return true;
+}
+
+bool
+CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
+{
+ FORWARD(regexp_toShared, (cx, proxy, g));
+}
+
+bool
+WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
+{
+ ObjectId objId = idOf(proxy);
+
+ ReturnStatus status;
+ nsString source;
+ unsigned flags = 0;
+ if (!SendRegExpToShared(objId, &status, &source, &flags))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ if (!ok(cx, status))
+ return false;
+
+ RootedObject regexp(cx);
+ regexp = JS_NewUCRegExpObject(cx, source.get(), source.Length(), flags);
+ if (!regexp)
+ return false;
+
+ return js::RegExpToSharedNonInline(cx, regexp, g);
+}
+
+void
+CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const
+{
+ AuxCPOWData* aux = AuxCPOWDataOf(proxy);
+
+ OwnerOf(proxy)->drop(proxy);
+
+ if (aux)
+ delete aux;
+}
+
+void
+CPOWProxyHandler::objectMoved(JSObject* proxy, const JSObject* old) const
+{
+ OwnerOf(proxy)->updatePointer(proxy, old);
+}
+
+bool
+CPOWProxyHandler::isCallable(JSObject* proxy) const
+{
+ AuxCPOWData* aux = AuxCPOWDataOf(proxy);
+ return aux->isCallable;
+}
+
+bool
+CPOWProxyHandler::isConstructor(JSObject* proxy) const
+{
+ AuxCPOWData* aux = AuxCPOWDataOf(proxy);
+ return aux->isConstructor;
+}
+
+void
+WrapperOwner::drop(JSObject* obj)
+{
+ ObjectId objId = idOf(obj);
+
+ cpows_.remove(objId);
+ if (active())
+ Unused << SendDropObject(objId);
+ decref();
+}
+
+void
+WrapperOwner::updatePointer(JSObject* obj, const JSObject* old)
+{
+ ObjectId objId = idOfUnchecked(obj);
+ MOZ_ASSERT(hasCPOW(objId, old));
+ cpows_.add(objId, obj);
+}
+
+bool
+WrapperOwner::init()
+{
+ if (!JavaScriptShared::init())
+ return false;
+
+ return true;
+}
+
+bool
+WrapperOwner::getPropertyKeys(JSContext* cx, HandleObject proxy, uint32_t flags, AutoIdVector& props)
+{
+ ObjectId objId = idOf(proxy);
+
+ ReturnStatus status;
+ InfallibleTArray<JSIDVariant> ids;
+ if (!SendGetPropertyKeys(objId, flags, &status, &ids))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ if (!ok(cx, status))
+ return false;
+
+ for (size_t i = 0; i < ids.Length(); i++) {
+ RootedId id(cx);
+ if (!fromJSIDVariant(cx, ids[i], &id))
+ return false;
+ if (!props.append(id))
+ return false;
+ }
+
+ return true;
+}
+
+namespace mozilla {
+namespace jsipc {
+
+bool
+IsCPOW(JSObject* obj)
+{
+ return IsProxy(obj) && GetProxyHandler(obj) == &CPOWProxyHandler::singleton;
+}
+
+bool
+IsWrappedCPOW(JSObject* obj)
+{
+ JSObject* unwrapped = js::UncheckedUnwrap(obj, true);
+ if (!unwrapped)
+ return false;
+ return IsCPOW(unwrapped);
+}
+
+void
+GetWrappedCPOWTag(JSObject* obj, nsACString& out)
+{
+ JSObject* unwrapped = js::UncheckedUnwrap(obj, true);
+ MOZ_ASSERT(IsCPOW(unwrapped));
+
+ AuxCPOWData* aux = AuxCPOWDataOf(unwrapped);
+ if (aux)
+ out = aux->objectTag;
+}
+
+nsresult
+InstanceOf(JSObject* proxy, const nsID* id, bool* bp)
+{
+ WrapperOwner* parent = OwnerOf(proxy);
+ if (!parent->active())
+ return NS_ERROR_UNEXPECTED;
+ return parent->instanceOf(proxy, id, bp);
+}
+
+bool
+DOMInstanceOf(JSContext* cx, JSObject* proxyArg, int prototypeID, int depth, bool* bp)
+{
+ RootedObject proxy(cx, proxyArg);
+ FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp));
+}
+
+} /* namespace jsipc */
+} /* namespace mozilla */
+
+nsresult
+WrapperOwner::instanceOf(JSObject* obj, const nsID* id, bool* bp)
+{
+ ObjectId objId = idOf(obj);
+
+ JSIID iid;
+ ConvertID(*id, &iid);
+
+ ReturnStatus status;
+ if (!SendInstanceOf(objId, iid, &status, bp))
+ return NS_ERROR_UNEXPECTED;
+
+ if (status.type() != ReturnStatus::TReturnSuccess)
+ return NS_ERROR_UNEXPECTED;
+
+ return NS_OK;
+}
+
+bool
+WrapperOwner::domInstanceOf(JSContext* cx, JSObject* obj, int prototypeID, int depth, bool* bp)
+{
+ ObjectId objId = idOf(obj);
+
+ ReturnStatus status;
+ if (!SendDOMInstanceOf(objId, prototypeID, depth, &status, bp))
+ return ipcfail(cx);
+
+ LOG_STACK();
+
+ return ok(cx, status);
+}
+
+void
+WrapperOwner::ActorDestroy(ActorDestroyReason why)
+{
+ inactive_ = true;
+
+ objects_.clear();
+ unwaivedObjectIds_.clear();
+ waivedObjectIds_.clear();
+}
+
+bool
+WrapperOwner::ipcfail(JSContext* cx)
+{
+ JS_ReportErrorASCII(cx, "cross-process JS call failed");
+ return false;
+}
+
+bool
+WrapperOwner::ok(JSContext* cx, const ReturnStatus& status)
+{
+ if (status.type() == ReturnStatus::TReturnSuccess)
+ return true;
+
+ if (status.type() == ReturnStatus::TReturnStopIteration)
+ return JS_ThrowStopIteration(cx);
+
+ if (status.type() == ReturnStatus::TReturnDeadCPOW) {
+ JS_ReportErrorASCII(cx, "operation not possible on dead CPOW");
+ return false;
+ }
+
+ RootedValue exn(cx);
+ if (!fromVariant(cx, status.get_ReturnException().exn(), &exn))
+ return false;
+
+ JS_SetPendingException(cx, exn);
+ return false;
+}
+
+bool
+WrapperOwner::ok(JSContext* cx, const ReturnStatus& status, ObjectOpResult& result)
+{
+ if (status.type() == ReturnStatus::TReturnObjectOpResult)
+ return result.fail(status.get_ReturnObjectOpResult().code());
+ if (!ok(cx, status))
+ return false;
+ return result.succeed();
+}
+
+// CPOWs can have a tag string attached to them, originating in the local
+// process from this function. It's sent with the CPOW to the remote process,
+// where it can be fetched with Components.utils.getCrossProcessWrapperTag.
+static nsCString
+GetRemoteObjectTag(JS::Handle<JSObject*> obj)
+{
+ if (nsCOMPtr<nsISupports> supports = xpc::UnwrapReflectorToISupports(obj)) {
+ nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(supports));
+ if (treeItem)
+ return NS_LITERAL_CSTRING("ContentDocShellTreeItem");
+
+ nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(supports));
+ if (doc)
+ return NS_LITERAL_CSTRING("ContentDocument");
+ }
+
+ return NS_LITERAL_CSTRING("generic");
+}
+
+static RemoteObject
+MakeRemoteObject(JSContext* cx, ObjectId id, HandleObject obj)
+{
+ return RemoteObject(id.serialize(),
+ JS::IsCallable(obj),
+ JS::IsConstructor(obj),
+ dom::IsDOMObject(obj),
+ GetRemoteObjectTag(obj));
+}
+
+bool
+WrapperOwner::toObjectVariant(JSContext* cx, JSObject* objArg, ObjectVariant* objVarp)
+{
+ RootedObject obj(cx, objArg);
+ MOZ_ASSERT(obj);
+
+ // We always save objects unwrapped in the CPOW table. If we stored
+ // wrappers, then the wrapper might be GCed while the target remained alive.
+ // Whenever operating on an object that comes from the table, we wrap it
+ // in findObjectById.
+ unsigned wrapperFlags = 0;
+ obj = js::UncheckedUnwrap(obj, true, &wrapperFlags);
+ if (obj && IsCPOW(obj) && OwnerOf(obj) == this) {
+ *objVarp = LocalObject(idOf(obj).serialize());
+ return true;
+ }
+ bool waiveXray = wrapperFlags & xpc::WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG;
+
+ ObjectId id = objectIdMap(waiveXray).find(obj);
+ if (!id.isNull()) {
+ MOZ_ASSERT(id.hasXrayWaiver() == waiveXray);
+ *objVarp = MakeRemoteObject(cx, id, obj);
+ return true;
+ }
+
+ // Need to call PreserveWrapper on |obj| in case it's a reflector.
+ // FIXME: What if it's an XPCWrappedNative?
+ if (mozilla::dom::IsDOMObject(obj))
+ mozilla::dom::TryPreserveWrapper(obj);
+
+ id = ObjectId(nextSerialNumber_++, waiveXray);
+ if (!objects_.add(id, obj))
+ return false;
+ if (!objectIdMap(waiveXray).add(cx, obj, id))
+ return false;
+
+ *objVarp = MakeRemoteObject(cx, id, obj);
+ return true;
+}
+
+JSObject*
+WrapperOwner::fromObjectVariant(JSContext* cx, const ObjectVariant& objVar)
+{
+ if (objVar.type() == ObjectVariant::TRemoteObject) {
+ return fromRemoteObjectVariant(cx, objVar.get_RemoteObject());
+ } else {
+ return fromLocalObjectVariant(cx, objVar.get_LocalObject());
+ }
+}
+
+JSObject*
+WrapperOwner::fromRemoteObjectVariant(JSContext* cx, const RemoteObject& objVar)
+{
+ ObjectId objId = ObjectId::deserialize(objVar.serializedId());
+ RootedObject obj(cx, findCPOWById(objId));
+ if (!obj) {
+
+ // All CPOWs live in the privileged junk scope.
+ RootedObject junkScope(cx, xpc::PrivilegedJunkScope());
+ JSAutoCompartment ac(cx, junkScope);
+ RootedValue v(cx, UndefinedValue());
+ // We need to setLazyProto for the getPrototype/getPrototypeIfOrdinary
+ // hooks.
+ ProxyOptions options;
+ options.setLazyProto(true);
+ obj = NewProxyObject(cx,
+ &CPOWProxyHandler::singleton,
+ v,
+ nullptr,
+ options);
+ if (!obj)
+ return nullptr;
+
+ if (!cpows_.add(objId, obj))
+ return nullptr;
+
+ nextCPOWNumber_ = objId.serialNumber() + 1;
+
+ // Incref once we know the decref will be called.
+ incref();
+
+ AuxCPOWData* aux = new AuxCPOWData(objId,
+ objVar.isCallable(),
+ objVar.isConstructor(),
+ objVar.isDOMObject(),
+ objVar.objectTag());
+
+ SetProxyExtra(obj, 0, PrivateValue(this));
+ SetProxyExtra(obj, 1, PrivateValue(aux));
+ }
+
+ if (!JS_WrapObject(cx, &obj))
+ return nullptr;
+ return obj;
+}
+
+JSObject*
+WrapperOwner::fromLocalObjectVariant(JSContext* cx, const LocalObject& objVar)
+{
+ ObjectId id = ObjectId::deserialize(objVar.serializedId());
+ Rooted<JSObject*> obj(cx, findObjectById(cx, id));
+ if (!obj)
+ return nullptr;
+ if (!JS_WrapObject(cx, &obj))
+ return nullptr;
+ return obj;
+}