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