Bug 1470365: Part 3 - Use shared memory for StringBundles loaded in the content process. r=erahm r?smaug
MozReview-Commit-ID: LunnQyndnBf
--- 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;
};