Bug 1839526 [wpt PR 40658] - Update wpt metadata, a=testonly
[gecko.git] / layout / style / GlobalStyleSheetCache.cpp
blob51287a7689c1bdde9da635619282fa6c865a6604
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "GlobalStyleSheetCache.h"
9 #include "nsAppDirectoryServiceDefs.h"
10 #include "nsExceptionHandler.h"
11 #include "mozilla/MemoryReporting.h"
12 #include "mozilla/Preferences.h"
13 #include "mozilla/StaticPrefs_layout.h"
14 #include "mozilla/StyleSheet.h"
15 #include "mozilla/StyleSheetInlines.h"
16 #include "mozilla/Telemetry.h"
17 #include "mozilla/css/Loader.h"
18 #include "mozilla/StaticPrefs_browser.h"
19 #include "mozilla/dom/ReferrerInfo.h"
20 #include "mozilla/dom/SRIMetadata.h"
21 #include "mozilla/ipc/SharedMemory.h"
22 #include "MainThreadUtils.h"
23 #include "nsColor.h"
24 #include "nsContentUtils.h"
25 #include "nsIConsoleService.h"
26 #include "nsIFile.h"
27 #include "nsIObserverService.h"
28 #include "nsIXULRuntime.h"
29 #include "nsNetUtil.h"
30 #include "nsPresContext.h"
31 #include "nsPrintfCString.h"
32 #include "nsServiceManagerUtils.h"
33 #include "nsXULAppAPI.h"
35 #include <mozilla/ServoBindings.h>
37 namespace mozilla {
39 // The GlobalStyleSheetCache is responsible for sharing user agent style sheet
40 // contents across processes using shared memory. Here is a high level view of
41 // how that works:
43 // * In the parent process, in the GlobalStyleSheetCache constructor (which is
44 // called early on in a process' lifetime), we parse all UA style sheets into
45 // Gecko StyleSheet objects.
47 // * The constructor calls InitSharedSheetsInParent, which creates a shared
48 // memory segment that we know ahead of time will be big enough to store the
49 // UA sheets.
51 // * It then creates a Rust SharedMemoryBuilder object and passes it a pointer
52 // to the start of the shared memory.
54 // * For each UA sheet, we call Servo_SharedMemoryBuilder_AddStylesheet, which
55 // takes the StylesheetContents::rules (an Arc<Locked<CssRules>>), produces a
56 // deep clone of it, and writes that clone into the shared memory:
58 // * The deep clone isn't a clone() call, but a call to ToShmem::to_shmem. The
59 // ToShmem trait must be implemented on every type that is reachable under
60 // the Arc<Locked<CssRules>>. The to_shmem call for each type will clone the
61 // value, but any heap allocation will be cloned and placed into the shared
62 // memory buffer, rather than heap allocated.
64 // * For most types, the ToShmem implementation is simple, and we just
65 // #[derive(ToShmem)] it. For the types that need special handling due to
66 // having heap allocations (Vec<T>, Box<T>, Arc<T>, etc.) we have impls that
67 // call to_shmem on the heap allocated data, and then create a new container
68 // (e.g. using Box::from_raw) that points into the shared memory.
70 // * Arc<T> and Locked<T> want to perform atomic writes on data that needs to
71 // be in the shared memory buffer (the reference count for Arc<T>, and the
72 // SharedRwLock's AtomicRefCell for Locked<T>), so we add special modes to
73 // those objects that skip the writes. For Arc<T>, that means never
74 // dropping the object since we don't track the reference count. That's
75 // fine, since we want to just drop the entire shared memory buffer at
76 // shutdown. For Locked<T>, we just panic on attempting to take the lock
77 // for writing. That's also fine, since we don't want devtools being able
78 // to modify UA sheets.
80 // * For Atoms in Rust, static atoms are represented by an index into the
81 // static atom table. Then if we need to Deref the Atom we look up the
82 // table. We panic if any Atom we encounter in the UA style sheets is
83 // not a static atom.
85 // * For each UA sheet, we create a new C++ StyleSheet object using the shared
86 // memory clone of the sheet contents, and throw away the original heap
87 // allocated one. (We could avoid creating a new C++ StyleSheet object
88 // wrapping the shared contents, and update the original StyleSheet object's
89 // contents, but it's doubtful that matters.)
91 // * When we initially map the shared memory in the parent process in
92 // InitSharedSheetsInParent, we choose an address which is far away from the
93 // current extent of the heap. Although not too far, since we don't want to
94 // unnecessarily fragment the virtual address space.
96 // * In the child process, as early as possible (in
97 // ContentChild::InitSharedUASheets), we try to map the shared memory at that
98 // same address, then pass the shared memory buffer to
99 // GlobalStyleSheetCache::SetSharedMemory. Since we map at the same
100 // address, this means any internal pointers in the UA sheets back into the
101 // shared memory buffer that were written by the parent process are valid in
102 // the child process too.
104 // * In practice, mapping at the address we need in the child process this works
105 // nearly all the time. If we fail to map at the address we need, the child
106 // process falls back to parsing and allocating its own copy of the UA sheets.
108 using namespace mozilla;
109 using namespace css;
111 #define PREF_LEGACY_STYLESHEET_CUSTOMIZATION \
112 "toolkit.legacyUserProfileCustomizations.stylesheets"
114 NS_IMPL_ISUPPORTS(GlobalStyleSheetCache, nsIObserver, nsIMemoryReporter)
116 nsresult GlobalStyleSheetCache::Observe(nsISupports* aSubject,
117 const char* aTopic,
118 const char16_t* aData) {
119 if (!strcmp(aTopic, "profile-before-change")) {
120 mUserContentSheet = nullptr;
121 mUserChromeSheet = nullptr;
122 } else if (!strcmp(aTopic, "profile-do-change")) {
123 InitFromProfile();
124 } else {
125 MOZ_ASSERT_UNREACHABLE("Unexpected observer topic.");
127 return NS_OK;
130 #define STYLE_SHEET(identifier_, url_, shared_) \
131 NotNull<StyleSheet*> GlobalStyleSheetCache::identifier_##Sheet() { \
132 if (!m##identifier_##Sheet) { \
133 m##identifier_##Sheet = LoadSheetURL(url_, eAgentSheetFeatures, eCrash); \
135 return WrapNotNull(m##identifier_##Sheet); \
137 #include "mozilla/UserAgentStyleSheetList.h"
138 #undef STYLE_SHEET
140 StyleSheet* GlobalStyleSheetCache::GetUserContentSheet() {
141 return mUserContentSheet;
144 StyleSheet* GlobalStyleSheetCache::GetUserChromeSheet() {
145 return mUserChromeSheet;
148 StyleSheet* GlobalStyleSheetCache::ChromePreferenceSheet() {
149 if (!mChromePreferenceSheet) {
150 BuildPreferenceSheet(&mChromePreferenceSheet,
151 PreferenceSheet::ChromePrefs());
154 return mChromePreferenceSheet;
157 StyleSheet* GlobalStyleSheetCache::ContentPreferenceSheet() {
158 if (!mContentPreferenceSheet) {
159 BuildPreferenceSheet(&mContentPreferenceSheet,
160 PreferenceSheet::ContentPrefs());
163 return mContentPreferenceSheet;
166 void GlobalStyleSheetCache::Shutdown() {
167 gCSSLoader = nullptr;
168 NS_WARNING_ASSERTION(!gStyleCache || !gUserContentSheetURL,
169 "Got the URL but never used?");
170 gStyleCache = nullptr;
171 gUserContentSheetURL = nullptr;
172 for (auto& r : URLExtraData::sShared) {
173 r = nullptr;
175 // We want to leak the shared memory forever, rather than cleaning up all
176 // potential DOM references and such that chrome code may have created.
179 void GlobalStyleSheetCache::SetUserContentCSSURL(nsIURI* aURI) {
180 MOZ_ASSERT(XRE_IsContentProcess(), "Only used in content processes.");
181 gUserContentSheetURL = aURI;
184 MOZ_DEFINE_MALLOC_SIZE_OF(LayoutStylesheetCacheMallocSizeOf)
186 NS_IMETHODIMP
187 GlobalStyleSheetCache::CollectReports(nsIHandleReportCallback* aHandleReport,
188 nsISupports* aData, bool aAnonymize) {
189 MOZ_COLLECT_REPORT("explicit/layout/style-sheet-cache/unshared", KIND_HEAP,
190 UNITS_BYTES,
191 SizeOfIncludingThis(LayoutStylesheetCacheMallocSizeOf),
192 "Memory used for built-in style sheets that are not "
193 "shared between processes.");
195 if (XRE_IsParentProcess()) {
196 MOZ_COLLECT_REPORT(
197 "explicit/layout/style-sheet-cache/shared", KIND_NONHEAP, UNITS_BYTES,
198 sSharedMemory ? sUsedSharedMemory : 0,
199 "Memory used for built-in style sheets that are shared to "
200 "child processes.");
203 return NS_OK;
206 size_t GlobalStyleSheetCache::SizeOfIncludingThis(
207 MallocSizeOf aMallocSizeOf) const {
208 size_t n = aMallocSizeOf(this);
210 #define MEASURE(s) n += s ? s->SizeOfIncludingThis(aMallocSizeOf) : 0;
212 #define STYLE_SHEET(identifier_, url_, shared_) MEASURE(m##identifier_##Sheet);
213 #include "mozilla/UserAgentStyleSheetList.h"
214 #undef STYLE_SHEET
216 MEASURE(mChromePreferenceSheet);
217 MEASURE(mContentPreferenceSheet);
218 MEASURE(mUserChromeSheet);
219 MEASURE(mUserContentSheet);
221 // Measurement of the following members may be added later if DMD finds it is
222 // worthwhile:
223 // - gCSSLoader
225 return n;
228 GlobalStyleSheetCache::GlobalStyleSheetCache() {
229 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
230 NS_ASSERTION(obsSvc, "No global observer service?");
232 if (obsSvc) {
233 obsSvc->AddObserver(this, "profile-before-change", false);
234 obsSvc->AddObserver(this, "profile-do-change", false);
237 // Load user style sheets.
238 InitFromProfile();
240 if (XRE_IsParentProcess()) {
241 // We know we need xul.css for the UI, so load that now too:
242 XULSheet();
245 if (gUserContentSheetURL) {
246 MOZ_ASSERT(XRE_IsContentProcess(), "Only used in content processes.");
247 mUserContentSheet =
248 LoadSheet(gUserContentSheetURL, eUserSheetFeatures, eLogToConsole);
249 gUserContentSheetURL = nullptr;
252 // If we are the in the parent process, then we load all of the UA sheets that
253 // are shareable and store them into shared memory. In both the parent and
254 // the content process, we load these sheets out of shared memory.
256 // The shared memory buffer's format is a Header object, which contains
257 // internal pointers to each of the shared style sheets, followed by the style
258 // sheets themselves.
259 if (StaticPrefs::layout_css_shared_memory_ua_sheets_enabled()) {
260 if (XRE_IsParentProcess()) {
261 // Load the style sheets and store them in a new shared memory buffer.
262 InitSharedSheetsInParent();
263 } else if (sSharedMemory) {
264 // Use the shared memory handle that was given to us by a SetSharedMemory
265 // call under ContentChild::InitXPCOM.
266 MOZ_ASSERT(sSharedMemory->memory(),
267 "GlobalStyleSheetCache::SetSharedMemory should have mapped "
268 "the shared memory");
272 // If we get here and we don't have a shared memory handle, then it means
273 // either we failed to create the shared memory buffer in the parent process
274 // (unexpected), or we failed to map the shared memory buffer at the address
275 // we needed in the content process (might happen).
277 // If sSharedMemory is non-null, but it is not currently mapped, then it means
278 // we are in the parent process, and we failed to re-map the memory after
279 // freezing it. (We keep sSharedMemory around so that we can still share it
280 // to content processes.)
282 // In the parent process, this means we'll just leave our eagerly loaded
283 // non-shared sheets in the mFooSheet fields. In a content process, we'll
284 // lazily load our own copies of the sheets later.
285 if (sSharedMemory) {
286 if (auto* header = static_cast<Header*>(sSharedMemory->memory())) {
287 MOZ_RELEASE_ASSERT(header->mMagic == Header::kMagic);
289 #define STYLE_SHEET(identifier_, url_, shared_) \
290 if (shared_) { \
291 LoadSheetFromSharedMemory(url_, &m##identifier_##Sheet, \
292 eAgentSheetFeatures, header, \
293 UserAgentStyleSheetID::identifier_); \
295 #include "mozilla/UserAgentStyleSheetList.h"
296 #undef STYLE_SHEET
301 void GlobalStyleSheetCache::LoadSheetFromSharedMemory(
302 const char* aURL, RefPtr<StyleSheet>* aSheet, SheetParsingMode aParsingMode,
303 Header* aHeader, UserAgentStyleSheetID aSheetID) {
304 auto i = size_t(aSheetID);
306 auto sheet =
307 MakeRefPtr<StyleSheet>(aParsingMode, CORS_NONE, dom::SRIMetadata());
309 nsCOMPtr<nsIURI> uri;
310 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), aURL));
312 sheet->SetPrincipal(nsContentUtils::GetSystemPrincipal());
313 sheet->SetURIs(uri, uri, uri);
314 sheet->SetSharedContents(aHeader->mSheets[i]);
315 sheet->SetComplete();
317 nsCOMPtr<nsIReferrerInfo> referrerInfo =
318 dom::ReferrerInfo::CreateForExternalCSSResources(sheet);
319 sheet->SetReferrerInfo(referrerInfo);
320 URLExtraData::sShared[i] = sheet->URLData();
322 *aSheet = std::move(sheet);
325 void GlobalStyleSheetCache::InitSharedSheetsInParent() {
326 MOZ_ASSERT(XRE_IsParentProcess());
327 MOZ_RELEASE_ASSERT(!sSharedMemory);
329 auto shm = MakeUnique<base::SharedMemory>();
330 if (NS_WARN_IF(!shm->CreateFreezeable(kSharedMemorySize))) {
331 return;
334 // We need to choose an address to map the shared memory in the parent process
335 // that we'll also be able to use in content processes. There's no way to
336 // pick an address that is guaranteed to be free in future content processes,
337 // so instead we pick an address that is some distance away from current heap
338 // allocations and hope that by the time the content process maps the shared
339 // memory, that address will be free.
341 // On 64 bit, we have a large amount of address space, so we pick an address
342 // half way through the next 8 GiB of free space, and this has a very good
343 // chance of succeeding. On 32 bit, address space is more constrained. We
344 // only have 3 GiB of space to work with, and we don't want to pick a location
345 // right in the middle, since that could cause future large allocations to
346 // fail. So we pick an address half way through the next 512 MiB of free
347 // space. Experimentally this seems to work 9 times out of 10; this is good
348 // enough, as it means only 1 in 10 content processes will have its own unique
349 // copies of the UA style sheets, and we're still getting a significant
350 // overall memory saving.
352 // In theory ASLR could reduce the likelihood of the mapping succeeding in
353 // content processes, due to our expectations of where the heap is being
354 // wrong, but in practice this isn't an issue.
355 #ifdef HAVE_64BIT_BUILD
356 constexpr size_t kOffset = 0x200000000ULL; // 8 GiB
357 #else
358 constexpr size_t kOffset = 0x20000000; // 512 MiB
359 #endif
361 void* address = nullptr;
362 if (void* p = base::SharedMemory::FindFreeAddressSpace(2 * kOffset)) {
363 address = reinterpret_cast<void*>(uintptr_t(p) + kOffset);
366 if (!shm->Map(kSharedMemorySize, address)) {
367 // Failed to map at the address we computed for some reason. Fall back
368 // to just allocating at a location of the OS's choosing, and hope that
369 // it works in the content process.
370 if (NS_WARN_IF(!shm->Map(kSharedMemorySize))) {
371 return;
374 address = shm->memory();
376 auto* header = static_cast<Header*>(address);
377 header->mMagic = Header::kMagic;
378 #ifdef DEBUG
379 for (const auto* ptr : header->mSheets) {
380 MOZ_RELEASE_ASSERT(!ptr, "expected shared memory to have been zeroed");
382 #endif
384 UniquePtr<StyleSharedMemoryBuilder> builder(Servo_SharedMemoryBuilder_Create(
385 header->mBuffer, kSharedMemorySize - offsetof(Header, mBuffer)));
387 nsCString message;
389 // Copy each one into the shared memory, and record its pointer.
391 // Normally calling ToShared on UA sheets should not fail. It happens
392 // in practice in odd cases that seem like corrupted installations; see bug
393 // 1621773. On failure, return early and fall back to non-shared sheets.
394 #define STYLE_SHEET(identifier_, url_, shared_) \
395 if (shared_) { \
396 StyleSheet* sheet = identifier_##Sheet(); \
397 size_t i = size_t(UserAgentStyleSheetID::identifier_); \
398 URLExtraData::sShared[i] = sheet->URLData(); \
399 header->mSheets[i] = sheet->ToShared(builder.get(), message); \
400 if (!header->mSheets[i]) { \
401 CrashReporter::AppendAppNotesToCrashReport("\n"_ns + message); \
402 return; \
405 #include "mozilla/UserAgentStyleSheetList.h"
406 #undef STYLE_SHEET
408 // Finished writing into the shared memory. Freeze it, so that a process
409 // can't confuse other processes by changing the UA style sheet contents.
410 if (NS_WARN_IF(!shm->Freeze())) {
411 return;
414 // The Freeze() call unmaps the shared memory. Re-map it again as read only.
415 // If this fails, due to something else being mapped into the same place
416 // between the Freeze() and Map() call, we can just fall back to keeping our
417 // own copy of the UA style sheets in the parent, and still try sending the
418 // shared memory to the content processes.
419 shm->Map(kSharedMemorySize, address);
421 // Record how must of the shared memory we have used, for memory reporting
422 // later. We round up to the nearest page since the free space at the end
423 // of the page isn't really usable for anything else.
425 // TODO(heycam): This won't be true on Windows unless we allow creating the
426 // shared memory with SEC_RESERVE so that the pages are reserved but not
427 // committed.
428 size_t pageSize = ipc::SharedMemory::SystemPageSize();
429 sUsedSharedMemory =
430 (Servo_SharedMemoryBuilder_GetLength(builder.get()) + pageSize - 1) &
431 ~(pageSize - 1);
433 sSharedMemory = shm.release();
436 GlobalStyleSheetCache::~GlobalStyleSheetCache() {
437 UnregisterWeakMemoryReporter(this);
440 void GlobalStyleSheetCache::InitMemoryReporter() {
441 RegisterWeakMemoryReporter(this);
444 /* static */
445 GlobalStyleSheetCache* GlobalStyleSheetCache::Singleton() {
446 MOZ_ASSERT(NS_IsMainThread());
448 if (!gStyleCache) {
449 gStyleCache = new GlobalStyleSheetCache;
450 gStyleCache->InitMemoryReporter();
452 // For each pref that controls a CSS feature that a UA style sheet depends
453 // on (such as a pref that enables a property that a UA style sheet uses),
454 // register DependentPrefChanged as a callback to ensure that the relevant
455 // style sheets will be re-parsed.
456 // Preferences::RegisterCallback(&DependentPrefChanged,
457 // "layout.css.example-pref.enabled");
460 return gStyleCache;
463 void GlobalStyleSheetCache::InitFromProfile() {
464 if (!Preferences::GetBool(PREF_LEGACY_STYLESHEET_CUSTOMIZATION)) {
465 return;
468 nsCOMPtr<nsIXULRuntime> appInfo =
469 do_GetService("@mozilla.org/xre/app-info;1");
470 if (appInfo) {
471 bool inSafeMode = false;
472 appInfo->GetInSafeMode(&inSafeMode);
473 if (inSafeMode) return;
475 nsCOMPtr<nsIFile> contentFile;
476 nsCOMPtr<nsIFile> chromeFile;
478 NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR, getter_AddRefs(contentFile));
479 if (!contentFile) {
480 // if we don't have a profile yet, that's OK!
481 return;
484 contentFile->Clone(getter_AddRefs(chromeFile));
485 if (!chromeFile) return;
487 contentFile->Append(u"userContent.css"_ns);
488 chromeFile->Append(u"userChrome.css"_ns);
490 mUserContentSheet = LoadSheetFile(contentFile, eUserSheetFeatures);
491 mUserChromeSheet = LoadSheetFile(chromeFile, eUserSheetFeatures);
494 RefPtr<StyleSheet> GlobalStyleSheetCache::LoadSheetURL(
495 const char* aURL, SheetParsingMode aParsingMode,
496 FailureAction aFailureAction) {
497 nsCOMPtr<nsIURI> uri;
498 NS_NewURI(getter_AddRefs(uri), aURL);
499 return LoadSheet(uri, aParsingMode, aFailureAction);
502 RefPtr<StyleSheet> GlobalStyleSheetCache::LoadSheetFile(
503 nsIFile* aFile, SheetParsingMode aParsingMode) {
504 bool exists = false;
505 aFile->Exists(&exists);
506 if (!exists) {
507 return nullptr;
510 nsCOMPtr<nsIURI> uri;
511 NS_NewFileURI(getter_AddRefs(uri), aFile);
512 return LoadSheet(uri, aParsingMode, eLogToConsole);
515 static void ErrorLoadingSheet(nsIURI* aURI, const char* aMsg,
516 FailureAction aFailureAction) {
517 nsPrintfCString errorMessage("%s loading built-in stylesheet '%s'", aMsg,
518 aURI ? aURI->GetSpecOrDefault().get() : "");
519 if (aFailureAction == eLogToConsole) {
520 nsCOMPtr<nsIConsoleService> cs =
521 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
522 if (cs) {
523 cs->LogStringMessage(NS_ConvertUTF8toUTF16(errorMessage).get());
524 return;
528 MOZ_CRASH_UNSAFE(errorMessage.get());
531 RefPtr<StyleSheet> GlobalStyleSheetCache::LoadSheet(
532 nsIURI* aURI, SheetParsingMode aParsingMode, FailureAction aFailureAction) {
533 if (!aURI) {
534 ErrorLoadingSheet(aURI, "null URI", eCrash);
535 return nullptr;
538 if (!gCSSLoader) {
539 gCSSLoader = new Loader;
542 // Note: The parallel parsing code assume that UA sheets are always loaded
543 // synchronously like they are here, and thus that we'll never attempt
544 // parallel parsing on them. If that ever changes, we'll either need to find a
545 // different way to prohibit parallel parsing for UA sheets, or handle
546 // -moz-bool-pref and various other things in the parallel parsing code.
547 auto result = gCSSLoader->LoadSheetSync(aURI, aParsingMode,
548 css::Loader::UseSystemPrincipal::Yes);
549 if (MOZ_UNLIKELY(result.isErr())) {
550 ErrorLoadingSheet(
551 aURI,
552 nsPrintfCString("LoadSheetSync failed with error %" PRIx32,
553 static_cast<uint32_t>(result.unwrapErr()))
554 .get(),
555 aFailureAction);
557 return result.unwrapOr(nullptr);
560 /* static */
561 void GlobalStyleSheetCache::InvalidatePreferenceSheets() {
562 if (gStyleCache) {
563 gStyleCache->mContentPreferenceSheet = nullptr;
564 gStyleCache->mChromePreferenceSheet = nullptr;
568 void GlobalStyleSheetCache::BuildPreferenceSheet(
569 RefPtr<StyleSheet>* aSheet, const PreferenceSheet::Prefs& aPrefs) {
570 *aSheet = new StyleSheet(eAgentSheetFeatures, CORS_NONE, dom::SRIMetadata());
572 StyleSheet* sheet = *aSheet;
574 nsCOMPtr<nsIURI> uri;
575 NS_NewURI(getter_AddRefs(uri), "about:PreferenceStyleSheet");
576 MOZ_ASSERT(uri, "URI creation shouldn't fail");
578 sheet->SetURIs(uri, uri, uri);
579 sheet->SetComplete();
581 static const uint32_t kPreallocSize = 1024;
583 nsCString sheetText;
584 sheetText.SetCapacity(kPreallocSize);
586 #define NS_GET_R_G_B(color_) \
587 NS_GET_R(color_), NS_GET_G(color_), NS_GET_B(color_)
589 sheetText.AppendLiteral(
590 "@namespace url(http://www.w3.org/1999/xhtml);\n"
591 "@namespace svg url(http://www.w3.org/2000/svg);\n");
593 // Rules for link styling.
594 const bool underlineLinks = StaticPrefs::browser_underline_anchors();
595 sheetText.AppendPrintf("*|*:any-link%s { text-decoration: %s; }\n",
596 underlineLinks ? ":not(svg|a)" : "",
597 underlineLinks ? "underline" : "none");
599 // Rules for focus styling.
601 const bool focusRingOnAnything =
602 StaticPrefs::browser_display_focus_ring_on_anything();
603 uint8_t focusRingWidth = StaticPrefs::browser_display_focus_ring_width();
604 uint8_t focusRingStyle = StaticPrefs::browser_display_focus_ring_style();
606 if ((focusRingWidth != 1 && focusRingWidth <= 4) || focusRingOnAnything) {
607 if (focusRingWidth != 1) {
608 // If the focus ring width is different from the default, fix buttons
609 // with rings.
610 sheetText.AppendPrintf(
611 "button::-moz-focus-inner, input[type=\"reset\"]::-moz-focus-inner, "
612 "input[type=\"button\"]::-moz-focus-inner, "
613 "input[type=\"submit\"]::-moz-focus-inner { "
614 "border: %dpx %s transparent !important; }\n",
615 focusRingWidth, focusRingStyle == 0 ? "solid" : "dotted");
617 sheetText.AppendLiteral(
618 "button:focus::-moz-focus-inner, "
619 "input[type=\"reset\"]:focus::-moz-focus-inner, "
620 "input[type=\"button\"]:focus::-moz-focus-inner, "
621 "input[type=\"submit\"]:focus::-moz-focus-inner { "
622 "border-color: ButtonText !important; }\n");
625 sheetText.AppendPrintf(
626 "%s { outline: %dpx %s !important; }\n",
627 focusRingOnAnything ? ":focus" : "*|*:link:focus, *|*:visited:focus",
628 focusRingWidth,
629 focusRingStyle == 0 ? "solid -moz-mac-focusring" : "dotted WindowText");
632 if (StaticPrefs::browser_display_use_focus_colors()) {
633 const auto& colors = aPrefs.mLightColors;
634 nscolor focusText = colors.mFocusText;
635 nscolor focusBG = colors.mFocusBackground;
636 sheetText.AppendPrintf(
637 "*:focus, *:focus > font { color: #%02x%02x%02x !important; "
638 "background-color: #%02x%02x%02x !important; }\n",
639 NS_GET_R_G_B(focusText), NS_GET_R_G_B(focusBG));
642 NS_ASSERTION(sheetText.Length() <= kPreallocSize,
643 "kPreallocSize should be big enough to build preference style "
644 "sheet without reallocation");
646 // NB: The pref sheet never has @import rules, thus no loader.
647 sheet->ParseSheetSync(nullptr, sheetText,
648 /* aLoadData = */ nullptr,
649 /* aLineNumber = */ 0);
651 #undef NS_GET_R_G_B
654 bool GlobalStyleSheetCache::AffectedByPref(const nsACString& aPref) {
655 const char* prefs[] = {
656 StaticPrefs::GetPrefName_browser_display_show_focus_rings(),
657 StaticPrefs::GetPrefName_browser_display_focus_ring_style(),
658 StaticPrefs::GetPrefName_browser_display_focus_ring_width(),
659 StaticPrefs::GetPrefName_browser_display_focus_ring_on_anything(),
660 StaticPrefs::GetPrefName_browser_display_use_focus_colors(),
661 StaticPrefs::GetPrefName_browser_underline_anchors(),
664 for (const char* pref : prefs) {
665 if (aPref.Equals(pref)) {
666 return true;
670 return false;
673 /* static */ void GlobalStyleSheetCache::SetSharedMemory(
674 base::SharedMemoryHandle aHandle, uintptr_t aAddress) {
675 MOZ_ASSERT(!XRE_IsParentProcess());
676 MOZ_ASSERT(!gStyleCache, "Too late, GlobalStyleSheetCache already created!");
677 MOZ_ASSERT(!sSharedMemory, "Shouldn't call this more than once");
679 auto shm = MakeUnique<base::SharedMemory>();
680 if (!shm->SetHandle(std::move(aHandle), /* read_only */ true)) {
681 return;
684 if (shm->Map(kSharedMemorySize, reinterpret_cast<void*>(aAddress))) {
685 sSharedMemory = shm.release();
689 base::SharedMemoryHandle GlobalStyleSheetCache::CloneHandle() {
690 MOZ_ASSERT(XRE_IsParentProcess());
691 if (sSharedMemory) {
692 return sSharedMemory->CloneHandle();
694 return nullptr;
697 StaticRefPtr<GlobalStyleSheetCache> GlobalStyleSheetCache::gStyleCache;
698 StaticRefPtr<css::Loader> GlobalStyleSheetCache::gCSSLoader;
699 StaticRefPtr<nsIURI> GlobalStyleSheetCache::gUserContentSheetURL;
701 StaticAutoPtr<base::SharedMemory> GlobalStyleSheetCache::sSharedMemory;
702 size_t GlobalStyleSheetCache::sUsedSharedMemory;
704 } // namespace mozilla