/* PRBool resolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval id); */ NS_IMETHODIMP nsJSIID::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, jsid id, PRUint32 flags, JSObject * *objp, PRBool *_retval) { XPCCallContext ccx(JS_CALLER, cx); AutoMarkingNativeInterfacePtr iface(ccx); const nsIID* iid; mInfo->GetIIDShared(&iid); iface = XPCNativeInterface::GetNewOrUsed(ccx, iid); if(!iface) return NS_OK; XPCNativeMember* member = iface->FindMember(id); if(member && member->IsConstant()) { jsval val; if(!member->GetConstantValue(ccx, iface, &val)) return NS_ERROR_OUT_OF_MEMORY; *objp = obj; *_retval = JS_DefinePropertyById(cx, obj, id, val, nsnull, nsnull, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); } return NS_OK; }
/* PRBool enumerate (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj); */ NS_IMETHODIMP nsJSIID::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, PRBool *_retval) { // In this case, let's just eagerly resolve... XPCCallContext ccx(JS_CALLER, cx); AutoMarkingNativeInterfacePtr iface(ccx); const nsIID* iid; mInfo->GetIIDShared(&iid); iface = XPCNativeInterface::GetNewOrUsed(ccx, iid); if(!iface) return NS_OK; PRUint16 count = iface->GetMemberCount(); for(PRUint16 i = 0; i < count; i++) { XPCNativeMember* member = iface->GetMemberAt(i); if(member && member->IsConstant() && !xpc_ForcePropertyResolve(cx, obj, member->GetName())) { return NS_ERROR_UNEXPECTED; } } return NS_OK; }
NS_IMETHODIMP nsJSIID::Enumerate(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * objArg, bool* _retval) { // In this case, let's just eagerly resolve... RootedObject obj(cx, objArg); XPCCallContext ccx(cx); RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(mInfo); if (!iface) return NS_OK; uint16_t count = iface->GetMemberCount(); for (uint16_t i = 0; i < count; i++) { XPCNativeMember* member = iface->GetMemberAt(i); if (member && member->IsConstant() && !xpc_ForcePropertyResolve(cx, obj, member->GetName())) { return NS_ERROR_UNEXPECTED; } } return NS_OK; }
NS_IMETHODIMP nsJSIID::Resolve(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * objArg, jsid idArg, bool* resolvedp, bool* _retval) { RootedObject obj(cx, objArg); RootedId id(cx, idArg); XPCCallContext ccx(JS_CALLER, cx); AutoMarkingNativeInterfacePtr iface(ccx); iface = XPCNativeInterface::GetNewOrUsed(mInfo); if (!iface) return NS_OK; XPCNativeMember* member = iface->FindMember(id); if (member && member->IsConstant()) { RootedValue val(cx); if (!member->GetConstantValue(ccx, iface, val.address())) return NS_ERROR_OUT_OF_MEMORY; *resolvedp = true; *_retval = JS_DefinePropertyById(cx, obj, id, val, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING); } return NS_OK; }
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(cx, obj, funobj, JSID_VOIDHANDLE, args.length(), args.array(), vp); XPCWrappedNative* wrapper = ccx.GetWrapper(); THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); RefPtr<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); }
XPCWrappedNative* XPCCallContext::UnwrapThisIfAllowed(HandleObject obj, HandleObject fun, unsigned argc) { // We should only get here for objects that aren't safe to unwrap. MOZ_ASSERT(!js::CheckedUnwrap(obj)); MOZ_ASSERT(js::IsObjectInContextCompartment(obj, mJSContext)); // We can't do anything here without a function. if (!fun) return nullptr; // Determine if we're allowed to unwrap the security wrapper to invoke the // method. // // We have the Interface and Member that this corresponds to, but // unfortunately our access checks are based on the object class name and // property name. So we cheat a little bit here - we verify that the object // does indeed implement the method's Interface, and then just check that we // can successfully access property with method's name from the object. // First, get the XPCWN out of the underlying object. We should have a wrapper // here, potentially an outer window proxy, and then an XPCWN. MOZ_ASSERT(js::IsWrapper(obj)); RootedObject unwrapped(mJSContext, js::UncheckedUnwrap(obj, /* stopAtOuter = */ false)); #ifdef DEBUG JS::Rooted<JSObject*> wrappedObj(mJSContext, js::Wrapper::wrappedObject(obj)); MOZ_ASSERT(unwrapped == JS_ObjectToInnerObject(mJSContext, wrappedObj)); #endif // Make sure we have an XPCWN, and grab it. if (!IS_WN_REFLECTOR(unwrapped)) return nullptr; XPCWrappedNative *wn = XPCWrappedNative::Get(unwrapped); // Next, get the call info off the function object. XPCNativeInterface *interface; XPCNativeMember *member; XPCNativeMember::GetCallInfo(fun, &interface, &member); // To be extra safe, make sure that the underlying native implements the // interface before unwrapping. Even if we didn't check this, we'd still // theoretically fail during tearoff lookup for mismatched methods. if (!wn->HasInterfaceNoQI(*interface->GetIID())) return nullptr; // See if the access is permitted. // // NB: This calculation of SET vs GET is a bit wonky, but that's what // XPC_WN_GetterSetter does. bool set = argc && argc != NO_ARGS && member->IsWritableAttribute(); js::Wrapper::Action act = set ? js::Wrapper::SET : js::Wrapper::GET; const js::Wrapper *handler = js::Wrapper::wrapperHandler(obj); bool ignored; JS::Rooted<jsid> id(mJSContext, member->GetName()); if (!handler->enter(mJSContext, obj, id, act, &ignored)) return nullptr; // Ok, this call is safe. return wn; }
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(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.GetContext()->GetStringID(XPCJSContext::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); }
bool XPC_WN_Shared_Enumerate(JSContext* cx, HandleObject obj) { XPCCallContext ccx(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; }
static bool ResolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid id, bool set, JSPropertyDescriptor *desc) { desc->obj = NULL; NS_ASSERTION(holder->getJSClass() == &HolderClass, "expected a native property holder object"); JSObject *wnObject = GetWrappedNativeObjectFromHolder(holder); XPCWrappedNative *wn = GetWrappedNative(wnObject); // This will do verification and the method lookup for us. XPCCallContext ccx(JS_CALLER, cx, wnObject, nsnull, id); // There are no native numeric properties, so we can shortcut here. We will not // find the property. if (!JSID_IS_ATOM(id)) { /* Not found */ return true; } XPCNativeInterface *iface; XPCNativeMember *member; if (ccx.GetWrapper() != wn || !wn->IsValid() || !(iface = ccx.GetInterface()) || !(member = ccx.GetMember())) { /* Not found */ return true; } desc->obj = holder; desc->attrs = JSPROP_ENUMERATE; desc->getter = NULL; desc->setter = NULL; desc->shortid = 0; desc->value = JSVAL_VOID; jsval fval = JSVAL_VOID; if (member->IsConstant()) { if (!member->GetConstantValue(ccx, iface, &desc->value)) { JS_ReportError(cx, "Failed to convert constant native property to JS value"); return false; } } else if (member->IsAttribute()) { // This is a getter/setter. Clone a function for it. if (!member->NewFunctionObject(ccx, iface, wrapper, &fval)) { JS_ReportError(cx, "Failed to clone function object for native getter/setter"); return false; } desc->attrs |= JSPROP_GETTER; if (member->IsWritableAttribute()) desc->attrs |= JSPROP_SETTER; // Make the property shared on the holder so no slot is allocated // for it. This avoids keeping garbage alive through that slot. desc->attrs |= JSPROP_SHARED; } else { // This is a method. Clone a function for it. if (!member->NewFunctionObject(ccx, iface, wrapper, &desc->value)) { JS_ReportError(cx, "Failed to clone function object for native function"); return false; } // Without a wrapper the function would live on the prototype. Since we // don't have one, we have to avoid calling the scriptable helper's // GetProperty method for this property, so stub out the getter and // setter here explicitly. desc->getter = JS_PropertyStub; desc->setter = JS_StrictPropertyStub; } if (!JS_WrapValue(cx, &desc->value) || !JS_WrapValue(cx, &fval)) return false; if (desc->attrs & JSPROP_GETTER) desc->getter = CastAsJSPropertyOp(JSVAL_TO_OBJECT(fval)); if (desc->attrs & JSPROP_SETTER) desc->setter = CastAsJSStrictPropertyOp(JSVAL_TO_OBJECT(fval)); // Define the property. return JS_DefinePropertyById(cx, holder, id, desc->value, desc->getter, desc->setter, desc->attrs); }
// static XPCNativeInterface* XPCNativeInterface::NewInstance(XPCCallContext& ccx, nsIInterfaceInfo* aInfo) { static const PRUint16 MAX_LOCAL_MEMBER_COUNT = 16; XPCNativeMember local_members[MAX_LOCAL_MEMBER_COUNT]; XPCNativeInterface* obj = nsnull; XPCNativeMember* members = nsnull; int i; JSBool failed = false; PRUint16 constCount; PRUint16 methodCount; PRUint16 totalCount; PRUint16 realTotalCount = 0; XPCNativeMember* cur; JSString* str = NULL; jsid name; jsid interfaceName; // XXX Investigate lazy init? This is a problem given the // 'placement new' scheme - we need to at least know how big to make // the object. We might do a scan of methods to determine needed size, // then make our object, but avoid init'ing *any* members until asked? // Find out how often we create these objects w/o really looking at // (or using) the members. bool canScript; if (NS_FAILED(aInfo->IsScriptable(&canScript)) || !canScript) return nsnull; if (NS_FAILED(aInfo->GetMethodCount(&methodCount)) || NS_FAILED(aInfo->GetConstantCount(&constCount))) return nsnull; // If the interface does not have nsISupports in its inheritance chain // then we know we can't reflect its methods. However, some interfaces that // are used just to reflect constants are declared this way. We need to // go ahead and build the thing. But, we'll ignore whatever methods it may // have. if (!nsXPConnect::IsISupportsDescendant(aInfo)) methodCount = 0; totalCount = methodCount + constCount; if (totalCount > MAX_LOCAL_MEMBER_COUNT) { members = new XPCNativeMember[totalCount]; if (!members) return nsnull; } else { members = local_members; } // NOTE: since getters and setters share a member, we might not use all // of the member objects. for (i = 0; i < methodCount; i++) { const nsXPTMethodInfo* info; if (NS_FAILED(aInfo->GetMethodInfo(i, &info))) { failed = true; break; } // don't reflect Addref or Release if (i == 1 || i == 2) continue; if (!XPCConvert::IsMethodReflectable(*info)) continue; str = JS_InternString(ccx, info->GetName()); if (!str) { NS_ERROR("bad method name"); failed = true; break; } name = INTERNED_STRING_TO_JSID(ccx, str); if (info->IsSetter()) { NS_ASSERTION(realTotalCount,"bad setter"); // Note: ASSUMES Getter/Setter pairs are next to each other // This is a rule of the typelib spec. cur = &members[realTotalCount-1]; NS_ASSERTION(cur->GetName() == name,"bad setter"); NS_ASSERTION(cur->IsReadOnlyAttribute(),"bad setter"); NS_ASSERTION(cur->GetIndex() == i-1,"bad setter"); cur->SetWritableAttribute(); } else { // XXX need better way to find dups // NS_ASSERTION(!LookupMemberByID(name),"duplicate method name"); cur = &members[realTotalCount++]; cur->SetName(name); if (info->IsGetter()) cur->SetReadOnlyAttribute(i); else cur->SetMethod(i); } } if (!failed) { for (i = 0; i < constCount; i++) { const nsXPTConstant* constant; if (NS_FAILED(aInfo->GetConstant(i, &constant))) { failed = true; break; } str = JS_InternString(ccx, constant->GetName()); if (!str) { NS_ERROR("bad constant name"); failed = true; break; } name = INTERNED_STRING_TO_JSID(ccx, str); // XXX need better way to find dups //NS_ASSERTION(!LookupMemberByID(name),"duplicate method/constant name"); cur = &members[realTotalCount++]; cur->SetName(name); cur->SetConstant(i); } } if (!failed) { const char* bytes; if (NS_FAILED(aInfo->GetNameShared(&bytes)) || !bytes || nsnull == (str = JS_InternString(ccx, bytes))) { failed = true; } interfaceName = INTERNED_STRING_TO_JSID(ccx, str); } if (!failed) { // Use placement new to create an object with the right amount of space // to hold the members array int size = sizeof(XPCNativeInterface); if (realTotalCount > 1) size += (realTotalCount - 1) * sizeof(XPCNativeMember); void* place = new char[size]; if (place) obj = new(place) XPCNativeInterface(aInfo, interfaceName); if (obj) { obj->mMemberCount = realTotalCount; // copy valid members if (realTotalCount) memcpy(obj->mMembers, members, realTotalCount * sizeof(XPCNativeMember)); } } if (members && members != local_members) delete [] members; return obj; }
// static XPCNativeInterface* XPCNativeInterface::NewInstance(nsIInterfaceInfo* aInfo) { AutoJSContext cx; static const uint16_t MAX_LOCAL_MEMBER_COUNT = 16; XPCNativeMember local_members[MAX_LOCAL_MEMBER_COUNT]; XPCNativeInterface* obj = nullptr; XPCNativeMember* members = nullptr; int i; bool failed = false; uint16_t constCount; uint16_t methodCount; uint16_t totalCount; uint16_t realTotalCount = 0; XPCNativeMember* cur; RootedString str(cx); RootedId interfaceName(cx); // XXX Investigate lazy init? This is a problem given the // 'placement new' scheme - we need to at least know how big to make // the object. We might do a scan of methods to determine needed size, // then make our object, but avoid init'ing *any* members until asked? // Find out how often we create these objects w/o really looking at // (or using) the members. bool canScript; if (NS_FAILED(aInfo->IsScriptable(&canScript)) || !canScript) return nullptr; bool mainProcessScriptableOnly; if (NS_FAILED(aInfo->IsMainProcessScriptableOnly(&mainProcessScriptableOnly))) return nullptr; if (mainProcessScriptableOnly && !XRE_IsParentProcess()) { nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); if (console) { const char* intfNameChars; aInfo->GetNameShared(&intfNameChars); nsPrintfCString errorMsg("Use of %s in content process is deprecated.", intfNameChars); nsAutoString filename; uint32_t lineno = 0, column = 0; nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column); nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); error->Init(NS_ConvertUTF8toUTF16(errorMsg), filename, EmptyString(), lineno, column, nsIScriptError::warningFlag, "chrome javascript"); console->LogMessage(error); } } if (NS_FAILED(aInfo->GetMethodCount(&methodCount)) || NS_FAILED(aInfo->GetConstantCount(&constCount))) return nullptr; // If the interface does not have nsISupports in its inheritance chain // then we know we can't reflect its methods. However, some interfaces that // are used just to reflect constants are declared this way. We need to // go ahead and build the thing. But, we'll ignore whatever methods it may // have. if (!nsXPConnect::IsISupportsDescendant(aInfo)) methodCount = 0; totalCount = methodCount + constCount; if (totalCount > MAX_LOCAL_MEMBER_COUNT) { members = new XPCNativeMember[totalCount]; if (!members) return nullptr; } else { members = local_members; } // NOTE: since getters and setters share a member, we might not use all // of the member objects. for (i = 0; i < methodCount; i++) { const nsXPTMethodInfo* info; if (NS_FAILED(aInfo->GetMethodInfo(i, &info))) { failed = true; break; } // don't reflect Addref or Release if (i == 1 || i == 2) continue; if (!XPCConvert::IsMethodReflectable(*info)) continue; str = JS_AtomizeAndPinString(cx, info->GetName()); if (!str) { NS_ERROR("bad method name"); failed = true; break; } jsid name = INTERNED_STRING_TO_JSID(cx, str); if (info->IsSetter()) { MOZ_ASSERT(realTotalCount,"bad setter"); // Note: ASSUMES Getter/Setter pairs are next to each other // This is a rule of the typelib spec. cur = &members[realTotalCount-1]; MOZ_ASSERT(cur->GetName() == name,"bad setter"); MOZ_ASSERT(cur->IsReadOnlyAttribute(),"bad setter"); MOZ_ASSERT(cur->GetIndex() == i-1,"bad setter"); cur->SetWritableAttribute(); } else { // XXX need better way to find dups // MOZ_ASSERT(!LookupMemberByID(name),"duplicate method name"); if (realTotalCount == XPCNativeMember::GetMaxIndexInInterface()) { NS_WARNING("Too many members in interface"); failed = true; break; } cur = &members[realTotalCount]; cur->SetName(name); if (info->IsGetter()) cur->SetReadOnlyAttribute(i); else cur->SetMethod(i); cur->SetIndexInInterface(realTotalCount); ++realTotalCount; } } if (!failed) { for (i = 0; i < constCount; i++) { RootedValue constant(cx); nsXPIDLCString namestr; if (NS_FAILED(aInfo->GetConstant(i, &constant, getter_Copies(namestr)))) { failed = true; break; } str = JS_AtomizeAndPinString(cx, namestr); if (!str) { NS_ERROR("bad constant name"); failed = true; break; } jsid name = INTERNED_STRING_TO_JSID(cx, str); // XXX need better way to find dups //MOZ_ASSERT(!LookupMemberByID(name),"duplicate method/constant name"); if (realTotalCount == XPCNativeMember::GetMaxIndexInInterface()) { NS_WARNING("Too many members in interface"); failed = true; break; } cur = &members[realTotalCount]; cur->SetName(name); cur->SetConstant(i); cur->SetIndexInInterface(realTotalCount); ++realTotalCount; } } if (!failed) { const char* bytes; if (NS_FAILED(aInfo->GetNameShared(&bytes)) || !bytes || nullptr == (str = JS_AtomizeAndPinString(cx, bytes))) { failed = true; } interfaceName = INTERNED_STRING_TO_JSID(cx, str); } if (!failed) { // Use placement new to create an object with the right amount of space // to hold the members array int size = sizeof(XPCNativeInterface); if (realTotalCount > 1) size += (realTotalCount - 1) * sizeof(XPCNativeMember); void* place = new char[size]; if (place) obj = new(place) XPCNativeInterface(aInfo, interfaceName); if (obj) { obj->mMemberCount = realTotalCount; // copy valid members if (realTotalCount) memcpy(obj->mMembers, members, realTotalCount * sizeof(XPCNativeMember)); } } if (members && members != local_members) delete [] members; return obj; }