1504 lines
51 KiB
C++
1504 lines
51 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
|
/* 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/. */
|
|
|
|
/* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */
|
|
|
|
#include "xpcprivate.h"
|
|
#include "jsprf.h"
|
|
#include "mozilla/dom/BindingUtils.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "nsIAddonInterposition.h"
|
|
#include "AddonWrapper.h"
|
|
#include "js/Class.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace JS;
|
|
|
|
/***************************************************************************/
|
|
|
|
// All of the exceptions thrown into JS from this file go through here.
|
|
// That makes this a nice place to set a breakpoint.
|
|
|
|
static bool Throw(nsresult errNum, JSContext* cx)
|
|
{
|
|
XPCThrower::Throw(errNum, cx);
|
|
return false;
|
|
}
|
|
|
|
// Handy macro used in many callback stub below.
|
|
|
|
#define THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper) \
|
|
PR_BEGIN_MACRO \
|
|
if (!wrapper) \
|
|
return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \
|
|
if (!wrapper->IsValid()) \
|
|
return Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx); \
|
|
PR_END_MACRO
|
|
|
|
/***************************************************************************/
|
|
|
|
static bool
|
|
ToStringGuts(XPCCallContext& ccx)
|
|
{
|
|
char* sz;
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
|
|
if (wrapper)
|
|
sz = wrapper->ToString(ccx.GetTearOff());
|
|
else
|
|
sz = JS_smprintf("[xpconnect wrapped native prototype]");
|
|
|
|
if (!sz) {
|
|
JS_ReportOutOfMemory(ccx);
|
|
return false;
|
|
}
|
|
|
|
JSString* str = JS_NewStringCopyZ(ccx, sz);
|
|
JS_smprintf_free(sz);
|
|
if (!str)
|
|
return false;
|
|
|
|
ccx.SetRetVal(JS::StringValue(str));
|
|
return true;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
static bool
|
|
XPC_WN_Shared_ToString(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
|
|
if (!obj)
|
|
return false;
|
|
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
if (!ccx.IsValid())
|
|
return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
|
|
ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
|
|
ccx.SetArgsAndResultPtr(args.length(), args.array(), vp);
|
|
return ToStringGuts(ccx);
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_Shared_ToSource(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
static const char empty[] = "({})";
|
|
JSString* str = JS_NewStringCopyN(cx, empty, sizeof(empty)-1);
|
|
if (!str)
|
|
return false;
|
|
args.rval().setString(str);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_Shared_toPrimitive(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
|
RootedObject obj(cx);
|
|
if (!JS_ValueToObject(cx, args.thisv(), &obj))
|
|
return false;
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
JSType hint;
|
|
if (!GetFirstArgumentAsTypeHint(cx, args, &hint))
|
|
return false;
|
|
|
|
if (hint == JSTYPE_NUMBER) {
|
|
args.rval().set(JS_GetNaNValue(cx));
|
|
return true;
|
|
}
|
|
|
|
MOZ_ASSERT(hint == JSTYPE_STRING || hint == JSTYPE_VOID);
|
|
ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
|
|
ccx.SetArgsAndResultPtr(0, nullptr, args.rval().address());
|
|
|
|
XPCNativeMember* member = ccx.GetMember();
|
|
if (member && member->IsMethod()) {
|
|
if (!XPCWrappedNative::CallMethod(ccx))
|
|
return false;
|
|
|
|
if (args.rval().isPrimitive())
|
|
return true;
|
|
}
|
|
|
|
// else...
|
|
return ToStringGuts(ccx);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
// A "double wrapped object" is a user JSObject that has been wrapped as a
|
|
// wrappedJS in order to be used by native code and then re-wrapped by a
|
|
// wrappedNative wrapper to be used by JS code. One might think of it as:
|
|
// wrappedNative(wrappedJS(underlying_JSObject))
|
|
// This is done (as opposed to just unwrapping the wrapped JS and automatically
|
|
// returning the underlying JSObject) so that JS callers will see what looks
|
|
// Like any other xpcom object - and be limited to use its interfaces.
|
|
//
|
|
// See the comment preceding nsIXPCWrappedJSObjectGetter in nsIXPConnect.idl.
|
|
|
|
static JSObject*
|
|
GetDoubleWrappedJSObject(XPCCallContext& ccx, XPCWrappedNative* wrapper)
|
|
{
|
|
RootedObject obj(ccx);
|
|
nsCOMPtr<nsIXPConnectWrappedJS>
|
|
underware = do_QueryInterface(wrapper->GetIdentityObject());
|
|
if (underware) {
|
|
RootedObject mainObj(ccx, underware->GetJSObject());
|
|
if (mainObj) {
|
|
RootedId id(ccx, ccx.GetRuntime()->
|
|
GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT));
|
|
|
|
JSAutoCompartment ac(ccx, mainObj);
|
|
|
|
RootedValue val(ccx);
|
|
if (JS_GetPropertyById(ccx, mainObj, id, &val) &&
|
|
!val.isPrimitive()) {
|
|
obj = val.toObjectOrNull();
|
|
}
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
// This is the getter native function we use to handle 'wrappedJSObject' for
|
|
// double wrapped JSObjects.
|
|
|
|
static bool
|
|
XPC_WN_DoubleWrappedGetter(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
CallArgs args = CallArgsFromVp(argc, vp);
|
|
|
|
RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
|
|
if (!obj)
|
|
return false;
|
|
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
|
|
|
|
RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper));
|
|
if (!realObject) {
|
|
// This is pretty unexpected at this point. The object originally
|
|
// responded to this get property call and now gives no object.
|
|
// XXX Should this throw something at the caller?
|
|
args.rval().setNull();
|
|
return true;
|
|
}
|
|
|
|
// It is a double wrapped object. This should really never appear in
|
|
// content these days, but addons still do it - see bug 965921.
|
|
if (MOZ_UNLIKELY(!nsContentUtils::IsCallerChrome())) {
|
|
JS_ReportError(cx, "Attempt to use .wrappedJSObject in untrusted code");
|
|
return false;
|
|
}
|
|
args.rval().setObject(*realObject);
|
|
return JS_WrapValue(cx, args.rval());
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
// This is our shared function to define properties on our JSObjects.
|
|
|
|
/*
|
|
* NOTE:
|
|
* We *never* set the tearoff names (e.g. nsIFoo) as JS_ENUMERATE.
|
|
* We *never* set toString or toSource as JS_ENUMERATE.
|
|
*/
|
|
|
|
static bool
|
|
DefinePropertyIfFound(XPCCallContext& ccx,
|
|
HandleObject obj,
|
|
HandleId idArg,
|
|
XPCNativeSet* set,
|
|
XPCNativeInterface* iface,
|
|
XPCNativeMember* member,
|
|
XPCWrappedNativeScope* scope,
|
|
bool reflectToStringAndToSource,
|
|
XPCWrappedNative* wrapperToReflectInterfaceNames,
|
|
XPCWrappedNative* wrapperToReflectDoubleWrap,
|
|
XPCNativeScriptableInfo* scriptableInfo,
|
|
unsigned propFlags,
|
|
bool* resolved)
|
|
{
|
|
RootedId id(ccx, idArg);
|
|
XPCJSRuntime* rt = ccx.GetRuntime();
|
|
bool found;
|
|
const char* name;
|
|
|
|
propFlags |= JSPROP_RESOLVING;
|
|
|
|
if (set) {
|
|
if (iface)
|
|
found = true;
|
|
else
|
|
found = set->FindMember(id, &member, &iface);
|
|
} else
|
|
found = (nullptr != (member = iface->FindMember(id)));
|
|
|
|
if (!found) {
|
|
if (reflectToStringAndToSource) {
|
|
JSNative call;
|
|
uint32_t flags = 0;
|
|
|
|
if (scriptableInfo) {
|
|
nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(
|
|
scriptableInfo->GetCallback());
|
|
|
|
if (classInfo) {
|
|
nsresult rv = classInfo->GetFlags(&flags);
|
|
if (NS_FAILED(rv))
|
|
return Throw(rv, ccx);
|
|
}
|
|
}
|
|
|
|
bool overwriteToString = !(flags & nsIClassInfo::DOM_OBJECT)
|
|
|| Preferences::GetBool("dom.XPCToStringForDOMClasses", false);
|
|
|
|
if(id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING)
|
|
&& overwriteToString)
|
|
{
|
|
call = XPC_WN_Shared_ToString;
|
|
name = rt->GetStringName(XPCJSRuntime::IDX_TO_STRING);
|
|
} else if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE)) {
|
|
call = XPC_WN_Shared_ToSource;
|
|
name = rt->GetStringName(XPCJSRuntime::IDX_TO_SOURCE);
|
|
} else if (id == SYMBOL_TO_JSID(
|
|
JS::GetWellKnownSymbol(ccx, JS::SymbolCode::toPrimitive)))
|
|
{
|
|
call = XPC_WN_Shared_toPrimitive;
|
|
name = "[Symbol.toPrimitive]";
|
|
} else {
|
|
call = nullptr;
|
|
}
|
|
|
|
if (call) {
|
|
RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, name));
|
|
if (!fun) {
|
|
JS_ReportOutOfMemory(ccx);
|
|
return false;
|
|
}
|
|
|
|
AutoResolveName arn(ccx, id);
|
|
if (resolved)
|
|
*resolved = true;
|
|
RootedObject value(ccx, JS_GetFunctionObject(fun));
|
|
return JS_DefinePropertyById(ccx, obj, id, value,
|
|
propFlags & ~JSPROP_ENUMERATE);
|
|
}
|
|
}
|
|
// This *might* be a tearoff name that is not yet part of our
|
|
// set. Let's lookup the name and see if it is the name of an
|
|
// interface. Then we'll see if the object actually *does* this
|
|
// interface and add a tearoff as necessary.
|
|
|
|
if (wrapperToReflectInterfaceNames) {
|
|
JSAutoByteString name;
|
|
AutoMarkingNativeInterfacePtr iface2(ccx);
|
|
XPCWrappedNativeTearOff* to;
|
|
RootedObject jso(ccx);
|
|
nsresult rv = NS_OK;
|
|
|
|
if (JSID_IS_STRING(id) &&
|
|
name.encodeLatin1(ccx, JSID_TO_STRING(id)) &&
|
|
(iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr()), iface2) &&
|
|
nullptr != (to = wrapperToReflectInterfaceNames->
|
|
FindTearOff(iface2, true, &rv)) &&
|
|
nullptr != (jso = to->GetJSObject()))
|
|
|
|
{
|
|
AutoResolveName arn(ccx, id);
|
|
if (resolved)
|
|
*resolved = true;
|
|
return JS_DefinePropertyById(ccx, obj, id, jso,
|
|
propFlags & ~JSPROP_ENUMERATE);
|
|
} else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) {
|
|
return Throw(rv, ccx);
|
|
}
|
|
}
|
|
|
|
// This *might* be a double wrapped JSObject
|
|
if (wrapperToReflectDoubleWrap &&
|
|
id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT) &&
|
|
GetDoubleWrappedJSObject(ccx, wrapperToReflectDoubleWrap)) {
|
|
// We build and add a getter function.
|
|
// A security check is done on a per-get basis.
|
|
|
|
JSFunction* fun;
|
|
|
|
id = rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT);
|
|
name = rt->GetStringName(XPCJSRuntime::IDX_WRAPPED_JSOBJECT);
|
|
|
|
fun = JS_NewFunction(ccx, XPC_WN_DoubleWrappedGetter,
|
|
0, 0, name);
|
|
|
|
if (!fun)
|
|
return false;
|
|
|
|
RootedObject funobj(ccx, JS_GetFunctionObject(fun));
|
|
if (!funobj)
|
|
return false;
|
|
|
|
propFlags |= JSPROP_GETTER | JSPROP_SHARED;
|
|
propFlags &= ~JSPROP_ENUMERATE;
|
|
|
|
AutoResolveName arn(ccx, id);
|
|
if (resolved)
|
|
*resolved = true;
|
|
return JS_DefinePropertyById(ccx, obj, id, UndefinedHandleValue, propFlags,
|
|
JS_DATA_TO_FUNC_PTR(JSNative, funobj.get()),
|
|
nullptr);
|
|
}
|
|
|
|
if (resolved)
|
|
*resolved = false;
|
|
return true;
|
|
}
|
|
|
|
if (!member) {
|
|
if (wrapperToReflectInterfaceNames) {
|
|
XPCWrappedNativeTearOff* to =
|
|
wrapperToReflectInterfaceNames->FindTearOff(iface, true);
|
|
|
|
if (!to)
|
|
return false;
|
|
RootedObject jso(ccx, to->GetJSObject());
|
|
if (!jso)
|
|
return false;
|
|
|
|
AutoResolveName arn(ccx, id);
|
|
if (resolved)
|
|
*resolved = true;
|
|
return JS_DefinePropertyById(ccx, obj, id, jso,
|
|
propFlags & ~JSPROP_ENUMERATE);
|
|
}
|
|
if (resolved)
|
|
*resolved = false;
|
|
return true;
|
|
}
|
|
|
|
if (member->IsConstant()) {
|
|
RootedValue val(ccx);
|
|
AutoResolveName arn(ccx, id);
|
|
if (resolved)
|
|
*resolved = true;
|
|
return member->GetConstantValue(ccx, iface, val.address()) &&
|
|
JS_DefinePropertyById(ccx, obj, id, val, propFlags);
|
|
}
|
|
|
|
if (scope->HasInterposition()) {
|
|
Rooted<JSPropertyDescriptor> desc(ccx);
|
|
if (!xpc::InterposeProperty(ccx, obj, iface->GetIID(), id, &desc))
|
|
return false;
|
|
|
|
if (desc.object()) {
|
|
AutoResolveName arn(ccx, id);
|
|
if (resolved)
|
|
*resolved = true;
|
|
desc.attributesRef() |= JSPROP_RESOLVING;
|
|
return JS_DefinePropertyById(ccx, obj, id, desc);
|
|
}
|
|
}
|
|
|
|
if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING) ||
|
|
id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE) ||
|
|
(scriptableInfo &&
|
|
scriptableInfo->GetFlags().DontEnumQueryInterface() &&
|
|
id == rt->GetStringID(XPCJSRuntime::IDX_QUERY_INTERFACE)))
|
|
propFlags &= ~JSPROP_ENUMERATE;
|
|
|
|
RootedValue funval(ccx);
|
|
if (!member->NewFunctionObject(ccx, iface, obj, funval.address()))
|
|
return false;
|
|
|
|
if (member->IsMethod()) {
|
|
AutoResolveName arn(ccx, id);
|
|
if (resolved)
|
|
*resolved = true;
|
|
return JS_DefinePropertyById(ccx, obj, id, funval, propFlags);
|
|
}
|
|
|
|
// else...
|
|
|
|
MOZ_ASSERT(member->IsAttribute(), "way broken!");
|
|
|
|
propFlags |= JSPROP_GETTER | JSPROP_SHARED;
|
|
propFlags &= ~JSPROP_READONLY;
|
|
JSObject* funobj = funval.toObjectOrNull();
|
|
JSNative getter = JS_DATA_TO_FUNC_PTR(JSNative, funobj);
|
|
JSNative setter;
|
|
if (member->IsWritableAttribute()) {
|
|
propFlags |= JSPROP_SETTER;
|
|
setter = JS_DATA_TO_FUNC_PTR(JSNative, funobj);
|
|
} else {
|
|
setter = nullptr;
|
|
}
|
|
|
|
AutoResolveName arn(ccx, id);
|
|
if (resolved)
|
|
*resolved = true;
|
|
|
|
return JS_DefinePropertyById(ccx, obj, id, UndefinedHandleValue, propFlags, getter, setter);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
static bool
|
|
XPC_WN_OnlyIWrite_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
|
|
{
|
|
XPCCallContext ccx(JS_CALLER, cx, obj, nullptr, id);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
// Allow only XPConnect to add/set the property
|
|
if (ccx.GetResolveName() == id)
|
|
return true;
|
|
|
|
return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_CannotModifyPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
|
|
HandleValue v)
|
|
{
|
|
return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_CantDeletePropertyStub(JSContext* cx, HandleObject obj, HandleId id,
|
|
ObjectOpResult& result)
|
|
{
|
|
return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_CannotModifySetPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
|
|
MutableHandleValue vp, ObjectOpResult& result)
|
|
{
|
|
return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_Shared_Enumerate(JSContext* cx, HandleObject obj)
|
|
{
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
// Since we aren't going to enumerate tearoff names and the prototype
|
|
// handles non-mutated members, we can do this potential short-circuit.
|
|
if (!wrapper->HasMutatedSet())
|
|
return true;
|
|
|
|
XPCNativeSet* set = wrapper->GetSet();
|
|
XPCNativeSet* protoSet = wrapper->HasProto() ?
|
|
wrapper->GetProto()->GetSet() : nullptr;
|
|
|
|
uint16_t interface_count = set->GetInterfaceCount();
|
|
XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
|
|
for (uint16_t i = 0; i < interface_count; i++) {
|
|
XPCNativeInterface* iface = interfaceArray[i];
|
|
uint16_t member_count = iface->GetMemberCount();
|
|
for (uint16_t k = 0; k < member_count; k++) {
|
|
XPCNativeMember* member = iface->GetMemberAt(k);
|
|
jsid name = member->GetName();
|
|
|
|
// Skip if this member is going to come from the proto.
|
|
uint16_t index;
|
|
if (protoSet &&
|
|
protoSet->FindMember(name, nullptr, &index) && index == i)
|
|
continue;
|
|
if (!xpc_ForcePropertyResolve(cx, obj, name))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
enum WNHelperType {
|
|
WN_NOHELPER,
|
|
WN_HELPER
|
|
};
|
|
|
|
static void
|
|
WrappedNativeFinalize(js::FreeOp* fop, JSObject* obj, WNHelperType helperType)
|
|
{
|
|
const js::Class* clazz = js::GetObjectClass(obj);
|
|
if (clazz->flags & JSCLASS_DOM_GLOBAL) {
|
|
mozilla::dom::DestroyProtoAndIfaceCache(obj);
|
|
}
|
|
nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
|
|
if (!p)
|
|
return;
|
|
|
|
XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
|
|
if (helperType == WN_HELPER)
|
|
wrapper->GetScriptableCallback()->Finalize(wrapper, js::CastToJSFreeOp(fop), obj);
|
|
wrapper->FlatJSObjectFinalized();
|
|
}
|
|
|
|
static void
|
|
WrappedNativeObjectMoved(JSObject* obj, const JSObject* old)
|
|
{
|
|
nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
|
|
if (!p)
|
|
return;
|
|
|
|
XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
|
|
wrapper->FlatJSObjectMoved(obj, old);
|
|
}
|
|
|
|
static void
|
|
XPC_WN_NoHelper_Finalize(js::FreeOp* fop, JSObject* obj)
|
|
{
|
|
WrappedNativeFinalize(fop, obj, WN_NOHELPER);
|
|
}
|
|
|
|
/*
|
|
* General comment about XPConnect tracing: Given a C++ object |wrapper| and its
|
|
* corresponding JS object |obj|, calling |wrapper->TraceSelf| will ask the JS
|
|
* engine to mark |obj|. Eventually, this will lead to the trace hook being
|
|
* called for |obj|. The trace hook should call |wrapper->TraceInside|, which
|
|
* should mark any JS objects held by |wrapper| as members.
|
|
*/
|
|
|
|
static void
|
|
MarkWrappedNative(JSTracer* trc, JSObject* obj)
|
|
{
|
|
const js::Class* clazz = js::GetObjectClass(obj);
|
|
if (clazz->flags & JSCLASS_DOM_GLOBAL) {
|
|
mozilla::dom::TraceProtoAndIfaceCache(trc, obj);
|
|
}
|
|
MOZ_ASSERT(IS_WN_CLASS(clazz));
|
|
|
|
XPCWrappedNative* wrapper = XPCWrappedNative::Get(obj);
|
|
if (wrapper && wrapper->IsValid())
|
|
wrapper->TraceInside(trc);
|
|
}
|
|
|
|
/* static */ void
|
|
XPCWrappedNative::Trace(JSTracer* trc, JSObject* obj)
|
|
{
|
|
MarkWrappedNative(trc, obj);
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_NoHelper_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
|
{
|
|
XPCCallContext ccx(JS_CALLER, cx, obj, nullptr, id);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
XPCNativeSet* set = ccx.GetSet();
|
|
if (!set)
|
|
return true;
|
|
|
|
// Don't resolve properties that are on our prototype.
|
|
if (ccx.GetInterface() && !ccx.GetStaticMemberIsLocal())
|
|
return true;
|
|
|
|
return DefinePropertyIfFound(ccx, obj, id,
|
|
set, nullptr, nullptr, wrapper->GetScope(),
|
|
true, wrapper, wrapper, nullptr,
|
|
JSPROP_ENUMERATE |
|
|
JSPROP_READONLY |
|
|
JSPROP_PERMANENT,
|
|
resolvedp);
|
|
}
|
|
|
|
const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = {
|
|
{ // base
|
|
"XPCWrappedNative_NoHelper", // name;
|
|
WRAPPER_FLAGS |
|
|
JSCLASS_PRIVATE_IS_NSISUPPORTS, // flags
|
|
|
|
/* Mandatory non-null function pointer members. */
|
|
XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty
|
|
XPC_WN_CantDeletePropertyStub, // delProperty
|
|
nullptr, // getProperty
|
|
nullptr, // setProperty
|
|
|
|
XPC_WN_Shared_Enumerate, // enumerate
|
|
XPC_WN_NoHelper_Resolve, // resolve
|
|
nullptr, // mayResolve
|
|
XPC_WN_NoHelper_Finalize, // finalize
|
|
|
|
/* Optionally non-null members start here. */
|
|
nullptr, // call
|
|
nullptr, // construct
|
|
nullptr, // hasInstance
|
|
XPCWrappedNative::Trace, // trace
|
|
JS_NULL_CLASS_SPEC,
|
|
|
|
// ClassExtension
|
|
{
|
|
true, // isWrappedNative
|
|
nullptr, // weakmapKeyDelegateOp
|
|
WrappedNativeObjectMoved
|
|
},
|
|
|
|
// ObjectOps
|
|
{
|
|
nullptr, // lookupProperty
|
|
nullptr, // defineProperty
|
|
nullptr, // hasProperty
|
|
nullptr, // getProperty
|
|
nullptr, // setProperty
|
|
nullptr, // getOwnPropertyDescriptor
|
|
nullptr, // deleteProperty
|
|
nullptr, nullptr, // watch/unwatch
|
|
nullptr, // getElements
|
|
nullptr, // enumerate
|
|
nullptr, // funToString
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
static bool
|
|
XPC_WN_MaybeResolvingPropertyStub(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
|
|
{
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
if (ccx.GetResolvingWrapper() == wrapper)
|
|
return true;
|
|
return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_MaybeResolvingSetPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
|
|
MutableHandleValue vp, ObjectOpResult& result)
|
|
{
|
|
result.succeed();
|
|
return XPC_WN_MaybeResolvingPropertyStub(cx, obj, id, vp);
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_MaybeResolvingDeletePropertyStub(JSContext* cx, HandleObject obj, HandleId id,
|
|
ObjectOpResult& result)
|
|
{
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
if (ccx.GetResolvingWrapper() == wrapper) {
|
|
return result.succeed();
|
|
}
|
|
return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
|
|
}
|
|
|
|
// macro fun!
|
|
#define PRE_HELPER_STUB \
|
|
JSObject* unwrapped = js::CheckedUnwrap(obj, false); \
|
|
if (!unwrapped) { \
|
|
JS_ReportError(cx, "Permission denied to operate on object."); \
|
|
return false; \
|
|
} \
|
|
if (!IS_WN_REFLECTOR(unwrapped)) { \
|
|
return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \
|
|
} \
|
|
XPCWrappedNative* wrapper = XPCWrappedNative::Get(unwrapped); \
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); \
|
|
bool retval = true; \
|
|
nsresult rv = wrapper->GetScriptableCallback()->
|
|
|
|
#define POST_HELPER_STUB \
|
|
if (NS_FAILED(rv)) \
|
|
return Throw(rv, cx); \
|
|
return retval;
|
|
|
|
#define POST_HELPER_STUB_WITH_OBJECTOPRESULT(failMethod) \
|
|
if (NS_FAILED(rv)) \
|
|
return Throw(rv, cx); \
|
|
return retval ? result.succeed() : result.failMethod();
|
|
|
|
static bool
|
|
XPC_WN_Helper_AddProperty(JSContext* cx, HandleObject obj, HandleId id,
|
|
HandleValue v)
|
|
{
|
|
PRE_HELPER_STUB
|
|
AddProperty(wrapper, cx, obj, id, v, &retval);
|
|
POST_HELPER_STUB
|
|
}
|
|
|
|
bool
|
|
XPC_WN_Helper_GetProperty(JSContext* cx, HandleObject obj, HandleId id,
|
|
MutableHandleValue vp)
|
|
{
|
|
PRE_HELPER_STUB
|
|
GetProperty(wrapper, cx, obj, id, vp.address(), &retval);
|
|
POST_HELPER_STUB
|
|
}
|
|
|
|
bool
|
|
XPC_WN_Helper_SetProperty(JSContext* cx, HandleObject obj, HandleId id,
|
|
MutableHandleValue vp, ObjectOpResult& result)
|
|
{
|
|
PRE_HELPER_STUB
|
|
SetProperty(wrapper, cx, obj, id, vp.address(), &retval);
|
|
POST_HELPER_STUB_WITH_OBJECTOPRESULT(failReadOnly)
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_Helper_Call(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
|
// N.B. we want obj to be the callee, not JS_THIS(cx, vp)
|
|
RootedObject obj(cx, &args.callee());
|
|
|
|
XPCCallContext ccx(JS_CALLER, cx, obj, nullptr, JSID_VOIDHANDLE, args.length(),
|
|
args.array(), args.rval().address());
|
|
if (!ccx.IsValid())
|
|
return false;
|
|
|
|
PRE_HELPER_STUB
|
|
Call(wrapper, cx, obj, args, &retval);
|
|
POST_HELPER_STUB
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_Helper_Construct(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
|
RootedObject obj(cx, &args.callee());
|
|
if (!obj)
|
|
return false;
|
|
|
|
XPCCallContext ccx(JS_CALLER, cx, obj, nullptr, JSID_VOIDHANDLE, args.length(),
|
|
args.array(), args.rval().address());
|
|
if (!ccx.IsValid())
|
|
return false;
|
|
|
|
PRE_HELPER_STUB
|
|
Construct(wrapper, cx, obj, args, &retval);
|
|
POST_HELPER_STUB
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_Helper_HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue valp, bool* bp)
|
|
{
|
|
bool retval2;
|
|
PRE_HELPER_STUB
|
|
HasInstance(wrapper, cx, obj, valp, &retval2, &retval);
|
|
*bp = retval2;
|
|
POST_HELPER_STUB
|
|
}
|
|
|
|
static void
|
|
XPC_WN_Helper_Finalize(js::FreeOp* fop, JSObject* obj)
|
|
{
|
|
WrappedNativeFinalize(fop, obj, WN_HELPER);
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_Helper_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
bool retval = true;
|
|
bool resolved = false;
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
RootedId old(cx, ccx.SetResolveName(id));
|
|
|
|
XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
|
|
if (si && si->GetFlags().WantResolve()) {
|
|
XPCWrappedNative* oldResolvingWrapper;
|
|
bool allowPropMods = si->GetFlags().AllowPropModsDuringResolve();
|
|
|
|
if (allowPropMods)
|
|
oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
|
|
|
|
rv = si->GetCallback()->Resolve(wrapper, cx, obj, id, &resolved, &retval);
|
|
|
|
if (allowPropMods)
|
|
(void)ccx.SetResolvingWrapper(oldResolvingWrapper);
|
|
}
|
|
|
|
old = ccx.SetResolveName(old);
|
|
MOZ_ASSERT(old == id, "bad nest");
|
|
|
|
if (NS_FAILED(rv)) {
|
|
return Throw(rv, cx);
|
|
}
|
|
|
|
if (resolved) {
|
|
*resolvedp = true;
|
|
} else if (wrapper->HasMutatedSet()) {
|
|
// We are here if scriptable did not resolve this property and
|
|
// it *might* be in the instance set but not the proto set.
|
|
|
|
XPCNativeSet* set = wrapper->GetSet();
|
|
XPCNativeSet* protoSet = wrapper->HasProto() ?
|
|
wrapper->GetProto()->GetSet() : nullptr;
|
|
XPCNativeMember* member;
|
|
XPCNativeInterface* iface;
|
|
bool IsLocal;
|
|
|
|
if (set->FindMember(id, &member, &iface, protoSet, &IsLocal) &&
|
|
IsLocal) {
|
|
XPCWrappedNative* oldResolvingWrapper;
|
|
|
|
XPCNativeScriptableFlags siFlags(0);
|
|
if (si)
|
|
siFlags = si->GetFlags();
|
|
|
|
XPCWrappedNative* wrapperForInterfaceNames =
|
|
siFlags.DontReflectInterfaceNames() ? nullptr : wrapper;
|
|
|
|
oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
|
|
retval = DefinePropertyIfFound(ccx, obj, id,
|
|
set, iface, member,
|
|
wrapper->GetScope(),
|
|
false,
|
|
wrapperForInterfaceNames,
|
|
nullptr, si,
|
|
JSPROP_ENUMERATE, resolvedp);
|
|
(void)ccx.SetResolvingWrapper(oldResolvingWrapper);
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_Helper_Enumerate(JSContext* cx, HandleObject obj)
|
|
{
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
|
|
if (!si || !si->GetFlags().WantEnumerate())
|
|
return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
|
|
|
|
if (!XPC_WN_Shared_Enumerate(cx, obj))
|
|
return false;
|
|
|
|
bool retval = true;
|
|
nsresult rv = si->GetCallback()->Enumerate(wrapper, cx, obj, &retval);
|
|
if (NS_FAILED(rv))
|
|
return Throw(rv, cx);
|
|
return retval;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
static bool
|
|
XPC_WN_JSOp_Enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
|
|
bool enumerableOnly)
|
|
{
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
|
|
if (!si || !si->GetFlags().WantNewEnumerate())
|
|
return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
|
|
|
|
if (!XPC_WN_Shared_Enumerate(cx, obj))
|
|
return false;
|
|
|
|
bool retval = true;
|
|
nsresult rv = si->GetCallback()->NewEnumerate(wrapper, cx, obj, properties, &retval);
|
|
if (NS_FAILED(rv))
|
|
return Throw(rv, cx);
|
|
return retval;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
// static
|
|
XPCNativeScriptableInfo*
|
|
XPCNativeScriptableInfo::Construct(const XPCNativeScriptableCreateInfo* sci)
|
|
{
|
|
MOZ_ASSERT(sci, "bad param");
|
|
MOZ_ASSERT(sci->GetCallback(), "bad param");
|
|
|
|
XPCNativeScriptableInfo* newObj =
|
|
new XPCNativeScriptableInfo(sci->GetCallback());
|
|
if (!newObj)
|
|
return nullptr;
|
|
|
|
char* name = nullptr;
|
|
if (NS_FAILED(sci->GetCallback()->GetClassName(&name)) || !name) {
|
|
delete newObj;
|
|
return nullptr;
|
|
}
|
|
|
|
bool success;
|
|
|
|
XPCJSRuntime* rt = XPCJSRuntime::Get();
|
|
XPCNativeScriptableSharedMap* map = rt->GetNativeScriptableSharedMap();
|
|
success = map->GetNewOrUsed(sci->GetFlags(), name, newObj);
|
|
|
|
if (!success) {
|
|
delete newObj;
|
|
return nullptr;
|
|
}
|
|
|
|
return newObj;
|
|
}
|
|
|
|
void
|
|
XPCNativeScriptableShared::PopulateJSClass()
|
|
{
|
|
MOZ_ASSERT(mJSClass.base.name, "bad state!");
|
|
|
|
mJSClass.base.flags = WRAPPER_FLAGS |
|
|
JSCLASS_PRIVATE_IS_NSISUPPORTS;
|
|
|
|
if (mFlags.IsGlobalObject())
|
|
mJSClass.base.flags |= XPCONNECT_GLOBAL_FLAGS;
|
|
|
|
JSAddPropertyOp addProperty;
|
|
if (mFlags.WantAddProperty())
|
|
addProperty = XPC_WN_Helper_AddProperty;
|
|
else if (mFlags.UseJSStubForAddProperty())
|
|
addProperty = nullptr;
|
|
else if (mFlags.AllowPropModsDuringResolve())
|
|
addProperty = XPC_WN_MaybeResolvingPropertyStub;
|
|
else
|
|
addProperty = XPC_WN_CannotModifyPropertyStub;
|
|
mJSClass.base.addProperty = addProperty;
|
|
|
|
JSDeletePropertyOp delProperty;
|
|
if (mFlags.UseJSStubForDelProperty())
|
|
delProperty = nullptr;
|
|
else if (mFlags.AllowPropModsDuringResolve())
|
|
delProperty = XPC_WN_MaybeResolvingDeletePropertyStub;
|
|
else
|
|
delProperty = XPC_WN_CantDeletePropertyStub;
|
|
mJSClass.base.delProperty = delProperty;
|
|
|
|
if (mFlags.WantGetProperty())
|
|
mJSClass.base.getProperty = XPC_WN_Helper_GetProperty;
|
|
else
|
|
mJSClass.base.getProperty = nullptr;
|
|
|
|
JSSetterOp setProperty;
|
|
if (mFlags.WantSetProperty())
|
|
setProperty = XPC_WN_Helper_SetProperty;
|
|
else if (mFlags.UseJSStubForSetProperty())
|
|
setProperty = nullptr;
|
|
else if (mFlags.AllowPropModsDuringResolve())
|
|
setProperty = XPC_WN_MaybeResolvingSetPropertyStub;
|
|
else
|
|
setProperty = XPC_WN_CannotModifySetPropertyStub;
|
|
mJSClass.base.setProperty = setProperty;
|
|
|
|
MOZ_ASSERT_IF(mFlags.WantEnumerate(), !mFlags.WantNewEnumerate());
|
|
MOZ_ASSERT_IF(mFlags.WantNewEnumerate(), !mFlags.WantEnumerate());
|
|
|
|
// We will use ops->enumerate set below for NewEnumerate
|
|
if (mFlags.WantNewEnumerate())
|
|
mJSClass.base.enumerate = nullptr;
|
|
else if (mFlags.WantEnumerate())
|
|
mJSClass.base.enumerate = XPC_WN_Helper_Enumerate;
|
|
else
|
|
mJSClass.base.enumerate = XPC_WN_Shared_Enumerate;
|
|
|
|
// We have to figure out resolve strategy at call time
|
|
mJSClass.base.resolve = XPC_WN_Helper_Resolve;
|
|
|
|
if (mFlags.WantFinalize())
|
|
mJSClass.base.finalize = XPC_WN_Helper_Finalize;
|
|
else
|
|
mJSClass.base.finalize = XPC_WN_NoHelper_Finalize;
|
|
|
|
js::ObjectOps* ops = &mJSClass.base.ops;
|
|
if (mFlags.WantNewEnumerate())
|
|
ops->enumerate = XPC_WN_JSOp_Enumerate;
|
|
|
|
if (mFlags.WantCall())
|
|
mJSClass.base.call = XPC_WN_Helper_Call;
|
|
if (mFlags.WantConstruct())
|
|
mJSClass.base.construct = XPC_WN_Helper_Construct;
|
|
|
|
if (mFlags.WantHasInstance())
|
|
mJSClass.base.hasInstance = XPC_WN_Helper_HasInstance;
|
|
|
|
if (mFlags.IsGlobalObject())
|
|
mJSClass.base.trace = JS_GlobalObjectTraceHook;
|
|
else
|
|
mJSClass.base.trace = XPCWrappedNative::Trace;
|
|
|
|
mJSClass.base.ext.isWrappedNative = true;
|
|
mJSClass.base.ext.objectMovedOp = WrappedNativeObjectMoved;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
// Compatibility hack.
|
|
//
|
|
// XPConnect used to do all sorts of funny tricks to find the "correct"
|
|
// |this| object for a given method (often to the detriment of proper
|
|
// call/apply). When these tricks were removed, a fair amount of chrome
|
|
// code broke, because it was relying on being able to grab methods off
|
|
// some XPCOM object (like the nsITelemetry service) and invoke them without
|
|
// a proper |this|. So, if it's quite clear that we're in this situation and
|
|
// about to use a |this| argument that just won't work, fix things up.
|
|
//
|
|
// This hack is only useful for getters/setters if someone sets an XPCOM object
|
|
// as the prototype for a vanilla JS object and expects the XPCOM attributes to
|
|
// work on the derived object, which we really don't want to support. But we
|
|
// handle it anyway, for now, to minimize regression risk on an already-risky
|
|
// landing.
|
|
//
|
|
// This hack is mainly useful for the NoHelper JSClass. We also fix up
|
|
// Components.utils because it implements nsIXPCScriptable (giving it a custom
|
|
// JSClass) but not nsIClassInfo (which would put the methods on a prototype).
|
|
|
|
#define IS_NOHELPER_CLASS(clasp) (clasp == &XPC_WN_NoHelper_JSClass.base)
|
|
#define IS_CU_CLASS(clasp) (clasp->name[0] == 'n' && !strcmp(clasp->name, "nsXPCComponents_Utils"))
|
|
|
|
MOZ_ALWAYS_INLINE JSObject*
|
|
FixUpThisIfBroken(JSObject* obj, JSObject* funobj)
|
|
{
|
|
if (funobj) {
|
|
JSObject* parentObj =
|
|
&js::GetFunctionNativeReserved(funobj,
|
|
XPC_FUNCTION_PARENT_OBJECT_SLOT).toObject();
|
|
const js::Class* parentClass = js::GetObjectClass(parentObj);
|
|
if (MOZ_UNLIKELY((IS_NOHELPER_CLASS(parentClass) || IS_CU_CLASS(parentClass)) &&
|
|
(js::GetObjectClass(obj) != parentClass)))
|
|
{
|
|
return parentObj;
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
bool
|
|
XPC_WN_CallMethod(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
|
MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
|
|
RootedObject funobj(cx, &args.callee());
|
|
|
|
RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
|
|
if (!obj)
|
|
return false;
|
|
|
|
obj = FixUpThisIfBroken(obj, funobj);
|
|
XPCCallContext ccx(JS_CALLER, cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
|
|
args.array(), vp);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
XPCNativeInterface* iface;
|
|
XPCNativeMember* member;
|
|
|
|
if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member))
|
|
return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx);
|
|
ccx.SetCallInfo(iface, member, false);
|
|
return XPCWrappedNative::CallMethod(ccx);
|
|
}
|
|
|
|
bool
|
|
XPC_WN_GetterSetter(JSContext* cx, unsigned argc, Value* vp)
|
|
{
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
|
MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
|
|
RootedObject funobj(cx, &args.callee());
|
|
|
|
RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
|
|
if (!obj)
|
|
return false;
|
|
|
|
obj = FixUpThisIfBroken(obj, funobj);
|
|
XPCCallContext ccx(JS_CALLER, cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
|
|
args.array(), vp);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
XPCNativeInterface* iface;
|
|
XPCNativeMember* member;
|
|
|
|
if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member))
|
|
return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx);
|
|
|
|
if (args.length() != 0 && member->IsWritableAttribute()) {
|
|
ccx.SetCallInfo(iface, member, true);
|
|
bool retval = XPCWrappedNative::SetAttribute(ccx);
|
|
if (retval)
|
|
args.rval().set(args[0]);
|
|
return retval;
|
|
}
|
|
// else...
|
|
|
|
ccx.SetCallInfo(iface, member, false);
|
|
return XPCWrappedNative::GetAttribute(ccx);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
static bool
|
|
XPC_WN_Shared_Proto_Enumerate(JSContext* cx, HandleObject obj)
|
|
{
|
|
MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
|
|
js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass ||
|
|
js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
|
|
js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass,
|
|
"bad proto");
|
|
XPCWrappedNativeProto* self =
|
|
(XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
|
|
if (!self)
|
|
return false;
|
|
|
|
XPCNativeSet* set = self->GetSet();
|
|
if (!set)
|
|
return false;
|
|
|
|
XPCCallContext ccx(JS_CALLER, cx);
|
|
if (!ccx.IsValid())
|
|
return false;
|
|
|
|
uint16_t interface_count = set->GetInterfaceCount();
|
|
XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
|
|
for (uint16_t i = 0; i < interface_count; i++) {
|
|
XPCNativeInterface* iface = interfaceArray[i];
|
|
uint16_t member_count = iface->GetMemberCount();
|
|
|
|
for (uint16_t k = 0; k < member_count; k++) {
|
|
if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName()))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
XPC_WN_Shared_Proto_Finalize(js::FreeOp* fop, JSObject* obj)
|
|
{
|
|
// This can be null if xpc shutdown has already happened
|
|
XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
|
|
if (p)
|
|
p->JSProtoObjectFinalized(fop, obj);
|
|
}
|
|
|
|
static void
|
|
XPC_WN_Shared_Proto_ObjectMoved(JSObject* obj, const JSObject* old)
|
|
{
|
|
// This can be null if xpc shutdown has already happened
|
|
XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
|
|
if (p)
|
|
p->JSProtoObjectMoved(obj, old);
|
|
}
|
|
|
|
static void
|
|
XPC_WN_Shared_Proto_Trace(JSTracer* trc, JSObject* obj)
|
|
{
|
|
// This can be null if xpc shutdown has already happened
|
|
XPCWrappedNativeProto* p =
|
|
(XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
|
|
if (p)
|
|
p->TraceInside(trc);
|
|
}
|
|
|
|
/*****************************************************/
|
|
|
|
static bool
|
|
XPC_WN_ModsAllowed_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvep)
|
|
{
|
|
MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
|
|
js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass,
|
|
"bad proto");
|
|
|
|
XPCWrappedNativeProto* self =
|
|
(XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
|
|
if (!self)
|
|
return false;
|
|
|
|
XPCCallContext ccx(JS_CALLER, cx);
|
|
if (!ccx.IsValid())
|
|
return false;
|
|
|
|
XPCNativeScriptableInfo* si = self->GetScriptableInfo();
|
|
return DefinePropertyIfFound(ccx, obj, id,
|
|
self->GetSet(), nullptr, nullptr,
|
|
self->GetScope(),
|
|
true, nullptr, nullptr, si,
|
|
JSPROP_ENUMERATE, resolvep);
|
|
}
|
|
|
|
#define XPC_WN_SHARED_PROTO_CLASS_EXT \
|
|
{ \
|
|
false, /* isWrappedNative */ \
|
|
nullptr, /* weakmapKeyDelegateOp */ \
|
|
XPC_WN_Shared_Proto_ObjectMoved \
|
|
}
|
|
|
|
const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass = {
|
|
"XPC_WN_ModsAllowed_WithCall_Proto_JSClass", // name;
|
|
WRAPPER_FLAGS, // flags;
|
|
|
|
/* Function pointer members. */
|
|
nullptr, // addProperty;
|
|
nullptr, // delProperty;
|
|
nullptr, // getProperty;
|
|
nullptr, // setProperty;
|
|
XPC_WN_Shared_Proto_Enumerate, // enumerate;
|
|
XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
|
|
nullptr, // mayResolve;
|
|
XPC_WN_Shared_Proto_Finalize, // finalize;
|
|
|
|
/* Optionally non-null members start here. */
|
|
nullptr, // call;
|
|
nullptr, // construct;
|
|
nullptr, // hasInstance;
|
|
XPC_WN_Shared_Proto_Trace, // trace;
|
|
|
|
JS_NULL_CLASS_SPEC,
|
|
XPC_WN_SHARED_PROTO_CLASS_EXT,
|
|
XPC_WN_WithCall_ObjectOps
|
|
};
|
|
|
|
const js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass = {
|
|
"XPC_WN_ModsAllowed_NoCall_Proto_JSClass", // name;
|
|
WRAPPER_FLAGS, // flags;
|
|
|
|
/* Function pointer members. */
|
|
nullptr, // addProperty;
|
|
nullptr, // delProperty;
|
|
nullptr, // getProperty;
|
|
nullptr, // setProperty;
|
|
XPC_WN_Shared_Proto_Enumerate, // enumerate;
|
|
XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
|
|
nullptr, // mayResolve;
|
|
XPC_WN_Shared_Proto_Finalize, // finalize;
|
|
|
|
/* Optionally non-null members start here. */
|
|
nullptr, // call;
|
|
nullptr, // construct;
|
|
nullptr, // hasInstance;
|
|
XPC_WN_Shared_Proto_Trace, // trace;
|
|
|
|
JS_NULL_CLASS_SPEC,
|
|
XPC_WN_SHARED_PROTO_CLASS_EXT,
|
|
XPC_WN_NoCall_ObjectOps
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
static bool
|
|
XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
|
|
HandleValue v)
|
|
{
|
|
MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
|
|
js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass,
|
|
"bad proto");
|
|
|
|
XPCWrappedNativeProto* self =
|
|
(XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
|
|
if (!self)
|
|
return false;
|
|
|
|
XPCCallContext ccx(JS_CALLER, cx);
|
|
if (!ccx.IsValid())
|
|
return false;
|
|
|
|
// Allow XPConnect to add the property only
|
|
if (ccx.GetResolveName() == id)
|
|
return true;
|
|
|
|
return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_NoMods_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
|
{
|
|
MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
|
|
js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass,
|
|
"bad proto");
|
|
|
|
XPCWrappedNativeProto* self =
|
|
(XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
|
|
if (!self)
|
|
return false;
|
|
|
|
XPCCallContext ccx(JS_CALLER, cx);
|
|
if (!ccx.IsValid())
|
|
return false;
|
|
|
|
XPCNativeScriptableInfo* si = self->GetScriptableInfo();
|
|
|
|
return DefinePropertyIfFound(ccx, obj, id,
|
|
self->GetSet(), nullptr, nullptr,
|
|
self->GetScope(),
|
|
true, nullptr, nullptr, si,
|
|
JSPROP_READONLY |
|
|
JSPROP_PERMANENT |
|
|
JSPROP_ENUMERATE, resolvedp);
|
|
}
|
|
|
|
const js::Class XPC_WN_NoMods_WithCall_Proto_JSClass = {
|
|
"XPC_WN_NoMods_WithCall_Proto_JSClass", // name;
|
|
WRAPPER_FLAGS, // flags;
|
|
|
|
/* Mandatory non-null function pointer members. */
|
|
XPC_WN_OnlyIWrite_Proto_AddPropertyStub, // addProperty;
|
|
XPC_WN_CantDeletePropertyStub, // delProperty;
|
|
nullptr, // getProperty;
|
|
nullptr, // setProperty;
|
|
XPC_WN_Shared_Proto_Enumerate, // enumerate;
|
|
XPC_WN_NoMods_Proto_Resolve, // resolve;
|
|
nullptr, // mayResolve;
|
|
XPC_WN_Shared_Proto_Finalize, // finalize;
|
|
|
|
/* Optionally non-null members start here. */
|
|
nullptr, // call;
|
|
nullptr, // construct;
|
|
nullptr, // hasInstance;
|
|
XPC_WN_Shared_Proto_Trace, // trace;
|
|
|
|
JS_NULL_CLASS_SPEC,
|
|
XPC_WN_SHARED_PROTO_CLASS_EXT,
|
|
XPC_WN_WithCall_ObjectOps
|
|
};
|
|
|
|
const js::Class XPC_WN_NoMods_NoCall_Proto_JSClass = {
|
|
"XPC_WN_NoMods_NoCall_Proto_JSClass", // name;
|
|
WRAPPER_FLAGS, // flags;
|
|
|
|
/* Mandatory non-null function pointer members. */
|
|
XPC_WN_OnlyIWrite_Proto_AddPropertyStub, // addProperty;
|
|
XPC_WN_CantDeletePropertyStub, // delProperty;
|
|
nullptr, // getProperty;
|
|
nullptr, // setProperty;
|
|
XPC_WN_Shared_Proto_Enumerate, // enumerate;
|
|
XPC_WN_NoMods_Proto_Resolve, // resolve;
|
|
nullptr, // mayResolve;
|
|
XPC_WN_Shared_Proto_Finalize, // finalize;
|
|
|
|
/* Optionally non-null members start here. */
|
|
nullptr, // call;
|
|
nullptr, // construct;
|
|
nullptr, // hasInstance;
|
|
XPC_WN_Shared_Proto_Trace, // trace;
|
|
|
|
JS_NULL_CLASS_SPEC,
|
|
XPC_WN_SHARED_PROTO_CLASS_EXT,
|
|
XPC_WN_NoCall_ObjectOps
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
static bool
|
|
XPC_WN_TearOff_Enumerate(JSContext* cx, HandleObject obj)
|
|
{
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
XPCWrappedNativeTearOff* to = ccx.GetTearOff();
|
|
XPCNativeInterface* iface;
|
|
|
|
if (!to || nullptr == (iface = to->GetInterface()))
|
|
return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
|
|
|
|
uint16_t member_count = iface->GetMemberCount();
|
|
for (uint16_t k = 0; k < member_count; k++) {
|
|
if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName()))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
XPC_WN_TearOff_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
|
{
|
|
XPCCallContext ccx(JS_CALLER, cx, obj);
|
|
XPCWrappedNative* wrapper = ccx.GetWrapper();
|
|
THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
|
|
|
|
XPCWrappedNativeTearOff* to = ccx.GetTearOff();
|
|
XPCNativeInterface* iface;
|
|
|
|
if (!to || nullptr == (iface = to->GetInterface()))
|
|
return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
|
|
|
|
return DefinePropertyIfFound(ccx, obj, id, nullptr, iface, nullptr,
|
|
wrapper->GetScope(),
|
|
true, nullptr, nullptr, nullptr,
|
|
JSPROP_READONLY |
|
|
JSPROP_PERMANENT |
|
|
JSPROP_ENUMERATE, resolvedp);
|
|
}
|
|
|
|
static void
|
|
XPC_WN_TearOff_Finalize(js::FreeOp* fop, JSObject* obj)
|
|
{
|
|
XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
|
|
xpc_GetJSPrivate(obj);
|
|
if (!p)
|
|
return;
|
|
p->JSObjectFinalized();
|
|
}
|
|
|
|
static void
|
|
XPC_WN_TearOff_ObjectMoved(JSObject* obj, const JSObject* old)
|
|
{
|
|
XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
|
|
xpc_GetJSPrivate(obj);
|
|
if (!p)
|
|
return;
|
|
p->JSObjectMoved(obj, old);
|
|
}
|
|
|
|
// Make sure WRAPPER_FLAGS has no reserved slots, so our XPC_WN_TEAROFF_RESERVED_SLOTS value is OK.
|
|
|
|
static_assert(((WRAPPER_FLAGS >> JSCLASS_RESERVED_SLOTS_SHIFT) &
|
|
JSCLASS_RESERVED_SLOTS_MASK) == 0,
|
|
"WRAPPER_FLAGS should not include any reserved slots");
|
|
|
|
const js::Class XPC_WN_Tearoff_JSClass = {
|
|
"WrappedNative_TearOff", // name;
|
|
WRAPPER_FLAGS |
|
|
JSCLASS_HAS_RESERVED_SLOTS(XPC_WN_TEAROFF_RESERVED_SLOTS), // flags;
|
|
XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty;
|
|
XPC_WN_CantDeletePropertyStub, // delProperty;
|
|
nullptr, // getProperty;
|
|
nullptr, // setProperty;
|
|
XPC_WN_TearOff_Enumerate, // enumerate;
|
|
XPC_WN_TearOff_Resolve, // resolve;
|
|
nullptr, // mayResolve;
|
|
XPC_WN_TearOff_Finalize, // finalize;
|
|
|
|
/* Optionally non-null members start here. */
|
|
nullptr, // call
|
|
nullptr, // construct
|
|
nullptr, // hasInstance
|
|
nullptr, // trace
|
|
JS_NULL_CLASS_SPEC,
|
|
|
|
// ClassExtension
|
|
{
|
|
false, // isWrappedNative
|
|
nullptr, // weakmapKeyDelegateOp
|
|
XPC_WN_TearOff_ObjectMoved
|
|
},
|
|
};
|