Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 <typename T>
struct RemoveAlreadyAddRefed {
using Type = T;
};
template <typename T>
struct RemoveAlreadyAddRefed<already_AddRefed<T>> {
using Type = T;
};
} // anonymous namespace
uint8_t gInvalidContracts[kContractCount / 8 + 1];
static StaticRefPtr<nsISupports> 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 <ComponentType type>
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<JSObject*> exports(cx);
if constexpr (type == ComponentType::JSM) {
JS::Rooted<JSObject*> global(cx);
MOZ_TRY(mozJSModuleLoader::Get()->Import(cx, aURI, &global, &exports));
} else {
MOZ_TRY(mozJSModuleLoader::Get()->ImportESModule(cx, aURI, &exports));
}
JS::Rooted<JS::Value> ctor(cx);
if (!JS_GetProperty(cx, exports, aConstructor, &ctor) ||
!ctor.isObject()) {
return NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
}
JS::Rooted<JSObject*> 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<ComponentType::JSM>(
aURI, aConstructor, aResult);
}
static nsresult ConstructESModuleComponent(const nsACString& aURI,
const char* aConstructor,
nsISupports** aResult) {
return ConstructJSMOrESMComponent<ComponentType::ESM>(
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<nsIFactory> 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<nsISupports> 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<nsIUTF8StringEnumerator>
StaticComponents::GetComponentJSMs() {
#ifdef HAS_COMPONENT_JSMS
auto jsms = MakeUnique<nsTArray<nsCString>>(MOZ_ARRAY_LENGTH(gComponentJSMs));
for (const auto& entry : gComponentJSMs) {
jsms->AppendElement(GetString(entry));
}
#else
auto jsms = MakeUnique<nsTArray<nsCString>>(0);
#endif
nsCOMPtr<nsIUTF8StringEnumerator> result;
MOZ_ALWAYS_SUCCEEDS(NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(result),
jsms.release()));
return result.forget();
}
/* static */ already_AddRefed<nsIUTF8StringEnumerator>
StaticComponents::GetComponentESModules() {
auto esModules = MakeUnique<nsTArray<nsCString>>(MOZ_ARRAY_LENGTH(gComponentESModules));
for (const auto& entry : gComponentESModules) {
esModules->AppendElement(GetString(entry));
}
nsCOMPtr<nsIUTF8StringEnumerator> result;
MOZ_ALWAYS_SUCCEEDS(NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(result),
esModules.release()));
return result.forget();
}
/* static */ Span<const JSServiceEntry> 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