/* 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; }
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; }
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 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; }