зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1478124: Part 4b - Support loading components from static lookup tables. r=froydnj
This patch essentially creates a separate, static component database for statically-defined CID and contract ID entries, and gives it precedence over the runtime DB. It combines the two separate databases by updating existing code to use lookup functions which understand both databases, and then access all entries through wrappers which defer to the appropriate underlying type. Static component entries require no runtime relocations, and require no writable data allocation aside from one pointer-sized BSS entry per CID, and one bit of BSS per contract ID. To achieve this, all strings in the static lookup tables are stored as indexes into a static string table, all constructor functions live in a switch statement which compiles to a relative jump table, and all writable data for static entries is accessed by indexed lookups into BSS arrays. We also avoid creating nsIFactory entries for static components when possible by adding a CreateInstance method to nsFactoryEntry and the corresponding entry wrapper to directly call the appropriate constructor method, and only create a factory object when required by external code. Differential Revision: https://phabricator.services.mozilla.com/D15035 --HG-- extra : rebase_source : 8d02ff3b67b8078d1ac837d8c12f54786155c6b6 extra : absorb_source : 0fe36ca220c9270e634abf5b1f320a01878e0ce7 extra : histedit_source : 51521ceae2c1b3e4e8bf63d4ed1e2e67e9468780
This commit is contained in:
Родитель
f6ce24cfbc
Коммит
43481fed07
|
@ -127,13 +127,13 @@ class TestPACMan : public ::testing::Test {
|
|||
|
||||
virtual void SetUp() {
|
||||
ASSERT_EQ(NS_OK, GetNetworkProxyType(&originalNetworkProxyTypePref));
|
||||
nsFactoryEntry* factoryEntry =
|
||||
nsComponentManagerImpl::gComponentManager->GetFactoryEntry(
|
||||
kNS_TESTDHCPCLIENTSERVICE_CID);
|
||||
if (factoryEntry) {
|
||||
nsresult rv =
|
||||
nsComponentManagerImpl::gComponentManager->UnregisterFactory(
|
||||
kNS_TESTDHCPCLIENTSERVICE_CID, factoryEntry->mFactory);
|
||||
nsCOMPtr<nsIFactory> factory;
|
||||
nsresult rv = nsComponentManagerImpl::gComponentManager->GetClassObject(
|
||||
kNS_TESTDHCPCLIENTSERVICE_CID, NS_GET_IID(nsIFactory),
|
||||
getter_AddRefs(factory));
|
||||
if (NS_SUCCEEDED(rv) && factory) {
|
||||
rv = nsComponentManagerImpl::gComponentManager->UnregisterFactory(
|
||||
kNS_TESTDHCPCLIENTSERVICE_CID, factory);
|
||||
ASSERT_EQ(NS_OK, rv);
|
||||
}
|
||||
nsComponentManagerImpl::gComponentManager->RegisterModule(
|
||||
|
|
|
@ -36,4 +36,4 @@ FINAL_LIBRARY = 'xul-gtest'
|
|||
LOCAL_INCLUDES += [
|
||||
'!/xpcom',
|
||||
'/xpcom/components'
|
||||
]
|
||||
]
|
||||
|
|
|
@ -0,0 +1,228 @@
|
|||
/* -*- 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/PerfectHash.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsComponentManager.h"
|
||||
#include "nsIFactory.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsString.h"
|
||||
|
||||
// Cleanup pollution from zipstruct.h
|
||||
#undef UNSUPPORTED
|
||||
|
||||
// Public includes
|
||||
//# @includes@
|
||||
|
||||
// Relative includes
|
||||
//# @relative_includes@
|
||||
|
||||
//# @decls@
|
||||
|
||||
namespace mozilla {
|
||||
namespace xpcom {
|
||||
|
||||
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@
|
||||
};
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
|
||||
//# @module_cid_table@
|
||||
|
||||
//# @module_contract_id_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@
|
||||
}
|
||||
|
||||
static nsresult CreateInstanceImpl(ModuleID aID, nsISupports* aOuter,
|
||||
const nsIID& aIID, void** aResult) {
|
||||
if (aOuter) {
|
||||
return NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
|
||||
// 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(nsISupports* aOuter,
|
||||
const nsIID& aIID,
|
||||
void** aResult) {
|
||||
return CreateInstanceImpl(mID, aOuter, aIID, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP StaticModuleFactory::LockFactory(bool aLock) {
|
||||
MOZ_CRASH("LockFactory is no longer a thing");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
already_AddRefed<nsIFactory> StaticModule::GetFactory() const {
|
||||
return do_AddRef(new StaticModuleFactory(ID()));
|
||||
}
|
||||
|
||||
bool StaticModule::Active() const {
|
||||
return FastProcessSelectorMatches(mProcessSelector);
|
||||
}
|
||||
|
||||
nsresult StaticModule::CreateInstance(nsISupports* aOuter, const nsIID& aIID,
|
||||
void** aResult) const {
|
||||
return CreateInstanceImpl(ID(), aOuter, aIID, aResult);
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
return FastProcessSelectorMatches(mProcessSelector);
|
||||
}
|
||||
|
||||
nsCString StaticCategory::Name() const {
|
||||
return GetString(mName);
|
||||
}
|
||||
|
||||
|
||||
/* 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 */ void StaticComponents::Shutdown() {
|
||||
CallUnloadFuncs();
|
||||
}
|
||||
|
||||
} // namespace xpcom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,203 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef StaticComponents_h
|
||||
#define StaticComponents_h
|
||||
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/Module.h"
|
||||
#include "nsID.h"
|
||||
#include "nsStringFwd.h"
|
||||
#include "nscore.h"
|
||||
|
||||
#include "mozilla/Components.h"
|
||||
#include "StaticComponentData.h"
|
||||
|
||||
class nsIFactory;
|
||||
class nsISupports;
|
||||
|
||||
namespace mozilla {
|
||||
namespace xpcom {
|
||||
|
||||
struct ContractEntry;
|
||||
struct StaticModule;
|
||||
|
||||
struct StaticCategoryEntry;
|
||||
struct StaticCategory;
|
||||
|
||||
extern const StaticModule gStaticModules[kStaticModuleCount];
|
||||
|
||||
extern const ContractEntry gContractEntries[kContractCount];
|
||||
extern uint8_t gInvalidContracts[kContractCount / 8 + 1];
|
||||
|
||||
extern const StaticCategory gStaticCategories[kStaticCategoryCount];
|
||||
extern const StaticCategoryEntry gStaticCategoryEntries[];
|
||||
|
||||
template <size_t N>
|
||||
static inline bool GetBit(const uint8_t (&aBits)[N], size_t aBit) {
|
||||
static constexpr size_t width = sizeof(aBits[0]) * 8;
|
||||
|
||||
size_t idx = aBit / width;
|
||||
MOZ_ASSERT(idx < N);
|
||||
return aBits[idx] & (1 << (aBit % width));
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
static inline void SetBit(uint8_t (&aBits)[N], size_t aBit,
|
||||
bool aValue = true) {
|
||||
static constexpr size_t width = sizeof(aBits[0]) * 8;
|
||||
|
||||
size_t idx = aBit / width;
|
||||
MOZ_ASSERT(idx < N);
|
||||
if (aValue) {
|
||||
aBits[idx] |= 1 << (aBit % width);
|
||||
} else {
|
||||
aBits[idx] &= ~(1 << (aBit % width));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a string entry in the static string table. Can be converted to a
|
||||
* nsCString using GetString() in StaticComponents.cpp.
|
||||
*
|
||||
* This is a struct rather than a pure offset primarily for the purposes of type
|
||||
* safety, but also so that it can easily be extended to include a static length
|
||||
* in the future, if efficiency concerns warrant it.
|
||||
*/
|
||||
struct StringOffset final {
|
||||
uint32_t mOffset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a static component entry defined in a `Classes` list in an XPCOM
|
||||
* manifest. Handles creating instances of and caching service instances for
|
||||
* that class.
|
||||
*/
|
||||
struct StaticModule {
|
||||
nsID mCID;
|
||||
Module::ProcessSelector mProcessSelector;
|
||||
|
||||
const nsID& CID() const { return mCID; }
|
||||
|
||||
ModuleID ID() const { return ModuleID(this - gStaticModules); }
|
||||
|
||||
/**
|
||||
* Returns this entry's index in the gStaticModules array.
|
||||
*/
|
||||
size_t Idx() const { return size_t(ID()); }
|
||||
|
||||
/**
|
||||
* Returns true if this entry is active. Typically this will only return false
|
||||
* if the entry's process selector does not match this process.
|
||||
*/
|
||||
bool Active() const;
|
||||
|
||||
already_AddRefed<nsIFactory> GetFactory() const;
|
||||
|
||||
nsresult CreateInstance(nsISupports* aOuter, const nsIID& aIID,
|
||||
void** aResult) const;
|
||||
|
||||
nsISupports* ServiceInstance() const;
|
||||
void SetServiceInstance(already_AddRefed<nsISupports> aInst) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a static mapping between a contract ID string and a StaticModule
|
||||
* entry.
|
||||
*/
|
||||
struct ContractEntry final {
|
||||
StringOffset mContractID;
|
||||
ModuleID mModuleID;
|
||||
|
||||
size_t Idx() const { return this - gContractEntries; }
|
||||
|
||||
nsCString ContractID() const;
|
||||
|
||||
const StaticModule& Module() const {
|
||||
return gStaticModules[size_t(mModuleID)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this entry's underlying module is active, and its contract
|
||||
* ID matches the given contract ID string. This is used by the PerfectHash
|
||||
* function to determine whether to return a result for this entry.
|
||||
*/
|
||||
bool Matches(const nsACString& aContractID) const;
|
||||
|
||||
/**
|
||||
* Returns true if this entry has been invalidated, and should be ignored.
|
||||
*
|
||||
* Contract IDs may be overwritten at runtime. When that happens for a static
|
||||
* contract ID, we mark its entry invalid, and ignore it thereafter.
|
||||
*/
|
||||
bool Invalid() const { return GetBit(gInvalidContracts, Idx()); }
|
||||
|
||||
/**
|
||||
* Marks this entry invalid (or unsets the invalid bit if aInvalid is false),
|
||||
* after which it will be ignored in contract ID lookup attempts. See
|
||||
* `Invalid()` above.
|
||||
*/
|
||||
void SetInvalid(bool aInvalid = true) const {
|
||||
return SetBit(gInvalidContracts, Idx(), aInvalid);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a declared category manager entry declared in an XPCOM manifest.
|
||||
*
|
||||
* The entire set of static category entries is read at startup and loaded into
|
||||
* the category manager's dynamic hash tables, so there is memory and
|
||||
* initialization overhead for each entry in these tables. This may be further
|
||||
* optimized in the future to reduce some of that overhead.
|
||||
*/
|
||||
struct StaticCategoryEntry final {
|
||||
StringOffset mEntry;
|
||||
StringOffset mValue;
|
||||
Module::ProcessSelector mProcessSelector;
|
||||
|
||||
nsCString Entry() const;
|
||||
nsCString Value() const;
|
||||
bool Active() const;
|
||||
};
|
||||
|
||||
struct StaticCategory final {
|
||||
StringOffset mName;
|
||||
uint16_t mStart;
|
||||
uint16_t mCount;
|
||||
|
||||
nsCString Name() const;
|
||||
|
||||
const StaticCategoryEntry* begin() const {
|
||||
return &gStaticCategoryEntries[mStart];
|
||||
}
|
||||
const StaticCategoryEntry* end() const {
|
||||
return &gStaticCategoryEntries[mStart + mCount];
|
||||
}
|
||||
};
|
||||
|
||||
class StaticComponents final {
|
||||
public:
|
||||
static const StaticModule* LookupByCID(const nsID& aCID);
|
||||
|
||||
static const StaticModule* LookupByContractID(const nsACString& aContractID);
|
||||
|
||||
/**
|
||||
* Marks a static contract ID entry invalid (or unsets the invalid bit if
|
||||
* aInvalid is false). See `CategoryEntry::Invalid()`.
|
||||
*/
|
||||
static bool InvalidateContractID(const nsACString& aContractID,
|
||||
bool aInvalid = true);
|
||||
|
||||
/**
|
||||
* Calls any module unload from manifests whose components have been loaded.
|
||||
*/
|
||||
static void Shutdown();
|
||||
};
|
||||
|
||||
} // namespace xpcom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // defined StaticComponents_h
|
|
@ -0,0 +1,674 @@
|
|||
import json
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
from collections import defaultdict
|
||||
|
||||
from uuid import UUID
|
||||
|
||||
from mozbuild.util import FileAvoidWrite
|
||||
from perfecthash import PerfectHash
|
||||
import buildconfig
|
||||
|
||||
|
||||
PHF_SIZE = 512
|
||||
|
||||
ENDIAN = '<' if buildconfig.substs['TARGET_ENDIANNESS'] == 'little' else '>'
|
||||
|
||||
|
||||
# Represents a UUID in the format used internally by Gecko, and supports
|
||||
# serializing it in that format to both C++ source and raw byte arrays.
|
||||
class UUIDRepr(object):
|
||||
|
||||
def __init__(self, uuid):
|
||||
self.uuid = uuid
|
||||
|
||||
fields = uuid.fields
|
||||
|
||||
self.a = fields[0]
|
||||
self.b = fields[1]
|
||||
self.c = fields[2]
|
||||
|
||||
d = list(fields[3:5])
|
||||
for i in range(0, 6):
|
||||
d.append(fields[5] >> (8 * (5 - i)) & 0xff)
|
||||
|
||||
self.d = tuple(d)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.uuid)
|
||||
|
||||
@property
|
||||
def bytes(self):
|
||||
return struct.pack(ENDIAN + 'IHHBBBBBBBB',
|
||||
self.a, self.b, self.c, *self.d)
|
||||
|
||||
def to_cxx(self):
|
||||
rest = ', '.join('0x%02x' % b for b in self.d)
|
||||
|
||||
return '{ 0x%x, 0x%x, 0x%x, { %s } }' % (self.a, self.b, self.c,
|
||||
rest)
|
||||
|
||||
|
||||
# Corresponds to the Module::ProcessSelector enum in Module.h. The actual
|
||||
# values don't matter, since the code generator emits symbolic constants for
|
||||
# these values, but we use the same values as the enum constants for clarity.
|
||||
class ProcessSelector:
|
||||
ANY_PROCESS = 0x0
|
||||
MAIN_PROCESS_ONLY = 0x1
|
||||
CONTENT_PROCESS_ONLY = 0x2
|
||||
ALLOW_IN_GPU_PROCESS = 0x4
|
||||
ALLOW_IN_VR_PROCESS = 0x8
|
||||
ALLOW_IN_SOCKET_PROCESS = 0x10
|
||||
ALLOW_IN_GPU_AND_SOCKET_PROCESS = (ALLOW_IN_GPU_PROCESS |
|
||||
ALLOW_IN_SOCKET_PROCESS)
|
||||
ALLOW_IN_GPU_AND_VR_PROCESS = ALLOW_IN_GPU_PROCESS | ALLOW_IN_VR_PROCESS
|
||||
ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS = (ALLOW_IN_GPU_PROCESS |
|
||||
ALLOW_IN_VR_PROCESS |
|
||||
ALLOW_IN_SOCKET_PROCESS)
|
||||
|
||||
|
||||
# Maps ProcessSelector constants to the name of the corresponding
|
||||
# Module::ProcessSelector enum value.
|
||||
PROCESSES = {
|
||||
ProcessSelector.ANY_PROCESS: 'ANY_PROCESS',
|
||||
ProcessSelector.MAIN_PROCESS_ONLY: 'MAIN_PROCESS_ONLY',
|
||||
ProcessSelector.CONTENT_PROCESS_ONLY: 'CONTENT_PROCESS_ONLY',
|
||||
ProcessSelector.ALLOW_IN_GPU_PROCESS: 'ALLOW_IN_GPU_PROCESS',
|
||||
ProcessSelector.ALLOW_IN_VR_PROCESS: 'ALLOW_IN_VR_PROCESS',
|
||||
ProcessSelector.ALLOW_IN_SOCKET_PROCESS: 'ALLOW_IN_SOCKET_PROCESS',
|
||||
ProcessSelector.ALLOW_IN_GPU_AND_SOCKET_PROCESS: 'ALLOW_IN_GPU_AND_SOCKET_PROCESS',
|
||||
ProcessSelector.ALLOW_IN_GPU_AND_VR_PROCESS: 'ALLOW_IN_GPU_AND_VR_PROCESS',
|
||||
ProcessSelector.ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS: 'ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS',
|
||||
}
|
||||
|
||||
|
||||
# Emits the C++ symbolic constant corresponding to a ProcessSelector constant.
|
||||
def lower_processes(processes):
|
||||
return 'Module::ProcessSelector::%s' % PROCESSES[processes]
|
||||
|
||||
|
||||
# Emits the C++ symbolic constant for a ModuleEntry's ModuleID enum entry.
|
||||
def lower_module_id(module):
|
||||
return 'ModuleID::%s' % module.name
|
||||
|
||||
|
||||
# Represents a static string table, indexed by offset. This allows us to
|
||||
# reference strings from static data structures without requiring runtime
|
||||
# relocations.
|
||||
class StringTable(object):
|
||||
|
||||
def __init__(self):
|
||||
self.entries = {}
|
||||
self.entry_list = []
|
||||
self.size = 0
|
||||
|
||||
self._serialized = False
|
||||
|
||||
# Returns the index of the given string in the `entry_list` array. If
|
||||
# no entry for the string exists, it first creates one.
|
||||
def get_idx(self, string):
|
||||
idx = self.entries.get(string, None)
|
||||
if idx is not None:
|
||||
return idx
|
||||
|
||||
assert not self._serialized
|
||||
|
||||
assert len(string) == len(string.encode('utf-8'))
|
||||
|
||||
idx = self.size
|
||||
self.size += len(string) + 1
|
||||
|
||||
self.entries[string] = idx
|
||||
self.entry_list.append(string)
|
||||
return idx
|
||||
|
||||
# Returns the C++ code representing string data of this string table, as a
|
||||
# single string literal. This must only be called after the last call to
|
||||
# `get_idx()` or `entry_to_cxx()` for this instance.
|
||||
def to_cxx(self):
|
||||
self._serialized = True
|
||||
|
||||
lines = []
|
||||
|
||||
idx = 0
|
||||
for entry in self.entry_list:
|
||||
str_ = entry.replace('\\', '\\\\').replace('"', r'\"') \
|
||||
.replace('\n', r'\n')
|
||||
|
||||
lines.append(' /* 0x%x */ "%s\\0"\n' % (idx, str_))
|
||||
|
||||
idx += len(entry) + 1
|
||||
|
||||
return ''.join(lines)
|
||||
|
||||
# Returns a `StringEntry` struct initializer for the string table entry
|
||||
# corresponding to the given string. If no matching entry exists, it is
|
||||
# first created.
|
||||
def entry_to_cxx(self, string):
|
||||
idx = self.get_idx(string)
|
||||
return '{ 0x%x } /* %s */' % (
|
||||
idx,
|
||||
pretty_string(string))
|
||||
|
||||
|
||||
strings = StringTable()
|
||||
|
||||
|
||||
# Represents a C++ namespace, containing a set of classes and potentially
|
||||
# sub-namespaces. This is used to generate pre-declarations for incomplete
|
||||
# types referenced in XPCOM manifests.
|
||||
class Namespace(object):
|
||||
|
||||
def __init__(self, name=None):
|
||||
self.name = name
|
||||
self.classes = set()
|
||||
self.namespaces = {}
|
||||
|
||||
# Returns a Namespace object for the sub-namespace with the given name.
|
||||
def sub(self, name):
|
||||
assert name not in self.classes
|
||||
|
||||
if name not in self.namespaces:
|
||||
self.namespaces[name] = Namespace(name)
|
||||
return self.namespaces[name]
|
||||
|
||||
# Generates C++ code to pre-declare all classes in this namespace and all
|
||||
# of its sub-namespaces.
|
||||
def to_cxx(self):
|
||||
res = ""
|
||||
if self.name:
|
||||
res += 'namespace %s {\n' % self.name
|
||||
|
||||
for clas in sorted(self.classes):
|
||||
res += 'class %s;\n' % clas
|
||||
|
||||
for ns in sorted(self.namespaces.keys()):
|
||||
res += self.namespaces[ns].to_cxx()
|
||||
|
||||
if self.name:
|
||||
res += '} // namespace %s\n' % self.name
|
||||
|
||||
return res
|
||||
|
||||
|
||||
# Represents a component defined in an XPCOM manifest's `Classes` array.
|
||||
class ModuleEntry(object):
|
||||
next_anon_id = 0
|
||||
|
||||
def __init__(self, data, init_idx):
|
||||
self.cid = UUIDRepr(UUID(data['cid']))
|
||||
self.contract_ids = data.get('contract_ids', [])
|
||||
self.type = data.get('type', 'nsISupports')
|
||||
self.categories = data.get('categories', {})
|
||||
self.processes = data.get('processes', 0)
|
||||
self.headers = data.get('headers', [])
|
||||
|
||||
# If the manifest declares Init or Unload functions, this contains its
|
||||
# index, as understood by the `CallInitFunc()` function.
|
||||
#
|
||||
# If it contains any value other than `None`, a corresponding
|
||||
# `CallInitFunc(init_idx)` call will be genrated before calling this
|
||||
# module's constructor.
|
||||
self.init_idx = init_idx
|
||||
|
||||
self.constructor = data.get('constructor', None)
|
||||
self.legacy_constructor = data.get('legacy_constructor', None)
|
||||
self.init_method = data.get('init_method', [])
|
||||
|
||||
self.external = data.get('external', not (self.headers or
|
||||
self.legacy_constructor))
|
||||
self.singleton = data.get('singleton', False)
|
||||
|
||||
if 'name' in data:
|
||||
self.anonymous = False
|
||||
self.name = data['name']
|
||||
else:
|
||||
self.anonymous = True
|
||||
self.name = 'Anonymous%03d' % ModuleEntry.next_anon_id
|
||||
ModuleEntry.next_anon_id += 1
|
||||
|
||||
def error(str_):
|
||||
raise Exception("Error defining component %s (%s): %s" % (
|
||||
str(self.cid), ', '.join(map(repr, self.contract_ids)),
|
||||
str_))
|
||||
|
||||
if self.external:
|
||||
if self.constructor or self.legacy_constructor:
|
||||
error("Externally-constructed components may not specify "
|
||||
"'constructor' or 'legacy_constructor' properties")
|
||||
if self.init_method:
|
||||
error("Externally-constructed components may not specify "
|
||||
"'init_method' properties")
|
||||
if self.type == 'nsISupports':
|
||||
error("Externally-constructed components must specify a type "
|
||||
"other than nsISupports")
|
||||
|
||||
if self.constructor and self.legacy_constructor:
|
||||
error("The 'constructor' and 'legacy_constructor' properties "
|
||||
"are mutually exclusive")
|
||||
|
||||
@property
|
||||
def contract_id(self):
|
||||
return self.contract_ids[0]
|
||||
|
||||
# Generates the C++ code for a StaticModule struct initializer
|
||||
# representing this component.
|
||||
def to_cxx(self):
|
||||
return """
|
||||
/* {name} */ {{
|
||||
/* {{{cid_string}}} */
|
||||
{cid},
|
||||
{processes},
|
||||
}}""".format(name=self.name, cid=self.cid.to_cxx(),
|
||||
cid_string=str(self.cid),
|
||||
processes=lower_processes(self.processes))
|
||||
|
||||
# Generates the C++ code necessary to construct an instance of this
|
||||
# component.
|
||||
#
|
||||
# This code lives in a function with the following arguments:
|
||||
#
|
||||
# - aIID: The `const nsIID&` interface ID that the resulting instance
|
||||
# will be queried to.
|
||||
#
|
||||
# - aResult: The `void**` pointer in which to store the result.
|
||||
#
|
||||
# And which returns an `nsresult` indicating success or failure.
|
||||
def lower_constructor(self):
|
||||
res = ''
|
||||
|
||||
if self.init_idx is not None:
|
||||
res += ' MOZ_TRY(CallInitFunc(%d));\n' % self.init_idx
|
||||
|
||||
if self.legacy_constructor:
|
||||
res += (' return /* legacy */ %s(nullptr, aIID, aResult);\n'
|
||||
% self.legacy_constructor)
|
||||
return res
|
||||
|
||||
if self.external:
|
||||
res += (' nsCOMPtr<nsISupports> inst = '
|
||||
'mozCreateComponent<%s>();\n' % self.type)
|
||||
else:
|
||||
res += ' RefPtr<%s> inst = ' % self.type
|
||||
|
||||
if not self.constructor:
|
||||
res += 'new %s();\n' % self.type
|
||||
else:
|
||||
res += '%s();\n' % self.constructor
|
||||
# The `new` operator is infallible, so we don't need to worry
|
||||
# about it returning null, but custom constructors may, so
|
||||
# check before calling any methods.
|
||||
res += ' NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY);\n'
|
||||
|
||||
# Check that the constructor function returns an appropriate
|
||||
# `already_AddRefed` value for our declared type.
|
||||
res += """
|
||||
using T =
|
||||
RemoveAlreadyAddRefed<decltype(%(constructor)s())>::Type;
|
||||
static_assert(
|
||||
mozilla::IsSame<already_AddRefed<T>, decltype(%(constructor)s())>::value,
|
||||
"Singleton constructor must return already_AddRefed");
|
||||
static_assert(
|
||||
mozilla::IsBaseOf<%(type)s, T>::value,
|
||||
"Singleton constructor must return correct already_AddRefed");
|
||||
|
||||
""" % {'type': self.type, 'constructor': self.constructor}
|
||||
|
||||
if self.init_method:
|
||||
res += ' MOZ_TRY(inst->%s());\n' % self.init_method
|
||||
|
||||
res += ' return inst->QueryInterface(aIID, aResult);\n'
|
||||
|
||||
return res
|
||||
|
||||
|
||||
# Returns a quoted string literal representing the given raw string, with
|
||||
# certain special characters replaced so that it can be used in a C++-style
|
||||
# (/* ... */) comment.
|
||||
def pretty_string(string):
|
||||
return (json.dumps(string).replace('*/', r'*\/')
|
||||
.replace('/*', r'/\*'))
|
||||
|
||||
|
||||
# Represents a static contract ID entry, corresponding to a C++ ContractEntry
|
||||
# struct, mapping a contract ID to a static module entry.
|
||||
class ContractEntry(object):
|
||||
|
||||
def __init__(self, contract, module):
|
||||
self.contract = contract
|
||||
self.module = module
|
||||
|
||||
def to_cxx(self):
|
||||
return """
|
||||
{{
|
||||
{contract},
|
||||
{module_id},
|
||||
}}""".format(contract=strings.entry_to_cxx(self.contract),
|
||||
module_id=lower_module_id(self.module))
|
||||
|
||||
|
||||
# Generates the C++ code for the StaticCategoryEntry and StaticCategory
|
||||
# structs for all category entries declared in XPCOM manifests.
|
||||
def gen_categories(substs, categories):
|
||||
cats = []
|
||||
ents = []
|
||||
|
||||
count = 0
|
||||
for category, entries in sorted(categories.items()):
|
||||
entries.sort()
|
||||
|
||||
cats.append(' { %s,\n'
|
||||
' %d, %d },\n'
|
||||
% (strings.entry_to_cxx(category),
|
||||
count, len(entries)))
|
||||
count += len(entries)
|
||||
|
||||
ents.append(' /* %s */\n' % pretty_string(category))
|
||||
for entry, value, processes in entries:
|
||||
ents.append(' { %s,\n'
|
||||
' %s,\n'
|
||||
' %s },\n'
|
||||
% (strings.entry_to_cxx(entry),
|
||||
strings.entry_to_cxx(value),
|
||||
lower_processes(processes)))
|
||||
ents.append('\n')
|
||||
ents.pop()
|
||||
|
||||
substs['category_count'] = len(cats)
|
||||
substs['categories'] = ''.join(cats)
|
||||
substs['category_entries'] = ''.join(ents)
|
||||
|
||||
|
||||
# Generates the C++ code for all Init and Unload functions declared in XPCOM
|
||||
# manifests. These form the bodies of the `CallInitFunc()` and `CallUnload`
|
||||
# functions in StaticComponents.cpp.
|
||||
def gen_module_funcs(substs, funcs):
|
||||
inits = []
|
||||
unloads = []
|
||||
|
||||
template = """\
|
||||
case %d:
|
||||
%s
|
||||
break;
|
||||
"""
|
||||
|
||||
for i, (init, unload) in enumerate(funcs):
|
||||
init_code = '%s();' % init if init else '/* empty */'
|
||||
inits.append(template % (i, init_code))
|
||||
|
||||
if unload:
|
||||
unloads.append("""\
|
||||
if (CalledInit(%d)) {
|
||||
%s();
|
||||
}
|
||||
""" % (i, unload))
|
||||
|
||||
substs['init_funcs'] = ''.join(inits)
|
||||
substs['unload_funcs'] = ''.join(unloads)
|
||||
substs['init_count'] = len(funcs)
|
||||
|
||||
|
||||
# Generates class pre-declarations for any types referenced in `Classes` array
|
||||
# entries which do not have corresponding `headers` entries to fully declare
|
||||
# their types.
|
||||
def gen_decls(types):
|
||||
root_ns = Namespace()
|
||||
|
||||
for type_ in sorted(types):
|
||||
parts = type_.split('::')
|
||||
|
||||
ns = root_ns
|
||||
for part in parts[:-1]:
|
||||
ns = ns.sub(part)
|
||||
ns.classes.add(parts[-1])
|
||||
|
||||
return root_ns.to_cxx()
|
||||
|
||||
|
||||
# Generates the `switch` body for the `CreateInstanceImpl()` function, with a
|
||||
# `case` for each value in ModuleID to construct an instance of the
|
||||
# corresponding component.
|
||||
def gen_constructors(entries):
|
||||
constructors = []
|
||||
for entry in entries:
|
||||
constructors.append("""\
|
||||
case {id}: {{
|
||||
{constructor}\
|
||||
}}
|
||||
""".format(id=lower_module_id(entry),
|
||||
constructor=entry.lower_constructor()))
|
||||
|
||||
return ''.join(constructors)
|
||||
|
||||
|
||||
def gen_includes(substs, all_headers):
|
||||
headers = set()
|
||||
absolute_headers = set()
|
||||
|
||||
for header in all_headers:
|
||||
if header.startswith('/'):
|
||||
absolute_headers.add(header)
|
||||
else:
|
||||
headers.add(header)
|
||||
|
||||
includes = ['#include "%s"' % header for header in sorted(headers)]
|
||||
substs['includes'] = '\n'.join(includes) + '\n'
|
||||
|
||||
relative_includes = ['#include "../..%s"' % header
|
||||
for header in sorted(absolute_headers)]
|
||||
substs['relative_includes'] = '\n'.join(relative_includes) + '\n'
|
||||
|
||||
|
||||
def to_list(val):
|
||||
if isinstance(val, (list, tuple)):
|
||||
return val
|
||||
return val,
|
||||
|
||||
|
||||
def gen_substs(manifests):
|
||||
module_funcs = []
|
||||
|
||||
headers = set()
|
||||
|
||||
modules = []
|
||||
|
||||
for manifest in manifests:
|
||||
headers |= set(manifest.get('Headers', []))
|
||||
|
||||
init_idx = None
|
||||
init = manifest.get('InitFunc')
|
||||
unload = manifest.get('UnloadFunc')
|
||||
if init or unload:
|
||||
init_idx = len(module_funcs)
|
||||
module_funcs.append((init, unload))
|
||||
|
||||
for clas in manifest['Classes']:
|
||||
modules.append(ModuleEntry(clas, init_idx))
|
||||
|
||||
contracts = []
|
||||
contract_map = {}
|
||||
categories = defaultdict(list)
|
||||
|
||||
types = set()
|
||||
|
||||
for mod in modules:
|
||||
headers |= set(mod.headers)
|
||||
|
||||
for contract_id in mod.contract_ids:
|
||||
if contract_id in contract_map:
|
||||
raise Exception('Duplicate contract ID: %s' % contract_id)
|
||||
|
||||
entry = ContractEntry(contract_id, mod)
|
||||
contracts.append(entry)
|
||||
contract_map[contract_id] = entry
|
||||
|
||||
for category, entries in mod.categories.items():
|
||||
for entry in to_list(entries):
|
||||
categories[category].append((entry, mod.contract_id,
|
||||
mod.processes))
|
||||
|
||||
if mod.type and not mod.headers:
|
||||
types.add(mod.type)
|
||||
|
||||
cid_phf = PerfectHash(modules, PHF_SIZE,
|
||||
key=lambda module: module.cid.bytes)
|
||||
|
||||
contract_phf = PerfectHash(contracts, PHF_SIZE,
|
||||
key=lambda entry: entry.contract)
|
||||
|
||||
substs = {}
|
||||
|
||||
gen_categories(substs, categories)
|
||||
|
||||
substs['module_ids'] = ''.join(' %s,\n' % entry.name
|
||||
for entry in cid_phf.entries)
|
||||
|
||||
substs['module_count'] = len(modules)
|
||||
substs['contract_count'] = len(contracts)
|
||||
|
||||
gen_module_funcs(substs, module_funcs)
|
||||
|
||||
gen_includes(substs, headers)
|
||||
|
||||
substs['decls'] = gen_decls(types)
|
||||
|
||||
substs['constructors'] = gen_constructors(cid_phf.entries)
|
||||
|
||||
substs['module_cid_table'] = cid_phf.cxx_codegen(
|
||||
name='ModuleByCID',
|
||||
entry_type='StaticModule',
|
||||
entries_name='gStaticModules',
|
||||
lower_entry=lambda entry: entry.to_cxx(),
|
||||
|
||||
return_type='const StaticModule*',
|
||||
return_entry=('return entry.CID().Equals(aKey) && entry.Active()'
|
||||
' ? &entry : nullptr;'),
|
||||
|
||||
key_type='const nsID&',
|
||||
key_bytes='reinterpret_cast<const char*>(&aKey)',
|
||||
key_length='sizeof(nsID)')
|
||||
|
||||
substs['module_contract_id_table'] = contract_phf.cxx_codegen(
|
||||
name='LookupContractID',
|
||||
entry_type='ContractEntry',
|
||||
entries_name='gContractEntries',
|
||||
lower_entry=lambda entry: entry.to_cxx(),
|
||||
|
||||
return_type='const ContractEntry*',
|
||||
return_entry='return entry.Matches(aKey) ? &entry : nullptr;',
|
||||
|
||||
key_type='const nsACString&',
|
||||
key_bytes='aKey.BeginReading()',
|
||||
key_length='aKey.Length()')
|
||||
|
||||
# Do this only after everything else has been emitted so we're sure the
|
||||
# string table is complete.
|
||||
substs['strings'] = strings.to_cxx()
|
||||
return substs
|
||||
|
||||
|
||||
# Returns true if the given build config substitution is defined and truthy.
|
||||
def defined(subst):
|
||||
return bool(buildconfig.substs.get(subst))
|
||||
|
||||
|
||||
def read_manifest(filename):
|
||||
glbl = {'buildconfig': buildconfig,
|
||||
'defined': defined,
|
||||
'ProcessSelector': ProcessSelector}
|
||||
execfile(filename, glbl)
|
||||
return glbl
|
||||
|
||||
|
||||
def main(fd, conf_file, template_file):
|
||||
def open_output(filename):
|
||||
return FileAvoidWrite(os.path.join(os.path.dirname(fd.name), filename))
|
||||
|
||||
conf = json.load(open(conf_file, 'r'))
|
||||
|
||||
deps = set()
|
||||
|
||||
manifests = []
|
||||
for filename in conf['manifests']:
|
||||
deps.add(filename)
|
||||
manifest = read_manifest(filename)
|
||||
manifests.append(manifest)
|
||||
manifest.setdefault('Priority', 50)
|
||||
manifest['__filename__'] = filename
|
||||
|
||||
manifests.sort(key=lambda man: (man['Priority'], man['__filename__']))
|
||||
|
||||
substs = gen_substs(manifests)
|
||||
|
||||
def replacer(match):
|
||||
return substs[match.group(1)]
|
||||
|
||||
with open_output('StaticComponents.cpp') as fh:
|
||||
with open(template_file, 'r') as tfh:
|
||||
template = tfh.read()
|
||||
|
||||
fh.write(re.sub(r'//# @([a-zA-Z_]+)@\n', replacer, template))
|
||||
|
||||
with open_output('StaticComponentData.h') as fh:
|
||||
fh.write("""\
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef StaticComponentData_h
|
||||
#define StaticComponentData_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace xpcom {
|
||||
|
||||
static constexpr size_t kStaticModuleCount = %(module_count)d;
|
||||
|
||||
static constexpr size_t kContractCount = %(contract_count)d;
|
||||
|
||||
static constexpr size_t kStaticCategoryCount = %(category_count)d;
|
||||
|
||||
static constexpr size_t kModuleInitCount = %(init_count)d;
|
||||
|
||||
} // namespace xpcom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
""" % substs)
|
||||
|
||||
fd.write("""\
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_Components_h
|
||||
#define mozilla_Components_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
struct nsID;
|
||||
|
||||
#define NS_IMPL_COMPONENT_FACTORY(iface) \\
|
||||
template <> \\
|
||||
already_AddRefed<nsISupports> mozCreateComponent<iface>()
|
||||
|
||||
template <typename T>
|
||||
already_AddRefed<nsISupports> mozCreateComponent();
|
||||
|
||||
namespace mozilla {
|
||||
namespace xpcom {
|
||||
|
||||
enum class ModuleID : uint16_t {
|
||||
%(module_ids)s
|
||||
};
|
||||
|
||||
} // namespace xpcom
|
||||
|
||||
#endif
|
||||
""" % substs)
|
||||
|
||||
return deps
|
|
@ -29,6 +29,20 @@ EXPORTS.mozilla += [
|
|||
'ModuleUtils.h',
|
||||
]
|
||||
|
||||
if CONFIG['COMPILE_ENVIRONMENT']:
|
||||
EXPORTS.mozilla += [
|
||||
'!Components.h',
|
||||
]
|
||||
|
||||
generated = ('Components.h', 'StaticComponentData.h',
|
||||
'StaticComponents.cpp')
|
||||
|
||||
GENERATED_FILES += [generated]
|
||||
|
||||
gen = GENERATED_FILES[generated]
|
||||
gen.script = 'gen_static_components.py'
|
||||
gen.inputs += ['!manifest-lists.json', 'StaticComponents.cpp.in']
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'GenericFactory.cpp',
|
||||
'ManifestParser.cpp',
|
||||
|
@ -38,6 +52,10 @@ UNIFIED_SOURCES += [
|
|||
'nsComponentManagerUtils.cpp',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'!StaticComponents.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
|
@ -53,3 +71,5 @@ LOCAL_INCLUDES += [
|
|||
|
||||
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
|
||||
CXXFLAGS += CONFIG['TK_CFLAGS']
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "nspr.h"
|
||||
#include "nsCRT.h" // for atoll
|
||||
|
||||
#include "StaticComponents.h"
|
||||
|
||||
#include "nsCategoryManager.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsComponentManager.h"
|
||||
|
@ -53,6 +55,7 @@
|
|||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/URLPreloader.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Variant.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
#include <new> // for placement new
|
||||
|
@ -157,12 +160,90 @@ bool FastProcessSelectorMatches(ProcessSelector aSelector) {
|
|||
} // namespace xpcom
|
||||
} // namespace mozilla
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* A wrapper simple wrapper class, which can hold either a dynamic
|
||||
* nsFactoryEntry instance, or a static StaticModule entry, and transparently
|
||||
* forwards method calls to the wrapped object.
|
||||
*
|
||||
* This allows the same code to work with either static or dynamic modules
|
||||
* without caring about the difference.
|
||||
*/
|
||||
class MOZ_STACK_CLASS EntryWrapper final {
|
||||
public:
|
||||
explicit EntryWrapper(nsFactoryEntry* aEntry) : mEntry(aEntry) {}
|
||||
|
||||
explicit EntryWrapper(const StaticModule* aEntry) : mEntry(aEntry) {}
|
||||
|
||||
#define MATCH(type, ifFactory, ifStatic) \
|
||||
struct Matcher { \
|
||||
type match(nsFactoryEntry* entry) { ifFactory; } \
|
||||
type match(const StaticModule* entry) { ifStatic; } \
|
||||
}; \
|
||||
return mEntry.match((Matcher()))
|
||||
|
||||
const nsID& CID() {
|
||||
MATCH(const nsID&, return *entry->mCIDEntry->cid, return entry->CID());
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFactory> GetFactory() {
|
||||
MATCH(already_AddRefed<nsIFactory>, return entry->GetFactory(),
|
||||
return entry->GetFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the underlying component. This should be used in
|
||||
* preference to GetFactory()->CreateInstance() where appropriate, since it
|
||||
* side-steps the necessity of creating a nsIFactory instance for static
|
||||
* modules.
|
||||
*/
|
||||
nsresult CreateInstance(nsISupports* aOuter, const nsIID& aIID,
|
||||
void** aResult) {
|
||||
if (mEntry.is<nsFactoryEntry*>()) {
|
||||
return mEntry.as<nsFactoryEntry*>()->CreateInstance(aOuter, aIID,
|
||||
aResult);
|
||||
}
|
||||
return mEntry.as<const StaticModule*>()->CreateInstance(aOuter, aIID,
|
||||
aResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cached service instance for this entry, if any. This should
|
||||
* only be accessed while mLock is held.
|
||||
*/
|
||||
nsISupports* ServiceInstance() {
|
||||
MATCH(nsISupports*, return entry->mServiceObject,
|
||||
return entry->ServiceInstance());
|
||||
}
|
||||
void SetServiceInstance(already_AddRefed<nsISupports> aInst) {
|
||||
if (mEntry.is<nsFactoryEntry*>()) {
|
||||
mEntry.as<nsFactoryEntry*>()->mServiceObject = aInst;
|
||||
} else {
|
||||
return mEntry.as<const StaticModule*>()->SetServiceInstance(
|
||||
std::move(aInst));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description string for the module this entry belongs to. For
|
||||
* static entries, always returns "<unknown module>".
|
||||
*/
|
||||
nsCString ModuleDescription() {
|
||||
MATCH(nsCString,
|
||||
return entry->mModule ? entry->mModule->Description()
|
||||
: NS_LITERAL_CSTRING("<unknown module>"),
|
||||
return NS_LITERAL_CSTRING("<unknown module>"));
|
||||
}
|
||||
|
||||
private:
|
||||
Variant<nsFactoryEntry*, const StaticModule*> mEntry;
|
||||
};
|
||||
|
||||
// GetService and a few other functions need to exit their mutex mid-function
|
||||
// without reentering it later in the block. This class supports that
|
||||
// style of early-exit that MutexAutoUnlock doesn't.
|
||||
|
||||
namespace {
|
||||
|
||||
class MOZ_STACK_CLASS MutexLock {
|
||||
public:
|
||||
explicit MutexLock(SafeMutex& aMutex) : mMutex(aMutex), mLocked(false) {
|
||||
|
@ -244,7 +325,7 @@ nsresult nsComponentManagerImpl::Create(nsISupports* aOuter, REFNSIID aIID,
|
|||
return gComponentManager->QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 1024;
|
||||
static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 512;
|
||||
|
||||
nsComponentManagerImpl::nsComponentManagerImpl()
|
||||
: mFactories(CONTRACTID_HASHTABLE_INITIAL_LENGTH),
|
||||
|
@ -398,6 +479,15 @@ nsresult nsComponentManagerImpl::Init() {
|
|||
RegisterModule((*sExtraStaticModules)[i]);
|
||||
}
|
||||
|
||||
auto* catMan = nsCategoryManager::GetSingleton();
|
||||
for (const auto& cat : gStaticCategories) {
|
||||
for (const auto& entry : cat) {
|
||||
if (entry.Active()) {
|
||||
catMan->AddCategoryEntry(cat.Name(), entry.Entry(), entry.Value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool loadChromeManifests;
|
||||
switch (XRE_GetProcessType()) {
|
||||
// We are going to assume that only a select few (see below) process types
|
||||
|
@ -495,6 +585,10 @@ nsresult nsComponentManagerImpl::Init() {
|
|||
|
||||
mStatus = NORMAL;
|
||||
|
||||
MOZ_ASSERT(!XRE_IsContentProcess() ||
|
||||
mFactories.Count() > CONTRACTID_HASHTABLE_INITIAL_LENGTH / 3,
|
||||
"Initial component hashtable size is too large");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -705,17 +799,11 @@ void nsComponentManagerImpl::ManifestComponent(ManifestProcessingContext& aCx,
|
|||
fl.GetURIString(hash);
|
||||
|
||||
MutexLock lock(mLock);
|
||||
nsFactoryEntry* f = mFactories.Get(&cid);
|
||||
if (f) {
|
||||
if (Maybe<EntryWrapper> f = LookupByCID(lock, cid)) {
|
||||
char idstr[NSID_LENGTH];
|
||||
cid.ToProvidedString(idstr);
|
||||
|
||||
nsCString existing;
|
||||
if (f->mModule) {
|
||||
existing = f->mModule->Description();
|
||||
} else {
|
||||
existing = "<unknown module>";
|
||||
}
|
||||
nsCString existing(f->ModuleDescription());
|
||||
|
||||
lock.Unlock();
|
||||
|
||||
|
@ -855,6 +943,8 @@ nsresult nsComponentManagerImpl::Shutdown(void) {
|
|||
mKnownModules.Clear();
|
||||
mKnownStaticModules.Clear();
|
||||
|
||||
StaticComponents::Shutdown();
|
||||
|
||||
delete sExtraStaticModules;
|
||||
delete sModuleLocations;
|
||||
|
||||
|
@ -891,20 +981,46 @@ nsresult nsComponentManagerImpl::GetInterface(const nsIID& aUuid,
|
|||
return QueryInterface(aUuid, aResult);
|
||||
}
|
||||
|
||||
nsFactoryEntry* nsComponentManagerImpl::GetFactoryEntry(
|
||||
const char* aContractID, uint32_t aContractIDLen) {
|
||||
SafeMutexAutoLock lock(mLock);
|
||||
return mContractIDs.Get(nsDependentCString(aContractID, aContractIDLen));
|
||||
Maybe<EntryWrapper> nsComponentManagerImpl::LookupByCID(const nsID& aCID) {
|
||||
return LookupByCID(MutexLock(mLock), aCID);
|
||||
}
|
||||
|
||||
nsFactoryEntry* nsComponentManagerImpl::GetFactoryEntry(const nsCID& aClass) {
|
||||
SafeMutexAutoLock lock(mLock);
|
||||
return mFactories.Get(&aClass);
|
||||
Maybe<EntryWrapper> nsComponentManagerImpl::LookupByCID(const MutexLock&,
|
||||
const nsID& aCID) {
|
||||
if (const StaticModule* module = StaticComponents::LookupByCID(aCID)) {
|
||||
return Some(EntryWrapper(module));
|
||||
}
|
||||
if (nsFactoryEntry* entry = mFactories.Get(&aCID)) {
|
||||
return Some(EntryWrapper(entry));
|
||||
}
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
Maybe<EntryWrapper> nsComponentManagerImpl::LookupByContractID(
|
||||
const nsACString& aContractID) {
|
||||
return LookupByContractID(MutexLock(mLock), aContractID);
|
||||
}
|
||||
|
||||
Maybe<EntryWrapper> nsComponentManagerImpl::LookupByContractID(
|
||||
const MutexLock&, const nsACString& aContractID) {
|
||||
if (const StaticModule* module =
|
||||
StaticComponents::LookupByContractID(aContractID)) {
|
||||
return Some(EntryWrapper(module));
|
||||
}
|
||||
if (nsFactoryEntry* entry = mContractIDs.Get(aContractID)) {
|
||||
// UnregisterFactory might have left a stale nsFactoryEntry in
|
||||
// mContractIDs, so we should check to see whether this entry has
|
||||
// anything useful.
|
||||
if (entry->mModule || entry->mFactory || entry->mServiceObject) {
|
||||
return Some(EntryWrapper(entry));
|
||||
}
|
||||
}
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
|
||||
const nsCID& aClass) {
|
||||
nsFactoryEntry* e = GetFactoryEntry(aClass);
|
||||
Maybe<EntryWrapper> e = LookupByCID(aClass);
|
||||
if (!e) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -914,7 +1030,8 @@ already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
|
|||
|
||||
already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
|
||||
const char* aContractID, uint32_t aContractIDLen) {
|
||||
nsFactoryEntry* entry = GetFactoryEntry(aContractID, aContractIDLen);
|
||||
Maybe<EntryWrapper> entry =
|
||||
LookupByContractID(nsDependentCString(aContractID, aContractIDLen));
|
||||
if (!entry) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1017,16 +1134,16 @@ nsComponentManagerImpl::CreateInstance(const nsCID& aClass,
|
|||
}
|
||||
*aResult = nullptr;
|
||||
|
||||
nsFactoryEntry* entry = GetFactoryEntry(aClass);
|
||||
Maybe<EntryWrapper> entry = LookupByCID(aClass);
|
||||
|
||||
if (!entry) {
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
#ifdef SHOW_CI_ON_EXISTING_SERVICE
|
||||
if (entry->mServiceObject) {
|
||||
char cid[NSID_LENGTH];
|
||||
aClass.ToProvidedString(cid);
|
||||
char cid[NSID_LENGTH];
|
||||
aClass.ToProvidedString(cid);
|
||||
if (entry->ServiceInstance()) {
|
||||
nsAutoCString message;
|
||||
message =
|
||||
NS_LITERAL_CSTRING("You are calling CreateInstance \"") +
|
||||
|
@ -1102,14 +1219,15 @@ nsComponentManagerImpl::CreateInstanceByContractID(const char* aContractID,
|
|||
}
|
||||
*aResult = nullptr;
|
||||
|
||||
nsFactoryEntry* entry = GetFactoryEntry(aContractID, strlen(aContractID));
|
||||
Maybe<EntryWrapper> entry =
|
||||
LookupByContractID(nsDependentCString(aContractID));
|
||||
|
||||
if (!entry) {
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
#ifdef SHOW_CI_ON_EXISTING_SERVICE
|
||||
if (entry->mServiceObject) {
|
||||
if (entry->ServiceInstance()) {
|
||||
nsAutoCString message;
|
||||
message =
|
||||
NS_LITERAL_CSTRING("You are calling CreateInstance \"") +
|
||||
|
@ -1155,6 +1273,10 @@ nsresult nsComponentManagerImpl::FreeServices() {
|
|||
entry->mServiceObject = nullptr;
|
||||
}
|
||||
|
||||
for (const auto& module : gStaticModules) {
|
||||
module.SetServiceInstance(nullptr);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1196,12 +1318,12 @@ PRThread* nsComponentManagerImpl::GetPendingServiceThread(
|
|||
}
|
||||
|
||||
nsresult nsComponentManagerImpl::GetServiceLocked(MutexLock& aLock,
|
||||
nsFactoryEntry& aEntry,
|
||||
EntryWrapper& aEntry,
|
||||
const nsIID& aIID,
|
||||
void** aResult) {
|
||||
if (aEntry.mServiceObject) {
|
||||
if (auto* service = aEntry.ServiceInstance()) {
|
||||
aLock.Unlock();
|
||||
return aEntry.mServiceObject->QueryInterface(aIID, aResult);
|
||||
return service->QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
PRThread* currentPRThread = PR_GetCurrentThread();
|
||||
|
@ -1211,7 +1333,7 @@ nsresult nsComponentManagerImpl::GetServiceLocked(MutexLock& aLock,
|
|||
nsIThread* currentThread = nullptr;
|
||||
|
||||
PRThread* pendingPRThread;
|
||||
while ((pendingPRThread = GetPendingServiceThread(*aEntry.mCIDEntry->cid))) {
|
||||
while ((pendingPRThread = GetPendingServiceThread(aEntry.CID()))) {
|
||||
if (pendingPRThread == currentPRThread) {
|
||||
NS_ERROR("Recursive GetService!");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
@ -1241,13 +1363,13 @@ nsresult nsComponentManagerImpl::GetServiceLocked(MutexLock& aLock,
|
|||
|
||||
// It's still possible that the other thread failed to create the
|
||||
// service so we're not guaranteed to have an entry or service yet.
|
||||
if (aEntry.mServiceObject) {
|
||||
if (auto* service = aEntry.ServiceInstance()) {
|
||||
aLock.Unlock();
|
||||
return aEntry.mServiceObject->QueryInterface(aIID, aResult);
|
||||
return service->QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
DebugOnly<PendingServiceInfo*> newInfo =
|
||||
AddPendingService(*aEntry.mCIDEntry->cid, currentPRThread);
|
||||
AddPendingService(aEntry.CID(), currentPRThread);
|
||||
NS_ASSERTION(newInfo, "Failed to add info to the array!");
|
||||
|
||||
// We need to not be holding the service manager's lock while calling
|
||||
|
@ -1267,8 +1389,7 @@ nsresult nsComponentManagerImpl::GetServiceLocked(MutexLock& aLock,
|
|||
nsresult rv;
|
||||
{
|
||||
SafeMutexAutoUnlock unlock(mLock);
|
||||
rv = CreateInstance(*aEntry.mCIDEntry->cid, nullptr, aIID,
|
||||
getter_AddRefs(service));
|
||||
rv = aEntry.CreateInstance(nullptr, aIID, getter_AddRefs(service));
|
||||
}
|
||||
if (NS_SUCCEEDED(rv) && !service) {
|
||||
NS_ERROR("Factory did not return an object but returned success");
|
||||
|
@ -1276,22 +1397,23 @@ nsresult nsComponentManagerImpl::GetServiceLocked(MutexLock& aLock,
|
|||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
pendingPRThread = GetPendingServiceThread(*aEntry.mCIDEntry->cid);
|
||||
pendingPRThread = GetPendingServiceThread(aEntry.CID());
|
||||
MOZ_ASSERT(pendingPRThread == currentPRThread,
|
||||
"Pending service array has been changed!");
|
||||
#endif
|
||||
RemovePendingService(*aEntry.mCIDEntry->cid);
|
||||
RemovePendingService(aEntry.CID());
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_ASSERTION(!aEntry.mServiceObject, "Created two instances of a service!");
|
||||
NS_ASSERTION(!aEntry.ServiceInstance(),
|
||||
"Created two instances of a service!");
|
||||
|
||||
aEntry.mServiceObject = service.forget();
|
||||
aEntry.SetServiceInstance(service.forget());
|
||||
|
||||
aLock.Unlock();
|
||||
*aResult = do_AddRef(aEntry.mServiceObject).take();
|
||||
*aResult = do_AddRef(aEntry.ServiceInstance()).take();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1317,7 +1439,7 @@ nsComponentManagerImpl::GetService(const nsCID& aClass, const nsIID& aIID,
|
|||
|
||||
MutexLock lock(mLock);
|
||||
|
||||
nsFactoryEntry* entry = mFactories.Get(&aClass);
|
||||
Maybe<EntryWrapper> entry = LookupByCID(lock, aClass);
|
||||
if (!entry) {
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
|
@ -1349,23 +1471,18 @@ nsComponentManagerImpl::IsServiceInstantiated(const nsCID& aClass,
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
nsFactoryEntry* entry;
|
||||
|
||||
{
|
||||
SafeMutexAutoLock lock(mLock);
|
||||
entry = mFactories.Get(&aClass);
|
||||
if (Maybe<EntryWrapper> entry = LookupByCID(aClass)) {
|
||||
if (auto* service = entry->ServiceInstance()) {
|
||||
nsCOMPtr<nsISupports> instance;
|
||||
nsresult rv = service->QueryInterface(
|
||||
aIID, getter_AddRefs(instance));
|
||||
*aResult = (instance != nullptr);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry && entry->mServiceObject) {
|
||||
nsCOMPtr<nsISupports> service;
|
||||
rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
|
||||
*aResult = (service != nullptr);
|
||||
} else {
|
||||
*aResult = false;
|
||||
}
|
||||
|
||||
return rv;
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1390,21 +1507,19 @@ nsComponentManagerImpl::IsServiceInstantiatedByContractID(
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
nsFactoryEntry* entry;
|
||||
{
|
||||
SafeMutexAutoLock lock(mLock);
|
||||
entry = mContractIDs.Get(nsDependentCString(aContractID));
|
||||
if (Maybe<EntryWrapper> entry =
|
||||
LookupByContractID(nsDependentCString(aContractID))) {
|
||||
if (auto* service = entry->ServiceInstance()) {
|
||||
nsCOMPtr<nsISupports> instance;
|
||||
nsresult rv = service->QueryInterface(
|
||||
aIID, getter_AddRefs(instance));
|
||||
*aResult = (instance != nullptr);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry && entry->mServiceObject) {
|
||||
nsCOMPtr<nsISupports> service;
|
||||
rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
|
||||
*aResult = (service != nullptr);
|
||||
} else {
|
||||
*aResult = false;
|
||||
}
|
||||
return rv;
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1429,7 +1544,8 @@ nsComponentManagerImpl::GetServiceByContractID(const char* aContractID,
|
|||
|
||||
MutexLock lock(mLock);
|
||||
|
||||
nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID));
|
||||
Maybe<EntryWrapper> entry =
|
||||
LookupByContractID(lock, nsDependentCString(aContractID));
|
||||
if (!entry) {
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
|
@ -1450,12 +1566,22 @@ nsComponentManagerImpl::RegisterFactory(const nsCID& aClass, const char* aName,
|
|||
|
||||
SafeMutexAutoLock lock(mLock);
|
||||
nsFactoryEntry* oldf = mFactories.Get(&aClass);
|
||||
if (!oldf) {
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
if (oldf) {
|
||||
mContractIDs.Put(nsDependentCString(aContractID), oldf);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mContractIDs.Put(nsDependentCString(aContractID), oldf);
|
||||
return NS_OK;
|
||||
if (StaticComponents::LookupByCID(aClass)) {
|
||||
// If this is the CID of a static module, just reset the invalid bit of
|
||||
// the static entry for this contract ID, and assume it points to the
|
||||
// correct class.
|
||||
nsDependentCString contractID(aContractID);
|
||||
if (StaticComponents::InvalidateContractID(contractID, false)) {
|
||||
mContractIDs.Remove(contractID);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
nsAutoPtr<nsFactoryEntry> f(new nsFactoryEntry(aClass, aFactory));
|
||||
|
@ -1464,8 +1590,16 @@ nsComponentManagerImpl::RegisterFactory(const nsCID& aClass, const char* aName,
|
|||
if (auto entry = mFactories.LookupForAdd(f->mCIDEntry->cid)) {
|
||||
return NS_ERROR_FACTORY_EXISTS;
|
||||
} else {
|
||||
if (StaticComponents::LookupByCID(*f->mCIDEntry->cid)) {
|
||||
entry.OrRemove();
|
||||
return NS_ERROR_FACTORY_EXISTS;
|
||||
}
|
||||
if (aContractID) {
|
||||
mContractIDs.Put(nsDependentCString(aContractID), f);
|
||||
nsDependentCString contractID(aContractID);
|
||||
mContractIDs.Put(contractID, f);
|
||||
// We allow dynamically-registered contract IDs to override static
|
||||
// entries, so invalidate any static entry for this contract ID.
|
||||
StaticComponents::InvalidateContractID(contractID);
|
||||
}
|
||||
entry.OrInsert([&f]() { return f.forget(); });
|
||||
}
|
||||
|
@ -1486,6 +1620,7 @@ nsComponentManagerImpl::UnregisterFactory(const nsCID& aClass,
|
|||
auto entry = mFactories.Lookup(&aClass);
|
||||
nsFactoryEntry* f = entry ? entry.Data() : nullptr;
|
||||
if (!f || f->mFactory != aFactory) {
|
||||
// Note: We do not support unregistering static factories.
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
|
@ -1530,7 +1665,7 @@ nsComponentManagerImpl::UnregisterFactoryLocation(const nsCID& aCID,
|
|||
|
||||
NS_IMETHODIMP
|
||||
nsComponentManagerImpl::IsCIDRegistered(const nsCID& aClass, bool* aResult) {
|
||||
*aResult = (nullptr != GetFactoryEntry(aClass));
|
||||
*aResult = LookupByCID(aClass).isSome();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1541,29 +1676,30 @@ nsComponentManagerImpl::IsContractIDRegistered(const char* aClass,
|
|||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsFactoryEntry* entry = GetFactoryEntry(aClass, strlen(aClass));
|
||||
Maybe<EntryWrapper> entry = LookupByContractID(nsDependentCString(aClass));
|
||||
|
||||
if (entry) {
|
||||
// UnregisterFactory might have left a stale nsFactoryEntry in
|
||||
// mContractIDs, so we should check to see whether this entry has
|
||||
// anything useful.
|
||||
*aResult = (bool(entry->mModule) || bool(entry->mFactory) ||
|
||||
bool(entry->mServiceObject));
|
||||
} else {
|
||||
*aResult = false;
|
||||
}
|
||||
*aResult = entry.isSome();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsComponentManagerImpl::EnumerateCIDs(nsISimpleEnumerator** aEnumerator) {
|
||||
nsCOMArray<nsISupports> array;
|
||||
for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) {
|
||||
const nsID* id = iter.Key();
|
||||
auto appendEntry = [&](const nsID& aCID) {
|
||||
nsCOMPtr<nsISupportsID> wrapper = new nsSupportsID();
|
||||
wrapper->SetData(id);
|
||||
wrapper->SetData(&aCID);
|
||||
array.AppendObject(wrapper);
|
||||
};
|
||||
|
||||
for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) {
|
||||
appendEntry(*iter.Key());
|
||||
}
|
||||
for (const auto& module : gStaticModules) {
|
||||
if (module.Active()) {
|
||||
appendEntry(module.CID());
|
||||
}
|
||||
}
|
||||
|
||||
return NS_NewArrayEnumerator(aEnumerator, array);
|
||||
}
|
||||
|
||||
|
@ -1576,6 +1712,12 @@ nsComponentManagerImpl::EnumerateContractIDs(
|
|||
array->AppendElement(contract);
|
||||
}
|
||||
|
||||
for (const auto& entry : gContractEntries) {
|
||||
if (!entry.Invalid()) {
|
||||
array->AppendElement(entry.ContractID());
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIUTF8StringEnumerator> e;
|
||||
nsresult rv = NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(e), array);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -1595,11 +1737,12 @@ NS_IMETHODIMP
|
|||
nsComponentManagerImpl::ContractIDToCID(const char* aContractID,
|
||||
nsCID** aResult) {
|
||||
{
|
||||
SafeMutexAutoLock lock(mLock);
|
||||
nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID));
|
||||
MutexLock lock(mLock);
|
||||
Maybe<EntryWrapper> entry =
|
||||
LookupByContractID(lock, nsDependentCString(aContractID));
|
||||
if (entry) {
|
||||
*aResult = (nsCID*)moz_xmalloc(sizeof(nsCID));
|
||||
**aResult = *entry->mCIDEntry->cid;
|
||||
**aResult = entry->CID();
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
@ -1722,6 +1865,13 @@ already_AddRefed<nsIFactory> nsFactoryEntry::GetFactory() {
|
|||
return factory.forget();
|
||||
}
|
||||
|
||||
nsresult nsFactoryEntry::CreateInstance(nsISupports* aOuter, const nsIID& aIID,
|
||||
void** aResult) {
|
||||
nsCOMPtr<nsIFactory> factory = GetFactory();
|
||||
NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
|
||||
return factory->CreateInstance(aOuter, aIID, aResult);
|
||||
}
|
||||
|
||||
size_t nsFactoryEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
|
||||
size_t n = aMallocSizeOf(this);
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
#include "nsClassHashtable.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "mozilla/Components.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Omnijar.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
|
@ -53,8 +55,9 @@ struct PRThread;
|
|||
extern const mozilla::Module kXPCOMModule;
|
||||
|
||||
namespace {
|
||||
class EntryWrapper;
|
||||
class MutexLock;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mozilla {
|
||||
namespace xpcom {
|
||||
|
@ -147,15 +150,19 @@ class nsComponentManagerImpl final : public nsIComponentManager,
|
|||
|
||||
already_AddRefed<nsIFactory> LoadFactory(nsFactoryEntry* aEntry);
|
||||
|
||||
nsFactoryEntry* GetFactoryEntry(const char* aContractID,
|
||||
uint32_t aContractIDLen);
|
||||
nsFactoryEntry* GetFactoryEntry(const nsCID& aClass);
|
||||
|
||||
nsDataHashtable<nsIDPointerHashKey, nsFactoryEntry*> mFactories;
|
||||
nsDataHashtable<nsCStringHashKey, nsFactoryEntry*> mContractIDs;
|
||||
|
||||
SafeMutex mLock;
|
||||
|
||||
mozilla::Maybe<EntryWrapper> LookupByCID(const nsID& aCID);
|
||||
mozilla::Maybe<EntryWrapper> LookupByCID(const MutexLock&, const nsID& aCID);
|
||||
|
||||
mozilla::Maybe<EntryWrapper> LookupByContractID(
|
||||
const nsACString& aContractID);
|
||||
mozilla::Maybe<EntryWrapper> LookupByContractID(
|
||||
const MutexLock&, const nsACString& aContractID);
|
||||
|
||||
static void InitializeStaticModules();
|
||||
static void InitializeModuleLocations();
|
||||
|
||||
|
@ -276,7 +283,7 @@ class nsComponentManagerImpl final : public nsIComponentManager,
|
|||
private:
|
||||
~nsComponentManagerImpl();
|
||||
|
||||
nsresult GetServiceLocked(MutexLock& aLock, nsFactoryEntry& aEntry,
|
||||
nsresult GetServiceLocked(MutexLock& aLock, EntryWrapper& aEntry,
|
||||
const nsIID& aIID, void** aResult);
|
||||
};
|
||||
|
||||
|
@ -295,6 +302,9 @@ struct nsFactoryEntry {
|
|||
|
||||
already_AddRefed<nsIFactory> GetFactory();
|
||||
|
||||
nsresult CreateInstance(nsISupports* aOuter, const nsIID& aIID,
|
||||
void** aResult);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
|
||||
|
||||
const mozilla::Module::CIDEntry* mCIDEntry;
|
||||
|
|
Загрузка…
Ссылка в новой задаче