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/. */
6
7 #include "nsLayoutStylesheetCache.h"
8
9 #include "nsAppDirectoryServiceDefs.h"
10 #include "mozilla/StyleSheetInlines.h"
11 #include "mozilla/MemoryReporting.h"
12 #include "mozilla/Preferences.h"
13 #include "mozilla/StyleSheet.h"
14 #include "mozilla/StyleSheetInlines.h"
15 #include "mozilla/css/Loader.h"
16 #include "mozilla/dom/SRIMetadata.h"
17 #include "nsIConsoleService.h"
18 #include "nsIFile.h"
19 #include "nsNetUtil.h"
20 #include "nsIObserverService.h"
21 #include "nsServiceManagerUtils.h"
22 #include "nsIXULRuntime.h"
23 #include "nsPrintfCString.h"
24 #include "nsXULAppAPI.h"
25
26 // Includes for the crash report annotation in ErrorLoadingSheet.
27 #ifdef MOZ_CRASHREPORTER
28 #include "mozilla/Omnijar.h"
29 #include "nsDirectoryService.h"
30 #include "nsDirectoryServiceDefs.h"
31 #include "nsExceptionHandler.h"
32 #include "nsIChromeRegistry.h"
33 #include "nsISimpleEnumerator.h"
34 #include "nsISubstitutingProtocolHandler.h"
35 #include "zlib.h"
36 #include "nsZipArchive.h"
37 #endif
38
39 using namespace mozilla;
40 using namespace mozilla::css;
41
42 static bool sNumberControlEnabled;
43
44 #define NUMBER_CONTROL_PREF "dom.forms.number"
45
NS_IMPL_ISUPPORTS(nsLayoutStylesheetCache,nsIObserver,nsIMemoryReporter)46 NS_IMPL_ISUPPORTS(
47 nsLayoutStylesheetCache, nsIObserver, nsIMemoryReporter)
48
49 nsresult
50 nsLayoutStylesheetCache::Observe(nsISupports* aSubject,
51 const char* aTopic,
52 const char16_t* aData)
53 {
54 if (!strcmp(aTopic, "profile-before-change")) {
55 mUserContentSheet = nullptr;
56 mUserChromeSheet = nullptr;
57 }
58 else if (!strcmp(aTopic, "profile-do-change")) {
59 InitFromProfile();
60 }
61 else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 ||
62 strcmp(aTopic, "chrome-flush-caches") == 0) {
63 mScrollbarsSheet = nullptr;
64 mFormsSheet = nullptr;
65 mNumberControlSheet = nullptr;
66 }
67 else {
68 NS_NOTREACHED("Unexpected observer topic.");
69 }
70 return NS_OK;
71 }
72
73 StyleSheet*
ScrollbarsSheet()74 nsLayoutStylesheetCache::ScrollbarsSheet()
75 {
76 if (!mScrollbarsSheet) {
77 // Scrollbars don't need access to unsafe rules
78 LoadSheetURL("chrome://global/skin/scrollbars.css",
79 &mScrollbarsSheet, eAuthorSheetFeatures, eCrash);
80 }
81
82 return mScrollbarsSheet;
83 }
84
85 StyleSheet*
FormsSheet()86 nsLayoutStylesheetCache::FormsSheet()
87 {
88 if (!mFormsSheet) {
89 // forms.css needs access to unsafe rules
90 LoadSheetURL("resource://gre-resources/forms.css",
91 &mFormsSheet, eAgentSheetFeatures, eCrash);
92 }
93
94 return mFormsSheet;
95 }
96
97 StyleSheet*
NumberControlSheet()98 nsLayoutStylesheetCache::NumberControlSheet()
99 {
100 if (!sNumberControlEnabled) {
101 return nullptr;
102 }
103
104 if (!mNumberControlSheet) {
105 LoadSheetURL("resource://gre-resources/number-control.css",
106 &mNumberControlSheet, eAgentSheetFeatures, eCrash);
107 }
108
109 return mNumberControlSheet;
110 }
111
112 StyleSheet*
UserContentSheet()113 nsLayoutStylesheetCache::UserContentSheet()
114 {
115 return mUserContentSheet;
116 }
117
118 StyleSheet*
UserChromeSheet()119 nsLayoutStylesheetCache::UserChromeSheet()
120 {
121 return mUserChromeSheet;
122 }
123
124 StyleSheet*
UASheet()125 nsLayoutStylesheetCache::UASheet()
126 {
127 if (!mUASheet) {
128 LoadSheetURL("resource://gre-resources/ua.css",
129 &mUASheet, eAgentSheetFeatures, eCrash);
130 }
131
132 return mUASheet;
133 }
134
135 StyleSheet*
HTMLSheet()136 nsLayoutStylesheetCache::HTMLSheet()
137 {
138 if (!mHTMLSheet) {
139 LoadSheetURL("resource://gre-resources/html.css",
140 &mHTMLSheet, eAgentSheetFeatures, eCrash);
141 }
142
143 return mHTMLSheet;
144 }
145
146 StyleSheet*
MinimalXULSheet()147 nsLayoutStylesheetCache::MinimalXULSheet()
148 {
149 return mMinimalXULSheet;
150 }
151
152 StyleSheet*
XULSheet()153 nsLayoutStylesheetCache::XULSheet()
154 {
155 return mXULSheet;
156 }
157
158 StyleSheet*
QuirkSheet()159 nsLayoutStylesheetCache::QuirkSheet()
160 {
161 return mQuirkSheet;
162 }
163
164 StyleSheet*
SVGSheet()165 nsLayoutStylesheetCache::SVGSheet()
166 {
167 return mSVGSheet;
168 }
169
170 StyleSheet*
MathMLSheet()171 nsLayoutStylesheetCache::MathMLSheet()
172 {
173 if (!mMathMLSheet) {
174 LoadSheetURL("resource://gre-resources/mathml.css",
175 &mMathMLSheet, eAgentSheetFeatures, eCrash);
176 }
177
178 return mMathMLSheet;
179 }
180
181 StyleSheet*
CounterStylesSheet()182 nsLayoutStylesheetCache::CounterStylesSheet()
183 {
184 return mCounterStylesSheet;
185 }
186
187 StyleSheet*
NoScriptSheet()188 nsLayoutStylesheetCache::NoScriptSheet()
189 {
190 if (!mNoScriptSheet) {
191 LoadSheetURL("resource://gre-resources/noscript.css",
192 &mNoScriptSheet, eAgentSheetFeatures, eCrash);
193 }
194
195 return mNoScriptSheet;
196 }
197
198 StyleSheet*
NoFramesSheet()199 nsLayoutStylesheetCache::NoFramesSheet()
200 {
201 if (!mNoFramesSheet) {
202 LoadSheetURL("resource://gre-resources/noframes.css",
203 &mNoFramesSheet, eAgentSheetFeatures, eCrash);
204 }
205
206 return mNoFramesSheet;
207 }
208
209 StyleSheet*
ChromePreferenceSheet(nsPresContext * aPresContext)210 nsLayoutStylesheetCache::ChromePreferenceSheet(nsPresContext* aPresContext)
211 {
212 if (!mChromePreferenceSheet) {
213 BuildPreferenceSheet(&mChromePreferenceSheet, aPresContext);
214 }
215
216 return mChromePreferenceSheet;
217 }
218
219 StyleSheet*
ContentPreferenceSheet(nsPresContext * aPresContext)220 nsLayoutStylesheetCache::ContentPreferenceSheet(nsPresContext* aPresContext)
221 {
222 if (!mContentPreferenceSheet) {
223 BuildPreferenceSheet(&mContentPreferenceSheet, aPresContext);
224 }
225
226 return mContentPreferenceSheet;
227 }
228
229 StyleSheet*
ContentEditableSheet()230 nsLayoutStylesheetCache::ContentEditableSheet()
231 {
232 if (!mContentEditableSheet) {
233 LoadSheetURL("resource://gre/res/contenteditable.css",
234 &mContentEditableSheet, eAgentSheetFeatures, eCrash);
235 }
236
237 return mContentEditableSheet;
238 }
239
240 StyleSheet*
DesignModeSheet()241 nsLayoutStylesheetCache::DesignModeSheet()
242 {
243 if (!mDesignModeSheet) {
244 LoadSheetURL("resource://gre/res/designmode.css",
245 &mDesignModeSheet, eAgentSheetFeatures, eCrash);
246 }
247
248 return mDesignModeSheet;
249 }
250
251 void
Shutdown()252 nsLayoutStylesheetCache::Shutdown()
253 {
254 gCSSLoader_Gecko = nullptr;
255 gCSSLoader_Servo = nullptr;
256 gStyleCache_Gecko = nullptr;
257 gStyleCache_Servo = nullptr;
258 MOZ_ASSERT(!gUserContentSheetURL, "Got the URL but never used?");
259 }
260
261 void
SetUserContentCSSURL(nsIURI * aURI)262 nsLayoutStylesheetCache::SetUserContentCSSURL(nsIURI* aURI)
263 {
264 MOZ_ASSERT(XRE_IsContentProcess(), "Only used in content processes.");
265 gUserContentSheetURL = aURI;
266 }
267
MOZ_DEFINE_MALLOC_SIZE_OF(LayoutStylesheetCacheMallocSizeOf)268 MOZ_DEFINE_MALLOC_SIZE_OF(LayoutStylesheetCacheMallocSizeOf)
269
270 NS_IMETHODIMP
271 nsLayoutStylesheetCache::CollectReports(nsIHandleReportCallback* aHandleReport,
272 nsISupports* aData, bool aAnonymize)
273 {
274 MOZ_COLLECT_REPORT(
275 "explicit/layout/style-sheet-cache", KIND_HEAP, UNITS_BYTES,
276 SizeOfIncludingThis(LayoutStylesheetCacheMallocSizeOf),
277 "Memory used for some built-in style sheets.");
278
279 return NS_OK;
280 }
281
282
283 size_t
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const284 nsLayoutStylesheetCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
285 {
286 size_t n = aMallocSizeOf(this);
287
288 #define MEASURE(s) n += s ? s->SizeOfIncludingThis(aMallocSizeOf) : 0;
289
290 MEASURE(mChromePreferenceSheet);
291 MEASURE(mContentEditableSheet);
292 MEASURE(mContentPreferenceSheet);
293 MEASURE(mCounterStylesSheet);
294 MEASURE(mDesignModeSheet);
295 MEASURE(mFormsSheet);
296 MEASURE(mHTMLSheet);
297 MEASURE(mMathMLSheet);
298 MEASURE(mMinimalXULSheet);
299 MEASURE(mNoFramesSheet);
300 MEASURE(mNoScriptSheet);
301 MEASURE(mNumberControlSheet);
302 MEASURE(mQuirkSheet);
303 MEASURE(mSVGSheet);
304 MEASURE(mScrollbarsSheet);
305 MEASURE(mUASheet);
306 MEASURE(mUserChromeSheet);
307 MEASURE(mUserContentSheet);
308 MEASURE(mXULSheet);
309
310 // Measurement of the following members may be added later if DMD finds it is
311 // worthwhile:
312 // - gCSSLoader_Gecko
313 // - gCSSLoader_Servo
314
315 return n;
316 }
317
nsLayoutStylesheetCache(StyleBackendType aType)318 nsLayoutStylesheetCache::nsLayoutStylesheetCache(StyleBackendType aType)
319 : mBackendType(aType)
320 {
321 nsCOMPtr<nsIObserverService> obsSvc =
322 mozilla::services::GetObserverService();
323 NS_ASSERTION(obsSvc, "No global observer service?");
324
325 if (obsSvc) {
326 obsSvc->AddObserver(this, "profile-before-change", false);
327 obsSvc->AddObserver(this, "profile-do-change", false);
328 obsSvc->AddObserver(this, "chrome-flush-skin-caches", false);
329 obsSvc->AddObserver(this, "chrome-flush-caches", false);
330 }
331
332 InitFromProfile();
333
334 // And make sure that we load our UA sheets. No need to do this
335 // per-profile, since they're profile-invariant.
336 LoadSheetURL("resource://gre-resources/counterstyles.css",
337 &mCounterStylesSheet, eAgentSheetFeatures, eCrash);
338 LoadSheetURL("chrome://global/content/minimal-xul.css",
339 &mMinimalXULSheet, eAgentSheetFeatures, eCrash);
340 LoadSheetURL("resource://gre-resources/quirk.css",
341 &mQuirkSheet, eAgentSheetFeatures, eCrash);
342 LoadSheetURL("resource://gre/res/svg.css",
343 &mSVGSheet, eAgentSheetFeatures, eCrash);
344 LoadSheetURL("chrome://global/content/xul.css",
345 &mXULSheet, eAgentSheetFeatures, eCrash);
346
347 if (gUserContentSheetURL) {
348 MOZ_ASSERT(XRE_IsContentProcess(), "Only used in content processes.");
349 LoadSheet(gUserContentSheetURL, &mUserContentSheet, eUserSheetFeatures, eLogToConsole);
350 gUserContentSheetURL = nullptr;
351 }
352
353 // The remaining sheets are created on-demand do to their use being rarer
354 // (which helps save memory for Firefox OS apps) or because they need to
355 // be re-loadable in DependentPrefChanged.
356 }
357
~nsLayoutStylesheetCache()358 nsLayoutStylesheetCache::~nsLayoutStylesheetCache()
359 {
360 mozilla::UnregisterWeakMemoryReporter(this);
361 }
362
363 void
InitMemoryReporter()364 nsLayoutStylesheetCache::InitMemoryReporter()
365 {
366 mozilla::RegisterWeakMemoryReporter(this);
367 }
368
369 /* static */ nsLayoutStylesheetCache*
For(StyleBackendType aType)370 nsLayoutStylesheetCache::For(StyleBackendType aType)
371 {
372 MOZ_ASSERT(NS_IsMainThread());
373
374 bool mustInit = !gStyleCache_Gecko && !gStyleCache_Servo;
375 auto& cache = aType == StyleBackendType::Gecko ? gStyleCache_Gecko :
376 gStyleCache_Servo;
377
378 if (!cache) {
379 cache = new nsLayoutStylesheetCache(aType);
380 cache->InitMemoryReporter();
381 }
382
383 if (mustInit) {
384 // Initialization that only needs to be done once for both
385 // nsLayoutStylesheetCaches.
386
387 Preferences::AddBoolVarCache(&sNumberControlEnabled, NUMBER_CONTROL_PREF,
388 true);
389
390 // For each pref that controls a CSS feature that a UA style sheet depends
391 // on (such as a pref that enables a property that a UA style sheet uses),
392 // register DependentPrefChanged as a callback to ensure that the relevant
393 // style sheets will be re-parsed.
394 // Preferences::RegisterCallback(&DependentPrefChanged,
395 // "layout.css.example-pref.enabled");
396 Preferences::RegisterCallback(&DependentPrefChanged,
397 "layout.css.grid.enabled");
398 Preferences::RegisterCallback(&DependentPrefChanged,
399 "dom.details_element.enabled");
400 }
401
402 return cache;
403 }
404
405 void
InitFromProfile()406 nsLayoutStylesheetCache::InitFromProfile()
407 {
408 nsCOMPtr<nsIXULRuntime> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
409 if (appInfo) {
410 bool inSafeMode = false;
411 appInfo->GetInSafeMode(&inSafeMode);
412 if (inSafeMode)
413 return;
414 }
415 nsCOMPtr<nsIFile> contentFile;
416 nsCOMPtr<nsIFile> chromeFile;
417
418 NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR,
419 getter_AddRefs(contentFile));
420 if (!contentFile) {
421 // if we don't have a profile yet, that's OK!
422 return;
423 }
424
425 contentFile->Clone(getter_AddRefs(chromeFile));
426 if (!chromeFile) return;
427
428 contentFile->Append(NS_LITERAL_STRING("userContent.css"));
429 chromeFile->Append(NS_LITERAL_STRING("userChrome.css"));
430
431 LoadSheetFile(contentFile, &mUserContentSheet, eUserSheetFeatures, eLogToConsole);
432 LoadSheetFile(chromeFile, &mUserChromeSheet, eUserSheetFeatures, eLogToConsole);
433 }
434
435 void
LoadSheetURL(const char * aURL,RefPtr<StyleSheet> * aSheet,SheetParsingMode aParsingMode,FailureAction aFailureAction)436 nsLayoutStylesheetCache::LoadSheetURL(const char* aURL,
437 RefPtr<StyleSheet>* aSheet,
438 SheetParsingMode aParsingMode,
439 FailureAction aFailureAction)
440 {
441 nsCOMPtr<nsIURI> uri;
442 NS_NewURI(getter_AddRefs(uri), aURL);
443 LoadSheet(uri, aSheet, aParsingMode, aFailureAction);
444 if (!aSheet) {
445 NS_ERROR(nsPrintfCString("Could not load %s", aURL).get());
446 }
447 }
448
449 void
LoadSheetFile(nsIFile * aFile,RefPtr<StyleSheet> * aSheet,SheetParsingMode aParsingMode,FailureAction aFailureAction)450 nsLayoutStylesheetCache::LoadSheetFile(nsIFile* aFile,
451 RefPtr<StyleSheet>* aSheet,
452 SheetParsingMode aParsingMode,
453 FailureAction aFailureAction)
454 {
455 bool exists = false;
456 aFile->Exists(&exists);
457
458 if (!exists) return;
459
460 nsCOMPtr<nsIURI> uri;
461 NS_NewFileURI(getter_AddRefs(uri), aFile);
462
463 LoadSheet(uri, aSheet, aParsingMode, aFailureAction);
464 }
465
466 #ifdef MOZ_CRASHREPORTER
467 static inline nsresult
ComputeCRC32(nsIFile * aFile,uint32_t * aResult)468 ComputeCRC32(nsIFile* aFile, uint32_t* aResult)
469 {
470 PRFileDesc* fd;
471 nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
472 NS_ENSURE_SUCCESS(rv, rv);
473
474 uint32_t crc = crc32(0, nullptr, 0);
475
476 unsigned char buf[512];
477 int32_t n;
478 while ((n = PR_Read(fd, buf, sizeof(buf))) > 0) {
479 crc = crc32(crc, buf, n);
480 }
481 PR_Close(fd);
482
483 if (n < 0) {
484 return NS_ERROR_FAILURE;
485 }
486
487 *aResult = crc;
488 return NS_OK;
489 }
490
491 static void
ListInterestingFiles(nsString & aAnnotation,nsIFile * aFile,const nsTArray<nsString> & aInterestingFilenames)492 ListInterestingFiles(nsString& aAnnotation, nsIFile* aFile,
493 const nsTArray<nsString>& aInterestingFilenames)
494 {
495 nsString filename;
496 aFile->GetLeafName(filename);
497 for (const nsString& interestingFilename : aInterestingFilenames) {
498 if (interestingFilename == filename) {
499 nsString path;
500 aFile->GetPath(path);
501 aAnnotation.AppendLiteral(" ");
502 aAnnotation.Append(path);
503 aAnnotation.AppendLiteral(" (");
504 int64_t size;
505 if (NS_SUCCEEDED(aFile->GetFileSize(&size))) {
506 aAnnotation.AppendPrintf("%ld", size);
507 } else {
508 aAnnotation.AppendLiteral("???");
509 }
510 aAnnotation.AppendLiteral(" bytes, crc32 = ");
511 uint32_t crc;
512 nsresult rv = ComputeCRC32(aFile, &crc);
513 if (NS_SUCCEEDED(rv)) {
514 aAnnotation.AppendPrintf("0x%08x)\n", crc);
515 } else {
516 aAnnotation.AppendPrintf("error 0x%08x)\n", uint32_t(rv));
517 }
518 return;
519 }
520 }
521
522 bool isDir = false;
523 aFile->IsDirectory(&isDir);
524
525 if (!isDir) {
526 return;
527 }
528
529 nsCOMPtr<nsISimpleEnumerator> entries;
530 if (NS_FAILED(aFile->GetDirectoryEntries(getter_AddRefs(entries)))) {
531 aAnnotation.AppendLiteral(" (failed to enumerated directory)\n");
532 return;
533 }
534
535 for (;;) {
536 bool hasMore = false;
537 if (NS_FAILED(entries->HasMoreElements(&hasMore))) {
538 aAnnotation.AppendLiteral(" (failed during directory enumeration)\n");
539 return;
540 }
541 if (!hasMore) {
542 break;
543 }
544
545 nsCOMPtr<nsISupports> entry;
546 if (NS_FAILED(entries->GetNext(getter_AddRefs(entry)))) {
547 aAnnotation.AppendLiteral(" (failed during directory enumeration)\n");
548 return;
549 }
550
551 nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
552 if (file) {
553 ListInterestingFiles(aAnnotation, file, aInterestingFilenames);
554 }
555 }
556 }
557
558 // Generate a crash report annotation to help debug issues with style
559 // sheets failing to load (bug 1194856).
560 static void
AnnotateCrashReport(nsIURI * aURI)561 AnnotateCrashReport(nsIURI* aURI)
562 {
563 nsAutoCString spec;
564 nsAutoCString scheme;
565 nsDependentCSubstring filename;
566 if (aURI) {
567 spec = aURI->GetSpecOrDefault();
568 aURI->GetScheme(scheme);
569 int32_t i = spec.RFindChar('/');
570 if (i != -1) {
571 filename.Rebind(spec, i + 1);
572 }
573 }
574
575 nsString annotation;
576
577 // The URL of the sheet that failed to load.
578 annotation.AppendLiteral("Error loading sheet: ");
579 annotation.Append(NS_ConvertUTF8toUTF16(spec).get());
580 annotation.Append('\n');
581
582 annotation.AppendLiteral("NS_ERROR_FILE_CORRUPTION reason: ");
583 if (nsZipArchive::sFileCorruptedReason) {
584 annotation.Append(NS_ConvertUTF8toUTF16(nsZipArchive::sFileCorruptedReason).get());
585 annotation.Append('\n');
586 } else {
587 annotation.AppendLiteral("(none)\n");
588 }
589
590 // The jar: or file: URL that the sheet's resource: or chrome: URL
591 // resolves to.
592 if (scheme.EqualsLiteral("resource")) {
593 annotation.AppendLiteral("Real location: ");
594 nsCOMPtr<nsISubstitutingProtocolHandler> handler;
595 nsCOMPtr<nsIIOService> io(do_GetIOService());
596 if (io) {
597 nsCOMPtr<nsIProtocolHandler> ph;
598 io->GetProtocolHandler(scheme.get(), getter_AddRefs(ph));
599 if (ph) {
600 handler = do_QueryInterface(ph);
601 }
602 }
603 if (!handler) {
604 annotation.AppendLiteral("(ResolveURI failed)\n");
605 } else {
606 nsAutoCString resolvedSpec;
607 handler->ResolveURI(aURI, resolvedSpec);
608 annotation.Append(NS_ConvertUTF8toUTF16(resolvedSpec));
609 annotation.Append('\n');
610 }
611 } else if (scheme.EqualsLiteral("chrome")) {
612 annotation.AppendLiteral("Real location: ");
613 nsCOMPtr<nsIChromeRegistry> reg =
614 mozilla::services::GetChromeRegistryService();
615 if (!reg) {
616 annotation.AppendLiteral("(no chrome registry)\n");
617 } else {
618 nsCOMPtr<nsIURI> resolvedURI;
619 reg->ConvertChromeURL(aURI, getter_AddRefs(resolvedURI));
620 if (!resolvedURI) {
621 annotation.AppendLiteral("(ConvertChromeURL failed)\n");
622 } else {
623 annotation.Append(
624 NS_ConvertUTF8toUTF16(resolvedURI->GetSpecOrDefault()));
625 annotation.Append('\n');
626 }
627 }
628 }
629
630 nsTArray<nsString> interestingFiles;
631 interestingFiles.AppendElement(NS_LITERAL_STRING("chrome.manifest"));
632 interestingFiles.AppendElement(NS_LITERAL_STRING("omni.ja"));
633 interestingFiles.AppendElement(NS_ConvertUTF8toUTF16(filename));
634
635 annotation.AppendLiteral("GRE directory: ");
636 nsCOMPtr<nsIFile> file;
637 nsDirectoryService::gService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
638 getter_AddRefs(file));
639 if (file) {
640 // The Firefox installation directory.
641 nsString path;
642 file->GetPath(path);
643 annotation.Append(path);
644 annotation.Append('\n');
645
646 // List interesting files -- any chrome.manifest or omni.ja file or any file
647 // whose name is the sheet's filename -- under the Firefox installation
648 // directory.
649 annotation.AppendLiteral("Interesting files in the GRE directory:\n");
650 ListInterestingFiles(annotation, file, interestingFiles);
651
652 // If the Firefox installation directory has a chrome.manifest file, let's
653 // see what's in it.
654 file->Append(NS_LITERAL_STRING("chrome.manifest"));
655 bool exists = false;
656 file->Exists(&exists);
657 if (exists) {
658 annotation.AppendLiteral("Contents of chrome.manifest:\n[[[\n");
659 PRFileDesc* fd;
660 if (NS_SUCCEEDED(file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) {
661 nsCString contents;
662 char buf[512];
663 int32_t n;
664 while ((n = PR_Read(fd, buf, sizeof(buf))) > 0) {
665 contents.Append(buf, n);
666 }
667 if (n < 0) {
668 annotation.AppendLiteral(" (error while reading)\n");
669 } else {
670 annotation.Append(NS_ConvertUTF8toUTF16(contents));
671 }
672 PR_Close(fd);
673 }
674 annotation.AppendLiteral("]]]\n");
675 }
676 } else {
677 annotation.AppendLiteral("(none)\n");
678 }
679
680 // The jar: or file: URL prefix that chrome: and resource: URLs get translated
681 // to.
682 annotation.AppendLiteral("GRE omnijar URI string: ");
683 nsCString uri;
684 nsresult rv = Omnijar::GetURIString(Omnijar::GRE, uri);
685 if (NS_FAILED(rv)) {
686 annotation.AppendLiteral("(failed)\n");
687 } else {
688 annotation.Append(NS_ConvertUTF8toUTF16(uri));
689 annotation.Append('\n');
690 }
691
692 RefPtr<nsZipArchive> zip = Omnijar::GetReader(Omnijar::GRE);
693 if (zip) {
694 // List interesting files in the GRE omnijar.
695 annotation.AppendLiteral("Interesting files in the GRE omnijar:\n");
696 nsZipFind* find;
697 rv = zip->FindInit(nullptr, &find);
698 if (NS_FAILED(rv)) {
699 annotation.AppendPrintf(" (FindInit failed with 0x%08x)\n", rv);
700 } else if (!find) {
701 annotation.AppendLiteral(" (FindInit returned null)\n");
702 } else {
703 const char* result;
704 uint16_t len;
705 while (NS_SUCCEEDED(find->FindNext(&result, &len))) {
706 nsCString itemPathname;
707 nsString itemFilename;
708 itemPathname.Append(result, len);
709 int32_t i = itemPathname.RFindChar('/');
710 if (i != -1) {
711 itemFilename = NS_ConvertUTF8toUTF16(Substring(itemPathname, i + 1));
712 }
713 for (const nsString& interestingFile : interestingFiles) {
714 if (interestingFile == itemFilename) {
715 annotation.AppendLiteral(" ");
716 annotation.Append(NS_ConvertUTF8toUTF16(itemPathname));
717 nsZipItem* item = zip->GetItem(itemPathname.get());
718 if (!item) {
719 annotation.AppendLiteral(" (GetItem failed)\n");
720 } else {
721 annotation.AppendPrintf(" (%d bytes, crc32 = 0x%08x)\n",
722 item->RealSize(),
723 item->CRC32());
724 }
725 break;
726 }
727 }
728 }
729 delete find;
730 }
731 } else {
732 annotation.AppendLiteral("No GRE omnijar\n");
733 }
734
735 CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("SheetLoadFailure"),
736 NS_ConvertUTF16toUTF8(annotation));
737 }
738 #endif
739
740 static void
ErrorLoadingSheet(nsIURI * aURI,const char * aMsg,FailureAction aFailureAction)741 ErrorLoadingSheet(nsIURI* aURI, const char* aMsg, FailureAction aFailureAction)
742 {
743 nsPrintfCString errorMessage("%s loading built-in stylesheet '%s'",
744 aMsg,
745 aURI ? aURI->GetSpecOrDefault().get() : "");
746 if (aFailureAction == eLogToConsole) {
747 nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
748 if (cs) {
749 cs->LogStringMessage(NS_ConvertUTF8toUTF16(errorMessage).get());
750 return;
751 }
752 }
753
754 #ifdef MOZ_CRASHREPORTER
755 AnnotateCrashReport(aURI);
756 #endif
757 NS_RUNTIMEABORT(errorMessage.get());
758 }
759
760 void
LoadSheet(nsIURI * aURI,RefPtr<StyleSheet> * aSheet,SheetParsingMode aParsingMode,FailureAction aFailureAction)761 nsLayoutStylesheetCache::LoadSheet(nsIURI* aURI,
762 RefPtr<StyleSheet>* aSheet,
763 SheetParsingMode aParsingMode,
764 FailureAction aFailureAction)
765 {
766 if (!aURI) {
767 ErrorLoadingSheet(aURI, "null URI", eCrash);
768 return;
769 }
770
771 auto& loader = mBackendType == StyleBackendType::Gecko ?
772 gCSSLoader_Gecko :
773 gCSSLoader_Servo;
774
775 if (!loader) {
776 loader = new mozilla::css::Loader(mBackendType);
777 if (!loader) {
778 ErrorLoadingSheet(aURI, "no Loader", eCrash);
779 return;
780 }
781 }
782
783 #ifdef MOZ_CRASHREPORTER
784 nsZipArchive::sFileCorruptedReason = nullptr;
785 #endif
786 nsresult rv = loader->LoadSheetSync(aURI, aParsingMode, true, aSheet);
787 if (NS_FAILED(rv)) {
788 ErrorLoadingSheet(aURI,
789 nsPrintfCString("LoadSheetSync failed with error %x", rv).get(),
790 aFailureAction);
791 }
792 }
793
794 /* static */ void
InvalidateSheet(RefPtr<StyleSheet> * aGeckoSheet,RefPtr<StyleSheet> * aServoSheet)795 nsLayoutStylesheetCache::InvalidateSheet(RefPtr<StyleSheet>* aGeckoSheet,
796 RefPtr<StyleSheet>* aServoSheet)
797 {
798 MOZ_ASSERT(gCSSLoader_Gecko || gCSSLoader_Servo,
799 "pref changed before we loaded a sheet?");
800
801 const bool gotGeckoSheet = aGeckoSheet && *aGeckoSheet;
802 const bool gotServoSheet = aServoSheet && *aServoSheet;
803
804 // Make sure sheets have the expected types
805 MOZ_ASSERT(!gotGeckoSheet || (*aGeckoSheet)->IsGecko());
806 MOZ_ASSERT(!gotServoSheet || (*aServoSheet)->IsServo());
807 // Make sure the URIs match
808 MOZ_ASSERT(!gotServoSheet || !gotGeckoSheet ||
809 (*aGeckoSheet)->GetSheetURI() == (*aServoSheet)->GetSheetURI(),
810 "Sheets passed should have the same URI");
811
812 nsIURI* uri;
813 if (gotGeckoSheet) {
814 uri = (*aGeckoSheet)->GetSheetURI();
815 } else if (gotServoSheet) {
816 uri = (*aServoSheet)->GetSheetURI();
817 } else {
818 return;
819 }
820
821 if (gCSSLoader_Gecko) {
822 gCSSLoader_Gecko->ObsoleteSheet(uri);
823 }
824 if (gCSSLoader_Servo) {
825 gCSSLoader_Servo->ObsoleteSheet(uri);
826 }
827 if (gotGeckoSheet) {
828 *aGeckoSheet = nullptr;
829 }
830 if (gotServoSheet) {
831 *aServoSheet = nullptr;
832 }
833 }
834
835 /* static */ void
DependentPrefChanged(const char * aPref,void * aData)836 nsLayoutStylesheetCache::DependentPrefChanged(const char* aPref, void* aData)
837 {
838 MOZ_ASSERT(gStyleCache_Gecko || gStyleCache_Servo,
839 "pref changed after shutdown?");
840
841 // Cause any UA style sheets whose parsing depends on the value of prefs
842 // to be re-parsed by dropping the sheet from gCSSLoader_{Gecko,Servo}'s cache
843 // then setting our cached sheet pointer to null. This will only work for
844 // sheets that are loaded lazily.
845
846 #define INVALIDATE(sheet_) \
847 InvalidateSheet(gStyleCache_Gecko ? &gStyleCache_Gecko->sheet_ : nullptr, \
848 gStyleCache_Servo ? &gStyleCache_Servo->sheet_ : nullptr);
849
850 INVALIDATE(mUASheet); // for layout.css.grid.enabled
851 INVALIDATE(mHTMLSheet); // for dom.details_element.enabled
852
853 #undef INVALIDATE
854 }
855
856 /* static */ void
InvalidatePreferenceSheets()857 nsLayoutStylesheetCache::InvalidatePreferenceSheets()
858 {
859 if (gStyleCache_Gecko) {
860 gStyleCache_Gecko->mContentPreferenceSheet = nullptr;
861 gStyleCache_Gecko->mChromePreferenceSheet = nullptr;
862 }
863 if (gStyleCache_Servo) {
864 gStyleCache_Servo->mContentPreferenceSheet = nullptr;
865 gStyleCache_Servo->mChromePreferenceSheet = nullptr;
866 }
867 }
868
869 void
BuildPreferenceSheet(RefPtr<StyleSheet> * aSheet,nsPresContext * aPresContext)870 nsLayoutStylesheetCache::BuildPreferenceSheet(RefPtr<StyleSheet>* aSheet,
871 nsPresContext* aPresContext)
872 {
873 if (mBackendType == StyleBackendType::Gecko) {
874 *aSheet = new CSSStyleSheet(eAgentSheetFeatures, CORS_NONE,
875 mozilla::net::RP_Default);
876 } else {
877 *aSheet = new ServoStyleSheet(eAgentSheetFeatures, CORS_NONE,
878 mozilla::net::RP_Default, dom::SRIMetadata());
879 }
880
881 StyleSheet* sheet = *aSheet;
882
883 nsCOMPtr<nsIURI> uri;
884 NS_NewURI(getter_AddRefs(uri), "about:PreferenceStyleSheet", nullptr);
885 MOZ_ASSERT(uri, "URI creation shouldn't fail");
886
887 sheet->SetURIs(uri, uri, uri);
888 sheet->SetComplete();
889
890 static const uint32_t kPreallocSize = 1024;
891
892 nsString sheetText;
893 sheetText.SetCapacity(kPreallocSize);
894
895 #define NS_GET_R_G_B(color_) \
896 NS_GET_R(color_), NS_GET_G(color_), NS_GET_B(color_)
897
898 sheetText.AppendLiteral(
899 "@namespace url(http://www.w3.org/1999/xhtml);\n"
900 "@namespace svg url(http://www.w3.org/2000/svg);\n");
901
902 // Rules for link styling.
903 nscolor linkColor = aPresContext->DefaultLinkColor();
904 nscolor activeColor = aPresContext->DefaultActiveLinkColor();
905 nscolor visitedColor = aPresContext->DefaultVisitedLinkColor();
906
907 sheetText.AppendPrintf(
908 "*|*:link { color: #%02x%02x%02x; }\n"
909 "*|*:any-link:active { color: #%02x%02x%02x; }\n"
910 "*|*:visited { color: #%02x%02x%02x; }\n",
911 NS_GET_R_G_B(linkColor),
912 NS_GET_R_G_B(activeColor),
913 NS_GET_R_G_B(visitedColor));
914
915 bool underlineLinks =
916 aPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks);
917 sheetText.AppendPrintf(
918 "*|*:any-link%s { text-decoration: %s; }\n",
919 underlineLinks ? ":not(svg|a)" : "",
920 underlineLinks ? "underline" : "none");
921
922 // Rules for focus styling.
923
924 bool focusRingOnAnything = aPresContext->GetFocusRingOnAnything();
925 uint8_t focusRingWidth = aPresContext->FocusRingWidth();
926 uint8_t focusRingStyle = aPresContext->GetFocusRingStyle();
927
928 if ((focusRingWidth != 1 && focusRingWidth <= 4) || focusRingOnAnything) {
929 if (focusRingWidth != 1) {
930 // If the focus ring width is different from the default, fix buttons
931 // with rings.
932 sheetText.AppendPrintf(
933 "button::-moz-focus-inner, input[type=\"reset\"]::-moz-focus-inner, "
934 "input[type=\"button\"]::-moz-focus-inner, "
935 "input[type=\"submit\"]::-moz-focus-inner { "
936 "padding: 1px 2px 1px 2px; "
937 "border: %dpx %s transparent !important; }\n",
938 focusRingWidth,
939 focusRingStyle == 0 ? "solid" : "dotted");
940
941 sheetText.AppendLiteral(
942 "button:focus::-moz-focus-inner, "
943 "input[type=\"reset\"]:focus::-moz-focus-inner, "
944 "input[type=\"button\"]:focus::-moz-focus-inner, "
945 "input[type=\"submit\"]:focus::-moz-focus-inner { "
946 "border-color: ButtonText !important; }\n");
947 }
948
949 sheetText.AppendPrintf(
950 "%s { outline: %dpx %s !important; %s}\n",
951 focusRingOnAnything ?
952 ":focus" :
953 "*|*:link:focus, *|*:visited:focus",
954 focusRingWidth,
955 focusRingStyle == 0 ? // solid
956 "solid -moz-mac-focusring" : "dotted WindowText",
957 focusRingStyle == 0 ? // solid
958 "-moz-outline-radius: 3px; outline-offset: 1px; " : "");
959 }
960
961 if (aPresContext->GetUseFocusColors()) {
962 nscolor focusText = aPresContext->FocusTextColor();
963 nscolor focusBG = aPresContext->FocusBackgroundColor();
964 sheetText.AppendPrintf(
965 "*:focus, *:focus > font { color: #%02x%02x%02x !important; "
966 "background-color: #%02x%02x%02x !important; }\n",
967 NS_GET_R_G_B(focusText),
968 NS_GET_R_G_B(focusBG));
969 }
970
971 NS_ASSERTION(sheetText.Length() <= kPreallocSize,
972 "kPreallocSize should be big enough to build preference style "
973 "sheet without reallocation");
974
975 if (sheet->IsGecko()) {
976 sheet->AsGecko()->ReparseSheet(sheetText);
977 } else {
978 nsresult rv = sheet->AsServo()->ParseSheet(sheetText, uri, uri, nullptr, 0);
979 // Parsing the about:PreferenceStyleSheet URI can only fail on OOM. If we
980 // are OOM before we parsed any documents we might as well abort.
981 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
982 }
983
984 #undef NS_GET_R_G_B
985 }
986
987 mozilla::StaticRefPtr<nsLayoutStylesheetCache>
988 nsLayoutStylesheetCache::gStyleCache_Gecko;
989
990 mozilla::StaticRefPtr<nsLayoutStylesheetCache>
991 nsLayoutStylesheetCache::gStyleCache_Servo;
992
993 mozilla::StaticRefPtr<mozilla::css::Loader>
994 nsLayoutStylesheetCache::gCSSLoader_Gecko;
995
996 mozilla::StaticRefPtr<mozilla::css::Loader>
997 nsLayoutStylesheetCache::gCSSLoader_Servo;
998
999 mozilla::StaticRefPtr<nsIURI>
1000 nsLayoutStylesheetCache::gUserContentSheetURL;
1001