Quellcode-Bibliothek nsComponentManager.cpp
Sprache: C
/* -*- 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 <stdlib.h> #include"nscore.h" #include"nsISupports.h" #include"nspr.h" #include"nsCRT.h"// for atoll
class AutoIDString : public nsAutoCStringN<NSID_LENGTH> { public: explicit AutoIDString(const nsID& aID) {
SetLength(NSID_LENGTH - 1);
aID.ToProvidedString(
*reinterpret_cast<char(*)[NSID_LENGTH]>(BeginWriting()));
}
};
} // namespace
namespace mozilla { namespace xpcom {
using ProcessSelector = Module::ProcessSelector;
// Note: These must be kept in sync with the ProcessSelector definition in // Module.h. bool ProcessSelectorMatches(ProcessSelector aSelector) {
GeckoProcessType type = XRE_GetProcessType(); if (type == GeckoProcessType_GPU) { return !!(aSelector & Module::ALLOW_IN_GPU_PROCESS);
}
if (type == GeckoProcessType_RDD) { return !!(aSelector & Module::ALLOW_IN_RDD_PROCESS);
}
if (type == GeckoProcessType_Socket) { return !!(aSelector & (Module::ALLOW_IN_SOCKET_PROCESS));
}
if (type == GeckoProcessType_VR) { return !!(aSelector & Module::ALLOW_IN_VR_PROCESS);
}
if (type == GeckoProcessType_Utility) { return !!(aSelector & Module::ALLOW_IN_UTILITY_PROCESS);
}
if (type == GeckoProcessType_GMPlugin) { return !!(aSelector & Module::ALLOW_IN_GMPLUGIN_PROCESS);
}
// Only allow XPCOM modules which can be loaded in all processes to be loaded // in the IPDLUnitTest process. if (type == GeckoProcessType_IPDLUnitTest) { return size_t(aSelector) == Module::kMaxProcessSelector;
}
if (aSelector & Module::MAIN_PROCESS_ONLY) { return type == GeckoProcessType_Default;
} if (aSelector & Module::CONTENT_PROCESS_ONLY) { return type == GeckoProcessType_Content;
} returntrue;
}
/** * 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) {}
/** * 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(const nsIID& aIID, void** aResult) { if (mEntry.is<nsFactoryEntry*>()) { return mEntry.as<nsFactoryEntry*>()->CreateInstance(aIID, aResult);
} return mEntry.as<const StaticModule*>()->CreateInstance(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. * Currently always returns "<unknown module>".
*/
nsCString ModuleDescription() { return""_ns; }
// this is safe to call during InitXPCOM static already_AddRefed<nsIFile> GetLocationFromDirectoryService( constchar* aProp) {
nsCOMPtr<nsIProperties> directoryService;
nsDirectoryService::Create(NS_GET_IID(nsIProperties),
getter_AddRefs(directoryService));
auto* catMan = nsCategoryManager::GetSingleton(); for (constauto& cat : gStaticCategories) { for (constauto& entry : cat) { if (entry.Active()) {
catMan->AddCategoryEntry(cat.Name(), entry.Entry(), entry.Value());
}
}
}
// This needs to be initialized late enough, so that preferences service can // be accessed but before the IO service, and we want it in all process types.
xpc::ReadOnlyPage::Init();
bool loadChromeManifests; switch (XRE_GetProcessType()) { // We are going to assume that only a select few (see below) process types // want to load chrome manifests, and that any new process types will not // want to load them, because they're not going to be executing JS. default:
loadChromeManifests = false; break;
// XXX The check this code replaced implicitly let through all of these // process types, but presumably only the default (parent) and content // processes really need chrome manifests...? case GeckoProcessType_Default: case GeckoProcessType_Content:
loadChromeManifests = true; break;
}
if (loadChromeManifests) { // This needs to be called very early, before anything in nsLayoutModule is // used, and before any calls are made into the JS engine.
nsLayoutModuleInitialize();
mJSLoaderReady = true;
// The overall order in which chrome.manifests are expected to be treated // is the following: // - greDir's omni.ja or greDir // - appDir's omni.ja or appDir
// NB: The logging preference watcher needs to be registered late enough in // startup that it's okay to use the preference system, but also as soon as // possible so that log modules enabled via preferences are turned on as // early as possible. // // We can't initialize the preference watcher when the log module manager is // initialized, as a number of things attempt to start logging before the // preference system is initialized. // // The preference system is registered as a component so at this point during // component manager initialization we know it is setup and we can register // for notifications.
LogModulePrefWatcher::RegisterPrefWatcher();
// Unfortunately, we can't register the nsCategoryManager memory reporter // in its constructor (which is triggered by the GetSingleton() call // above) because the memory reporter manager isn't initialized at that // point. So we wait until now.
nsCategoryManager::GetSingleton()->InitMemoryReporter();
template <typename T> staticvoid AssertNotStackAllocated(T* aPtr) { // On all of our supported platforms, the stack grows down. Any address // located below the address of our argument is therefore guaranteed not to be // stack-allocated by the caller. // // For addresses above our argument, things get trickier. The main thread // stack is traditionally placed at the top of the program's address space, // but that is becoming less reliable as more and more systems adopt address // space layout randomization strategies, so we have to guess how much space // above our argument pointer we need to care about. // // On most systems, we're guaranteed at least several KiB at the top of each // stack for TLS. We'd probably be safe assuming at least 4KiB in the stack // segment above our argument address, but safer is... well, safer. // // For threads with huge stacks, it's theoretically possible that we could // wind up being passed a stack-allocated string from farther up the stack, // but this is a best-effort thing, so we'll assume we only care about the // immediate caller. For that case, max 2KiB per stack frame is probably a // reasonable guess most of the time, and is less than the ~4KiB that we // expect for TLS, so go with that to avoid the risk of bumping into heap // data just above the stack. #ifdef DEBUG static constexpr size_t kFuzz = 2048;
nsresult nsComponentManagerImpl::GetInterface(const nsIID& aUuid, void** aResult) {
NS_WARNING("This isn't supported"); // fall through to QI as anything QIable is a superset of what can be // got via the GetInterface() return QueryInterface(aUuid, aResult);
}
Maybe<EntryWrapper> nsComponentManagerImpl::LookupByContractID( const MonitorAutoLock&, 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->mFactory || entry->mServiceObject) { return Some(EntryWrapper(entry));
}
} return Nothing();
}
already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory( const nsCID& aClass) {
Maybe<EntryWrapper> e = LookupByCID(aClass); if (!e) { return nullptr;
}
/** * GetClassObject() * * Given a classID, this finds the singleton ClassObject that implements the * CID. Returns an interface of type aIID off the singleton classobject.
*/
NS_IMETHODIMP
nsComponentManagerImpl::GetClassObject(const nsCID& aClass, const nsIID& aIID, void** aResult) {
nsresult rv;
/** * CreateInstance() * * Create an instance of an object that implements an interface and belongs * to the implementation aClass using the factory. The factory is immediately * released and not held onto for any longer.
*/
NS_IMETHODIMP
nsComponentManagerImpl::CreateInstance(const nsCID& aClass, const nsIID& aIID, void** aResult) { // test this first, since there's no point in creating a component during // shutdown -- whether it's available or not would depend on the order it // occurs in the list if (gXPCOMShuttingDown) { // When processing shutdown, don't process new GetService() requests #ifdef SHOW_DENIED_ON_SHUTDOWN
fprintf(stderr, "Creating new instance on shutdown. Denied.\n" " CID: %s\n IID: %s\n",
AutoIDString(aClass).get(), AutoIDString(aIID).get()); #endif/* SHOW_DENIED_ON_SHUTDOWN */ return NS_ERROR_UNEXPECTED;
}
if (!aResult) { return NS_ERROR_NULL_POINTER;
}
*aResult = nullptr;
Maybe<EntryWrapper> entry = LookupByCID(aClass);
if (!entry) { return NS_ERROR_FACTORY_NOT_REGISTERED;
}
#ifdef SHOW_CI_ON_EXISTING_SERVICE if (entry->ServiceInstance()) {
nsAutoCString message;
message = "You are calling CreateInstance \""_ns + AutoIDString(aClass) + "\" when a service forthis CID already exists!"_ns;
NS_ERROR(message.get());
} #endif
nsresult rv;
nsCOMPtr<nsIFactory> factory = entry->GetFactory(); if (factory) {
rv = factory->CreateInstance(aIID, aResult); if (NS_SUCCEEDED(rv) && !*aResult) {
NS_ERROR("Factory did not return an object but returned success!");
rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
}
} else { // Translate error values
rv = NS_ERROR_FACTORY_NOT_REGISTERED;
}
/** * CreateInstanceByContractID() * * A variant of CreateInstance() that creates an instance of the object that * implements the interface aIID and whose implementation has a contractID * aContractID. * * This is only a convenience routine that turns around can calls the * CreateInstance() with classid and iid.
*/
NS_IMETHODIMP
nsComponentManagerImpl::CreateInstanceByContractID(constchar* aContractID, const nsIID& aIID, void** aResult) { if (NS_WARN_IF(!aContractID)) { return NS_ERROR_INVALID_ARG;
}
// test this first, since there's no point in creating a component during // shutdown -- whether it's available or not would depend on the order it // occurs in the list if (gXPCOMShuttingDown) { // When processing shutdown, don't process new GetService() requests #ifdef SHOW_DENIED_ON_SHUTDOWN
fprintf(stderr, "Creating new instance on shutdown. Denied.\n" " ContractID: %s\n IID: %s\n",
aContractID, AutoIDString(aIID).get()); #endif/* SHOW_DENIED_ON_SHUTDOWN */ return NS_ERROR_UNEXPECTED;
}
if (!aResult) { return NS_ERROR_NULL_POINTER;
}
*aResult = nullptr;
if (!entry) { return NS_ERROR_FACTORY_NOT_REGISTERED;
}
#ifdef SHOW_CI_ON_EXISTING_SERVICE if (entry->ServiceInstance()) {
nsAutoCString message;
message = "You are calling CreateInstance \""_ns +
nsDependentCString(aContractID) +
nsLiteralCString( "\" when a service forthis CID already exists! " "Add it to abusedContracts to track down the service consumer.");
NS_ERROR(message.get());
} #endif
nsresult rv;
nsCOMPtr<nsIFactory> factory = entry->GetFactory(); if (factory) {
rv = factory->CreateInstance(aIID, aResult); if (NS_SUCCEEDED(rv) && !*aResult) {
NS_ERROR("Factory did not return an object but returned success!");
rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
}
} else { // Translate error values
rv = NS_ERROR_FACTORY_NOT_REGISTERED;
}
for (constauto& module : gStaticModules) {
module.SetServiceInstance(nullptr);
}
return NS_OK;
}
// This should only ever be called within the monitor!
nsComponentManagerImpl::PendingServiceInfo*
nsComponentManagerImpl::AddPendingService(const nsCID& aServiceCID,
PRThread* aThread) {
PendingServiceInfo* newInfo = mPendingServices.AppendElement(); if (newInfo) {
newInfo->cid = &aServiceCID;
newInfo->thread = aThread;
} return newInfo;
}
// This should only ever be called within the monitor! void nsComponentManagerImpl::RemovePendingService(MonitorAutoLock& aLock, const nsCID& aServiceCID) {
uint32_t pendingCount = mPendingServices.Length(); for (uint32_t index = 0; index < pendingCount; ++index) { const PendingServiceInfo& info = mPendingServices.ElementAt(index); if (info.cid->Equals(aServiceCID)) {
mPendingServices.RemoveElementAt(index);
aLock.NotifyAll(); return;
}
}
}
// This should only ever be called within the monitor!
PRThread* nsComponentManagerImpl::GetPendingServiceThread( const nsCID& aServiceCID) const {
uint32_t pendingCount = mPendingServices.Length(); for (uint32_t index = 0; index < pendingCount; ++index) { const PendingServiceInfo& info = mPendingServices.ElementAt(index); if (info.cid->Equals(aServiceCID)) { return info.thread;
}
} return nullptr;
}
if (auto* service = aEntry.ServiceInstance()) {
aLock.reset(); return service->QueryInterface(aIID, aResult);
}
PRThread* currentPRThread = PR_GetCurrentThread();
MOZ_ASSERT(currentPRThread, "This should never be null!");
PRThread* pendingPRThread; while ((pendingPRThread = GetPendingServiceThread(aEntry.CID()))) { if (pendingPRThread == currentPRThread) {
NS_ERROR("Recursive GetService!"); return NS_ERROR_NOT_AVAILABLE;
}
aLock->Wait();
}
// 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 (auto* service = aEntry.ServiceInstance()) {
aLock.reset(); return service->QueryInterface(aIID, aResult);
}
DebugOnly<PendingServiceInfo*> newInfo =
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 // CreateInstance, because it invokes user code which could try to re-enter // the service manager:
nsCOMPtr<nsISupports> service; auto cleanup = MakeScopeExit([&]() { // `service` must be released after the lock is released, so if we fail and // still have a reference, release the lock before releasing it. if (service) {
MOZ_ASSERT(aLock.isSome());
aLock.reset();
service = nullptr;
}
});
nsresult rv;
mLock.AssertCurrentThreadOwns();
{
MonitorAutoUnlock unlock(mLock);
AUTO_PROFILER_MARKER_TEXT( "GetService", OTHER, MarkerStack::Capture(),
nsDependentCString(nsIDToCString(aEntry.CID()).get()));
rv = aEntry.CreateInstance(aIID, getter_AddRefs(service));
} if (NS_SUCCEEDED(rv) && !service) {
NS_ERROR("Factory did not return an object but returned success"); return NS_ERROR_SERVICE_NOT_AVAILABLE;
}
#ifdef DEBUG
pendingPRThread = GetPendingServiceThread(aEntry.CID());
MOZ_ASSERT(pendingPRThread == currentPRThread, "Pending service array has been changed!"); #endif
MOZ_ASSERT(aLock.isSome());
RemovePendingService(*aLock, aEntry.CID());
if (NS_FAILED(rv)) { return rv;
}
NS_ASSERTION(!aEntry.ServiceInstance(), "Created two instances of a service!");
NS_IMETHODIMP
nsComponentManagerImpl::GetService(const nsCID& aClass, const nsIID& aIID, void** aResult) { // test this first, since there's no point in returning a service during // shutdown -- whether it's available or not would depend on the order it // occurs in the list if (gXPCOMShuttingDown) { // When processing shutdown, don't process new GetService() requests #ifdef SHOW_DENIED_ON_SHUTDOWN
fprintf(stderr, "Getting service on shutdown. Denied.\n" " CID: %s\n IID: %s\n",
AutoIDString(aClass).get(), AutoIDString(aIID).get()); #endif/* SHOW_DENIED_ON_SHUTDOWN */ return NS_ERROR_UNEXPECTED;
}
// test this first, since there's no point in returning a service during // shutdown -- whether it's available or not would depend on the order it // occurs in the list if (gXPCOMShuttingDown) { // When processing shutdown, don't process new GetService() requests #ifdef SHOW_DENIED_ON_SHUTDOWN
fprintf(stderr, "Getting service on shutdown. Denied.\n" " CID: %s\n IID: %s\n",
AutoIDString(entry.CID()).get(), AutoIDString(aIID).get()); #endif/* SHOW_DENIED_ON_SHUTDOWN */ return NS_ERROR_UNEXPECTED;
}
Maybe<EntryWrapper> wrapper; if (entry.Overridable()) { // If we expect this service to be overridden by test code, we need to look // it up by contract ID every time.
wrapper = LookupByContractID(*lock, entry.ContractID()); if (!wrapper) { return NS_ERROR_FACTORY_NOT_REGISTERED;
}
} elseif (!entry.Active()) { return NS_ERROR_FACTORY_NOT_REGISTERED;
} else {
wrapper.emplace(&entry);
} return GetServiceLocked(lock, *wrapper, aIID, aResult);
}
NS_IMETHODIMP
nsComponentManagerImpl::IsServiceInstantiated(const nsCID& aClass, const nsIID& aIID, bool* aResult) { // Now we want to get the service if we already got it. If not, we don't want // to create an instance of it. mmh!
// test this first, since there's no point in returning a service during // shutdown -- whether it's available or not would depend on the order it // occurs in the list if (gXPCOMShuttingDown) { // When processing shutdown, don't process new GetService() requests #ifdef SHOW_DENIED_ON_SHUTDOWN
fprintf(stderr, "Checking for service on shutdown. Denied.\n" " CID: %s\n IID: %s\n",
AutoIDString(aClass).get(), AutoIDString(aIID).get()); #endif/* SHOW_DENIED_ON_SHUTDOWN */ return NS_ERROR_UNEXPECTED;
}
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;
}
}
*aResult = false; return NS_OK;
}
NS_IMETHODIMP
nsComponentManagerImpl::IsServiceInstantiatedByContractID( constchar* aContractID, const nsIID& aIID, bool* aResult) { // Now we want to get the service if we already got it. If not, we don't want // to create an instance of it. mmh!
// test this first, since there's no point in returning a service during // shutdown -- whether it's available or not would depend on the order it // occurs in the list if (gXPCOMShuttingDown) { // When processing shutdown, don't process new GetService() requests #ifdef SHOW_DENIED_ON_SHUTDOWN
fprintf(stderr, "Checking for service on shutdown. Denied.\n" " ContractID: %s\n IID: %s\n",
aContractID, AutoIDString(aIID).get()); #endif/* SHOW_DENIED_ON_SHUTDOWN */ return NS_ERROR_UNEXPECTED;
}
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;
}
}
*aResult = false; return NS_OK;
}
NS_IMETHODIMP
nsComponentManagerImpl::GetServiceByContractID(constchar* aContractID, const nsIID& aIID, void** aResult) { // test this first, since there's no point in returning a service during // shutdown -- whether it's available or not would depend on the order it // occurs in the list if (gXPCOMShuttingDown) { // When processing shutdown, don't process new GetService() requests #ifdef SHOW_DENIED_ON_SHUTDOWN
fprintf(stderr, "Getting service on shutdown. Denied.\n" " ContractID: %s\n IID: %s\n",
aContractID, AutoIDString(aIID).get()); #endif/* SHOW_DENIED_ON_SHUTDOWN */ return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
nsComponentManagerImpl::RegisterFactory(const nsCID& aClass, constchar* aName, constchar* aContractID,
nsIFactory* aFactory) { if (!aFactory) { // If a null factory is passed in, this call just wants to reset // the contract ID to point to an existing CID entry. if (!aContractID) { return NS_ERROR_INVALID_ARG;
}
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. if (StaticComponents::InvalidateContractID(contractID, false)) {
mContractIDs.Remove(contractID); return NS_OK;
}
} return NS_ERROR_FACTORY_NOT_REGISTERED;
}
auto f = MakeUnique<nsFactoryEntry>(aClass, aFactory);
MonitorAutoLock lock(mLock); return mFactories.WithEntryHandle(&f->mCID, [&](auto&& entry) { if (entry) { return NS_ERROR_FACTORY_EXISTS;
} if (StaticComponents::LookupByCID(f->mCID)) { return NS_ERROR_FACTORY_EXISTS;
} if (aContractID) {
nsDependentCString contractID(aContractID);
mContractIDs.InsertOrUpdate(contractID, f.get()); // We allow dynamically-registered contract IDs to override static // entries, so invalidate any static entry for this contract ID.
StaticComponents::InvalidateContractID(contractID);
}
entry.Insert(f.release());
return NS_OK;
});
}
NS_IMETHODIMP
nsComponentManagerImpl::UnregisterFactory(const nsCID& aClass,
nsIFactory* aFactory) { // Don't release the dying factory or service object until releasing // the component manager monitor.
nsCOMPtr<nsIFactory> dyingFactory;
nsCOMPtr<nsISupports> dyingServiceObject;
{
MonitorAutoLock lock(mLock); 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;
}
entry.Remove();
// This might leave a stale contractid -> factory mapping in // place, so null out the factory entry (see // nsFactoryEntry::GetFactory)
f->mFactory.swap(dyingFactory);
f->mServiceObject.swap(dyingServiceObject);
}
NS_IMETHODIMP
nsComponentManagerImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) {
MOZ_COLLECT_REPORT("explicit/xpcom/component-manager", KIND_HEAP, UNITS_BYTES,
SizeOfIncludingThis(ComponentManagerMallocSizeOf), "Memory used for the XPCOM component manager.");
return NS_OK;
}
size_t nsComponentManagerImpl::SizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const {
size_t n = aMallocSizeOf(this);
n += mFactories.ShallowSizeOfExcludingThis(aMallocSizeOf); for (constauto& data : mFactories.Values()) {
n += data->SizeOfIncludingThis(aMallocSizeOf);
}
n += mContractIDs.ShallowSizeOfExcludingThis(aMallocSizeOf); for (constauto& key : mContractIDs.Keys()) { // We don't measure the nsFactoryEntry data because it's owned by // mFactories (which is measured above).
n += key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
}
if (sModuleLocations) {
n += sModuleLocations->ShallowSizeOfIncludingThis(aMallocSizeOf);
}
n += mPendingServices.ShallowSizeOfExcludingThis(aMallocSizeOf);
// Measurement of the following members may be added later if DMD finds it is // worthwhile: // - mMon // - sModuleLocations' entries
// Expose some important global interfaces to rust for the rust xpcom API. These // methods return a non-owning reference to the component manager, which should // live for the lifetime of XPCOM. extern"C" {
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.113Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.