Add an ObjectOpResult out-param for DefineProperty functions everywhere. We leave a few js::DefineProperty() convenience functions with no *result out-param. These have strict behavior: that is, they automatically check the result and throw if it is false. In bug 1125624 these strict signatures may end up being called DefinePropertyOrThrow, as that is what the spec calls it.
1071 lines
29 KiB
C++
1071 lines
29 KiB
C++
/* -*- 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 "xpcprivate.h"
|
|
#include "CPOWTimer.h"
|
|
#include "WrapperFactory.h"
|
|
|
|
#include "nsIRemoteTagService.h"
|
|
|
|
using namespace js;
|
|
using namespace JS;
|
|
using namespace mozilla;
|
|
using namespace mozilla::jsipc;
|
|
|
|
struct AuxCPOWData
|
|
{
|
|
ObjectId id;
|
|
bool isCallable;
|
|
bool isConstructor;
|
|
|
|
// The object tag is just some auxilliary information that clients can use
|
|
// however they see fit.
|
|
nsCString objectTag;
|
|
|
|
AuxCPOWData(ObjectId id, bool isCallable, bool isConstructor, const nsACString &objectTag)
|
|
: id(id),
|
|
isCallable(isCallable),
|
|
isConstructor(isConstructor),
|
|
objectTag(objectTag)
|
|
{}
|
|
};
|
|
|
|
WrapperOwner::WrapperOwner(JSRuntime *rt)
|
|
: JavaScriptShared(rt),
|
|
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(findCPOWById(objId) == obj);
|
|
return objId;
|
|
}
|
|
|
|
class CPOWProxyHandler : public BaseProxyHandler
|
|
{
|
|
public:
|
|
MOZ_CONSTEXPR CPOWProxyHandler()
|
|
: BaseProxyHandler(&family) {}
|
|
|
|
virtual bool finalizeInBackground(Value priv) const MOZ_OVERRIDE {
|
|
return false;
|
|
}
|
|
|
|
virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
|
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
|
virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
|
MutableHandle<JSPropertyDescriptor> desc,
|
|
ObjectOpResult &result) const MOZ_OVERRIDE;
|
|
virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
|
|
AutoIdVector &props) const MOZ_OVERRIDE;
|
|
virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
|
virtual bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const MOZ_OVERRIDE;
|
|
virtual bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const MOZ_OVERRIDE;
|
|
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
|
|
virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
|
virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
|
|
HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
|
|
virtual bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
|
|
JS::HandleId id, bool strict, JS::MutableHandleValue vp) const MOZ_OVERRIDE;
|
|
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
|
|
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
|
|
|
|
virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
|
MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
|
|
virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
|
|
virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
|
|
AutoIdVector &props) const MOZ_OVERRIDE;
|
|
virtual bool hasInstance(JSContext *cx, HandleObject proxy,
|
|
MutableHandleValue v, bool *bp) const MOZ_OVERRIDE;
|
|
virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue,
|
|
JSContext *cx) const MOZ_OVERRIDE;
|
|
virtual const char* className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
|
|
virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
|
|
virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
|
|
virtual void objectMoved(JSObject *proxy, const JSObject *old) const MOZ_OVERRIDE;
|
|
virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE;
|
|
virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE;
|
|
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const MOZ_OVERRIDE;
|
|
|
|
static const char family;
|
|
static const CPOWProxyHandler singleton;
|
|
};
|
|
|
|
const char CPOWProxyHandler::family = 0;
|
|
const CPOWProxyHandler CPOWProxyHandler::singleton;
|
|
|
|
#define FORWARD(call, args) \
|
|
WrapperOwner *owner = OwnerOf(proxy); \
|
|
if (!owner->active()) { \
|
|
JS_ReportError(cx, "cannot use a CPOW whose process is gone"); \
|
|
return false; \
|
|
} \
|
|
{ \
|
|
CPOWTimer timer; \
|
|
return owner->call args; \
|
|
}
|
|
|
|
bool
|
|
CPOWProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
|
MutableHandle<JSPropertyDescriptor> desc) const
|
|
{
|
|
FORWARD(getPropertyDescriptor, (cx, proxy, id, desc));
|
|
}
|
|
|
|
bool
|
|
WrapperOwner::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
|
MutableHandle<JSPropertyDescriptor> 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<JSPropertyDescriptor> desc) const
|
|
{
|
|
FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc));
|
|
}
|
|
|
|
bool
|
|
WrapperOwner::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
|
MutableHandle<JSPropertyDescriptor> 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,
|
|
MutableHandle<JSPropertyDescriptor> desc,
|
|
ObjectOpResult &result) const
|
|
{
|
|
FORWARD(defineProperty, (cx, proxy, id, desc, result));
|
|
}
|
|
|
|
bool
|
|
WrapperOwner::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
|
MutableHandle<JSPropertyDescriptor> 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, bool *bp) const
|
|
{
|
|
FORWARD(delete_, (cx, proxy, id, bp));
|
|
}
|
|
|
|
bool
|
|
WrapperOwner::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
|
|
{
|
|
ObjectId objId = idOf(proxy);
|
|
|
|
JSIDVariant idVar;
|
|
if (!toJSIDVariant(cx, id, &idVar))
|
|
return false;
|
|
|
|
ReturnStatus status;
|
|
if (!SendDelete(objId, idVar, &status, bp))
|
|
return ipcfail(cx);
|
|
|
|
LOG_STACK();
|
|
|
|
return ok(cx, status);
|
|
}
|
|
|
|
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, HandleObject receiver,
|
|
HandleId id, MutableHandleValue vp) const
|
|
{
|
|
FORWARD(get, (cx, proxy, receiver, id, vp));
|
|
}
|
|
|
|
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_ReportError(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::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
|
|
HandleId id, MutableHandleValue vp)
|
|
{
|
|
ObjectId objId = idOf(proxy);
|
|
|
|
ObjectVariant receiverVar;
|
|
if (!toObjectVariant(cx, receiver, &receiverVar))
|
|
return false;
|
|
|
|
JSIDVariant idVar;
|
|
if (!toJSIDVariant(cx, id, &idVar))
|
|
return false;
|
|
|
|
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, proxy, "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::HandleObject receiver,
|
|
JS::HandleId id, bool strict, JS::MutableHandleValue vp) const
|
|
{
|
|
FORWARD(set, (cx, proxy, receiver, id, strict, vp));
|
|
}
|
|
|
|
bool
|
|
WrapperOwner::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
|
|
JS::HandleId id, bool strict, JS::MutableHandleValue vp)
|
|
{
|
|
ObjectId objId = idOf(proxy);
|
|
|
|
ObjectVariant receiverVar;
|
|
if (!toObjectVariant(cx, receiver, &receiverVar))
|
|
return false;
|
|
|
|
JSIDVariant idVar;
|
|
if (!toJSIDVariant(cx, id, &idVar))
|
|
return false;
|
|
|
|
JSVariant val;
|
|
if (!toVariant(cx, vp, &val))
|
|
return false;
|
|
|
|
ReturnStatus status;
|
|
JSVariant result;
|
|
if (!SendSet(objId, receiverVar, idVar, strict, val, &status, &result))
|
|
return ipcfail(cx);
|
|
|
|
LOG_STACK();
|
|
|
|
if (!ok(cx, status))
|
|
return false;
|
|
|
|
return fromVariant(cx, result, vp);
|
|
}
|
|
|
|
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, bool *succeeded) const
|
|
{
|
|
FORWARD(preventExtensions, (cx, proxy, succeeded));
|
|
}
|
|
|
|
bool
|
|
WrapperOwner::preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded)
|
|
{
|
|
ObjectId objId = idOf(proxy);
|
|
|
|
ReturnStatus status;
|
|
if (!SendPreventExtensions(objId, &status, succeeded))
|
|
return ipcfail(cx);
|
|
|
|
LOG_STACK();
|
|
|
|
return ok(cx, status);
|
|
}
|
|
|
|
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_ReportError(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::objectClassIs(HandleObject proxy, js::ESClassValue classValue, JSContext *cx) const
|
|
{
|
|
FORWARD(objectClassIs, (cx, proxy, classValue));
|
|
}
|
|
|
|
bool
|
|
WrapperOwner::objectClassIs(JSContext *cx, HandleObject proxy, js::ESClassValue classValue)
|
|
{
|
|
ObjectId objId = idOf(proxy);
|
|
|
|
// This function is assumed infallible, so we just return false if the IPC
|
|
// channel fails.
|
|
bool result;
|
|
if (!SendObjectClassIs(objId, classValue, &result))
|
|
return false;
|
|
|
|
LOG_STACK();
|
|
|
|
return result;
|
|
}
|
|
|
|
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)
|
|
{
|
|
ObjectId objId = idOf(proxy);
|
|
|
|
nsString name;
|
|
if (!SendClassName(objId, &name))
|
|
return "<error>";
|
|
|
|
LOG_STACK();
|
|
|
|
return ToNewCString(name);
|
|
}
|
|
|
|
bool
|
|
CPOWProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject objp) const
|
|
{
|
|
FORWARD(getPrototypeOf, (cx, proxy, objp));
|
|
}
|
|
|
|
bool
|
|
WrapperOwner::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject objp)
|
|
{
|
|
ObjectId objId = idOf(proxy);
|
|
|
|
ObjectOrNullVariant val;
|
|
ReturnStatus status;
|
|
if (!SendGetPrototypeOf(objId, &status, &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);
|
|
RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
|
|
regexp = JS_NewUCRegExpObject(cx, global, 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(findCPOWById(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 *proxy, int prototypeID, int depth, bool *bp)
|
|
{
|
|
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_ReportError(cx, "child process crashed or timedout");
|
|
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);
|
|
|
|
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();
|
|
}
|
|
|
|
static RemoteObject
|
|
MakeRemoteObject(JSContext *cx, ObjectId id, HandleObject obj)
|
|
{
|
|
nsCString objectTag;
|
|
|
|
nsCOMPtr<nsIRemoteTagService> service =
|
|
do_GetService("@mozilla.org/addons/remote-tag-service;1");
|
|
if (service) {
|
|
RootedValue objVal(cx, ObjectValue(*obj));
|
|
service->GetRemoteObjectTag(objVal, objectTag);
|
|
}
|
|
|
|
return RemoteObject(id.serialize(),
|
|
JS::IsCallable(obj),
|
|
JS::IsConstructor(obj),
|
|
objectTag);
|
|
}
|
|
|
|
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, 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, 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 getPrototypeOf hook.
|
|
ProxyOptions options;
|
|
options.setLazyProto(true);
|
|
obj = NewProxyObject(cx,
|
|
&CPOWProxyHandler::singleton,
|
|
v,
|
|
nullptr,
|
|
options);
|
|
if (!obj)
|
|
return nullptr;
|
|
|
|
if (!cpows_.add(objId, obj))
|
|
return nullptr;
|
|
|
|
// Incref once we know the decref will be called.
|
|
incref();
|
|
|
|
AuxCPOWData *aux = new AuxCPOWData(objId,
|
|
objVar.isCallable(),
|
|
objVar.isConstructor(),
|
|
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, 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;
|
|
}
|