Browse Source

reinstate XPCOM infra

Bill Gianopoulos 2 years ago
parent
commit
612dcaa647

+ 902 - 0
comm-central/patches/TOP-PLASTER-backout-1773770-12.patch

@@ -0,0 +1,902 @@
+# HG changeset patch
+# User Bill Gianopoulos <wgianopoulos@gmail.com>
+# Date 1657108142 0
+Reinstate XPCOM infrastructure removed in bug 1773770 part 12.
+Bug 1773770: Part 12 - Remove XPCOM Module infrastructure.
+
+diff --git a/xpcom/build/nsXPCOM.h b/xpcom/build/nsXPCOM.h
+--- a/xpcom/build/nsXPCOM.h
++++ b/xpcom/build/nsXPCOM.h
+@@ -34,16 +34,19 @@ DECL_CLASS(nsIDebug2);
+ extern bool gXPCOMShuttingDown;
+ extern mozilla::Atomic<bool, mozilla::SequentiallyConsistent>
+     gXPCOMThreadsShutDown;
+ extern bool gXPCOMMainThreadEventsAreDoomed;
+ #endif
+ 
+ #ifdef __cplusplus
+ #  include "nsStringFwd.h"
++namespace mozilla {
++struct Module;
++}  // namespace mozilla
+ #endif
+ 
+ /**
+  * Initialises XPCOM. You must call one of the NS_InitXPCOM methods
+  * before proceeding to use xpcom. The one exception is that you may
+  * call NS_NewLocalFile to create a nsIFile.
+  *
+  * @note Use <CODE>NS_NewLocalFile</CODE> or <CODE>NS_NewNativeLocalFile</CODE>
+diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h
+--- a/xpcom/build/nsXULAppAPI.h
++++ b/xpcom/build/nsXULAppAPI.h
+@@ -23,16 +23,17 @@ class nsIFile;
+ class nsISupports;
+ struct JSContext;
+ struct XREChildData;
+ struct XREShellData;
+ 
+ namespace mozilla {
+ class XREAppData;
+ struct BootstrapConfig;
++struct Module;
+ }  // namespace mozilla
+ 
+ /**
+  * A directory service key which provides the platform-correct "application
+  * data" directory as follows, where $name and $vendor are as defined above and
+  * $vendor is optional:
+  *
+  * Windows:
+@@ -214,16 +215,21 @@ int XRE_main(int argc, char* argv[], con
+ nsresult XRE_GetFileFromPath(const char* aPath, nsIFile** aResult);
+ 
+ /**
+  * Get the path of the running application binary and store it in aResult.
+  */
+ nsresult XRE_GetBinaryPath(nsIFile** aResult);
+ 
+ /**
++ * Get the static module built in to libxul.
++ */
++const mozilla::Module* XRE_GetStaticModule();
++
++/**
+  * Lock a profile directory using platform-specific semantics.
+  *
+  * @param aDirectory  The profile directory to lock.
+  * @param aLockObject An opaque lock object. The directory will remain locked
+  *                    as long as the XPCOM reference is held.
+  */
+ nsresult XRE_LockProfileDirectory(nsIFile* aDirectory,
+                                   nsISupports** aLockObject);
+@@ -247,16 +253,23 @@ nsresult XRE_LockProfileDirectory(nsIFil
+  * a given process. Use XRE_TermEmbedding to clean up and free
+  * resources allocated by XRE_InitEmbedding.
+  */
+ 
+ nsresult XRE_InitEmbedding2(nsIFile* aLibXULDirectory, nsIFile* aAppDirectory,
+                             nsIDirectoryServiceProvider* aAppDirProvider);
+ 
+ /**
++ * Register static XPCOM component information.
++ * This method may be called at any time before or after XRE_main or
++ * XRE_InitEmbedding.
++ */
++nsresult XRE_AddStaticComponent(const mozilla::Module* aComponent);
++
++/**
+  * Register XPCOM components found in an array of files/directories.
+  * This method may be called at any time before or after XRE_main or
+  * XRE_InitEmbedding.
+  *
+  * @param aFiles An array of files or directories.
+  * @param aFileCount the number of items in the aFiles array.
+  * @note appdir/components is registered automatically.
+  *
+diff --git a/xpcom/components/GenericFactory.h b/xpcom/components/GenericFactory.h
+--- a/xpcom/components/GenericFactory.h
++++ b/xpcom/components/GenericFactory.h
+@@ -2,30 +2,32 @@
+ /* 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_GenericFactory_h
+ #define mozilla_GenericFactory_h
+ 
+-#include "nsIFactory.h"
++#include "mozilla/Attributes.h"
++
++#include "mozilla/Module.h"
+ 
+ namespace mozilla {
+ 
+ /**
+  * A generic factory which uses a constructor function to create instances.
+  * This class is intended for use by the component manager and the generic
+  * module.
+  */
+ class GenericFactory final : public nsIFactory {
+-  ~GenericFactory() = default;
++  ~GenericFactory() {}
+ 
+  public:
+-  typedef nsresult (*ConstructorProcPtr)(const nsIID& aIID, void** aResult);
++  typedef Module::ConstructorProcPtr ConstructorProcPtr;
+ 
+   NS_DECL_THREADSAFE_ISUPPORTS
+   NS_DECL_NSIFACTORY
+ 
+   explicit GenericFactory(ConstructorProcPtr aCtor) : mCtor(aCtor) {
+     NS_ASSERTION(mCtor, "GenericFactory with no constructor");
+   }
+ 
+diff --git a/xpcom/components/Module.h b/xpcom/components/Module.h
+--- a/xpcom/components/Module.h
++++ b/xpcom/components/Module.h
+@@ -3,22 +3,41 @@
+ /* 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_Module_h
+ #define mozilla_Module_h
+ 
+ #include "nscore.h"
++#include "nsID.h"
++#include "nsIFactory.h"
++#include "nsCOMPtr.h"  // for already_AddRefed
+ 
+ namespace mozilla {
+ 
+-namespace Module {
++/**
++ * A module implements one or more XPCOM components. This structure is used
++ * for binary modules.
++ */
++struct Module {
++  static const unsigned int kVersion = 104;
++
++  struct CIDEntry;
++
++  typedef already_AddRefed<nsIFactory> (*GetFactoryProcPtr)(
++      const Module& module, const CIDEntry& entry);
++
++  typedef nsresult (*ConstructorProcPtr)(const nsIID& aIID, void** aResult);
++
++  typedef nsresult (*LoadFuncPtr)();
++  typedef void (*UnloadFuncPtr)();
++
+   /**
+-   * This selector allows components to be marked so that they're only loaded
++   * This selector allows CIDEntrys to be marked so that they're only loaded
+    * into certain kinds of processes. Selectors can be combined.
+    */
+   // Note: This must be kept in sync with the selector matching in
+   // nsComponentManager.cpp.
+   enum ProcessSelector {
+     ANY_PROCESS = 0x0,
+     MAIN_PROCESS_ONLY = 0x1,
+     CONTENT_PROCESS_ONLY = 0x2,
+@@ -64,13 +83,83 @@ namespace Module {
+    * not loaded when in backgroundtask mode.
+    */
+   // Note: This must be kept in sync with the selector matching in
+   // StaticComponents.cpp.in.
+   enum BackgroundTasksSelector {
+     NO_TASKS = 0x0,
+     ALL_TASKS = 0xFFFF,
+   };
++
++  /**
++   * The constructor callback is an implementation detail of the default binary
++   * loader and may be null.
++   */
++  struct CIDEntry {
++    const nsCID* cid;
++    bool service;
++    GetFactoryProcPtr getFactoryProc;
++    ConstructorProcPtr constructorProc;
++    ProcessSelector processSelector;
++  };
++
++  struct ContractIDEntry {
++    const char* contractid;
++    nsID const* cid;
++    ProcessSelector processSelector;
++  };
++
++  struct CategoryEntry {
++    const char* category;
++    const char* entry;
++    const char* value;
++  };
++
++  /**
++   * Binary compatibility check, should be kModuleVersion.
++   */
++  unsigned int mVersion;
++
++  /**
++   * An array of CIDs (class IDs) implemented by this module. The final entry
++   * should be { nullptr }.
++   */
++  const CIDEntry* mCIDs;
++
++  /**
++   * An array of mappings from contractid to CID. The final entry should
++   * be { nullptr }.
++   */
++  const ContractIDEntry* mContractIDs;
++
++  /**
++   * An array of category manager entries. The final entry should be
++   * { nullptr }.
++   */
++  const CategoryEntry* mCategoryEntries;
++
++  /**
++   * When the component manager tries to get the factory for a CID, it first
++   * checks for this module-level getfactory callback. If this function is
++   * not implemented, it checks the CIDEntry getfactory callback. If that is
++   * also nullptr, a generic factory is generated using the CIDEntry
++   * constructor callback which must be non-nullptr.
++   */
++  GetFactoryProcPtr getFactoryProc;
++
++  /**
++   * Optional Function which are called when this module is loaded and
++   * at shutdown. These are not C++ constructor/destructors to avoid
++   * calling them too early in startup or too late in shutdown.
++   */
++  LoadFuncPtr loadProc;
++  UnloadFuncPtr unloadProc;
++
++  /**
++   * Optional flags which control whether the module loads on a process-type
++   * basis.
++   */
++  ProcessSelector selector;
+ };
+ 
+ }  // namespace mozilla
+ 
+ #endif  // mozilla_Module_h
+diff --git a/xpcom/components/ModuleUtils.h b/xpcom/components/ModuleUtils.h
+--- a/xpcom/components/ModuleUtils.h
++++ b/xpcom/components/ModuleUtils.h
+@@ -4,17 +4,16 @@
+  * 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_GenericModule_h
+ #define mozilla_GenericModule_h
+ 
+ #include <type_traits>
+ 
+-#include "mozilla/AlreadyAddRefed.h"
+ #include "mozilla/Attributes.h"
+ #include "mozilla/Module.h"
+ 
+ #define NS_GENERIC_FACTORY_CONSTRUCTOR(_InstanceClass)                         \
+   static nsresult _InstanceClass##Constructor(REFNSIID aIID, void** aResult) { \
+     RefPtr<_InstanceClass> inst;                                               \
+                                                                                \
+     *aResult = nullptr;                                                        \
+diff --git a/xpcom/components/nsComponentManager.cpp b/xpcom/components/nsComponentManager.cpp
+--- a/xpcom/components/nsComponentManager.cpp
++++ b/xpcom/components/nsComponentManager.cpp
+@@ -158,17 +158,17 @@ class MOZ_STACK_CLASS EntryWrapper final
+ #define MATCH(type, ifFactory, ifStatic)                     \
+   struct Matcher {                                           \
+     type operator()(nsFactoryEntry* entry) { ifFactory; }    \
+     type operator()(const StaticModule* entry) { ifStatic; } \
+   };                                                         \
+   return mEntry.match((Matcher()))
+ 
+   const nsID& CID() {
+-    MATCH(const nsID&, return entry->mCID, return entry->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());
+   }
+ 
+   /**
+@@ -197,21 +197,24 @@ class MOZ_STACK_CLASS EntryWrapper final
+       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>".
++   * Returns the description string for the module this entry belongs to. For
++   * static entries, always returns "<unknown module>".
+    */
+   nsCString ModuleDescription() {
+-    return "<unknown module>"_ns;
++    MATCH(nsCString,
++          return entry->mModule ? entry->mModule->Description()
++                                : "<unknown module>"_ns,
++          return "<unknown module>"_ns);
+   }
+ 
+  private:
+   Variant<nsFactoryEntry*, const StaticModule*> mEntry;
+ };
+ 
+ }  // namespace
+ 
+@@ -263,16 +266,27 @@ nsresult nsComponentManagerImpl::Create(
+ static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 8;
+ 
+ nsComponentManagerImpl::nsComponentManagerImpl()
+     : mFactories(CONTRACTID_HASHTABLE_INITIAL_LENGTH),
+       mContractIDs(CONTRACTID_HASHTABLE_INITIAL_LENGTH),
+       mLock("nsComponentManagerImpl.mLock"),
+       mStatus(NOT_INITIALIZED) {}
+ 
++static nsTArray<const mozilla::Module*>* sExtraStaticModules;
++
++/* static */
++void nsComponentManagerImpl::InitializeStaticModules() {
++  if (sExtraStaticModules) {
++    return;
++  }
++
++  sExtraStaticModules = new nsTArray<const mozilla::Module*>;
++}
++
+ nsTArray<nsComponentManagerImpl::ComponentLocation>*
+     nsComponentManagerImpl::sModuleLocations;
+ 
+ /* static */
+ void nsComponentManagerImpl::InitializeModuleLocations() {
+   if (sModuleLocations) {
+     return;
+   }
+@@ -331,18 +345,24 @@ nsresult nsComponentManagerImpl::Init() 
+   }
+ 
+   MOZ_ASSERT(NOT_INITIALIZED == mStatus);
+ 
+   nsCOMPtr<nsIFile> greDir = GetLocationFromDirectoryService(NS_GRE_DIR);
+   nsCOMPtr<nsIFile> appDir =
+       GetLocationFromDirectoryService(NS_XPCOM_CURRENT_PROCESS_DIR);
+ 
++  InitializeStaticModules();
++
+   nsCategoryManager::GetSingleton()->SuppressNotifications(true);
+ 
++  for (uint32_t i = 0; i < sExtraStaticModules->Length(); ++i) {
++    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());
+       }
+     }
+   }
+@@ -443,16 +463,18 @@ nsresult nsComponentManagerImpl::Init() 
+   MOZ_ASSERT(!XRE_IsContentProcess() ||
+              CONTRACTID_HASHTABLE_INITIAL_LENGTH <= 8 ||
+                  mFactories.Count() > CONTRACTID_HASHTABLE_INITIAL_LENGTH / 3,
+              "Initial component hashtable size is too large");
+ 
+   return NS_OK;
+ }
+ 
++static const int kModuleVersionWithSelector = 51;
++
+ template <typename T>
+ static void AssertNotMallocAllocated(T* aPtr) {
+ #if defined(DEBUG) && defined(MOZ_MEMORY)
+   jemalloc_ptr_info_t info;
+   jemalloc_ptr_info((void*)aPtr, &info);
+   MOZ_ASSERT(info.tag == TagUnknown);
+ #endif
+ }
+@@ -483,16 +505,150 @@ static void AssertNotStackAllocated(T* a
+ #ifdef DEBUG
+   static constexpr size_t kFuzz = 2048;
+ 
+   MOZ_ASSERT(uintptr_t(aPtr) < uintptr_t(&aPtr) ||
+              uintptr_t(aPtr) > uintptr_t(&aPtr) + kFuzz);
+ #endif
+ }
+ 
++static inline nsCString AsLiteralCString(const char* aStr) {
++  AssertNotMallocAllocated(aStr);
++  AssertNotStackAllocated(aStr);
++
++  nsCString str;
++  str.AssignLiteral(aStr, strlen(aStr));
++  return str;
++}
++
++void nsComponentManagerImpl::RegisterModule(const mozilla::Module* aModule) {
++  mLock.AssertNotCurrentThreadOwns();
++
++  if (aModule->mVersion >= kModuleVersionWithSelector &&
++      !ProcessSelectorMatches(aModule->selector)) {
++    return;
++  }
++
++  {
++    // Scope the monitor so that we don't hold it while calling into the
++    // category manager.
++    MonitorAutoLock lock(mLock);
++
++    KnownModule* m = new KnownModule(aModule);
++    mKnownStaticModules.AppendElement(m);
++
++    if (aModule->mCIDs) {
++      const mozilla::Module::CIDEntry* entry;
++      for (entry = aModule->mCIDs; entry->cid; ++entry) {
++        RegisterCIDEntryLocked(entry, m);
++      }
++    }
++
++    if (aModule->mContractIDs) {
++      const mozilla::Module::ContractIDEntry* entry;
++      for (entry = aModule->mContractIDs; entry->contractid; ++entry) {
++        RegisterContractIDLocked(entry);
++      }
++      MOZ_ASSERT(!entry->cid, "Incorrectly terminated contract list");
++    }
++  }
++
++  if (aModule->mCategoryEntries) {
++    const mozilla::Module::CategoryEntry* entry;
++    for (entry = aModule->mCategoryEntries; entry->category; ++entry)
++      nsCategoryManager::GetSingleton()->AddCategoryEntry(
++          AsLiteralCString(entry->category), AsLiteralCString(entry->entry),
++          AsLiteralCString(entry->value));
++  }
++}
++
++void nsComponentManagerImpl::RegisterCIDEntryLocked(
++    const mozilla::Module::CIDEntry* aEntry, KnownModule* aModule) {
++  mLock.AssertCurrentThreadOwns();
++
++  if (!ProcessSelectorMatches(aEntry->processSelector)) {
++    return;
++  }
++
++#ifdef DEBUG
++  // If we're still in the static initialization phase, check that we're not
++  // registering something that was already registered.
++  if (mStatus != NORMAL) {
++    if (StaticComponents::LookupByCID(*aEntry->cid)) {
++      MOZ_CRASH_UNSAFE_PRINTF(
++          "While registering XPCOM module %s, trying to re-register CID '%s' "
++          "already registered by a static component.",
++          aModule->Description().get(), AutoIDString(*aEntry->cid).get());
++    }
++  }
++#endif
++
++  mFactories.WithEntryHandle(aEntry->cid, [&](auto&& entry) {
++    mLock.AssertCurrentThreadOwns();
++    if (entry) {
++      nsFactoryEntry* f = entry.Data();
++      NS_WARNING("Re-registering a CID?");
++
++      nsCString existing;
++      if (f->mModule) {
++        existing = f->mModule->Description();
++      } else {
++        existing = "<unknown module>";
++      }
++      MonitorAutoUnlock unlock(mLock);
++      LogMessage(
++          "While registering XPCOM module %s, trying to re-register CID '%s' "
++          "already registered by %s.",
++          aModule->Description().get(), AutoIDString(*aEntry->cid).get(),
++          existing.get());
++    } else {
++      entry.Insert(new nsFactoryEntry(aEntry, aModule));
++    }
++  });
++}
++
++void nsComponentManagerImpl::RegisterContractIDLocked(
++    const mozilla::Module::ContractIDEntry* aEntry) {
++  mLock.AssertCurrentThreadOwns();
++
++  if (!ProcessSelectorMatches(aEntry->processSelector)) {
++    return;
++  }
++
++#ifdef DEBUG
++  // If we're still in the static initialization phase, check that we're not
++  // registering something that was already registered.
++  if (mStatus != NORMAL) {
++    if (const StaticModule* module = StaticComponents::LookupByContractID(
++            nsAutoCString(aEntry->contractid))) {
++      MOZ_CRASH_UNSAFE_PRINTF(
++          "Could not map contract ID '%s' to CID %s because it is already "
++          "mapped to CID %s.",
++          aEntry->contractid, AutoIDString(*aEntry->cid).get(),
++          AutoIDString(module->CID()).get());
++    }
++  }
++#endif
++
++  nsFactoryEntry* f = mFactories.Get(aEntry->cid);
++  if (!f) {
++    NS_WARNING("No CID found when attempting to map contract ID");
++
++    MonitorAutoUnlock unlock(mLock);
++    LogMessage(
++        "Could not map contract ID '%s' to CID %s because no implementation of "
++        "the CID is registered.",
++        aEntry->contractid, AutoIDString(*aEntry->cid).get());
++
++    return;
++  }
++
++  mContractIDs.InsertOrUpdate(AsLiteralCString(aEntry->contractid), f);
++}
++
+ static void DoRegisterManifest(NSLocationType aType, FileLocation& aFile,
+                                bool aChromeOnly) {
+   auto result = URLPreloader::Read(aFile);
+   if (result.isOk()) {
+     nsCString buf(result.unwrap());
+     ParseManifest(aType, aFile, buf.BeginWriting(), aChromeOnly);
+   } else if (NS_BOOTSTRAPPED_LOCATION != aType) {
+     nsCString uri;
+@@ -532,33 +688,57 @@ void nsComponentManagerImpl::RereadChrom
+   }
+ 
+   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+   if (obs) {
+     obs->NotifyObservers(nullptr, "chrome-manifests-loaded", nullptr);
+   }
+ }
+ 
++bool nsComponentManagerImpl::KnownModule::Load() {
++  if (mFailed) {
++    return false;
++  }
++  MOZ_ASSERT(mModule);
++  if (!mLoaded) {
++    if (mModule->loadProc) {
++      nsresult rv = mModule->loadProc();
++      if (NS_FAILED(rv)) {
++        mFailed = true;
++        return false;
++      }
++    }
++    mLoaded = true;
++  }
++  return true;
++}
++
++nsCString nsComponentManagerImpl::KnownModule::Description() const {
++  return "<static module>"_ns;
++}
++
+ nsresult nsComponentManagerImpl::Shutdown(void) {
+   MOZ_ASSERT(NORMAL == mStatus);
+ 
+   mStatus = SHUTDOWN_IN_PROGRESS;
+ 
+   // Shutdown the component manager
+   MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+           ("nsComponentManager: Beginning Shutdown."));
+ 
+   UnregisterWeakMemoryReporter(this);
+ 
+   // Release all cached factories
+   mContractIDs.Clear();
+   mFactories.Clear();  // XXX release the objects, don't just clear
++  mKnownStaticModules.Clear();
+ 
+   StaticComponents::Shutdown();
+ 
++  delete sExtraStaticModules;
+   delete sModuleLocations;
+ 
+   mStatus = SHUTDOWN_COMPLETE;
+ 
+   MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+           ("nsComponentManager: Shutdown complete."));
+ 
+   return NS_OK;
+@@ -614,17 +794,17 @@ Maybe<EntryWrapper> nsComponentManagerIm
+   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) {
++    if (entry->mModule || entry->mFactory || entry->mServiceObject) {
+       return Some(EntryWrapper(entry));
+     }
+   }
+   return Nothing();
+ }
+ 
+ already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
+     const nsCID& aClass) {
+@@ -1197,21 +1377,21 @@ nsComponentManagerImpl::RegisterFactory(
+       }
+     }
+     return NS_ERROR_FACTORY_NOT_REGISTERED;
+   }
+ 
+   auto f = MakeUnique<nsFactoryEntry>(aClass, aFactory);
+ 
+   MonitorAutoLock lock(mLock);
+-  return mFactories.WithEntryHandle(&f->mCID, [&](auto&& entry) {
++  return mFactories.WithEntryHandle(f->mCIDEntry->cid, [&](auto&& entry) {
+     if (entry) {
+       return NS_ERROR_FACTORY_EXISTS;
+     }
+-    if (StaticComponents::LookupByCID(f->mCID)) {
++    if (StaticComponents::LookupByCID(*f->mCIDEntry->cid)) {
+       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);
+@@ -1356,57 +1536,112 @@ size_t nsComponentManagerImpl::SizeOfInc
+ 
+   n += mContractIDs.ShallowSizeOfExcludingThis(aMallocSizeOf);
+   for (const auto& key : mContractIDs.Keys()) {
+     // We don't measure the nsFactoryEntry data because it's owned by
+     // mFactories (which is measured above).
+     n += key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+   }
+ 
++  n += sExtraStaticModules->ShallowSizeOfIncludingThis(aMallocSizeOf);
+   if (sModuleLocations) {
+     n += sModuleLocations->ShallowSizeOfIncludingThis(aMallocSizeOf);
+   }
+ 
++  n += mKnownStaticModules.ShallowSizeOfExcludingThis(aMallocSizeOf);
++
+   n += mPendingServices.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ 
+   // Measurement of the following members may be added later if DMD finds it is
+   // worthwhile:
+   // - mMon
+   // - sModuleLocations' entries
++  // - mKnownStaticModules' entries?
+ 
+   return n;
+ }
+ 
+ ////////////////////////////////////////////////////////////////////////////////
+ // nsFactoryEntry
+ ////////////////////////////////////////////////////////////////////////////////
+ 
++nsFactoryEntry::nsFactoryEntry(const mozilla::Module::CIDEntry* aEntry,
++                               nsComponentManagerImpl::KnownModule* aModule)
++    : mCIDEntry(aEntry), mModule(aModule) {}
++
+ nsFactoryEntry::nsFactoryEntry(const nsCID& aCID, nsIFactory* aFactory)
+-    : mCID(aCID), mFactory(aFactory) {
++    : mCIDEntry(nullptr), mModule(nullptr), mFactory(aFactory) {
++  auto* e = new mozilla::Module::CIDEntry();
++  auto* cid = new nsCID;
++  *cid = aCID;
++  e->cid = cid;
++  mCIDEntry = e;
++}
++
++nsFactoryEntry::~nsFactoryEntry() {
++  // If this was a RegisterFactory entry, we own the CIDEntry/CID
++  if (!mModule) {
++    delete mCIDEntry->cid;
++    delete mCIDEntry;
++  }
+ }
+ 
+ already_AddRefed<nsIFactory> nsFactoryEntry::GetFactory() {
+   nsComponentManagerImpl::gComponentManager->mLock.AssertNotCurrentThreadOwns();
+ 
++  if (!mFactory) {
++    // RegisterFactory then UnregisterFactory can leave an entry in mContractIDs
++    // pointing to an unusable nsFactoryEntry.
++    if (!mModule) {
++      return nullptr;
++    }
++
++    if (!mModule->Load()) {
++      return nullptr;
++    }
++
++    // Don't set mFactory directly, it needs to be locked
++    nsCOMPtr<nsIFactory> factory;
++
++    if (mModule->Module()->getFactoryProc) {
++      factory =
++          mModule->Module()->getFactoryProc(*mModule->Module(), *mCIDEntry);
++    } else if (mCIDEntry->getFactoryProc) {
++      factory = mCIDEntry->getFactoryProc(*mModule->Module(), *mCIDEntry);
++    } else {
++      NS_ASSERTION(mCIDEntry->constructorProc, "no getfactory or constructor");
++      factory = new mozilla::GenericFactory(mCIDEntry->constructorProc);
++    }
++    if (!factory) {
++      return nullptr;
++    }
++
++    MonitorAutoLock lock(nsComponentManagerImpl::gComponentManager->mLock);
++    // Threads can race to set mFactory
++    if (!mFactory) {
++      factory.swap(mFactory);
++    }
++  }
+   nsCOMPtr<nsIFactory> factory = mFactory;
+   return factory.forget();
+ }
+ 
+ nsresult nsFactoryEntry::CreateInstance(const nsIID& aIID, void** aResult) {
+   nsCOMPtr<nsIFactory> factory = GetFactory();
+   NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
+   return factory->CreateInstance(aIID, aResult);
+ }
+ 
+ size_t nsFactoryEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
+   size_t n = aMallocSizeOf(this);
+ 
+   // Measurement of the following members may be added later if DMD finds it is
+   // worthwhile:
+-  // - mCID;
++  // - mCIDEntry;
++  // - mModule;
+   // - mFactory;
+   // - mServiceObject;
+ 
+   return n;
+ }
+ 
+ ////////////////////////////////////////////////////////////////////////////////
+ // Static Access Functions
+@@ -1434,16 +1669,30 @@ nsresult NS_GetComponentRegistrar(nsICom
+   if (!nsComponentManagerImpl::gComponentManager) {
+     return NS_ERROR_NOT_INITIALIZED;
+   }
+ 
+   NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+   return NS_OK;
+ }
+ 
++EXPORT_XPCOM_API(nsresult)
++XRE_AddStaticComponent(const mozilla::Module* aComponent) {
++  nsComponentManagerImpl::InitializeStaticModules();
++  sExtraStaticModules->AppendElement(aComponent);
++
++  if (nsComponentManagerImpl::gComponentManager &&
++      nsComponentManagerImpl::NORMAL ==
++          nsComponentManagerImpl::gComponentManager->mStatus) {
++    nsComponentManagerImpl::gComponentManager->RegisterModule(aComponent);
++  }
++
++  return NS_OK;
++}
++
+ NS_IMETHODIMP
+ nsComponentManagerImpl::AddBootstrappedManifestLocation(nsIFile* aLocation) {
+   NS_ENSURE_ARG_POINTER(aLocation);
+ 
+   nsString path;
+   nsresult rv = aLocation->GetPath(path);
+   if (NS_FAILED(rv)) {
+     return rv;
+diff --git a/xpcom/components/nsComponentManager.h b/xpcom/components/nsComponentManager.h
+--- a/xpcom/components/nsComponentManager.h
++++ b/xpcom/components/nsComponentManager.h
+@@ -132,16 +132,58 @@ class nsComponentManagerImpl final : pub
+     bool Equals(const ComponentLocation& aA,
+                 const ComponentLocation& aB) const {
+       return (aA.type == aB.type && aA.location.Equals(aB.location));
+     }
+   };
+ 
+   static nsTArray<ComponentLocation>* sModuleLocations;
+ 
++  class KnownModule {
++   public:
++    /**
++     * Static or binary module.
++     */
++    explicit KnownModule(const mozilla::Module* aModule)
++        : mModule(aModule), mLoaded(false), mFailed(false) {}
++
++    ~KnownModule() {
++      if (mLoaded && mModule->unloadProc) {
++        mModule->unloadProc();
++      }
++    }
++
++    bool Load();
++
++    const mozilla::Module* Module() const { return mModule; }
++
++    /**
++     * For error logging, get a description of this module, either the
++     * file path, or <static module>.
++     */
++    nsCString Description() const;
++
++   private:
++    const mozilla::Module* mModule;
++    bool mLoaded;
++    bool mFailed;
++  };
++
++  // The KnownModule is kept alive by these members, it is
++  // referenced by pointer from the factory entries.
++  nsTArray<mozilla::UniquePtr<KnownModule>> mKnownStaticModules;
++
++  // Mutex not held
++  void RegisterModule(const mozilla::Module* aModule);
++
++  // Mutex held
++  void RegisterCIDEntryLocked(const mozilla::Module::CIDEntry* aEntry,
++                              KnownModule* aModule);
++  void RegisterContractIDLocked(const mozilla::Module::ContractIDEntry* aEntry);
++
+   // Mutex not held
+   void RegisterManifest(NSLocationType aType, mozilla::FileLocation& aFile,
+                         bool aChromeOnly);
+ 
+   struct ManifestProcessingContext {
+     ManifestProcessingContext(NSLocationType aType,
+                               mozilla::FileLocation& aFile, bool aChromeOnly)
+         : mType(aType), mFile(aFile), mChromeOnly(aChromeOnly) {}
+@@ -193,26 +235,30 @@ class nsComponentManagerImpl final : pub
+                             void** aResult);
+ };
+ 
+ #define NS_MAX_FILENAME_LEN 1024
+ 
+ #define NS_ERROR_IS_DIR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_XPCOM, 24)
+ 
+ struct nsFactoryEntry {
++  nsFactoryEntry(const mozilla::Module::CIDEntry* aEntry,
++                 nsComponentManagerImpl::KnownModule* aModule);
++
+   // nsIComponentRegistrar.registerFactory support
+   nsFactoryEntry(const nsCID& aClass, nsIFactory* aFactory);
+ 
+-  ~nsFactoryEntry() = default;
++  ~nsFactoryEntry();
+ 
+   already_AddRefed<nsIFactory> GetFactory();
+ 
+   nsresult CreateInstance(const nsIID& aIID, void** aResult);
+ 
+   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+ 
+-  const nsCID mCID;
++  const mozilla::Module::CIDEntry* mCIDEntry;
++  nsComponentManagerImpl::KnownModule* mModule;
+ 
+   nsCOMPtr<nsIFactory> mFactory;
+   nsCOMPtr<nsISupports> mServiceObject;
+ };
+ 
+ #endif  // nsComponentManager_h__

+ 1 - 0
comm-central/patches/series

@@ -8,5 +8,6 @@ TOP-1611647-2-fix-xul-references-suite.patch
 TOP-9999999-port1646560-suite-91a1.patch
 TOP-9999999-port1646560-suite-91a1.patch
 TOP-9999999-port1769442-suite-102a1.patch
 TOP-9999999-port1769442-suite-102a1.patch
 TOP-9999999-port1514936-suite-102a1.patch
 TOP-9999999-port1514936-suite-102a1.patch
+TOP-PLASTER-backout-1773770-12.patch
 WIP-9999999-port1712633-suite-90a1.patch
 WIP-9999999-port1712633-suite-90a1.patch
 WIP-1650630-2-port-1603712-suite.patch
 WIP-1650630-2-port-1603712-suite.patch