Bug 1879449 [wpt PR 44489] - [wptrunner] Add `infrastructure/expected-fail/` test...
[gecko.git] / layout / style / Loader.cpp
blob1ea37b094fee2fde364e397a0aaa333c685d8cfb
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* loading of CSS style sheets using the network APIs */
9 #include "mozilla/css/Loader.h"
11 #include "MainThreadUtils.h"
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/css/ErrorReporter.h"
14 #include "mozilla/dom/DocGroup.h"
15 #include "mozilla/dom/FetchPriority.h"
16 #include "mozilla/dom/SRILogHelper.h"
17 #include "mozilla/IntegerPrintfMacros.h"
18 #include "mozilla/AutoRestore.h"
19 #include "mozilla/LoadInfo.h"
20 #include "mozilla/Logging.h"
21 #include "mozilla/MemoryReporting.h"
22 #include "mozilla/PreloadHashKey.h"
23 #include "mozilla/ResultExtensions.h"
24 #include "mozilla/SchedulerGroup.h"
25 #include "mozilla/URLPreloader.h"
26 #include "nsIChildChannel.h"
27 #include "nsIPrincipal.h"
28 #include "nsISupportsPriority.h"
29 #include "nsITimedChannel.h"
30 #include "nsICachingChannel.h"
31 #include "nsSyncLoadService.h"
32 #include "nsContentSecurityManager.h"
33 #include "nsCOMPtr.h"
34 #include "nsString.h"
35 #include "nsIContent.h"
36 #include "nsIContentInlines.h"
37 #include "nsICookieJarSettings.h"
38 #include "mozilla/dom/Document.h"
39 #include "nsIURI.h"
40 #include "nsContentUtils.h"
41 #include "nsIScriptSecurityManager.h"
42 #include "nsContentPolicyUtils.h"
43 #include "nsIHttpChannel.h"
44 #include "nsIHttpChannelInternal.h"
45 #include "nsIClassOfService.h"
46 #include "nsIScriptError.h"
47 #include "nsMimeTypes.h"
48 #include "nsICSSLoaderObserver.h"
49 #include "nsThreadUtils.h"
50 #include "nsGkAtoms.h"
51 #include "nsIThreadInternal.h"
52 #include "nsINetworkPredictor.h"
53 #include "nsQueryActor.h"
54 #include "nsStringStream.h"
55 #include "mozilla/dom/MediaList.h"
56 #include "mozilla/dom/ShadowRoot.h"
57 #include "mozilla/dom/URL.h"
58 #include "mozilla/net/UrlClassifierFeatureFactory.h"
59 #include "mozilla/AsyncEventDispatcher.h"
60 #include "mozilla/ProfilerLabels.h"
61 #include "mozilla/ServoBindings.h"
62 #include "mozilla/StyleSheet.h"
63 #include "mozilla/StyleSheetInlines.h"
64 #include "mozilla/ConsoleReportCollector.h"
65 #include "mozilla/ServoUtils.h"
66 #include "mozilla/css/StreamLoader.h"
67 #include "mozilla/SharedStyleSheetCache.h"
68 #include "mozilla/StaticPrefs_layout.h"
69 #include "mozilla/StaticPrefs_network.h"
70 #include "mozilla/StaticPrefs_dom.h"
71 #include "mozilla/StaticPrefs_network.h"
72 #include "mozilla/Try.h"
73 #include "ReferrerInfo.h"
75 #include "nsXULPrototypeCache.h"
77 #include "nsError.h"
79 #include "mozilla/dom/SRICheck.h"
81 #include "mozilla/Encoding.h"
83 using namespace mozilla::dom;
85 // 1024 bytes is specified in https://drafts.csswg.org/css-syntax/
86 #define SNIFFING_BUFFER_SIZE 1024
88 /**
89 * OVERALL ARCHITECTURE
91 * The CSS Loader gets requests to load various sorts of style sheets:
92 * inline style from <style> elements, linked style, @import-ed child
93 * sheets, non-document sheets. The loader handles the following tasks:
94 * 1) Creation of the actual style sheet objects: CreateSheet()
95 * 2) setting of the right media, title, enabled state, etc on the
96 * sheet: PrepareSheet()
97 * 3) Insertion of the sheet in the proper cascade order:
98 * InsertSheetInTree() and InsertChildSheet()
99 * 4) Load of the sheet: LoadSheet() including security checks
100 * 5) Parsing of the sheet: ParseSheet()
101 * 6) Cleanup: SheetComplete()
103 * The detailed documentation for these functions is found with the
104 * function implementations.
106 * The following helper object is used:
107 * SheetLoadData -- a small class that is used to store all the
108 * information needed for the loading of a sheet;
109 * this class handles listening for the stream
110 * loader completion and also handles charset
111 * determination.
114 extern mozilla::LazyLogModule sCssLoaderLog;
115 mozilla::LazyLogModule sCssLoaderLog("nsCSSLoader");
117 static mozilla::LazyLogModule gSriPRLog("SRI");
119 static bool IsPrivilegedURI(nsIURI* aURI) {
120 return aURI->SchemeIs("chrome") || aURI->SchemeIs("resource");
123 #define LOG_ERROR(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Error, args)
124 #define LOG_WARN(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Warning, args)
125 #define LOG_DEBUG(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Debug, args)
126 #define LOG(args) LOG_DEBUG(args)
128 #define LOG_ERROR_ENABLED() \
129 MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Error)
130 #define LOG_WARN_ENABLED() \
131 MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Warning)
132 #define LOG_DEBUG_ENABLED() \
133 MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Debug)
134 #define LOG_ENABLED() LOG_DEBUG_ENABLED()
136 #define LOG_URI(format, uri) \
137 PR_BEGIN_MACRO \
138 NS_ASSERTION(uri, "Logging null uri"); \
139 if (LOG_ENABLED()) { \
140 LOG((format, uri->GetSpecOrDefault().get())); \
142 PR_END_MACRO
144 // And some convenience strings...
145 static const char* const gStateStrings[] = {"NeedsParser", "Pending", "Loading",
146 "Complete"};
148 namespace mozilla {
150 SheetLoadDataHashKey::SheetLoadDataHashKey(const css::SheetLoadData& aLoadData)
151 : mURI(aLoadData.mURI),
152 mPrincipal(aLoadData.mTriggeringPrincipal),
153 mLoaderPrincipal(aLoadData.mLoader->LoaderPrincipal()),
154 mPartitionPrincipal(aLoadData.mLoader->PartitionedPrincipal()),
155 mEncodingGuess(aLoadData.mGuessedEncoding),
156 mCORSMode(aLoadData.mSheet->GetCORSMode()),
157 mParsingMode(aLoadData.mSheet->ParsingMode()),
158 mCompatMode(aLoadData.mCompatMode),
159 mIsLinkRelPreload(aLoadData.IsLinkRelPreload()) {
160 MOZ_COUNT_CTOR(SheetLoadDataHashKey);
161 MOZ_ASSERT(mURI);
162 MOZ_ASSERT(mPrincipal);
163 MOZ_ASSERT(mLoaderPrincipal);
164 MOZ_ASSERT(mPartitionPrincipal);
165 aLoadData.mSheet->GetIntegrity(mSRIMetadata);
168 bool SheetLoadDataHashKey::KeyEquals(const SheetLoadDataHashKey& aKey) const {
170 bool eq;
171 if (NS_FAILED(mURI->Equals(aKey.mURI, &eq)) || !eq) {
172 return false;
176 LOG_URI("KeyEquals(%s)\n", mURI);
178 if (mParsingMode != aKey.mParsingMode) {
179 LOG((" > Parsing mode mismatch\n"));
180 return false;
183 // Chrome URIs ignore everything else.
184 if (IsPrivilegedURI(mURI)) {
185 return true;
188 if (!mPrincipal->Equals(aKey.mPrincipal)) {
189 LOG((" > Principal mismatch\n"));
190 return false;
193 // We only check for partition principal equality if any of the loads are
194 // triggered by a document rather than e.g. an extension (which have different
195 // origins than the loader principal).
196 if (mPrincipal->Equals(mLoaderPrincipal) ||
197 aKey.mPrincipal->Equals(aKey.mLoaderPrincipal)) {
198 if (!mPartitionPrincipal->Equals(aKey.mPartitionPrincipal)) {
199 LOG((" > Partition principal mismatch\n"));
200 return false;
204 if (mCORSMode != aKey.mCORSMode) {
205 LOG((" > CORS mismatch\n"));
206 return false;
209 if (mCompatMode != aKey.mCompatMode) {
210 LOG((" > Quirks mismatch\n"));
211 return false;
214 // If encoding differs, then don't reuse the cache.
216 // TODO(emilio): When the encoding is determined from the request (either
217 // BOM or Content-Length or @charset), we could do a bit better,
218 // theoretically.
219 if (mEncodingGuess != aKey.mEncodingGuess) {
220 LOG((" > Encoding guess mismatch\n"));
221 return false;
224 // Consuming stylesheet tags must never coalesce to <link preload> initiated
225 // speculative loads with a weaker SRI hash or its different value. This
226 // check makes sure that regular loads will never find such a weaker preload
227 // and rather start a new, independent load with new, stronger SRI checker
228 // set up, so that integrity is ensured.
229 if (mIsLinkRelPreload != aKey.mIsLinkRelPreload) {
230 const auto& linkPreloadMetadata =
231 mIsLinkRelPreload ? mSRIMetadata : aKey.mSRIMetadata;
232 const auto& consumerPreloadMetadata =
233 mIsLinkRelPreload ? aKey.mSRIMetadata : mSRIMetadata;
235 if (!consumerPreloadMetadata.CanTrustBeDelegatedTo(linkPreloadMetadata)) {
236 LOG((" > Preload SRI metadata mismatch\n"));
237 return false;
241 return true;
244 namespace css {
246 static NotNull<const Encoding*> GetFallbackEncoding(
247 Loader& aLoader, nsINode* aOwningNode,
248 const Encoding* aPreloadOrParentDataEncoding) {
249 const Encoding* encoding;
250 // Now try the charset on the <link> or processing instruction
251 // that loaded us
252 if (aOwningNode) {
253 nsAutoString label16;
254 LinkStyle::FromNode(*aOwningNode)->GetCharset(label16);
255 encoding = Encoding::ForLabel(label16);
256 if (encoding) {
257 return WrapNotNull(encoding);
261 // Try preload or parent sheet encoding.
262 if (aPreloadOrParentDataEncoding) {
263 return WrapNotNull(aPreloadOrParentDataEncoding);
266 if (auto* doc = aLoader.GetDocument()) {
267 // Use the document charset.
268 return doc->GetDocumentCharacterSet();
271 return UTF_8_ENCODING;
274 /********************************
275 * SheetLoadData implementation *
276 ********************************/
277 NS_IMPL_ISUPPORTS(SheetLoadData, nsISupports)
279 SheetLoadData::SheetLoadData(
280 css::Loader* aLoader, const nsAString& aTitle, nsIURI* aURI,
281 StyleSheet* aSheet, SyncLoad aSyncLoad, nsINode* aOwningNode,
282 IsAlternate aIsAlternate, MediaMatched aMediaMatches,
283 StylePreloadKind aPreloadKind, nsICSSLoaderObserver* aObserver,
284 nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
285 const nsAString& aNonce, FetchPriority aFetchPriority)
286 : mLoader(aLoader),
287 mTitle(aTitle),
288 mEncoding(nullptr),
289 mURI(aURI),
290 mSheet(aSheet),
291 mPendingChildren(0),
292 mSyncLoad(aSyncLoad == SyncLoad::Yes),
293 mIsNonDocumentSheet(false),
294 mIsChildSheet(aSheet->GetParentSheet()),
295 mIsBeingParsed(false),
296 mIsLoading(false),
297 mIsCancelled(false),
298 mMustNotify(false),
299 mHadOwnerNode(!!aOwningNode),
300 mWasAlternate(aIsAlternate == IsAlternate::Yes),
301 mMediaMatched(aMediaMatches == MediaMatched::Yes),
302 mUseSystemPrincipal(false),
303 mSheetAlreadyComplete(false),
304 mIsCrossOriginNoCORS(false),
305 mBlockResourceTiming(false),
306 mLoadFailed(false),
307 mPreloadKind(aPreloadKind),
308 mObserver(aObserver),
309 mTriggeringPrincipal(aTriggeringPrincipal),
310 mReferrerInfo(aReferrerInfo),
311 mNonce(aNonce),
312 mFetchPriority{aFetchPriority},
313 mGuessedEncoding(GetFallbackEncoding(*aLoader, aOwningNode, nullptr)),
314 mCompatMode(aLoader->CompatMode(aPreloadKind)),
315 mRecordErrors(
316 aLoader && aLoader->GetDocument() &&
317 css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())) {
318 MOZ_ASSERT(!aOwningNode || dom::LinkStyle::FromNode(*aOwningNode),
319 "Must implement LinkStyle");
320 MOZ_ASSERT(mTriggeringPrincipal);
321 MOZ_ASSERT(mLoader, "Must have a loader!");
324 SheetLoadData::SheetLoadData(css::Loader* aLoader, nsIURI* aURI,
325 StyleSheet* aSheet, SheetLoadData* aParentData,
326 nsICSSLoaderObserver* aObserver,
327 nsIPrincipal* aTriggeringPrincipal,
328 nsIReferrerInfo* aReferrerInfo)
329 : mLoader(aLoader),
330 mEncoding(nullptr),
331 mURI(aURI),
332 mSheet(aSheet),
333 mParentData(aParentData),
334 mPendingChildren(0),
335 mSyncLoad(aParentData && aParentData->mSyncLoad),
336 mIsNonDocumentSheet(aParentData && aParentData->mIsNonDocumentSheet),
337 mIsChildSheet(aSheet->GetParentSheet()),
338 mIsBeingParsed(false),
339 mIsLoading(false),
340 mIsCancelled(false),
341 mMustNotify(false),
342 mHadOwnerNode(false),
343 mWasAlternate(false),
344 mMediaMatched(true),
345 mUseSystemPrincipal(aParentData && aParentData->mUseSystemPrincipal),
346 mSheetAlreadyComplete(false),
347 mIsCrossOriginNoCORS(false),
348 mBlockResourceTiming(false),
349 mLoadFailed(false),
350 mPreloadKind(StylePreloadKind::None),
351 mObserver(aObserver),
352 mTriggeringPrincipal(aTriggeringPrincipal),
353 mReferrerInfo(aReferrerInfo),
354 mNonce(u""_ns),
355 mFetchPriority(FetchPriority::Auto),
356 mGuessedEncoding(GetFallbackEncoding(
357 *aLoader, nullptr, aParentData ? aParentData->mEncoding : nullptr)),
358 mCompatMode(aLoader->CompatMode(mPreloadKind)),
359 mRecordErrors(
360 aLoader && aLoader->GetDocument() &&
361 css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())) {
362 MOZ_ASSERT(mLoader, "Must have a loader!");
363 MOZ_ASSERT(mTriggeringPrincipal);
364 MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
365 "Shouldn't use system principal for async loads");
366 MOZ_ASSERT_IF(aParentData, mIsChildSheet);
369 SheetLoadData::SheetLoadData(
370 css::Loader* aLoader, nsIURI* aURI, StyleSheet* aSheet, SyncLoad aSyncLoad,
371 UseSystemPrincipal aUseSystemPrincipal, StylePreloadKind aPreloadKind,
372 const Encoding* aPreloadEncoding, nsICSSLoaderObserver* aObserver,
373 nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
374 const nsAString& aNonce, FetchPriority aFetchPriority)
375 : mLoader(aLoader),
376 mEncoding(nullptr),
377 mURI(aURI),
378 mSheet(aSheet),
379 mPendingChildren(0),
380 mSyncLoad(aSyncLoad == SyncLoad::Yes),
381 mIsNonDocumentSheet(true),
382 mIsChildSheet(false),
383 mIsBeingParsed(false),
384 mIsLoading(false),
385 mIsCancelled(false),
386 mMustNotify(false),
387 mHadOwnerNode(false),
388 mWasAlternate(false),
389 mMediaMatched(true),
390 mUseSystemPrincipal(aUseSystemPrincipal == UseSystemPrincipal::Yes),
391 mSheetAlreadyComplete(false),
392 mIsCrossOriginNoCORS(false),
393 mBlockResourceTiming(false),
394 mLoadFailed(false),
395 mPreloadKind(aPreloadKind),
396 mObserver(aObserver),
397 mTriggeringPrincipal(aTriggeringPrincipal),
398 mReferrerInfo(aReferrerInfo),
399 mNonce(aNonce),
400 mFetchPriority(aFetchPriority),
401 mGuessedEncoding(
402 GetFallbackEncoding(*aLoader, nullptr, aPreloadEncoding)),
403 mCompatMode(aLoader->CompatMode(aPreloadKind)),
404 mRecordErrors(
405 aLoader && aLoader->GetDocument() &&
406 css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())) {
407 MOZ_ASSERT(mTriggeringPrincipal);
408 MOZ_ASSERT(mLoader, "Must have a loader!");
409 MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
410 "Shouldn't use system principal for async loads");
411 MOZ_ASSERT(!aSheet->GetParentSheet(), "Shouldn't be used for child loads");
414 SheetLoadData::~SheetLoadData() {
415 MOZ_RELEASE_ASSERT(mSheetCompleteCalled || mIntentionallyDropped,
416 "Should always call SheetComplete, except when "
417 "dropping the load");
420 RefPtr<StyleSheet> SheetLoadData::ValueForCache() const {
421 // We need to clone the sheet on insertion to the cache because otherwise the
422 // stylesheets can keep full windows alive via either their JS wrapper, or via
423 // StyleSheet::mRelevantGlobal.
425 // If this ever changes, then you also need to fix up the memory reporting in
426 // both SizeOfIncludingThis and nsXULPrototypeCache::CollectMemoryReports.
427 return mSheet->Clone(nullptr, nullptr);
430 void SheetLoadData::PrioritizeAsPreload(nsIChannel* aChannel) {
431 if (nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(aChannel)) {
432 sp->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
436 void SheetLoadData::StartPendingLoad() {
437 mLoader->LoadSheet(*this, Loader::SheetState::NeedsParser, 0,
438 Loader::PendingLoad::Yes);
441 already_AddRefed<AsyncEventDispatcher>
442 SheetLoadData::PrepareLoadEventIfNeeded() {
443 nsCOMPtr<nsINode> node = mSheet->GetOwnerNode();
444 if (!node) {
445 return nullptr;
447 MOZ_ASSERT(!RootLoadData().IsLinkRelPreload(),
448 "rel=preload handled elsewhere");
449 RefPtr<AsyncEventDispatcher> dispatcher;
450 if (BlocksLoadEvent()) {
451 dispatcher = new LoadBlockingAsyncEventDispatcher(
452 node, mLoadFailed ? u"error"_ns : u"load"_ns, CanBubble::eNo,
453 ChromeOnlyDispatch::eNo);
454 } else {
455 // Fire the load event on the link, but don't block the document load.
456 dispatcher =
457 new AsyncEventDispatcher(node, mLoadFailed ? u"error"_ns : u"load"_ns,
458 CanBubble::eNo, ChromeOnlyDispatch::eNo);
460 return dispatcher.forget();
463 nsINode* SheetLoadData::GetRequestingNode() const {
464 if (nsINode* node = mSheet->GetOwnerNodeOfOutermostSheet()) {
465 return node;
467 return mLoader->GetDocument();
470 /*********************
471 * Style sheet reuse *
472 *********************/
474 bool LoaderReusableStyleSheets::FindReusableStyleSheet(
475 nsIURI* aURL, RefPtr<StyleSheet>& aResult) {
476 MOZ_ASSERT(aURL);
477 for (size_t i = mReusableSheets.Length(); i > 0; --i) {
478 size_t index = i - 1;
479 bool sameURI;
480 MOZ_ASSERT(mReusableSheets[index]->GetOriginalURI());
481 nsresult rv =
482 aURL->Equals(mReusableSheets[index]->GetOriginalURI(), &sameURI);
483 if (!NS_FAILED(rv) && sameURI) {
484 aResult = mReusableSheets[index];
485 mReusableSheets.RemoveElementAt(index);
486 return true;
489 return false;
491 /*************************
492 * Loader Implementation *
493 *************************/
495 Loader::Loader()
496 : mDocument(nullptr),
497 mDocumentCompatMode(eCompatibility_FullStandards),
498 mReporter(new ConsoleReportCollector()) {}
500 Loader::Loader(DocGroup* aDocGroup) : Loader() { mDocGroup = aDocGroup; }
502 Loader::Loader(Document* aDocument) : Loader() {
503 MOZ_ASSERT(aDocument, "We should get a valid document from the caller!");
504 mDocument = aDocument;
505 mIsDocumentAssociated = true;
506 mDocumentCompatMode = aDocument->GetCompatibilityMode();
507 mSheets = SharedStyleSheetCache::Get();
508 RegisterInSheetCache();
511 // Note: no real need to revoke our stylesheet loaded events -- they hold strong
512 // references to us, so if we're going away that means they're all done.
513 Loader::~Loader() = default;
515 void Loader::RegisterInSheetCache() {
516 MOZ_ASSERT(mDocument);
517 MOZ_ASSERT(mSheets);
519 mSheets->RegisterLoader(*this);
522 void Loader::DeregisterFromSheetCache() {
523 MOZ_ASSERT(mDocument);
524 MOZ_ASSERT(mSheets);
526 mSheets->CancelLoadsForLoader(*this);
527 mSheets->UnregisterLoader(*this);
530 void Loader::DropDocumentReference() {
531 // Flush out pending datas just so we don't leak by accident.
532 if (mSheets) {
533 DeregisterFromSheetCache();
535 mDocument = nullptr;
538 void Loader::DocumentStyleSheetSetChanged() {
539 MOZ_ASSERT(mDocument);
541 // start any pending alternates that aren't alternates anymore
542 mSheets->StartPendingLoadsForLoader(*this, [&](const SheetLoadData& aData) {
543 return IsAlternateSheet(aData.mTitle, true) != IsAlternate::Yes;
547 static const char kCharsetSym[] = "@charset \"";
549 static bool GetCharsetFromData(const char* aStyleSheetData,
550 uint32_t aDataLength, nsACString& aCharset) {
551 aCharset.Truncate();
552 if (aDataLength <= sizeof(kCharsetSym) - 1) return false;
554 if (strncmp(aStyleSheetData, kCharsetSym, sizeof(kCharsetSym) - 1)) {
555 return false;
558 for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
559 char c = aStyleSheetData[i];
560 if (c == '"') {
561 ++i;
562 if (i < aDataLength && aStyleSheetData[i] == ';') {
563 return true;
565 // fail
566 break;
568 aCharset.Append(c);
571 // Did not see end quote or semicolon
572 aCharset.Truncate();
573 return false;
576 NotNull<const Encoding*> SheetLoadData::DetermineNonBOMEncoding(
577 const nsACString& aSegment, nsIChannel* aChannel) const {
578 const Encoding* encoding;
579 nsAutoCString label;
581 // Check HTTP
582 if (aChannel && NS_SUCCEEDED(aChannel->GetContentCharset(label))) {
583 encoding = Encoding::ForLabel(label);
584 if (encoding) {
585 return WrapNotNull(encoding);
589 // Check @charset
590 auto sniffingLength = aSegment.Length();
591 if (sniffingLength > SNIFFING_BUFFER_SIZE) {
592 sniffingLength = SNIFFING_BUFFER_SIZE;
594 if (GetCharsetFromData(aSegment.BeginReading(), sniffingLength, label)) {
595 encoding = Encoding::ForLabel(label);
596 if (encoding == UTF_16BE_ENCODING || encoding == UTF_16LE_ENCODING) {
597 return UTF_8_ENCODING;
599 if (encoding) {
600 return WrapNotNull(encoding);
603 return mGuessedEncoding;
606 static nsresult VerifySheetIntegrity(const SRIMetadata& aMetadata,
607 nsIChannel* aChannel,
608 const nsACString& aFirst,
609 const nsACString& aSecond,
610 const nsACString& aSourceFileURI,
611 nsIConsoleReportCollector* aReporter) {
612 NS_ENSURE_ARG_POINTER(aReporter);
614 if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), LogLevel::Debug)) {
615 nsAutoCString requestURL;
616 nsCOMPtr<nsIURI> originalURI;
617 if (aChannel &&
618 NS_SUCCEEDED(aChannel->GetOriginalURI(getter_AddRefs(originalURI))) &&
619 originalURI) {
620 originalURI->GetAsciiSpec(requestURL);
622 MOZ_LOG(SRILogHelper::GetSriLog(), LogLevel::Debug,
623 ("VerifySheetIntegrity (unichar stream)"));
626 SRICheckDataVerifier verifier(aMetadata, aSourceFileURI, aReporter);
627 nsresult rv =
628 verifier.Update(aFirst.Length(), (const uint8_t*)aFirst.BeginReading());
629 NS_ENSURE_SUCCESS(rv, rv);
630 rv =
631 verifier.Update(aSecond.Length(), (const uint8_t*)aSecond.BeginReading());
632 NS_ENSURE_SUCCESS(rv, rv);
634 return verifier.Verify(aMetadata, aChannel, aSourceFileURI, aReporter);
637 static bool AllLoadsCanceled(const SheetLoadData& aData) {
638 const SheetLoadData* data = &aData;
639 do {
640 if (!data->IsCancelled()) {
641 return false;
643 } while ((data = data->mNext));
644 return true;
648 * Stream completion code shared by Stylo and the old style system.
650 * Here we need to check that the load did not give us an http error
651 * page and check the mimetype on the channel to make sure we're not
652 * loading non-text/css data in standards mode.
654 nsresult SheetLoadData::VerifySheetReadyToParse(
655 nsresult aStatus, const nsACString& aBytes1, const nsACString& aBytes2,
656 nsIChannel* aChannel, nsIURI* aFinalChannelURI,
657 nsIPrincipal* aChannelResultPrincipal) {
658 LOG(("SheetLoadData::VerifySheetReadyToParse"));
659 NS_ASSERTION((!NS_IsMainThread() || !mLoader->mSyncCallback),
660 "Synchronous callback from necko");
662 if (AllLoadsCanceled(*this)) {
663 if (NS_IsMainThread()) {
664 LOG_WARN((" All loads are canceled, dropping"));
665 mLoader->SheetComplete(*this, NS_BINDING_ABORTED);
667 return NS_OK;
670 if (!NS_IsMainThread() && mRecordErrors) {
671 // we cannot parse sheet OMT if we need to record errors
672 return NS_OK;
675 if (NS_FAILED(aStatus)) {
676 if (NS_IsMainThread()) {
677 LOG_WARN(
678 (" Load failed: status 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
679 // Handle sheet not loading error because source was a tracking URL (or
680 // fingerprinting, cryptomining, etc).
681 // We make a note of this sheet node by including it in a dedicated
682 // array of blocked tracking nodes under its parent document.
684 // Multiple sheet load instances might be tied to this request,
685 // we annotate each one linked to a valid owning element (node).
686 if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
687 aStatus)) {
688 if (Document* doc = mLoader->GetDocument()) {
689 for (SheetLoadData* data = this; data; data = data->mNext) {
690 // owner node may be null but AddBlockTrackingNode can cope
691 doc->AddBlockedNodeByClassifier(data->mSheet->GetOwnerNode());
695 mLoader->SheetComplete(*this, aStatus);
697 return NS_OK;
700 if (!aChannel) {
701 MOZ_ASSERT(NS_IsMainThread());
702 mLoader->SheetComplete(*this, NS_OK);
703 return NS_OK;
706 nsCOMPtr<nsIURI> originalURI;
707 aChannel->GetOriginalURI(getter_AddRefs(originalURI));
709 // If the channel's original URI is "chrome:", we want that, since
710 // the observer code in nsXULPrototypeCache depends on chrome stylesheets
711 // having a chrome URI. (Whether or not chrome stylesheets come through
712 // this codepath seems nondeterministic.)
713 // Otherwise we want the potentially-HTTP-redirected URI.
714 if (!aFinalChannelURI || !originalURI) {
715 MOZ_ASSERT(NS_IsMainThread());
716 NS_ERROR("Someone just violated the nsIRequest contract");
717 LOG_WARN((" Channel without a URI. Bad!"));
718 mLoader->SheetComplete(*this, NS_ERROR_UNEXPECTED);
719 return NS_OK;
722 nsCOMPtr<nsIPrincipal> principal;
723 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
724 nsresult result = NS_ERROR_NOT_AVAILABLE;
725 if (secMan) { // Could be null if we already shut down
726 if (mUseSystemPrincipal) {
727 result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
728 } else {
729 if (aChannelResultPrincipal) {
730 principal = aChannelResultPrincipal;
731 result = NS_OK;
736 if (NS_FAILED(result)) {
737 if (NS_IsMainThread()) {
738 LOG_WARN((" Couldn't get principal"));
739 mLoader->SheetComplete(*this, result);
741 return NS_OK;
744 mSheet->SetPrincipal(principal);
746 // If it's an HTTP channel, we want to make sure this is not an
747 // error document we got.
748 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
749 bool requestSucceeded;
750 result = httpChannel->GetRequestSucceeded(&requestSucceeded);
751 if (NS_SUCCEEDED(result) && !requestSucceeded) {
752 if (NS_IsMainThread()) {
753 LOG((" Load returned an error page"));
754 mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
756 return NS_OK;
759 nsCString sourceMapURL;
760 if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
761 mSheet->SetSourceMapURL(std::move(sourceMapURL));
765 nsAutoCString contentType;
766 aChannel->GetContentType(contentType);
768 // In standards mode, a style sheet must have one of these MIME
769 // types to be processed at all. In quirks mode, we accept any
770 // MIME type, but only if the style sheet is same-origin with the
771 // requesting document or parent sheet. See bug 524223.
773 bool validType = contentType.EqualsLiteral("text/css") ||
774 contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
775 contentType.IsEmpty();
777 if (!validType) {
778 if (!NS_IsMainThread()) {
779 return NS_OK;
781 const char* errorMessage;
782 uint32_t errorFlag;
783 bool sameOrigin = true;
785 bool subsumed;
786 result = mTriggeringPrincipal->Subsumes(principal, &subsumed);
787 if (NS_FAILED(result) || !subsumed) {
788 sameOrigin = false;
791 if (sameOrigin && mCompatMode == eCompatibility_NavQuirks) {
792 errorMessage = "MimeNotCssWarn";
793 errorFlag = nsIScriptError::warningFlag;
794 } else {
795 errorMessage = "MimeNotCss";
796 errorFlag = nsIScriptError::errorFlag;
799 AutoTArray<nsString, 2> strings;
800 CopyUTF8toUTF16(aFinalChannelURI->GetSpecOrDefault(),
801 *strings.AppendElement());
802 CopyASCIItoUTF16(contentType, *strings.AppendElement());
804 nsCOMPtr<nsIURI> referrer = ReferrerInfo()->GetOriginalReferrer();
805 nsContentUtils::ReportToConsole(
806 errorFlag, "CSS Loader"_ns, mLoader->mDocument,
807 nsContentUtils::eCSS_PROPERTIES, errorMessage, strings, referrer);
809 if (errorFlag == nsIScriptError::errorFlag) {
810 LOG_WARN(
811 (" Ignoring sheet with improper MIME type %s", contentType.get()));
812 mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
813 return NS_OK;
817 SRIMetadata sriMetadata;
818 mSheet->GetIntegrity(sriMetadata);
819 if (!sriMetadata.IsEmpty()) {
820 if (!NS_IsMainThread()) {
821 // We dont process any further in OMT.
822 // This is because we have some main-thread only accesses below.
823 // We need to find a way to optimize this handling.
824 // See Bug 1882046.
825 return NS_OK;
827 nsAutoCString sourceUri;
828 if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
829 mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
831 nsresult rv = VerifySheetIntegrity(sriMetadata, aChannel, aBytes1, aBytes2,
832 sourceUri, mLoader->mReporter);
834 nsCOMPtr<nsILoadGroup> loadGroup;
835 aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
836 if (loadGroup) {
837 mLoader->mReporter->FlushConsoleReports(loadGroup);
838 } else {
839 mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
842 if (NS_FAILED(rv)) {
843 LOG((" Load was blocked by SRI"));
844 MOZ_LOG(gSriPRLog, LogLevel::Debug,
845 ("css::Loader::OnStreamComplete, bad metadata"));
846 mLoader->SheetComplete(*this, NS_ERROR_SRI_CORRUPT);
847 return NS_OK;
851 if (mSheet->GetCORSMode() == CORS_NONE &&
852 !mTriggeringPrincipal->Subsumes(principal)) {
853 mIsCrossOriginNoCORS = true;
855 // Enough to set the URIs on mSheet, since any sibling datas we have share
856 // the same mInner as mSheet and will thus get the same URI.
857 mSheet->SetURIs(aFinalChannelURI, originalURI, aFinalChannelURI);
859 ReferrerPolicy policy =
860 nsContentUtils::GetReferrerPolicyFromChannel(aChannel);
861 nsCOMPtr<nsIReferrerInfo> referrerInfo =
862 ReferrerInfo::CreateForExternalCSSResources(mSheet, policy);
864 mSheet->SetReferrerInfo(referrerInfo);
865 return NS_OK_PARSE_SHEET;
868 Loader::IsAlternate Loader::IsAlternateSheet(const nsAString& aTitle,
869 bool aHasAlternateRel) {
870 // A sheet is alternate if it has a nonempty title that doesn't match the
871 // currently selected style set. But if there _is_ no currently selected
872 // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
873 // is nonempty, we should select the style set corresponding to aTitle, since
874 // that's a preferred sheet.
875 if (aTitle.IsEmpty()) {
876 return IsAlternate::No;
879 if (mDocument) {
880 const nsString& currentSheetSet = mDocument->GetCurrentStyleSheetSet();
881 if (!aHasAlternateRel && currentSheetSet.IsEmpty()) {
882 // There's no preferred set yet, and we now have a sheet with a title.
883 // Make that be the preferred set.
884 // FIXME(emilio): This is kinda wild, can we do it somewhere else?
885 mDocument->SetPreferredStyleSheetSet(aTitle);
886 // We're definitely not an alternate. Also, beware that at this point
887 // currentSheetSet may dangle.
888 return IsAlternate::No;
891 if (aTitle.Equals(currentSheetSet)) {
892 return IsAlternate::No;
896 return IsAlternate::Yes;
899 nsresult Loader::CheckContentPolicy(nsIPrincipal* aLoadingPrincipal,
900 nsIPrincipal* aTriggeringPrincipal,
901 nsIURI* aTargetURI,
902 nsINode* aRequestingNode,
903 const nsAString& aNonce,
904 StylePreloadKind aPreloadKind) {
905 // When performing a system load don't consult content policies.
906 if (!mDocument) {
907 return NS_OK;
910 nsContentPolicyType contentPolicyType =
911 aPreloadKind == StylePreloadKind::None
912 ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
913 : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
915 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
916 aLoadingPrincipal, aTriggeringPrincipal, aRequestingNode,
917 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, contentPolicyType);
918 secCheckLoadInfo->SetCspNonce(aNonce);
920 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
921 nsresult rv =
922 NS_CheckContentLoadPolicy(aTargetURI, secCheckLoadInfo, &shouldLoad,
923 nsContentUtils::GetContentPolicy());
924 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
925 // Asynchronously notify observers (e.g devtools) of CSP failure.
926 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
927 "Loader::NotifyOnFailedCheckPolicy",
928 [targetURI = RefPtr<nsIURI>(aTargetURI),
929 requestingNode = RefPtr<nsINode>(aRequestingNode),
930 contentPolicyType]() {
931 nsCOMPtr<nsIChannel> channel;
932 NS_NewChannel(
933 getter_AddRefs(channel), targetURI, requestingNode,
934 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT,
935 contentPolicyType);
936 NS_SetRequestBlockingReason(
937 channel, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_GENERAL);
938 nsCOMPtr<nsIObserverService> obsService =
939 services::GetObserverService();
940 if (obsService) {
941 obsService->NotifyObservers(
942 channel, "http-on-failed-opening-request", nullptr);
944 }));
945 return NS_ERROR_CONTENT_BLOCKED;
947 return NS_OK;
950 static void RecordUseCountersIfNeeded(Document* aDoc,
951 const StyleSheet& aSheet) {
952 if (!aDoc) {
953 return;
955 const StyleUseCounters* docCounters = aDoc->GetStyleUseCounters();
956 if (!docCounters) {
957 return;
959 if (aSheet.URLData()->ChromeRulesEnabled()) {
960 return;
962 const auto* sheetCounters = aSheet.GetStyleUseCounters();
963 if (!sheetCounters) {
964 return;
966 Servo_UseCounters_Merge(docCounters, sheetCounters);
967 aDoc->MaybeWarnAboutZoom();
971 * CreateSheet() creates a StyleSheet object for the given URI.
973 * We check for an existing style sheet object for that uri in various caches
974 * and clone it if we find it. Cloned sheets will have the title/media/enabled
975 * state of the sheet they are clones off; make sure to call PrepareSheet() on
976 * the result of CreateSheet().
978 std::tuple<RefPtr<StyleSheet>, Loader::SheetState> Loader::CreateSheet(
979 nsIURI* aURI, nsIContent* aLinkingContent,
980 nsIPrincipal* aTriggeringPrincipal, css::SheetParsingMode aParsingMode,
981 CORSMode aCORSMode, const Encoding* aPreloadOrParentDataEncoding,
982 const nsAString& aIntegrity, bool aSyncLoad,
983 StylePreloadKind aPreloadKind) {
984 MOZ_ASSERT(aURI, "This path is not taken for inline stylesheets");
985 LOG(("css::Loader::CreateSheet(%s)", aURI->GetSpecOrDefault().get()));
987 SRIMetadata sriMetadata;
988 if (!aIntegrity.IsEmpty()) {
989 MOZ_LOG(gSriPRLog, LogLevel::Debug,
990 ("css::Loader::CreateSheet, integrity=%s",
991 NS_ConvertUTF16toUTF8(aIntegrity).get()));
992 nsAutoCString sourceUri;
993 if (mDocument && mDocument->GetDocumentURI()) {
994 mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
996 SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata);
999 if (mSheets) {
1000 SheetLoadDataHashKey key(aURI, aTriggeringPrincipal, LoaderPrincipal(),
1001 PartitionedPrincipal(),
1002 GetFallbackEncoding(*this, aLinkingContent,
1003 aPreloadOrParentDataEncoding),
1004 aCORSMode, aParsingMode, CompatMode(aPreloadKind),
1005 sriMetadata, aPreloadKind);
1006 auto cacheResult = mSheets->Lookup(*this, key, aSyncLoad);
1007 if (cacheResult.mState != CachedSubResourceState::Miss) {
1008 SheetState sheetState = SheetState::Complete;
1009 RefPtr<StyleSheet> sheet;
1010 if (cacheResult.mCompleteValue) {
1011 sheet = cacheResult.mCompleteValue->Clone(nullptr, nullptr);
1012 mDocument->SetDidHitCompleteSheetCache();
1013 RecordUseCountersIfNeeded(mDocument, *sheet);
1014 mLoadsPerformed.PutEntry(key);
1015 } else {
1016 MOZ_ASSERT(cacheResult.mLoadingOrPendingValue);
1017 sheet = cacheResult.mLoadingOrPendingValue->ValueForCache();
1018 sheetState = cacheResult.mState == CachedSubResourceState::Loading
1019 ? SheetState::Loading
1020 : SheetState::Pending;
1022 LOG((" Hit cache with state: %s", gStateStrings[size_t(sheetState)]));
1023 return {std::move(sheet), sheetState};
1027 nsIURI* sheetURI = aURI;
1028 nsIURI* baseURI = aURI;
1029 nsIURI* originalURI = aURI;
1031 auto sheet = MakeRefPtr<StyleSheet>(aParsingMode, aCORSMode, sriMetadata);
1032 sheet->SetURIs(sheetURI, originalURI, baseURI);
1033 nsCOMPtr<nsIReferrerInfo> referrerInfo =
1034 ReferrerInfo::CreateForExternalCSSResources(sheet);
1035 sheet->SetReferrerInfo(referrerInfo);
1036 LOG((" Needs parser"));
1037 return {std::move(sheet), SheetState::NeedsParser};
1040 static Loader::MediaMatched MediaListMatches(const MediaList* aMediaList,
1041 const Document* aDocument) {
1042 if (!aMediaList || !aDocument) {
1043 return Loader::MediaMatched::Yes;
1046 if (aMediaList->Matches(*aDocument)) {
1047 return Loader::MediaMatched::Yes;
1050 return Loader::MediaMatched::No;
1054 * PrepareSheet() handles setting the media and title on the sheet, as
1055 * well as setting the enabled state based on the title and whether
1056 * the sheet had "alternate" in its rel.
1058 Loader::MediaMatched Loader::PrepareSheet(
1059 StyleSheet& aSheet, const nsAString& aTitle, const nsAString& aMediaString,
1060 MediaList* aMediaList, IsAlternate aIsAlternate,
1061 IsExplicitlyEnabled aIsExplicitlyEnabled) {
1062 RefPtr<MediaList> mediaList(aMediaList);
1064 if (!aMediaString.IsEmpty()) {
1065 NS_ASSERTION(!aMediaList,
1066 "must not provide both aMediaString and aMediaList");
1067 mediaList = MediaList::Create(NS_ConvertUTF16toUTF8(aMediaString));
1070 aSheet.SetMedia(do_AddRef(mediaList));
1072 aSheet.SetTitle(aTitle);
1073 aSheet.SetEnabled(aIsAlternate == IsAlternate::No ||
1074 aIsExplicitlyEnabled == IsExplicitlyEnabled::Yes);
1075 return MediaListMatches(mediaList, mDocument);
1079 * InsertSheetInTree handles ordering of sheets in the document or shadow root.
1081 * Here we have two types of sheets -- those with linking elements and
1082 * those without. The latter are loaded by Link: headers, and are only added to
1083 * the document.
1085 * The following constraints are observed:
1086 * 1) Any sheet with a linking element comes after all sheets without
1087 * linking elements
1088 * 2) Sheets without linking elements are inserted in the order in
1089 * which the inserting requests come in, since all of these are
1090 * inserted during header data processing in the content sink
1091 * 3) Sheets with linking elements are ordered based on document order
1092 * as determined by CompareDocumentPosition.
1094 void Loader::InsertSheetInTree(StyleSheet& aSheet) {
1095 LOG(("css::Loader::InsertSheetInTree"));
1096 MOZ_ASSERT(mDocument, "Must have a document to insert into");
1098 nsINode* owningNode = aSheet.GetOwnerNode();
1099 MOZ_ASSERT(!owningNode || owningNode->IsInUncomposedDoc() ||
1100 owningNode->IsInShadowTree(),
1101 "Why would we insert it anywhere?");
1102 ShadowRoot* shadow = owningNode ? owningNode->GetContainingShadow() : nullptr;
1104 auto& target = shadow ? static_cast<DocumentOrShadowRoot&>(*shadow)
1105 : static_cast<DocumentOrShadowRoot&>(*mDocument);
1107 // XXX Need to cancel pending sheet loads for this element, if any
1109 int32_t sheetCount = target.SheetCount();
1112 * Start the walk at the _end_ of the list, since in the typical
1113 * case we'll just want to append anyway. We want to break out of
1114 * the loop when insertionPoint points to just before the index we
1115 * want to insert at. In other words, when we leave the loop
1116 * insertionPoint is the index of the stylesheet that immediately
1117 * precedes the one we're inserting.
1119 int32_t insertionPoint = sheetCount - 1;
1120 for (; insertionPoint >= 0; --insertionPoint) {
1121 nsINode* sheetOwner = target.SheetAt(insertionPoint)->GetOwnerNode();
1122 if (sheetOwner && !owningNode) {
1123 // Keep moving; all sheets with a sheetOwner come after all
1124 // sheets without a linkingNode
1125 continue;
1128 if (!sheetOwner) {
1129 // Aha! The current sheet has no sheet owner, so we want to insert after
1130 // it no matter whether we have a linking content or not.
1131 break;
1134 MOZ_ASSERT(owningNode != sheetOwner, "Why do we still have our old sheet?");
1136 // Have to compare
1137 if (nsContentUtils::PositionIsBefore(sheetOwner, owningNode)) {
1138 // The current sheet comes before us, and it better be the first
1139 // such, because now we break
1140 break;
1144 ++insertionPoint;
1146 if (shadow) {
1147 shadow->InsertSheetAt(insertionPoint, aSheet);
1148 } else {
1149 mDocument->InsertSheetAt(insertionPoint, aSheet);
1152 LOG((" Inserting into target (doc: %d) at position %d",
1153 target.AsNode().IsDocument(), insertionPoint));
1157 * InsertChildSheet handles ordering of @import-ed sheet in their
1158 * parent sheets. Here we want to just insert based on order of the
1159 * @import rules that imported the sheets. In theory we can't just
1160 * append to the end because the CSSOM can insert @import rules. In
1161 * practice, we get the call to load the child sheet before the CSSOM
1162 * has finished inserting the @import rule, so we have no idea where
1163 * to put it anyway. So just append for now. (In the future if we
1164 * want to insert the sheet at the correct position, we'll need to
1165 * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
1166 * bug 1220506.)
1168 void Loader::InsertChildSheet(StyleSheet& aSheet, StyleSheet& aParentSheet) {
1169 LOG(("css::Loader::InsertChildSheet"));
1171 // child sheets should always start out enabled, even if they got
1172 // cloned off of top-level sheets which were disabled
1173 aSheet.SetEnabled(true);
1174 aParentSheet.AppendStyleSheet(aSheet);
1176 LOG((" Inserting into parent sheet"));
1179 nsresult Loader::LoadSheetSyncInternal(SheetLoadData& aLoadData,
1180 SheetState aSheetState) {
1181 LOG((" Synchronous load"));
1182 MOZ_ASSERT(!aLoadData.mObserver, "Observer for a sync load?");
1183 MOZ_ASSERT(aSheetState == SheetState::NeedsParser,
1184 "Sync loads can't reuse existing async loads");
1186 nsINode* requestingNode = aLoadData.GetRequestingNode();
1188 nsresult rv = NS_OK;
1190 // Create a StreamLoader instance to which we will feed
1191 // the data from the sync load. Do this before creating the
1192 // channel to make error recovery simpler.
1193 auto streamLoader = MakeRefPtr<StreamLoader>(aLoadData);
1195 if (mDocument) {
1196 net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
1197 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, mDocument);
1200 // Synchronous loads should only be used internally. Therefore no CORS
1201 // policy is needed.
1202 nsSecurityFlags securityFlags =
1203 nsContentSecurityManager::ComputeSecurityFlags(
1204 CORSMode::CORS_NONE, nsContentSecurityManager::CORSSecurityMapping::
1205 CORS_NONE_MAPS_TO_INHERITED_CONTEXT);
1207 securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
1209 nsContentPolicyType contentPolicyType =
1210 aLoadData.mPreloadKind == StylePreloadKind::None
1211 ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1212 : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
1214 // Just load it
1215 nsCOMPtr<nsIChannel> channel;
1216 // Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both
1217 // a node and a principal.
1218 // This is because of a case where the node is the document being styled and
1219 // the principal is the stylesheet (perhaps from a different origin) that is
1220 // applying the styles.
1221 if (requestingNode) {
1222 rv = NS_NewChannelWithTriggeringPrincipal(
1223 getter_AddRefs(channel), aLoadData.mURI, requestingNode,
1224 aLoadData.mTriggeringPrincipal, securityFlags, contentPolicyType);
1225 } else {
1226 MOZ_ASSERT(aLoadData.mTriggeringPrincipal->Equals(LoaderPrincipal()));
1227 auto result = URLPreloader::ReadURI(aLoadData.mURI);
1228 if (result.isOk()) {
1229 nsCOMPtr<nsIInputStream> stream;
1230 MOZ_TRY(
1231 NS_NewCStringInputStream(getter_AddRefs(stream), result.unwrap()));
1233 rv = NS_NewInputStreamChannel(
1234 getter_AddRefs(channel), aLoadData.mURI, stream.forget(),
1235 aLoadData.mTriggeringPrincipal, securityFlags, contentPolicyType);
1236 } else {
1237 rv = NS_NewChannel(getter_AddRefs(channel), aLoadData.mURI,
1238 aLoadData.mTriggeringPrincipal, securityFlags,
1239 contentPolicyType);
1242 if (NS_FAILED(rv)) {
1243 LOG_ERROR((" Failed to create channel"));
1244 streamLoader->ChannelOpenFailed(rv);
1245 SheetComplete(aLoadData, rv);
1246 return rv;
1249 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1250 loadInfo->SetCspNonce(aLoadData.Nonce());
1252 nsCOMPtr<nsIInputStream> stream;
1253 rv = channel->Open(getter_AddRefs(stream));
1255 if (NS_FAILED(rv)) {
1256 LOG_ERROR((" Failed to open URI synchronously"));
1257 streamLoader->ChannelOpenFailed(rv);
1258 SheetComplete(aLoadData, rv);
1259 return rv;
1262 // Force UA sheets to be UTF-8.
1263 // XXX this is only necessary because the default in
1264 // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
1265 channel->SetContentCharset("UTF-8"_ns);
1267 // Manually feed the streamloader the contents of the stream.
1268 // This will call back into OnStreamComplete
1269 // and thence to ParseSheet. Regardless of whether this fails,
1270 // SheetComplete has been called.
1271 return nsSyncLoadService::PushSyncStreamToListener(stream.forget(),
1272 streamLoader, channel);
1275 bool Loader::MaybeDeferLoad(SheetLoadData& aLoadData, SheetState aSheetState,
1276 PendingLoad aPendingLoad,
1277 const SheetLoadDataHashKey& aKey) {
1278 MOZ_ASSERT(mSheets);
1280 // If we have at least one other load ongoing, then we can defer it until
1281 // all non-pending loads are done.
1282 if (aSheetState == SheetState::NeedsParser &&
1283 aPendingLoad == PendingLoad::No && aLoadData.ShouldDefer() &&
1284 mOngoingLoadCount > mPendingLoadCount + 1) {
1285 LOG((" Deferring sheet load"));
1286 ++mPendingLoadCount;
1287 mSheets->DeferLoad(aKey, aLoadData);
1288 return true;
1290 return false;
1293 bool Loader::MaybeCoalesceLoadAndNotifyOpen(SheetLoadData& aLoadData,
1294 SheetState aSheetState,
1295 const SheetLoadDataHashKey& aKey,
1296 const PreloadHashKey& aPreloadKey) {
1297 bool coalescedLoad = false;
1298 auto cacheState = [&aSheetState] {
1299 switch (aSheetState) {
1300 case SheetState::Complete:
1301 return CachedSubResourceState::Complete;
1302 case SheetState::Pending:
1303 return CachedSubResourceState::Pending;
1304 case SheetState::Loading:
1305 return CachedSubResourceState::Loading;
1306 case SheetState::NeedsParser:
1307 return CachedSubResourceState::Miss;
1309 MOZ_ASSERT_UNREACHABLE("wat");
1310 return CachedSubResourceState::Miss;
1311 }();
1313 if ((coalescedLoad = mSheets->CoalesceLoad(aKey, aLoadData, cacheState))) {
1314 if (aSheetState == SheetState::Pending) {
1315 ++mPendingLoadCount;
1316 } else {
1317 aLoadData.NotifyOpen(
1318 aPreloadKey, mDocument,
1319 aLoadData.IsLinkRelPreload() /* TODO: why not `IsPreload()`?*/);
1322 return coalescedLoad;
1326 * LoadSheet handles the actual load of a sheet. If the load is
1327 * supposed to be synchronous it just opens a channel synchronously
1328 * using the given uri, wraps the resulting stream in a converter
1329 * stream and calls ParseSheet. Otherwise it tries to look for an
1330 * existing load for this URI and piggyback on it. Failing all that,
1331 * a new load is kicked off asynchronously.
1333 nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState,
1334 uint64_t aEarlyHintPreloaderId,
1335 PendingLoad aPendingLoad) {
1336 LOG(("css::Loader::LoadSheet"));
1337 MOZ_ASSERT(aLoadData.mURI, "Need a URI to load");
1338 MOZ_ASSERT(aLoadData.mSheet, "Need a sheet to load into");
1339 MOZ_ASSERT(aSheetState != SheetState::Complete, "Why bother?");
1340 MOZ_ASSERT(!aLoadData.mUseSystemPrincipal || aLoadData.mSyncLoad,
1341 "Shouldn't use system principal for async loads");
1343 LOG_URI(" Load from: '%s'", aLoadData.mURI);
1345 // If we're firing a pending load, this load is already accounted for the
1346 // first time it went through this function.
1347 if (aPendingLoad == PendingLoad::No) {
1348 if (aLoadData.BlocksLoadEvent()) {
1349 IncrementOngoingLoadCountAndMaybeBlockOnload();
1352 // We technically never defer non-top-level sheets, so this condition could
1353 // be outside the branch, but conceptually it should be here.
1354 if (aLoadData.mParentData) {
1355 ++aLoadData.mParentData->mPendingChildren;
1359 if (!mDocument && !aLoadData.mIsNonDocumentSheet) {
1360 // No point starting the load; just release all the data and such.
1361 LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
1362 SheetComplete(aLoadData, NS_BINDING_ABORTED);
1363 return NS_BINDING_ABORTED;
1366 if (aLoadData.mSyncLoad) {
1367 return LoadSheetSyncInternal(aLoadData, aSheetState);
1370 SheetLoadDataHashKey key(aLoadData);
1372 auto preloadKey = PreloadHashKey::CreateAsStyle(aLoadData);
1373 if (mSheets) {
1374 if (MaybeDeferLoad(aLoadData, aSheetState, aPendingLoad, key)) {
1375 return NS_OK;
1378 if (MaybeCoalesceLoadAndNotifyOpen(aLoadData, aSheetState, key,
1379 preloadKey)) {
1380 // All done here; once the load completes we'll be marked complete
1381 // automatically.
1382 return NS_OK;
1386 aLoadData.NotifyOpen(preloadKey, mDocument, aLoadData.IsLinkRelPreload());
1388 return LoadSheetAsyncInternal(aLoadData, aEarlyHintPreloaderId, key);
1391 void Loader::AdjustPriority(const SheetLoadData& aLoadData,
1392 nsIChannel* aChannel) {
1393 if (!aLoadData.ShouldDefer() && aLoadData.IsLinkRelPreload()) {
1394 SheetLoadData::PrioritizeAsPreload(aChannel);
1397 if (!StaticPrefs::network_fetchpriority_enabled()) {
1398 return;
1401 nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(aChannel);
1403 if (!sp) {
1404 return;
1407 // Adjusting priorites is specified as implementation-defined.
1408 // See corresponding preferences in StaticPrefList.yaml for more context.
1409 const int32_t supportsPriorityDelta = [&]() {
1410 if (aLoadData.ShouldDefer()) {
1411 return FETCH_PRIORITY_ADJUSTMENT_FOR(deferred_style,
1412 aLoadData.mFetchPriority);
1414 if (aLoadData.IsLinkRelPreload()) {
1415 return FETCH_PRIORITY_ADJUSTMENT_FOR(link_preload_style,
1416 aLoadData.mFetchPriority);
1418 return FETCH_PRIORITY_ADJUSTMENT_FOR(non_deferred_style,
1419 aLoadData.mFetchPriority);
1420 }();
1422 sp->AdjustPriority(supportsPriorityDelta);
1423 #ifdef DEBUG
1424 int32_t adjustedPriority;
1425 sp->GetPriority(&adjustedPriority);
1426 LogPriorityMapping(sCssLoaderLog, aLoadData.mFetchPriority, adjustedPriority);
1427 #endif
1430 nsresult Loader::LoadSheetAsyncInternal(SheetLoadData& aLoadData,
1431 uint64_t aEarlyHintPreloaderId,
1432 const SheetLoadDataHashKey& aKey) {
1433 nsresult rv = NS_OK;
1435 SRIMetadata sriMetadata;
1436 aLoadData.mSheet->GetIntegrity(sriMetadata);
1438 nsINode* requestingNode = aLoadData.GetRequestingNode();
1440 nsCOMPtr<nsILoadGroup> loadGroup;
1441 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
1442 if (mDocument) {
1443 loadGroup = mDocument->GetDocumentLoadGroup();
1444 // load for a document with no loadgrup indicates that something is
1445 // completely bogus, let's bail out early.
1446 if (!loadGroup) {
1447 LOG_ERROR((" Failed to query loadGroup from document"));
1448 SheetComplete(aLoadData, NS_ERROR_UNEXPECTED);
1449 return NS_ERROR_UNEXPECTED;
1452 cookieJarSettings = mDocument->CookieJarSettings();
1455 #ifdef DEBUG
1456 AutoRestore<bool> syncCallbackGuard(mSyncCallback);
1457 mSyncCallback = true;
1458 #endif
1460 nsSecurityFlags securityFlags =
1461 nsContentSecurityManager::ComputeSecurityFlags(
1462 aLoadData.mSheet->GetCORSMode(),
1463 nsContentSecurityManager::CORSSecurityMapping::
1464 CORS_NONE_MAPS_TO_INHERITED_CONTEXT);
1466 securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
1468 nsContentPolicyType contentPolicyType =
1469 aLoadData.mPreloadKind == StylePreloadKind::None
1470 ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1471 : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
1473 nsCOMPtr<nsIChannel> channel;
1474 // Note we are calling NS_NewChannelWithTriggeringPrincipal here with a node
1475 // and a principal. This is because of a case where the node is the document
1476 // being styled and the principal is the stylesheet (perhaps from a different
1477 // origin) that is applying the styles.
1478 if (requestingNode) {
1479 rv = NS_NewChannelWithTriggeringPrincipal(
1480 getter_AddRefs(channel), aLoadData.mURI, requestingNode,
1481 aLoadData.mTriggeringPrincipal, securityFlags, contentPolicyType,
1482 /* PerformanceStorage */ nullptr, loadGroup);
1483 } else {
1484 MOZ_ASSERT(aLoadData.mTriggeringPrincipal->Equals(LoaderPrincipal()));
1485 rv = NS_NewChannel(getter_AddRefs(channel), aLoadData.mURI,
1486 aLoadData.mTriggeringPrincipal, securityFlags,
1487 contentPolicyType, cookieJarSettings,
1488 /* aPerformanceStorage */ nullptr, loadGroup);
1491 if (NS_FAILED(rv)) {
1492 LOG_ERROR((" Failed to create channel"));
1493 SheetComplete(aLoadData, rv);
1494 return rv;
1497 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1498 loadInfo->SetCspNonce(aLoadData.Nonce());
1500 if (!aLoadData.ShouldDefer()) {
1501 if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(channel)) {
1502 cos->AddClassFlags(nsIClassOfService::Leader);
1505 if (aLoadData.IsLinkRelPreload()) {
1506 SheetLoadData::AddLoadBackgroundFlag(channel);
1510 AdjustPriority(aLoadData, channel);
1512 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
1513 if (nsCOMPtr<nsIReferrerInfo> referrerInfo = aLoadData.ReferrerInfo()) {
1514 rv = httpChannel->SetReferrerInfo(referrerInfo);
1515 Unused << NS_WARN_IF(NS_FAILED(rv));
1518 nsCOMPtr<nsIHttpChannelInternal> internalChannel =
1519 do_QueryInterface(httpChannel);
1520 if (internalChannel) {
1521 rv = internalChannel->SetIntegrityMetadata(
1522 sriMetadata.GetIntegrityString());
1523 NS_ENSURE_SUCCESS(rv, rv);
1526 // Set the initiator type
1527 if (nsCOMPtr<nsITimedChannel> timedChannel =
1528 do_QueryInterface(httpChannel)) {
1529 if (aLoadData.mParentData) {
1530 timedChannel->SetInitiatorType(u"css"_ns);
1532 // This is a child sheet load.
1534 // The resource timing of the sub-resources that a document loads
1535 // should normally be reported to the document. One exception is any
1536 // sub-resources of any cross-origin resources that are loaded. We
1537 // don't mind reporting timing data for a direct child cross-origin
1538 // resource since the resource that linked to it (and hence potentially
1539 // anything in that parent origin) is aware that the cross-origin
1540 // resources is to be loaded. However, we do not want to report
1541 // timings for any sub-resources that a cross-origin resource may load
1542 // since that obviously leaks information about what the cross-origin
1543 // resource loads, which is bad.
1545 // In addition to checking whether we're an immediate child resource of
1546 // a cross-origin resource (by checking if mIsCrossOriginNoCORS is set
1547 // to true on our parent), we also check our parent to see whether it
1548 // itself is a sub-resource of a cross-origin resource by checking
1549 // mBlockResourceTiming. If that is set then we too are such a
1550 // sub-resource and so we set the flag on ourself too to propagate it
1551 // on down.
1552 if (aLoadData.mParentData->mIsCrossOriginNoCORS ||
1553 aLoadData.mParentData->mBlockResourceTiming) {
1554 // Set a flag so any other stylesheet triggered by this one will
1555 // not be reported
1556 aLoadData.mBlockResourceTiming = true;
1558 // Mark the channel so PerformanceMainThread::AddEntry will not
1559 // report the resource.
1560 timedChannel->SetReportResourceTiming(false);
1563 } else if (aEarlyHintPreloaderId) {
1564 timedChannel->SetInitiatorType(u"early-hints"_ns);
1565 } else {
1566 timedChannel->SetInitiatorType(u"link"_ns);
1571 // Now tell the channel we expect text/css data back.... We do
1572 // this before opening it, so it's only treated as a hint.
1573 channel->SetContentType("text/css"_ns);
1575 // We don't have to hold on to the stream loader. The ownership
1576 // model is: Necko owns the stream loader, which owns the load data,
1577 // which owns us
1578 auto streamLoader = MakeRefPtr<StreamLoader>(aLoadData);
1579 if (mDocument) {
1580 net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
1581 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, mDocument);
1584 if (aEarlyHintPreloaderId) {
1585 nsCOMPtr<nsIHttpChannelInternal> channelInternal =
1586 do_QueryInterface(channel);
1587 NS_ENSURE_TRUE(channelInternal != nullptr, NS_ERROR_FAILURE);
1589 rv = channelInternal->SetEarlyHintPreloaderId(aEarlyHintPreloaderId);
1590 NS_ENSURE_SUCCESS(rv, rv);
1592 rv = channel->AsyncOpen(streamLoader);
1593 if (NS_FAILED(rv)) {
1594 LOG_ERROR((" Failed to create stream loader"));
1595 streamLoader->ChannelOpenFailed(rv);
1596 // NOTE: NotifyStop will be done in SheetComplete -> NotifyObservers.
1597 aLoadData.NotifyStart(channel);
1598 SheetComplete(aLoadData, rv);
1599 return rv;
1602 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1603 if (nsCOMPtr<nsIHttpChannelInternal> hci = do_QueryInterface(channel)) {
1604 hci->DoDiagnosticAssertWhenOnStopNotCalledOnDestroy();
1606 #endif
1608 if (mSheets) {
1609 mSheets->LoadStarted(aKey, aLoadData);
1611 return NS_OK;
1615 * ParseSheet handles parsing the data stream.
1617 Loader::Completed Loader::ParseSheet(
1618 const nsACString& aBytes, const RefPtr<SheetLoadDataHolder>& aLoadData,
1619 AllowAsyncParse aAllowAsync) {
1620 LOG(("css::Loader::ParseSheet"));
1621 SheetLoadData* loadData = aLoadData->get();
1622 MOZ_ASSERT(loadData);
1624 if (loadData->mURI) {
1625 LOG_URI(" Load succeeded for URI: '%s', parsing", loadData->mURI);
1627 AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(LAYOUT_CSSParsing);
1629 ++mParsedSheetCount;
1631 loadData->mIsBeingParsed = true;
1633 StyleSheet* sheet = loadData->mSheet;
1634 MOZ_ASSERT(sheet);
1636 // Some cases, like inline style and UA stylesheets, need to be parsed
1637 // synchronously. The former may trigger child loads, the latter must not.
1638 if (loadData->mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
1639 sheet->ParseSheetSync(this, aBytes, loadData);
1640 loadData->mIsBeingParsed = false;
1642 bool noPendingChildren = loadData->mPendingChildren == 0;
1643 MOZ_ASSERT_IF(loadData->mSyncLoad, noPendingChildren);
1644 if (noPendingChildren) {
1645 SheetComplete(*loadData, NS_OK);
1646 return Completed::Yes;
1648 return Completed::No;
1651 // This parse does not need to be synchronous. \o/
1653 // Note that load is already blocked from
1654 // IncrementOngoingLoadCountAndMaybeBlockOnload(), and will be unblocked from
1655 // SheetFinishedParsingAsync which will end up in NotifyObservers as needed.
1656 sheet->ParseSheet(*this, aBytes, aLoadData)
1657 ->Then(
1658 GetMainThreadSerialEventTarget(), __func__,
1659 [loadData = aLoadData](bool aDummy) {
1660 MOZ_ASSERT(NS_IsMainThread());
1661 loadData->get()->SheetFinishedParsingAsync();
1663 [] { MOZ_CRASH("rejected parse promise"); });
1664 return Completed::No;
1667 void Loader::NotifyObservers(SheetLoadData& aData, nsresult aStatus) {
1668 RecordUseCountersIfNeeded(mDocument, *aData.mSheet);
1669 RefPtr loadDispatcher = aData.PrepareLoadEventIfNeeded();
1670 if (aData.mURI) {
1671 mLoadsPerformed.PutEntry(SheetLoadDataHashKey(aData));
1672 aData.NotifyStop(aStatus);
1673 // NOTE(emilio): This needs to happen before notifying observers, as
1674 // FontFaceSet for example checks for pending sheet loads from the
1675 // StyleSheetLoaded callback.
1676 if (aData.BlocksLoadEvent()) {
1677 DecrementOngoingLoadCountAndMaybeUnblockOnload();
1678 if (mPendingLoadCount && mPendingLoadCount == mOngoingLoadCount) {
1679 LOG((" No more loading sheets; starting deferred loads"));
1680 StartDeferredLoads();
1684 if (!aData.mTitle.IsEmpty() && NS_SUCCEEDED(aStatus)) {
1685 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
1686 "Loader::NotifyObservers - Create PageStyle actor",
1687 [doc = RefPtr{mDocument}] {
1688 // Force creating the page style actor, if available.
1689 // This will no-op if no actor with this name is registered (outside
1690 // of desktop Firefox).
1691 nsCOMPtr<nsISupports> pageStyleActor =
1692 do_QueryActor("PageStyle", doc);
1693 Unused << pageStyleActor;
1694 }));
1696 if (aData.mMustNotify) {
1697 if (nsCOMPtr<nsICSSLoaderObserver> observer = std::move(aData.mObserver)) {
1698 LOG((" Notifying observer %p for data %p. deferred: %d", observer.get(),
1699 &aData, aData.ShouldDefer()));
1700 observer->StyleSheetLoaded(aData.mSheet, aData.ShouldDefer(), aStatus);
1703 for (nsCOMPtr<nsICSSLoaderObserver> obs : mObservers.ForwardRange()) {
1704 LOG((" Notifying global observer %p for data %p. deferred: %d",
1705 obs.get(), &aData, aData.ShouldDefer()));
1706 obs->StyleSheetLoaded(aData.mSheet, aData.ShouldDefer(), aStatus);
1709 if (loadDispatcher) {
1710 loadDispatcher->RunDOMEventWhenSafe();
1712 } else if (loadDispatcher) {
1713 loadDispatcher->PostDOMEvent();
1718 * SheetComplete is the do-it-all cleanup function. It removes the
1719 * load data from the "loading" hashtable, adds the sheet to the
1720 * "completed" hashtable, massages the XUL cache, handles siblings of
1721 * the load data (other loads for the same URI), handles unblocking
1722 * blocked parent loads as needed, and most importantly calls
1723 * NS_RELEASE on the load data to destroy the whole mess.
1725 void Loader::SheetComplete(SheetLoadData& aLoadData, nsresult aStatus) {
1726 LOG(("css::Loader::SheetComplete, status: 0x%" PRIx32,
1727 static_cast<uint32_t>(aStatus)));
1728 SharedStyleSheetCache::LoadCompleted(mSheets.get(), aLoadData, aStatus);
1731 // static
1732 void Loader::MarkLoadTreeFailed(SheetLoadData& aLoadData,
1733 Loader* aOnlyForLoader) {
1734 if (aLoadData.mURI) {
1735 LOG_URI(" Load failed: '%s'", aLoadData.mURI);
1738 SheetLoadData* data = &aLoadData;
1739 do {
1740 if (!aOnlyForLoader || aOnlyForLoader == data->mLoader) {
1741 data->mLoadFailed = true;
1742 data->mSheet->MaybeRejectReplacePromise();
1745 if (data->mParentData) {
1746 MarkLoadTreeFailed(*data->mParentData, aOnlyForLoader);
1749 data = data->mNext;
1750 } while (data);
1753 RefPtr<StyleSheet> Loader::LookupInlineSheetInCache(
1754 const nsAString& aBuffer, nsIPrincipal* aSheetPrincipal) {
1755 auto result = mInlineSheets.Lookup(aBuffer);
1756 if (!result) {
1757 return nullptr;
1759 StyleSheet* sheet = result.Data();
1760 if (NS_WARN_IF(sheet->HasModifiedRules())) {
1761 // Remove it now that we know that we're never going to use this stylesheet
1762 // again.
1763 result.Remove();
1764 return nullptr;
1766 if (NS_WARN_IF(!sheet->Principal()->Equals(aSheetPrincipal))) {
1767 // If the sheet is going to have different access rights, don't return it
1768 // from the cache.
1769 return nullptr;
1771 return sheet->Clone(nullptr, nullptr);
1774 void Loader::MaybeNotifyPreloadUsed(SheetLoadData& aData) {
1775 if (!mDocument) {
1776 return;
1779 auto key = PreloadHashKey::CreateAsStyle(aData);
1780 RefPtr<PreloaderBase> preload = mDocument->Preloads().LookupPreload(key);
1781 if (!preload) {
1782 return;
1785 preload->NotifyUsage(mDocument);
1788 Result<Loader::LoadSheetResult, nsresult> Loader::LoadInlineStyle(
1789 const SheetInfo& aInfo, const nsAString& aBuffer,
1790 nsICSSLoaderObserver* aObserver) {
1791 LOG(("css::Loader::LoadInlineStyle"));
1792 MOZ_ASSERT(aInfo.mContent);
1794 if (!mEnabled) {
1795 LOG_WARN((" Not enabled"));
1796 return Err(NS_ERROR_NOT_AVAILABLE);
1799 if (!mDocument) {
1800 return Err(NS_ERROR_NOT_INITIALIZED);
1803 MOZ_ASSERT(LinkStyle::FromNodeOrNull(aInfo.mContent),
1804 "Element is not a style linking element!");
1806 // Since we're not planning to load a URI, no need to hand a principal to the
1807 // load data or to CreateSheet().
1809 // Check IsAlternateSheet now, since it can mutate our document.
1810 auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
1811 LOG((" Sheet is alternate: %d", static_cast<int>(isAlternate)));
1813 // Use the document's base URL so that @import in the inline sheet picks up
1814 // the right base.
1815 nsIURI* baseURI = aInfo.mContent->GetBaseURI();
1816 nsIURI* sheetURI = aInfo.mContent->OwnerDoc()->GetDocumentURI();
1817 nsIURI* originalURI = nullptr;
1819 MOZ_ASSERT(aInfo.mIntegrity.IsEmpty());
1820 nsIPrincipal* loadingPrincipal = LoaderPrincipal();
1821 nsIPrincipal* principal = aInfo.mTriggeringPrincipal
1822 ? aInfo.mTriggeringPrincipal.get()
1823 : loadingPrincipal;
1824 nsIPrincipal* sheetPrincipal = [&] {
1825 // The triggering principal may be an expanded principal, which is safe to
1826 // use for URL security checks, but not as the loader principal for a
1827 // stylesheet. So treat this as principal inheritance, and downgrade if
1828 // necessary.
1830 // FIXME(emilio): Why doing this for inline sheets but not for links?
1831 if (aInfo.mTriggeringPrincipal) {
1832 return BasePrincipal::Cast(aInfo.mTriggeringPrincipal)
1833 ->PrincipalToInherit();
1835 return LoaderPrincipal();
1836 }();
1838 // We only cache sheets if in shadow trees, since regular document sheets are
1839 // likely to be unique.
1840 const bool isWorthCaching =
1841 StaticPrefs::layout_css_inline_style_caching_always_enabled() ||
1842 aInfo.mContent->IsInShadowTree();
1843 RefPtr<StyleSheet> sheet;
1844 if (isWorthCaching) {
1845 sheet = LookupInlineSheetInCache(aBuffer, sheetPrincipal);
1847 const bool isSheetFromCache = !!sheet;
1848 if (!isSheetFromCache) {
1849 sheet = MakeRefPtr<StyleSheet>(eAuthorSheetFeatures, aInfo.mCORSMode,
1850 SRIMetadata{});
1851 sheet->SetURIs(sheetURI, originalURI, baseURI);
1852 nsIReferrerInfo* referrerInfo =
1853 aInfo.mContent->OwnerDoc()->ReferrerInfoForInternalCSSAndSVGResources();
1854 sheet->SetReferrerInfo(referrerInfo);
1855 // We never actually load this, so just set its principal directly.
1856 sheet->SetPrincipal(sheetPrincipal);
1859 auto matched = PrepareSheet(*sheet, aInfo.mTitle, aInfo.mMedia, nullptr,
1860 isAlternate, aInfo.mIsExplicitlyEnabled);
1861 if (auto* linkStyle = LinkStyle::FromNode(*aInfo.mContent)) {
1862 linkStyle->SetStyleSheet(sheet);
1864 MOZ_ASSERT(sheet->IsComplete() == isSheetFromCache);
1866 Completed completed;
1867 auto data = MakeRefPtr<SheetLoadData>(
1868 this, aInfo.mTitle, /* aURI = */ nullptr, sheet, SyncLoad::No,
1869 aInfo.mContent, isAlternate, matched, StylePreloadKind::None, aObserver,
1870 principal, aInfo.mReferrerInfo, aInfo.mNonce, aInfo.mFetchPriority);
1871 MOZ_ASSERT(data->GetRequestingNode() == aInfo.mContent);
1872 if (isSheetFromCache) {
1873 MOZ_ASSERT(sheet->IsComplete());
1874 MOZ_ASSERT(sheet->GetOwnerNode() == aInfo.mContent);
1875 completed = Completed::Yes;
1876 InsertSheetInTree(*sheet);
1877 NotifyOfCachedLoad(std::move(data));
1878 } else {
1879 // Parse completion releases the load data.
1881 // Note that we need to parse synchronously, since the web expects that the
1882 // effects of inline stylesheets are visible immediately (aside from
1883 // @imports).
1884 NS_ConvertUTF16toUTF8 utf8(aBuffer);
1885 RefPtr<SheetLoadDataHolder> holder(
1886 new nsMainThreadPtrHolder<css::SheetLoadData>(__func__, data.get(),
1887 true));
1888 completed = ParseSheet(utf8, holder, AllowAsyncParse::No);
1889 if (completed == Completed::Yes) {
1890 if (isWorthCaching) {
1891 mInlineSheets.InsertOrUpdate(aBuffer, std::move(sheet));
1893 } else {
1894 data->mMustNotify = true;
1898 return LoadSheetResult{completed, isAlternate, matched};
1901 Result<Loader::LoadSheetResult, nsresult> Loader::LoadStyleLink(
1902 const SheetInfo& aInfo, nsICSSLoaderObserver* aObserver) {
1903 MOZ_ASSERT(aInfo.mURI, "Must have URL to load");
1904 LOG(("css::Loader::LoadStyleLink"));
1905 LOG_URI(" Link uri: '%s'", aInfo.mURI);
1906 LOG((" Link title: '%s'", NS_ConvertUTF16toUTF8(aInfo.mTitle).get()));
1907 LOG((" Link media: '%s'", NS_ConvertUTF16toUTF8(aInfo.mMedia).get()));
1908 LOG((" Link alternate rel: %d", aInfo.mHasAlternateRel));
1910 if (!mEnabled) {
1911 LOG_WARN((" Not enabled"));
1912 return Err(NS_ERROR_NOT_AVAILABLE);
1915 if (!mDocument) {
1916 return Err(NS_ERROR_NOT_INITIALIZED);
1919 MOZ_ASSERT_IF(aInfo.mContent,
1920 aInfo.mContent->NodePrincipal() == mDocument->NodePrincipal());
1921 nsIPrincipal* loadingPrincipal = LoaderPrincipal();
1922 nsIPrincipal* principal = aInfo.mTriggeringPrincipal
1923 ? aInfo.mTriggeringPrincipal.get()
1924 : loadingPrincipal;
1926 nsINode* requestingNode =
1927 aInfo.mContent ? static_cast<nsINode*>(aInfo.mContent) : mDocument;
1928 const bool syncLoad = [&] {
1929 if (!aInfo.mContent) {
1930 return false;
1932 const bool privilegedShadowTree =
1933 aInfo.mContent->IsInShadowTree() &&
1934 (aInfo.mContent->ChromeOnlyAccess() ||
1935 aInfo.mContent->OwnerDoc()->ChromeRulesEnabled());
1936 if (!privilegedShadowTree) {
1937 return false;
1939 if (!IsPrivilegedURI(aInfo.mURI)) {
1940 return false;
1942 // We're loading a chrome/resource URI in a chrome doc shadow tree or UA
1943 // widget. Load synchronously to avoid FOUC.
1944 return true;
1945 }();
1946 LOG((" Link sync load: '%s'", syncLoad ? "true" : "false"));
1947 MOZ_ASSERT_IF(syncLoad, !aObserver);
1949 nsresult rv =
1950 CheckContentPolicy(loadingPrincipal, principal, aInfo.mURI,
1951 requestingNode, aInfo.mNonce, StylePreloadKind::None);
1952 if (NS_WARN_IF(NS_FAILED(rv))) {
1953 // Don't fire the error event if our document is loaded as data. We're
1954 // supposed to not even try to do loads in that case... Unfortunately, we
1955 // implement that via nsDataDocumentContentPolicy, which doesn't have a good
1956 // way to communicate back to us that _it_ is the thing that blocked the
1957 // load.
1958 if (aInfo.mContent && !mDocument->IsLoadedAsData()) {
1959 // Fire an async error event on it.
1960 RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
1961 new LoadBlockingAsyncEventDispatcher(aInfo.mContent, u"error"_ns,
1962 CanBubble::eNo,
1963 ChromeOnlyDispatch::eNo);
1964 loadBlockingAsyncDispatcher->PostDOMEvent();
1966 return Err(rv);
1969 // Check IsAlternateSheet now, since it can mutate our document and make
1970 // pending sheets go to the non-pending state.
1971 auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
1972 auto [sheet, state] = CreateSheet(aInfo, eAuthorSheetFeatures, syncLoad,
1973 StylePreloadKind::None);
1975 LOG((" Sheet is alternate: %d", static_cast<int>(isAlternate)));
1977 auto matched = PrepareSheet(*sheet, aInfo.mTitle, aInfo.mMedia, nullptr,
1978 isAlternate, aInfo.mIsExplicitlyEnabled);
1980 if (auto* linkStyle = LinkStyle::FromNodeOrNull(aInfo.mContent)) {
1981 linkStyle->SetStyleSheet(sheet);
1983 MOZ_ASSERT(sheet->IsComplete() == (state == SheetState::Complete));
1985 // We may get here with no content for Link: headers for example.
1986 MOZ_ASSERT(!aInfo.mContent || LinkStyle::FromNode(*aInfo.mContent),
1987 "If there is any node, it should be a LinkStyle");
1988 auto data = MakeRefPtr<SheetLoadData>(
1989 this, aInfo.mTitle, aInfo.mURI, sheet, SyncLoad(syncLoad), aInfo.mContent,
1990 isAlternate, matched, StylePreloadKind::None, aObserver, principal,
1991 aInfo.mReferrerInfo, aInfo.mNonce, aInfo.mFetchPriority);
1993 MOZ_ASSERT(data->GetRequestingNode() == requestingNode);
1995 MaybeNotifyPreloadUsed(*data);
1997 if (state == SheetState::Complete) {
1998 LOG((" Sheet already complete: 0x%p", sheet.get()));
1999 MOZ_ASSERT(sheet->GetOwnerNode() == aInfo.mContent);
2000 InsertSheetInTree(*sheet);
2001 NotifyOfCachedLoad(std::move(data));
2002 return LoadSheetResult{Completed::Yes, isAlternate, matched};
2005 // Now we need to actually load it.
2006 auto result = LoadSheetResult{Completed::No, isAlternate, matched};
2008 MOZ_ASSERT(result.ShouldBlock() == !data->ShouldDefer(),
2009 "These should better match!");
2011 // Load completion will free the data
2012 rv = LoadSheet(*data, state, 0);
2013 if (NS_FAILED(rv)) {
2014 return Err(rv);
2017 if (!syncLoad) {
2018 data->mMustNotify = true;
2020 return result;
2023 static bool HaveAncestorDataWithURI(SheetLoadData& aData, nsIURI* aURI) {
2024 if (!aData.mURI) {
2025 // Inline style; this won't have any ancestors
2026 MOZ_ASSERT(!aData.mParentData, "How does inline style have a parent?");
2027 return false;
2030 bool equal;
2031 if (NS_FAILED(aData.mURI->Equals(aURI, &equal)) || equal) {
2032 return true;
2035 // Datas down the mNext chain have the same URI as aData, so we
2036 // don't have to compare to them. But they might have different
2037 // parents, and we have to check all of those.
2038 SheetLoadData* data = &aData;
2039 do {
2040 if (data->mParentData &&
2041 HaveAncestorDataWithURI(*data->mParentData, aURI)) {
2042 return true;
2045 data = data->mNext;
2046 } while (data);
2048 return false;
2051 nsresult Loader::LoadChildSheet(StyleSheet& aParentSheet,
2052 SheetLoadData* aParentData, nsIURI* aURL,
2053 dom::MediaList* aMedia,
2054 LoaderReusableStyleSheets* aReusableSheets) {
2055 LOG(("css::Loader::LoadChildSheet"));
2056 MOZ_ASSERT(aURL, "Must have a URI to load");
2058 if (!mEnabled) {
2059 LOG_WARN((" Not enabled"));
2060 return NS_ERROR_NOT_AVAILABLE;
2063 LOG_URI(" Child uri: '%s'", aURL);
2065 nsCOMPtr<nsINode> owningNode;
2067 nsINode* requestingNode = aParentSheet.GetOwnerNodeOfOutermostSheet();
2068 if (requestingNode) {
2069 MOZ_ASSERT(LoaderPrincipal() == requestingNode->NodePrincipal());
2070 } else {
2071 requestingNode = mDocument;
2074 nsIPrincipal* principal = aParentSheet.Principal();
2075 nsresult rv =
2076 CheckContentPolicy(LoaderPrincipal(), principal, aURL, requestingNode,
2077 /* aNonce = */ u""_ns, StylePreloadKind::None);
2078 if (NS_WARN_IF(NS_FAILED(rv))) {
2079 if (aParentData) {
2080 MarkLoadTreeFailed(*aParentData);
2082 return rv;
2085 nsCOMPtr<nsICSSLoaderObserver> observer;
2087 if (aParentData) {
2088 LOG((" Have a parent load"));
2089 // Check for cycles
2090 if (HaveAncestorDataWithURI(*aParentData, aURL)) {
2091 // Houston, we have a loop, blow off this child and pretend this never
2092 // happened
2093 LOG_ERROR((" @import cycle detected, dropping load"));
2094 return NS_OK;
2097 NS_ASSERTION(aParentData->mSheet == &aParentSheet,
2098 "Unexpected call to LoadChildSheet");
2099 } else {
2100 LOG((" No parent load; must be CSSOM"));
2101 // No parent load data, so the sheet will need to be notified when
2102 // we finish, if it can be, if we do the load asynchronously.
2103 observer = &aParentSheet;
2106 // Now that we know it's safe to load this (passes security check and not a
2107 // loop) do so.
2108 RefPtr<StyleSheet> sheet;
2109 SheetState state;
2110 if (aReusableSheets && aReusableSheets->FindReusableStyleSheet(aURL, sheet)) {
2111 state = SheetState::Complete;
2112 } else {
2113 // For now, use CORS_NONE for child sheets
2114 std::tie(sheet, state) = CreateSheet(
2115 aURL, nullptr, principal, aParentSheet.ParsingMode(), CORS_NONE,
2116 aParentData ? aParentData->mEncoding : nullptr,
2117 u""_ns, // integrity is only checked on main sheet
2118 aParentData && aParentData->mSyncLoad, StylePreloadKind::None);
2119 PrepareSheet(*sheet, u""_ns, u""_ns, aMedia, IsAlternate::No,
2120 IsExplicitlyEnabled::No);
2123 MOZ_ASSERT(sheet);
2124 InsertChildSheet(*sheet, aParentSheet);
2126 auto data =
2127 MakeRefPtr<SheetLoadData>(this, aURL, sheet, aParentData, observer,
2128 principal, aParentSheet.GetReferrerInfo());
2129 MOZ_ASSERT(data->GetRequestingNode() == requestingNode);
2131 MaybeNotifyPreloadUsed(*data);
2133 if (state == SheetState::Complete) {
2134 LOG((" Sheet already complete"));
2135 // We're completely done. No need to notify, even, since the
2136 // @import rule addition/modification will trigger the right style
2137 // changes automatically.
2138 data->mIntentionallyDropped = true;
2139 return NS_OK;
2142 bool syncLoad = data->mSyncLoad;
2144 // Load completion will release the data
2145 rv = LoadSheet(*data, state, 0);
2146 NS_ENSURE_SUCCESS(rv, rv);
2148 if (!syncLoad) {
2149 data->mMustNotify = true;
2151 return rv;
2154 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheetSync(
2155 nsIURI* aURL, SheetParsingMode aParsingMode,
2156 UseSystemPrincipal aUseSystemPrincipal) {
2157 LOG(("css::Loader::LoadSheetSync"));
2158 nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(nullptr);
2159 return InternalLoadNonDocumentSheet(
2160 aURL, StylePreloadKind::None, aParsingMode, aUseSystemPrincipal, nullptr,
2161 referrerInfo, nullptr, CORS_NONE, u""_ns, u""_ns, 0, FetchPriority::Auto);
2164 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheet(
2165 nsIURI* aURI, SheetParsingMode aParsingMode,
2166 UseSystemPrincipal aUseSystemPrincipal, nsICSSLoaderObserver* aObserver) {
2167 nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(nullptr);
2168 return InternalLoadNonDocumentSheet(
2169 aURI, StylePreloadKind::None, aParsingMode, aUseSystemPrincipal, nullptr,
2170 referrerInfo, aObserver, CORS_NONE, u""_ns, u""_ns, 0,
2171 FetchPriority::Auto);
2174 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheet(
2175 nsIURI* aURL, StylePreloadKind aPreloadKind,
2176 const Encoding* aPreloadEncoding, nsIReferrerInfo* aReferrerInfo,
2177 nsICSSLoaderObserver* aObserver, uint64_t aEarlyHintPreloaderId,
2178 CORSMode aCORSMode, const nsAString& aNonce, const nsAString& aIntegrity,
2179 FetchPriority aFetchPriority) {
2180 LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
2181 return InternalLoadNonDocumentSheet(
2182 aURL, aPreloadKind, eAuthorSheetFeatures, UseSystemPrincipal::No,
2183 aPreloadEncoding, aReferrerInfo, aObserver, aCORSMode, aNonce, aIntegrity,
2184 aEarlyHintPreloaderId, aFetchPriority);
2187 Result<RefPtr<StyleSheet>, nsresult> Loader::InternalLoadNonDocumentSheet(
2188 nsIURI* aURL, StylePreloadKind aPreloadKind, SheetParsingMode aParsingMode,
2189 UseSystemPrincipal aUseSystemPrincipal, const Encoding* aPreloadEncoding,
2190 nsIReferrerInfo* aReferrerInfo, nsICSSLoaderObserver* aObserver,
2191 CORSMode aCORSMode, const nsAString& aNonce, const nsAString& aIntegrity,
2192 uint64_t aEarlyHintPreloaderId, FetchPriority aFetchPriority) {
2193 MOZ_ASSERT(aURL, "Must have a URI to load");
2194 MOZ_ASSERT(aUseSystemPrincipal == UseSystemPrincipal::No || !aObserver,
2195 "Shouldn't load system-principal sheets async");
2196 MOZ_ASSERT(aReferrerInfo, "Must have referrerInfo");
2198 LOG_URI(" Non-document sheet uri: '%s'", aURL);
2200 if (!mEnabled) {
2201 LOG_WARN((" Not enabled"));
2202 return Err(NS_ERROR_NOT_AVAILABLE);
2205 nsIPrincipal* loadingPrincipal = LoaderPrincipal();
2206 nsIPrincipal* triggeringPrincipal = loadingPrincipal;
2207 nsresult rv = CheckContentPolicy(loadingPrincipal, triggeringPrincipal, aURL,
2208 mDocument, aNonce, aPreloadKind);
2209 if (NS_FAILED(rv)) {
2210 return Err(rv);
2213 bool syncLoad = !aObserver;
2214 auto [sheet, state] =
2215 CreateSheet(aURL, nullptr, triggeringPrincipal, aParsingMode, aCORSMode,
2216 aPreloadEncoding, aIntegrity, syncLoad, aPreloadKind);
2218 PrepareSheet(*sheet, u""_ns, u""_ns, nullptr, IsAlternate::No,
2219 IsExplicitlyEnabled::No);
2221 auto data = MakeRefPtr<SheetLoadData>(
2222 this, aURL, sheet, SyncLoad(syncLoad), aUseSystemPrincipal, aPreloadKind,
2223 aPreloadEncoding, aObserver, triggeringPrincipal, aReferrerInfo, aNonce,
2224 aFetchPriority);
2225 MOZ_ASSERT(data->GetRequestingNode() == mDocument);
2226 if (state == SheetState::Complete) {
2227 LOG((" Sheet already complete"));
2228 NotifyOfCachedLoad(std::move(data));
2229 return sheet;
2232 rv = LoadSheet(*data, state, aEarlyHintPreloaderId);
2233 if (NS_FAILED(rv)) {
2234 return Err(rv);
2236 if (aObserver) {
2237 data->mMustNotify = true;
2239 return sheet;
2242 void Loader::NotifyOfCachedLoad(RefPtr<SheetLoadData> aLoadData) {
2243 LOG(("css::Loader::PostLoadEvent"));
2244 MOZ_ASSERT(aLoadData->mSheet->IsComplete(),
2245 "Only expected to be used for cached sheets");
2246 // If we get to this code, the stylesheet loaded correctly at some point, so
2247 // we can just schedule a load event and don't need to touch the data's
2248 // mLoadFailed.
2249 // Note that we do this here and not from inside our SheetComplete so that we
2250 // don't end up running the load event more async than needed.
2251 MOZ_ASSERT(!aLoadData->mLoadFailed, "Why are we marked as failed?");
2252 aLoadData->mSheetAlreadyComplete = true;
2254 // We need to check mURI to match
2255 // DecrementOngoingLoadCountAndMaybeUnblockOnload().
2256 if (aLoadData->mURI && aLoadData->BlocksLoadEvent()) {
2257 IncrementOngoingLoadCountAndMaybeBlockOnload();
2259 SheetComplete(*aLoadData, NS_OK);
2262 void Loader::Stop() {
2263 if (mSheets) {
2264 mSheets->CancelLoadsForLoader(*this);
2268 bool Loader::HasPendingLoads() { return mOngoingLoadCount; }
2270 void Loader::AddObserver(nsICSSLoaderObserver* aObserver) {
2271 MOZ_ASSERT(aObserver, "Must have observer");
2272 mObservers.AppendElementUnlessExists(aObserver);
2275 void Loader::RemoveObserver(nsICSSLoaderObserver* aObserver) {
2276 mObservers.RemoveElement(aObserver);
2279 void Loader::StartDeferredLoads() {
2280 if (mSheets && mPendingLoadCount) {
2281 mSheets->StartPendingLoadsForLoader(
2282 *this, [](const SheetLoadData&) { return true; });
2286 NS_IMPL_CYCLE_COLLECTION_CLASS(Loader)
2288 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Loader)
2289 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheets);
2290 for (const auto& data : tmp->mInlineSheets.Values()) {
2291 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "Inline sheet cache in Loader");
2292 cb.NoteXPCOMChild(data);
2294 for (nsCOMPtr<nsICSSLoaderObserver>& obs : tmp->mObservers.ForwardRange()) {
2295 ImplCycleCollectionTraverse(cb, obs, "mozilla::css::Loader.mObservers");
2297 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
2298 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2300 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Loader)
2301 if (tmp->mSheets) {
2302 if (tmp->mDocument) {
2303 tmp->DeregisterFromSheetCache();
2305 tmp->mSheets = nullptr;
2307 tmp->mInlineSheets.Clear();
2308 tmp->mObservers.Clear();
2309 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocGroup)
2310 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2312 size_t Loader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
2313 size_t n = aMallocSizeOf(this);
2315 n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
2317 n += mInlineSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
2318 for (const auto& entry : mInlineSheets) {
2319 n += entry.GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2320 // If the sheet has a parent, then its parent will report it so we don't
2321 // have to worry about it here.
2322 const StyleSheet* sheet = entry.GetWeak();
2323 MOZ_ASSERT(!sheet->GetParentSheet(),
2324 "How did an @import rule end up here?");
2325 if (!sheet->GetOwnerNode()) {
2326 n += sheet->SizeOfIncludingThis(aMallocSizeOf);
2330 // Measurement of the following members may be added later if DMD finds it is
2331 // worthwhile:
2332 // The following members aren't measured:
2333 // - mDocument, because it's a weak backpointer
2335 return n;
2338 nsIPrincipal* Loader::LoaderPrincipal() const {
2339 if (mDocument) {
2340 return mDocument->NodePrincipal();
2342 // Loaders without a document do system loads.
2343 return nsContentUtils::GetSystemPrincipal();
2346 nsIPrincipal* Loader::PartitionedPrincipal() const {
2347 if (mDocument && StaticPrefs::privacy_partition_network_state()) {
2348 return mDocument->PartitionedPrincipal();
2350 return LoaderPrincipal();
2353 bool Loader::ShouldBypassCache() const {
2354 if (!mDocument) {
2355 return false;
2357 RefPtr<nsILoadGroup> lg = mDocument->GetDocumentLoadGroup();
2358 if (!lg) {
2359 return false;
2361 nsLoadFlags flags;
2362 if (NS_FAILED(lg->GetLoadFlags(&flags))) {
2363 return false;
2365 return flags & (nsIRequest::LOAD_BYPASS_CACHE |
2366 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE);
2369 void Loader::BlockOnload() {
2370 if (mDocument) {
2371 mDocument->BlockOnload();
2375 void Loader::UnblockOnload(bool aFireSync) {
2376 if (mDocument) {
2377 mDocument->UnblockOnload(aFireSync);
2381 } // namespace css
2382 } // namespace mozilla