Bug 1470365: Part 3 - Use shared memory for StringBundles loaded in the content process. r=erahm r?smaug draft
authorKris Maglione <maglione.k@gmail.com>
Thu, 28 Jun 2018 15:48:26 -0700
changeset 812241 81446a894aaacca1ddf2fc7f0b1bf9fded96f7a6
parent 812240 35ac9ee7f69d3294a2cd1a61bc943886b953cfe8
push id114513
push usermaglione.k@gmail.com
push dateThu, 28 Jun 2018 23:15:42 +0000
reviewerserahm, smaug
bugs1470365
milestone62.0a1
Bug 1470365: Part 3 - Use shared memory for StringBundles loaded in the content process. r=erahm r?smaug MozReview-Commit-ID: LunnQyndnBf
dom/base/nsContentUtils.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
intl/strres/nsIStringBundle.idl
intl/strres/nsStringBundle.cpp
intl/strres/nsStringBundleService.h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3919,16 +3919,28 @@ nsContentUtils::EnsureStringBundle(Prope
   }
   return NS_OK;
 }
 
 /* static */
 void
 nsContentUtils::AsyncPrecreateStringBundles()
 {
+  // We only ever want to pre-create bundles in the parent process.
+  //
+  // All nsContentUtils bundles are shared between the parent and child
+  // precesses, and the shared memory regions that back them *must* be created
+  // in the parent, and then sent to all children.
+  //
+  // If we attempt to create a bundle in the child before its memory region is
+  // available, we need to create a temporary non-shared bundle, and later
+  // replace that with the shared memory copy. So attempting to pre-load in the
+  // child is wasteful and unnecessary.
+  MOZ_ASSERT(XRE_IsParentProcess());
+
   for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT; ++bundleIndex) {
     nsresult rv = NS_IdleDispatchToCurrentThread(
       NS_NewRunnableFunction("AsyncPrecreateStringBundles",
                              [bundleIndex]() {
                                PropertiesFile file = static_cast<PropertiesFile>(bundleIndex);
                                EnsureStringBundle(file);
                                nsIStringBundle *bundle = sStringBundles[file];
                                bundle->AsyncPreload();
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -79,16 +79,17 @@
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/WebBrowserPersistDocumentChild.h"
 #include "mozilla/HangDetails.h"
 #include "imgLoader.h"
 #include "GMPServiceChild.h"
 #include "NullPrincipal.h"
 #include "nsISimpleEnumerator.h"
+#include "nsIStringBundle.h"
 #include "nsIWorkerDebuggerManager.h"
 
 #if !defined(XP_WIN)
 #include "mozilla/Omnijar.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #include "ChildProfilerController.h"
@@ -2261,21 +2262,16 @@ ContentChild::RecvRegisterChrome(Infalli
                                  const nsCString& locale,
                                  const bool& reset)
 {
   nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
   nsChromeRegistryContent* chromeRegistry =
     static_cast<nsChromeRegistryContent*>(registrySvc.get());
   chromeRegistry->RegisterRemoteChrome(packages, resources, overrides,
                                        locale, reset);
-  static bool preloadDone = false;
-  if (!preloadDone) {
-    preloadDone = true;
-    nsContentUtils::AsyncPrecreateStringBundles();
-  }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvRegisterChromeItem(const ChromeRegistryItem& item)
 {
   nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
   nsChromeRegistryContent* chromeRegistry =
@@ -2556,16 +2552,30 @@ ContentChild::RecvUpdateSharedData(const
                         std::move(blobImpls),
                         std::move(aChangedKeys));
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+ContentChild::RecvRegisterStringBundles(nsTArray<mozilla::dom::StringBundleDescriptor>&& aDescriptors)
+{
+  nsCOMPtr<nsIStringBundleService> stringBundleService =
+    services::GetStringBundleService();
+
+  for (auto& descriptor : aDescriptors) {
+    stringBundleService->RegisterContentBundle(descriptor.bundleURL(), descriptor.mapFile(),
+                                               descriptor.mapSize());
+  }
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ContentChild::RecvGeolocationUpdate(nsIDOMGeoPosition* aPosition)
 {
   nsCOMPtr<nsIGeolocationUpdate> gs =
     do_GetService("@mozilla.org/geolocation/service;1");
   if (!gs) {
     return IPC_OK();
   }
   gs->Update(aPosition);
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -400,16 +400,18 @@ public:
                                                    const IPC::Principal& aPrincipal,
                                                    const ClonedMessageData& aData) override;
 
   mozilla::ipc::IPCResult RecvUpdateSharedData(const FileDescriptor& aMapFile,
                                                const uint32_t& aMapSize,
                                                nsTArray<IPCBlob>&& aBlobs,
                                                nsTArray<nsCString>&& aChangedKeys) override;
 
+  mozilla::ipc::IPCResult RecvRegisterStringBundles(nsTArray<StringBundleDescriptor>&& stringBundles) override;
+
   virtual mozilla::ipc::IPCResult RecvGeolocationUpdate(nsIDOMGeoPosition* aPosition) override;
 
   virtual mozilla::ipc::IPCResult RecvGeolocationError(const uint16_t& errorCode) override;
 
   virtual mozilla::ipc::IPCResult RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries) override;
 
   virtual mozilla::ipc::IPCResult RecvUpdateFontList(InfallibleTArray<SystemFontListEntry>&& aFontList) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -141,16 +141,17 @@
 #include "nsIParentChannel.h"
 #include "nsIPresShell.h"
 #include "nsIRemoteWindowContext.h"
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISiteSecurityService.h"
 #include "nsISound.h"
 #include "nsISpellChecker.h"
+#include "nsIStringBundle.h"
 #include "nsISupportsPrimitives.h"
 #include "nsITimer.h"
 #include "nsIURIFixup.h"
 #include "nsIURL.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIXULWindow.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIWindowWatcher.h"
@@ -1271,16 +1272,27 @@ ContentParent::GetAllEvenIfDead(nsTArray
 {
   aArray.Clear();
 
   for (auto* cp : AllProcesses(eAll)) {
     aArray.AppendElement(cp);
   }
 }
 
+void
+ContentParent::BroadcastStringBundle(const StringBundleDescriptor& aBundle)
+{
+  AutoTArray<StringBundleDescriptor, 1> array;
+  array.AppendElement(aBundle);
+
+  for (auto* cp : AllProcesses(eLive)) {
+    Unused << cp->SendRegisterStringBundles(array);
+  }
+}
+
 const nsAString&
 ContentParent::GetRemoteType() const
 {
   return mRemoteType;
 }
 
 void
 ContentParent::Init()
@@ -2315,16 +2327,20 @@ ContentParent::InitInternal(ProcessPrior
                                           fontList, sharedData->CloneMapFile(),
                                           sharedData->GetMapSize());
 
   nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
   nsChromeRegistryChrome* chromeRegistry =
     static_cast<nsChromeRegistryChrome*>(registrySvc.get());
   chromeRegistry->SendRegisteredChrome(this);
 
+  nsCOMPtr<nsIStringBundleService> stringBundleService =
+    services::GetStringBundleService();
+  stringBundleService->SendContentBundles(this);
+
   if (gAppData) {
     nsCString version(gAppData->version);
     nsCString buildID(gAppData->buildID);
     nsCString name(gAppData->name);
     nsCString UAName(gAppData->UAName);
     nsCString ID(gAppData->ID);
     nsCString vendor(gAppData->vendor);
     nsCString sourceURL(gAppData->sourceURL);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -198,16 +198,18 @@ public:
                 ContentParent* aOpenerContentParent,
                 TabParent* aSameTabGroupAs,
                 uint64_t aNextTabParentId);
 
   static void GetAll(nsTArray<ContentParent*>& aArray);
 
   static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);
 
+  static void BroadcastStringBundle(const StringBundleDescriptor&);
+
   const nsAString& GetRemoteType() const;
 
   virtual void DoGetRemoteType(nsAString& aRemoteType, ErrorResult& aError) const override
   {
     aRemoteType = GetRemoteType();
   }
 
   enum CPIteratorPolicy {
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -284,16 +284,23 @@ struct XPCOMInitData
     ContentDeviceData contentDeviceData;
     GfxInfoFeatureStatus[] gfxFeatureStatus;
     DataStorageEntry[] dataStorage;
     nsCString[] appLocales;
     nsCString[] requestedLocales;
     DynamicScalarDefinition[] dynamicScalarDefs;
 };
 
+struct StringBundleDescriptor
+{
+    nsCString bundleURL;
+    FileDescriptor mapFile;
+    uint32_t mapSize;
+};
+
 /**
  * The PContent protocol is a top-level protocol between the UI process
  * and a content process. There is exactly one PContentParent/PContentChild pair
  * for each content process.
  */
 nested(upto inside_cpow) sync protocol PContent
 {
     manages PBrowser;
@@ -454,16 +461,18 @@ child:
 
     async UpdateAppLocales(nsCString[] appLocales);
     async UpdateRequestedLocales(nsCString[] requestedLocales);
 
     async UpdateSharedData(FileDescriptor mapFile, uint32_t aSize,
                            IPCBlob[] blobs,
                            nsCString[] changedKeys);
 
+    async RegisterStringBundles(StringBundleDescriptor[] stringBundles);
+
     // nsIPermissionManager messages
     async AddPermission(Permission permission);
 
     async FlushMemory(nsString reason);
 
     async GarbageCollect();
     async CycleCollect();
     async UnlinkGhosts();
--- a/intl/strres/nsIStringBundle.idl
+++ b/intl/strres/nsIStringBundle.idl
@@ -4,16 +4,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsISimpleEnumerator.idl"
 
 %{C++
 #include "mozilla/MemoryReporting.h"
 
+namespace mozilla {
+namespace dom {
+class ContentParent;
+}
+namespace ipc {
+class FileDescriptor;
+}
+}
+
 // Define Contractid and CID
 // {D85A17C1-AA7C-11d2-9B8C-00805F8A16D9}
 #define NS_STRINGBUNDLESERVICE_CID \
 { 0xd85a17c1, 0xaa7c, 0x11d2, \
   { 0x9b, 0x8c, 0x0, 0x80, 0x5f, 0x8a, 0x16, 0xd9 } }
 
 #define NS_STRINGBUNDLE_CONTRACTID "@mozilla.org/intl/stringbundle;1"
 
@@ -94,10 +103,16 @@ interface nsIStringBundleService : nsISu
    * because any bundles that are floating around when the locale changes
    * will suddenly contain bad data
    *
    */
   void flushBundles();
 
   %{C++
     virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
+
+  virtual void SendContentBundles(mozilla::dom::ContentParent* aContentParent) const = 0;
+
+  virtual void RegisterContentBundle(const nsCString& aBundleURL,
+                                     const mozilla::ipc::FileDescriptor& aMapFile,
+                                     size_t aMapSize) = 0;
   %}
 };
--- a/intl/strres/nsStringBundle.cpp
+++ b/intl/strres/nsStringBundle.cpp
@@ -27,26 +27,29 @@
 #include "nsContentUtils.h"
 #include "nsPersistentProperties.h"
 #include "nsQueryObject.h"
 #include "nsStringStream.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/URLPreloader.h"
 #include "mozilla/ResultExtensions.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ipc/SharedStringMap.h"
 
 // for async loading
 #ifdef ASYNC_LOADING
 #include "nsIBinaryInputStream.h"
 #include "nsIStringStream.h"
 #endif
 
 using namespace mozilla;
 
+using mozilla::dom::ContentParent;
+using mozilla::dom::StringBundleDescriptor;
 using mozilla::dom::ipc::SharedStringMap;
 using mozilla::dom::ipc::SharedStringMapBuilder;
 using mozilla::ipc::FileDescriptor;
 
 static NS_DEFINE_CID(kErrorServiceCID, NS_ERRORSERVICE_CID);
 
 /**
  * A set of string bundle URLs which are loaded by content processes, and
@@ -218,16 +221,27 @@ public:
     if (mMapFile.isSome()) {
       return mMapSize;
     }
     return mStringMap->MapSize();
   }
 
   bool Initialized() const { return mStringMap || mMapFile.isSome(); }
 
+  StringBundleDescriptor GetDescriptor() const
+  {
+    MOZ_ASSERT(Initialized());
+
+    StringBundleDescriptor descriptor;
+    descriptor.bundleURL() = BundleURL();
+    descriptor.mapFile() = CloneFileDescriptor();
+    descriptor.mapSize() = MapSize();
+    return descriptor;
+  }
+
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
   static SharedStringBundle* Cast(nsIStringBundle* aStringBundle)
   {
     return static_cast<SharedStringBundle*>(aStringBundle);
   }
 
 protected:
@@ -457,16 +471,19 @@ SharedStringBundle::LoadProperties()
     nsString value;
     MOZ_TRY(elem->GetKey(key));
     MOZ_TRY(elem->GetValue(value));
 
     builder.Add(key, value);
   }
 
   mStringMap = new SharedStringMap(std::move(builder));
+
+  ContentParent::BroadcastStringBundle(GetDescriptor());
+
   return NS_OK;
 }
 
 void
 SharedStringBundle::SetMapFile(const FileDescriptor& aFile, size_t aSize)
 {
   MOZ_ASSERT(NS_IsContentProcess());
   mStringMap = nullptr;
@@ -994,16 +1011,61 @@ nsStringBundleService::flushBundleCache(
 NS_IMETHODIMP
 nsStringBundleService::FlushBundles()
 {
   flushBundleCache();
   return NS_OK;
 }
 
 void
+nsStringBundleService::SendContentBundles(ContentParent* aContentParent) const
+{
+  nsTArray<StringBundleDescriptor> bundles;
+
+  for (auto* entry : mSharedBundles) {
+    auto bundle = SharedStringBundle::Cast(entry->mBundle);
+
+    if (bundle->Initialized()) {
+      bundles.AppendElement(bundle->GetDescriptor());
+    }
+  }
+
+  Unused << aContentParent->SendRegisterStringBundles(std::move(bundles));
+}
+
+void
+nsStringBundleService::RegisterContentBundle(const nsCString& aBundleURL,
+                                             const FileDescriptor& aMapFile,
+                                             size_t aMapSize)
+{
+  RefPtr<StringBundleProxy> proxy;
+
+  auto* cacheEntry = mBundleMap.Get(aBundleURL);
+  if (cacheEntry) {
+    if (RefPtr<SharedStringBundle> shared = do_QueryObject(cacheEntry->mBundle)) {
+      return;
+    }
+
+    proxy = do_QueryObject(cacheEntry->mBundle);
+    MOZ_ASSERT(proxy);
+    cacheEntry->remove();
+  }
+
+  auto bundle = MakeRefPtr<SharedStringBundle>(aBundleURL.get(), mOverrideStrings);
+  bundle->SetMapFile(aMapFile, aMapSize);
+
+  if (proxy) {
+    proxy->Retarget(bundle);
+  }
+
+  cacheEntry = insertIntoCache(bundle.forget(), aBundleURL);
+  mSharedBundles.insertBack(cacheEntry);
+}
+
+void
 nsStringBundleService::getStringBundle(const char *aURLSpec,
                                        nsIStringBundle **aResult)
 {
   nsDependentCString key(aURLSpec);
   bundleCacheEntry_t* cacheEntry = mBundleMap.Get(key);
 
   RefPtr<SharedStringBundle> shared;
 
@@ -1058,17 +1120,17 @@ nsStringBundleService::getStringBundle(c
 
   // finally, return the value
   *aResult = cacheEntry->mBundle;
   NS_ADDREF(*aResult);
 }
 
 bundleCacheEntry_t *
 nsStringBundleService::insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
-                                       const nsCString &aHashKey)
+                                       const nsACString& aHashKey)
 {
   bundleCacheEntry_t *cacheEntry;
 
   if (mBundleMap.Count() < MAX_CACHED_BUNDLES ||
       mBundleCache.isEmpty()) {
     // cache not full - create a new entry
     cacheEntry = new bundleCacheEntry_t();
   } else {
--- a/intl/strres/nsStringBundleService.h
+++ b/intl/strres/nsStringBundleService.h
@@ -46,28 +46,34 @@ public:
       "explicit/string-bundle-service", KIND_HEAP, UNITS_BYTES,
       amt,
       "Memory used for StringBundleService bundles");
     return NS_OK;
   };
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
+  void SendContentBundles(mozilla::dom::ContentParent* aContentParent) const override;
+
+  void RegisterContentBundle(const nsCString& aBundleURL,
+                             const mozilla::ipc::FileDescriptor& aMapFile,
+                             size_t aMapSize) override;
+
 private:
   virtual ~nsStringBundleService();
 
   void getStringBundle(const char *aUrl, nsIStringBundle** aResult);
   nsresult FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus,
                             uint32_t argCount, char16_t** argArray,
                             nsAString& result);
 
   void flushBundleCache();
 
-  bundleCacheEntry_t *insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
-                                      const nsCString &aHashKey);
+  bundleCacheEntry_t* insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
+                                      const nsACString &aHashKey);
 
   nsDataHashtable<nsCStringHashKey, bundleCacheEntry_t*> mBundleMap;
   mozilla::LinkedList<bundleCacheEntry_t> mBundleCache;
   mozilla::AutoCleanLinkedList<bundleCacheEntry_t> mSharedBundles;
 
   nsCOMPtr<nsIErrorService> mErrorService;
   nsCOMPtr<nsIStringBundleOverride> mOverrideStrings;
 };