diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 7349db717aef..f53833ac9569 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -591,7 +591,9 @@ NS_INTERFACE_MAP_END mozilla::ipc::IPCResult ContentChild::RecvSetXPCOMProcessAttributes( const XPCOMInitData& aXPCOMInit, const StructuredCloneData& aInitialData, nsTArray&& aLookAndFeelIntCache, - nsTArray&& aFontList) { + nsTArray&& aFontList, + const Maybe& aSharedUASheetHandle, + const uintptr_t& aSharedUASheetAddress) { if (!sShutdownCanary) { return IPC_OK(); } @@ -599,6 +601,7 @@ mozilla::ipc::IPCResult ContentChild::RecvSetXPCOMProcessAttributes( mLookAndFeelCache = std::move(aLookAndFeelIntCache); mFontList = std::move(aFontList); gfx::gfxVars::SetValuesForInitialize(aXPCOMInit.gfxNonDefaultVarUpdates()); + InitSharedUASheets(aSharedUASheetHandle, aSharedUASheetAddress); InitXPCOM(aXPCOMInit, aInitialData); InitGraphicsDeviceData(aXPCOMInit.contentDeviceData()); @@ -1198,6 +1201,20 @@ void ContentChild::InitGraphicsDeviceData(const ContentDeviceData& aData) { gfxPlatform::InitChild(aData); } +void ContentChild::InitSharedUASheets(const Maybe& aHandle, + uintptr_t aAddress) { + MOZ_ASSERT_IF(!aHandle, !aAddress); + + if (!aAddress) { + return; + } + + // Map the shared memory storing the user agent style sheets. Do this as + // early as possible to maximize the chance of being able to map at the + // address we want. + nsLayoutStylesheetCache::SetSharedMemory(*aHandle, aAddress); +} + void ContentChild::InitXPCOM( const XPCOMInitData& aXPCOMInit, const mozilla::dom::ipc::StructuredCloneData& aInitialData) { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index da2b61875029..0044a641739e 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -7,6 +7,7 @@ #ifndef mozilla_dom_ContentChild_h #define mozilla_dom_ContentChild_h +#include "base/shared_memory.h" #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/dom/PBrowserOrId.h" @@ -125,6 +126,9 @@ class ContentChild final : public PContentChild, void InitXPCOM(const XPCOMInitData& aXPCOMInit, const mozilla::dom::ipc::StructuredCloneData& aInitialData); + void InitSharedUASheets(const Maybe& aHandle, + uintptr_t aAddress); + void InitGraphicsDeviceData(const ContentDeviceData& aData); static ContentChild* GetSingleton() { return sSingleton; } @@ -585,7 +589,9 @@ class ContentChild final : public PContentChild, mozilla::ipc::IPCResult RecvSetXPCOMProcessAttributes( const XPCOMInitData& aXPCOMInit, const StructuredCloneData& aInitialData, nsTArray&& aLookAndFeelIntCache, - nsTArray&& aFontList); + nsTArray&& aFontList, + const Maybe& aSharedUASheetHandle, + const uintptr_t& aSharedUASheetAddress); mozilla::ipc::IPCResult RecvProvideAnonymousTemporaryFile( const uint64_t& aID, const FileDescOrError& aFD); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 3ac3c3ef3f60..18d49dfcdb76 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -2464,8 +2464,21 @@ void ContentParent::InitInternal(ProcessPriority aInitialPriority) { ScreenManager& screenManager = ScreenManager::GetSingleton(); screenManager.CopyScreensToRemote(this); + // Send the UA sheet shared memory buffer and the address it is mapped at. + auto cache = nsLayoutStylesheetCache::Singleton(); + Maybe sharedUASheetHandle; + uintptr_t sharedUASheetAddress = cache->GetSharedMemoryAddress(); + + SharedMemoryHandle handle; + if (cache->ShareToProcess(OtherPid(), &handle)) { + sharedUASheetHandle.emplace(handle); + } else { + sharedUASheetAddress = 0; + } + Unused << SendSetXPCOMProcessAttributes(xpcomInit, initialData, lnfCache, - fontList); + fontList, sharedUASheetHandle, + sharedUASheetAddress); ipc::WritableSharedMap* sharedData = nsFrameMessageManager::sParentProcessManager->SharedData(); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 7178223c76a9..02f356ece22f 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -101,6 +101,7 @@ using mozilla::dom::BrowsingContextId from "mozilla/dom/ipc/IdType.h"; using mozilla::dom::BrowsingContextTransaction from "mozilla/dom/BrowsingContext.h"; using mozilla::dom::BrowsingContextFieldEpochs from "mozilla/dom/BrowsingContext.h"; using mozilla::dom::BrowsingContextInitializer from "mozilla/dom/BrowsingContext.h"; +using base::SharedMemoryHandle from "base/shared_memory.h"; union ChromeRegistryItem { @@ -563,7 +564,9 @@ child: StructuredCloneData initialData, LookAndFeelInt[] lookAndFeelIntCache, /* used on MacOSX and Linux only: */ - SystemFontListEntry[] systemFontList); + SystemFontListEntry[] systemFontList, + SharedMemoryHandle? sharedUASheetHandle, + uintptr_t sharedUASheetAddress); // Notify child that last-pb-context-exited notification was observed async LastPrivateDocShellDestroyed(); diff --git a/layout/style/StyleSheet.cpp b/layout/style/StyleSheet.cpp index 78f6caf46538..8bd226b77be0 100644 --- a/layout/style/StyleSheet.cpp +++ b/layout/style/StyleSheet.cpp @@ -23,6 +23,7 @@ #include "mozilla/StyleSheetInlines.h" #include "mozAutoDocUpdate.h" +#include "nsLayoutStylesheetCache.h" namespace mozilla { @@ -303,6 +304,10 @@ StyleSheetInfo::StyleSheetInfo(StyleSheetInfo& aCopy, StyleSheet* aPrimarySheet) mSourceURL(aCopy.mSourceURL), mContents(Servo_StyleSheet_Clone(aCopy.mContents.get(), aPrimarySheet) .Consume()), + // Cloning aCopy.mContents will still leave us with some references to + // data in shared memory (for example, any SelectorList objects will still + // be shared), so continue to keep it alive. + mSharedMemory(aCopy.mSharedMemory), mURLData(aCopy.mURLData) #ifdef DEBUG , @@ -315,7 +320,12 @@ StyleSheetInfo::StyleSheetInfo(StyleSheetInfo& aCopy, StyleSheet* aPrimarySheet) MOZ_COUNT_CTOR(StyleSheetInfo); } -StyleSheetInfo::~StyleSheetInfo() { MOZ_COUNT_DTOR(StyleSheetInfo); } +StyleSheetInfo::~StyleSheetInfo() { + MOZ_COUNT_DTOR(StyleSheetInfo); + + // Drop the sheet contents before the shared memory. + mContents = nullptr; +} StyleSheetInfo* StyleSheetInfo::CloneFor(StyleSheet* aPrimarySheet) { return new StyleSheetInfo(*this, aPrimarySheet); @@ -326,9 +336,15 @@ MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSheetMallocEnclosingSizeOf) size_t StyleSheetInfo::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = aMallocSizeOf(this); - n += Servo_StyleSheet_SizeOfIncludingThis( - ServoStyleSheetMallocSizeOf, ServoStyleSheetMallocEnclosingSizeOf, - mContents); + + // If this sheet came from shared memory, then it will be counted by + // nsLayoutStylesheetCache in the parent process. + if (!mSharedMemory) { + n += Servo_StyleSheet_SizeOfIncludingThis( + ServoStyleSheetMallocSizeOf, ServoStyleSheetMallocEnclosingSizeOf, + mContents); + } + return n; } @@ -1165,4 +1181,35 @@ OriginFlags StyleSheet::GetOrigin() { Servo_StyleSheet_GetOrigin(Inner().mContents)); } +void StyleSheet::SetSharedContents(nsLayoutStylesheetCache::Shm* aSharedMemory, + const ServoCssRules* aSharedRules) { + MOZ_ASSERT(aSharedMemory); + MOZ_ASSERT(!IsComplete()); + + SetURLExtraData(); + + // Hold a strong reference to the shared memory that aSharedRules comes + // from, so that we don't end up releasing the shared memory before the + // StyleSheetInner. + Inner().mSharedMemory = aSharedMemory; + + Inner().mContents = + Servo_StyleSheet_FromSharedData(Inner().mURLData, aSharedRules).Consume(); + + // Don't call FinishParse(), since that tries to set source map URLs, + // which we don't have. +} + +const ServoCssRules* StyleSheet::ToShared( + RawServoSharedMemoryBuilder* aBuilder) { + // Assert some things we assume when creating a StyleSheet using shared + // memory. + MOZ_ASSERT(GetReferrerPolicy() == net::RP_Unset); + MOZ_ASSERT(GetCORSMode() == CORS_NONE); + MOZ_ASSERT(Inner().mIntegrity.IsEmpty()); + MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(Principal())); + + return Servo_SharedMemoryBuilder_AddStylesheet(aBuilder, Inner().mContents); +} + } // namespace mozilla diff --git a/layout/style/StyleSheet.h b/layout/style/StyleSheet.h index f7a7e7b52345..01bdbf022a98 100644 --- a/layout/style/StyleSheet.h +++ b/layout/style/StyleSheet.h @@ -25,6 +25,8 @@ class nsINode; class nsIPrincipal; +struct nsLayoutStylesheetCacheShm; +struct RawServoSharedMemoryBuilder; namespace mozilla { @@ -376,6 +378,17 @@ class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache { } } + // Copy the contents of this style sheet into the shared memory buffer managed + // by aBuilder. Returns the pointer into the buffer that the sheet contents + // were stored at. (The returned pointer is to an Arc> value.) + const ServoCssRules* ToShared(RawServoSharedMemoryBuilder* aBuilder); + + // Sets the contents of this style sheet to the specified aSharedRules + // pointer, which must be a pointer somewhere in the aSharedMemory buffer + // as previously returned by a ToShared() call. + void SetSharedContents(nsLayoutStylesheetCacheShm* aSharedMemory, + const ServoCssRules* aSharedRules); + private: dom::ShadowRoot* GetContainingShadow() const; diff --git a/layout/style/StyleSheetInfo.h b/layout/style/StyleSheetInfo.h index 2745d0e9e43f..2106d27ace07 100644 --- a/layout/style/StyleSheetInfo.h +++ b/layout/style/StyleSheetInfo.h @@ -15,6 +15,7 @@ #include "nsIURI.h" class nsIPrincipal; +struct nsLayoutStylesheetCacheShm; namespace mozilla { class StyleSheet; @@ -77,6 +78,12 @@ struct StyleSheetInfo final { RefPtr mContents; + // The shared memory buffer that stores the rules in the style sheet, if + // this style sheet was loaded from the style sheet cache's shared memory. + // + // We need to hold on to this so it doesn't go away before we do. + RefPtr mSharedMemory; + // XXX We already have mSheetURI, mBaseURI, and mPrincipal. // // Can we somehow replace them with URLExtraData directly? The issue diff --git a/layout/style/UserAgentStyleSheetList.h b/layout/style/UserAgentStyleSheetList.h index e25c51866942..40fe249d9bf9 100644 --- a/layout/style/UserAgentStyleSheetList.h +++ b/layout/style/UserAgentStyleSheetList.h @@ -7,7 +7,7 @@ /* list of user agent style sheets that nsLayoutStylesheetCache manages */ /* - * STYLE_SHEET(identifier_, url_, lazy_) + * STYLE_SHEET(identifier_, url_, shared_) * * identifier_ * An identifier for the style sheet, suitable for use as an enum class value. @@ -15,22 +15,23 @@ * url_ * The URL of the style sheet. * - * lazy_ - * A boolean indicating whether the sheet is loaded lazily. + * shared_ + * A boolean indicating whether the sheet can be safely placed in shared + * memory. */ STYLE_SHEET(ContentEditable, "resource://gre/res/contenteditable.css", true) -STYLE_SHEET(CounterStyles, "resource://gre-resources/counterstyles.css", false) +STYLE_SHEET(CounterStyles, "resource://gre-resources/counterstyles.css", true) STYLE_SHEET(DesignMode, "resource://gre/res/designmode.css", true) STYLE_SHEET(Forms, "resource://gre-resources/forms.css", true) -STYLE_SHEET(HTML, "resource://gre-resources/html.css", false) +STYLE_SHEET(HTML, "resource://gre-resources/html.css", true) STYLE_SHEET(MathML, "resource://gre-resources/mathml.css", true) -STYLE_SHEET(MinimalXUL, "chrome://global/content/minimal-xul.css", false) +STYLE_SHEET(MinimalXUL, "chrome://global/content/minimal-xul.css", true) STYLE_SHEET(NoFrames, "resource://gre-resources/noframes.css", true) STYLE_SHEET(NoScript, "resource://gre-resources/noscript.css", true) STYLE_SHEET(PluginProblem, "resource://gre-resources/pluginproblem.css", true) -STYLE_SHEET(Quirk, "resource://gre-resources/quirk.css", false) +STYLE_SHEET(Quirk, "resource://gre-resources/quirk.css", true) STYLE_SHEET(Scrollbars, "chrome://global/skin/scrollbars.css", true) -STYLE_SHEET(SVG, "resource://gre/res/svg.css", false) +STYLE_SHEET(SVG, "resource://gre/res/svg.css", true) STYLE_SHEET(UA, "resource://gre-resources/ua.css", true) -STYLE_SHEET(XUL, "chrome://global/content/xul.css", true) +STYLE_SHEET(XUL, "chrome://global/content/xul.css", false) diff --git a/layout/style/nsLayoutStylesheetCache.cpp b/layout/style/nsLayoutStylesheetCache.cpp index 9ed6f39c11e1..05e64605257a 100644 --- a/layout/style/nsLayoutStylesheetCache.cpp +++ b/layout/style/nsLayoutStylesheetCache.cpp @@ -14,8 +14,10 @@ #include "mozilla/Telemetry.h" #include "mozilla/css/Loader.h" #include "mozilla/dom/SRIMetadata.h" +#include "mozilla/ipc/SharedMemory.h" #include "MainThreadUtils.h" #include "nsColor.h" +#include "nsContentUtils.h" #include "nsIConsoleService.h" #include "nsIFile.h" #include "nsIObserverService.h" @@ -26,6 +28,8 @@ #include "nsServiceManagerUtils.h" #include "nsXULAppAPI.h" +#include + using namespace mozilla; using namespace mozilla::css; @@ -49,9 +53,9 @@ nsresult nsLayoutStylesheetCache::Observe(nsISupports* aSubject, return NS_OK; } -#define STYLE_SHEET(identifier_, url_, lazy_) \ +#define STYLE_SHEET(identifier_, url_, shared_) \ NotNull nsLayoutStylesheetCache::identifier_##Sheet() { \ - if (lazy_ && !m##identifier_##Sheet) { \ + if (!m##identifier_##Sheet) { \ LoadSheetURL(url_, &m##identifier_##Sheet, eAgentSheetFeatures, eCrash); \ } \ return WrapNotNull(m##identifier_##Sheet); \ @@ -94,6 +98,10 @@ void nsLayoutStylesheetCache::Shutdown() { for (auto& r : URLExtraData::sShared) { r = nullptr; } + // Some content processes don't get around to consuming the shared memory + // buffer we store in sSharedMemory (e.g. a preloaded content process that + // doesn't get a document loaded in it), so clear it out here to avoid leaks. + sSharedMemory = nullptr; } void nsLayoutStylesheetCache::SetUserContentCSSURL(nsIURI* aURI) { @@ -106,10 +114,19 @@ MOZ_DEFINE_MALLOC_SIZE_OF(LayoutStylesheetCacheMallocSizeOf) NS_IMETHODIMP nsLayoutStylesheetCache::CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) { - MOZ_COLLECT_REPORT("explicit/layout/style-sheet-cache", KIND_HEAP, + MOZ_COLLECT_REPORT("explicit/layout/style-sheet-cache/unshared", KIND_HEAP, UNITS_BYTES, SizeOfIncludingThis(LayoutStylesheetCacheMallocSizeOf), - "Memory used for some built-in style sheets."); + "Memory used for built-in style sheets that are not " + "shared between processes."); + + if (XRE_IsParentProcess()) { + MOZ_COLLECT_REPORT( + "explicit/layout/style-sheet-cache/shared", KIND_NONHEAP, UNITS_BYTES, + mSharedMemory ? mUsedSharedMemory : 0, + "Memory used for built-in style sheets that are shared to " + "child processes."); + } return NS_OK; } @@ -120,7 +137,7 @@ size_t nsLayoutStylesheetCache::SizeOfIncludingThis( #define MEASURE(s) n += s ? s->SizeOfIncludingThis(aMallocSizeOf) : 0; -#define STYLE_SHEET(identifier_, url_, lazy_) MEASURE(m##identifier_##Sheet); +#define STYLE_SHEET(identifier_, url_, shared_) MEASURE(m##identifier_##Sheet); #include "mozilla/UserAgentStyleSheetList.h" #undef STYLE_SHEET @@ -136,7 +153,7 @@ size_t nsLayoutStylesheetCache::SizeOfIncludingThis( return n; } -nsLayoutStylesheetCache::nsLayoutStylesheetCache() { +nsLayoutStylesheetCache::nsLayoutStylesheetCache() : mUsedSharedMemory(0) { nsCOMPtr obsSvc = mozilla::services::GetObserverService(); NS_ASSERTION(obsSvc, "No global observer service?"); @@ -147,17 +164,9 @@ nsLayoutStylesheetCache::nsLayoutStylesheetCache() { obsSvc->AddObserver(this, "chrome-flush-caches", false); } + // Load user style sheets. InitFromProfile(); - // And make sure that we load our UA sheets. No need to do this - // per-profile, since they're profile-invariant. -#define STYLE_SHEET(identifier_, url_, lazy_) \ - if (!lazy_) { \ - LoadSheetURL(url_, &m##identifier_##Sheet, eAgentSheetFeatures, eCrash); \ - } -#include "mozilla/UserAgentStyleSheetList.h" -#undef STYLE_SHEET - if (XRE_IsParentProcess()) { // We know we need xul.css for the UI, so load that now too: XULSheet(); @@ -170,9 +179,147 @@ nsLayoutStylesheetCache::nsLayoutStylesheetCache() { gUserContentSheetURL = nullptr; } - // The remaining sheets are created on-demand do to their use being rarer - // (which helps save memory for Firefox OS apps) or because they need to - // be re-loadable in DependentPrefChanged. + // If we are the in the parent process, then we load all of the UA sheets that + // are shareable and store them into shared memory. In both the parent and + // the content process, we load these sheets out of shared memory. + // + // The shared memory buffer's format is a Header object, which contains + // internal pointers to each of the shared style sheets, followed by the style + // sheets themselves. + if (StaticPrefs::layout_css_shared_memory_ua_sheets_enabled()) { + if (XRE_IsParentProcess()) { + MOZ_ASSERT(!sSharedMemory); + // Load the style sheets and store them in a new shared memory buffer. + InitSharedSheetsInParent(); + } else if (sSharedMemory) { + // Use the shared memory handle that was given to us by a SetSharedMemory + // call under ContentChild::InitXPCOM. + mSharedMemory = sSharedMemory.forget(); + } + } + + // If we get here and we don't have a shared memory handle, then it means + // either we failed to create the shared memory buffer in the parent process + // (unexpected), or we failed to map the shared memory buffer at the address + // we needed in the content process (might happen). + // + // In the parent process, this means we'll just leave our eagerly loaded + // non-shared sheets in the mFooSheet fields. In a content process, we'll + // lazily load our own copies of the sheets later. + if (mSharedMemory) { + Header* header = static_cast(mSharedMemory->mShm.memory()); + MOZ_RELEASE_ASSERT(header->mMagic == Header::kMagic); + +#define STYLE_SHEET(identifier_, url_, shared_) \ + if (shared_) { \ + LoadSheetFromSharedMemory(url_, &m##identifier_##Sheet, \ + eAgentSheetFeatures, mSharedMemory, header, \ + UserAgentStyleSheetID::identifier_); \ + } +#include "mozilla/UserAgentStyleSheetList.h" +#undef STYLE_SHEET + } +} + +void nsLayoutStylesheetCache::LoadSheetFromSharedMemory( + const char* aURL, RefPtr* aSheet, SheetParsingMode aParsingMode, + Shm* aSharedMemory, Header* aHeader, UserAgentStyleSheetID aSheetID) { + auto i = size_t(aSheetID); + + auto sheet = MakeRefPtr( + aParsingMode, CORS_NONE, mozilla::net::RP_Unset, dom::SRIMetadata()); + + nsCOMPtr uri; + MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), aURL)); + + sheet->SetPrincipal(nsContentUtils::GetSystemPrincipal()); + sheet->SetURIs(uri, uri, uri); + sheet->SetSharedContents(aSharedMemory, aHeader->mSheets[i]); + sheet->SetComplete(); + + URLExtraData::sShared[i] = sheet->URLData(); + + *aSheet = sheet.forget(); +} + +void nsLayoutStylesheetCache::InitSharedSheetsInParent() { + MOZ_ASSERT(XRE_IsParentProcess()); + + mSharedMemory = new Shm(); + mSharedMemory->mShm.Create(kSharedMemorySize); + + // We need to choose an address to map the shared memory in the parent process + // that we'll also be able to use in content processes. There's no way to + // pick an address that is guaranteed to be free in future content processes, + // so instead we pick an address that is some distance away from current heap + // allocations and hope that by the time the content process maps the shared + // memory, that address will be free. + // + // On 64 bit, we have a large amount of address space, so we pick an address + // half way through the next 8 GiB of free space, and this has a very good + // chance of succeeding. On 32 bit, address space is more constrained. We + // only have 3 GiB of space to work with, and we don't want to pick a location + // right in the middle, since that could cause future large allocations to + // fail. So we pick an address half way through the next 512 MiB of free + // space. Experimentally this seems to work 9 times out of 10; this is good + // enough, as it means only 1 in 10 content processes will have its own unique + // copies of the UA style sheets, and we're still getting a significant + // overall memory saving. + // + // In theory ASLR could reduce the likelihood of the mapping succeeding in + // content processes, due to our expectations of where the heap is being + // wrong, but in practice this isn't an issue. +#ifdef HAVE_64BIT_BUILD + constexpr size_t kOffset = 0x200000000ULL; // 8 GiB +#else + constexpr size_t kOffset = 0x20000000; // 512 MiB +#endif + + void* address = nullptr; + if (void* p = base::SharedMemory::FindFreeAddressSpace(2 * kOffset)) { + address = reinterpret_cast(uintptr_t(p) + kOffset); + } + if (!mSharedMemory->mShm.Map(kSharedMemorySize, address)) { + // Failed to map at the address we computed for some reason. Fall back + // to just allocating at a location of the OS's choosing, and hope that + // it works in the content process. + mSharedMemory->mShm.Map(kSharedMemorySize); + } + + Header* header = static_cast(mSharedMemory->mShm.memory()); + header->mMagic = Header::kMagic; +#ifdef DEBUG + for (auto ptr : header->mSheets) { + MOZ_RELEASE_ASSERT(!ptr, "expected shared memory to have been zeroed"); + } +#endif + + UniquePtr builder( + Servo_SharedMemoryBuilder_Create( + header->mBuffer, kSharedMemorySize - offsetof(Header, mBuffer))); + + // Copy each one into the shared memory, and record its pointer. +#define STYLE_SHEET(identifier_, url_, shared_) \ + if (shared_) { \ + StyleSheet* sheet = identifier_##Sheet(); \ + size_t i = size_t(UserAgentStyleSheetID::identifier_); \ + URLExtraData::sShared[i] = sheet->URLData(); \ + header->mSheets[i] = sheet->ToShared(builder.get()); \ + } +#include "mozilla/UserAgentStyleSheetList.h" +#undef STYLE_SHEET + + // Record how must of the shared memory we have used, for memory reporting + // later. We round up to the nearest page since the free space at the end + // of the page isn't really usable for anything else. + // + // TODO(heycam): This won't be true on Windows unless we allow creating the + // shared memory with SEC_RESERVE so that the pages are reserved but not + // committed. + size_t pageSize = ipc::SharedMemory::SystemPageSize(); + mUsedSharedMemory = + (Servo_SharedMemoryBuilder_GetLength(builder.get()) + pageSize - 1) & + ~(pageSize - 1); } nsLayoutStylesheetCache::~nsLayoutStylesheetCache() { @@ -423,9 +570,32 @@ void nsLayoutStylesheetCache::BuildPreferenceSheet( #undef NS_GET_R_G_B } +/* static */ void nsLayoutStylesheetCache::SetSharedMemory( + const base::SharedMemoryHandle& aHandle, uintptr_t aAddress) { + MOZ_ASSERT(!XRE_IsParentProcess()); + MOZ_ASSERT(!gStyleCache, + "Too late, nsLayoutStylesheetCache already created!"); + MOZ_ASSERT(!sSharedMemory, "Shouldn't call this more than once"); + + RefPtr shm = new Shm(); + if (shm->mShm.SetHandle(aHandle, /* read_only */ true) && + shm->mShm.Map(kSharedMemorySize, reinterpret_cast(aAddress))) { + sSharedMemory = shm.forget(); + } +} + +bool nsLayoutStylesheetCache::ShareToProcess( + base::ProcessId aProcessId, base::SharedMemoryHandle* aHandle) { + MOZ_ASSERT(XRE_IsParentProcess()); + return mSharedMemory && + mSharedMemory->mShm.ShareToProcess(aProcessId, aHandle); +} + mozilla::StaticRefPtr nsLayoutStylesheetCache::gStyleCache; mozilla::StaticRefPtr nsLayoutStylesheetCache::gCSSLoader; mozilla::StaticRefPtr nsLayoutStylesheetCache::gUserContentSheetURL; + +StaticRefPtr nsLayoutStylesheetCache::sSharedMemory; diff --git a/layout/style/nsLayoutStylesheetCache.h b/layout/style/nsLayoutStylesheetCache.h index fe0b63ad9dd8..215ac2233948 100644 --- a/layout/style/nsLayoutStylesheetCache.h +++ b/layout/style/nsLayoutStylesheetCache.h @@ -9,6 +9,7 @@ #include "nsIMemoryReporter.h" #include "nsIObserver.h" +#include "base/shared_memory.h" #include "mozilla/Attributes.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PreferenceSheet.h" @@ -32,15 +33,28 @@ enum FailureAction { eCrash = 0, eLogToConsole }; } // namespace css } // namespace mozilla +// Reference counted wrapper around a base::SharedMemory that will store the +// User Agent style sheets. +struct nsLayoutStylesheetCacheShm final { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsLayoutStylesheetCacheShm) + base::SharedMemory mShm; + + private: + ~nsLayoutStylesheetCacheShm() = default; +}; + class nsLayoutStylesheetCache final : public nsIObserver, public nsIMemoryReporter { + public: + using Shm = nsLayoutStylesheetCacheShm; + NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER NS_DECL_NSIMEMORYREPORTER static nsLayoutStylesheetCache* Singleton(); -#define STYLE_SHEET(identifier_, url_, lazy_) \ +#define STYLE_SHEET(identifier_, url_, shared_) \ mozilla::NotNull identifier_##Sheet(); #include "mozilla/UserAgentStyleSheetList.h" #undef STYLE_SHEET @@ -58,11 +72,47 @@ class nsLayoutStylesheetCache final : public nsIObserver, size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + // Set the shared memory segment to load the shared UA sheets from. + // Called early on in a content process' life from + // ContentChild::InitSharedUASheets, before the nsLayoutStylesheetCache + // singleton has been created. + static void SetSharedMemory(const base::SharedMemoryHandle& aHandle, + uintptr_t aAddress); + + // Obtain a shared memory handle for the shared UA sheets to pass into a + // content process. Called by ContentParent::InitInternal shortly after + // a content process has been created. + bool ShareToProcess(base::ProcessId aProcessId, + base::SharedMemoryHandle* aHandle); + + // Returns the address of the shared memory segment that holds the shared UA + // sheets. + uintptr_t GetSharedMemoryAddress() { + return mSharedMemory ? uintptr_t(mSharedMemory->mShm.memory()) : 0; + } + + // Size of the shared memory buffer we'll create to store the shared UA + // sheets. We choose a value that is big enough on both 64 bit and 32 bit. + // + // If this isn't big enough for the current contents of the shared UA + // sheets, we'll crash under InitSharedSheetsInParent. + static constexpr size_t kSharedMemorySize = 1024 * 400; + private: + // Shared memory header. + struct Header { + static constexpr uint32_t kMagic = 0x55415353; + uint32_t mMagic; // Must be set to kMagic. + const ServoCssRules* mSheets[size_t(mozilla::UserAgentStyleSheetID::Count)]; + uint8_t mBuffer[1]; + }; + nsLayoutStylesheetCache(); ~nsLayoutStylesheetCache(); void InitFromProfile(); + void InitSharedSheetsInParent(); + void InitSharedSheetsInChild(already_AddRefed aSharedMemory); void InitMemoryReporter(); void LoadSheetURL(const char* aURL, RefPtr* aSheet, mozilla::css::SheetParsingMode aParsingMode, @@ -73,6 +123,11 @@ class nsLayoutStylesheetCache final : public nsIObserver, void LoadSheet(nsIURI* aURI, RefPtr* aSheet, mozilla::css::SheetParsingMode aParsingMode, mozilla::css::FailureAction aFailureAction); + void LoadSheetFromSharedMemory(const char* aURL, + RefPtr* aSheet, + mozilla::css::SheetParsingMode aParsingMode, + Shm* aSharedMemory, Header* aHeader, + mozilla::UserAgentStyleSheetID aSheetID); void BuildPreferenceSheet(RefPtr* aSheet, const mozilla::PreferenceSheet::Prefs&); @@ -80,7 +135,7 @@ class nsLayoutStylesheetCache final : public nsIObserver, static mozilla::StaticRefPtr gCSSLoader; static mozilla::StaticRefPtr gUserContentSheetURL; -#define STYLE_SHEET(identifier_, url_, lazy_) \ +#define STYLE_SHEET(identifier_, url_, shared_) \ RefPtr m##identifier_##Sheet; #include "mozilla/UserAgentStyleSheetList.h" #undef STYLE_SHEET @@ -89,6 +144,17 @@ class nsLayoutStylesheetCache final : public nsIObserver, RefPtr mContentPreferenceSheet; RefPtr mUserChromeSheet; RefPtr mUserContentSheet; + + // Shared memory segment storing shared style sheets. + RefPtr mSharedMemory; + + // How much of the shared memory buffer we ended up using. Used for memory + // reporting. + size_t mUsedSharedMemory; + + // The shared memory to use once the nsLayoutStylesheetCache instance is + // created. + static mozilla::StaticRefPtr sSharedMemory; }; #endif diff --git a/modules/libpref/init/StaticPrefList.h b/modules/libpref/init/StaticPrefList.h index 325d7e590c7e..2ab1641dbac4 100644 --- a/modules/libpref/init/StaticPrefList.h +++ b/modules/libpref/init/StaticPrefList.h @@ -1082,6 +1082,13 @@ VARCACHE_PREF( RelaxedAtomicBool, false ) +// Are shared memory User Agent style sheets enabled? +VARCACHE_PREF( + "layout.css.shared-memory-ua-sheets.enabled", + layout_css_shared_memory_ua_sheets_enabled, + bool, false +) + //--------------------------------------------------------------------------- // JavaScript prefs //---------------------------------------------------------------------------