/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "StaticComponents.h" #include "mozilla/ArrayUtils.h" #ifdef MOZ_BACKGROUNDTASKS # include "mozilla/BackgroundTasks.h" #endif #include "mozilla/PerfectHash.h" #include "mozilla/ResultExtensions.h" #include "mozilla/StaticPtr.h" #include "mozilla/UniquePtr.h" #include "mozilla/dom/ScriptSettings.h" #include "mozJSModuleLoader.h" #include "nsCOMPtr.h" #include "nsComponentManager.h" #include "nsContentUtils.h" #include "nsIFactory.h" #include "nsISupports.h" #include "nsIXPConnect.h" #include "nsString.h" #include "nsStringEnumerator.h" #include "nsTArray.h" #include "xptdata.h" #include "xptinfo.h" #include "js/PropertyAndElement.h" // JS_GetProperty // Cleanup pollution from zipstruct.h #undef UNSUPPORTED // Public includes //# @includes@ // Relative includes //# @relative_includes@ //# @decls@ namespace mozilla { using dom::AutoJSAPI; namespace xpcom { static constexpr uint32_t kNoContractID = 0xffffffff; namespace { // Template helpers for constructor function sanity checks. template struct RemoveAlreadyAddRefed { using Type = T; }; template struct RemoveAlreadyAddRefed> { using Type = T; }; } // anonymous namespace uint8_t gInvalidContracts[kContractCount / 8 + 1]; static StaticRefPtr gServiceInstances[kStaticModuleCount]; uint8_t gInitCalled[kModuleInitCount / 8 + 1]; static const char gStrings[] = //# @strings@ ""; const StaticCategory gStaticCategories[kStaticCategoryCount] = { //# @categories@ }; const StaticCategoryEntry gStaticCategoryEntries[] = { //# @category_entries@ }; const nsXPTInterface gInterfaces[] = { //# @interfaces@ }; //# @define_has_component_jsms@ #ifdef HAS_COMPONENT_JSMS // TODO: Remove this once m-c and c-c migrations finish (bug 1881887). const StringOffset gComponentJSMs[] = { //# @component_jsms@ }; #endif const StringOffset gComponentESModules[] = { //# @component_esmodules@ }; /** * Returns a nsCString corresponding to the given entry in the `gStrings` string * table. The resulting nsCString points directly to static storage, and does * not incur any memory allocation overhead. */ static inline nsCString GetString(const StringOffset& aOffset) { const char* str = &gStrings[aOffset.mOffset]; nsCString result; result.AssignLiteral(str, strlen(str)); return result; } nsCString ContractEntry::ContractID() const { return GetString(mContractID); } bool ContractEntry::Matches(const nsACString& aContractID) const { return aContractID == ContractID() && Module().Active(); } enum class ComponentType { JSM, ESM }; template static nsresult ConstructJSMOrESMComponent(const nsACString& aURI, const char* aConstructor, nsISupports** aResult) { if (!nsComponentManagerImpl::JSLoaderReady()) { return NS_ERROR_NOT_AVAILABLE; } AutoJSAPI jsapi; MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope())); JSContext* cx = jsapi.cx(); JS::Rooted exports(cx); if constexpr (type == ComponentType::JSM) { JS::Rooted global(cx); MOZ_TRY(mozJSModuleLoader::Get()->Import(cx, aURI, &global, &exports)); } else { MOZ_TRY(mozJSModuleLoader::Get()->ImportESModule(cx, aURI, &exports)); } JS::Rooted ctor(cx); if (!JS_GetProperty(cx, exports, aConstructor, &ctor) || !ctor.isObject()) { return NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED; } JS::Rooted inst(cx); if (!JS::Construct(cx, ctor, JS::HandleValueArray::empty(), &inst)) { return NS_ERROR_FAILURE; } return nsContentUtils::XPConnect()->WrapJS(cx, inst, NS_GET_IID(nsISupports), (void**)aResult); } [[maybe_unused]] static nsresult ConstructJSMComponent(const nsACString& aURI, const char* aConstructor, nsISupports** aResult) { return ConstructJSMOrESMComponent( aURI, aConstructor, aResult); } static nsresult ConstructESModuleComponent(const nsACString& aURI, const char* aConstructor, nsISupports** aResult) { return ConstructJSMOrESMComponent( aURI, aConstructor, aResult); } //# @module_cid_table@ //# @module_contract_id_table@ //# @js_services_table@ //# @protocol_handlers_table@ static inline bool CalledInit(size_t aIdx) { return GetBit(gInitCalled, aIdx); } static nsresult CallInitFunc(size_t aIdx) { if (CalledInit(aIdx)) { return NS_OK; } nsresult rv = NS_OK; switch (aIdx) { //# @init_funcs@ } SetBit(gInitCalled, aIdx); MOZ_ASSERT(NS_SUCCEEDED(rv)); return rv; } static void CallUnloadFuncs() { //# @unload_funcs@ } nsresult CreateInstanceImpl(ModuleID aID, const nsIID& aIID, void** aResult) { // The full set of constructors for all static modules. // This switch statement will be compiled to a relative address jump table // with no runtime relocations and a single indirect jump. switch (aID) { //# @constructors@ } MOZ_ASSERT_UNREACHABLE("Constructor didn't return"); return NS_ERROR_FAILURE; } namespace { class StaticModuleFactory final : public nsIFactory { NS_DECL_ISUPPORTS NS_DECL_NSIFACTORY explicit StaticModuleFactory(ModuleID aID) : mID(aID) {} private: ~StaticModuleFactory() = default; const ModuleID mID; }; NS_IMPL_ISUPPORTS(StaticModuleFactory, nsIFactory) NS_IMETHODIMP StaticModuleFactory::CreateInstance(const nsIID& aIID, void** aResult) { return CreateInstanceImpl(mID, aIID, aResult); } } // anonymous namespace already_AddRefed StaticModule::GetFactory() const { return do_AddRef(new StaticModuleFactory(ID())); } bool StaticModule::Active() const { return FastProcessSelectorMatches(mProcessSelector); } bool StaticModule::Overridable() const { return mContractID.mOffset != kNoContractID; } nsCString StaticModule::ContractID() const { MOZ_ASSERT(Overridable()); return GetString(mContractID); } nsresult StaticModule::CreateInstance(const nsIID& aIID, void** aResult) const { return CreateInstanceImpl(ID(), aIID, aResult); } GetServiceHelper StaticModule::GetService() const { return { ID(), nullptr }; } GetServiceHelper StaticModule::GetService(nsresult* aRv) const { return { ID(), aRv }; } nsISupports* StaticModule::ServiceInstance() const { return gServiceInstances[Idx()]; } void StaticModule::SetServiceInstance( already_AddRefed aInst) const { gServiceInstances[Idx()] = aInst; } nsCString StaticCategoryEntry::Entry() const { return GetString(mEntry); } nsCString StaticCategoryEntry::Value() const { return GetString(mValue); } bool StaticCategoryEntry::Active() const { if (!FastProcessSelectorMatches(mProcessSelector)) { return false; } #ifdef MOZ_BACKGROUNDTASKS if (MOZ_UNLIKELY(BackgroundTasks::IsBackgroundTaskMode())) { return mBackgroundTasksSelector != Module::BackgroundTasksSelector::NO_TASKS; } #endif /* MOZ_BACKGROUNDTASKS */ return true; } nsCString StaticCategory::Name() const { return GetString(mName); } nsCString JSServiceEntry::Name() const { return GetString(mName); } JSServiceEntry::InterfaceList JSServiceEntry::Interfaces() const { InterfaceList iids; iids.SetCapacity(mInterfaceCount); for (size_t i = 0; i < mInterfaceCount; i++) { nsXPTInterface ifaceID = gInterfaces[mInterfaceOffset.mOffset + i]; iids.AppendElement(&nsXPTInterfaceInfo::Get(ifaceID)->IID()); } return iids; } /* static */ const JSServiceEntry* JSServiceEntry::Lookup(const nsACString& aName) { return LookupJSService(aName); } nsCString StaticProtocolHandler::Scheme() const { return GetString(mScheme); } /* static */ const StaticProtocolHandler* StaticProtocolHandler::Lookup(const nsACString& aScheme) { return LookupProtocolHandler(aScheme); } /* static */ const StaticModule* StaticComponents::LookupByCID( const nsID& aCID) { return ModuleByCID(aCID); } /* static */ const StaticModule* StaticComponents::LookupByContractID( const nsACString& aContractID) { if (const ContractEntry* entry = LookupContractID(aContractID)) { if (!entry->Invalid()) { return &entry->Module(); } } return nullptr; } /* static */ bool StaticComponents::InvalidateContractID( const nsACString& aContractID, bool aInvalid) { if (const ContractEntry* entry = LookupContractID(aContractID)) { entry->SetInvalid(aInvalid); return true; } return false; } /* static */ already_AddRefed StaticComponents::GetComponentJSMs() { #ifdef HAS_COMPONENT_JSMS auto jsms = MakeUnique>(MOZ_ARRAY_LENGTH(gComponentJSMs)); for (const auto& entry : gComponentJSMs) { jsms->AppendElement(GetString(entry)); } #else auto jsms = MakeUnique>(0); #endif nsCOMPtr result; MOZ_ALWAYS_SUCCEEDS(NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(result), jsms.release())); return result.forget(); } /* static */ already_AddRefed StaticComponents::GetComponentESModules() { auto esModules = MakeUnique>(MOZ_ARRAY_LENGTH(gComponentESModules)); for (const auto& entry : gComponentESModules) { esModules->AppendElement(GetString(entry)); } nsCOMPtr result; MOZ_ALWAYS_SUCCEEDS(NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(result), esModules.release())); return result.forget(); } /* static */ Span StaticComponents::GetJSServices() { return { gJSServices, ArrayLength(gJSServices) }; } /* static */ void StaticComponents::Shutdown() { CallUnloadFuncs(); } /* static */ const nsID& Components::GetCID(ModuleID aID) { return gStaticModules[size_t(aID)].CID(); } nsresult GetServiceHelper::operator()(const nsIID& aIID, void** aResult) const { nsresult rv = nsComponentManagerImpl::gComponentManager->GetService(mId, aIID, aResult); return SetResult(rv); } nsresult CreateInstanceHelper::operator()(const nsIID& aIID, void** aResult) const { const auto& entry = gStaticModules[size_t(mId)]; if (!entry.Active()) { return SetResult(NS_ERROR_FACTORY_NOT_REGISTERED); } nsresult rv = entry.CreateInstance(aIID, aResult); return SetResult(rv); } } // namespace xpcom } // namespace mozilla