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