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/SRILogHelper.h"
14 #include "mozilla/IntegerPrintfMacros.h"
15 #include "mozilla/AutoRestore.h"
16 #include "mozilla/LoadInfo.h"
17 #include "mozilla/Logging.h"
18 #include "mozilla/MemoryReporting.h"
19 #include "mozilla/PreloadHashKey.h"
20 #include "mozilla/ResultExtensions.h"
21 #include "mozilla/SchedulerGroup.h"
22 #include "mozilla/URLPreloader.h"
23 #include "nsIRunnable.h"
24 #include "nsISupportsPriority.h"
25 #include "nsITimedChannel.h"
26 #include "nsICachingChannel.h"
27 #include "nsSyncLoadService.h"
30 #include "nsIContent.h"
31 #include "nsIContentInlines.h"
32 #include "nsICookieJarSettings.h"
33 #include "mozilla/dom/Document.h"
35 #include "nsNetUtil.h"
36 #include "nsContentUtils.h"
37 #include "nsIScriptSecurityManager.h"
38 #include "nsContentPolicyUtils.h"
39 #include "nsIHttpChannel.h"
40 #include "nsIHttpChannelInternal.h"
41 #include "nsIClassOfService.h"
42 #include "nsIScriptError.h"
43 #include "nsMimeTypes.h"
44 #include "nsICSSLoaderObserver.h"
45 #include "nsThreadUtils.h"
46 #include "nsGkAtoms.h"
47 #include "nsIThreadInternal.h"
48 #include "nsINetworkPredictor.h"
49 #include "nsStringStream.h"
50 #include "mozilla/dom/MediaList.h"
51 #include "mozilla/dom/ShadowRoot.h"
52 #include "mozilla/dom/URL.h"
53 #include "mozilla/net/UrlClassifierFeatureFactory.h"
54 #include "mozilla/AsyncEventDispatcher.h"
55 #include "mozilla/ProfilerLabels.h"
56 #include "mozilla/ServoBindings.h"
57 #include "mozilla/StyleSheet.h"
58 #include "mozilla/StyleSheetInlines.h"
59 #include "mozilla/ConsoleReportCollector.h"
60 #include "mozilla/ServoUtils.h"
61 #include "mozilla/css/StreamLoader.h"
62 #include "mozilla/SharedStyleSheetCache.h"
63 #include "mozilla/StaticPrefs_dom.h"
64 #include "ReferrerInfo.h"
67 # include "nsXULPrototypeCache.h"
72 #include "mozilla/dom/SRICheck.h"
74 #include "mozilla/Encoding.h"
76 using namespace mozilla::dom
;
78 // 1024 bytes is specified in https://drafts.csswg.org/css-syntax/
79 #define SNIFFING_BUFFER_SIZE 1024
82 * OVERALL ARCHITECTURE
84 * The CSS Loader gets requests to load various sorts of style sheets:
85 * inline style from <style> elements, linked style, @import-ed child
86 * sheets, non-document sheets. The loader handles the following tasks:
87 * 1) Creation of the actual style sheet objects: CreateSheet()
88 * 2) setting of the right media, title, enabled state, etc on the
89 * sheet: PrepareSheet()
90 * 3) Insertion of the sheet in the proper cascade order:
91 * InsertSheetInTree() and InsertChildSheet()
92 * 4) Load of the sheet: LoadSheet() including security checks
93 * 5) Parsing of the sheet: ParseSheet()
94 * 6) Cleanup: SheetComplete()
96 * The detailed documentation for these functions is found with the
97 * function implementations.
99 * The following helper object is used:
100 * SheetLoadData -- a small class that is used to store all the
101 * information needed for the loading of a sheet;
102 * this class handles listening for the stream
103 * loader completion and also handles charset
107 extern mozilla::LazyLogModule sCssLoaderLog
;
108 mozilla::LazyLogModule
sCssLoaderLog("nsCSSLoader");
110 static mozilla::LazyLogModule
gSriPRLog("SRI");
112 #define LOG_ERROR(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Error, args)
113 #define LOG_WARN(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Warning, args)
114 #define LOG_DEBUG(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Debug, args)
115 #define LOG(args) LOG_DEBUG(args)
117 #define LOG_ERROR_ENABLED() \
118 MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Error)
119 #define LOG_WARN_ENABLED() \
120 MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Warning)
121 #define LOG_DEBUG_ENABLED() \
122 MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Debug)
123 #define LOG_ENABLED() LOG_DEBUG_ENABLED()
125 #define LOG_URI(format, uri) \
127 NS_ASSERTION(uri, "Logging null uri"); \
128 if (LOG_ENABLED()) { \
129 LOG((format, uri->GetSpecOrDefault().get())); \
133 // And some convenience strings...
134 static const char* const gStateStrings
[] = {"NeedsParser", "Pending", "Loading",
139 SheetLoadDataHashKey::SheetLoadDataHashKey(const css::SheetLoadData
& aLoadData
)
140 : mURI(aLoadData
.mURI
),
141 mPrincipal(aLoadData
.mTriggeringPrincipal
),
142 mLoaderPrincipal(aLoadData
.mLoader
->LoaderPrincipal()),
143 mPartitionPrincipal(aLoadData
.mLoader
->PartitionedPrincipal()),
144 mEncodingGuess(aLoadData
.mGuessedEncoding
),
145 mCORSMode(aLoadData
.mSheet
->GetCORSMode()),
146 mParsingMode(aLoadData
.mSheet
->ParsingMode()),
147 mCompatMode(aLoadData
.mCompatMode
),
148 mIsLinkRelPreload(aLoadData
.IsLinkRelPreload()) {
149 MOZ_COUNT_CTOR(SheetLoadDataHashKey
);
151 MOZ_ASSERT(mPrincipal
);
152 MOZ_ASSERT(mLoaderPrincipal
);
153 MOZ_ASSERT(mPartitionPrincipal
);
154 aLoadData
.mSheet
->GetIntegrity(mSRIMetadata
);
157 bool SheetLoadDataHashKey::KeyEquals(const SheetLoadDataHashKey
& aKey
) const {
160 if (NS_FAILED(mURI
->Equals(aKey
.mURI
, &eq
)) || !eq
) {
165 LOG_URI("KeyEquals(%s)\n", mURI
);
167 if (!mPrincipal
->Equals(aKey
.mPrincipal
)) {
168 LOG((" > Principal mismatch\n"));
172 // We only check for partition principal equality if any of the loads are
173 // triggered by a document rather than e.g. an extension (which have different
174 // origins than the loader principal).
175 if (mPrincipal
->Equals(mLoaderPrincipal
) ||
176 aKey
.mPrincipal
->Equals(aKey
.mLoaderPrincipal
)) {
177 if (!mPartitionPrincipal
->Equals(aKey
.mPartitionPrincipal
)) {
178 LOG((" > Partition principal mismatch\n"));
183 if (mCORSMode
!= aKey
.mCORSMode
) {
184 LOG((" > CORS mismatch\n"));
188 if (mParsingMode
!= aKey
.mParsingMode
) {
189 LOG((" > Parsing mode mismatch\n"));
193 if (mCompatMode
!= aKey
.mCompatMode
) {
194 LOG((" > Quirks mismatch\n"));
198 // If encoding differs, then don't reuse the cache.
200 // TODO(emilio): When the encoding is determined from the request (either
201 // BOM or Content-Length or @charset), we could do a bit better,
203 if (mEncodingGuess
!= aKey
.mEncodingGuess
) {
204 LOG((" > Encoding guess mismatch\n"));
208 // Consuming stylesheet tags must never coalesce to <link preload> initiated
209 // speculative loads with a weaker SRI hash or its different value. This
210 // check makes sure that regular loads will never find such a weaker preload
211 // and rather start a new, independent load with new, stronger SRI checker
212 // set up, so that integrity is ensured.
213 if (mIsLinkRelPreload
!= aKey
.mIsLinkRelPreload
) {
214 const auto& linkPreloadMetadata
=
215 mIsLinkRelPreload
? mSRIMetadata
: aKey
.mSRIMetadata
;
216 const auto& consumerPreloadMetadata
=
217 mIsLinkRelPreload
? aKey
.mSRIMetadata
: mSRIMetadata
;
219 if (!consumerPreloadMetadata
.CanTrustBeDelegatedTo(linkPreloadMetadata
)) {
220 LOG((" > Preload SRI metadata mismatch\n"));
230 static NotNull
<const Encoding
*> GetFallbackEncoding(
231 Loader
& aLoader
, nsINode
* aOwningNode
,
232 const Encoding
* aPreloadOrParentDataEncoding
) {
233 const Encoding
* encoding
;
234 // Now try the charset on the <link> or processing instruction
237 nsAutoString label16
;
238 LinkStyle::FromNode(*aOwningNode
)->GetCharset(label16
);
239 encoding
= Encoding::ForLabel(label16
);
241 return WrapNotNull(encoding
);
245 // Try preload or parent sheet encoding.
246 if (aPreloadOrParentDataEncoding
) {
247 return WrapNotNull(aPreloadOrParentDataEncoding
);
250 if (auto* doc
= aLoader
.GetDocument()) {
251 // Use the document charset.
252 return doc
->GetDocumentCharacterSet();
255 return UTF_8_ENCODING
;
258 /********************************
259 * SheetLoadData implementation *
260 ********************************/
261 NS_IMPL_ISUPPORTS(SheetLoadData
, nsIRunnable
, nsIThreadObserver
)
263 SheetLoadData::SheetLoadData(
264 Loader
* aLoader
, const nsAString
& aTitle
, nsIURI
* aURI
, StyleSheet
* aSheet
,
265 bool aSyncLoad
, nsINode
* aOwningNode
, IsAlternate aIsAlternate
,
266 MediaMatched aMediaMatches
, StylePreloadKind aPreloadKind
,
267 nsICSSLoaderObserver
* aObserver
, nsIPrincipal
* aTriggeringPrincipal
,
268 nsIReferrerInfo
* aReferrerInfo
, nsINode
* aRequestingNode
)
277 mSyncLoad(aSyncLoad
),
278 mIsNonDocumentSheet(false),
279 mIsChildSheet(aSheet
->GetParentSheet()),
281 mIsBeingParsed(false),
284 mWasAlternate(aIsAlternate
== IsAlternate::Yes
),
285 mMediaMatched(aMediaMatches
== MediaMatched::Yes
),
286 mUseSystemPrincipal(false),
287 mSheetAlreadyComplete(false),
288 mIsCrossOriginNoCORS(false),
289 mBlockResourceTiming(false),
291 mPreloadKind(aPreloadKind
),
292 mOwningNode(aOwningNode
),
293 mObserver(aObserver
),
294 mTriggeringPrincipal(aTriggeringPrincipal
),
295 mReferrerInfo(aReferrerInfo
),
296 mRequestingNode(aRequestingNode
),
297 mGuessedEncoding(GetFallbackEncoding(*aLoader
, aOwningNode
, nullptr)),
298 mCompatMode(aLoader
->CompatMode(aPreloadKind
)) {
299 MOZ_ASSERT(!mOwningNode
|| dom::LinkStyle::FromNode(*mOwningNode
),
300 "Must implement LinkStyle");
301 MOZ_ASSERT(mTriggeringPrincipal
);
302 MOZ_ASSERT(mLoader
, "Must have a loader!");
305 SheetLoadData::SheetLoadData(Loader
* aLoader
, nsIURI
* aURI
, StyleSheet
* aSheet
,
306 SheetLoadData
* aParentData
,
307 nsICSSLoaderObserver
* aObserver
,
308 nsIPrincipal
* aTriggeringPrincipal
,
309 nsIReferrerInfo
* aReferrerInfo
,
310 nsINode
* aRequestingNode
)
317 mParentData(aParentData
),
319 mSyncLoad(aParentData
&& aParentData
->mSyncLoad
),
320 mIsNonDocumentSheet(aParentData
&& aParentData
->mIsNonDocumentSheet
),
321 mIsChildSheet(aSheet
->GetParentSheet()),
323 mIsBeingParsed(false),
326 mWasAlternate(false),
328 mUseSystemPrincipal(aParentData
&& aParentData
->mUseSystemPrincipal
),
329 mSheetAlreadyComplete(false),
330 mIsCrossOriginNoCORS(false),
331 mBlockResourceTiming(false),
333 mPreloadKind(StylePreloadKind::None
),
334 mOwningNode(nullptr),
335 mObserver(aObserver
),
336 mTriggeringPrincipal(aTriggeringPrincipal
),
337 mReferrerInfo(aReferrerInfo
),
338 mRequestingNode(aRequestingNode
),
339 mGuessedEncoding(GetFallbackEncoding(
340 *aLoader
, nullptr, aParentData
? aParentData
->mEncoding
: nullptr)),
341 mCompatMode(aLoader
->CompatMode(mPreloadKind
)) {
342 MOZ_ASSERT(mLoader
, "Must have a loader!");
343 MOZ_ASSERT(mTriggeringPrincipal
);
344 MOZ_ASSERT(!mUseSystemPrincipal
|| mSyncLoad
,
345 "Shouldn't use system principal for async loads");
346 MOZ_ASSERT_IF(aParentData
, mIsChildSheet
);
349 SheetLoadData::SheetLoadData(
350 Loader
* aLoader
, nsIURI
* aURI
, StyleSheet
* aSheet
, bool aSyncLoad
,
351 UseSystemPrincipal aUseSystemPrincipal
, StylePreloadKind aPreloadKind
,
352 const Encoding
* aPreloadEncoding
, nsICSSLoaderObserver
* aObserver
,
353 nsIPrincipal
* aTriggeringPrincipal
, nsIReferrerInfo
* aReferrerInfo
,
354 nsINode
* aRequestingNode
)
362 mSyncLoad(aSyncLoad
),
363 mIsNonDocumentSheet(true),
364 mIsChildSheet(false),
366 mIsBeingParsed(false),
369 mWasAlternate(false),
371 mUseSystemPrincipal(aUseSystemPrincipal
== UseSystemPrincipal::Yes
),
372 mSheetAlreadyComplete(false),
373 mIsCrossOriginNoCORS(false),
374 mBlockResourceTiming(false),
376 mPreloadKind(aPreloadKind
),
377 mOwningNode(nullptr),
378 mObserver(aObserver
),
379 mTriggeringPrincipal(aTriggeringPrincipal
),
380 mReferrerInfo(aReferrerInfo
),
381 mRequestingNode(aRequestingNode
),
383 GetFallbackEncoding(*aLoader
, nullptr, aPreloadEncoding
)),
384 mCompatMode(aLoader
->CompatMode(aPreloadKind
)) {
385 MOZ_ASSERT(mTriggeringPrincipal
);
386 MOZ_ASSERT(mLoader
, "Must have a loader!");
387 MOZ_ASSERT(!mUseSystemPrincipal
|| mSyncLoad
,
388 "Shouldn't use system principal for async loads");
389 MOZ_ASSERT(!aSheet
->GetParentSheet(), "Shouldn't be used for child loads");
392 SheetLoadData::~SheetLoadData() {
393 MOZ_DIAGNOSTIC_ASSERT(mSheetCompleteCalled
|| mIntentionallyDropped
,
394 "Should always call SheetComplete, except when "
395 "dropping the load");
397 // Do this iteratively to avoid blowing up the stack.
398 RefPtr
<SheetLoadData
> next
= std::move(mNext
);
400 next
= std::move(next
->mNext
);
405 SheetLoadData::Run() {
406 mLoader
->HandleLoadEvent(*this);
411 SheetLoadData::OnDispatchedEvent() { return NS_OK
; }
414 SheetLoadData::OnProcessNextEvent(nsIThreadInternal
* aThread
, bool aMayWait
) {
415 // XXXkhuey this is insane!
416 // We want to fire our load even before or after event processing,
417 // whichever comes first.
418 FireLoadEvent(aThread
);
423 SheetLoadData::AfterProcessNextEvent(nsIThreadInternal
* aThread
,
424 bool aEventWasProcessed
) {
425 // XXXkhuey this too!
426 // We want to fire our load even before or after event processing,
427 // whichever comes first.
428 FireLoadEvent(aThread
);
432 void SheetLoadData::PrioritizeAsPreload(nsIChannel
* aChannel
) {
433 if (nsCOMPtr
<nsISupportsPriority
> sp
= do_QueryInterface(aChannel
)) {
434 sp
->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST
);
438 void SheetLoadData::PrioritizeAsPreload() { PrioritizeAsPreload(Channel()); }
440 void SheetLoadData::FireLoadEvent(nsIThreadInternal
* aThread
) {
441 // First remove ourselves as a thread observer. But we need to keep
442 // ourselves alive while doing that!
443 RefPtr
<SheetLoadData
> kungFuDeathGrip(this);
444 aThread
->RemoveObserver(this);
446 // Now fire the event.
448 // NOTE(emilio): A bit weird that we fire the event even if the node is no
449 // longer in the tree, or the sheet that just loaded / errored is not the
450 // current node.sheet, but...
451 nsCOMPtr
<nsINode
> node
= mOwningNode
;
452 MOZ_ASSERT(node
, "How did that happen???");
454 nsContentUtils::DispatchTrustedEvent(node
->OwnerDoc(), node
,
455 mLoadFailed
? u
"error"_ns
: u
"load"_ns
,
456 CanBubble::eNo
, Cancelable::eNo
);
458 MOZ_ASSERT(BlocksLoadEvent());
459 mLoader
->UnblockOnload(true);
462 void SheetLoadData::ScheduleLoadEventIfNeeded() {
467 MOZ_ASSERT(BlocksLoadEvent(), "The rel=preload load event happens elsewhere");
469 nsCOMPtr
<nsIThread
> thread
= do_GetCurrentThread();
470 nsCOMPtr
<nsIThreadInternal
> internalThread
= do_QueryInterface(thread
);
471 if (NS_SUCCEEDED(internalThread
->AddObserver(this))) {
472 mLoader
->BlockOnload();
476 /*********************
477 * Style sheet reuse *
478 *********************/
480 bool LoaderReusableStyleSheets::FindReusableStyleSheet(
481 nsIURI
* aURL
, RefPtr
<StyleSheet
>& aResult
) {
483 for (size_t i
= mReusableSheets
.Length(); i
> 0; --i
) {
484 size_t index
= i
- 1;
486 MOZ_ASSERT(mReusableSheets
[index
]->GetOriginalURI());
488 aURL
->Equals(mReusableSheets
[index
]->GetOriginalURI(), &sameURI
);
489 if (!NS_FAILED(rv
) && sameURI
) {
490 aResult
= mReusableSheets
[index
];
491 mReusableSheets
.RemoveElementAt(index
);
497 /*************************
498 * Loader Implementation *
499 *************************/
502 : mDocument(nullptr),
503 mDocumentCompatMode(eCompatibility_FullStandards
),
504 mReporter(new ConsoleReportCollector()) {}
506 Loader::Loader(DocGroup
* aDocGroup
) : Loader() { mDocGroup
= aDocGroup
; }
508 Loader::Loader(Document
* aDocument
) : Loader() {
509 MOZ_ASSERT(aDocument
, "We should get a valid document from the caller!");
510 mDocument
= aDocument
;
511 mDocumentCompatMode
= aDocument
->GetCompatibilityMode();
512 mSheets
= SharedStyleSheetCache::Get();
513 RegisterInSheetCache();
517 // Note: no real need to revoke our stylesheet loaded events -- they
518 // hold strong references to us, so if we're going away that means
522 void Loader::RegisterInSheetCache() {
523 MOZ_ASSERT(mDocument
);
526 mSheets
->RegisterLoader(*this);
529 void Loader::DeregisterFromSheetCache() {
530 MOZ_ASSERT(mDocument
);
533 mSheets
->CancelLoadsForLoader(*this);
534 mSheets
->UnregisterLoader(*this);
537 void Loader::DropDocumentReference() {
538 // Flush out pending datas just so we don't leak by accident.
540 DeregisterFromSheetCache();
545 void Loader::DocumentStyleSheetSetChanged() {
546 MOZ_ASSERT(mDocument
);
548 // start any pending alternates that aren't alternates anymore
549 mSheets
->StartDeferredLoadsForLoader(
550 *this, SharedStyleSheetCache::StartLoads::IfNonAlternate
);
553 static const char kCharsetSym
[] = "@charset \"";
555 static bool GetCharsetFromData(const char* aStyleSheetData
,
556 uint32_t aDataLength
, nsACString
& aCharset
) {
558 if (aDataLength
<= sizeof(kCharsetSym
) - 1) return false;
560 if (strncmp(aStyleSheetData
, kCharsetSym
, sizeof(kCharsetSym
) - 1)) {
564 for (uint32_t i
= sizeof(kCharsetSym
) - 1; i
< aDataLength
; ++i
) {
565 char c
= aStyleSheetData
[i
];
568 if (i
< aDataLength
&& aStyleSheetData
[i
] == ';') {
577 // Did not see end quote or semicolon
582 NotNull
<const Encoding
*> SheetLoadData::DetermineNonBOMEncoding(
583 const nsACString
& aSegment
, nsIChannel
* aChannel
) const {
584 const Encoding
* encoding
;
588 if (aChannel
&& NS_SUCCEEDED(aChannel
->GetContentCharset(label
))) {
589 encoding
= Encoding::ForLabel(label
);
591 return WrapNotNull(encoding
);
596 auto sniffingLength
= aSegment
.Length();
597 if (sniffingLength
> SNIFFING_BUFFER_SIZE
) {
598 sniffingLength
= SNIFFING_BUFFER_SIZE
;
600 if (GetCharsetFromData(aSegment
.BeginReading(), sniffingLength
, label
)) {
601 encoding
= Encoding::ForLabel(label
);
602 if (encoding
== UTF_16BE_ENCODING
|| encoding
== UTF_16LE_ENCODING
) {
603 return UTF_8_ENCODING
;
606 return WrapNotNull(encoding
);
609 return mGuessedEncoding
;
612 static nsresult
VerifySheetIntegrity(const SRIMetadata
& aMetadata
,
613 nsIChannel
* aChannel
,
614 const nsACString
& aFirst
,
615 const nsACString
& aSecond
,
616 const nsACString
& aSourceFileURI
,
617 nsIConsoleReportCollector
* aReporter
) {
618 NS_ENSURE_ARG_POINTER(aReporter
);
620 if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), LogLevel::Debug
)) {
621 nsAutoCString requestURL
;
622 nsCOMPtr
<nsIURI
> originalURI
;
624 NS_SUCCEEDED(aChannel
->GetOriginalURI(getter_AddRefs(originalURI
))) &&
626 originalURI
->GetAsciiSpec(requestURL
);
628 MOZ_LOG(SRILogHelper::GetSriLog(), LogLevel::Debug
,
629 ("VerifySheetIntegrity (unichar stream)"));
632 SRICheckDataVerifier
verifier(aMetadata
, aSourceFileURI
, aReporter
);
634 verifier
.Update(aFirst
.Length(), (const uint8_t*)aFirst
.BeginReading());
635 NS_ENSURE_SUCCESS(rv
, rv
);
637 verifier
.Update(aSecond
.Length(), (const uint8_t*)aSecond
.BeginReading());
638 NS_ENSURE_SUCCESS(rv
, rv
);
640 return verifier
.Verify(aMetadata
, aChannel
, aSourceFileURI
, aReporter
);
643 static bool AllLoadsCanceled(const SheetLoadData
& aData
) {
644 const SheetLoadData
* data
= &aData
;
646 if (!data
->mIsCancelled
) {
649 } while ((data
= data
->mNext
));
654 * Stream completion code shared by Stylo and the old style system.
656 * Here we need to check that the load did not give us an http error
657 * page and check the mimetype on the channel to make sure we're not
658 * loading non-text/css data in standards mode.
660 nsresult
SheetLoadData::VerifySheetReadyToParse(nsresult aStatus
,
661 const nsACString
& aBytes1
,
662 const nsACString
& aBytes2
,
663 nsIChannel
* aChannel
) {
664 LOG(("SheetLoadData::VerifySheetReadyToParse"));
665 NS_ASSERTION(!mLoader
->mSyncCallback
, "Synchronous callback from necko");
667 if (AllLoadsCanceled(*this)) {
668 LOG_WARN((" All loads are canceled, dropping"));
669 mLoader
->SheetComplete(*this, NS_BINDING_ABORTED
);
673 if (NS_FAILED(aStatus
)) {
675 (" Load failed: status 0x%" PRIx32
, static_cast<uint32_t>(aStatus
)));
676 // Handle sheet not loading error because source was a tracking URL (or
677 // fingerprinting, cryptomining, etc).
678 // We make a note of this sheet node by including it in a dedicated
679 // array of blocked tracking nodes under its parent document.
681 // Multiple sheet load instances might be tied to this request,
682 // we annotate each one linked to a valid owning element (node).
683 if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
685 if (Document
* doc
= mLoader
->GetDocument()) {
686 for (SheetLoadData
* data
= this; data
; data
= data
->mNext
) {
687 // mOwningNode may be null but AddBlockTrackingNode can cope
688 doc
->AddBlockedNodeByClassifier(
689 nsIContent::FromNodeOrNull(data
->mOwningNode
));
693 mLoader
->SheetComplete(*this, aStatus
);
698 mLoader
->SheetComplete(*this, NS_OK
);
702 nsCOMPtr
<nsIURI
> originalURI
;
703 aChannel
->GetOriginalURI(getter_AddRefs(originalURI
));
705 // If the channel's original URI is "chrome:", we want that, since
706 // the observer code in nsXULPrototypeCache depends on chrome stylesheets
707 // having a chrome URI. (Whether or not chrome stylesheets come through
708 // this codepath seems nondeterministic.)
709 // Otherwise we want the potentially-HTTP-redirected URI.
710 nsCOMPtr
<nsIURI
> channelURI
;
711 NS_GetFinalChannelURI(aChannel
, getter_AddRefs(channelURI
));
713 if (!channelURI
|| !originalURI
) {
714 NS_ERROR("Someone just violated the nsIRequest contract");
715 LOG_WARN((" Channel without a URI. Bad!"));
716 mLoader
->SheetComplete(*this, NS_ERROR_UNEXPECTED
);
720 nsCOMPtr
<nsIPrincipal
> principal
;
721 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
722 nsresult result
= NS_ERROR_NOT_AVAILABLE
;
723 if (secMan
) { // Could be null if we already shut down
724 if (mUseSystemPrincipal
) {
725 result
= secMan
->GetSystemPrincipal(getter_AddRefs(principal
));
727 result
= secMan
->GetChannelResultPrincipal(aChannel
,
728 getter_AddRefs(principal
));
732 if (NS_FAILED(result
)) {
733 LOG_WARN((" Couldn't get principal"));
734 mLoader
->SheetComplete(*this, result
);
738 mSheet
->SetPrincipal(principal
);
740 if (mSheet
->GetCORSMode() == CORS_NONE
&&
741 !mTriggeringPrincipal
->Subsumes(principal
)) {
742 mIsCrossOriginNoCORS
= true;
745 // If it's an HTTP channel, we want to make sure this is not an
746 // error document we got.
747 if (nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
)) {
748 bool requestSucceeded
;
749 result
= httpChannel
->GetRequestSucceeded(&requestSucceeded
);
750 if (NS_SUCCEEDED(result
) && !requestSucceeded
) {
751 LOG((" Load returned an error page"));
752 mLoader
->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE
);
756 nsAutoCString sourceMapURL
;
757 if (nsContentUtils::GetSourceMapURL(httpChannel
, sourceMapURL
)) {
758 mSheet
->SetSourceMapURL(NS_ConvertUTF8toUTF16(sourceMapURL
));
762 nsAutoCString contentType
;
763 aChannel
->GetContentType(contentType
);
765 // In standards mode, a style sheet must have one of these MIME
766 // types to be processed at all. In quirks mode, we accept any
767 // MIME type, but only if the style sheet is same-origin with the
768 // requesting document or parent sheet. See bug 524223.
770 bool validType
= contentType
.EqualsLiteral("text/css") ||
771 contentType
.EqualsLiteral(UNKNOWN_CONTENT_TYPE
) ||
772 contentType
.IsEmpty();
775 const char* errorMessage
;
777 bool sameOrigin
= true;
780 result
= mTriggeringPrincipal
->Subsumes(principal
, &subsumed
);
781 if (NS_FAILED(result
) || !subsumed
) {
785 if (sameOrigin
&& mCompatMode
== eCompatibility_NavQuirks
) {
786 errorMessage
= "MimeNotCssWarn";
787 errorFlag
= nsIScriptError::warningFlag
;
789 errorMessage
= "MimeNotCss";
790 errorFlag
= nsIScriptError::errorFlag
;
793 AutoTArray
<nsString
, 2> strings
;
794 CopyUTF8toUTF16(channelURI
->GetSpecOrDefault(), *strings
.AppendElement());
795 CopyASCIItoUTF16(contentType
, *strings
.AppendElement());
797 nsCOMPtr
<nsIURI
> referrer
= ReferrerInfo()->GetOriginalReferrer();
798 nsContentUtils::ReportToConsole(
799 errorFlag
, "CSS Loader"_ns
, mLoader
->mDocument
,
800 nsContentUtils::eCSS_PROPERTIES
, errorMessage
, strings
, referrer
);
802 if (errorFlag
== nsIScriptError::errorFlag
) {
804 (" Ignoring sheet with improper MIME type %s", contentType
.get()));
805 mLoader
->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE
);
810 SRIMetadata sriMetadata
;
811 mSheet
->GetIntegrity(sriMetadata
);
812 if (!sriMetadata
.IsEmpty()) {
813 nsAutoCString sourceUri
;
814 if (mLoader
->mDocument
&& mLoader
->mDocument
->GetDocumentURI()) {
815 mLoader
->mDocument
->GetDocumentURI()->GetAsciiSpec(sourceUri
);
817 nsresult rv
= VerifySheetIntegrity(sriMetadata
, aChannel
, aBytes1
, aBytes2
,
818 sourceUri
, mLoader
->mReporter
);
820 nsCOMPtr
<nsILoadGroup
> loadGroup
;
821 aChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
823 mLoader
->mReporter
->FlushConsoleReports(loadGroup
);
825 mLoader
->mReporter
->FlushConsoleReports(mLoader
->mDocument
);
829 LOG((" Load was blocked by SRI"));
830 MOZ_LOG(gSriPRLog
, LogLevel::Debug
,
831 ("css::Loader::OnStreamComplete, bad metadata"));
832 mLoader
->SheetComplete(*this, NS_ERROR_SRI_CORRUPT
);
837 // Enough to set the URIs on mSheet, since any sibling datas we have share
838 // the same mInner as mSheet and will thus get the same URI.
839 mSheet
->SetURIs(channelURI
, originalURI
, channelURI
);
841 ReferrerPolicy policy
=
842 nsContentUtils::GetReferrerPolicyFromChannel(aChannel
);
843 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
844 ReferrerInfo::CreateForExternalCSSResources(mSheet
, policy
);
846 mSheet
->SetReferrerInfo(referrerInfo
);
847 return NS_OK_PARSE_SHEET
;
850 Loader::IsAlternate
Loader::IsAlternateSheet(const nsAString
& aTitle
,
851 bool aHasAlternateRel
) {
852 // A sheet is alternate if it has a nonempty title that doesn't match the
853 // currently selected style set. But if there _is_ no currently selected
854 // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
855 // is nonempty, we should select the style set corresponding to aTitle, since
856 // that's a preferred sheet.
857 if (aTitle
.IsEmpty()) {
858 return IsAlternate::No
;
862 const nsString
& currentSheetSet
= mDocument
->GetCurrentStyleSheetSet();
863 if (!aHasAlternateRel
&& currentSheetSet
.IsEmpty()) {
864 // There's no preferred set yet, and we now have a sheet with a title.
865 // Make that be the preferred set.
866 // FIXME(emilio): This is kinda wild, can we do it somewhere else?
867 mDocument
->SetPreferredStyleSheetSet(aTitle
);
868 // We're definitely not an alternate. Also, beware that at this point
869 // currentSheetSet may dangle.
870 return IsAlternate::No
;
873 if (aTitle
.Equals(currentSheetSet
)) {
874 return IsAlternate::No
;
878 return IsAlternate::Yes
;
881 nsresult
Loader::CheckContentPolicy(nsIPrincipal
* aLoadingPrincipal
,
882 nsIPrincipal
* aTriggeringPrincipal
,
884 nsINode
* aRequestingNode
,
885 const nsAString
& aNonce
,
886 StylePreloadKind aPreloadKind
) {
887 // When performing a system load don't consult content policies.
892 nsContentPolicyType contentPolicyType
=
893 aPreloadKind
== StylePreloadKind::None
894 ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
895 : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
;
897 nsCOMPtr
<nsILoadInfo
> secCheckLoadInfo
= new net::LoadInfo(
898 aLoadingPrincipal
, aTriggeringPrincipal
, aRequestingNode
,
899 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK
, contentPolicyType
);
901 // snapshot the nonce at load start time for performing CSP checks
902 if (contentPolicyType
== nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
) {
903 secCheckLoadInfo
->SetCspNonce(aNonce
);
904 MOZ_ASSERT_IF(aPreloadKind
!= StylePreloadKind::None
, aNonce
.IsEmpty());
907 int16_t shouldLoad
= nsIContentPolicy::ACCEPT
;
908 nsresult rv
= NS_CheckContentLoadPolicy(aTargetURI
, secCheckLoadInfo
,
909 "text/css"_ns
, &shouldLoad
,
910 nsContentUtils::GetContentPolicy());
911 if (NS_FAILED(rv
) || NS_CP_REJECTED(shouldLoad
)) {
912 return NS_ERROR_CONTENT_BLOCKED
;
917 static void RecordUseCountersIfNeeded(Document
* aDoc
,
918 const StyleUseCounters
* aCounters
) {
919 if (!aDoc
|| !aCounters
) {
922 const StyleUseCounters
* docCounters
= aDoc
->GetStyleUseCounters();
926 Servo_UseCounters_Merge(docCounters
, aCounters
);
927 aDoc
->MaybeWarnAboutZoom();
930 void Loader::DidHitCompleteSheetCache(const SheetLoadDataHashKey
& aKey
,
931 const StyleUseCounters
* aCounters
) {
932 MOZ_ASSERT(mDocument
);
933 if (mLoadsPerformed
.EnsureInserted(aKey
)) {
934 RecordUseCountersIfNeeded(mDocument
, aCounters
);
939 * CreateSheet() creates a StyleSheet object for the given URI.
941 * We check for an existing style sheet object for that uri in various caches
942 * and clone it if we find it. Cloned sheets will have the title/media/enabled
943 * state of the sheet they are clones off; make sure to call PrepareSheet() on
944 * the result of CreateSheet().
946 std::tuple
<RefPtr
<StyleSheet
>, Loader::SheetState
> Loader::CreateSheet(
947 nsIURI
* aURI
, nsIContent
* aLinkingContent
,
948 nsIPrincipal
* aTriggeringPrincipal
, css::SheetParsingMode aParsingMode
,
949 CORSMode aCORSMode
, const Encoding
* aPreloadOrParentDataEncoding
,
950 const nsAString
& aIntegrity
, bool aSyncLoad
,
951 StylePreloadKind aPreloadKind
) {
952 MOZ_ASSERT(aURI
, "This path is not taken for inline stylesheets");
953 LOG(("css::Loader::CreateSheet(%s)", aURI
->GetSpecOrDefault().get()));
955 SRIMetadata sriMetadata
;
956 if (!aIntegrity
.IsEmpty()) {
957 MOZ_LOG(gSriPRLog
, LogLevel::Debug
,
958 ("css::Loader::CreateSheet, integrity=%s",
959 NS_ConvertUTF16toUTF8(aIntegrity
).get()));
960 nsAutoCString sourceUri
;
961 if (mDocument
&& mDocument
->GetDocumentURI()) {
962 mDocument
->GetDocumentURI()->GetAsciiSpec(sourceUri
);
964 SRICheck::IntegrityMetadata(aIntegrity
, sourceUri
, mReporter
, &sriMetadata
);
968 SheetLoadDataHashKey
key(aURI
, aTriggeringPrincipal
, LoaderPrincipal(),
969 PartitionedPrincipal(),
970 GetFallbackEncoding(*this, aLinkingContent
,
971 aPreloadOrParentDataEncoding
),
972 aCORSMode
, aParsingMode
, CompatMode(aPreloadKind
),
973 sriMetadata
, aPreloadKind
);
974 auto cacheResult
= mSheets
->Lookup(*this, key
, aSyncLoad
);
975 if (const auto& [styleSheet
, sheetState
] = cacheResult
; styleSheet
) {
976 LOG((" Hit cache with state: %s", gStateStrings
[size_t(sheetState
)]));
981 nsIURI
* sheetURI
= aURI
;
982 nsIURI
* baseURI
= aURI
;
983 nsIURI
* originalURI
= aURI
;
985 auto sheet
= MakeRefPtr
<StyleSheet
>(aParsingMode
, aCORSMode
, sriMetadata
);
986 sheet
->SetURIs(sheetURI
, originalURI
, baseURI
);
987 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
988 ReferrerInfo::CreateForExternalCSSResources(sheet
);
989 sheet
->SetReferrerInfo(referrerInfo
);
990 LOG((" Needs parser"));
991 return {std::move(sheet
), SheetState::NeedsParser
};
994 static Loader::MediaMatched
MediaListMatches(const MediaList
* aMediaList
,
995 const Document
* aDocument
) {
996 if (!aMediaList
|| !aDocument
) {
997 return Loader::MediaMatched::Yes
;
1000 if (aMediaList
->Matches(*aDocument
)) {
1001 return Loader::MediaMatched::Yes
;
1004 return Loader::MediaMatched::No
;
1008 * PrepareSheet() handles setting the media and title on the sheet, as
1009 * well as setting the enabled state based on the title and whether
1010 * the sheet had "alternate" in its rel.
1012 Loader::MediaMatched
Loader::PrepareSheet(
1013 StyleSheet
& aSheet
, const nsAString
& aTitle
, const nsAString
& aMediaString
,
1014 MediaList
* aMediaList
, IsAlternate aIsAlternate
,
1015 IsExplicitlyEnabled aIsExplicitlyEnabled
) {
1016 RefPtr
<MediaList
> mediaList(aMediaList
);
1018 if (!aMediaString
.IsEmpty()) {
1019 NS_ASSERTION(!aMediaList
,
1020 "must not provide both aMediaString and aMediaList");
1021 mediaList
= MediaList::Create(NS_ConvertUTF16toUTF8(aMediaString
));
1024 aSheet
.SetMedia(do_AddRef(mediaList
));
1026 aSheet
.SetTitle(aTitle
);
1027 aSheet
.SetEnabled(aIsAlternate
== IsAlternate::No
||
1028 aIsExplicitlyEnabled
== IsExplicitlyEnabled::Yes
);
1029 return MediaListMatches(mediaList
, mDocument
);
1033 * InsertSheetInTree handles ordering of sheets in the document or shadow root.
1035 * Here we have two types of sheets -- those with linking elements and
1036 * those without. The latter are loaded by Link: headers, and are only added to
1039 * The following constraints are observed:
1040 * 1) Any sheet with a linking element comes after all sheets without
1042 * 2) Sheets without linking elements are inserted in the order in
1043 * which the inserting requests come in, since all of these are
1044 * inserted during header data processing in the content sink
1045 * 3) Sheets with linking elements are ordered based on document order
1046 * as determined by CompareDocumentPosition.
1048 void Loader::InsertSheetInTree(StyleSheet
& aSheet
, nsINode
* aOwningNode
) {
1049 LOG(("css::Loader::InsertSheetInTree"));
1050 MOZ_ASSERT(mDocument
, "Must have a document to insert into");
1051 MOZ_ASSERT(!aOwningNode
|| aOwningNode
->IsInUncomposedDoc() ||
1052 aOwningNode
->IsInShadowTree(),
1053 "Why would we insert it anywhere?");
1054 ShadowRoot
* shadow
=
1055 aOwningNode
? aOwningNode
->GetContainingShadow() : nullptr;
1057 auto& target
= shadow
? static_cast<DocumentOrShadowRoot
&>(*shadow
)
1058 : static_cast<DocumentOrShadowRoot
&>(*mDocument
);
1060 // XXX Need to cancel pending sheet loads for this element, if any
1062 int32_t sheetCount
= target
.SheetCount();
1065 * Start the walk at the _end_ of the list, since in the typical
1066 * case we'll just want to append anyway. We want to break out of
1067 * the loop when insertionPoint points to just before the index we
1068 * want to insert at. In other words, when we leave the loop
1069 * insertionPoint is the index of the stylesheet that immediately
1070 * precedes the one we're inserting.
1072 int32_t insertionPoint
= sheetCount
- 1;
1073 for (; insertionPoint
>= 0; --insertionPoint
) {
1074 nsINode
* sheetOwner
= target
.SheetAt(insertionPoint
)->GetOwnerNode();
1075 if (sheetOwner
&& !aOwningNode
) {
1076 // Keep moving; all sheets with a sheetOwner come after all
1077 // sheets without a linkingNode
1082 // Aha! The current sheet has no sheet owner, so we want to insert after
1083 // it no matter whether we have a linking content or not.
1087 MOZ_ASSERT(aOwningNode
!= sheetOwner
,
1088 "Why do we still have our old sheet?");
1091 if (nsContentUtils::PositionIsBefore(sheetOwner
, aOwningNode
)) {
1092 // The current sheet comes before us, and it better be the first
1093 // such, because now we break
1101 shadow
->InsertSheetAt(insertionPoint
, aSheet
);
1103 mDocument
->InsertSheetAt(insertionPoint
, aSheet
);
1106 LOG((" Inserting into target (doc: %d) at position %d",
1107 target
.AsNode().IsDocument(), insertionPoint
));
1111 * InsertChildSheet handles ordering of @import-ed sheet in their
1112 * parent sheets. Here we want to just insert based on order of the
1113 * @import rules that imported the sheets. In theory we can't just
1114 * append to the end because the CSSOM can insert @import rules. In
1115 * practice, we get the call to load the child sheet before the CSSOM
1116 * has finished inserting the @import rule, so we have no idea where
1117 * to put it anyway. So just append for now. (In the future if we
1118 * want to insert the sheet at the correct position, we'll need to
1119 * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
1122 void Loader::InsertChildSheet(StyleSheet
& aSheet
, StyleSheet
& aParentSheet
) {
1123 LOG(("css::Loader::InsertChildSheet"));
1125 // child sheets should always start out enabled, even if they got
1126 // cloned off of top-level sheets which were disabled
1127 aSheet
.SetEnabled(true);
1128 aParentSheet
.AppendStyleSheet(aSheet
);
1130 LOG((" Inserting into parent sheet"));
1134 * LoadSheet handles the actual load of a sheet. If the load is
1135 * supposed to be synchronous it just opens a channel synchronously
1136 * using the given uri, wraps the resulting stream in a converter
1137 * stream and calls ParseSheet. Otherwise it tries to look for an
1138 * existing load for this URI and piggyback on it. Failing all that,
1139 * a new load is kicked off asynchronously.
1141 nsresult
Loader::LoadSheet(SheetLoadData
& aLoadData
, SheetState aSheetState
,
1142 PendingLoad aPendingLoad
) {
1143 LOG(("css::Loader::LoadSheet"));
1144 MOZ_ASSERT(aLoadData
.mURI
, "Need a URI to load");
1145 MOZ_ASSERT(aLoadData
.mSheet
, "Need a sheet to load into");
1146 MOZ_ASSERT(aSheetState
!= SheetState::Complete
, "Why bother?");
1147 MOZ_ASSERT(!aLoadData
.mUseSystemPrincipal
|| aLoadData
.mSyncLoad
,
1148 "Shouldn't use system principal for async loads");
1150 LOG_URI(" Load from: '%s'", aLoadData
.mURI
);
1152 // If we're firing a pending load, this load is already accounted for the
1153 // first time it went through this function.
1154 if (aPendingLoad
== PendingLoad::No
) {
1155 if (aLoadData
.BlocksLoadEvent()) {
1156 IncrementOngoingLoadCount();
1159 // We technically never defer non-top-level sheets, so this condition could
1160 // be outside the branch, but conceptually it should be here.
1161 if (aLoadData
.mParentData
) {
1162 ++aLoadData
.mParentData
->mPendingChildren
;
1166 nsresult rv
= NS_OK
;
1168 if (!mDocument
&& !aLoadData
.mIsNonDocumentSheet
) {
1169 // No point starting the load; just release all the data and such.
1170 LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
1171 SheetComplete(aLoadData
, NS_BINDING_ABORTED
);
1172 return NS_BINDING_ABORTED
;
1175 SRIMetadata sriMetadata
;
1176 aLoadData
.mSheet
->GetIntegrity(sriMetadata
);
1178 if (aLoadData
.mSyncLoad
) {
1179 LOG((" Synchronous load"));
1180 MOZ_ASSERT(!aLoadData
.mObserver
, "Observer for a sync load?");
1181 MOZ_ASSERT(aSheetState
== SheetState::NeedsParser
,
1182 "Sync loads can't reuse existing async loads");
1184 // Create a StreamLoader instance to which we will feed
1185 // the data from the sync load. Do this before creating the
1186 // channel to make error recovery simpler.
1187 auto streamLoader
= MakeRefPtr
<StreamLoader
>(aLoadData
);
1190 net::PredictorLearn(aLoadData
.mURI
, mDocument
->GetDocumentURI(),
1191 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE
,
1195 nsSecurityFlags securityFlags
=
1196 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
|
1197 nsILoadInfo::SEC_ALLOW_CHROME
;
1199 nsContentPolicyType contentPolicyType
=
1200 aLoadData
.mPreloadKind
== StylePreloadKind::None
1201 ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1202 : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
;
1205 nsCOMPtr
<nsIChannel
> channel
;
1206 // Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both
1207 // a node and a principal.
1208 // This is because of a case where the node is the document being styled and
1209 // the principal is the stylesheet (perhaps from a different origin) that is
1210 // applying the styles.
1211 if (aLoadData
.mRequestingNode
) {
1212 rv
= NS_NewChannelWithTriggeringPrincipal(
1213 getter_AddRefs(channel
), aLoadData
.mURI
, aLoadData
.mRequestingNode
,
1214 aLoadData
.mTriggeringPrincipal
, securityFlags
, contentPolicyType
);
1216 MOZ_ASSERT(aLoadData
.mTriggeringPrincipal
->Equals(LoaderPrincipal()));
1217 auto result
= URLPreloader::ReadURI(aLoadData
.mURI
);
1218 if (result
.isOk()) {
1219 nsCOMPtr
<nsIInputStream
> stream
;
1221 NS_NewCStringInputStream(getter_AddRefs(stream
), result
.unwrap()));
1223 rv
= NS_NewInputStreamChannel(
1224 getter_AddRefs(channel
), aLoadData
.mURI
, stream
.forget(),
1225 aLoadData
.mTriggeringPrincipal
, securityFlags
, contentPolicyType
);
1227 rv
= NS_NewChannel(getter_AddRefs(channel
), aLoadData
.mURI
,
1228 aLoadData
.mTriggeringPrincipal
, securityFlags
,
1232 if (NS_FAILED(rv
)) {
1233 LOG_ERROR((" Failed to create channel"));
1234 streamLoader
->ChannelOpenFailed(rv
);
1235 SheetComplete(aLoadData
, rv
);
1239 // snapshot the nonce at load start time for performing CSP checks
1240 if (contentPolicyType
== nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
) {
1241 if (aLoadData
.mRequestingNode
) {
1242 // TODO(bug 1607009) move to SheetLoadData
1243 nsString
* cspNonce
= static_cast<nsString
*>(
1244 aLoadData
.mRequestingNode
->GetProperty(nsGkAtoms::nonce
));
1246 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
1247 loadInfo
->SetCspNonce(*cspNonce
);
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
);
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 SheetLoadDataHashKey
key(aLoadData
);
1277 auto preloadKey
= PreloadHashKey::CreateAsStyle(aLoadData
);
1278 bool coalescedLoad
= false;
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
->DeferSheetLoad(key
, aLoadData
);
1291 if ((coalescedLoad
= mSheets
->CoalesceLoad(key
, aLoadData
, aSheetState
))) {
1292 if (aSheetState
== SheetState::Pending
) {
1293 ++mPendingLoadCount
;
1299 aLoadData
.NotifyOpen(preloadKey
, mDocument
, aLoadData
.IsLinkRelPreload());
1300 if (coalescedLoad
) {
1301 // All done here; once the load completes we'll be marked complete
1306 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1307 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
1309 loadGroup
= mDocument
->GetDocumentLoadGroup();
1310 // load for a document with no loadgrup indicates that something is
1311 // completely bogus, let's bail out early.
1313 LOG_ERROR((" Failed to query loadGroup from document"));
1314 SheetComplete(aLoadData
, NS_ERROR_UNEXPECTED
);
1315 return NS_ERROR_UNEXPECTED
;
1318 cookieJarSettings
= mDocument
->CookieJarSettings();
1322 AutoRestore
<bool> syncCallbackGuard(mSyncCallback
);
1323 mSyncCallback
= true;
1326 CORSMode ourCORSMode
= aLoadData
.mSheet
->GetCORSMode();
1327 nsSecurityFlags securityFlags
=
1328 ourCORSMode
== CORS_NONE
1329 ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
1330 : nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
;
1331 if (ourCORSMode
== CORS_ANONYMOUS
) {
1332 securityFlags
|= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN
;
1333 } else if (ourCORSMode
== CORS_USE_CREDENTIALS
) {
1334 securityFlags
|= nsILoadInfo::SEC_COOKIES_INCLUDE
;
1336 securityFlags
|= nsILoadInfo::SEC_ALLOW_CHROME
;
1338 nsContentPolicyType contentPolicyType
=
1339 aLoadData
.mPreloadKind
== StylePreloadKind::None
1340 ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1341 : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
;
1343 nsCOMPtr
<nsIChannel
> channel
;
1344 // Note we are calling NS_NewChannelWithTriggeringPrincipal here with a node
1345 // and a principal. This is because of a case where the node is the document
1346 // being styled and the principal is the stylesheet (perhaps from a different
1347 // origin) that is applying the styles.
1348 if (aLoadData
.mRequestingNode
) {
1349 rv
= NS_NewChannelWithTriggeringPrincipal(
1350 getter_AddRefs(channel
), aLoadData
.mURI
, aLoadData
.mRequestingNode
,
1351 aLoadData
.mTriggeringPrincipal
, securityFlags
, contentPolicyType
,
1352 /* PerformanceStorage */ nullptr, loadGroup
);
1354 MOZ_ASSERT(aLoadData
.mTriggeringPrincipal
->Equals(LoaderPrincipal()));
1355 rv
= NS_NewChannel(getter_AddRefs(channel
), aLoadData
.mURI
,
1356 aLoadData
.mTriggeringPrincipal
, securityFlags
,
1357 contentPolicyType
, cookieJarSettings
,
1358 /* aPerformanceStorage */ nullptr, loadGroup
);
1361 if (NS_FAILED(rv
)) {
1362 LOG_ERROR((" Failed to create channel"));
1363 SheetComplete(aLoadData
, rv
);
1367 // snapshot the nonce at load start time for performing CSP checks
1368 if (contentPolicyType
== nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
) {
1369 if (aLoadData
.mRequestingNode
) {
1370 // TODO(bug 1607009) move to SheetLoadData
1371 nsString
* cspNonce
= static_cast<nsString
*>(
1372 aLoadData
.mRequestingNode
->GetProperty(nsGkAtoms::nonce
));
1374 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
1375 loadInfo
->SetCspNonce(*cspNonce
);
1380 if (!aLoadData
.ShouldDefer()) {
1381 if (nsCOMPtr
<nsIClassOfService
> cos
= do_QueryInterface(channel
)) {
1382 cos
->AddClassFlags(nsIClassOfService::Leader
);
1384 if (aLoadData
.IsLinkRelPreload()) {
1385 SheetLoadData::PrioritizeAsPreload(channel
);
1386 SheetLoadData::AddLoadBackgroundFlag(channel
);
1390 if (nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(channel
)) {
1391 if (nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= aLoadData
.ReferrerInfo()) {
1392 rv
= httpChannel
->SetReferrerInfo(referrerInfo
);
1393 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
1396 nsCOMPtr
<nsIHttpChannelInternal
> internalChannel
=
1397 do_QueryInterface(httpChannel
);
1398 if (internalChannel
) {
1399 rv
= internalChannel
->SetIntegrityMetadata(
1400 sriMetadata
.GetIntegrityString());
1401 NS_ENSURE_SUCCESS(rv
, rv
);
1404 // Set the initiator type
1405 if (nsCOMPtr
<nsITimedChannel
> timedChannel
=
1406 do_QueryInterface(httpChannel
)) {
1407 if (aLoadData
.mParentData
) {
1408 timedChannel
->SetInitiatorType(u
"css"_ns
);
1410 // This is a child sheet load.
1412 // The resource timing of the sub-resources that a document loads
1413 // should normally be reported to the document. One exception is any
1414 // sub-resources of any cross-origin resources that are loaded. We
1415 // don't mind reporting timing data for a direct child cross-origin
1416 // resource since the resource that linked to it (and hence potentially
1417 // anything in that parent origin) is aware that the cross-origin
1418 // resources is to be loaded. However, we do not want to report
1419 // timings for any sub-resources that a cross-origin resource may load
1420 // since that obviously leaks information about what the cross-origin
1421 // resource loads, which is bad.
1423 // In addition to checking whether we're an immediate child resource of
1424 // a cross-origin resource (by checking if mIsCrossOriginNoCORS is set
1425 // to true on our parent), we also check our parent to see whether it
1426 // itself is a sub-resource of a cross-origin resource by checking
1427 // mBlockResourceTiming. If that is set then we too are such a
1428 // sub-resource and so we set the flag on ourself too to propagate it
1430 if (aLoadData
.mParentData
->mIsCrossOriginNoCORS
||
1431 aLoadData
.mParentData
->mBlockResourceTiming
) {
1432 // Set a flag so any other stylesheet triggered by this one will
1434 aLoadData
.mBlockResourceTiming
= true;
1436 // Mark the channel so PerformanceMainThread::AddEntry will not
1437 // report the resource.
1438 timedChannel
->SetReportResourceTiming(false);
1442 timedChannel
->SetInitiatorType(u
"link"_ns
);
1447 // Now tell the channel we expect text/css data back.... We do
1448 // this before opening it, so it's only treated as a hint.
1449 channel
->SetContentType("text/css"_ns
);
1451 // We don't have to hold on to the stream loader. The ownership
1452 // model is: Necko owns the stream loader, which owns the load data,
1454 auto streamLoader
= MakeRefPtr
<StreamLoader
>(aLoadData
);
1456 net::PredictorLearn(aLoadData
.mURI
, mDocument
->GetDocumentURI(),
1457 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE
, mDocument
);
1460 rv
= channel
->AsyncOpen(streamLoader
);
1461 if (NS_FAILED(rv
)) {
1462 LOG_ERROR((" Failed to create stream loader"));
1463 streamLoader
->ChannelOpenFailed(rv
);
1464 // NOTE: NotifyStop will be done in SheetComplete -> NotifyObservers.
1465 aLoadData
.NotifyStart(channel
);
1466 SheetComplete(aLoadData
, rv
);
1470 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1471 if (nsCOMPtr
<nsIHttpChannelInternal
> hci
= do_QueryInterface(channel
)) {
1472 hci
->DoDiagnosticAssertWhenOnStopNotCalledOnDestroy();
1477 mSheets
->LoadStarted(key
, aLoadData
);
1483 * ParseSheet handles parsing the data stream.
1485 Loader::Completed
Loader::ParseSheet(const nsACString
& aBytes
,
1486 SheetLoadData
& aLoadData
,
1487 AllowAsyncParse aAllowAsync
) {
1488 LOG(("css::Loader::ParseSheet"));
1489 if (aLoadData
.mURI
) {
1490 LOG_URI(" Load succeeded for URI: '%s', parsing", aLoadData
.mURI
);
1492 AUTO_PROFILER_LABEL("css::Loader::ParseSheet", LAYOUT_CSSParsing
);
1494 ++mParsedSheetCount
;
1496 aLoadData
.mIsBeingParsed
= true;
1498 StyleSheet
* sheet
= aLoadData
.mSheet
;
1501 // Some cases, like inline style and UA stylesheets, need to be parsed
1502 // synchronously. The former may trigger child loads, the latter must not.
1503 if (aLoadData
.mSyncLoad
|| aAllowAsync
== AllowAsyncParse::No
) {
1504 sheet
->ParseSheetSync(this, aBytes
, &aLoadData
, aLoadData
.mLineNumber
);
1505 aLoadData
.mIsBeingParsed
= false;
1507 bool noPendingChildren
= aLoadData
.mPendingChildren
== 0;
1508 MOZ_ASSERT_IF(aLoadData
.mSyncLoad
, noPendingChildren
);
1509 if (noPendingChildren
) {
1510 SheetComplete(aLoadData
, NS_OK
);
1511 return Completed::Yes
;
1513 return Completed::No
;
1516 // This parse does not need to be synchronous. \o/
1518 // Note that load is already blocked from IncrementOngoingLoadCount(), and
1519 // will be unblocked from SheetFinishedParsingAsync which will end up in
1520 // NotifyObservers as needed.
1521 nsCOMPtr
<nsISerialEventTarget
> target
= DispatchTarget();
1522 sheet
->ParseSheet(*this, aBytes
, aLoadData
)
1525 [loadData
= RefPtr
<SheetLoadData
>(&aLoadData
)](bool aDummy
) {
1526 MOZ_ASSERT(NS_IsMainThread());
1527 loadData
->SheetFinishedParsingAsync();
1529 [] { MOZ_CRASH("rejected parse promise"); });
1530 return Completed::No
;
1533 void Loader::NotifyObservers(SheetLoadData
& aData
, nsresult aStatus
) {
1534 RecordUseCountersIfNeeded(mDocument
, aData
.mUseCounters
.get());
1536 mLoadsPerformed
.PutEntry(SheetLoadDataHashKey(aData
));
1537 aData
.NotifyStop(aStatus
);
1538 // NOTE(emilio): This needs to happen before notifying observers, as
1539 // FontFaceSet for example checks for pending sheet loads from the
1540 // StyleSheetLoaded callback.
1541 if (aData
.BlocksLoadEvent()) {
1542 DecrementOngoingLoadCount();
1546 if (aData
.mMustNotify
) {
1547 if (aData
.mObserver
) {
1548 LOG((" Notifying observer %p for data %p. deferred: %d",
1549 aData
.mObserver
.get(), &aData
, aData
.ShouldDefer()));
1550 aData
.mObserver
->StyleSheetLoaded(aData
.mSheet
, aData
.ShouldDefer(),
1554 for (nsCOMPtr
<nsICSSLoaderObserver
> obs
: mObservers
.ForwardRange()) {
1555 LOG((" Notifying global observer %p for data %p. deferred: %d",
1556 obs
.get(), &aData
, aData
.ShouldDefer()));
1557 obs
->StyleSheetLoaded(aData
.mSheet
, aData
.ShouldDefer(), aStatus
);
1561 if (mPendingLoadCount
&& mPendingLoadCount
== mOngoingLoadCount
) {
1562 LOG((" No more loading sheets; starting deferred loads"));
1563 StartDeferredLoads();
1568 * SheetComplete is the do-it-all cleanup function. It removes the
1569 * load data from the "loading" hashtable, adds the sheet to the
1570 * "completed" hashtable, massages the XUL cache, handles siblings of
1571 * the load data (other loads for the same URI), handles unblocking
1572 * blocked parent loads as needed, and most importantly calls
1573 * NS_RELEASE on the load data to destroy the whole mess.
1575 void Loader::SheetComplete(SheetLoadData
& aLoadData
, nsresult aStatus
) {
1576 LOG(("css::Loader::SheetComplete, status: 0x%" PRIx32
,
1577 static_cast<uint32_t>(aStatus
)));
1578 SharedStyleSheetCache::LoadCompleted(mSheets
.get(), aLoadData
, aStatus
);
1582 void Loader::MarkLoadTreeFailed(SheetLoadData
& aLoadData
,
1583 Loader
* aOnlyForLoader
) {
1584 if (aLoadData
.mURI
) {
1585 LOG_URI(" Load failed: '%s'", aLoadData
.mURI
);
1588 SheetLoadData
* data
= &aLoadData
;
1590 if (!aOnlyForLoader
|| aOnlyForLoader
== data
->mLoader
) {
1591 data
->mLoadFailed
= true;
1592 data
->mSheet
->MaybeRejectReplacePromise();
1595 if (data
->mParentData
) {
1596 MarkLoadTreeFailed(*data
->mParentData
, aOnlyForLoader
);
1603 RefPtr
<StyleSheet
> Loader::LookupInlineSheetInCache(const nsAString
& aBuffer
) {
1604 auto result
= mInlineSheets
.Lookup(aBuffer
);
1608 if (result
.Data()->HasModifiedRules()) {
1609 // Remove it now that we know that we're never going to use this stylesheet
1614 return result
.Data()->Clone(nullptr, nullptr, nullptr, nullptr);
1617 void Loader::MaybeNotifyPreloadUsed(SheetLoadData
& aData
) {
1622 auto key
= PreloadHashKey::CreateAsStyle(aData
);
1623 RefPtr
<PreloaderBase
> preload
= mDocument
->Preloads().LookupPreload(key
);
1628 preload
->NotifyUsage();
1631 Result
<Loader::LoadSheetResult
, nsresult
> Loader::LoadInlineStyle(
1632 const SheetInfo
& aInfo
, const nsAString
& aBuffer
, uint32_t aLineNumber
,
1633 nsICSSLoaderObserver
* aObserver
) {
1634 LOG(("css::Loader::LoadInlineStyle"));
1635 MOZ_ASSERT(aInfo
.mContent
);
1638 LOG_WARN((" Not enabled"));
1639 return Err(NS_ERROR_NOT_AVAILABLE
);
1643 return Err(NS_ERROR_NOT_INITIALIZED
);
1646 MOZ_ASSERT(LinkStyle::FromNodeOrNull(aInfo
.mContent
),
1647 "Element is not a style linking element!");
1649 // Since we're not planning to load a URI, no need to hand a principal to the
1650 // load data or to CreateSheet().
1652 // Check IsAlternateSheet now, since it can mutate our document.
1653 auto isAlternate
= IsAlternateSheet(aInfo
.mTitle
, aInfo
.mHasAlternateRel
);
1654 LOG((" Sheet is alternate: %d", static_cast<int>(isAlternate
)));
1656 // Use the document's base URL so that @import in the inline sheet picks up
1658 nsIURI
* baseURI
= aInfo
.mContent
->GetBaseURI();
1659 nsIURI
* sheetURI
= aInfo
.mContent
->OwnerDoc()->GetDocumentURI();
1660 nsIURI
* originalURI
= nullptr;
1662 MOZ_ASSERT(aInfo
.mIntegrity
.IsEmpty());
1664 nsIPrincipal
* loadingPrincipal
= LoaderPrincipal();
1665 nsIPrincipal
* principal
= aInfo
.mTriggeringPrincipal
1666 ? aInfo
.mTriggeringPrincipal
.get()
1669 // We only cache sheets if in shadow trees, since regular document sheets are
1670 // likely to be unique.
1671 const bool isWorthCaching
= aInfo
.mContent
->IsInShadowTree();
1672 RefPtr
<StyleSheet
> sheet
;
1673 if (isWorthCaching
) {
1674 sheet
= LookupInlineSheetInCache(aBuffer
);
1676 const bool sheetFromCache
= !!sheet
;
1678 sheet
= MakeRefPtr
<StyleSheet
>(eAuthorSheetFeatures
, aInfo
.mCORSMode
,
1680 sheet
->SetURIs(sheetURI
, originalURI
, baseURI
);
1681 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
1682 ReferrerInfo::CreateForInternalCSSResources(aInfo
.mContent
->OwnerDoc());
1683 sheet
->SetReferrerInfo(referrerInfo
);
1685 nsIPrincipal
* sheetPrincipal
= principal
;
1686 if (aInfo
.mTriggeringPrincipal
) {
1687 // The triggering principal may be an expanded principal, which is safe to
1688 // use for URL security checks, but not as the loader principal for a
1689 // stylesheet. So treat this as principal inheritance, and downgrade if
1692 BasePrincipal::Cast(aInfo
.mTriggeringPrincipal
)->PrincipalToInherit();
1695 // We never actually load this, so just set its principal directly
1696 sheet
->SetPrincipal(sheetPrincipal
);
1699 auto matched
= PrepareSheet(*sheet
, aInfo
.mTitle
, aInfo
.mMedia
, nullptr,
1700 isAlternate
, aInfo
.mIsExplicitlyEnabled
);
1702 if (auto* linkStyle
= LinkStyle::FromNodeOrNull(aInfo
.mContent
)) {
1703 linkStyle
->SetStyleSheet(sheet
);
1705 if (sheet
->IsComplete()) {
1706 InsertSheetInTree(*sheet
, aInfo
.mContent
);
1709 Completed completed
;
1710 if (sheetFromCache
) {
1711 MOZ_ASSERT(sheet
->IsComplete());
1712 completed
= Completed::Yes
;
1714 auto data
= MakeRefPtr
<SheetLoadData
>(
1715 this, aInfo
.mTitle
, nullptr, sheet
, false, aInfo
.mContent
, isAlternate
,
1716 matched
, StylePreloadKind::None
, aObserver
, principal
,
1717 aInfo
.mReferrerInfo
, aInfo
.mContent
);
1718 data
->mLineNumber
= aLineNumber
;
1720 // Parse completion releases the load data.
1722 // Note that we need to parse synchronously, since the web expects that the
1723 // effects of inline stylesheets are visible immediately (aside from
1725 NS_ConvertUTF16toUTF8
utf8(aBuffer
);
1726 completed
= ParseSheet(utf8
, *data
, AllowAsyncParse::No
);
1727 if (completed
== Completed::Yes
) {
1728 // TODO(emilio): Try to cache sheets with @import rules, maybe?
1729 if (isWorthCaching
) {
1730 mInlineSheets
.InsertOrUpdate(aBuffer
, std::move(sheet
));
1733 data
->mMustNotify
= true;
1737 return LoadSheetResult
{completed
, isAlternate
, matched
};
1740 Result
<Loader::LoadSheetResult
, nsresult
> Loader::LoadStyleLink(
1741 const SheetInfo
& aInfo
, nsICSSLoaderObserver
* aObserver
) {
1742 MOZ_ASSERT(aInfo
.mURI
, "Must have URL to load");
1743 LOG(("css::Loader::LoadStyleLink"));
1744 LOG_URI(" Link uri: '%s'", aInfo
.mURI
);
1745 LOG((" Link title: '%s'", NS_ConvertUTF16toUTF8(aInfo
.mTitle
).get()));
1746 LOG((" Link media: '%s'", NS_ConvertUTF16toUTF8(aInfo
.mMedia
).get()));
1747 LOG((" Link alternate rel: %d", aInfo
.mHasAlternateRel
));
1750 LOG_WARN((" Not enabled"));
1751 return Err(NS_ERROR_NOT_AVAILABLE
);
1755 return Err(NS_ERROR_NOT_INITIALIZED
);
1758 MOZ_ASSERT_IF(aInfo
.mContent
,
1759 aInfo
.mContent
->NodePrincipal() == mDocument
->NodePrincipal());
1760 nsIPrincipal
* loadingPrincipal
= LoaderPrincipal();
1761 nsIPrincipal
* principal
= aInfo
.mTriggeringPrincipal
1762 ? aInfo
.mTriggeringPrincipal
.get()
1765 nsINode
* context
= aInfo
.mContent
;
1767 context
= mDocument
;
1770 bool syncLoad
= aInfo
.mContent
&& aInfo
.mContent
->IsInUAWidget() &&
1771 IsChromeURI(aInfo
.mURI
);
1772 LOG((" Link sync load: '%s'", syncLoad
? "true" : "false"));
1773 MOZ_ASSERT_IF(syncLoad
, !aObserver
);
1776 CheckContentPolicy(loadingPrincipal
, principal
, aInfo
.mURI
, context
,
1777 aInfo
.mNonce
, StylePreloadKind::None
);
1778 if (NS_WARN_IF(NS_FAILED(rv
))) {
1779 // Don't fire the error event if our document is loaded as data. We're
1780 // supposed to not even try to do loads in that case... Unfortunately, we
1781 // implement that via nsDataDocumentContentPolicy, which doesn't have a good
1782 // way to communicate back to us that _it_ is the thing that blocked the
1784 if (aInfo
.mContent
&& !mDocument
->IsLoadedAsData()) {
1785 // Fire an async error event on it.
1786 RefPtr
<AsyncEventDispatcher
> loadBlockingAsyncDispatcher
=
1787 new LoadBlockingAsyncEventDispatcher(aInfo
.mContent
, u
"error"_ns
,
1789 ChromeOnlyDispatch::eNo
);
1790 loadBlockingAsyncDispatcher
->PostDOMEvent();
1795 // Check IsAlternateSheet now, since it can mutate our document and make
1796 // pending sheets go to the non-pending state.
1797 auto isAlternate
= IsAlternateSheet(aInfo
.mTitle
, aInfo
.mHasAlternateRel
);
1798 auto [sheet
, state
] = CreateSheet(aInfo
, eAuthorSheetFeatures
, syncLoad
,
1799 StylePreloadKind::None
);
1801 LOG((" Sheet is alternate: %d", static_cast<int>(isAlternate
)));
1803 auto matched
= PrepareSheet(*sheet
, aInfo
.mTitle
, aInfo
.mMedia
, nullptr,
1804 isAlternate
, aInfo
.mIsExplicitlyEnabled
);
1806 if (auto* linkStyle
= LinkStyle::FromNodeOrNull(aInfo
.mContent
)) {
1807 linkStyle
->SetStyleSheet(sheet
);
1809 if (sheet
->IsComplete()) {
1810 InsertSheetInTree(*sheet
, aInfo
.mContent
);
1813 // We may get here with no content for Link: headers for example.
1814 MOZ_ASSERT(!aInfo
.mContent
|| LinkStyle::FromNode(*aInfo
.mContent
),
1815 "If there is any node, it should be a LinkStyle");
1816 auto data
= MakeRefPtr
<SheetLoadData
>(
1817 this, aInfo
.mTitle
, aInfo
.mURI
, sheet
, syncLoad
, aInfo
.mContent
,
1818 isAlternate
, matched
, StylePreloadKind::None
, aObserver
, principal
,
1819 aInfo
.mReferrerInfo
, context
);
1821 MaybeNotifyPreloadUsed(*data
);
1823 if (state
== SheetState::Complete
) {
1824 LOG((" Sheet already complete: 0x%p", sheet
.get()));
1825 if (aObserver
|| !mObservers
.IsEmpty() || aInfo
.mContent
) {
1826 rv
= PostLoadEvent(std::move(data
));
1827 if (NS_FAILED(rv
)) {
1831 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1832 // We don't have to notify anyone of this load, as it was complete, so
1833 // drop it intentionally.
1834 data
->mIntentionallyDropped
= true;
1838 // The load hasn't been completed yet, will be done in PostLoadEvent.
1839 return LoadSheetResult
{Completed::No
, isAlternate
, matched
};
1842 // Now we need to actually load it.
1843 auto result
= LoadSheetResult
{Completed::No
, isAlternate
, matched
};
1845 MOZ_ASSERT(result
.ShouldBlock() == !data
->ShouldDefer(),
1846 "These should better match!");
1848 // Load completion will free the data
1849 rv
= LoadSheet(*data
, state
);
1850 if (NS_FAILED(rv
)) {
1855 data
->mMustNotify
= true;
1860 static bool HaveAncestorDataWithURI(SheetLoadData
& aData
, nsIURI
* aURI
) {
1862 // Inline style; this won't have any ancestors
1863 MOZ_ASSERT(!aData
.mParentData
, "How does inline style have a parent?");
1868 if (NS_FAILED(aData
.mURI
->Equals(aURI
, &equal
)) || equal
) {
1872 // Datas down the mNext chain have the same URI as aData, so we
1873 // don't have to compare to them. But they might have different
1874 // parents, and we have to check all of those.
1875 SheetLoadData
* data
= &aData
;
1877 if (data
->mParentData
&&
1878 HaveAncestorDataWithURI(*data
->mParentData
, aURI
)) {
1888 nsresult
Loader::LoadChildSheet(StyleSheet
& aParentSheet
,
1889 SheetLoadData
* aParentData
, nsIURI
* aURL
,
1890 dom::MediaList
* aMedia
,
1891 LoaderReusableStyleSheets
* aReusableSheets
) {
1892 LOG(("css::Loader::LoadChildSheet"));
1893 MOZ_ASSERT(aURL
, "Must have a URI to load");
1896 LOG_WARN((" Not enabled"));
1897 return NS_ERROR_NOT_AVAILABLE
;
1900 LOG_URI(" Child uri: '%s'", aURL
);
1902 nsCOMPtr
<nsINode
> owningNode
;
1904 // Check for an associated document or shadow root: if none, don't bother
1905 // walking up the parent sheets.
1906 if (aParentSheet
.GetAssociatedDocumentOrShadowRoot()) {
1907 StyleSheet
* topSheet
= &aParentSheet
;
1908 while (StyleSheet
* parent
= topSheet
->GetParentSheet()) {
1911 owningNode
= topSheet
->GetOwnerNode();
1914 nsINode
* context
= nullptr;
1916 context
= owningNode
;
1917 MOZ_ASSERT(LoaderPrincipal() == owningNode
->NodePrincipal());
1918 } else if (mDocument
) {
1919 context
= mDocument
;
1922 nsIPrincipal
* principal
= aParentSheet
.Principal();
1923 nsresult rv
= CheckContentPolicy(LoaderPrincipal(), principal
, aURL
, context
,
1924 u
""_ns
, StylePreloadKind::None
);
1925 if (NS_WARN_IF(NS_FAILED(rv
))) {
1927 MarkLoadTreeFailed(*aParentData
);
1932 nsCOMPtr
<nsICSSLoaderObserver
> observer
;
1935 LOG((" Have a parent load"));
1937 if (HaveAncestorDataWithURI(*aParentData
, aURL
)) {
1938 // Houston, we have a loop, blow off this child and pretend this never
1940 LOG_ERROR((" @import cycle detected, dropping load"));
1944 NS_ASSERTION(aParentData
->mSheet
== &aParentSheet
,
1945 "Unexpected call to LoadChildSheet");
1947 LOG((" No parent load; must be CSSOM"));
1948 // No parent load data, so the sheet will need to be notified when
1949 // we finish, if it can be, if we do the load asynchronously.
1950 observer
= &aParentSheet
;
1953 // Now that we know it's safe to load this (passes security check and not a
1955 RefPtr
<StyleSheet
> sheet
;
1957 if (aReusableSheets
&& aReusableSheets
->FindReusableStyleSheet(aURL
, sheet
)) {
1958 state
= SheetState::Complete
;
1960 // For now, use CORS_NONE for child sheets
1961 std::tie(sheet
, state
) = CreateSheet(
1962 aURL
, nullptr, principal
, aParentSheet
.ParsingMode(), CORS_NONE
,
1963 aParentData
? aParentData
->mEncoding
: nullptr,
1964 u
""_ns
, // integrity is only checked on main sheet
1965 aParentData
&& aParentData
->mSyncLoad
, StylePreloadKind::None
);
1966 PrepareSheet(*sheet
, u
""_ns
, u
""_ns
, aMedia
, IsAlternate::No
,
1967 IsExplicitlyEnabled::No
);
1971 InsertChildSheet(*sheet
, aParentSheet
);
1973 auto data
= MakeRefPtr
<SheetLoadData
>(
1974 this, aURL
, sheet
, aParentData
, observer
, principal
,
1975 aParentSheet
.GetReferrerInfo(), context
);
1977 MaybeNotifyPreloadUsed(*data
);
1979 if (state
== SheetState::Complete
) {
1980 LOG((" Sheet already complete"));
1981 // We're completely done. No need to notify, even, since the
1982 // @import rule addition/modification will trigger the right style
1983 // changes automatically.
1984 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1985 data
->mIntentionallyDropped
= true;
1990 bool syncLoad
= data
->mSyncLoad
;
1992 // Load completion will release the data
1993 rv
= LoadSheet(*data
, state
);
1994 NS_ENSURE_SUCCESS(rv
, rv
);
1997 data
->mMustNotify
= true;
2002 Result
<RefPtr
<StyleSheet
>, nsresult
> Loader::LoadSheetSync(
2003 nsIURI
* aURL
, SheetParsingMode aParsingMode
,
2004 UseSystemPrincipal aUseSystemPrincipal
) {
2005 LOG(("css::Loader::LoadSheetSync"));
2006 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= new ReferrerInfo(nullptr);
2007 return InternalLoadNonDocumentSheet(
2008 aURL
, StylePreloadKind::None
, aParsingMode
, aUseSystemPrincipal
, nullptr,
2009 referrerInfo
, nullptr, CORS_NONE
, u
""_ns
);
2012 Result
<RefPtr
<StyleSheet
>, nsresult
> Loader::LoadSheet(
2013 nsIURI
* aURI
, SheetParsingMode aParsingMode
,
2014 UseSystemPrincipal aUseSystemPrincipal
, nsICSSLoaderObserver
* aObserver
) {
2015 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= new ReferrerInfo(nullptr);
2016 return InternalLoadNonDocumentSheet(
2017 aURI
, StylePreloadKind::None
, aParsingMode
, aUseSystemPrincipal
, nullptr,
2018 referrerInfo
, aObserver
, CORS_NONE
, u
""_ns
);
2021 Result
<RefPtr
<StyleSheet
>, nsresult
> Loader::LoadSheet(
2022 nsIURI
* aURL
, StylePreloadKind aPreloadKind
,
2023 const Encoding
* aPreloadEncoding
, nsIReferrerInfo
* aReferrerInfo
,
2024 nsICSSLoaderObserver
* aObserver
, CORSMode aCORSMode
,
2025 const nsAString
& aIntegrity
) {
2026 LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
2027 return InternalLoadNonDocumentSheet(
2028 aURL
, aPreloadKind
, eAuthorSheetFeatures
, UseSystemPrincipal::No
,
2029 aPreloadEncoding
, aReferrerInfo
, aObserver
, aCORSMode
, aIntegrity
);
2032 Result
<RefPtr
<StyleSheet
>, nsresult
> Loader::InternalLoadNonDocumentSheet(
2033 nsIURI
* aURL
, StylePreloadKind aPreloadKind
, SheetParsingMode aParsingMode
,
2034 UseSystemPrincipal aUseSystemPrincipal
, const Encoding
* aPreloadEncoding
,
2035 nsIReferrerInfo
* aReferrerInfo
, nsICSSLoaderObserver
* aObserver
,
2036 CORSMode aCORSMode
, const nsAString
& aIntegrity
) {
2037 MOZ_ASSERT(aURL
, "Must have a URI to load");
2038 MOZ_ASSERT(aUseSystemPrincipal
== UseSystemPrincipal::No
|| !aObserver
,
2039 "Shouldn't load system-principal sheets async");
2040 MOZ_ASSERT(aReferrerInfo
, "Must have referrerInfo");
2042 LOG_URI(" Non-document sheet uri: '%s'", aURL
);
2045 LOG_WARN((" Not enabled"));
2046 return Err(NS_ERROR_NOT_AVAILABLE
);
2049 nsIPrincipal
* loadingPrincipal
= LoaderPrincipal();
2050 nsIPrincipal
* triggeringPrincipal
= loadingPrincipal
;
2051 nsresult rv
= CheckContentPolicy(loadingPrincipal
, triggeringPrincipal
, aURL
,
2052 mDocument
, u
""_ns
, aPreloadKind
);
2053 if (NS_FAILED(rv
)) {
2057 bool syncLoad
= !aObserver
;
2058 auto [sheet
, state
] =
2059 CreateSheet(aURL
, nullptr, triggeringPrincipal
, aParsingMode
, aCORSMode
,
2060 aPreloadEncoding
, aIntegrity
, syncLoad
, aPreloadKind
);
2062 PrepareSheet(*sheet
, u
""_ns
, u
""_ns
, nullptr, IsAlternate::No
,
2063 IsExplicitlyEnabled::No
);
2065 auto data
= MakeRefPtr
<SheetLoadData
>(
2066 this, aURL
, sheet
, syncLoad
, aUseSystemPrincipal
, aPreloadKind
,
2067 aPreloadEncoding
, aObserver
, triggeringPrincipal
, aReferrerInfo
,
2069 if (state
== SheetState::Complete
) {
2070 LOG((" Sheet already complete"));
2071 if (aObserver
|| !mObservers
.IsEmpty()) {
2072 rv
= PostLoadEvent(std::move(data
));
2073 if (NS_FAILED(rv
)) {
2077 // We don't have to notify anyone of this load, as it was complete, so
2078 // drop it intentionally.
2079 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2080 data
->mIntentionallyDropped
= true;
2086 rv
= LoadSheet(*data
, state
);
2087 if (NS_FAILED(rv
)) {
2091 data
->mMustNotify
= true;
2096 nsresult
Loader::PostLoadEvent(RefPtr
<SheetLoadData
> aLoadData
) {
2097 LOG(("css::Loader::PostLoadEvent"));
2098 mPostedEvents
.AppendElement(aLoadData
);
2101 RefPtr
<SheetLoadData
> runnable(aLoadData
);
2103 rv
= mDocument
->Dispatch(TaskCategory::Other
, runnable
.forget());
2104 } else if (mDocGroup
) {
2105 rv
= mDocGroup
->Dispatch(TaskCategory::Other
, runnable
.forget());
2107 rv
= SchedulerGroup::Dispatch(TaskCategory::Other
, runnable
.forget());
2110 if (NS_FAILED(rv
)) {
2111 NS_WARNING("failed to dispatch stylesheet load event");
2112 mPostedEvents
.RemoveElement(aLoadData
);
2114 if (aLoadData
->BlocksLoadEvent()) {
2115 IncrementOngoingLoadCount();
2118 // We want to notify the observer for this data.
2119 aLoadData
->mMustNotify
= true;
2120 aLoadData
->mSheetAlreadyComplete
= true;
2122 // If we get to this code, aSheet loaded correctly at some point, so
2123 // we can just schedule a load event and don't need to touch the
2124 // data's mLoadFailed. Note that we do this here and not from
2125 // inside our SheetComplete so that we don't end up running the load
2127 MOZ_ASSERT(!aLoadData
->mLoadFailed
, "Why are we marked as failed?");
2128 aLoadData
->ScheduleLoadEventIfNeeded();
2134 void Loader::HandleLoadEvent(SheetLoadData
& aEvent
) {
2135 // XXXbz can't assert this yet.... May not have an observer because
2136 // we're unblocking the parser
2137 // NS_ASSERTION(aEvent->mObserver, "Must have observer");
2138 NS_ASSERTION(aEvent
.mSheet
, "Must have sheet");
2140 mPostedEvents
.RemoveElement(&aEvent
);
2141 SheetComplete(aEvent
, NS_OK
);
2144 void Loader::Stop() {
2146 mSheets
->CancelLoadsForLoader(*this);
2149 auto arr
= std::move(mPostedEvents
);
2150 for (auto& data
: arr
) {
2151 data
->mIsCancelled
= true;
2155 bool Loader::HasPendingLoads() { return mOngoingLoadCount
; }
2157 void Loader::AddObserver(nsICSSLoaderObserver
* aObserver
) {
2158 MOZ_ASSERT(aObserver
, "Must have observer");
2159 mObservers
.AppendElementUnlessExists(aObserver
);
2162 void Loader::RemoveObserver(nsICSSLoaderObserver
* aObserver
) {
2163 mObservers
.RemoveElement(aObserver
);
2166 void Loader::StartDeferredLoads() {
2167 if (mSheets
&& mPendingLoadCount
) {
2168 mSheets
->StartDeferredLoadsForLoader(
2169 *this, SharedStyleSheetCache::StartLoads::Always
);
2173 NS_IMPL_CYCLE_COLLECTION_CLASS(Loader
)
2175 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Loader
)
2176 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheets
);
2177 for (auto iter
= tmp
->mInlineSheets
.Iter(); !iter
.Done(); iter
.Next()) {
2178 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "Inline sheet cache in Loader");
2179 cb
.NoteXPCOMChild(iter
.UserData());
2181 for (nsCOMPtr
<nsICSSLoaderObserver
>& obs
: tmp
->mObservers
.ForwardRange()) {
2182 ImplCycleCollectionTraverse(cb
, obs
, "mozilla::css::Loader.mObservers");
2184 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2186 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Loader
)
2188 if (tmp
->mDocument
) {
2189 tmp
->DeregisterFromSheetCache();
2191 tmp
->mSheets
= nullptr;
2193 tmp
->mInlineSheets
.Clear();
2194 tmp
->mObservers
.Clear();
2195 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2197 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Loader
, AddRef
)
2198 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Loader
, Release
)
2200 size_t Loader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const {
2201 size_t n
= aMallocSizeOf(this);
2203 n
+= mObservers
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
2205 n
+= mInlineSheets
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
2206 for (auto iter
= mInlineSheets
.ConstIter(); !iter
.Done(); iter
.Next()) {
2207 n
+= iter
.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
2208 // If the sheet has a parent, then its parent will report it so we don't
2209 // have to worry about it here.
2210 const StyleSheet
* sheet
= iter
.UserData();
2211 MOZ_ASSERT(!sheet
->GetParentSheet(),
2212 "How did an @import rule end up here?");
2213 if (!sheet
->GetOwnerNode()) {
2214 n
+= sheet
->SizeOfIncludingThis(aMallocSizeOf
);
2218 // Measurement of the following members may be added later if DMD finds it is
2220 // - mPostedEvents: transient, and should be small
2222 // The following members aren't measured:
2223 // - mDocument, because it's a weak backpointer
2228 nsIPrincipal
* Loader::LoaderPrincipal() const {
2230 return mDocument
->NodePrincipal();
2232 // Loaders without a document do system loads.
2233 return nsContentUtils::GetSystemPrincipal();
2236 nsIPrincipal
* Loader::PartitionedPrincipal() const {
2237 if (mDocument
&& StaticPrefs::privacy_partition_network_state()) {
2238 return mDocument
->PartitionedPrincipal();
2240 return LoaderPrincipal();
2243 bool Loader::ShouldBypassCache() const {
2247 RefPtr
<nsILoadGroup
> lg
= mDocument
->GetDocumentLoadGroup();
2252 if (NS_FAILED(lg
->GetLoadFlags(&flags
))) {
2255 return flags
& (nsIRequest::LOAD_BYPASS_CACHE
|
2256 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE
);
2259 void Loader::BlockOnload() {
2261 mDocument
->BlockOnload();
2265 void Loader::UnblockOnload(bool aFireSync
) {
2267 mDocument
->UnblockOnload(aFireSync
);
2271 already_AddRefed
<nsISerialEventTarget
> Loader::DispatchTarget() {
2272 nsCOMPtr
<nsISerialEventTarget
> target
;
2274 // If you change this, you may want to change StyleSheet::Replace
2275 target
= mDocument
->EventTargetFor(TaskCategory::Other
);
2276 } else if (mDocGroup
) {
2277 target
= mDocGroup
->EventTargetFor(TaskCategory::Other
);
2279 target
= GetMainThreadSerialEventTarget();
2282 return target
.forget();
2286 } // namespace mozilla