Bug 1883861 - Part 1: Move visitMemoryBarrier into the common CodeGenerator file...
[gecko.git] / layout / style / Loader.cpp
blob14ff5d9bc56857fed46a86b88debc707b2a1a2e3
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 "mozilla/ArrayUtils.h"
12 #include "mozilla/dom/DocGroup.h"
13 #include "mozilla/dom/FetchPriority.h"
14 #include "mozilla/dom/SRILogHelper.h"
15 #include "mozilla/IntegerPrintfMacros.h"
16 #include "mozilla/AutoRestore.h"
17 #include "mozilla/LoadInfo.h"
18 #include "mozilla/Logging.h"
19 #include "mozilla/MemoryReporting.h"
20 #include "mozilla/PreloadHashKey.h"
21 #include "mozilla/ResultExtensions.h"
22 #include "mozilla/SchedulerGroup.h"
23 #include "mozilla/URLPreloader.h"
24 #include "nsIChildChannel.h"
25 #include "nsISupportsPriority.h"
26 #include "nsITimedChannel.h"
27 #include "nsICachingChannel.h"
28 #include "nsSyncLoadService.h"
29 #include "nsContentSecurityManager.h"
30 #include "nsCOMPtr.h"
31 #include "nsString.h"
32 #include "nsIContent.h"
33 #include "nsIContentInlines.h"
34 #include "nsICookieJarSettings.h"
35 #include "mozilla/dom/Document.h"
36 #include "nsIURI.h"
37 #include "nsNetUtil.h"
38 #include "nsContentUtils.h"
39 #include "nsIScriptSecurityManager.h"
40 #include "nsContentPolicyUtils.h"
41 #include "nsIHttpChannel.h"
42 #include "nsIHttpChannelInternal.h"
43 #include "nsIClassOfService.h"
44 #include "nsIScriptError.h"
45 #include "nsMimeTypes.h"
46 #include "nsICSSLoaderObserver.h"
47 #include "nsThreadUtils.h"
48 #include "nsGkAtoms.h"
49 #include "nsIThreadInternal.h"
50 #include "nsINetworkPredictor.h"
51 #include "nsQueryActor.h"
52 #include "nsStringStream.h"
53 #include "mozilla/dom/MediaList.h"
54 #include "mozilla/dom/ShadowRoot.h"
55 #include "mozilla/dom/URL.h"
56 #include "mozilla/net/UrlClassifierFeatureFactory.h"
57 #include "mozilla/AsyncEventDispatcher.h"
58 #include "mozilla/ProfilerLabels.h"
59 #include "mozilla/ServoBindings.h"
60 #include "mozilla/StyleSheet.h"
61 #include "mozilla/StyleSheetInlines.h"
62 #include "mozilla/ConsoleReportCollector.h"
63 #include "mozilla/ServoUtils.h"
64 #include "mozilla/css/StreamLoader.h"
65 #include "mozilla/SharedStyleSheetCache.h"
66 #include "mozilla/StaticPrefs_layout.h"
67 #include "mozilla/StaticPrefs_dom.h"
68 #include "mozilla/StaticPrefs_network.h"
69 #include "mozilla/Try.h"
70 #include "ReferrerInfo.h"
72 #include "nsXULPrototypeCache.h"
74 #include "nsError.h"
76 #include "mozilla/dom/SRICheck.h"
78 #include "mozilla/Encoding.h"
80 using namespace mozilla::dom;
82 // 1024 bytes is specified in https://drafts.csswg.org/css-syntax/
83 #define SNIFFING_BUFFER_SIZE 1024
85 /**
86 * OVERALL ARCHITECTURE
88 * The CSS Loader gets requests to load various sorts of style sheets:
89 * inline style from <style> elements, linked style, @import-ed child
90 * sheets, non-document sheets. The loader handles the following tasks:
91 * 1) Creation of the actual style sheet objects: CreateSheet()
92 * 2) setting of the right media, title, enabled state, etc on the
93 * sheet: PrepareSheet()
94 * 3) Insertion of the sheet in the proper cascade order:
95 * InsertSheetInTree() and InsertChildSheet()
96 * 4) Load of the sheet: LoadSheet() including security checks
97 * 5) Parsing of the sheet: ParseSheet()
98 * 6) Cleanup: SheetComplete()
100 * The detailed documentation for these functions is found with the
101 * function implementations.
103 * The following helper object is used:
104 * SheetLoadData -- a small class that is used to store all the
105 * information needed for the loading of a sheet;
106 * this class handles listening for the stream
107 * loader completion and also handles charset
108 * determination.
111 extern mozilla::LazyLogModule sCssLoaderLog;
112 mozilla::LazyLogModule sCssLoaderLog("nsCSSLoader");
114 static mozilla::LazyLogModule gSriPRLog("SRI");
116 static bool IsPrivilegedURI(nsIURI* aURI) {
117 return aURI->SchemeIs("chrome") || aURI->SchemeIs("resource");
120 #define LOG_ERROR(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Error, args)
121 #define LOG_WARN(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Warning, args)
122 #define LOG_DEBUG(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Debug, args)
123 #define LOG(args) LOG_DEBUG(args)
125 #define LOG_ERROR_ENABLED() \
126 MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Error)
127 #define LOG_WARN_ENABLED() \
128 MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Warning)
129 #define LOG_DEBUG_ENABLED() \
130 MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Debug)
131 #define LOG_ENABLED() LOG_DEBUG_ENABLED()
133 #define LOG_URI(format, uri) \
134 PR_BEGIN_MACRO \
135 NS_ASSERTION(uri, "Logging null uri"); \
136 if (LOG_ENABLED()) { \
137 LOG((format, uri->GetSpecOrDefault().get())); \
139 PR_END_MACRO
141 // And some convenience strings...
142 static const char* const gStateStrings[] = {"NeedsParser", "Pending", "Loading",
143 "Complete"};
145 namespace mozilla {
147 SheetLoadDataHashKey::SheetLoadDataHashKey(const css::SheetLoadData& aLoadData)
148 : mURI(aLoadData.mURI),
149 mPrincipal(aLoadData.mTriggeringPrincipal),
150 mLoaderPrincipal(aLoadData.mLoader->LoaderPrincipal()),
151 mPartitionPrincipal(aLoadData.mLoader->PartitionedPrincipal()),
152 mEncodingGuess(aLoadData.mGuessedEncoding),
153 mCORSMode(aLoadData.mSheet->GetCORSMode()),
154 mParsingMode(aLoadData.mSheet->ParsingMode()),
155 mCompatMode(aLoadData.mCompatMode),
156 mIsLinkRelPreload(aLoadData.IsLinkRelPreload()) {
157 MOZ_COUNT_CTOR(SheetLoadDataHashKey);
158 MOZ_ASSERT(mURI);
159 MOZ_ASSERT(mPrincipal);
160 MOZ_ASSERT(mLoaderPrincipal);
161 MOZ_ASSERT(mPartitionPrincipal);
162 aLoadData.mSheet->GetIntegrity(mSRIMetadata);
165 bool SheetLoadDataHashKey::KeyEquals(const SheetLoadDataHashKey& aKey) const {
167 bool eq;
168 if (NS_FAILED(mURI->Equals(aKey.mURI, &eq)) || !eq) {
169 return false;
173 LOG_URI("KeyEquals(%s)\n", mURI);
175 if (mParsingMode != aKey.mParsingMode) {
176 LOG((" > Parsing mode mismatch\n"));
177 return false;
180 // Chrome URIs ignore everything else.
181 if (IsPrivilegedURI(mURI)) {
182 return true;
185 if (!mPrincipal->Equals(aKey.mPrincipal)) {
186 LOG((" > Principal mismatch\n"));
187 return false;
190 // We only check for partition principal equality if any of the loads are
191 // triggered by a document rather than e.g. an extension (which have different
192 // origins than the loader principal).
193 if (mPrincipal->Equals(mLoaderPrincipal) ||
194 aKey.mPrincipal->Equals(aKey.mLoaderPrincipal)) {
195 if (!mPartitionPrincipal->Equals(aKey.mPartitionPrincipal)) {
196 LOG((" > Partition principal mismatch\n"));
197 return false;
201 if (mCORSMode != aKey.mCORSMode) {
202 LOG((" > CORS mismatch\n"));
203 return false;
206 if (mCompatMode != aKey.mCompatMode) {
207 LOG((" > Quirks mismatch\n"));
208 return false;
211 // If encoding differs, then don't reuse the cache.
213 // TODO(emilio): When the encoding is determined from the request (either
214 // BOM or Content-Length or @charset), we could do a bit better,
215 // theoretically.
216 if (mEncodingGuess != aKey.mEncodingGuess) {
217 LOG((" > Encoding guess mismatch\n"));
218 return false;
221 // Consuming stylesheet tags must never coalesce to <link preload> initiated
222 // speculative loads with a weaker SRI hash or its different value. This
223 // check makes sure that regular loads will never find such a weaker preload
224 // and rather start a new, independent load with new, stronger SRI checker
225 // set up, so that integrity is ensured.
226 if (mIsLinkRelPreload != aKey.mIsLinkRelPreload) {
227 const auto& linkPreloadMetadata =
228 mIsLinkRelPreload ? mSRIMetadata : aKey.mSRIMetadata;
229 const auto& consumerPreloadMetadata =
230 mIsLinkRelPreload ? aKey.mSRIMetadata : mSRIMetadata;
232 if (!consumerPreloadMetadata.CanTrustBeDelegatedTo(linkPreloadMetadata)) {
233 LOG((" > Preload SRI metadata mismatch\n"));
234 return false;
238 return true;
241 namespace css {
243 static NotNull<const Encoding*> GetFallbackEncoding(
244 Loader& aLoader, nsINode* aOwningNode,
245 const Encoding* aPreloadOrParentDataEncoding) {
246 const Encoding* encoding;
247 // Now try the charset on the <link> or processing instruction
248 // that loaded us
249 if (aOwningNode) {
250 nsAutoString label16;
251 LinkStyle::FromNode(*aOwningNode)->GetCharset(label16);
252 encoding = Encoding::ForLabel(label16);
253 if (encoding) {
254 return WrapNotNull(encoding);
258 // Try preload or parent sheet encoding.
259 if (aPreloadOrParentDataEncoding) {
260 return WrapNotNull(aPreloadOrParentDataEncoding);
263 if (auto* doc = aLoader.GetDocument()) {
264 // Use the document charset.
265 return doc->GetDocumentCharacterSet();
268 return UTF_8_ENCODING;
271 /********************************
272 * SheetLoadData implementation *
273 ********************************/
274 NS_IMPL_ISUPPORTS(SheetLoadData, nsISupports)
276 SheetLoadData::SheetLoadData(
277 css::Loader* aLoader, const nsAString& aTitle, nsIURI* aURI,
278 StyleSheet* aSheet, SyncLoad aSyncLoad, nsINode* aOwningNode,
279 IsAlternate aIsAlternate, MediaMatched aMediaMatches,
280 StylePreloadKind aPreloadKind, nsICSSLoaderObserver* aObserver,
281 nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
282 const nsAString& aNonce, FetchPriority aFetchPriority)
283 : mLoader(aLoader),
284 mTitle(aTitle),
285 mEncoding(nullptr),
286 mURI(aURI),
287 mSheet(aSheet),
288 mPendingChildren(0),
289 mSyncLoad(aSyncLoad == SyncLoad::Yes),
290 mIsNonDocumentSheet(false),
291 mIsChildSheet(aSheet->GetParentSheet()),
292 mIsBeingParsed(false),
293 mIsLoading(false),
294 mIsCancelled(false),
295 mMustNotify(false),
296 mHadOwnerNode(!!aOwningNode),
297 mWasAlternate(aIsAlternate == IsAlternate::Yes),
298 mMediaMatched(aMediaMatches == MediaMatched::Yes),
299 mUseSystemPrincipal(false),
300 mSheetAlreadyComplete(false),
301 mIsCrossOriginNoCORS(false),
302 mBlockResourceTiming(false),
303 mLoadFailed(false),
304 mPreloadKind(aPreloadKind),
305 mObserver(aObserver),
306 mTriggeringPrincipal(aTriggeringPrincipal),
307 mReferrerInfo(aReferrerInfo),
308 mNonce(aNonce),
309 mFetchPriority{aFetchPriority},
310 mGuessedEncoding(GetFallbackEncoding(*aLoader, aOwningNode, nullptr)),
311 mCompatMode(aLoader->CompatMode(aPreloadKind)) {
312 MOZ_ASSERT(!aOwningNode || dom::LinkStyle::FromNode(*aOwningNode),
313 "Must implement LinkStyle");
314 MOZ_ASSERT(mTriggeringPrincipal);
315 MOZ_ASSERT(mLoader, "Must have a loader!");
318 SheetLoadData::SheetLoadData(css::Loader* aLoader, nsIURI* aURI,
319 StyleSheet* aSheet, SheetLoadData* aParentData,
320 nsICSSLoaderObserver* aObserver,
321 nsIPrincipal* aTriggeringPrincipal,
322 nsIReferrerInfo* aReferrerInfo)
323 : mLoader(aLoader),
324 mEncoding(nullptr),
325 mURI(aURI),
326 mSheet(aSheet),
327 mParentData(aParentData),
328 mPendingChildren(0),
329 mSyncLoad(aParentData && aParentData->mSyncLoad),
330 mIsNonDocumentSheet(aParentData && aParentData->mIsNonDocumentSheet),
331 mIsChildSheet(aSheet->GetParentSheet()),
332 mIsBeingParsed(false),
333 mIsLoading(false),
334 mIsCancelled(false),
335 mMustNotify(false),
336 mHadOwnerNode(false),
337 mWasAlternate(false),
338 mMediaMatched(true),
339 mUseSystemPrincipal(aParentData && aParentData->mUseSystemPrincipal),
340 mSheetAlreadyComplete(false),
341 mIsCrossOriginNoCORS(false),
342 mBlockResourceTiming(false),
343 mLoadFailed(false),
344 mPreloadKind(StylePreloadKind::None),
345 mObserver(aObserver),
346 mTriggeringPrincipal(aTriggeringPrincipal),
347 mReferrerInfo(aReferrerInfo),
348 mNonce(u""_ns),
349 mFetchPriority(FetchPriority::Auto),
350 mGuessedEncoding(GetFallbackEncoding(
351 *aLoader, nullptr, aParentData ? aParentData->mEncoding : nullptr)),
352 mCompatMode(aLoader->CompatMode(mPreloadKind)) {
353 MOZ_ASSERT(mLoader, "Must have a loader!");
354 MOZ_ASSERT(mTriggeringPrincipal);
355 MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
356 "Shouldn't use system principal for async loads");
357 MOZ_ASSERT_IF(aParentData, mIsChildSheet);
360 SheetLoadData::SheetLoadData(
361 css::Loader* aLoader, nsIURI* aURI, StyleSheet* aSheet, SyncLoad aSyncLoad,
362 UseSystemPrincipal aUseSystemPrincipal, StylePreloadKind aPreloadKind,
363 const Encoding* aPreloadEncoding, nsICSSLoaderObserver* aObserver,
364 nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
365 const nsAString& aNonce, FetchPriority aFetchPriority)
366 : mLoader(aLoader),
367 mEncoding(nullptr),
368 mURI(aURI),
369 mSheet(aSheet),
370 mPendingChildren(0),
371 mSyncLoad(aSyncLoad == SyncLoad::Yes),
372 mIsNonDocumentSheet(true),
373 mIsChildSheet(false),
374 mIsBeingParsed(false),
375 mIsLoading(false),
376 mIsCancelled(false),
377 mMustNotify(false),
378 mHadOwnerNode(false),
379 mWasAlternate(false),
380 mMediaMatched(true),
381 mUseSystemPrincipal(aUseSystemPrincipal == UseSystemPrincipal::Yes),
382 mSheetAlreadyComplete(false),
383 mIsCrossOriginNoCORS(false),
384 mBlockResourceTiming(false),
385 mLoadFailed(false),
386 mPreloadKind(aPreloadKind),
387 mObserver(aObserver),
388 mTriggeringPrincipal(aTriggeringPrincipal),
389 mReferrerInfo(aReferrerInfo),
390 mNonce(aNonce),
391 mFetchPriority(aFetchPriority),
392 mGuessedEncoding(
393 GetFallbackEncoding(*aLoader, nullptr, aPreloadEncoding)),
394 mCompatMode(aLoader->CompatMode(aPreloadKind)) {
395 MOZ_ASSERT(mTriggeringPrincipal);
396 MOZ_ASSERT(mLoader, "Must have a loader!");
397 MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
398 "Shouldn't use system principal for async loads");
399 MOZ_ASSERT(!aSheet->GetParentSheet(), "Shouldn't be used for child loads");
402 SheetLoadData::~SheetLoadData() {
403 MOZ_RELEASE_ASSERT(mSheetCompleteCalled || mIntentionallyDropped,
404 "Should always call SheetComplete, except when "
405 "dropping the load");
408 RefPtr<StyleSheet> SheetLoadData::ValueForCache() const {
409 // We need to clone the sheet on insertion to the cache because otherwise the
410 // stylesheets can keep full windows alive via either their JS wrapper, or via
411 // StyleSheet::mRelevantGlobal.
413 // If this ever changes, then you also need to fix up the memory reporting in
414 // both SizeOfIncludingThis and nsXULPrototypeCache::CollectMemoryReports.
415 return mSheet->Clone(nullptr, nullptr);
418 void SheetLoadData::PrioritizeAsPreload(nsIChannel* aChannel) {
419 if (nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(aChannel)) {
420 sp->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
424 void SheetLoadData::StartPendingLoad() {
425 mLoader->LoadSheet(*this, Loader::SheetState::NeedsParser, 0,
426 Loader::PendingLoad::Yes);
429 already_AddRefed<AsyncEventDispatcher>
430 SheetLoadData::PrepareLoadEventIfNeeded() {
431 nsCOMPtr<nsINode> node = mSheet->GetOwnerNode();
432 if (!node) {
433 return nullptr;
435 MOZ_ASSERT(!RootLoadData().IsLinkRelPreload(),
436 "rel=preload handled elsewhere");
437 RefPtr<AsyncEventDispatcher> dispatcher;
438 if (BlocksLoadEvent()) {
439 dispatcher = new LoadBlockingAsyncEventDispatcher(
440 node, mLoadFailed ? u"error"_ns : u"load"_ns, CanBubble::eNo,
441 ChromeOnlyDispatch::eNo);
442 } else {
443 // Fire the load event on the link, but don't block the document load.
444 dispatcher =
445 new AsyncEventDispatcher(node, mLoadFailed ? u"error"_ns : u"load"_ns,
446 CanBubble::eNo, ChromeOnlyDispatch::eNo);
448 return dispatcher.forget();
451 nsINode* SheetLoadData::GetRequestingNode() const {
452 if (nsINode* node = mSheet->GetOwnerNodeOfOutermostSheet()) {
453 return node;
455 return mLoader->GetDocument();
458 /*********************
459 * Style sheet reuse *
460 *********************/
462 bool LoaderReusableStyleSheets::FindReusableStyleSheet(
463 nsIURI* aURL, RefPtr<StyleSheet>& aResult) {
464 MOZ_ASSERT(aURL);
465 for (size_t i = mReusableSheets.Length(); i > 0; --i) {
466 size_t index = i - 1;
467 bool sameURI;
468 MOZ_ASSERT(mReusableSheets[index]->GetOriginalURI());
469 nsresult rv =
470 aURL->Equals(mReusableSheets[index]->GetOriginalURI(), &sameURI);
471 if (!NS_FAILED(rv) && sameURI) {
472 aResult = mReusableSheets[index];
473 mReusableSheets.RemoveElementAt(index);
474 return true;
477 return false;
479 /*************************
480 * Loader Implementation *
481 *************************/
483 Loader::Loader()
484 : mDocument(nullptr),
485 mDocumentCompatMode(eCompatibility_FullStandards),
486 mReporter(new ConsoleReportCollector()) {}
488 Loader::Loader(DocGroup* aDocGroup) : Loader() { mDocGroup = aDocGroup; }
490 Loader::Loader(Document* aDocument) : Loader() {
491 MOZ_ASSERT(aDocument, "We should get a valid document from the caller!");
492 mDocument = aDocument;
493 mIsDocumentAssociated = true;
494 mDocumentCompatMode = aDocument->GetCompatibilityMode();
495 mSheets = SharedStyleSheetCache::Get();
496 RegisterInSheetCache();
499 // Note: no real need to revoke our stylesheet loaded events -- they hold strong
500 // references to us, so if we're going away that means they're all done.
501 Loader::~Loader() = default;
503 void Loader::RegisterInSheetCache() {
504 MOZ_ASSERT(mDocument);
505 MOZ_ASSERT(mSheets);
507 mSheets->RegisterLoader(*this);
510 void Loader::DeregisterFromSheetCache() {
511 MOZ_ASSERT(mDocument);
512 MOZ_ASSERT(mSheets);
514 mSheets->CancelLoadsForLoader(*this);
515 mSheets->UnregisterLoader(*this);
518 void Loader::DropDocumentReference() {
519 // Flush out pending datas just so we don't leak by accident.
520 if (mSheets) {
521 DeregisterFromSheetCache();
523 mDocument = nullptr;
526 void Loader::DocumentStyleSheetSetChanged() {
527 MOZ_ASSERT(mDocument);
529 // start any pending alternates that aren't alternates anymore
530 mSheets->StartPendingLoadsForLoader(*this, [&](const SheetLoadData& aData) {
531 return IsAlternateSheet(aData.mTitle, true) != IsAlternate::Yes;
535 static const char kCharsetSym[] = "@charset \"";
537 static bool GetCharsetFromData(const char* aStyleSheetData,
538 uint32_t aDataLength, nsACString& aCharset) {
539 aCharset.Truncate();
540 if (aDataLength <= sizeof(kCharsetSym) - 1) return false;
542 if (strncmp(aStyleSheetData, kCharsetSym, sizeof(kCharsetSym) - 1)) {
543 return false;
546 for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
547 char c = aStyleSheetData[i];
548 if (c == '"') {
549 ++i;
550 if (i < aDataLength && aStyleSheetData[i] == ';') {
551 return true;
553 // fail
554 break;
556 aCharset.Append(c);
559 // Did not see end quote or semicolon
560 aCharset.Truncate();
561 return false;
564 NotNull<const Encoding*> SheetLoadData::DetermineNonBOMEncoding(
565 const nsACString& aSegment, nsIChannel* aChannel) const {
566 const Encoding* encoding;
567 nsAutoCString label;
569 // Check HTTP
570 if (aChannel && NS_SUCCEEDED(aChannel->GetContentCharset(label))) {
571 encoding = Encoding::ForLabel(label);
572 if (encoding) {
573 return WrapNotNull(encoding);
577 // Check @charset
578 auto sniffingLength = aSegment.Length();
579 if (sniffingLength > SNIFFING_BUFFER_SIZE) {
580 sniffingLength = SNIFFING_BUFFER_SIZE;
582 if (GetCharsetFromData(aSegment.BeginReading(), sniffingLength, label)) {
583 encoding = Encoding::ForLabel(label);
584 if (encoding == UTF_16BE_ENCODING || encoding == UTF_16LE_ENCODING) {
585 return UTF_8_ENCODING;
587 if (encoding) {
588 return WrapNotNull(encoding);
591 return mGuessedEncoding;
594 static nsresult VerifySheetIntegrity(const SRIMetadata& aMetadata,
595 nsIChannel* aChannel,
596 const nsACString& aFirst,
597 const nsACString& aSecond,
598 const nsACString& aSourceFileURI,
599 nsIConsoleReportCollector* aReporter) {
600 NS_ENSURE_ARG_POINTER(aReporter);
602 if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), LogLevel::Debug)) {
603 nsAutoCString requestURL;
604 nsCOMPtr<nsIURI> originalURI;
605 if (aChannel &&
606 NS_SUCCEEDED(aChannel->GetOriginalURI(getter_AddRefs(originalURI))) &&
607 originalURI) {
608 originalURI->GetAsciiSpec(requestURL);
610 MOZ_LOG(SRILogHelper::GetSriLog(), LogLevel::Debug,
611 ("VerifySheetIntegrity (unichar stream)"));
614 SRICheckDataVerifier verifier(aMetadata, aSourceFileURI, aReporter);
615 nsresult rv =
616 verifier.Update(aFirst.Length(), (const uint8_t*)aFirst.BeginReading());
617 NS_ENSURE_SUCCESS(rv, rv);
618 rv =
619 verifier.Update(aSecond.Length(), (const uint8_t*)aSecond.BeginReading());
620 NS_ENSURE_SUCCESS(rv, rv);
622 return verifier.Verify(aMetadata, aChannel, aSourceFileURI, aReporter);
625 static bool AllLoadsCanceled(const SheetLoadData& aData) {
626 const SheetLoadData* data = &aData;
627 do {
628 if (!data->IsCancelled()) {
629 return false;
631 } while ((data = data->mNext));
632 return true;
636 * Stream completion code shared by Stylo and the old style system.
638 * Here we need to check that the load did not give us an http error
639 * page and check the mimetype on the channel to make sure we're not
640 * loading non-text/css data in standards mode.
642 nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
643 const nsACString& aBytes1,
644 const nsACString& aBytes2,
645 nsIChannel* aChannel) {
646 LOG(("SheetLoadData::VerifySheetReadyToParse"));
647 NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
649 if (AllLoadsCanceled(*this)) {
650 LOG_WARN((" All loads are canceled, dropping"));
651 mLoader->SheetComplete(*this, NS_BINDING_ABORTED);
652 return NS_OK;
655 if (NS_FAILED(aStatus)) {
656 LOG_WARN(
657 (" Load failed: status 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
658 // Handle sheet not loading error because source was a tracking URL (or
659 // fingerprinting, cryptomining, etc).
660 // We make a note of this sheet node by including it in a dedicated
661 // array of blocked tracking nodes under its parent document.
663 // Multiple sheet load instances might be tied to this request,
664 // we annotate each one linked to a valid owning element (node).
665 if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
666 aStatus)) {
667 if (Document* doc = mLoader->GetDocument()) {
668 for (SheetLoadData* data = this; data; data = data->mNext) {
669 // owner node may be null but AddBlockTrackingNode can cope
670 doc->AddBlockedNodeByClassifier(data->mSheet->GetOwnerNode());
674 mLoader->SheetComplete(*this, aStatus);
675 return NS_OK;
678 if (!aChannel) {
679 mLoader->SheetComplete(*this, NS_OK);
680 return NS_OK;
683 nsCOMPtr<nsIURI> originalURI;
684 aChannel->GetOriginalURI(getter_AddRefs(originalURI));
686 // If the channel's original URI is "chrome:", we want that, since
687 // the observer code in nsXULPrototypeCache depends on chrome stylesheets
688 // having a chrome URI. (Whether or not chrome stylesheets come through
689 // this codepath seems nondeterministic.)
690 // Otherwise we want the potentially-HTTP-redirected URI.
691 nsCOMPtr<nsIURI> channelURI;
692 NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
694 if (!channelURI || !originalURI) {
695 NS_ERROR("Someone just violated the nsIRequest contract");
696 LOG_WARN((" Channel without a URI. Bad!"));
697 mLoader->SheetComplete(*this, NS_ERROR_UNEXPECTED);
698 return NS_OK;
701 nsCOMPtr<nsIPrincipal> principal;
702 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
703 nsresult result = NS_ERROR_NOT_AVAILABLE;
704 if (secMan) { // Could be null if we already shut down
705 if (mUseSystemPrincipal) {
706 result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
707 } else {
708 result = secMan->GetChannelResultPrincipal(aChannel,
709 getter_AddRefs(principal));
713 if (NS_FAILED(result)) {
714 LOG_WARN((" Couldn't get principal"));
715 mLoader->SheetComplete(*this, result);
716 return NS_OK;
719 mSheet->SetPrincipal(principal);
721 if (mSheet->GetCORSMode() == CORS_NONE &&
722 !mTriggeringPrincipal->Subsumes(principal)) {
723 mIsCrossOriginNoCORS = true;
726 // If it's an HTTP channel, we want to make sure this is not an
727 // error document we got.
728 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
729 bool requestSucceeded;
730 result = httpChannel->GetRequestSucceeded(&requestSucceeded);
731 if (NS_SUCCEEDED(result) && !requestSucceeded) {
732 LOG((" Load returned an error page"));
733 mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
734 return NS_OK;
737 nsCString sourceMapURL;
738 if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
739 mSheet->SetSourceMapURL(std::move(sourceMapURL));
743 nsAutoCString contentType;
744 aChannel->GetContentType(contentType);
746 // In standards mode, a style sheet must have one of these MIME
747 // types to be processed at all. In quirks mode, we accept any
748 // MIME type, but only if the style sheet is same-origin with the
749 // requesting document or parent sheet. See bug 524223.
751 bool validType = contentType.EqualsLiteral("text/css") ||
752 contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
753 contentType.IsEmpty();
755 if (!validType) {
756 const char* errorMessage;
757 uint32_t errorFlag;
758 bool sameOrigin = true;
760 bool subsumed;
761 result = mTriggeringPrincipal->Subsumes(principal, &subsumed);
762 if (NS_FAILED(result) || !subsumed) {
763 sameOrigin = false;
766 if (sameOrigin && mCompatMode == eCompatibility_NavQuirks) {
767 errorMessage = "MimeNotCssWarn";
768 errorFlag = nsIScriptError::warningFlag;
769 } else {
770 errorMessage = "MimeNotCss";
771 errorFlag = nsIScriptError::errorFlag;
774 AutoTArray<nsString, 2> strings;
775 CopyUTF8toUTF16(channelURI->GetSpecOrDefault(), *strings.AppendElement());
776 CopyASCIItoUTF16(contentType, *strings.AppendElement());
778 nsCOMPtr<nsIURI> referrer = ReferrerInfo()->GetOriginalReferrer();
779 nsContentUtils::ReportToConsole(
780 errorFlag, "CSS Loader"_ns, mLoader->mDocument,
781 nsContentUtils::eCSS_PROPERTIES, errorMessage, strings, referrer);
783 if (errorFlag == nsIScriptError::errorFlag) {
784 LOG_WARN(
785 (" Ignoring sheet with improper MIME type %s", contentType.get()));
786 mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
787 return NS_OK;
791 SRIMetadata sriMetadata;
792 mSheet->GetIntegrity(sriMetadata);
793 if (!sriMetadata.IsEmpty()) {
794 nsAutoCString sourceUri;
795 if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
796 mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
798 nsresult rv = VerifySheetIntegrity(sriMetadata, aChannel, aBytes1, aBytes2,
799 sourceUri, mLoader->mReporter);
801 nsCOMPtr<nsILoadGroup> loadGroup;
802 aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
803 if (loadGroup) {
804 mLoader->mReporter->FlushConsoleReports(loadGroup);
805 } else {
806 mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
809 if (NS_FAILED(rv)) {
810 LOG((" Load was blocked by SRI"));
811 MOZ_LOG(gSriPRLog, LogLevel::Debug,
812 ("css::Loader::OnStreamComplete, bad metadata"));
813 mLoader->SheetComplete(*this, NS_ERROR_SRI_CORRUPT);
814 return NS_OK;
818 // Enough to set the URIs on mSheet, since any sibling datas we have share
819 // the same mInner as mSheet and will thus get the same URI.
820 mSheet->SetURIs(channelURI, originalURI, channelURI);
822 ReferrerPolicy policy =
823 nsContentUtils::GetReferrerPolicyFromChannel(aChannel);
824 nsCOMPtr<nsIReferrerInfo> referrerInfo =
825 ReferrerInfo::CreateForExternalCSSResources(mSheet, policy);
827 mSheet->SetReferrerInfo(referrerInfo);
828 return NS_OK_PARSE_SHEET;
831 Loader::IsAlternate Loader::IsAlternateSheet(const nsAString& aTitle,
832 bool aHasAlternateRel) {
833 // A sheet is alternate if it has a nonempty title that doesn't match the
834 // currently selected style set. But if there _is_ no currently selected
835 // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
836 // is nonempty, we should select the style set corresponding to aTitle, since
837 // that's a preferred sheet.
838 if (aTitle.IsEmpty()) {
839 return IsAlternate::No;
842 if (mDocument) {
843 const nsString& currentSheetSet = mDocument->GetCurrentStyleSheetSet();
844 if (!aHasAlternateRel && currentSheetSet.IsEmpty()) {
845 // There's no preferred set yet, and we now have a sheet with a title.
846 // Make that be the preferred set.
847 // FIXME(emilio): This is kinda wild, can we do it somewhere else?
848 mDocument->SetPreferredStyleSheetSet(aTitle);
849 // We're definitely not an alternate. Also, beware that at this point
850 // currentSheetSet may dangle.
851 return IsAlternate::No;
854 if (aTitle.Equals(currentSheetSet)) {
855 return IsAlternate::No;
859 return IsAlternate::Yes;
862 nsresult Loader::CheckContentPolicy(nsIPrincipal* aLoadingPrincipal,
863 nsIPrincipal* aTriggeringPrincipal,
864 nsIURI* aTargetURI,
865 nsINode* aRequestingNode,
866 const nsAString& aNonce,
867 StylePreloadKind aPreloadKind) {
868 // When performing a system load don't consult content policies.
869 if (!mDocument) {
870 return NS_OK;
873 nsContentPolicyType contentPolicyType =
874 aPreloadKind == StylePreloadKind::None
875 ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
876 : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
878 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
879 aLoadingPrincipal, aTriggeringPrincipal, aRequestingNode,
880 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, contentPolicyType);
881 secCheckLoadInfo->SetCspNonce(aNonce);
883 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
884 nsresult rv =
885 NS_CheckContentLoadPolicy(aTargetURI, secCheckLoadInfo, &shouldLoad,
886 nsContentUtils::GetContentPolicy());
887 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
888 // Asynchronously notify observers (e.g devtools) of CSP failure.
889 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
890 "Loader::NotifyOnFailedCheckPolicy",
891 [targetURI = RefPtr<nsIURI>(aTargetURI),
892 requestingNode = RefPtr<nsINode>(aRequestingNode),
893 contentPolicyType]() {
894 nsCOMPtr<nsIChannel> channel;
895 NS_NewChannel(
896 getter_AddRefs(channel), targetURI, requestingNode,
897 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT,
898 contentPolicyType);
899 NS_SetRequestBlockingReason(
900 channel, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_GENERAL);
901 nsCOMPtr<nsIObserverService> obsService =
902 services::GetObserverService();
903 if (obsService) {
904 obsService->NotifyObservers(
905 channel, "http-on-failed-opening-request", nullptr);
907 }));
908 return NS_ERROR_CONTENT_BLOCKED;
910 return NS_OK;
913 static void RecordUseCountersIfNeeded(Document* aDoc,
914 const StyleSheet& aSheet) {
915 if (!aDoc) {
916 return;
918 const StyleUseCounters* docCounters = aDoc->GetStyleUseCounters();
919 if (!docCounters) {
920 return;
922 if (aSheet.URLData()->ChromeRulesEnabled()) {
923 return;
925 const auto* sheetCounters = aSheet.GetStyleUseCounters();
926 if (!sheetCounters) {
927 return;
929 Servo_UseCounters_Merge(docCounters, sheetCounters);
930 aDoc->MaybeWarnAboutZoom();
934 * CreateSheet() creates a StyleSheet object for the given URI.
936 * We check for an existing style sheet object for that uri in various caches
937 * and clone it if we find it. Cloned sheets will have the title/media/enabled
938 * state of the sheet they are clones off; make sure to call PrepareSheet() on
939 * the result of CreateSheet().
941 std::tuple<RefPtr<StyleSheet>, Loader::SheetState> Loader::CreateSheet(
942 nsIURI* aURI, nsIContent* aLinkingContent,
943 nsIPrincipal* aTriggeringPrincipal, css::SheetParsingMode aParsingMode,
944 CORSMode aCORSMode, const Encoding* aPreloadOrParentDataEncoding,
945 const nsAString& aIntegrity, bool aSyncLoad,
946 StylePreloadKind aPreloadKind) {
947 MOZ_ASSERT(aURI, "This path is not taken for inline stylesheets");
948 LOG(("css::Loader::CreateSheet(%s)", aURI->GetSpecOrDefault().get()));
950 SRIMetadata sriMetadata;
951 if (!aIntegrity.IsEmpty()) {
952 MOZ_LOG(gSriPRLog, LogLevel::Debug,
953 ("css::Loader::CreateSheet, integrity=%s",
954 NS_ConvertUTF16toUTF8(aIntegrity).get()));
955 nsAutoCString sourceUri;
956 if (mDocument && mDocument->GetDocumentURI()) {
957 mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
959 SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata);
962 if (mSheets) {
963 SheetLoadDataHashKey key(aURI, aTriggeringPrincipal, LoaderPrincipal(),
964 PartitionedPrincipal(),
965 GetFallbackEncoding(*this, aLinkingContent,
966 aPreloadOrParentDataEncoding),
967 aCORSMode, aParsingMode, CompatMode(aPreloadKind),
968 sriMetadata, aPreloadKind);
969 auto cacheResult = mSheets->Lookup(*this, key, aSyncLoad);
970 if (cacheResult.mState != CachedSubResourceState::Miss) {
971 SheetState sheetState = SheetState::Complete;
972 RefPtr<StyleSheet> sheet;
973 if (cacheResult.mCompleteValue) {
974 sheet = cacheResult.mCompleteValue->Clone(nullptr, nullptr);
975 mDocument->SetDidHitCompleteSheetCache();
976 RecordUseCountersIfNeeded(mDocument, *sheet);
977 mLoadsPerformed.PutEntry(key);
978 } else {
979 MOZ_ASSERT(cacheResult.mLoadingOrPendingValue);
980 sheet = cacheResult.mLoadingOrPendingValue->ValueForCache();
981 sheetState = cacheResult.mState == CachedSubResourceState::Loading
982 ? SheetState::Loading
983 : SheetState::Pending;
985 LOG((" Hit cache with state: %s", gStateStrings[size_t(sheetState)]));
986 return {std::move(sheet), sheetState};
990 nsIURI* sheetURI = aURI;
991 nsIURI* baseURI = aURI;
992 nsIURI* originalURI = aURI;
994 auto sheet = MakeRefPtr<StyleSheet>(aParsingMode, aCORSMode, sriMetadata);
995 sheet->SetURIs(sheetURI, originalURI, baseURI);
996 nsCOMPtr<nsIReferrerInfo> referrerInfo =
997 ReferrerInfo::CreateForExternalCSSResources(sheet);
998 sheet->SetReferrerInfo(referrerInfo);
999 LOG((" Needs parser"));
1000 return {std::move(sheet), SheetState::NeedsParser};
1003 static Loader::MediaMatched MediaListMatches(const MediaList* aMediaList,
1004 const Document* aDocument) {
1005 if (!aMediaList || !aDocument) {
1006 return Loader::MediaMatched::Yes;
1009 if (aMediaList->Matches(*aDocument)) {
1010 return Loader::MediaMatched::Yes;
1013 return Loader::MediaMatched::No;
1017 * PrepareSheet() handles setting the media and title on the sheet, as
1018 * well as setting the enabled state based on the title and whether
1019 * the sheet had "alternate" in its rel.
1021 Loader::MediaMatched Loader::PrepareSheet(
1022 StyleSheet& aSheet, const nsAString& aTitle, const nsAString& aMediaString,
1023 MediaList* aMediaList, IsAlternate aIsAlternate,
1024 IsExplicitlyEnabled aIsExplicitlyEnabled) {
1025 RefPtr<MediaList> mediaList(aMediaList);
1027 if (!aMediaString.IsEmpty()) {
1028 NS_ASSERTION(!aMediaList,
1029 "must not provide both aMediaString and aMediaList");
1030 mediaList = MediaList::Create(NS_ConvertUTF16toUTF8(aMediaString));
1033 aSheet.SetMedia(do_AddRef(mediaList));
1035 aSheet.SetTitle(aTitle);
1036 aSheet.SetEnabled(aIsAlternate == IsAlternate::No ||
1037 aIsExplicitlyEnabled == IsExplicitlyEnabled::Yes);
1038 return MediaListMatches(mediaList, mDocument);
1042 * InsertSheetInTree handles ordering of sheets in the document or shadow root.
1044 * Here we have two types of sheets -- those with linking elements and
1045 * those without. The latter are loaded by Link: headers, and are only added to
1046 * the document.
1048 * The following constraints are observed:
1049 * 1) Any sheet with a linking element comes after all sheets without
1050 * linking elements
1051 * 2) Sheets without linking elements are inserted in the order in
1052 * which the inserting requests come in, since all of these are
1053 * inserted during header data processing in the content sink
1054 * 3) Sheets with linking elements are ordered based on document order
1055 * as determined by CompareDocumentPosition.
1057 void Loader::InsertSheetInTree(StyleSheet& aSheet) {
1058 LOG(("css::Loader::InsertSheetInTree"));
1059 MOZ_ASSERT(mDocument, "Must have a document to insert into");
1061 nsINode* owningNode = aSheet.GetOwnerNode();
1062 MOZ_ASSERT(!owningNode || owningNode->IsInUncomposedDoc() ||
1063 owningNode->IsInShadowTree(),
1064 "Why would we insert it anywhere?");
1065 ShadowRoot* shadow = owningNode ? owningNode->GetContainingShadow() : nullptr;
1067 auto& target = shadow ? static_cast<DocumentOrShadowRoot&>(*shadow)
1068 : static_cast<DocumentOrShadowRoot&>(*mDocument);
1070 // XXX Need to cancel pending sheet loads for this element, if any
1072 int32_t sheetCount = target.SheetCount();
1075 * Start the walk at the _end_ of the list, since in the typical
1076 * case we'll just want to append anyway. We want to break out of
1077 * the loop when insertionPoint points to just before the index we
1078 * want to insert at. In other words, when we leave the loop
1079 * insertionPoint is the index of the stylesheet that immediately
1080 * precedes the one we're inserting.
1082 int32_t insertionPoint = sheetCount - 1;
1083 for (; insertionPoint >= 0; --insertionPoint) {
1084 nsINode* sheetOwner = target.SheetAt(insertionPoint)->GetOwnerNode();
1085 if (sheetOwner && !owningNode) {
1086 // Keep moving; all sheets with a sheetOwner come after all
1087 // sheets without a linkingNode
1088 continue;
1091 if (!sheetOwner) {
1092 // Aha! The current sheet has no sheet owner, so we want to insert after
1093 // it no matter whether we have a linking content or not.
1094 break;
1097 MOZ_ASSERT(owningNode != sheetOwner, "Why do we still have our old sheet?");
1099 // Have to compare
1100 if (nsContentUtils::PositionIsBefore(sheetOwner, owningNode)) {
1101 // The current sheet comes before us, and it better be the first
1102 // such, because now we break
1103 break;
1107 ++insertionPoint;
1109 if (shadow) {
1110 shadow->InsertSheetAt(insertionPoint, aSheet);
1111 } else {
1112 mDocument->InsertSheetAt(insertionPoint, aSheet);
1115 LOG((" Inserting into target (doc: %d) at position %d",
1116 target.AsNode().IsDocument(), insertionPoint));
1120 * InsertChildSheet handles ordering of @import-ed sheet in their
1121 * parent sheets. Here we want to just insert based on order of the
1122 * @import rules that imported the sheets. In theory we can't just
1123 * append to the end because the CSSOM can insert @import rules. In
1124 * practice, we get the call to load the child sheet before the CSSOM
1125 * has finished inserting the @import rule, so we have no idea where
1126 * to put it anyway. So just append for now. (In the future if we
1127 * want to insert the sheet at the correct position, we'll need to
1128 * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
1129 * bug 1220506.)
1131 void Loader::InsertChildSheet(StyleSheet& aSheet, StyleSheet& aParentSheet) {
1132 LOG(("css::Loader::InsertChildSheet"));
1134 // child sheets should always start out enabled, even if they got
1135 // cloned off of top-level sheets which were disabled
1136 aSheet.SetEnabled(true);
1137 aParentSheet.AppendStyleSheet(aSheet);
1139 LOG((" Inserting into parent sheet"));
1142 nsresult Loader::LoadSheetSyncInternal(SheetLoadData& aLoadData,
1143 SheetState aSheetState) {
1144 LOG((" Synchronous load"));
1145 MOZ_ASSERT(!aLoadData.mObserver, "Observer for a sync load?");
1146 MOZ_ASSERT(aSheetState == SheetState::NeedsParser,
1147 "Sync loads can't reuse existing async loads");
1149 nsINode* requestingNode = aLoadData.GetRequestingNode();
1151 nsresult rv = NS_OK;
1153 // Create a StreamLoader instance to which we will feed
1154 // the data from the sync load. Do this before creating the
1155 // channel to make error recovery simpler.
1156 auto streamLoader = MakeRefPtr<StreamLoader>(aLoadData);
1158 if (mDocument) {
1159 net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
1160 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, mDocument);
1163 // Synchronous loads should only be used internally. Therefore no CORS
1164 // policy is needed.
1165 nsSecurityFlags securityFlags =
1166 nsContentSecurityManager::ComputeSecurityFlags(
1167 CORSMode::CORS_NONE, nsContentSecurityManager::CORSSecurityMapping::
1168 CORS_NONE_MAPS_TO_INHERITED_CONTEXT);
1170 securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
1172 nsContentPolicyType contentPolicyType =
1173 aLoadData.mPreloadKind == StylePreloadKind::None
1174 ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1175 : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
1177 // Just load it
1178 nsCOMPtr<nsIChannel> channel;
1179 // Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both
1180 // a node and a principal.
1181 // This is because of a case where the node is the document being styled and
1182 // the principal is the stylesheet (perhaps from a different origin) that is
1183 // applying the styles.
1184 if (requestingNode) {
1185 rv = NS_NewChannelWithTriggeringPrincipal(
1186 getter_AddRefs(channel), aLoadData.mURI, requestingNode,
1187 aLoadData.mTriggeringPrincipal, securityFlags, contentPolicyType);
1188 } else {
1189 MOZ_ASSERT(aLoadData.mTriggeringPrincipal->Equals(LoaderPrincipal()));
1190 auto result = URLPreloader::ReadURI(aLoadData.mURI);
1191 if (result.isOk()) {
1192 nsCOMPtr<nsIInputStream> stream;
1193 MOZ_TRY(
1194 NS_NewCStringInputStream(getter_AddRefs(stream), result.unwrap()));
1196 rv = NS_NewInputStreamChannel(
1197 getter_AddRefs(channel), aLoadData.mURI, stream.forget(),
1198 aLoadData.mTriggeringPrincipal, securityFlags, contentPolicyType);
1199 } else {
1200 rv = NS_NewChannel(getter_AddRefs(channel), aLoadData.mURI,
1201 aLoadData.mTriggeringPrincipal, securityFlags,
1202 contentPolicyType);
1205 if (NS_FAILED(rv)) {
1206 LOG_ERROR((" Failed to create channel"));
1207 streamLoader->ChannelOpenFailed(rv);
1208 SheetComplete(aLoadData, rv);
1209 return rv;
1212 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1213 loadInfo->SetCspNonce(aLoadData.Nonce());
1215 nsCOMPtr<nsIInputStream> stream;
1216 rv = channel->Open(getter_AddRefs(stream));
1218 if (NS_FAILED(rv)) {
1219 LOG_ERROR((" Failed to open URI synchronously"));
1220 streamLoader->ChannelOpenFailed(rv);
1221 SheetComplete(aLoadData, rv);
1222 return rv;
1225 // Force UA sheets to be UTF-8.
1226 // XXX this is only necessary because the default in
1227 // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
1228 channel->SetContentCharset("UTF-8"_ns);
1230 // Manually feed the streamloader the contents of the stream.
1231 // This will call back into OnStreamComplete
1232 // and thence to ParseSheet. Regardless of whether this fails,
1233 // SheetComplete has been called.
1234 return nsSyncLoadService::PushSyncStreamToListener(stream.forget(),
1235 streamLoader, channel);
1238 bool Loader::MaybeDeferLoad(SheetLoadData& aLoadData, SheetState aSheetState,
1239 PendingLoad aPendingLoad,
1240 const SheetLoadDataHashKey& aKey) {
1241 MOZ_ASSERT(mSheets);
1243 // If we have at least one other load ongoing, then we can defer it until
1244 // all non-pending loads are done.
1245 if (aSheetState == SheetState::NeedsParser &&
1246 aPendingLoad == PendingLoad::No && aLoadData.ShouldDefer() &&
1247 mOngoingLoadCount > mPendingLoadCount + 1) {
1248 LOG((" Deferring sheet load"));
1249 ++mPendingLoadCount;
1250 mSheets->DeferLoad(aKey, aLoadData);
1251 return true;
1253 return false;
1256 bool Loader::MaybeCoalesceLoadAndNotifyOpen(SheetLoadData& aLoadData,
1257 SheetState aSheetState,
1258 const SheetLoadDataHashKey& aKey,
1259 const PreloadHashKey& aPreloadKey) {
1260 bool coalescedLoad = false;
1261 auto cacheState = [&aSheetState] {
1262 switch (aSheetState) {
1263 case SheetState::Complete:
1264 return CachedSubResourceState::Complete;
1265 case SheetState::Pending:
1266 return CachedSubResourceState::Pending;
1267 case SheetState::Loading:
1268 return CachedSubResourceState::Loading;
1269 case SheetState::NeedsParser:
1270 return CachedSubResourceState::Miss;
1272 MOZ_ASSERT_UNREACHABLE("wat");
1273 return CachedSubResourceState::Miss;
1274 }();
1276 if ((coalescedLoad = mSheets->CoalesceLoad(aKey, aLoadData, cacheState))) {
1277 if (aSheetState == SheetState::Pending) {
1278 ++mPendingLoadCount;
1279 } else {
1280 aLoadData.NotifyOpen(
1281 aPreloadKey, mDocument,
1282 aLoadData.IsLinkRelPreload() /* TODO: why not `IsPreload()`?*/);
1285 return coalescedLoad;
1289 * LoadSheet handles the actual load of a sheet. If the load is
1290 * supposed to be synchronous it just opens a channel synchronously
1291 * using the given uri, wraps the resulting stream in a converter
1292 * stream and calls ParseSheet. Otherwise it tries to look for an
1293 * existing load for this URI and piggyback on it. Failing all that,
1294 * a new load is kicked off asynchronously.
1296 nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState,
1297 uint64_t aEarlyHintPreloaderId,
1298 PendingLoad aPendingLoad) {
1299 LOG(("css::Loader::LoadSheet"));
1300 MOZ_ASSERT(aLoadData.mURI, "Need a URI to load");
1301 MOZ_ASSERT(aLoadData.mSheet, "Need a sheet to load into");
1302 MOZ_ASSERT(aSheetState != SheetState::Complete, "Why bother?");
1303 MOZ_ASSERT(!aLoadData.mUseSystemPrincipal || aLoadData.mSyncLoad,
1304 "Shouldn't use system principal for async loads");
1306 LOG_URI(" Load from: '%s'", aLoadData.mURI);
1308 // If we're firing a pending load, this load is already accounted for the
1309 // first time it went through this function.
1310 if (aPendingLoad == PendingLoad::No) {
1311 if (aLoadData.BlocksLoadEvent()) {
1312 IncrementOngoingLoadCountAndMaybeBlockOnload();
1315 // We technically never defer non-top-level sheets, so this condition could
1316 // be outside the branch, but conceptually it should be here.
1317 if (aLoadData.mParentData) {
1318 ++aLoadData.mParentData->mPendingChildren;
1322 if (!mDocument && !aLoadData.mIsNonDocumentSheet) {
1323 // No point starting the load; just release all the data and such.
1324 LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
1325 SheetComplete(aLoadData, NS_BINDING_ABORTED);
1326 return NS_BINDING_ABORTED;
1329 if (aLoadData.mSyncLoad) {
1330 return LoadSheetSyncInternal(aLoadData, aSheetState);
1333 SheetLoadDataHashKey key(aLoadData);
1335 auto preloadKey = PreloadHashKey::CreateAsStyle(aLoadData);
1336 if (mSheets) {
1337 if (MaybeDeferLoad(aLoadData, aSheetState, aPendingLoad, key)) {
1338 return NS_OK;
1341 if (MaybeCoalesceLoadAndNotifyOpen(aLoadData, aSheetState, key,
1342 preloadKey)) {
1343 // All done here; once the load completes we'll be marked complete
1344 // automatically.
1345 return NS_OK;
1349 aLoadData.NotifyOpen(preloadKey, mDocument, aLoadData.IsLinkRelPreload());
1351 return LoadSheetAsyncInternal(aLoadData, aEarlyHintPreloaderId, key);
1354 void Loader::AdjustPriority(const SheetLoadData& aLoadData,
1355 nsIChannel* aChannel) {
1356 if (!aLoadData.ShouldDefer() && aLoadData.IsLinkRelPreload()) {
1357 SheetLoadData::PrioritizeAsPreload(aChannel);
1360 if (!StaticPrefs::network_fetchpriority_enabled()) {
1361 return;
1364 nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(aChannel);
1366 if (!sp) {
1367 return;
1370 // Adjusting priorites is specified as implementation-defined.
1371 // See corresponding preferences in StaticPrefList.yaml for more context.
1372 const int32_t supportsPriorityDelta = [&]() {
1373 if (aLoadData.ShouldDefer()) {
1374 return FETCH_PRIORITY_ADJUSTMENT_FOR(deferred_style,
1375 aLoadData.mFetchPriority);
1377 if (aLoadData.IsLinkRelPreload()) {
1378 return FETCH_PRIORITY_ADJUSTMENT_FOR(link_preload_style,
1379 aLoadData.mFetchPriority);
1381 return FETCH_PRIORITY_ADJUSTMENT_FOR(non_deferred_style,
1382 aLoadData.mFetchPriority);
1383 }();
1385 sp->AdjustPriority(supportsPriorityDelta);
1386 #ifdef DEBUG
1387 int32_t adjustedPriority;
1388 sp->GetPriority(&adjustedPriority);
1389 LogPriorityMapping(sCssLoaderLog, aLoadData.mFetchPriority, adjustedPriority);
1390 #endif
1393 nsresult Loader::LoadSheetAsyncInternal(SheetLoadData& aLoadData,
1394 uint64_t aEarlyHintPreloaderId,
1395 const SheetLoadDataHashKey& aKey) {
1396 nsresult rv = NS_OK;
1398 SRIMetadata sriMetadata;
1399 aLoadData.mSheet->GetIntegrity(sriMetadata);
1401 nsINode* requestingNode = aLoadData.GetRequestingNode();
1403 nsCOMPtr<nsILoadGroup> loadGroup;
1404 nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
1405 if (mDocument) {
1406 loadGroup = mDocument->GetDocumentLoadGroup();
1407 // load for a document with no loadgrup indicates that something is
1408 // completely bogus, let's bail out early.
1409 if (!loadGroup) {
1410 LOG_ERROR((" Failed to query loadGroup from document"));
1411 SheetComplete(aLoadData, NS_ERROR_UNEXPECTED);
1412 return NS_ERROR_UNEXPECTED;
1415 cookieJarSettings = mDocument->CookieJarSettings();
1418 #ifdef DEBUG
1419 AutoRestore<bool> syncCallbackGuard(mSyncCallback);
1420 mSyncCallback = true;
1421 #endif
1423 nsSecurityFlags securityFlags =
1424 nsContentSecurityManager::ComputeSecurityFlags(
1425 aLoadData.mSheet->GetCORSMode(),
1426 nsContentSecurityManager::CORSSecurityMapping::
1427 CORS_NONE_MAPS_TO_INHERITED_CONTEXT);
1429 securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
1431 nsContentPolicyType contentPolicyType =
1432 aLoadData.mPreloadKind == StylePreloadKind::None
1433 ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1434 : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
1436 nsCOMPtr<nsIChannel> channel;
1437 // Note we are calling NS_NewChannelWithTriggeringPrincipal here with a node
1438 // and a principal. This is because of a case where the node is the document
1439 // being styled and the principal is the stylesheet (perhaps from a different
1440 // origin) that is applying the styles.
1441 if (requestingNode) {
1442 rv = NS_NewChannelWithTriggeringPrincipal(
1443 getter_AddRefs(channel), aLoadData.mURI, requestingNode,
1444 aLoadData.mTriggeringPrincipal, securityFlags, contentPolicyType,
1445 /* PerformanceStorage */ nullptr, loadGroup);
1446 } else {
1447 MOZ_ASSERT(aLoadData.mTriggeringPrincipal->Equals(LoaderPrincipal()));
1448 rv = NS_NewChannel(getter_AddRefs(channel), aLoadData.mURI,
1449 aLoadData.mTriggeringPrincipal, securityFlags,
1450 contentPolicyType, cookieJarSettings,
1451 /* aPerformanceStorage */ nullptr, loadGroup);
1454 if (NS_FAILED(rv)) {
1455 LOG_ERROR((" Failed to create channel"));
1456 SheetComplete(aLoadData, rv);
1457 return rv;
1460 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1461 loadInfo->SetCspNonce(aLoadData.Nonce());
1463 if (!aLoadData.ShouldDefer()) {
1464 if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(channel)) {
1465 cos->AddClassFlags(nsIClassOfService::Leader);
1468 if (aLoadData.IsLinkRelPreload()) {
1469 SheetLoadData::AddLoadBackgroundFlag(channel);
1473 AdjustPriority(aLoadData, channel);
1475 if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
1476 if (nsCOMPtr<nsIReferrerInfo> referrerInfo = aLoadData.ReferrerInfo()) {
1477 rv = httpChannel->SetReferrerInfo(referrerInfo);
1478 Unused << NS_WARN_IF(NS_FAILED(rv));
1481 nsCOMPtr<nsIHttpChannelInternal> internalChannel =
1482 do_QueryInterface(httpChannel);
1483 if (internalChannel) {
1484 rv = internalChannel->SetIntegrityMetadata(
1485 sriMetadata.GetIntegrityString());
1486 NS_ENSURE_SUCCESS(rv, rv);
1489 // Set the initiator type
1490 if (nsCOMPtr<nsITimedChannel> timedChannel =
1491 do_QueryInterface(httpChannel)) {
1492 if (aLoadData.mParentData) {
1493 timedChannel->SetInitiatorType(u"css"_ns);
1495 // This is a child sheet load.
1497 // The resource timing of the sub-resources that a document loads
1498 // should normally be reported to the document. One exception is any
1499 // sub-resources of any cross-origin resources that are loaded. We
1500 // don't mind reporting timing data for a direct child cross-origin
1501 // resource since the resource that linked to it (and hence potentially
1502 // anything in that parent origin) is aware that the cross-origin
1503 // resources is to be loaded. However, we do not want to report
1504 // timings for any sub-resources that a cross-origin resource may load
1505 // since that obviously leaks information about what the cross-origin
1506 // resource loads, which is bad.
1508 // In addition to checking whether we're an immediate child resource of
1509 // a cross-origin resource (by checking if mIsCrossOriginNoCORS is set
1510 // to true on our parent), we also check our parent to see whether it
1511 // itself is a sub-resource of a cross-origin resource by checking
1512 // mBlockResourceTiming. If that is set then we too are such a
1513 // sub-resource and so we set the flag on ourself too to propagate it
1514 // on down.
1515 if (aLoadData.mParentData->mIsCrossOriginNoCORS ||
1516 aLoadData.mParentData->mBlockResourceTiming) {
1517 // Set a flag so any other stylesheet triggered by this one will
1518 // not be reported
1519 aLoadData.mBlockResourceTiming = true;
1521 // Mark the channel so PerformanceMainThread::AddEntry will not
1522 // report the resource.
1523 timedChannel->SetReportResourceTiming(false);
1526 } else if (aEarlyHintPreloaderId) {
1527 timedChannel->SetInitiatorType(u"early-hints"_ns);
1528 } else {
1529 timedChannel->SetInitiatorType(u"link"_ns);
1534 // Now tell the channel we expect text/css data back.... We do
1535 // this before opening it, so it's only treated as a hint.
1536 channel->SetContentType("text/css"_ns);
1538 // We don't have to hold on to the stream loader. The ownership
1539 // model is: Necko owns the stream loader, which owns the load data,
1540 // which owns us
1541 auto streamLoader = MakeRefPtr<StreamLoader>(aLoadData);
1542 if (mDocument) {
1543 net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
1544 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, mDocument);
1547 if (aEarlyHintPreloaderId) {
1548 nsCOMPtr<nsIHttpChannelInternal> channelInternal =
1549 do_QueryInterface(channel);
1550 NS_ENSURE_TRUE(channelInternal != nullptr, NS_ERROR_FAILURE);
1552 rv = channelInternal->SetEarlyHintPreloaderId(aEarlyHintPreloaderId);
1553 NS_ENSURE_SUCCESS(rv, rv);
1555 rv = channel->AsyncOpen(streamLoader);
1556 if (NS_FAILED(rv)) {
1557 LOG_ERROR((" Failed to create stream loader"));
1558 streamLoader->ChannelOpenFailed(rv);
1559 // NOTE: NotifyStop will be done in SheetComplete -> NotifyObservers.
1560 aLoadData.NotifyStart(channel);
1561 SheetComplete(aLoadData, rv);
1562 return rv;
1565 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1566 if (nsCOMPtr<nsIHttpChannelInternal> hci = do_QueryInterface(channel)) {
1567 hci->DoDiagnosticAssertWhenOnStopNotCalledOnDestroy();
1569 #endif
1571 if (mSheets) {
1572 mSheets->LoadStarted(aKey, aLoadData);
1574 return NS_OK;
1578 * ParseSheet handles parsing the data stream.
1580 Loader::Completed Loader::ParseSheet(const nsACString& aBytes,
1581 SheetLoadData& aLoadData,
1582 AllowAsyncParse aAllowAsync) {
1583 LOG(("css::Loader::ParseSheet"));
1584 if (aLoadData.mURI) {
1585 LOG_URI(" Load succeeded for URI: '%s', parsing", aLoadData.mURI);
1587 AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(LAYOUT_CSSParsing);
1589 ++mParsedSheetCount;
1591 aLoadData.mIsBeingParsed = true;
1593 StyleSheet* sheet = aLoadData.mSheet;
1594 MOZ_ASSERT(sheet);
1596 // Some cases, like inline style and UA stylesheets, need to be parsed
1597 // synchronously. The former may trigger child loads, the latter must not.
1598 if (aLoadData.mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
1599 sheet->ParseSheetSync(this, aBytes, &aLoadData);
1600 aLoadData.mIsBeingParsed = false;
1602 bool noPendingChildren = aLoadData.mPendingChildren == 0;
1603 MOZ_ASSERT_IF(aLoadData.mSyncLoad, noPendingChildren);
1604 if (noPendingChildren) {
1605 SheetComplete(aLoadData, NS_OK);
1606 return Completed::Yes;
1608 return Completed::No;
1611 // This parse does not need to be synchronous. \o/
1613 // Note that load is already blocked from
1614 // IncrementOngoingLoadCountAndMaybeBlockOnload(), and will be unblocked from
1615 // SheetFinishedParsingAsync which will end up in NotifyObservers as needed.
1616 sheet->ParseSheet(*this, aBytes, aLoadData)
1617 ->Then(
1618 GetMainThreadSerialEventTarget(), __func__,
1619 [loadData = RefPtr<SheetLoadData>(&aLoadData)](bool aDummy) {
1620 MOZ_ASSERT(NS_IsMainThread());
1621 loadData->SheetFinishedParsingAsync();
1623 [] { MOZ_CRASH("rejected parse promise"); });
1624 return Completed::No;
1627 void Loader::NotifyObservers(SheetLoadData& aData, nsresult aStatus) {
1628 RecordUseCountersIfNeeded(mDocument, *aData.mSheet);
1629 RefPtr loadDispatcher = aData.PrepareLoadEventIfNeeded();
1630 if (aData.mURI) {
1631 mLoadsPerformed.PutEntry(SheetLoadDataHashKey(aData));
1632 aData.NotifyStop(aStatus);
1633 // NOTE(emilio): This needs to happen before notifying observers, as
1634 // FontFaceSet for example checks for pending sheet loads from the
1635 // StyleSheetLoaded callback.
1636 if (aData.BlocksLoadEvent()) {
1637 DecrementOngoingLoadCountAndMaybeUnblockOnload();
1638 if (mPendingLoadCount && mPendingLoadCount == mOngoingLoadCount) {
1639 LOG((" No more loading sheets; starting deferred loads"));
1640 StartDeferredLoads();
1644 if (!aData.mTitle.IsEmpty() && NS_SUCCEEDED(aStatus)) {
1645 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
1646 "Loader::NotifyObservers - Create PageStyle actor",
1647 [doc = RefPtr{mDocument}] {
1648 // Force creating the page style actor, if available.
1649 // This will no-op if no actor with this name is registered (outside
1650 // of desktop Firefox).
1651 nsCOMPtr<nsISupports> pageStyleActor =
1652 do_QueryActor("PageStyle", doc);
1653 Unused << pageStyleActor;
1654 }));
1656 if (aData.mMustNotify) {
1657 if (nsCOMPtr<nsICSSLoaderObserver> observer = std::move(aData.mObserver)) {
1658 LOG((" Notifying observer %p for data %p. deferred: %d", observer.get(),
1659 &aData, aData.ShouldDefer()));
1660 observer->StyleSheetLoaded(aData.mSheet, aData.ShouldDefer(), aStatus);
1663 for (nsCOMPtr<nsICSSLoaderObserver> obs : mObservers.ForwardRange()) {
1664 LOG((" Notifying global observer %p for data %p. deferred: %d",
1665 obs.get(), &aData, aData.ShouldDefer()));
1666 obs->StyleSheetLoaded(aData.mSheet, aData.ShouldDefer(), aStatus);
1669 if (loadDispatcher) {
1670 loadDispatcher->RunDOMEventWhenSafe();
1672 } else if (loadDispatcher) {
1673 loadDispatcher->PostDOMEvent();
1678 * SheetComplete is the do-it-all cleanup function. It removes the
1679 * load data from the "loading" hashtable, adds the sheet to the
1680 * "completed" hashtable, massages the XUL cache, handles siblings of
1681 * the load data (other loads for the same URI), handles unblocking
1682 * blocked parent loads as needed, and most importantly calls
1683 * NS_RELEASE on the load data to destroy the whole mess.
1685 void Loader::SheetComplete(SheetLoadData& aLoadData, nsresult aStatus) {
1686 LOG(("css::Loader::SheetComplete, status: 0x%" PRIx32,
1687 static_cast<uint32_t>(aStatus)));
1688 SharedStyleSheetCache::LoadCompleted(mSheets.get(), aLoadData, aStatus);
1691 // static
1692 void Loader::MarkLoadTreeFailed(SheetLoadData& aLoadData,
1693 Loader* aOnlyForLoader) {
1694 if (aLoadData.mURI) {
1695 LOG_URI(" Load failed: '%s'", aLoadData.mURI);
1698 SheetLoadData* data = &aLoadData;
1699 do {
1700 if (!aOnlyForLoader || aOnlyForLoader == data->mLoader) {
1701 data->mLoadFailed = true;
1702 data->mSheet->MaybeRejectReplacePromise();
1705 if (data->mParentData) {
1706 MarkLoadTreeFailed(*data->mParentData, aOnlyForLoader);
1709 data = data->mNext;
1710 } while (data);
1713 RefPtr<StyleSheet> Loader::LookupInlineSheetInCache(
1714 const nsAString& aBuffer, nsIPrincipal* aSheetPrincipal) {
1715 auto result = mInlineSheets.Lookup(aBuffer);
1716 if (!result) {
1717 return nullptr;
1719 StyleSheet* sheet = result.Data();
1720 if (NS_WARN_IF(sheet->HasModifiedRules())) {
1721 // Remove it now that we know that we're never going to use this stylesheet
1722 // again.
1723 result.Remove();
1724 return nullptr;
1726 if (NS_WARN_IF(!sheet->Principal()->Equals(aSheetPrincipal))) {
1727 // If the sheet is going to have different access rights, don't return it
1728 // from the cache.
1729 return nullptr;
1731 return sheet->Clone(nullptr, nullptr);
1734 void Loader::MaybeNotifyPreloadUsed(SheetLoadData& aData) {
1735 if (!mDocument) {
1736 return;
1739 auto key = PreloadHashKey::CreateAsStyle(aData);
1740 RefPtr<PreloaderBase> preload = mDocument->Preloads().LookupPreload(key);
1741 if (!preload) {
1742 return;
1745 preload->NotifyUsage(mDocument);
1748 Result<Loader::LoadSheetResult, nsresult> Loader::LoadInlineStyle(
1749 const SheetInfo& aInfo, const nsAString& aBuffer,
1750 nsICSSLoaderObserver* aObserver) {
1751 LOG(("css::Loader::LoadInlineStyle"));
1752 MOZ_ASSERT(aInfo.mContent);
1754 if (!mEnabled) {
1755 LOG_WARN((" Not enabled"));
1756 return Err(NS_ERROR_NOT_AVAILABLE);
1759 if (!mDocument) {
1760 return Err(NS_ERROR_NOT_INITIALIZED);
1763 MOZ_ASSERT(LinkStyle::FromNodeOrNull(aInfo.mContent),
1764 "Element is not a style linking element!");
1766 // Since we're not planning to load a URI, no need to hand a principal to the
1767 // load data or to CreateSheet().
1769 // Check IsAlternateSheet now, since it can mutate our document.
1770 auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
1771 LOG((" Sheet is alternate: %d", static_cast<int>(isAlternate)));
1773 // Use the document's base URL so that @import in the inline sheet picks up
1774 // the right base.
1775 nsIURI* baseURI = aInfo.mContent->GetBaseURI();
1776 nsIURI* sheetURI = aInfo.mContent->OwnerDoc()->GetDocumentURI();
1777 nsIURI* originalURI = nullptr;
1779 MOZ_ASSERT(aInfo.mIntegrity.IsEmpty());
1780 nsIPrincipal* loadingPrincipal = LoaderPrincipal();
1781 nsIPrincipal* principal = aInfo.mTriggeringPrincipal
1782 ? aInfo.mTriggeringPrincipal.get()
1783 : loadingPrincipal;
1784 nsIPrincipal* sheetPrincipal = [&] {
1785 // The triggering principal may be an expanded principal, which is safe to
1786 // use for URL security checks, but not as the loader principal for a
1787 // stylesheet. So treat this as principal inheritance, and downgrade if
1788 // necessary.
1790 // FIXME(emilio): Why doing this for inline sheets but not for links?
1791 if (aInfo.mTriggeringPrincipal) {
1792 return BasePrincipal::Cast(aInfo.mTriggeringPrincipal)
1793 ->PrincipalToInherit();
1795 return LoaderPrincipal();
1796 }();
1798 // We only cache sheets if in shadow trees, since regular document sheets are
1799 // likely to be unique.
1800 const bool isWorthCaching =
1801 StaticPrefs::layout_css_inline_style_caching_always_enabled() ||
1802 aInfo.mContent->IsInShadowTree();
1803 RefPtr<StyleSheet> sheet;
1804 if (isWorthCaching) {
1805 sheet = LookupInlineSheetInCache(aBuffer, sheetPrincipal);
1807 const bool isSheetFromCache = !!sheet;
1808 if (!isSheetFromCache) {
1809 sheet = MakeRefPtr<StyleSheet>(eAuthorSheetFeatures, aInfo.mCORSMode,
1810 SRIMetadata{});
1811 sheet->SetURIs(sheetURI, originalURI, baseURI);
1812 nsIReferrerInfo* referrerInfo =
1813 aInfo.mContent->OwnerDoc()->ReferrerInfoForInternalCSSAndSVGResources();
1814 sheet->SetReferrerInfo(referrerInfo);
1815 // We never actually load this, so just set its principal directly.
1816 sheet->SetPrincipal(sheetPrincipal);
1819 auto matched = PrepareSheet(*sheet, aInfo.mTitle, aInfo.mMedia, nullptr,
1820 isAlternate, aInfo.mIsExplicitlyEnabled);
1821 if (auto* linkStyle = LinkStyle::FromNode(*aInfo.mContent)) {
1822 linkStyle->SetStyleSheet(sheet);
1824 MOZ_ASSERT(sheet->IsComplete() == isSheetFromCache);
1826 Completed completed;
1827 auto data = MakeRefPtr<SheetLoadData>(
1828 this, aInfo.mTitle, /* aURI = */ nullptr, sheet, SyncLoad::No,
1829 aInfo.mContent, isAlternate, matched, StylePreloadKind::None, aObserver,
1830 principal, aInfo.mReferrerInfo, aInfo.mNonce, aInfo.mFetchPriority);
1831 MOZ_ASSERT(data->GetRequestingNode() == aInfo.mContent);
1832 if (isSheetFromCache) {
1833 MOZ_ASSERT(sheet->IsComplete());
1834 MOZ_ASSERT(sheet->GetOwnerNode() == aInfo.mContent);
1835 completed = Completed::Yes;
1836 InsertSheetInTree(*sheet);
1837 NotifyOfCachedLoad(std::move(data));
1838 } else {
1839 // Parse completion releases the load data.
1841 // Note that we need to parse synchronously, since the web expects that the
1842 // effects of inline stylesheets are visible immediately (aside from
1843 // @imports).
1844 NS_ConvertUTF16toUTF8 utf8(aBuffer);
1845 completed = ParseSheet(utf8, *data, AllowAsyncParse::No);
1846 if (completed == Completed::Yes) {
1847 if (isWorthCaching) {
1848 mInlineSheets.InsertOrUpdate(aBuffer, std::move(sheet));
1850 } else {
1851 data->mMustNotify = true;
1855 return LoadSheetResult{completed, isAlternate, matched};
1858 Result<Loader::LoadSheetResult, nsresult> Loader::LoadStyleLink(
1859 const SheetInfo& aInfo, nsICSSLoaderObserver* aObserver) {
1860 MOZ_ASSERT(aInfo.mURI, "Must have URL to load");
1861 LOG(("css::Loader::LoadStyleLink"));
1862 LOG_URI(" Link uri: '%s'", aInfo.mURI);
1863 LOG((" Link title: '%s'", NS_ConvertUTF16toUTF8(aInfo.mTitle).get()));
1864 LOG((" Link media: '%s'", NS_ConvertUTF16toUTF8(aInfo.mMedia).get()));
1865 LOG((" Link alternate rel: %d", aInfo.mHasAlternateRel));
1867 if (!mEnabled) {
1868 LOG_WARN((" Not enabled"));
1869 return Err(NS_ERROR_NOT_AVAILABLE);
1872 if (!mDocument) {
1873 return Err(NS_ERROR_NOT_INITIALIZED);
1876 MOZ_ASSERT_IF(aInfo.mContent,
1877 aInfo.mContent->NodePrincipal() == mDocument->NodePrincipal());
1878 nsIPrincipal* loadingPrincipal = LoaderPrincipal();
1879 nsIPrincipal* principal = aInfo.mTriggeringPrincipal
1880 ? aInfo.mTriggeringPrincipal.get()
1881 : loadingPrincipal;
1883 nsINode* requestingNode =
1884 aInfo.mContent ? static_cast<nsINode*>(aInfo.mContent) : mDocument;
1885 const bool syncLoad = [&] {
1886 if (!aInfo.mContent) {
1887 return false;
1889 const bool privilegedShadowTree =
1890 aInfo.mContent->IsInShadowTree() &&
1891 (aInfo.mContent->ChromeOnlyAccess() ||
1892 aInfo.mContent->OwnerDoc()->ChromeRulesEnabled());
1893 if (!privilegedShadowTree) {
1894 return false;
1896 if (!IsPrivilegedURI(aInfo.mURI)) {
1897 return false;
1899 // We're loading a chrome/resource URI in a chrome doc shadow tree or UA
1900 // widget. Load synchronously to avoid FOUC.
1901 return true;
1902 }();
1903 LOG((" Link sync load: '%s'", syncLoad ? "true" : "false"));
1904 MOZ_ASSERT_IF(syncLoad, !aObserver);
1906 nsresult rv =
1907 CheckContentPolicy(loadingPrincipal, principal, aInfo.mURI,
1908 requestingNode, aInfo.mNonce, StylePreloadKind::None);
1909 if (NS_WARN_IF(NS_FAILED(rv))) {
1910 // Don't fire the error event if our document is loaded as data. We're
1911 // supposed to not even try to do loads in that case... Unfortunately, we
1912 // implement that via nsDataDocumentContentPolicy, which doesn't have a good
1913 // way to communicate back to us that _it_ is the thing that blocked the
1914 // load.
1915 if (aInfo.mContent && !mDocument->IsLoadedAsData()) {
1916 // Fire an async error event on it.
1917 RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
1918 new LoadBlockingAsyncEventDispatcher(aInfo.mContent, u"error"_ns,
1919 CanBubble::eNo,
1920 ChromeOnlyDispatch::eNo);
1921 loadBlockingAsyncDispatcher->PostDOMEvent();
1923 return Err(rv);
1926 // Check IsAlternateSheet now, since it can mutate our document and make
1927 // pending sheets go to the non-pending state.
1928 auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
1929 auto [sheet, state] = CreateSheet(aInfo, eAuthorSheetFeatures, syncLoad,
1930 StylePreloadKind::None);
1932 LOG((" Sheet is alternate: %d", static_cast<int>(isAlternate)));
1934 auto matched = PrepareSheet(*sheet, aInfo.mTitle, aInfo.mMedia, nullptr,
1935 isAlternate, aInfo.mIsExplicitlyEnabled);
1937 if (auto* linkStyle = LinkStyle::FromNodeOrNull(aInfo.mContent)) {
1938 linkStyle->SetStyleSheet(sheet);
1940 MOZ_ASSERT(sheet->IsComplete() == (state == SheetState::Complete));
1942 // We may get here with no content for Link: headers for example.
1943 MOZ_ASSERT(!aInfo.mContent || LinkStyle::FromNode(*aInfo.mContent),
1944 "If there is any node, it should be a LinkStyle");
1945 auto data = MakeRefPtr<SheetLoadData>(
1946 this, aInfo.mTitle, aInfo.mURI, sheet, SyncLoad(syncLoad), aInfo.mContent,
1947 isAlternate, matched, StylePreloadKind::None, aObserver, principal,
1948 aInfo.mReferrerInfo, aInfo.mNonce, aInfo.mFetchPriority);
1950 MOZ_ASSERT(data->GetRequestingNode() == requestingNode);
1952 MaybeNotifyPreloadUsed(*data);
1954 if (state == SheetState::Complete) {
1955 LOG((" Sheet already complete: 0x%p", sheet.get()));
1956 MOZ_ASSERT(sheet->GetOwnerNode() == aInfo.mContent);
1957 InsertSheetInTree(*sheet);
1958 NotifyOfCachedLoad(std::move(data));
1959 return LoadSheetResult{Completed::Yes, isAlternate, matched};
1962 // Now we need to actually load it.
1963 auto result = LoadSheetResult{Completed::No, isAlternate, matched};
1965 MOZ_ASSERT(result.ShouldBlock() == !data->ShouldDefer(),
1966 "These should better match!");
1968 // Load completion will free the data
1969 rv = LoadSheet(*data, state, 0);
1970 if (NS_FAILED(rv)) {
1971 return Err(rv);
1974 if (!syncLoad) {
1975 data->mMustNotify = true;
1977 return result;
1980 static bool HaveAncestorDataWithURI(SheetLoadData& aData, nsIURI* aURI) {
1981 if (!aData.mURI) {
1982 // Inline style; this won't have any ancestors
1983 MOZ_ASSERT(!aData.mParentData, "How does inline style have a parent?");
1984 return false;
1987 bool equal;
1988 if (NS_FAILED(aData.mURI->Equals(aURI, &equal)) || equal) {
1989 return true;
1992 // Datas down the mNext chain have the same URI as aData, so we
1993 // don't have to compare to them. But they might have different
1994 // parents, and we have to check all of those.
1995 SheetLoadData* data = &aData;
1996 do {
1997 if (data->mParentData &&
1998 HaveAncestorDataWithURI(*data->mParentData, aURI)) {
1999 return true;
2002 data = data->mNext;
2003 } while (data);
2005 return false;
2008 nsresult Loader::LoadChildSheet(StyleSheet& aParentSheet,
2009 SheetLoadData* aParentData, nsIURI* aURL,
2010 dom::MediaList* aMedia,
2011 LoaderReusableStyleSheets* aReusableSheets) {
2012 LOG(("css::Loader::LoadChildSheet"));
2013 MOZ_ASSERT(aURL, "Must have a URI to load");
2015 if (!mEnabled) {
2016 LOG_WARN((" Not enabled"));
2017 return NS_ERROR_NOT_AVAILABLE;
2020 LOG_URI(" Child uri: '%s'", aURL);
2022 nsCOMPtr<nsINode> owningNode;
2024 nsINode* requestingNode = aParentSheet.GetOwnerNodeOfOutermostSheet();
2025 if (requestingNode) {
2026 MOZ_ASSERT(LoaderPrincipal() == requestingNode->NodePrincipal());
2027 } else {
2028 requestingNode = mDocument;
2031 nsIPrincipal* principal = aParentSheet.Principal();
2032 nsresult rv =
2033 CheckContentPolicy(LoaderPrincipal(), principal, aURL, requestingNode,
2034 /* aNonce = */ u""_ns, StylePreloadKind::None);
2035 if (NS_WARN_IF(NS_FAILED(rv))) {
2036 if (aParentData) {
2037 MarkLoadTreeFailed(*aParentData);
2039 return rv;
2042 nsCOMPtr<nsICSSLoaderObserver> observer;
2044 if (aParentData) {
2045 LOG((" Have a parent load"));
2046 // Check for cycles
2047 if (HaveAncestorDataWithURI(*aParentData, aURL)) {
2048 // Houston, we have a loop, blow off this child and pretend this never
2049 // happened
2050 LOG_ERROR((" @import cycle detected, dropping load"));
2051 return NS_OK;
2054 NS_ASSERTION(aParentData->mSheet == &aParentSheet,
2055 "Unexpected call to LoadChildSheet");
2056 } else {
2057 LOG((" No parent load; must be CSSOM"));
2058 // No parent load data, so the sheet will need to be notified when
2059 // we finish, if it can be, if we do the load asynchronously.
2060 observer = &aParentSheet;
2063 // Now that we know it's safe to load this (passes security check and not a
2064 // loop) do so.
2065 RefPtr<StyleSheet> sheet;
2066 SheetState state;
2067 if (aReusableSheets && aReusableSheets->FindReusableStyleSheet(aURL, sheet)) {
2068 state = SheetState::Complete;
2069 } else {
2070 // For now, use CORS_NONE for child sheets
2071 std::tie(sheet, state) = CreateSheet(
2072 aURL, nullptr, principal, aParentSheet.ParsingMode(), CORS_NONE,
2073 aParentData ? aParentData->mEncoding : nullptr,
2074 u""_ns, // integrity is only checked on main sheet
2075 aParentData && aParentData->mSyncLoad, StylePreloadKind::None);
2076 PrepareSheet(*sheet, u""_ns, u""_ns, aMedia, IsAlternate::No,
2077 IsExplicitlyEnabled::No);
2080 MOZ_ASSERT(sheet);
2081 InsertChildSheet(*sheet, aParentSheet);
2083 auto data =
2084 MakeRefPtr<SheetLoadData>(this, aURL, sheet, aParentData, observer,
2085 principal, aParentSheet.GetReferrerInfo());
2086 MOZ_ASSERT(data->GetRequestingNode() == requestingNode);
2088 MaybeNotifyPreloadUsed(*data);
2090 if (state == SheetState::Complete) {
2091 LOG((" Sheet already complete"));
2092 // We're completely done. No need to notify, even, since the
2093 // @import rule addition/modification will trigger the right style
2094 // changes automatically.
2095 data->mIntentionallyDropped = true;
2096 return NS_OK;
2099 bool syncLoad = data->mSyncLoad;
2101 // Load completion will release the data
2102 rv = LoadSheet(*data, state, 0);
2103 NS_ENSURE_SUCCESS(rv, rv);
2105 if (!syncLoad) {
2106 data->mMustNotify = true;
2108 return rv;
2111 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheetSync(
2112 nsIURI* aURL, SheetParsingMode aParsingMode,
2113 UseSystemPrincipal aUseSystemPrincipal) {
2114 LOG(("css::Loader::LoadSheetSync"));
2115 nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(nullptr);
2116 return InternalLoadNonDocumentSheet(
2117 aURL, StylePreloadKind::None, aParsingMode, aUseSystemPrincipal, nullptr,
2118 referrerInfo, nullptr, CORS_NONE, u""_ns, u""_ns, 0, FetchPriority::Auto);
2121 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheet(
2122 nsIURI* aURI, SheetParsingMode aParsingMode,
2123 UseSystemPrincipal aUseSystemPrincipal, nsICSSLoaderObserver* aObserver) {
2124 nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(nullptr);
2125 return InternalLoadNonDocumentSheet(
2126 aURI, StylePreloadKind::None, aParsingMode, aUseSystemPrincipal, nullptr,
2127 referrerInfo, aObserver, CORS_NONE, u""_ns, u""_ns, 0,
2128 FetchPriority::Auto);
2131 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheet(
2132 nsIURI* aURL, StylePreloadKind aPreloadKind,
2133 const Encoding* aPreloadEncoding, nsIReferrerInfo* aReferrerInfo,
2134 nsICSSLoaderObserver* aObserver, uint64_t aEarlyHintPreloaderId,
2135 CORSMode aCORSMode, const nsAString& aNonce, const nsAString& aIntegrity,
2136 FetchPriority aFetchPriority) {
2137 LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
2138 return InternalLoadNonDocumentSheet(
2139 aURL, aPreloadKind, eAuthorSheetFeatures, UseSystemPrincipal::No,
2140 aPreloadEncoding, aReferrerInfo, aObserver, aCORSMode, aNonce, aIntegrity,
2141 aEarlyHintPreloaderId, aFetchPriority);
2144 Result<RefPtr<StyleSheet>, nsresult> Loader::InternalLoadNonDocumentSheet(
2145 nsIURI* aURL, StylePreloadKind aPreloadKind, SheetParsingMode aParsingMode,
2146 UseSystemPrincipal aUseSystemPrincipal, const Encoding* aPreloadEncoding,
2147 nsIReferrerInfo* aReferrerInfo, nsICSSLoaderObserver* aObserver,
2148 CORSMode aCORSMode, const nsAString& aNonce, const nsAString& aIntegrity,
2149 uint64_t aEarlyHintPreloaderId, FetchPriority aFetchPriority) {
2150 MOZ_ASSERT(aURL, "Must have a URI to load");
2151 MOZ_ASSERT(aUseSystemPrincipal == UseSystemPrincipal::No || !aObserver,
2152 "Shouldn't load system-principal sheets async");
2153 MOZ_ASSERT(aReferrerInfo, "Must have referrerInfo");
2155 LOG_URI(" Non-document sheet uri: '%s'", aURL);
2157 if (!mEnabled) {
2158 LOG_WARN((" Not enabled"));
2159 return Err(NS_ERROR_NOT_AVAILABLE);
2162 nsIPrincipal* loadingPrincipal = LoaderPrincipal();
2163 nsIPrincipal* triggeringPrincipal = loadingPrincipal;
2164 nsresult rv = CheckContentPolicy(loadingPrincipal, triggeringPrincipal, aURL,
2165 mDocument, aNonce, aPreloadKind);
2166 if (NS_FAILED(rv)) {
2167 return Err(rv);
2170 bool syncLoad = !aObserver;
2171 auto [sheet, state] =
2172 CreateSheet(aURL, nullptr, triggeringPrincipal, aParsingMode, aCORSMode,
2173 aPreloadEncoding, aIntegrity, syncLoad, aPreloadKind);
2175 PrepareSheet(*sheet, u""_ns, u""_ns, nullptr, IsAlternate::No,
2176 IsExplicitlyEnabled::No);
2178 auto data = MakeRefPtr<SheetLoadData>(
2179 this, aURL, sheet, SyncLoad(syncLoad), aUseSystemPrincipal, aPreloadKind,
2180 aPreloadEncoding, aObserver, triggeringPrincipal, aReferrerInfo, aNonce,
2181 aFetchPriority);
2182 MOZ_ASSERT(data->GetRequestingNode() == mDocument);
2183 if (state == SheetState::Complete) {
2184 LOG((" Sheet already complete"));
2185 NotifyOfCachedLoad(std::move(data));
2186 return sheet;
2189 rv = LoadSheet(*data, state, aEarlyHintPreloaderId);
2190 if (NS_FAILED(rv)) {
2191 return Err(rv);
2193 if (aObserver) {
2194 data->mMustNotify = true;
2196 return sheet;
2199 void Loader::NotifyOfCachedLoad(RefPtr<SheetLoadData> aLoadData) {
2200 LOG(("css::Loader::PostLoadEvent"));
2201 MOZ_ASSERT(aLoadData->mSheet->IsComplete(),
2202 "Only expected to be used for cached sheets");
2203 // If we get to this code, the stylesheet loaded correctly at some point, so
2204 // we can just schedule a load event and don't need to touch the data's
2205 // mLoadFailed.
2206 // Note that we do this here and not from inside our SheetComplete so that we
2207 // don't end up running the load event more async than needed.
2208 MOZ_ASSERT(!aLoadData->mLoadFailed, "Why are we marked as failed?");
2209 aLoadData->mSheetAlreadyComplete = true;
2211 // We need to check mURI to match
2212 // DecrementOngoingLoadCountAndMaybeUnblockOnload().
2213 if (aLoadData->mURI && aLoadData->BlocksLoadEvent()) {
2214 IncrementOngoingLoadCountAndMaybeBlockOnload();
2216 SheetComplete(*aLoadData, NS_OK);
2219 void Loader::Stop() {
2220 if (mSheets) {
2221 mSheets->CancelLoadsForLoader(*this);
2225 bool Loader::HasPendingLoads() { return mOngoingLoadCount; }
2227 void Loader::AddObserver(nsICSSLoaderObserver* aObserver) {
2228 MOZ_ASSERT(aObserver, "Must have observer");
2229 mObservers.AppendElementUnlessExists(aObserver);
2232 void Loader::RemoveObserver(nsICSSLoaderObserver* aObserver) {
2233 mObservers.RemoveElement(aObserver);
2236 void Loader::StartDeferredLoads() {
2237 if (mSheets && mPendingLoadCount) {
2238 mSheets->StartPendingLoadsForLoader(
2239 *this, [](const SheetLoadData&) { return true; });
2243 NS_IMPL_CYCLE_COLLECTION_CLASS(Loader)
2245 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Loader)
2246 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheets);
2247 for (const auto& data : tmp->mInlineSheets.Values()) {
2248 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "Inline sheet cache in Loader");
2249 cb.NoteXPCOMChild(data);
2251 for (nsCOMPtr<nsICSSLoaderObserver>& obs : tmp->mObservers.ForwardRange()) {
2252 ImplCycleCollectionTraverse(cb, obs, "mozilla::css::Loader.mObservers");
2254 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
2255 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2257 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Loader)
2258 if (tmp->mSheets) {
2259 if (tmp->mDocument) {
2260 tmp->DeregisterFromSheetCache();
2262 tmp->mSheets = nullptr;
2264 tmp->mInlineSheets.Clear();
2265 tmp->mObservers.Clear();
2266 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocGroup)
2267 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2269 size_t Loader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
2270 size_t n = aMallocSizeOf(this);
2272 n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
2274 n += mInlineSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
2275 for (const auto& entry : mInlineSheets) {
2276 n += entry.GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2277 // If the sheet has a parent, then its parent will report it so we don't
2278 // have to worry about it here.
2279 const StyleSheet* sheet = entry.GetWeak();
2280 MOZ_ASSERT(!sheet->GetParentSheet(),
2281 "How did an @import rule end up here?");
2282 if (!sheet->GetOwnerNode()) {
2283 n += sheet->SizeOfIncludingThis(aMallocSizeOf);
2287 // Measurement of the following members may be added later if DMD finds it is
2288 // worthwhile:
2289 // The following members aren't measured:
2290 // - mDocument, because it's a weak backpointer
2292 return n;
2295 nsIPrincipal* Loader::LoaderPrincipal() const {
2296 if (mDocument) {
2297 return mDocument->NodePrincipal();
2299 // Loaders without a document do system loads.
2300 return nsContentUtils::GetSystemPrincipal();
2303 nsIPrincipal* Loader::PartitionedPrincipal() const {
2304 if (mDocument && StaticPrefs::privacy_partition_network_state()) {
2305 return mDocument->PartitionedPrincipal();
2307 return LoaderPrincipal();
2310 bool Loader::ShouldBypassCache() const {
2311 if (!mDocument) {
2312 return false;
2314 RefPtr<nsILoadGroup> lg = mDocument->GetDocumentLoadGroup();
2315 if (!lg) {
2316 return false;
2318 nsLoadFlags flags;
2319 if (NS_FAILED(lg->GetLoadFlags(&flags))) {
2320 return false;
2322 return flags & (nsIRequest::LOAD_BYPASS_CACHE |
2323 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE);
2326 void Loader::BlockOnload() {
2327 if (mDocument) {
2328 mDocument->BlockOnload();
2332 void Loader::UnblockOnload(bool aFireSync) {
2333 if (mDocument) {
2334 mDocument->UnblockOnload(aFireSync);
2338 } // namespace css
2339 } // namespace mozilla