michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */ michael@0: michael@0: #include "xpcprivate.h" michael@0: #include "jsprf.h" michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace JS; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: // All of the exceptions thrown into JS from this file go through here. michael@0: // That makes this a nice place to set a breakpoint. michael@0: michael@0: static bool Throw(nsresult errNum, JSContext* cx) michael@0: { michael@0: XPCThrower::Throw(errNum, cx); michael@0: return false; michael@0: } michael@0: michael@0: // Handy macro used in many callback stub below. michael@0: michael@0: #define THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (!wrapper) \ michael@0: return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \ michael@0: if (!wrapper->IsValid()) \ michael@0: return Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx); \ michael@0: PR_END_MACRO michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: static bool michael@0: ToStringGuts(XPCCallContext& ccx) michael@0: { michael@0: char* sz; michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: michael@0: if (wrapper) michael@0: sz = wrapper->ToString(ccx.GetTearOff()); michael@0: else michael@0: sz = JS_smprintf("[xpconnect wrapped native prototype]"); michael@0: michael@0: if (!sz) { michael@0: JS_ReportOutOfMemory(ccx); michael@0: return false; michael@0: } michael@0: michael@0: JSString* str = JS_NewStringCopyZ(ccx, sz); michael@0: JS_smprintf_free(sz); michael@0: if (!str) michael@0: return false; michael@0: michael@0: ccx.SetRetVal(STRING_TO_JSVAL(str)); michael@0: return true; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: static bool michael@0: XPC_WN_Shared_ToString(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: if (!ccx.IsValid()) michael@0: return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); michael@0: ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING)); michael@0: ccx.SetArgsAndResultPtr(args.length(), args.array(), vp); michael@0: return ToStringGuts(ccx); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_Shared_ToSource(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: static const char empty[] = "({})"; michael@0: JSString *str = JS_NewStringCopyN(cx, empty, sizeof(empty)-1); michael@0: if (!str) michael@0: return false; michael@0: args.rval().setString(str); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: // A "double wrapped object" is a user JSObject that has been wrapped as a michael@0: // wrappedJS in order to be used by native code and then re-wrapped by a michael@0: // wrappedNative wrapper to be used by JS code. One might think of it as: michael@0: // wrappedNative(wrappedJS(underlying_JSObject)) michael@0: // This is done (as opposed to just unwrapping the wrapped JS and automatically michael@0: // returning the underlying JSObject) so that JS callers will see what looks michael@0: // Like any other xpcom object - and be limited to use its interfaces. michael@0: // michael@0: // See the comment preceding nsIXPCWrappedJSObjectGetter in nsIXPConnect.idl. michael@0: michael@0: static JSObject* michael@0: GetDoubleWrappedJSObject(XPCCallContext& ccx, XPCWrappedNative* wrapper) michael@0: { michael@0: RootedObject obj(ccx); michael@0: nsCOMPtr michael@0: underware = do_QueryInterface(wrapper->GetIdentityObject()); michael@0: if (underware) { michael@0: RootedObject mainObj(ccx, underware->GetJSObject()); michael@0: if (mainObj) { michael@0: RootedId id(ccx, ccx.GetRuntime()-> michael@0: GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)); michael@0: michael@0: JSAutoCompartment ac(ccx, mainObj); michael@0: michael@0: RootedValue val(ccx); michael@0: if (JS_GetPropertyById(ccx, mainObj, id, &val) && michael@0: !JSVAL_IS_PRIMITIVE(val)) { michael@0: obj = JSVAL_TO_OBJECT(val); michael@0: } michael@0: } michael@0: } michael@0: return obj; michael@0: } michael@0: michael@0: // This is the getter native function we use to handle 'wrappedJSObject' for michael@0: // double wrapped JSObjects. michael@0: michael@0: static bool michael@0: XPC_WN_DoubleWrappedGetter(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function"); michael@0: michael@0: RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper)); michael@0: if (!realObject) { michael@0: // This is pretty unexpected at this point. The object originally michael@0: // responded to this get property call and now gives no object. michael@0: // XXX Should this throw something at the caller? michael@0: args.rval().setNull(); michael@0: return true; michael@0: } michael@0: michael@0: // It is a double wrapped object. This should really never appear in michael@0: // content these days, but addons still do it - see bug 965921. michael@0: if (MOZ_UNLIKELY(!nsContentUtils::IsCallerChrome())) { michael@0: JS_ReportError(cx, "Attempt to use .wrappedJSObject in untrusted code"); michael@0: return false; michael@0: } michael@0: args.rval().setObject(*realObject); michael@0: return JS_WrapValue(cx, args.rval()); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: // This is our shared function to define properties on our JSObjects. michael@0: michael@0: /* michael@0: * NOTE: michael@0: * We *never* set the tearoff names (e.g. nsIFoo) as JS_ENUMERATE. michael@0: * We *never* set toString or toSource as JS_ENUMERATE. michael@0: */ michael@0: michael@0: static bool michael@0: DefinePropertyIfFound(XPCCallContext& ccx, michael@0: HandleObject obj, michael@0: HandleId idArg, michael@0: XPCNativeSet* set, michael@0: XPCNativeInterface* iface, michael@0: XPCNativeMember* member, michael@0: XPCWrappedNativeScope* scope, michael@0: bool reflectToStringAndToSource, michael@0: XPCWrappedNative* wrapperToReflectInterfaceNames, michael@0: XPCWrappedNative* wrapperToReflectDoubleWrap, michael@0: XPCNativeScriptableInfo* scriptableInfo, michael@0: unsigned propFlags, michael@0: bool* resolved) michael@0: { michael@0: RootedId id(ccx, idArg); michael@0: XPCJSRuntime* rt = ccx.GetRuntime(); michael@0: bool found; michael@0: const char* name; michael@0: michael@0: if (set) { michael@0: if (iface) michael@0: found = true; michael@0: else michael@0: found = set->FindMember(id, &member, &iface); michael@0: } else michael@0: found = (nullptr != (member = iface->FindMember(id))); michael@0: michael@0: if (!found) { michael@0: if (reflectToStringAndToSource) { michael@0: JSNative call; michael@0: uint32_t flags = 0; michael@0: michael@0: if (scriptableInfo) { michael@0: nsCOMPtr classInfo = do_QueryInterface( michael@0: scriptableInfo->GetCallback()); michael@0: michael@0: if (classInfo) { michael@0: nsresult rv = classInfo->GetFlags(&flags); michael@0: if (NS_FAILED(rv)) michael@0: return Throw(rv, ccx); michael@0: } michael@0: } michael@0: michael@0: bool overwriteToString = !(flags & nsIClassInfo::DOM_OBJECT) michael@0: || Preferences::GetBool("dom.XPCToStringForDOMClasses", false); michael@0: michael@0: if(id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING) michael@0: && overwriteToString) michael@0: { michael@0: call = XPC_WN_Shared_ToString; michael@0: name = rt->GetStringName(XPCJSRuntime::IDX_TO_STRING); michael@0: id = rt->GetStringID(XPCJSRuntime::IDX_TO_STRING); michael@0: } else if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE)) { michael@0: call = XPC_WN_Shared_ToSource; michael@0: name = rt->GetStringName(XPCJSRuntime::IDX_TO_SOURCE); michael@0: id = rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE); michael@0: } michael@0: michael@0: else michael@0: call = nullptr; michael@0: michael@0: if (call) { michael@0: RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, obj, name)); michael@0: if (!fun) { michael@0: JS_ReportOutOfMemory(ccx); michael@0: return false; michael@0: } michael@0: michael@0: AutoResolveName arn(ccx, id); michael@0: if (resolved) michael@0: *resolved = true; michael@0: return JS_DefinePropertyById(ccx, obj, id, michael@0: OBJECT_TO_JSVAL(JS_GetFunctionObject(fun)), michael@0: nullptr, nullptr, michael@0: propFlags & ~JSPROP_ENUMERATE); michael@0: } michael@0: } michael@0: // This *might* be a tearoff name that is not yet part of our michael@0: // set. Let's lookup the name and see if it is the name of an michael@0: // interface. Then we'll see if the object actually *does* this michael@0: // interface and add a tearoff as necessary. michael@0: michael@0: if (wrapperToReflectInterfaceNames) { michael@0: JSAutoByteString name; michael@0: AutoMarkingNativeInterfacePtr iface2(ccx); michael@0: XPCWrappedNativeTearOff* to; michael@0: RootedObject jso(ccx); michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (JSID_IS_STRING(id) && michael@0: name.encodeLatin1(ccx, JSID_TO_STRING(id)) && michael@0: (iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr()), iface2) && michael@0: nullptr != (to = wrapperToReflectInterfaceNames-> michael@0: FindTearOff(iface2, true, &rv)) && michael@0: nullptr != (jso = to->GetJSObject())) michael@0: michael@0: { michael@0: AutoResolveName arn(ccx, id); michael@0: if (resolved) michael@0: *resolved = true; michael@0: return JS_DefinePropertyById(ccx, obj, id, OBJECT_TO_JSVAL(jso), michael@0: nullptr, nullptr, michael@0: propFlags & ~JSPROP_ENUMERATE); michael@0: } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) { michael@0: return Throw(rv, ccx); michael@0: } michael@0: } michael@0: michael@0: // This *might* be a double wrapped JSObject michael@0: if (wrapperToReflectDoubleWrap && michael@0: id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT) && michael@0: GetDoubleWrappedJSObject(ccx, wrapperToReflectDoubleWrap)) { michael@0: // We build and add a getter function. michael@0: // A security check is done on a per-get basis. michael@0: michael@0: JSFunction* fun; michael@0: michael@0: id = rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT); michael@0: name = rt->GetStringName(XPCJSRuntime::IDX_WRAPPED_JSOBJECT); michael@0: michael@0: fun = JS_NewFunction(ccx, XPC_WN_DoubleWrappedGetter, michael@0: 0, 0, obj, name); michael@0: michael@0: if (!fun) michael@0: return false; michael@0: michael@0: RootedObject funobj(ccx, JS_GetFunctionObject(fun)); michael@0: if (!funobj) michael@0: return false; michael@0: michael@0: propFlags |= JSPROP_GETTER | JSPROP_SHARED; michael@0: propFlags &= ~JSPROP_ENUMERATE; michael@0: michael@0: AutoResolveName arn(ccx, id); michael@0: if (resolved) michael@0: *resolved = true; michael@0: return JS_DefinePropertyById(ccx, obj, id, JSVAL_VOID, michael@0: JS_DATA_TO_FUNC_PTR(JSPropertyOp, michael@0: funobj.get()), michael@0: nullptr, propFlags); michael@0: } michael@0: michael@0: if (resolved) michael@0: *resolved = false; michael@0: return true; michael@0: } michael@0: michael@0: if (!member) { michael@0: if (wrapperToReflectInterfaceNames) { michael@0: XPCWrappedNativeTearOff* to = michael@0: wrapperToReflectInterfaceNames->FindTearOff(iface, true); michael@0: michael@0: if (!to) michael@0: return false; michael@0: RootedObject jso(ccx, to->GetJSObject()); michael@0: if (!jso) michael@0: return false; michael@0: michael@0: AutoResolveName arn(ccx, id); michael@0: if (resolved) michael@0: *resolved = true; michael@0: return JS_DefinePropertyById(ccx, obj, id, OBJECT_TO_JSVAL(jso), michael@0: nullptr, nullptr, michael@0: propFlags & ~JSPROP_ENUMERATE); michael@0: } michael@0: if (resolved) michael@0: *resolved = false; michael@0: return true; michael@0: } michael@0: michael@0: if (member->IsConstant()) { michael@0: RootedValue val(ccx); michael@0: AutoResolveName arn(ccx, id); michael@0: if (resolved) michael@0: *resolved = true; michael@0: return member->GetConstantValue(ccx, iface, val.address()) && michael@0: JS_DefinePropertyById(ccx, obj, id, val, nullptr, nullptr, michael@0: propFlags); michael@0: } michael@0: michael@0: if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING) || michael@0: id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE) || michael@0: (scriptableInfo && michael@0: scriptableInfo->GetFlags().DontEnumQueryInterface() && michael@0: id == rt->GetStringID(XPCJSRuntime::IDX_QUERY_INTERFACE))) michael@0: propFlags &= ~JSPROP_ENUMERATE; michael@0: michael@0: RootedValue funval(ccx); michael@0: if (!member->NewFunctionObject(ccx, iface, obj, funval.address())) michael@0: return false; michael@0: michael@0: if (member->IsMethod()) { michael@0: AutoResolveName arn(ccx, id); michael@0: if (resolved) michael@0: *resolved = true; michael@0: return JS_DefinePropertyById(ccx, obj, id, funval, nullptr, nullptr, michael@0: propFlags); michael@0: } michael@0: michael@0: // else... michael@0: michael@0: MOZ_ASSERT(member->IsAttribute(), "way broken!"); michael@0: michael@0: propFlags |= JSPROP_GETTER | JSPROP_SHARED; michael@0: JSObject* funobj = JSVAL_TO_OBJECT(funval); michael@0: JSPropertyOp getter = JS_DATA_TO_FUNC_PTR(JSPropertyOp, funobj); michael@0: JSStrictPropertyOp setter; michael@0: if (member->IsWritableAttribute()) { michael@0: propFlags |= JSPROP_SETTER; michael@0: propFlags &= ~JSPROP_READONLY; michael@0: setter = JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, funobj); michael@0: } else { michael@0: setter = js_GetterOnlyPropertyStub; michael@0: } michael@0: michael@0: AutoResolveName arn(ccx, id); michael@0: if (resolved) michael@0: *resolved = true; michael@0: michael@0: return JS_DefinePropertyById(ccx, obj, id, JSVAL_VOID, getter, setter, michael@0: propFlags); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: /***************************************************************************/ michael@0: michael@0: static bool michael@0: XPC_WN_OnlyIWrite_AddPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) michael@0: { michael@0: XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), id); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: // Allow only XPConnect to add/set the property michael@0: if (ccx.GetResolveName() == id) michael@0: return true; michael@0: michael@0: return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_OnlyIWrite_SetPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict, michael@0: MutableHandleValue vp) michael@0: { michael@0: return XPC_WN_OnlyIWrite_AddPropertyStub(cx, obj, id, vp); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_CannotModifyPropertyStub(JSContext *cx, HandleObject obj, HandleId id, michael@0: MutableHandleValue vp) michael@0: { michael@0: return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_CantDeletePropertyStub(JSContext *cx, HandleObject obj, HandleId id, michael@0: bool *succeeded) michael@0: { michael@0: return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_CannotModifyStrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict, michael@0: MutableHandleValue vp) michael@0: { michael@0: return XPC_WN_CannotModifyPropertyStub(cx, obj, id, vp); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_Shared_Convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp) michael@0: { michael@0: if (type == JSTYPE_OBJECT) { michael@0: vp.set(OBJECT_TO_JSVAL(obj)); michael@0: return true; michael@0: } michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: switch (type) { michael@0: case JSTYPE_FUNCTION: michael@0: { michael@0: if (!ccx.GetTearOff()) { michael@0: XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo(); michael@0: if (si && (si->GetFlags().WantCall() || michael@0: si->GetFlags().WantConstruct())) { michael@0: vp.set(OBJECT_TO_JSVAL(obj)); michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: return Throw(NS_ERROR_XPC_CANT_CONVERT_WN_TO_FUN, cx); michael@0: case JSTYPE_NUMBER: michael@0: vp.set(JS_GetNaNValue(cx)); michael@0: return true; michael@0: case JSTYPE_BOOLEAN: michael@0: vp.set(JSVAL_TRUE); michael@0: return true; michael@0: case JSTYPE_VOID: michael@0: case JSTYPE_STRING: michael@0: { michael@0: ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING)); michael@0: ccx.SetArgsAndResultPtr(0, nullptr, vp.address()); michael@0: michael@0: XPCNativeMember* member = ccx.GetMember(); michael@0: if (member && member->IsMethod()) { michael@0: if (!XPCWrappedNative::CallMethod(ccx)) michael@0: return false; michael@0: michael@0: if (JSVAL_IS_PRIMITIVE(vp)) michael@0: return true; michael@0: } michael@0: michael@0: // else... michael@0: return ToStringGuts(ccx); michael@0: } michael@0: default: michael@0: NS_ERROR("bad type in conversion"); michael@0: return false; michael@0: } michael@0: NS_NOTREACHED("huh?"); michael@0: return false; michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_Shared_Enumerate(JSContext *cx, HandleObject obj) michael@0: { michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: // Since we aren't going to enumerate tearoff names and the prototype michael@0: // handles non-mutated members, we can do this potential short-circuit. michael@0: if (!wrapper->HasMutatedSet()) michael@0: return true; michael@0: michael@0: XPCNativeSet* set = wrapper->GetSet(); michael@0: XPCNativeSet* protoSet = wrapper->HasProto() ? michael@0: wrapper->GetProto()->GetSet() : nullptr; michael@0: michael@0: uint16_t interface_count = set->GetInterfaceCount(); michael@0: XPCNativeInterface** interfaceArray = set->GetInterfaceArray(); michael@0: for (uint16_t i = 0; i < interface_count; i++) { michael@0: XPCNativeInterface* iface = interfaceArray[i]; michael@0: uint16_t member_count = iface->GetMemberCount(); michael@0: for (uint16_t k = 0; k < member_count; k++) { michael@0: XPCNativeMember* member = iface->GetMemberAt(k); michael@0: jsid name = member->GetName(); michael@0: michael@0: // Skip if this member is going to come from the proto. michael@0: uint16_t index; michael@0: if (protoSet && michael@0: protoSet->FindMember(name, nullptr, &index) && index == i) michael@0: continue; michael@0: if (!xpc_ForcePropertyResolve(cx, obj, name)) michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: enum WNHelperType { michael@0: WN_NOHELPER, michael@0: WN_HELPER michael@0: }; michael@0: michael@0: static void michael@0: WrappedNativeFinalize(js::FreeOp *fop, JSObject *obj, WNHelperType helperType) michael@0: { michael@0: const js::Class* clazz = js::GetObjectClass(obj); michael@0: if (clazz->flags & JSCLASS_DOM_GLOBAL) { michael@0: mozilla::dom::DestroyProtoAndIfaceCache(obj); michael@0: } michael@0: nsISupports* p = static_cast(xpc_GetJSPrivate(obj)); michael@0: if (!p) michael@0: return; michael@0: michael@0: XPCWrappedNative* wrapper = static_cast(p); michael@0: if (helperType == WN_HELPER) michael@0: wrapper->GetScriptableCallback()->Finalize(wrapper, js::CastToJSFreeOp(fop), obj); michael@0: wrapper->FlatJSObjectFinalized(); michael@0: } michael@0: michael@0: static void michael@0: XPC_WN_NoHelper_Finalize(js::FreeOp *fop, JSObject *obj) michael@0: { michael@0: WrappedNativeFinalize(fop, obj, WN_NOHELPER); michael@0: } michael@0: michael@0: /* michael@0: * General comment about XPConnect tracing: Given a C++ object |wrapper| and its michael@0: * corresponding JS object |obj|, calling |wrapper->TraceSelf| will ask the JS michael@0: * engine to mark |obj|. Eventually, this will lead to the trace hook being michael@0: * called for |obj|. The trace hook should call |wrapper->TraceInside|, which michael@0: * should mark any JS objects held by |wrapper| as members. michael@0: */ michael@0: michael@0: static void michael@0: MarkWrappedNative(JSTracer *trc, JSObject *obj) michael@0: { michael@0: const js::Class* clazz = js::GetObjectClass(obj); michael@0: if (clazz->flags & JSCLASS_DOM_GLOBAL) { michael@0: mozilla::dom::TraceProtoAndIfaceCache(trc, obj); michael@0: } michael@0: MOZ_ASSERT(IS_WN_CLASS(clazz)); michael@0: michael@0: XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj); michael@0: if (wrapper && wrapper->IsValid()) michael@0: wrapper->TraceInside(trc); michael@0: } michael@0: michael@0: /* static */ void michael@0: XPCWrappedNative::Trace(JSTracer *trc, JSObject *obj) michael@0: { michael@0: MarkWrappedNative(trc, obj); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_NoHelper_Resolve(JSContext *cx, HandleObject obj, HandleId id) michael@0: { michael@0: XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), id); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: XPCNativeSet* set = ccx.GetSet(); michael@0: if (!set) michael@0: return true; michael@0: michael@0: // Don't resolve properties that are on our prototype. michael@0: if (ccx.GetInterface() && !ccx.GetStaticMemberIsLocal()) michael@0: return true; michael@0: michael@0: return DefinePropertyIfFound(ccx, obj, id, michael@0: set, nullptr, nullptr, wrapper->GetScope(), michael@0: true, wrapper, wrapper, nullptr, michael@0: JSPROP_ENUMERATE | michael@0: JSPROP_READONLY | michael@0: JSPROP_PERMANENT, nullptr); michael@0: } michael@0: michael@0: static JSObject * michael@0: XPC_WN_OuterObject(JSContext *cx, HandleObject objArg) michael@0: { michael@0: JSObject *obj = objArg; michael@0: michael@0: XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj); michael@0: if (!wrapper) { michael@0: Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!wrapper->IsValid()) { michael@0: Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo(); michael@0: if (si && si->GetFlags().WantOuterObject()) { michael@0: RootedObject newThis(cx); michael@0: nsresult rv = michael@0: si->GetCallback()->OuterObject(wrapper, cx, obj, newThis.address()); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: Throw(rv, cx); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: obj = newThis; michael@0: } michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = { michael@0: { // base michael@0: "XPCWrappedNative_NoHelper", // name; michael@0: WRAPPER_SLOTS | michael@0: JSCLASS_PRIVATE_IS_NSISUPPORTS, // flags michael@0: michael@0: /* Mandatory non-null function pointer members. */ michael@0: XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty michael@0: XPC_WN_CantDeletePropertyStub, // delProperty michael@0: JS_PropertyStub, // getProperty michael@0: XPC_WN_OnlyIWrite_SetPropertyStub, // setProperty michael@0: michael@0: XPC_WN_Shared_Enumerate, // enumerate michael@0: XPC_WN_NoHelper_Resolve, // resolve michael@0: XPC_WN_Shared_Convert, // convert michael@0: XPC_WN_NoHelper_Finalize, // finalize michael@0: michael@0: /* Optionally non-null members start here. */ michael@0: nullptr, // call michael@0: nullptr, // construct michael@0: nullptr, // hasInstance michael@0: XPCWrappedNative::Trace, // trace michael@0: JS_NULL_CLASS_SPEC, michael@0: michael@0: // ClassExtension michael@0: { michael@0: nullptr, // outerObject michael@0: nullptr, // innerObject michael@0: nullptr, // iteratorObject michael@0: true, // isWrappedNative michael@0: }, michael@0: michael@0: // ObjectOps michael@0: { michael@0: nullptr, // lookupGeneric michael@0: nullptr, // lookupProperty michael@0: nullptr, // lookupElement michael@0: nullptr, // defineGeneric michael@0: nullptr, // defineProperty michael@0: nullptr, // defineElement michael@0: nullptr, // getGeneric michael@0: nullptr, // getProperty michael@0: nullptr, // getElement michael@0: nullptr, // setGeneric michael@0: nullptr, // setProperty michael@0: nullptr, // setElement michael@0: nullptr, // getGenericAttributes michael@0: nullptr, // setGenericAttributes michael@0: nullptr, // deleteProperty michael@0: nullptr, // deleteElement michael@0: nullptr, nullptr, // watch/unwatch michael@0: nullptr, // slice michael@0: XPC_WN_JSOp_Enumerate, michael@0: XPC_WN_JSOp_ThisObject, michael@0: } michael@0: }, michael@0: 0 // interfacesBitmap michael@0: }; michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: static bool michael@0: XPC_WN_MaybeResolvingPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) michael@0: { michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: if (ccx.GetResolvingWrapper() == wrapper) michael@0: return true; michael@0: return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_MaybeResolvingStrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict, michael@0: MutableHandleValue vp) michael@0: { michael@0: return XPC_WN_MaybeResolvingPropertyStub(cx, obj, id, vp); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_MaybeResolvingDeletePropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) michael@0: { michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: if (ccx.GetResolvingWrapper() == wrapper) { michael@0: *succeeded = true; michael@0: return true; michael@0: } michael@0: return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); michael@0: } michael@0: michael@0: // macro fun! michael@0: #define PRE_HELPER_STUB \ michael@0: JSObject *unwrapped = js::CheckedUnwrap(obj, false); \ michael@0: if (!unwrapped) { \ michael@0: JS_ReportError(cx, "Permission denied to operate on object."); \ michael@0: return false; \ michael@0: } \ michael@0: if (!IS_WN_REFLECTOR(unwrapped)) { \ michael@0: return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \ michael@0: } \ michael@0: XPCWrappedNative *wrapper = XPCWrappedNative::Get(unwrapped); \ michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); \ michael@0: bool retval = true; \ michael@0: nsresult rv = wrapper->GetScriptableCallback()-> michael@0: michael@0: #define POST_HELPER_STUB \ michael@0: if (NS_FAILED(rv)) \ michael@0: return Throw(rv, cx); \ michael@0: return retval; michael@0: michael@0: static bool michael@0: XPC_WN_Helper_AddProperty(JSContext *cx, HandleObject obj, HandleId id, michael@0: MutableHandleValue vp) michael@0: { michael@0: PRE_HELPER_STUB michael@0: AddProperty(wrapper, cx, obj, id, vp.address(), &retval); michael@0: POST_HELPER_STUB michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_Helper_DelProperty(JSContext *cx, HandleObject obj, HandleId id, michael@0: bool *succeeded) michael@0: { michael@0: *succeeded = true; michael@0: PRE_HELPER_STUB michael@0: DelProperty(wrapper, cx, obj, id, &retval); michael@0: POST_HELPER_STUB michael@0: } michael@0: michael@0: bool michael@0: XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, michael@0: MutableHandleValue vp) michael@0: { michael@0: PRE_HELPER_STUB michael@0: GetProperty(wrapper, cx, obj, id, vp.address(), &retval); michael@0: POST_HELPER_STUB michael@0: } michael@0: michael@0: bool michael@0: XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, michael@0: MutableHandleValue vp) michael@0: { michael@0: PRE_HELPER_STUB michael@0: SetProperty(wrapper, cx, obj, id, vp.address(), &retval); michael@0: POST_HELPER_STUB michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_Helper_Convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp) michael@0: { michael@0: PRE_HELPER_STUB michael@0: Convert(wrapper, cx, obj, type, vp.address(), &retval); michael@0: POST_HELPER_STUB michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_Helper_Call(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: JS::CallArgs args = JS::CallArgsFromVp(argc, vp); michael@0: // N.B. we want obj to be the callee, not JS_THIS(cx, vp) michael@0: RootedObject obj(cx, &args.callee()); michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), JSID_VOIDHANDLE, args.length(), michael@0: args.array(), args.rval().address()); michael@0: if (!ccx.IsValid()) michael@0: return false; michael@0: michael@0: MOZ_ASSERT(obj == ccx.GetFlattenedJSObject()); michael@0: michael@0: PRE_HELPER_STUB michael@0: Call(wrapper, cx, obj, args, &retval); michael@0: POST_HELPER_STUB michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_Helper_Construct(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: JS::CallArgs args = JS::CallArgsFromVp(argc, vp); michael@0: RootedObject obj(cx, &args.callee()); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), JSID_VOIDHANDLE, args.length(), michael@0: args.array(), args.rval().address()); michael@0: if (!ccx.IsValid()) michael@0: return false; michael@0: michael@0: MOZ_ASSERT(obj == ccx.GetFlattenedJSObject()); michael@0: michael@0: PRE_HELPER_STUB michael@0: Construct(wrapper, cx, obj, args, &retval); michael@0: POST_HELPER_STUB michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_Helper_HasInstance(JSContext *cx, HandleObject obj, MutableHandleValue valp, bool *bp) michael@0: { michael@0: bool retval2; michael@0: PRE_HELPER_STUB michael@0: HasInstance(wrapper, cx, obj, valp, &retval2, &retval); michael@0: *bp = retval2; michael@0: POST_HELPER_STUB michael@0: } michael@0: michael@0: static void michael@0: XPC_WN_Helper_Finalize(js::FreeOp *fop, JSObject *obj) michael@0: { michael@0: WrappedNativeFinalize(fop, obj, WN_HELPER); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_Helper_NewResolve(JSContext *cx, HandleObject obj, HandleId id, michael@0: MutableHandleObject objp) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: bool retval = true; michael@0: RootedObject obj2FromScriptable(cx); michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: RootedId old(cx, ccx.SetResolveName(id)); michael@0: michael@0: XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo(); michael@0: if (si && si->GetFlags().WantNewResolve()) { michael@0: XPCWrappedNative* oldResolvingWrapper; michael@0: bool allowPropMods = si->GetFlags().AllowPropModsDuringResolve(); michael@0: michael@0: if (allowPropMods) michael@0: oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper); michael@0: michael@0: rv = si->GetCallback()->NewResolve(wrapper, cx, obj, id, michael@0: obj2FromScriptable.address(), &retval); michael@0: michael@0: if (allowPropMods) michael@0: (void)ccx.SetResolvingWrapper(oldResolvingWrapper); michael@0: } michael@0: michael@0: old = ccx.SetResolveName(old); michael@0: MOZ_ASSERT(old == id, "bad nest"); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return Throw(rv, cx); michael@0: } michael@0: michael@0: if (obj2FromScriptable) { michael@0: objp.set(obj2FromScriptable); michael@0: } else if (wrapper->HasMutatedSet()) { michael@0: // We are here if scriptable did not resolve this property and michael@0: // it *might* be in the instance set but not the proto set. michael@0: michael@0: XPCNativeSet* set = wrapper->GetSet(); michael@0: XPCNativeSet* protoSet = wrapper->HasProto() ? michael@0: wrapper->GetProto()->GetSet() : nullptr; michael@0: XPCNativeMember* member; michael@0: XPCNativeInterface* iface; michael@0: bool IsLocal; michael@0: michael@0: if (set->FindMember(id, &member, &iface, protoSet, &IsLocal) && michael@0: IsLocal) { michael@0: XPCWrappedNative* oldResolvingWrapper; michael@0: michael@0: XPCNativeScriptableFlags siFlags(0); michael@0: if (si) michael@0: siFlags = si->GetFlags(); michael@0: michael@0: unsigned enumFlag = michael@0: siFlags.DontEnumStaticProps() ? 0 : JSPROP_ENUMERATE; michael@0: michael@0: XPCWrappedNative* wrapperForInterfaceNames = michael@0: siFlags.DontReflectInterfaceNames() ? nullptr : wrapper; michael@0: michael@0: bool resolved; michael@0: oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper); michael@0: retval = DefinePropertyIfFound(ccx, obj, id, michael@0: set, iface, member, michael@0: wrapper->GetScope(), michael@0: false, michael@0: wrapperForInterfaceNames, michael@0: nullptr, si, michael@0: enumFlag, &resolved); michael@0: (void)ccx.SetResolvingWrapper(oldResolvingWrapper); michael@0: if (retval && resolved) michael@0: objp.set(obj); michael@0: } michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: /* michael@0: Here are the enumerator cases: michael@0: michael@0: set jsclass enumerate to stub (unless noted otherwise) michael@0: michael@0: if ( helper wants new enumerate ) michael@0: if ( DONT_ENUM_STATICS ) michael@0: forward to scriptable enumerate michael@0: else michael@0: if ( set not mutated ) michael@0: forward to scriptable enumerate michael@0: else michael@0: call shared enumerate michael@0: forward to scriptable enumerate michael@0: else if ( helper wants old enumerate ) michael@0: use this JSOp michael@0: if ( DONT_ENUM_STATICS ) michael@0: call scriptable enumerate michael@0: call stub michael@0: else michael@0: if ( set not mutated ) michael@0: call scriptable enumerate michael@0: call stub michael@0: else michael@0: call shared enumerate michael@0: call scriptable enumerate michael@0: call stub michael@0: michael@0: else //... if ( helper wants NO enumerate ) michael@0: if ( DONT_ENUM_STATICS ) michael@0: use enumerate stub - don't use this JSOp thing at all michael@0: else michael@0: do shared enumerate - don't use this JSOp thing at all michael@0: */ michael@0: michael@0: bool michael@0: XPC_WN_JSOp_Enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op, michael@0: MutableHandleValue statep, MutableHandleId idp) michael@0: { michael@0: const js::Class *clazz = js::GetObjectClass(obj); michael@0: if (!IS_WN_CLASS(clazz) || clazz == &XPC_WN_NoHelper_JSClass.base) { michael@0: // obj must be a prototype object or a wrapper w/o a michael@0: // helper. Short circuit this call to the default michael@0: // implementation. michael@0: michael@0: return JS_EnumerateState(cx, obj, enum_op, statep, idp); michael@0: } michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo(); michael@0: if (!si) michael@0: return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); michael@0: michael@0: bool retval = true; michael@0: nsresult rv; michael@0: michael@0: if (si->GetFlags().WantNewEnumerate()) { michael@0: if (((enum_op == JSENUMERATE_INIT && michael@0: !si->GetFlags().DontEnumStaticProps()) || michael@0: enum_op == JSENUMERATE_INIT_ALL) && michael@0: wrapper->HasMutatedSet() && michael@0: !XPC_WN_Shared_Enumerate(cx, obj)) { michael@0: statep.set(JSVAL_NULL); michael@0: return false; michael@0: } michael@0: michael@0: // XXX Might we really need to wrap this call and *also* call michael@0: // js_ObjectOps.enumerate ??? michael@0: michael@0: rv = si->GetCallback()-> michael@0: NewEnumerate(wrapper, cx, obj, enum_op, statep.address(), idp.address(), &retval); michael@0: michael@0: if ((enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL) && michael@0: (NS_FAILED(rv) || !retval)) { michael@0: statep.set(JSVAL_NULL); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return Throw(rv, cx); michael@0: return retval; michael@0: } michael@0: michael@0: if (si->GetFlags().WantEnumerate()) { michael@0: if (enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL) { michael@0: if ((enum_op == JSENUMERATE_INIT_ALL || michael@0: !si->GetFlags().DontEnumStaticProps()) && michael@0: wrapper->HasMutatedSet() && michael@0: !XPC_WN_Shared_Enumerate(cx, obj)) { michael@0: statep.set(JSVAL_NULL); michael@0: return false; michael@0: } michael@0: rv = si->GetCallback()-> michael@0: Enumerate(wrapper, cx, obj, &retval); michael@0: michael@0: if (NS_FAILED(rv) || !retval) michael@0: statep.set(JSVAL_NULL); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return Throw(rv, cx); michael@0: if (!retval) michael@0: return false; michael@0: // Then fall through and call the default implementation... michael@0: } michael@0: } michael@0: michael@0: // else call js_ObjectOps.enumerate... michael@0: michael@0: return JS_EnumerateState(cx, obj, enum_op, statep, idp); michael@0: } michael@0: michael@0: JSObject* michael@0: XPC_WN_JSOp_ThisObject(JSContext *cx, HandleObject obj) michael@0: { michael@0: return JS_ObjectToOuterObject(cx, obj); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: // static michael@0: XPCNativeScriptableInfo* michael@0: XPCNativeScriptableInfo::Construct(const XPCNativeScriptableCreateInfo* sci) michael@0: { michael@0: MOZ_ASSERT(sci, "bad param"); michael@0: MOZ_ASSERT(sci->GetCallback(), "bad param"); michael@0: michael@0: XPCNativeScriptableInfo* newObj = michael@0: new XPCNativeScriptableInfo(sci->GetCallback()); michael@0: if (!newObj) michael@0: return nullptr; michael@0: michael@0: char* name = nullptr; michael@0: if (NS_FAILED(sci->GetCallback()->GetClassName(&name)) || !name) { michael@0: delete newObj; michael@0: return nullptr; michael@0: } michael@0: michael@0: bool success; michael@0: michael@0: XPCJSRuntime* rt = XPCJSRuntime::Get(); michael@0: XPCNativeScriptableSharedMap* map = rt->GetNativeScriptableSharedMap(); michael@0: success = map->GetNewOrUsed(sci->GetFlags(), name, michael@0: sci->GetInterfacesBitmap(), newObj); michael@0: michael@0: if (!success) { michael@0: delete newObj; michael@0: return nullptr; michael@0: } michael@0: michael@0: return newObj; michael@0: } michael@0: michael@0: void michael@0: XPCNativeScriptableShared::PopulateJSClass() michael@0: { michael@0: MOZ_ASSERT(mJSClass.base.name, "bad state!"); michael@0: michael@0: mJSClass.base.flags = WRAPPER_SLOTS | michael@0: JSCLASS_PRIVATE_IS_NSISUPPORTS | michael@0: JSCLASS_NEW_RESOLVE; michael@0: michael@0: if (mFlags.IsGlobalObject()) michael@0: mJSClass.base.flags |= XPCONNECT_GLOBAL_FLAGS; michael@0: michael@0: JSPropertyOp addProperty; michael@0: if (mFlags.WantAddProperty()) michael@0: addProperty = XPC_WN_Helper_AddProperty; michael@0: else if (mFlags.UseJSStubForAddProperty()) michael@0: addProperty = JS_PropertyStub; michael@0: else if (mFlags.AllowPropModsDuringResolve()) michael@0: addProperty = XPC_WN_MaybeResolvingPropertyStub; michael@0: else michael@0: addProperty = XPC_WN_CannotModifyPropertyStub; michael@0: mJSClass.base.addProperty = addProperty; michael@0: michael@0: JSDeletePropertyOp delProperty; michael@0: if (mFlags.WantDelProperty()) michael@0: delProperty = XPC_WN_Helper_DelProperty; michael@0: else if (mFlags.UseJSStubForDelProperty()) michael@0: delProperty = JS_DeletePropertyStub; michael@0: else if (mFlags.AllowPropModsDuringResolve()) michael@0: delProperty = XPC_WN_MaybeResolvingDeletePropertyStub; michael@0: else michael@0: delProperty = XPC_WN_CantDeletePropertyStub; michael@0: mJSClass.base.delProperty = delProperty; michael@0: michael@0: if (mFlags.WantGetProperty()) michael@0: mJSClass.base.getProperty = XPC_WN_Helper_GetProperty; michael@0: else michael@0: mJSClass.base.getProperty = JS_PropertyStub; michael@0: michael@0: JSStrictPropertyOp setProperty; michael@0: if (mFlags.WantSetProperty()) michael@0: setProperty = XPC_WN_Helper_SetProperty; michael@0: else if (mFlags.UseJSStubForSetProperty()) michael@0: setProperty = JS_StrictPropertyStub; michael@0: else if (mFlags.AllowPropModsDuringResolve()) michael@0: setProperty = XPC_WN_MaybeResolvingStrictPropertyStub; michael@0: else michael@0: setProperty = XPC_WN_CannotModifyStrictPropertyStub; michael@0: mJSClass.base.setProperty = setProperty; michael@0: michael@0: // We figure out most of the enumerate strategy at call time. michael@0: michael@0: if (mFlags.WantNewEnumerate() || mFlags.WantEnumerate() || michael@0: mFlags.DontEnumStaticProps()) michael@0: mJSClass.base.enumerate = JS_EnumerateStub; michael@0: else michael@0: mJSClass.base.enumerate = XPC_WN_Shared_Enumerate; michael@0: michael@0: // We have to figure out resolve strategy at call time michael@0: mJSClass.base.resolve = (JSResolveOp) XPC_WN_Helper_NewResolve; michael@0: michael@0: // We need to respect content-defined toString() hooks on Window objects. michael@0: // In particular, js::DefaultValue checks for a convert stub, and the one michael@0: // we would install below ignores anything implemented in JS. michael@0: // michael@0: // We've always had this behavior for most XPCWrappedNative-implemented michael@0: // objects. However, Window was special, because the outer-window proxy michael@0: // had a null convert hook, which means that we'd end up with the default michael@0: // JS-engine behavior (which respects toString() overrides). We've fixed michael@0: // the convert hook on the outer-window proxy to invoke the defaultValue michael@0: // hook on the proxy, which in this case invokes js::DefaultValue on the michael@0: // target. So now we need to special-case this for Window to maintain michael@0: // consistent behavior. This can go away once Window is on WebIDL bindings. michael@0: // michael@0: // Note that WantOuterObject() is true if and only if this is a Window object. michael@0: if (mFlags.WantConvert()) michael@0: mJSClass.base.convert = XPC_WN_Helper_Convert; michael@0: else if (mFlags.WantOuterObject()) michael@0: mJSClass.base.convert = JS_ConvertStub; michael@0: else michael@0: mJSClass.base.convert = XPC_WN_Shared_Convert; michael@0: michael@0: if (mFlags.WantFinalize()) michael@0: mJSClass.base.finalize = XPC_WN_Helper_Finalize; michael@0: else michael@0: mJSClass.base.finalize = XPC_WN_NoHelper_Finalize; michael@0: michael@0: js::ObjectOps *ops = &mJSClass.base.ops; michael@0: ops->enumerate = XPC_WN_JSOp_Enumerate; michael@0: ops->thisObject = XPC_WN_JSOp_ThisObject; michael@0: michael@0: michael@0: if (mFlags.WantCall()) michael@0: mJSClass.base.call = XPC_WN_Helper_Call; michael@0: if (mFlags.WantConstruct()) michael@0: mJSClass.base.construct = XPC_WN_Helper_Construct; michael@0: michael@0: if (mFlags.WantHasInstance()) michael@0: mJSClass.base.hasInstance = XPC_WN_Helper_HasInstance; michael@0: michael@0: if (mFlags.IsGlobalObject()) michael@0: mJSClass.base.trace = JS_GlobalObjectTraceHook; michael@0: else michael@0: mJSClass.base.trace = XPCWrappedNative::Trace; michael@0: michael@0: if (mFlags.WantOuterObject()) michael@0: mJSClass.base.ext.outerObject = XPC_WN_OuterObject; michael@0: michael@0: mJSClass.base.ext.isWrappedNative = true; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: /***************************************************************************/ michael@0: michael@0: // Compatibility hack. michael@0: // michael@0: // XPConnect used to do all sorts of funny tricks to find the "correct" michael@0: // |this| object for a given method (often to the detriment of proper michael@0: // call/apply). When these tricks were removed, a fair amount of chrome michael@0: // code broke, because it was relying on being able to grab methods off michael@0: // some XPCOM object (like the nsITelemetry service) and invoke them without michael@0: // a proper |this|. So, if it's quite clear that we're in this situation and michael@0: // about to use a |this| argument that just won't work, fix things up. michael@0: // michael@0: // This hack is only useful for getters/setters if someone sets an XPCOM object michael@0: // as the prototype for a vanilla JS object and expects the XPCOM attributes to michael@0: // work on the derived object, which we really don't want to support. But we michael@0: // handle it anyway, for now, to minimize regression risk on an already-risky michael@0: // landing. michael@0: // michael@0: // This hack is mainly useful for the NoHelper JSClass. We also fix up michael@0: // Components.utils because it implements nsIXPCScriptable (giving it a custom michael@0: // JSClass) but not nsIClassInfo (which would put the methods on a prototype). michael@0: michael@0: #define IS_NOHELPER_CLASS(clasp) (clasp == &XPC_WN_NoHelper_JSClass.base) michael@0: #define IS_CU_CLASS(clasp) (clasp->name[0] == 'n' && !strcmp(clasp->name, "nsXPCComponents_Utils")) michael@0: michael@0: MOZ_ALWAYS_INLINE JSObject* michael@0: FixUpThisIfBroken(JSObject *obj, JSObject *funobj) michael@0: { michael@0: if (funobj) { michael@0: const js::Class *parentClass = js::GetObjectClass(js::GetObjectParent(funobj)); michael@0: if (MOZ_UNLIKELY((IS_NOHELPER_CLASS(parentClass) || IS_CU_CLASS(parentClass)) && michael@0: (js::GetObjectClass(obj) != parentClass))) michael@0: { michael@0: return js::GetObjectParent(funobj); michael@0: } michael@0: } michael@0: return obj; michael@0: } michael@0: michael@0: bool michael@0: XPC_WN_CallMethod(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: JS::CallArgs args = JS::CallArgsFromVp(argc, vp); michael@0: MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function"); michael@0: RootedObject funobj(cx, &args.callee()); michael@0: michael@0: RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: obj = FixUpThisIfBroken(obj, funobj); michael@0: XPCCallContext ccx(JS_CALLER, cx, obj, funobj, JSID_VOIDHANDLE, args.length(), michael@0: args.array(), vp); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: XPCNativeInterface* iface; michael@0: XPCNativeMember* member; michael@0: michael@0: if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member)) michael@0: return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx); michael@0: ccx.SetCallInfo(iface, member, false); michael@0: return XPCWrappedNative::CallMethod(ccx); michael@0: } michael@0: michael@0: bool michael@0: XPC_WN_GetterSetter(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: JS::CallArgs args = JS::CallArgsFromVp(argc, vp); michael@0: MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function"); michael@0: RootedObject funobj(cx, &args.callee()); michael@0: michael@0: RootedObject obj(cx, JS_THIS_OBJECT(cx, vp)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: obj = FixUpThisIfBroken(obj, funobj); michael@0: XPCCallContext ccx(JS_CALLER, cx, obj, funobj, JSID_VOIDHANDLE, args.length(), michael@0: args.array(), vp); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: XPCNativeInterface* iface; michael@0: XPCNativeMember* member; michael@0: michael@0: if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member)) michael@0: return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx); michael@0: michael@0: if (args.length() != 0 && member->IsWritableAttribute()) { michael@0: ccx.SetCallInfo(iface, member, true); michael@0: bool retval = XPCWrappedNative::SetAttribute(ccx); michael@0: if (retval) michael@0: args.rval().set(args[0]); michael@0: return retval; michael@0: } michael@0: // else... michael@0: michael@0: ccx.SetCallInfo(iface, member, false); michael@0: return XPCWrappedNative::GetAttribute(ccx); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: static bool michael@0: XPC_WN_Shared_Proto_Enumerate(JSContext *cx, HandleObject obj) michael@0: { michael@0: MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass || michael@0: js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass || michael@0: js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass || michael@0: js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass, michael@0: "bad proto"); michael@0: XPCWrappedNativeProto* self = michael@0: (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj); michael@0: if (!self) michael@0: return false; michael@0: michael@0: if (self->GetScriptableInfo() && michael@0: self->GetScriptableInfo()->GetFlags().DontEnumStaticProps()) michael@0: return true; michael@0: michael@0: XPCNativeSet* set = self->GetSet(); michael@0: if (!set) michael@0: return false; michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx); michael@0: if (!ccx.IsValid()) michael@0: return false; michael@0: michael@0: uint16_t interface_count = set->GetInterfaceCount(); michael@0: XPCNativeInterface** interfaceArray = set->GetInterfaceArray(); michael@0: for (uint16_t i = 0; i < interface_count; i++) { michael@0: XPCNativeInterface* iface = interfaceArray[i]; michael@0: uint16_t member_count = iface->GetMemberCount(); michael@0: michael@0: for (uint16_t k = 0; k < member_count; k++) { michael@0: if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName())) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: XPC_WN_Shared_Proto_Finalize(js::FreeOp *fop, JSObject *obj) michael@0: { michael@0: // This can be null if xpc shutdown has already happened michael@0: XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj); michael@0: if (p) michael@0: p->JSProtoObjectFinalized(fop, obj); michael@0: } michael@0: michael@0: static void michael@0: XPC_WN_Shared_Proto_Trace(JSTracer *trc, JSObject *obj) michael@0: { michael@0: // This can be null if xpc shutdown has already happened michael@0: XPCWrappedNativeProto* p = michael@0: (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj); michael@0: if (p) michael@0: p->TraceInside(trc); michael@0: } michael@0: michael@0: /*****************************************************/ michael@0: michael@0: static bool michael@0: XPC_WN_ModsAllowed_Proto_Resolve(JSContext *cx, HandleObject obj, HandleId id) michael@0: { michael@0: MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass || michael@0: js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass, michael@0: "bad proto"); michael@0: michael@0: XPCWrappedNativeProto* self = michael@0: (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj); michael@0: if (!self) michael@0: return false; michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx); michael@0: if (!ccx.IsValid()) michael@0: return false; michael@0: michael@0: XPCNativeScriptableInfo* si = self->GetScriptableInfo(); michael@0: unsigned enumFlag = (si && si->GetFlags().DontEnumStaticProps()) ? michael@0: 0 : JSPROP_ENUMERATE; michael@0: michael@0: return DefinePropertyIfFound(ccx, obj, id, michael@0: self->GetSet(), nullptr, nullptr, michael@0: self->GetScope(), michael@0: true, nullptr, nullptr, si, michael@0: enumFlag, nullptr); michael@0: } michael@0: michael@0: const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass = { michael@0: "XPC_WN_ModsAllowed_WithCall_Proto_JSClass", // name; michael@0: WRAPPER_SLOTS, // flags; michael@0: michael@0: /* Mandatory non-null function pointer members. */ michael@0: JS_PropertyStub, // addProperty; michael@0: JS_DeletePropertyStub, // delProperty; michael@0: JS_PropertyStub, // getProperty; michael@0: JS_StrictPropertyStub, // setProperty; michael@0: XPC_WN_Shared_Proto_Enumerate, // enumerate; michael@0: XPC_WN_ModsAllowed_Proto_Resolve, // resolve; michael@0: JS_ConvertStub, // convert; michael@0: XPC_WN_Shared_Proto_Finalize, // finalize; michael@0: michael@0: /* Optionally non-null members start here. */ michael@0: nullptr, // call; michael@0: nullptr, // construct; michael@0: nullptr, // hasInstance; michael@0: XPC_WN_Shared_Proto_Trace, // trace; michael@0: michael@0: JS_NULL_CLASS_SPEC, michael@0: JS_NULL_CLASS_EXT, michael@0: XPC_WN_WithCall_ObjectOps michael@0: }; michael@0: michael@0: const js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass = { michael@0: "XPC_WN_ModsAllowed_NoCall_Proto_JSClass", // name; michael@0: WRAPPER_SLOTS, // flags; michael@0: michael@0: /* Mandatory non-null function pointer members. */ michael@0: JS_PropertyStub, // addProperty; michael@0: JS_DeletePropertyStub, // delProperty; michael@0: JS_PropertyStub, // getProperty; michael@0: JS_StrictPropertyStub, // setProperty; michael@0: XPC_WN_Shared_Proto_Enumerate, // enumerate; michael@0: XPC_WN_ModsAllowed_Proto_Resolve, // resolve; michael@0: JS_ConvertStub, // convert; michael@0: XPC_WN_Shared_Proto_Finalize, // finalize; michael@0: michael@0: /* Optionally non-null members start here. */ michael@0: nullptr, // call; michael@0: nullptr, // construct; michael@0: nullptr, // hasInstance; michael@0: XPC_WN_Shared_Proto_Trace, // trace; michael@0: michael@0: JS_NULL_CLASS_SPEC, michael@0: JS_NULL_CLASS_EXT, michael@0: XPC_WN_NoCall_ObjectOps michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: static bool michael@0: XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext *cx, HandleObject obj, HandleId id, michael@0: MutableHandleValue vp) michael@0: { michael@0: MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass || michael@0: js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass, michael@0: "bad proto"); michael@0: michael@0: XPCWrappedNativeProto* self = michael@0: (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj); michael@0: if (!self) michael@0: return false; michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx); michael@0: if (!ccx.IsValid()) michael@0: return false; michael@0: michael@0: // Allow XPConnect to add the property only michael@0: if (ccx.GetResolveName() == id) michael@0: return true; michael@0: michael@0: return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_OnlyIWrite_Proto_SetPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict, michael@0: MutableHandleValue vp) michael@0: { michael@0: return XPC_WN_OnlyIWrite_Proto_AddPropertyStub(cx, obj, id, vp); michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_NoMods_Proto_Resolve(JSContext *cx, HandleObject obj, HandleId id) michael@0: { michael@0: MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass || michael@0: js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass, michael@0: "bad proto"); michael@0: michael@0: XPCWrappedNativeProto* self = michael@0: (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj); michael@0: if (!self) michael@0: return false; michael@0: michael@0: XPCCallContext ccx(JS_CALLER, cx); michael@0: if (!ccx.IsValid()) michael@0: return false; michael@0: michael@0: XPCNativeScriptableInfo* si = self->GetScriptableInfo(); michael@0: unsigned enumFlag = (si && si->GetFlags().DontEnumStaticProps()) ? michael@0: 0 : JSPROP_ENUMERATE; michael@0: michael@0: return DefinePropertyIfFound(ccx, obj, id, michael@0: self->GetSet(), nullptr, nullptr, michael@0: self->GetScope(), michael@0: true, nullptr, nullptr, si, michael@0: JSPROP_READONLY | michael@0: JSPROP_PERMANENT | michael@0: enumFlag, nullptr); michael@0: } michael@0: michael@0: const js::Class XPC_WN_NoMods_WithCall_Proto_JSClass = { michael@0: "XPC_WN_NoMods_WithCall_Proto_JSClass", // name; michael@0: WRAPPER_SLOTS, // flags; michael@0: michael@0: /* Mandatory non-null function pointer members. */ michael@0: XPC_WN_OnlyIWrite_Proto_AddPropertyStub, // addProperty; michael@0: XPC_WN_CantDeletePropertyStub, // delProperty; michael@0: JS_PropertyStub, // getProperty; michael@0: XPC_WN_OnlyIWrite_Proto_SetPropertyStub, // setProperty; michael@0: XPC_WN_Shared_Proto_Enumerate, // enumerate; michael@0: XPC_WN_NoMods_Proto_Resolve, // resolve; michael@0: JS_ConvertStub, // convert; michael@0: XPC_WN_Shared_Proto_Finalize, // finalize; michael@0: michael@0: /* Optionally non-null members start here. */ michael@0: nullptr, // call; michael@0: nullptr, // construct; michael@0: nullptr, // hasInstance; michael@0: XPC_WN_Shared_Proto_Trace, // trace; michael@0: michael@0: JS_NULL_CLASS_SPEC, michael@0: JS_NULL_CLASS_EXT, michael@0: XPC_WN_WithCall_ObjectOps michael@0: }; michael@0: michael@0: const js::Class XPC_WN_NoMods_NoCall_Proto_JSClass = { michael@0: "XPC_WN_NoMods_NoCall_Proto_JSClass", // name; michael@0: WRAPPER_SLOTS, // flags; michael@0: michael@0: /* Mandatory non-null function pointer members. */ michael@0: XPC_WN_OnlyIWrite_Proto_AddPropertyStub, // addProperty; michael@0: XPC_WN_CantDeletePropertyStub, // delProperty; michael@0: JS_PropertyStub, // getProperty; michael@0: XPC_WN_OnlyIWrite_Proto_SetPropertyStub, // setProperty; michael@0: XPC_WN_Shared_Proto_Enumerate, // enumerate; michael@0: XPC_WN_NoMods_Proto_Resolve, // resolve; michael@0: JS_ConvertStub, // convert; michael@0: XPC_WN_Shared_Proto_Finalize, // finalize; michael@0: michael@0: /* Optionally non-null members start here. */ michael@0: nullptr, // call; michael@0: nullptr, // construct; michael@0: nullptr, // hasInstance; michael@0: XPC_WN_Shared_Proto_Trace, // trace; michael@0: michael@0: JS_NULL_CLASS_SPEC, michael@0: JS_NULL_CLASS_EXT, michael@0: XPC_WN_NoCall_ObjectOps michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: static bool michael@0: XPC_WN_TearOff_Enumerate(JSContext *cx, HandleObject obj) michael@0: { michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: XPCWrappedNativeTearOff* to = ccx.GetTearOff(); michael@0: XPCNativeInterface* iface; michael@0: michael@0: if (!to || nullptr == (iface = to->GetInterface())) michael@0: return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); michael@0: michael@0: uint16_t member_count = iface->GetMemberCount(); michael@0: for (uint16_t k = 0; k < member_count; k++) { michael@0: if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName())) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: XPC_WN_TearOff_Resolve(JSContext *cx, HandleObject obj, HandleId id) michael@0: { michael@0: XPCCallContext ccx(JS_CALLER, cx, obj); michael@0: XPCWrappedNative* wrapper = ccx.GetWrapper(); michael@0: THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); michael@0: michael@0: XPCWrappedNativeTearOff* to = ccx.GetTearOff(); michael@0: XPCNativeInterface* iface; michael@0: michael@0: if (!to || nullptr == (iface = to->GetInterface())) michael@0: return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); michael@0: michael@0: return DefinePropertyIfFound(ccx, obj, id, nullptr, iface, nullptr, michael@0: wrapper->GetScope(), michael@0: true, nullptr, nullptr, nullptr, michael@0: JSPROP_READONLY | michael@0: JSPROP_PERMANENT | michael@0: JSPROP_ENUMERATE, nullptr); michael@0: } michael@0: michael@0: static void michael@0: XPC_WN_TearOff_Finalize(js::FreeOp *fop, JSObject *obj) michael@0: { michael@0: XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*) michael@0: xpc_GetJSPrivate(obj); michael@0: if (!p) michael@0: return; michael@0: p->JSObjectFinalized(); michael@0: } michael@0: michael@0: const js::Class XPC_WN_Tearoff_JSClass = { michael@0: "WrappedNative_TearOff", // name; michael@0: WRAPPER_SLOTS, // flags; michael@0: michael@0: XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty; michael@0: XPC_WN_CantDeletePropertyStub, // delProperty; michael@0: JS_PropertyStub, // getProperty; michael@0: XPC_WN_OnlyIWrite_SetPropertyStub, // setProperty; michael@0: XPC_WN_TearOff_Enumerate, // enumerate; michael@0: XPC_WN_TearOff_Resolve, // resolve; michael@0: XPC_WN_Shared_Convert, // convert; michael@0: XPC_WN_TearOff_Finalize // finalize; michael@0: };