1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* loading of CSS style sheets using the network APIs */
8 
9 #include "mozilla/css/Loader.h"
10 
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"
28 #include "nsCOMPtr.h"
29 #include "nsString.h"
30 #include "nsIContent.h"
31 #include "nsIContentInlines.h"
32 #include "nsICookieJarSettings.h"
33 #include "mozilla/dom/Document.h"
34 #include "nsIURI.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"
65 
66 #ifdef MOZ_XUL
67 #  include "nsXULPrototypeCache.h"
68 #endif
69 
70 #include "nsError.h"
71 
72 #include "mozilla/dom/SRICheck.h"
73 
74 #include "mozilla/Encoding.h"
75 
76 using namespace mozilla::dom;
77 
78 // 1024 bytes is specified in https://drafts.csswg.org/css-syntax/
79 #define SNIFFING_BUFFER_SIZE 1024
80 
81 /**
82  * OVERALL ARCHITECTURE
83  *
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()
95  *
96  * The detailed documentation for these functions is found with the
97  * function implementations.
98  *
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
104  *                     determination.
105  */
106 
107 extern mozilla::LazyLogModule sCssLoaderLog;
108 mozilla::LazyLogModule sCssLoaderLog("nsCSSLoader");
109 
110 static mozilla::LazyLogModule gSriPRLog("SRI");
111 
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)
116 
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()
124 
125 #define LOG_URI(format, uri)                      \
126   PR_BEGIN_MACRO                                  \
127   NS_ASSERTION(uri, "Logging null uri");          \
128   if (LOG_ENABLED()) {                            \
129     LOG((format, uri->GetSpecOrDefault().get())); \
130   }                                               \
131   PR_END_MACRO
132 
133 // And some convenience strings...
134 static const char* const gStateStrings[] = {"NeedsParser", "Pending", "Loading",
135                                             "Complete"};
136 
137 namespace mozilla {
138 
SheetLoadDataHashKey(const css::SheetLoadData & aLoadData)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);
150   MOZ_ASSERT(mURI);
151   MOZ_ASSERT(mPrincipal);
152   MOZ_ASSERT(mLoaderPrincipal);
153   MOZ_ASSERT(mPartitionPrincipal);
154   aLoadData.mSheet->GetIntegrity(mSRIMetadata);
155 }
156 
KeyEquals(const SheetLoadDataHashKey & aKey) const157 bool SheetLoadDataHashKey::KeyEquals(const SheetLoadDataHashKey& aKey) const {
158   {
159     bool eq;
160     if (NS_FAILED(mURI->Equals(aKey.mURI, &eq)) || !eq) {
161       return false;
162     }
163   }
164 
165   LOG_URI("KeyEquals(%s)\n", mURI);
166 
167   if (!mPrincipal->Equals(aKey.mPrincipal)) {
168     LOG((" > Principal mismatch\n"));
169     return false;
170   }
171 
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"));
179       return false;
180     }
181   }
182 
183   if (mCORSMode != aKey.mCORSMode) {
184     LOG((" > CORS mismatch\n"));
185     return false;
186   }
187 
188   if (mParsingMode != aKey.mParsingMode) {
189     LOG((" > Parsing mode mismatch\n"));
190     return false;
191   }
192 
193   if (mCompatMode != aKey.mCompatMode) {
194     LOG((" > Quirks mismatch\n"));
195     return false;
196   }
197 
198   // If encoding differs, then don't reuse the cache.
199   //
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,
202   // theoretically.
203   if (mEncodingGuess != aKey.mEncodingGuess) {
204     LOG((" > Encoding guess mismatch\n"));
205     return false;
206   }
207 
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;
218 
219     if (!consumerPreloadMetadata.CanTrustBeDelegatedTo(linkPreloadMetadata)) {
220       LOG((" > Preload SRI metadata mismatch\n"));
221       return false;
222     }
223   }
224 
225   return true;
226 }
227 
228 namespace css {
229 
GetFallbackEncoding(Loader & aLoader,nsINode * aOwningNode,const Encoding * aPreloadOrParentDataEncoding)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
235   // that loaded us
236   if (aOwningNode) {
237     nsAutoString label16;
238     LinkStyle::FromNode(*aOwningNode)->GetCharset(label16);
239     encoding = Encoding::ForLabel(label16);
240     if (encoding) {
241       return WrapNotNull(encoding);
242     }
243   }
244 
245   // Try preload or parent sheet encoding.
246   if (aPreloadOrParentDataEncoding) {
247     return WrapNotNull(aPreloadOrParentDataEncoding);
248   }
249 
250   if (auto* doc = aLoader.GetDocument()) {
251     // Use the document charset.
252     return doc->GetDocumentCharacterSet();
253   }
254 
255   return UTF_8_ENCODING;
256 }
257 
258 /********************************
259  * SheetLoadData implementation *
260  ********************************/
NS_IMPL_ISUPPORTS(SheetLoadData,nsIRunnable,nsIThreadObserver)261 NS_IMPL_ISUPPORTS(SheetLoadData, nsIRunnable, nsIThreadObserver)
262 
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)
269     : mLoader(aLoader),
270       mTitle(aTitle),
271       mEncoding(nullptr),
272       mURI(aURI),
273       mLineNumber(1),
274       mSheet(aSheet),
275       mNext(nullptr),
276       mPendingChildren(0),
277       mSyncLoad(aSyncLoad),
278       mIsNonDocumentSheet(false),
279       mIsChildSheet(aSheet->GetParentSheet()),
280       mIsLoading(false),
281       mIsBeingParsed(false),
282       mIsCancelled(false),
283       mMustNotify(false),
284       mWasAlternate(aIsAlternate == IsAlternate::Yes),
285       mMediaMatched(aMediaMatches == MediaMatched::Yes),
286       mUseSystemPrincipal(false),
287       mSheetAlreadyComplete(false),
288       mIsCrossOriginNoCORS(false),
289       mBlockResourceTiming(false),
290       mLoadFailed(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!");
303 }
304 
SheetLoadData(Loader * aLoader,nsIURI * aURI,StyleSheet * aSheet,SheetLoadData * aParentData,nsICSSLoaderObserver * aObserver,nsIPrincipal * aTriggeringPrincipal,nsIReferrerInfo * aReferrerInfo,nsINode * aRequestingNode)305 SheetLoadData::SheetLoadData(Loader* aLoader, nsIURI* aURI, StyleSheet* aSheet,
306                              SheetLoadData* aParentData,
307                              nsICSSLoaderObserver* aObserver,
308                              nsIPrincipal* aTriggeringPrincipal,
309                              nsIReferrerInfo* aReferrerInfo,
310                              nsINode* aRequestingNode)
311     : mLoader(aLoader),
312       mEncoding(nullptr),
313       mURI(aURI),
314       mLineNumber(1),
315       mSheet(aSheet),
316       mNext(nullptr),
317       mParentData(aParentData),
318       mPendingChildren(0),
319       mSyncLoad(aParentData && aParentData->mSyncLoad),
320       mIsNonDocumentSheet(aParentData && aParentData->mIsNonDocumentSheet),
321       mIsChildSheet(aSheet->GetParentSheet()),
322       mIsLoading(false),
323       mIsBeingParsed(false),
324       mIsCancelled(false),
325       mMustNotify(false),
326       mWasAlternate(false),
327       mMediaMatched(true),
328       mUseSystemPrincipal(aParentData && aParentData->mUseSystemPrincipal),
329       mSheetAlreadyComplete(false),
330       mIsCrossOriginNoCORS(false),
331       mBlockResourceTiming(false),
332       mLoadFailed(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);
347 }
348 
SheetLoadData(Loader * aLoader,nsIURI * aURI,StyleSheet * aSheet,bool aSyncLoad,UseSystemPrincipal aUseSystemPrincipal,StylePreloadKind aPreloadKind,const Encoding * aPreloadEncoding,nsICSSLoaderObserver * aObserver,nsIPrincipal * aTriggeringPrincipal,nsIReferrerInfo * aReferrerInfo,nsINode * aRequestingNode)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)
355     : mLoader(aLoader),
356       mEncoding(nullptr),
357       mURI(aURI),
358       mLineNumber(1),
359       mSheet(aSheet),
360       mNext(nullptr),
361       mPendingChildren(0),
362       mSyncLoad(aSyncLoad),
363       mIsNonDocumentSheet(true),
364       mIsChildSheet(false),
365       mIsLoading(false),
366       mIsBeingParsed(false),
367       mIsCancelled(false),
368       mMustNotify(false),
369       mWasAlternate(false),
370       mMediaMatched(true),
371       mUseSystemPrincipal(aUseSystemPrincipal == UseSystemPrincipal::Yes),
372       mSheetAlreadyComplete(false),
373       mIsCrossOriginNoCORS(false),
374       mBlockResourceTiming(false),
375       mLoadFailed(false),
376       mPreloadKind(aPreloadKind),
377       mOwningNode(nullptr),
378       mObserver(aObserver),
379       mTriggeringPrincipal(aTriggeringPrincipal),
380       mReferrerInfo(aReferrerInfo),
381       mRequestingNode(aRequestingNode),
382       mGuessedEncoding(
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");
390 }
391 
~SheetLoadData()392 SheetLoadData::~SheetLoadData() {
393   MOZ_DIAGNOSTIC_ASSERT(mSheetCompleteCalled || mIntentionallyDropped,
394                         "Should always call SheetComplete, except when "
395                         "dropping the load");
396 
397   // Do this iteratively to avoid blowing up the stack.
398   RefPtr<SheetLoadData> next = std::move(mNext);
399   while (next) {
400     next = std::move(next->mNext);
401   }
402 }
403 
404 NS_IMETHODIMP
Run()405 SheetLoadData::Run() {
406   mLoader->HandleLoadEvent(*this);
407   return NS_OK;
408 }
409 
410 NS_IMETHODIMP
OnDispatchedEvent()411 SheetLoadData::OnDispatchedEvent() { return NS_OK; }
412 
413 NS_IMETHODIMP
OnProcessNextEvent(nsIThreadInternal * aThread,bool aMayWait)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);
419   return NS_OK;
420 }
421 
422 NS_IMETHODIMP
AfterProcessNextEvent(nsIThreadInternal * aThread,bool aEventWasProcessed)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);
429   return NS_OK;
430 }
431 
PrioritizeAsPreload(nsIChannel * aChannel)432 void SheetLoadData::PrioritizeAsPreload(nsIChannel* aChannel) {
433   if (nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(aChannel)) {
434     sp->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
435   }
436 }
437 
PrioritizeAsPreload()438 void SheetLoadData::PrioritizeAsPreload() { PrioritizeAsPreload(Channel()); }
439 
FireLoadEvent(nsIThreadInternal * aThread)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);
445 
446   // Now fire the event.
447   //
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???");
453 
454   nsContentUtils::DispatchTrustedEvent(node->OwnerDoc(), node,
455                                        mLoadFailed ? u"error"_ns : u"load"_ns,
456                                        CanBubble::eNo, Cancelable::eNo);
457 
458   MOZ_ASSERT(BlocksLoadEvent());
459   mLoader->UnblockOnload(true);
460 }
461 
ScheduleLoadEventIfNeeded()462 void SheetLoadData::ScheduleLoadEventIfNeeded() {
463   if (!mOwningNode) {
464     return;
465   }
466 
467   MOZ_ASSERT(BlocksLoadEvent(), "The rel=preload load event happens elsewhere");
468 
469   nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
470   nsCOMPtr<nsIThreadInternal> internalThread = do_QueryInterface(thread);
471   if (NS_SUCCEEDED(internalThread->AddObserver(this))) {
472     mLoader->BlockOnload();
473   }
474 }
475 
476 /*********************
477  * Style sheet reuse *
478  *********************/
479 
FindReusableStyleSheet(nsIURI * aURL,RefPtr<StyleSheet> & aResult)480 bool LoaderReusableStyleSheets::FindReusableStyleSheet(
481     nsIURI* aURL, RefPtr<StyleSheet>& aResult) {
482   MOZ_ASSERT(aURL);
483   for (size_t i = mReusableSheets.Length(); i > 0; --i) {
484     size_t index = i - 1;
485     bool sameURI;
486     MOZ_ASSERT(mReusableSheets[index]->GetOriginalURI());
487     nsresult rv =
488         aURL->Equals(mReusableSheets[index]->GetOriginalURI(), &sameURI);
489     if (!NS_FAILED(rv) && sameURI) {
490       aResult = mReusableSheets[index];
491       mReusableSheets.RemoveElementAt(index);
492       return true;
493     }
494   }
495   return false;
496 }
497 /*************************
498  * Loader Implementation *
499  *************************/
500 
Loader()501 Loader::Loader()
502     : mDocument(nullptr),
503       mDocumentCompatMode(eCompatibility_FullStandards),
504       mReporter(new ConsoleReportCollector()) {}
505 
Loader(DocGroup * aDocGroup)506 Loader::Loader(DocGroup* aDocGroup) : Loader() { mDocGroup = aDocGroup; }
507 
Loader(Document * aDocument)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();
514 }
515 
~Loader()516 Loader::~Loader() {
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
519   // they're all done.
520 }
521 
RegisterInSheetCache()522 void Loader::RegisterInSheetCache() {
523   MOZ_ASSERT(mDocument);
524   MOZ_ASSERT(mSheets);
525 
526   mSheets->RegisterLoader(*this);
527 }
528 
DeregisterFromSheetCache()529 void Loader::DeregisterFromSheetCache() {
530   MOZ_ASSERT(mDocument);
531   MOZ_ASSERT(mSheets);
532 
533   mSheets->CancelLoadsForLoader(*this);
534   mSheets->UnregisterLoader(*this);
535 }
536 
DropDocumentReference()537 void Loader::DropDocumentReference() {
538   // Flush out pending datas just so we don't leak by accident.
539   if (mSheets) {
540     DeregisterFromSheetCache();
541   }
542   mDocument = nullptr;
543 }
544 
DocumentStyleSheetSetChanged()545 void Loader::DocumentStyleSheetSetChanged() {
546   MOZ_ASSERT(mDocument);
547 
548   // start any pending alternates that aren't alternates anymore
549   mSheets->StartDeferredLoadsForLoader(
550       *this, SharedStyleSheetCache::StartLoads::IfNonAlternate);
551 }
552 
553 static const char kCharsetSym[] = "@charset \"";
554 
GetCharsetFromData(const char * aStyleSheetData,uint32_t aDataLength,nsACString & aCharset)555 static bool GetCharsetFromData(const char* aStyleSheetData,
556                                uint32_t aDataLength, nsACString& aCharset) {
557   aCharset.Truncate();
558   if (aDataLength <= sizeof(kCharsetSym) - 1) return false;
559 
560   if (strncmp(aStyleSheetData, kCharsetSym, sizeof(kCharsetSym) - 1)) {
561     return false;
562   }
563 
564   for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
565     char c = aStyleSheetData[i];
566     if (c == '"') {
567       ++i;
568       if (i < aDataLength && aStyleSheetData[i] == ';') {
569         return true;
570       }
571       // fail
572       break;
573     }
574     aCharset.Append(c);
575   }
576 
577   // Did not see end quote or semicolon
578   aCharset.Truncate();
579   return false;
580 }
581 
DetermineNonBOMEncoding(const nsACString & aSegment,nsIChannel * aChannel) const582 NotNull<const Encoding*> SheetLoadData::DetermineNonBOMEncoding(
583     const nsACString& aSegment, nsIChannel* aChannel) const {
584   const Encoding* encoding;
585   nsAutoCString label;
586 
587   // Check HTTP
588   if (aChannel && NS_SUCCEEDED(aChannel->GetContentCharset(label))) {
589     encoding = Encoding::ForLabel(label);
590     if (encoding) {
591       return WrapNotNull(encoding);
592     }
593   }
594 
595   // Check @charset
596   auto sniffingLength = aSegment.Length();
597   if (sniffingLength > SNIFFING_BUFFER_SIZE) {
598     sniffingLength = SNIFFING_BUFFER_SIZE;
599   }
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;
604     }
605     if (encoding) {
606       return WrapNotNull(encoding);
607     }
608   }
609   return mGuessedEncoding;
610 }
611 
VerifySheetIntegrity(const SRIMetadata & aMetadata,nsIChannel * aChannel,const nsACString & aFirst,const nsACString & aSecond,const nsACString & aSourceFileURI,nsIConsoleReportCollector * aReporter)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);
619 
620   if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), LogLevel::Debug)) {
621     nsAutoCString requestURL;
622     nsCOMPtr<nsIURI> originalURI;
623     if (aChannel &&
624         NS_SUCCEEDED(aChannel->GetOriginalURI(getter_AddRefs(originalURI))) &&
625         originalURI) {
626       originalURI->GetAsciiSpec(requestURL);
627     }
628     MOZ_LOG(SRILogHelper::GetSriLog(), LogLevel::Debug,
629             ("VerifySheetIntegrity (unichar stream)"));
630   }
631 
632   SRICheckDataVerifier verifier(aMetadata, aSourceFileURI, aReporter);
633   nsresult rv =
634       verifier.Update(aFirst.Length(), (const uint8_t*)aFirst.BeginReading());
635   NS_ENSURE_SUCCESS(rv, rv);
636   rv =
637       verifier.Update(aSecond.Length(), (const uint8_t*)aSecond.BeginReading());
638   NS_ENSURE_SUCCESS(rv, rv);
639 
640   return verifier.Verify(aMetadata, aChannel, aSourceFileURI, aReporter);
641 }
642 
AllLoadsCanceled(const SheetLoadData & aData)643 static bool AllLoadsCanceled(const SheetLoadData& aData) {
644   const SheetLoadData* data = &aData;
645   do {
646     if (!data->mIsCancelled) {
647       return false;
648     }
649   } while ((data = data->mNext));
650   return true;
651 }
652 
653 /*
654  * Stream completion code shared by Stylo and the old style system.
655  *
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.
659  */
VerifySheetReadyToParse(nsresult aStatus,const nsACString & aBytes1,const nsACString & aBytes2,nsIChannel * aChannel)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");
666 
667   if (AllLoadsCanceled(*this)) {
668     LOG_WARN(("  All loads are canceled, dropping"));
669     mLoader->SheetComplete(*this, NS_BINDING_ABORTED);
670     return NS_OK;
671   }
672 
673   if (NS_FAILED(aStatus)) {
674     LOG_WARN(
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.
680     //
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(
684             aStatus)) {
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));
690         }
691       }
692     }
693     mLoader->SheetComplete(*this, aStatus);
694     return NS_OK;
695   }
696 
697   if (!aChannel) {
698     mLoader->SheetComplete(*this, NS_OK);
699     return NS_OK;
700   }
701 
702   nsCOMPtr<nsIURI> originalURI;
703   aChannel->GetOriginalURI(getter_AddRefs(originalURI));
704 
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));
712 
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);
717     return NS_OK;
718   }
719 
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));
726     } else {
727       result = secMan->GetChannelResultPrincipal(aChannel,
728                                                  getter_AddRefs(principal));
729     }
730   }
731 
732   if (NS_FAILED(result)) {
733     LOG_WARN(("  Couldn't get principal"));
734     mLoader->SheetComplete(*this, result);
735     return NS_OK;
736   }
737 
738   mSheet->SetPrincipal(principal);
739 
740   if (mSheet->GetCORSMode() == CORS_NONE &&
741       !mTriggeringPrincipal->Subsumes(principal)) {
742     mIsCrossOriginNoCORS = true;
743   }
744 
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);
753       return NS_OK;
754     }
755 
756     nsAutoCString sourceMapURL;
757     if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
758       mSheet->SetSourceMapURL(NS_ConvertUTF8toUTF16(sourceMapURL));
759     }
760   }
761 
762   nsAutoCString contentType;
763   aChannel->GetContentType(contentType);
764 
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.
769 
770   bool validType = contentType.EqualsLiteral("text/css") ||
771                    contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
772                    contentType.IsEmpty();
773 
774   if (!validType) {
775     const char* errorMessage;
776     uint32_t errorFlag;
777     bool sameOrigin = true;
778 
779     bool subsumed;
780     result = mTriggeringPrincipal->Subsumes(principal, &subsumed);
781     if (NS_FAILED(result) || !subsumed) {
782       sameOrigin = false;
783     }
784 
785     if (sameOrigin && mCompatMode == eCompatibility_NavQuirks) {
786       errorMessage = "MimeNotCssWarn";
787       errorFlag = nsIScriptError::warningFlag;
788     } else {
789       errorMessage = "MimeNotCss";
790       errorFlag = nsIScriptError::errorFlag;
791     }
792 
793     AutoTArray<nsString, 2> strings;
794     CopyUTF8toUTF16(channelURI->GetSpecOrDefault(), *strings.AppendElement());
795     CopyASCIItoUTF16(contentType, *strings.AppendElement());
796 
797     nsCOMPtr<nsIURI> referrer = ReferrerInfo()->GetOriginalReferrer();
798     nsContentUtils::ReportToConsole(
799         errorFlag, "CSS Loader"_ns, mLoader->mDocument,
800         nsContentUtils::eCSS_PROPERTIES, errorMessage, strings, referrer);
801 
802     if (errorFlag == nsIScriptError::errorFlag) {
803       LOG_WARN(
804           ("  Ignoring sheet with improper MIME type %s", contentType.get()));
805       mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
806       return NS_OK;
807     }
808   }
809 
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);
816     }
817     nsresult rv = VerifySheetIntegrity(sriMetadata, aChannel, aBytes1, aBytes2,
818                                        sourceUri, mLoader->mReporter);
819 
820     nsCOMPtr<nsILoadGroup> loadGroup;
821     aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
822     if (loadGroup) {
823       mLoader->mReporter->FlushConsoleReports(loadGroup);
824     } else {
825       mLoader->mReporter->FlushConsoleReports(mLoader->mDocument);
826     }
827 
828     if (NS_FAILED(rv)) {
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);
833       return NS_OK;
834     }
835   }
836 
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);
840 
841   ReferrerPolicy policy =
842       nsContentUtils::GetReferrerPolicyFromChannel(aChannel);
843   nsCOMPtr<nsIReferrerInfo> referrerInfo =
844       ReferrerInfo::CreateForExternalCSSResources(mSheet, policy);
845 
846   mSheet->SetReferrerInfo(referrerInfo);
847   return NS_OK_PARSE_SHEET;
848 }
849 
IsAlternateSheet(const nsAString & aTitle,bool aHasAlternateRel)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;
859   }
860 
861   if (mDocument) {
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;
871     }
872 
873     if (aTitle.Equals(currentSheetSet)) {
874       return IsAlternate::No;
875     }
876   }
877 
878   return IsAlternate::Yes;
879 }
880 
CheckContentPolicy(nsIPrincipal * aLoadingPrincipal,nsIPrincipal * aTriggeringPrincipal,nsIURI * aTargetURI,nsINode * aRequestingNode,const nsAString & aNonce,StylePreloadKind aPreloadKind)881 nsresult Loader::CheckContentPolicy(nsIPrincipal* aLoadingPrincipal,
882                                     nsIPrincipal* aTriggeringPrincipal,
883                                     nsIURI* aTargetURI,
884                                     nsINode* aRequestingNode,
885                                     const nsAString& aNonce,
886                                     StylePreloadKind aPreloadKind) {
887   // When performing a system load don't consult content policies.
888   if (!mDocument) {
889     return NS_OK;
890   }
891 
892   nsContentPolicyType contentPolicyType =
893       aPreloadKind == StylePreloadKind::None
894           ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
895           : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
896 
897   nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
898       aLoadingPrincipal, aTriggeringPrincipal, aRequestingNode,
899       nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, contentPolicyType);
900 
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());
905   }
906 
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     // Asynchronously notify observers (e.g devtools) of CSP failure.
913     nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
914         "Loader::NotifyOnFailedCheckPolicy",
915         [targetURI = RefPtr<nsIURI>(aTargetURI),
916          requestingNode = RefPtr<nsINode>(aRequestingNode),
917          contentPolicyType]() {
918           nsCOMPtr<nsIChannel> channel;
919           NS_NewChannel(
920               getter_AddRefs(channel), targetURI, requestingNode,
921               nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT,
922               contentPolicyType);
923           NS_SetRequestBlockingReason(
924               channel, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_GENERAL);
925           nsCOMPtr<nsIObserverService> obsService =
926               services::GetObserverService();
927           if (obsService) {
928             obsService->NotifyObservers(
929                 channel, "http-on-failed-opening-request", nullptr);
930           }
931         }));
932     return NS_ERROR_CONTENT_BLOCKED;
933   }
934   return NS_OK;
935 }
936 
RecordUseCountersIfNeeded(Document * aDoc,const StyleUseCounters * aCounters)937 static void RecordUseCountersIfNeeded(Document* aDoc,
938                                       const StyleUseCounters* aCounters) {
939   if (!aDoc || !aCounters) {
940     return;
941   }
942   const StyleUseCounters* docCounters = aDoc->GetStyleUseCounters();
943   if (!docCounters) {
944     return;
945   }
946   Servo_UseCounters_Merge(docCounters, aCounters);
947   aDoc->MaybeWarnAboutZoom();
948 }
949 
DidHitCompleteSheetCache(const SheetLoadDataHashKey & aKey,const StyleUseCounters * aCounters)950 void Loader::DidHitCompleteSheetCache(const SheetLoadDataHashKey& aKey,
951                                       const StyleUseCounters* aCounters) {
952   MOZ_ASSERT(mDocument);
953   if (mLoadsPerformed.EnsureInserted(aKey)) {
954     RecordUseCountersIfNeeded(mDocument, aCounters);
955   }
956 }
957 
958 /**
959  * CreateSheet() creates a StyleSheet object for the given URI.
960  *
961  * We check for an existing style sheet object for that uri in various caches
962  * and clone it if we find it.  Cloned sheets will have the title/media/enabled
963  * state of the sheet they are clones off; make sure to call PrepareSheet() on
964  * the result of CreateSheet().
965  */
CreateSheet(nsIURI * aURI,nsIContent * aLinkingContent,nsIPrincipal * aTriggeringPrincipal,css::SheetParsingMode aParsingMode,CORSMode aCORSMode,const Encoding * aPreloadOrParentDataEncoding,const nsAString & aIntegrity,bool aSyncLoad,StylePreloadKind aPreloadKind)966 std::tuple<RefPtr<StyleSheet>, Loader::SheetState> Loader::CreateSheet(
967     nsIURI* aURI, nsIContent* aLinkingContent,
968     nsIPrincipal* aTriggeringPrincipal, css::SheetParsingMode aParsingMode,
969     CORSMode aCORSMode, const Encoding* aPreloadOrParentDataEncoding,
970     const nsAString& aIntegrity, bool aSyncLoad,
971     StylePreloadKind aPreloadKind) {
972   MOZ_ASSERT(aURI, "This path is not taken for inline stylesheets");
973   LOG(("css::Loader::CreateSheet(%s)", aURI->GetSpecOrDefault().get()));
974 
975   SRIMetadata sriMetadata;
976   if (!aIntegrity.IsEmpty()) {
977     MOZ_LOG(gSriPRLog, LogLevel::Debug,
978             ("css::Loader::CreateSheet, integrity=%s",
979              NS_ConvertUTF16toUTF8(aIntegrity).get()));
980     nsAutoCString sourceUri;
981     if (mDocument && mDocument->GetDocumentURI()) {
982       mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
983     }
984     SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata);
985   }
986 
987   if (mSheets) {
988     SheetLoadDataHashKey key(aURI, aTriggeringPrincipal, LoaderPrincipal(),
989                              PartitionedPrincipal(),
990                              GetFallbackEncoding(*this, aLinkingContent,
991                                                  aPreloadOrParentDataEncoding),
992                              aCORSMode, aParsingMode, CompatMode(aPreloadKind),
993                              sriMetadata, aPreloadKind);
994     auto cacheResult = mSheets->Lookup(*this, key, aSyncLoad);
995     if (const auto& [styleSheet, sheetState] = cacheResult; styleSheet) {
996       LOG(("  Hit cache with state: %s", gStateStrings[size_t(sheetState)]));
997       return cacheResult;
998     }
999   }
1000 
1001   nsIURI* sheetURI = aURI;
1002   nsIURI* baseURI = aURI;
1003   nsIURI* originalURI = aURI;
1004 
1005   auto sheet = MakeRefPtr<StyleSheet>(aParsingMode, aCORSMode, sriMetadata);
1006   sheet->SetURIs(sheetURI, originalURI, baseURI);
1007   nsCOMPtr<nsIReferrerInfo> referrerInfo =
1008       ReferrerInfo::CreateForExternalCSSResources(sheet);
1009   sheet->SetReferrerInfo(referrerInfo);
1010   LOG(("  Needs parser"));
1011   return {std::move(sheet), SheetState::NeedsParser};
1012 }
1013 
MediaListMatches(const MediaList * aMediaList,const Document * aDocument)1014 static Loader::MediaMatched MediaListMatches(const MediaList* aMediaList,
1015                                              const Document* aDocument) {
1016   if (!aMediaList || !aDocument) {
1017     return Loader::MediaMatched::Yes;
1018   }
1019 
1020   if (aMediaList->Matches(*aDocument)) {
1021     return Loader::MediaMatched::Yes;
1022   }
1023 
1024   return Loader::MediaMatched::No;
1025 }
1026 
1027 /**
1028  * PrepareSheet() handles setting the media and title on the sheet, as
1029  * well as setting the enabled state based on the title and whether
1030  * the sheet had "alternate" in its rel.
1031  */
PrepareSheet(StyleSheet & aSheet,const nsAString & aTitle,const nsAString & aMediaString,MediaList * aMediaList,IsAlternate aIsAlternate,IsExplicitlyEnabled aIsExplicitlyEnabled)1032 Loader::MediaMatched Loader::PrepareSheet(
1033     StyleSheet& aSheet, const nsAString& aTitle, const nsAString& aMediaString,
1034     MediaList* aMediaList, IsAlternate aIsAlternate,
1035     IsExplicitlyEnabled aIsExplicitlyEnabled) {
1036   RefPtr<MediaList> mediaList(aMediaList);
1037 
1038   if (!aMediaString.IsEmpty()) {
1039     NS_ASSERTION(!aMediaList,
1040                  "must not provide both aMediaString and aMediaList");
1041     mediaList = MediaList::Create(NS_ConvertUTF16toUTF8(aMediaString));
1042   }
1043 
1044   aSheet.SetMedia(do_AddRef(mediaList));
1045 
1046   aSheet.SetTitle(aTitle);
1047   aSheet.SetEnabled(aIsAlternate == IsAlternate::No ||
1048                     aIsExplicitlyEnabled == IsExplicitlyEnabled::Yes);
1049   return MediaListMatches(mediaList, mDocument);
1050 }
1051 
1052 /**
1053  * InsertSheetInTree handles ordering of sheets in the document or shadow root.
1054  *
1055  * Here we have two types of sheets -- those with linking elements and
1056  * those without.  The latter are loaded by Link: headers, and are only added to
1057  * the document.
1058  *
1059  * The following constraints are observed:
1060  * 1) Any sheet with a linking element comes after all sheets without
1061  *    linking elements
1062  * 2) Sheets without linking elements are inserted in the order in
1063  *    which the inserting requests come in, since all of these are
1064  *    inserted during header data processing in the content sink
1065  * 3) Sheets with linking elements are ordered based on document order
1066  *    as determined by CompareDocumentPosition.
1067  */
InsertSheetInTree(StyleSheet & aSheet,nsINode * aOwningNode)1068 void Loader::InsertSheetInTree(StyleSheet& aSheet, nsINode* aOwningNode) {
1069   LOG(("css::Loader::InsertSheetInTree"));
1070   MOZ_ASSERT(mDocument, "Must have a document to insert into");
1071   MOZ_ASSERT(!aOwningNode || aOwningNode->IsInUncomposedDoc() ||
1072                  aOwningNode->IsInShadowTree(),
1073              "Why would we insert it anywhere?");
1074   ShadowRoot* shadow =
1075       aOwningNode ? aOwningNode->GetContainingShadow() : nullptr;
1076 
1077   auto& target = shadow ? static_cast<DocumentOrShadowRoot&>(*shadow)
1078                         : static_cast<DocumentOrShadowRoot&>(*mDocument);
1079 
1080   // XXX Need to cancel pending sheet loads for this element, if any
1081 
1082   int32_t sheetCount = target.SheetCount();
1083 
1084   /*
1085    * Start the walk at the _end_ of the list, since in the typical
1086    * case we'll just want to append anyway.  We want to break out of
1087    * the loop when insertionPoint points to just before the index we
1088    * want to insert at.  In other words, when we leave the loop
1089    * insertionPoint is the index of the stylesheet that immediately
1090    * precedes the one we're inserting.
1091    */
1092   int32_t insertionPoint = sheetCount - 1;
1093   for (; insertionPoint >= 0; --insertionPoint) {
1094     nsINode* sheetOwner = target.SheetAt(insertionPoint)->GetOwnerNode();
1095     if (sheetOwner && !aOwningNode) {
1096       // Keep moving; all sheets with a sheetOwner come after all
1097       // sheets without a linkingNode
1098       continue;
1099     }
1100 
1101     if (!sheetOwner) {
1102       // Aha!  The current sheet has no sheet owner, so we want to insert after
1103       // it no matter whether we have a linking content or not.
1104       break;
1105     }
1106 
1107     MOZ_ASSERT(aOwningNode != sheetOwner,
1108                "Why do we still have our old sheet?");
1109 
1110     // Have to compare
1111     if (nsContentUtils::PositionIsBefore(sheetOwner, aOwningNode)) {
1112       // The current sheet comes before us, and it better be the first
1113       // such, because now we break
1114       break;
1115     }
1116   }
1117 
1118   ++insertionPoint;
1119 
1120   if (shadow) {
1121     shadow->InsertSheetAt(insertionPoint, aSheet);
1122   } else {
1123     mDocument->InsertSheetAt(insertionPoint, aSheet);
1124   }
1125 
1126   LOG(("  Inserting into target (doc: %d) at position %d",
1127        target.AsNode().IsDocument(), insertionPoint));
1128 }
1129 
1130 /**
1131  * InsertChildSheet handles ordering of @import-ed sheet in their
1132  * parent sheets.  Here we want to just insert based on order of the
1133  * @import rules that imported the sheets.  In theory we can't just
1134  * append to the end because the CSSOM can insert @import rules.  In
1135  * practice, we get the call to load the child sheet before the CSSOM
1136  * has finished inserting the @import rule, so we have no idea where
1137  * to put it anyway.  So just append for now.  (In the future if we
1138  * want to insert the sheet at the correct position, we'll need to
1139  * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
1140  * bug 1220506.)
1141  */
InsertChildSheet(StyleSheet & aSheet,StyleSheet & aParentSheet)1142 void Loader::InsertChildSheet(StyleSheet& aSheet, StyleSheet& aParentSheet) {
1143   LOG(("css::Loader::InsertChildSheet"));
1144 
1145   // child sheets should always start out enabled, even if they got
1146   // cloned off of top-level sheets which were disabled
1147   aSheet.SetEnabled(true);
1148   aParentSheet.AppendStyleSheet(aSheet);
1149 
1150   LOG(("  Inserting into parent sheet"));
1151 }
1152 
1153 /**
1154  * LoadSheet handles the actual load of a sheet.  If the load is
1155  * supposed to be synchronous it just opens a channel synchronously
1156  * using the given uri, wraps the resulting stream in a converter
1157  * stream and calls ParseSheet.  Otherwise it tries to look for an
1158  * existing load for this URI and piggyback on it.  Failing all that,
1159  * a new load is kicked off asynchronously.
1160  */
LoadSheet(SheetLoadData & aLoadData,SheetState aSheetState,PendingLoad aPendingLoad)1161 nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState,
1162                            PendingLoad aPendingLoad) {
1163   LOG(("css::Loader::LoadSheet"));
1164   MOZ_ASSERT(aLoadData.mURI, "Need a URI to load");
1165   MOZ_ASSERT(aLoadData.mSheet, "Need a sheet to load into");
1166   MOZ_ASSERT(aSheetState != SheetState::Complete, "Why bother?");
1167   MOZ_ASSERT(!aLoadData.mUseSystemPrincipal || aLoadData.mSyncLoad,
1168              "Shouldn't use system principal for async loads");
1169 
1170   LOG_URI("  Load from: '%s'", aLoadData.mURI);
1171 
1172   // If we're firing a pending load, this load is already accounted for the
1173   // first time it went through this function.
1174   if (aPendingLoad == PendingLoad::No) {
1175     if (aLoadData.BlocksLoadEvent()) {
1176       IncrementOngoingLoadCount();
1177     }
1178 
1179     // We technically never defer non-top-level sheets, so this condition could
1180     // be outside the branch, but conceptually it should be here.
1181     if (aLoadData.mParentData) {
1182       ++aLoadData.mParentData->mPendingChildren;
1183     }
1184   }
1185 
1186   nsresult rv = NS_OK;
1187 
1188   if (!mDocument && !aLoadData.mIsNonDocumentSheet) {
1189     // No point starting the load; just release all the data and such.
1190     LOG_WARN(("  No document and not non-document sheet; pre-dropping load"));
1191     SheetComplete(aLoadData, NS_BINDING_ABORTED);
1192     return NS_BINDING_ABORTED;
1193   }
1194 
1195   SRIMetadata sriMetadata;
1196   aLoadData.mSheet->GetIntegrity(sriMetadata);
1197 
1198   if (aLoadData.mSyncLoad) {
1199     LOG(("  Synchronous load"));
1200     MOZ_ASSERT(!aLoadData.mObserver, "Observer for a sync load?");
1201     MOZ_ASSERT(aSheetState == SheetState::NeedsParser,
1202                "Sync loads can't reuse existing async loads");
1203 
1204     // Create a StreamLoader instance to which we will feed
1205     // the data from the sync load.  Do this before creating the
1206     // channel to make error recovery simpler.
1207     auto streamLoader = MakeRefPtr<StreamLoader>(aLoadData);
1208 
1209     if (mDocument) {
1210       net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
1211                           nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
1212                           mDocument);
1213     }
1214 
1215     nsSecurityFlags securityFlags =
1216         nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT |
1217         nsILoadInfo::SEC_ALLOW_CHROME;
1218 
1219     nsContentPolicyType contentPolicyType =
1220         aLoadData.mPreloadKind == StylePreloadKind::None
1221             ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1222             : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
1223 
1224     // Just load it
1225     nsCOMPtr<nsIChannel> channel;
1226     // Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both
1227     // a node and a principal.
1228     // This is because of a case where the node is the document being styled and
1229     // the principal is the stylesheet (perhaps from a different origin) that is
1230     // applying the styles.
1231     if (aLoadData.mRequestingNode) {
1232       rv = NS_NewChannelWithTriggeringPrincipal(
1233           getter_AddRefs(channel), aLoadData.mURI, aLoadData.mRequestingNode,
1234           aLoadData.mTriggeringPrincipal, securityFlags, contentPolicyType);
1235     } else {
1236       MOZ_ASSERT(aLoadData.mTriggeringPrincipal->Equals(LoaderPrincipal()));
1237       auto result = URLPreloader::ReadURI(aLoadData.mURI);
1238       if (result.isOk()) {
1239         nsCOMPtr<nsIInputStream> stream;
1240         MOZ_TRY(
1241             NS_NewCStringInputStream(getter_AddRefs(stream), result.unwrap()));
1242 
1243         rv = NS_NewInputStreamChannel(
1244             getter_AddRefs(channel), aLoadData.mURI, stream.forget(),
1245             aLoadData.mTriggeringPrincipal, securityFlags, contentPolicyType);
1246       } else {
1247         rv = NS_NewChannel(getter_AddRefs(channel), aLoadData.mURI,
1248                            aLoadData.mTriggeringPrincipal, securityFlags,
1249                            contentPolicyType);
1250       }
1251     }
1252     if (NS_FAILED(rv)) {
1253       LOG_ERROR(("  Failed to create channel"));
1254       streamLoader->ChannelOpenFailed(rv);
1255       SheetComplete(aLoadData, rv);
1256       return rv;
1257     }
1258 
1259     // snapshot the nonce at load start time for performing CSP checks
1260     if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
1261       if (aLoadData.mRequestingNode) {
1262         // TODO(bug 1607009) move to SheetLoadData
1263         nsString* cspNonce = static_cast<nsString*>(
1264             aLoadData.mRequestingNode->GetProperty(nsGkAtoms::nonce));
1265         if (cspNonce) {
1266           nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1267           loadInfo->SetCspNonce(*cspNonce);
1268         }
1269       }
1270     }
1271 
1272     nsCOMPtr<nsIInputStream> stream;
1273     rv = channel->Open(getter_AddRefs(stream));
1274 
1275     if (NS_FAILED(rv)) {
1276       LOG_ERROR(("  Failed to open URI synchronously"));
1277       streamLoader->ChannelOpenFailed(rv);
1278       SheetComplete(aLoadData, rv);
1279       return rv;
1280     }
1281 
1282     // Force UA sheets to be UTF-8.
1283     // XXX this is only necessary because the default in
1284     // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
1285     channel->SetContentCharset("UTF-8"_ns);
1286 
1287     // Manually feed the streamloader the contents of the stream.
1288     // This will call back into OnStreamComplete
1289     // and thence to ParseSheet.  Regardless of whether this fails,
1290     // SheetComplete has been called.
1291     return nsSyncLoadService::PushSyncStreamToListener(stream.forget(),
1292                                                        streamLoader, channel);
1293   }
1294 
1295   SheetLoadDataHashKey key(aLoadData);
1296 
1297   auto preloadKey = PreloadHashKey::CreateAsStyle(aLoadData);
1298   bool coalescedLoad = false;
1299   if (mSheets) {
1300     // If we have at least one other load ongoing, then we can defer it until
1301     // all non-pending loads are done.
1302     if (aSheetState == SheetState::NeedsParser &&
1303         aPendingLoad == PendingLoad::No && aLoadData.ShouldDefer() &&
1304         mOngoingLoadCount > mPendingLoadCount + 1) {
1305       LOG(("  Deferring sheet load"));
1306       ++mPendingLoadCount;
1307       mSheets->DeferSheetLoad(key, aLoadData);
1308       return NS_OK;
1309     }
1310 
1311     if ((coalescedLoad = mSheets->CoalesceLoad(key, aLoadData, aSheetState))) {
1312       if (aSheetState == SheetState::Pending) {
1313         ++mPendingLoadCount;
1314         return NS_OK;
1315       }
1316     }
1317   }
1318 
1319   aLoadData.NotifyOpen(preloadKey, mDocument, aLoadData.IsLinkRelPreload());
1320   if (coalescedLoad) {
1321     // All done here; once the load completes we'll be marked complete
1322     // automatically.
1323     return NS_OK;
1324   }
1325 
1326   nsCOMPtr<nsILoadGroup> loadGroup;
1327   nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
1328   if (mDocument) {
1329     loadGroup = mDocument->GetDocumentLoadGroup();
1330     // load for a document with no loadgrup indicates that something is
1331     // completely bogus, let's bail out early.
1332     if (!loadGroup) {
1333       LOG_ERROR(("  Failed to query loadGroup from document"));
1334       SheetComplete(aLoadData, NS_ERROR_UNEXPECTED);
1335       return NS_ERROR_UNEXPECTED;
1336     }
1337 
1338     cookieJarSettings = mDocument->CookieJarSettings();
1339   }
1340 
1341 #ifdef DEBUG
1342   AutoRestore<bool> syncCallbackGuard(mSyncCallback);
1343   mSyncCallback = true;
1344 #endif
1345 
1346   CORSMode ourCORSMode = aLoadData.mSheet->GetCORSMode();
1347   nsSecurityFlags securityFlags =
1348       ourCORSMode == CORS_NONE
1349           ? nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
1350           : nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT;
1351   if (ourCORSMode == CORS_ANONYMOUS) {
1352     securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
1353   } else if (ourCORSMode == CORS_USE_CREDENTIALS) {
1354     securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
1355   }
1356   securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
1357 
1358   nsContentPolicyType contentPolicyType =
1359       aLoadData.mPreloadKind == StylePreloadKind::None
1360           ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
1361           : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
1362 
1363   nsCOMPtr<nsIChannel> channel;
1364   // Note we are calling NS_NewChannelWithTriggeringPrincipal here with a node
1365   // and a principal. This is because of a case where the node is the document
1366   // being styled and the principal is the stylesheet (perhaps from a different
1367   // origin)  that is applying the styles.
1368   if (aLoadData.mRequestingNode) {
1369     rv = NS_NewChannelWithTriggeringPrincipal(
1370         getter_AddRefs(channel), aLoadData.mURI, aLoadData.mRequestingNode,
1371         aLoadData.mTriggeringPrincipal, securityFlags, contentPolicyType,
1372         /* PerformanceStorage */ nullptr, loadGroup);
1373   } else {
1374     MOZ_ASSERT(aLoadData.mTriggeringPrincipal->Equals(LoaderPrincipal()));
1375     rv = NS_NewChannel(getter_AddRefs(channel), aLoadData.mURI,
1376                        aLoadData.mTriggeringPrincipal, securityFlags,
1377                        contentPolicyType, cookieJarSettings,
1378                        /* aPerformanceStorage */ nullptr, loadGroup);
1379   }
1380 
1381   if (NS_FAILED(rv)) {
1382     LOG_ERROR(("  Failed to create channel"));
1383     SheetComplete(aLoadData, rv);
1384     return rv;
1385   }
1386 
1387   // snapshot the nonce at load start time for performing CSP checks
1388   if (contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET) {
1389     if (aLoadData.mRequestingNode) {
1390       // TODO(bug 1607009) move to SheetLoadData
1391       nsString* cspNonce = static_cast<nsString*>(
1392           aLoadData.mRequestingNode->GetProperty(nsGkAtoms::nonce));
1393       if (cspNonce) {
1394         nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1395         loadInfo->SetCspNonce(*cspNonce);
1396       }
1397     }
1398   }
1399 
1400   if (!aLoadData.ShouldDefer()) {
1401     if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(channel)) {
1402       cos->AddClassFlags(nsIClassOfService::Leader);
1403     }
1404     if (aLoadData.IsLinkRelPreload()) {
1405       SheetLoadData::PrioritizeAsPreload(channel);
1406       SheetLoadData::AddLoadBackgroundFlag(channel);
1407     }
1408   }
1409 
1410   if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
1411     if (nsCOMPtr<nsIReferrerInfo> referrerInfo = aLoadData.ReferrerInfo()) {
1412       rv = httpChannel->SetReferrerInfo(referrerInfo);
1413       Unused << NS_WARN_IF(NS_FAILED(rv));
1414     }
1415 
1416     nsCOMPtr<nsIHttpChannelInternal> internalChannel =
1417         do_QueryInterface(httpChannel);
1418     if (internalChannel) {
1419       rv = internalChannel->SetIntegrityMetadata(
1420           sriMetadata.GetIntegrityString());
1421       NS_ENSURE_SUCCESS(rv, rv);
1422     }
1423 
1424     // Set the initiator type
1425     if (nsCOMPtr<nsITimedChannel> timedChannel =
1426             do_QueryInterface(httpChannel)) {
1427       if (aLoadData.mParentData) {
1428         timedChannel->SetInitiatorType(u"css"_ns);
1429 
1430         // This is a child sheet load.
1431         //
1432         // The resource timing of the sub-resources that a document loads
1433         // should normally be reported to the document.  One exception is any
1434         // sub-resources of any cross-origin resources that are loaded.  We
1435         // don't mind reporting timing data for a direct child cross-origin
1436         // resource since the resource that linked to it (and hence potentially
1437         // anything in that parent origin) is aware that the cross-origin
1438         // resources is to be loaded.  However, we do not want to report
1439         // timings for any sub-resources that a cross-origin resource may load
1440         // since that obviously leaks information about what the cross-origin
1441         // resource loads, which is bad.
1442         //
1443         // In addition to checking whether we're an immediate child resource of
1444         // a cross-origin resource (by checking if mIsCrossOriginNoCORS is set
1445         // to true on our parent), we also check our parent to see whether it
1446         // itself is a sub-resource of a cross-origin resource by checking
1447         // mBlockResourceTiming.  If that is set then we too are such a
1448         // sub-resource and so we set the flag on ourself too to propagate it
1449         // on down.
1450         if (aLoadData.mParentData->mIsCrossOriginNoCORS ||
1451             aLoadData.mParentData->mBlockResourceTiming) {
1452           // Set a flag so any other stylesheet triggered by this one will
1453           // not be reported
1454           aLoadData.mBlockResourceTiming = true;
1455 
1456           // Mark the channel so PerformanceMainThread::AddEntry will not
1457           // report the resource.
1458           timedChannel->SetReportResourceTiming(false);
1459         }
1460 
1461       } else {
1462         timedChannel->SetInitiatorType(u"link"_ns);
1463       }
1464     }
1465   }
1466 
1467   // Now tell the channel we expect text/css data back....  We do
1468   // this before opening it, so it's only treated as a hint.
1469   channel->SetContentType("text/css"_ns);
1470 
1471   // We don't have to hold on to the stream loader.  The ownership
1472   // model is: Necko owns the stream loader, which owns the load data,
1473   // which owns us
1474   auto streamLoader = MakeRefPtr<StreamLoader>(aLoadData);
1475   if (mDocument) {
1476     net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
1477                         nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, mDocument);
1478   }
1479 
1480   rv = channel->AsyncOpen(streamLoader);
1481   if (NS_FAILED(rv)) {
1482     LOG_ERROR(("  Failed to create stream loader"));
1483     streamLoader->ChannelOpenFailed(rv);
1484     // NOTE: NotifyStop will be done in SheetComplete -> NotifyObservers.
1485     aLoadData.NotifyStart(channel);
1486     SheetComplete(aLoadData, rv);
1487     return rv;
1488   }
1489 
1490 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1491   if (nsCOMPtr<nsIHttpChannelInternal> hci = do_QueryInterface(channel)) {
1492     hci->DoDiagnosticAssertWhenOnStopNotCalledOnDestroy();
1493   }
1494 #endif
1495 
1496   if (mSheets) {
1497     mSheets->LoadStarted(key, aLoadData);
1498   }
1499   return NS_OK;
1500 }
1501 
1502 /**
1503  * ParseSheet handles parsing the data stream.
1504  */
ParseSheet(const nsACString & aBytes,SheetLoadData & aLoadData,AllowAsyncParse aAllowAsync)1505 Loader::Completed Loader::ParseSheet(const nsACString& aBytes,
1506                                      SheetLoadData& aLoadData,
1507                                      AllowAsyncParse aAllowAsync) {
1508   LOG(("css::Loader::ParseSheet"));
1509   if (aLoadData.mURI) {
1510     LOG_URI("  Load succeeded for URI: '%s', parsing", aLoadData.mURI);
1511   }
1512   AUTO_PROFILER_LABEL("css::Loader::ParseSheet", LAYOUT_CSSParsing);
1513 
1514   ++mParsedSheetCount;
1515 
1516   aLoadData.mIsBeingParsed = true;
1517 
1518   StyleSheet* sheet = aLoadData.mSheet;
1519   MOZ_ASSERT(sheet);
1520 
1521   // Some cases, like inline style and UA stylesheets, need to be parsed
1522   // synchronously. The former may trigger child loads, the latter must not.
1523   if (aLoadData.mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
1524     sheet->ParseSheetSync(this, aBytes, &aLoadData, aLoadData.mLineNumber);
1525     aLoadData.mIsBeingParsed = false;
1526 
1527     bool noPendingChildren = aLoadData.mPendingChildren == 0;
1528     MOZ_ASSERT_IF(aLoadData.mSyncLoad, noPendingChildren);
1529     if (noPendingChildren) {
1530       SheetComplete(aLoadData, NS_OK);
1531       return Completed::Yes;
1532     }
1533     return Completed::No;
1534   }
1535 
1536   // This parse does not need to be synchronous. \o/
1537   //
1538   // Note that load is already blocked from IncrementOngoingLoadCount(), and
1539   // will be unblocked from SheetFinishedParsingAsync which will end up in
1540   // NotifyObservers as needed.
1541   nsCOMPtr<nsISerialEventTarget> target = DispatchTarget();
1542   sheet->ParseSheet(*this, aBytes, aLoadData)
1543       ->Then(
1544           target, __func__,
1545           [loadData = RefPtr<SheetLoadData>(&aLoadData)](bool aDummy) {
1546             MOZ_ASSERT(NS_IsMainThread());
1547             loadData->SheetFinishedParsingAsync();
1548           },
1549           [] { MOZ_CRASH("rejected parse promise"); });
1550   return Completed::No;
1551 }
1552 
NotifyObservers(SheetLoadData & aData,nsresult aStatus)1553 void Loader::NotifyObservers(SheetLoadData& aData, nsresult aStatus) {
1554   RecordUseCountersIfNeeded(mDocument, aData.mUseCounters.get());
1555   if (aData.mURI) {
1556     mLoadsPerformed.PutEntry(SheetLoadDataHashKey(aData));
1557     aData.NotifyStop(aStatus);
1558     // NOTE(emilio): This needs to happen before notifying observers, as
1559     // FontFaceSet for example checks for pending sheet loads from the
1560     // StyleSheetLoaded callback.
1561     if (aData.BlocksLoadEvent()) {
1562       DecrementOngoingLoadCount();
1563     }
1564   }
1565 
1566   if (aData.mMustNotify) {
1567     if (aData.mObserver) {
1568       LOG(("  Notifying observer %p for data %p.  deferred: %d",
1569            aData.mObserver.get(), &aData, aData.ShouldDefer()));
1570       aData.mObserver->StyleSheetLoaded(aData.mSheet, aData.ShouldDefer(),
1571                                         aStatus);
1572     }
1573 
1574     for (nsCOMPtr<nsICSSLoaderObserver> obs : mObservers.ForwardRange()) {
1575       LOG(("  Notifying global observer %p for data %p.  deferred: %d",
1576            obs.get(), &aData, aData.ShouldDefer()));
1577       obs->StyleSheetLoaded(aData.mSheet, aData.ShouldDefer(), aStatus);
1578     }
1579   }
1580 
1581   if (mPendingLoadCount && mPendingLoadCount == mOngoingLoadCount) {
1582     LOG(("  No more loading sheets; starting deferred loads"));
1583     StartDeferredLoads();
1584   }
1585 }
1586 
1587 /**
1588  * SheetComplete is the do-it-all cleanup function.  It removes the
1589  * load data from the "loading" hashtable, adds the sheet to the
1590  * "completed" hashtable, massages the XUL cache, handles siblings of
1591  * the load data (other loads for the same URI), handles unblocking
1592  * blocked parent loads as needed, and most importantly calls
1593  * NS_RELEASE on the load data to destroy the whole mess.
1594  */
SheetComplete(SheetLoadData & aLoadData,nsresult aStatus)1595 void Loader::SheetComplete(SheetLoadData& aLoadData, nsresult aStatus) {
1596   LOG(("css::Loader::SheetComplete, status: 0x%" PRIx32,
1597        static_cast<uint32_t>(aStatus)));
1598   SharedStyleSheetCache::LoadCompleted(mSheets.get(), aLoadData, aStatus);
1599 }
1600 
1601 // static
MarkLoadTreeFailed(SheetLoadData & aLoadData,Loader * aOnlyForLoader)1602 void Loader::MarkLoadTreeFailed(SheetLoadData& aLoadData,
1603                                 Loader* aOnlyForLoader) {
1604   if (aLoadData.mURI) {
1605     LOG_URI("  Load failed: '%s'", aLoadData.mURI);
1606   }
1607 
1608   SheetLoadData* data = &aLoadData;
1609   do {
1610     if (!aOnlyForLoader || aOnlyForLoader == data->mLoader) {
1611       data->mLoadFailed = true;
1612       data->mSheet->MaybeRejectReplacePromise();
1613     }
1614 
1615     if (data->mParentData) {
1616       MarkLoadTreeFailed(*data->mParentData, aOnlyForLoader);
1617     }
1618 
1619     data = data->mNext;
1620   } while (data);
1621 }
1622 
LookupInlineSheetInCache(const nsAString & aBuffer)1623 RefPtr<StyleSheet> Loader::LookupInlineSheetInCache(const nsAString& aBuffer) {
1624   auto result = mInlineSheets.Lookup(aBuffer);
1625   if (!result) {
1626     return nullptr;
1627   }
1628   if (result.Data()->HasModifiedRules()) {
1629     // Remove it now that we know that we're never going to use this stylesheet
1630     // again.
1631     result.Remove();
1632     return nullptr;
1633   }
1634   return result.Data()->Clone(nullptr, nullptr, nullptr, nullptr);
1635 }
1636 
MaybeNotifyPreloadUsed(SheetLoadData & aData)1637 void Loader::MaybeNotifyPreloadUsed(SheetLoadData& aData) {
1638   if (!mDocument) {
1639     return;
1640   }
1641 
1642   auto key = PreloadHashKey::CreateAsStyle(aData);
1643   RefPtr<PreloaderBase> preload = mDocument->Preloads().LookupPreload(key);
1644   if (!preload) {
1645     return;
1646   }
1647 
1648   preload->NotifyUsage();
1649 }
1650 
LoadInlineStyle(const SheetInfo & aInfo,const nsAString & aBuffer,uint32_t aLineNumber,nsICSSLoaderObserver * aObserver)1651 Result<Loader::LoadSheetResult, nsresult> Loader::LoadInlineStyle(
1652     const SheetInfo& aInfo, const nsAString& aBuffer, uint32_t aLineNumber,
1653     nsICSSLoaderObserver* aObserver) {
1654   LOG(("css::Loader::LoadInlineStyle"));
1655   MOZ_ASSERT(aInfo.mContent);
1656 
1657   if (!mEnabled) {
1658     LOG_WARN(("  Not enabled"));
1659     return Err(NS_ERROR_NOT_AVAILABLE);
1660   }
1661 
1662   if (!mDocument) {
1663     return Err(NS_ERROR_NOT_INITIALIZED);
1664   }
1665 
1666   MOZ_ASSERT(LinkStyle::FromNodeOrNull(aInfo.mContent),
1667              "Element is not a style linking element!");
1668 
1669   // Since we're not planning to load a URI, no need to hand a principal to the
1670   // load data or to CreateSheet().
1671 
1672   // Check IsAlternateSheet now, since it can mutate our document.
1673   auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
1674   LOG(("  Sheet is alternate: %d", static_cast<int>(isAlternate)));
1675 
1676   // Use the document's base URL so that @import in the inline sheet picks up
1677   // the right base.
1678   nsIURI* baseURI = aInfo.mContent->GetBaseURI();
1679   nsIURI* sheetURI = aInfo.mContent->OwnerDoc()->GetDocumentURI();
1680   nsIURI* originalURI = nullptr;
1681 
1682   MOZ_ASSERT(aInfo.mIntegrity.IsEmpty());
1683 
1684   nsIPrincipal* loadingPrincipal = LoaderPrincipal();
1685   nsIPrincipal* principal = aInfo.mTriggeringPrincipal
1686                                 ? aInfo.mTriggeringPrincipal.get()
1687                                 : loadingPrincipal;
1688 
1689   // We only cache sheets if in shadow trees, since regular document sheets are
1690   // likely to be unique.
1691   const bool isWorthCaching = aInfo.mContent->IsInShadowTree();
1692   RefPtr<StyleSheet> sheet;
1693   if (isWorthCaching) {
1694     sheet = LookupInlineSheetInCache(aBuffer);
1695   }
1696   const bool sheetFromCache = !!sheet;
1697   if (!sheet) {
1698     sheet = MakeRefPtr<StyleSheet>(eAuthorSheetFeatures, aInfo.mCORSMode,
1699                                    SRIMetadata{});
1700     sheet->SetURIs(sheetURI, originalURI, baseURI);
1701     nsCOMPtr<nsIReferrerInfo> referrerInfo =
1702         ReferrerInfo::CreateForInternalCSSResources(aInfo.mContent->OwnerDoc());
1703     sheet->SetReferrerInfo(referrerInfo);
1704 
1705     nsIPrincipal* sheetPrincipal = principal;
1706     if (aInfo.mTriggeringPrincipal) {
1707       // The triggering principal may be an expanded principal, which is safe to
1708       // use for URL security checks, but not as the loader principal for a
1709       // stylesheet. So treat this as principal inheritance, and downgrade if
1710       // necessary.
1711       sheetPrincipal =
1712           BasePrincipal::Cast(aInfo.mTriggeringPrincipal)->PrincipalToInherit();
1713     }
1714 
1715     // We never actually load this, so just set its principal directly
1716     sheet->SetPrincipal(sheetPrincipal);
1717   }
1718 
1719   auto matched = PrepareSheet(*sheet, aInfo.mTitle, aInfo.mMedia, nullptr,
1720                               isAlternate, aInfo.mIsExplicitlyEnabled);
1721 
1722   if (auto* linkStyle = LinkStyle::FromNodeOrNull(aInfo.mContent)) {
1723     linkStyle->SetStyleSheet(sheet);
1724   }
1725   if (sheet->IsComplete()) {
1726     InsertSheetInTree(*sheet, aInfo.mContent);
1727   }
1728 
1729   Completed completed;
1730   if (sheetFromCache) {
1731     MOZ_ASSERT(sheet->IsComplete());
1732     completed = Completed::Yes;
1733   } else {
1734     auto data = MakeRefPtr<SheetLoadData>(
1735         this, aInfo.mTitle, nullptr, sheet, false, aInfo.mContent, isAlternate,
1736         matched, StylePreloadKind::None, aObserver, principal,
1737         aInfo.mReferrerInfo, aInfo.mContent);
1738     data->mLineNumber = aLineNumber;
1739 
1740     // Parse completion releases the load data.
1741     //
1742     // Note that we need to parse synchronously, since the web expects that the
1743     // effects of inline stylesheets are visible immediately (aside from
1744     // @imports).
1745     NS_ConvertUTF16toUTF8 utf8(aBuffer);
1746     completed = ParseSheet(utf8, *data, AllowAsyncParse::No);
1747     if (completed == Completed::Yes) {
1748       // TODO(emilio): Try to cache sheets with @import rules, maybe?
1749       if (isWorthCaching) {
1750         mInlineSheets.InsertOrUpdate(aBuffer, std::move(sheet));
1751       }
1752     } else {
1753       data->mMustNotify = true;
1754     }
1755   }
1756 
1757   return LoadSheetResult{completed, isAlternate, matched};
1758 }
1759 
LoadStyleLink(const SheetInfo & aInfo,nsICSSLoaderObserver * aObserver)1760 Result<Loader::LoadSheetResult, nsresult> Loader::LoadStyleLink(
1761     const SheetInfo& aInfo, nsICSSLoaderObserver* aObserver) {
1762   MOZ_ASSERT(aInfo.mURI, "Must have URL to load");
1763   LOG(("css::Loader::LoadStyleLink"));
1764   LOG_URI("  Link uri: '%s'", aInfo.mURI);
1765   LOG(("  Link title: '%s'", NS_ConvertUTF16toUTF8(aInfo.mTitle).get()));
1766   LOG(("  Link media: '%s'", NS_ConvertUTF16toUTF8(aInfo.mMedia).get()));
1767   LOG(("  Link alternate rel: %d", aInfo.mHasAlternateRel));
1768 
1769   if (!mEnabled) {
1770     LOG_WARN(("  Not enabled"));
1771     return Err(NS_ERROR_NOT_AVAILABLE);
1772   }
1773 
1774   if (!mDocument) {
1775     return Err(NS_ERROR_NOT_INITIALIZED);
1776   }
1777 
1778   MOZ_ASSERT_IF(aInfo.mContent,
1779                 aInfo.mContent->NodePrincipal() == mDocument->NodePrincipal());
1780   nsIPrincipal* loadingPrincipal = LoaderPrincipal();
1781   nsIPrincipal* principal = aInfo.mTriggeringPrincipal
1782                                 ? aInfo.mTriggeringPrincipal.get()
1783                                 : loadingPrincipal;
1784 
1785   nsINode* context = aInfo.mContent;
1786   if (!context) {
1787     context = mDocument;
1788   }
1789 
1790   bool syncLoad = aInfo.mContent && aInfo.mContent->IsInUAWidget() &&
1791                   IsChromeURI(aInfo.mURI);
1792   LOG(("  Link sync load: '%s'", syncLoad ? "true" : "false"));
1793   MOZ_ASSERT_IF(syncLoad, !aObserver);
1794 
1795   nsresult rv =
1796       CheckContentPolicy(loadingPrincipal, principal, aInfo.mURI, context,
1797                          aInfo.mNonce, StylePreloadKind::None);
1798   if (NS_WARN_IF(NS_FAILED(rv))) {
1799     // Don't fire the error event if our document is loaded as data.  We're
1800     // supposed to not even try to do loads in that case... Unfortunately, we
1801     // implement that via nsDataDocumentContentPolicy, which doesn't have a good
1802     // way to communicate back to us that _it_ is the thing that blocked the
1803     // load.
1804     if (aInfo.mContent && !mDocument->IsLoadedAsData()) {
1805       // Fire an async error event on it.
1806       RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
1807           new LoadBlockingAsyncEventDispatcher(aInfo.mContent, u"error"_ns,
1808                                                CanBubble::eNo,
1809                                                ChromeOnlyDispatch::eNo);
1810       loadBlockingAsyncDispatcher->PostDOMEvent();
1811     }
1812     return Err(rv);
1813   }
1814 
1815   // Check IsAlternateSheet now, since it can mutate our document and make
1816   // pending sheets go to the non-pending state.
1817   auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
1818   auto [sheet, state] = CreateSheet(aInfo, eAuthorSheetFeatures, syncLoad,
1819                                     StylePreloadKind::None);
1820 
1821   LOG(("  Sheet is alternate: %d", static_cast<int>(isAlternate)));
1822 
1823   auto matched = PrepareSheet(*sheet, aInfo.mTitle, aInfo.mMedia, nullptr,
1824                               isAlternate, aInfo.mIsExplicitlyEnabled);
1825 
1826   if (auto* linkStyle = LinkStyle::FromNodeOrNull(aInfo.mContent)) {
1827     linkStyle->SetStyleSheet(sheet);
1828   }
1829   if (sheet->IsComplete()) {
1830     InsertSheetInTree(*sheet, aInfo.mContent);
1831   }
1832 
1833   // We may get here with no content for Link: headers for example.
1834   MOZ_ASSERT(!aInfo.mContent || LinkStyle::FromNode(*aInfo.mContent),
1835              "If there is any node, it should be a LinkStyle");
1836   auto data = MakeRefPtr<SheetLoadData>(
1837       this, aInfo.mTitle, aInfo.mURI, sheet, syncLoad, aInfo.mContent,
1838       isAlternate, matched, StylePreloadKind::None, aObserver, principal,
1839       aInfo.mReferrerInfo, context);
1840 
1841   MaybeNotifyPreloadUsed(*data);
1842 
1843   if (state == SheetState::Complete) {
1844     LOG(("  Sheet already complete: 0x%p", sheet.get()));
1845     if (aObserver || !mObservers.IsEmpty() || aInfo.mContent) {
1846       rv = PostLoadEvent(std::move(data));
1847       if (NS_FAILED(rv)) {
1848         return Err(rv);
1849       }
1850     } else {
1851 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1852       // We don't have to notify anyone of this load, as it was complete, so
1853       // drop it intentionally.
1854       data->mIntentionallyDropped = true;
1855 #endif
1856     }
1857 
1858     // The load hasn't been completed yet, will be done in PostLoadEvent.
1859     return LoadSheetResult{Completed::No, isAlternate, matched};
1860   }
1861 
1862   // Now we need to actually load it.
1863   auto result = LoadSheetResult{Completed::No, isAlternate, matched};
1864 
1865   MOZ_ASSERT(result.ShouldBlock() == !data->ShouldDefer(),
1866              "These should better match!");
1867 
1868   // Load completion will free the data
1869   rv = LoadSheet(*data, state);
1870   if (NS_FAILED(rv)) {
1871     return Err(rv);
1872   }
1873 
1874   if (!syncLoad) {
1875     data->mMustNotify = true;
1876   }
1877   return result;
1878 }
1879 
HaveAncestorDataWithURI(SheetLoadData & aData,nsIURI * aURI)1880 static bool HaveAncestorDataWithURI(SheetLoadData& aData, nsIURI* aURI) {
1881   if (!aData.mURI) {
1882     // Inline style; this won't have any ancestors
1883     MOZ_ASSERT(!aData.mParentData, "How does inline style have a parent?");
1884     return false;
1885   }
1886 
1887   bool equal;
1888   if (NS_FAILED(aData.mURI->Equals(aURI, &equal)) || equal) {
1889     return true;
1890   }
1891 
1892   // Datas down the mNext chain have the same URI as aData, so we
1893   // don't have to compare to them.  But they might have different
1894   // parents, and we have to check all of those.
1895   SheetLoadData* data = &aData;
1896   do {
1897     if (data->mParentData &&
1898         HaveAncestorDataWithURI(*data->mParentData, aURI)) {
1899       return true;
1900     }
1901 
1902     data = data->mNext;
1903   } while (data);
1904 
1905   return false;
1906 }
1907 
LoadChildSheet(StyleSheet & aParentSheet,SheetLoadData * aParentData,nsIURI * aURL,dom::MediaList * aMedia,LoaderReusableStyleSheets * aReusableSheets)1908 nsresult Loader::LoadChildSheet(StyleSheet& aParentSheet,
1909                                 SheetLoadData* aParentData, nsIURI* aURL,
1910                                 dom::MediaList* aMedia,
1911                                 LoaderReusableStyleSheets* aReusableSheets) {
1912   LOG(("css::Loader::LoadChildSheet"));
1913   MOZ_ASSERT(aURL, "Must have a URI to load");
1914 
1915   if (!mEnabled) {
1916     LOG_WARN(("  Not enabled"));
1917     return NS_ERROR_NOT_AVAILABLE;
1918   }
1919 
1920   LOG_URI("  Child uri: '%s'", aURL);
1921 
1922   nsCOMPtr<nsINode> owningNode;
1923 
1924   // Check for an associated document or shadow root: if none, don't bother
1925   // walking up the parent sheets.
1926   if (aParentSheet.GetAssociatedDocumentOrShadowRoot()) {
1927     StyleSheet* topSheet = &aParentSheet;
1928     while (StyleSheet* parent = topSheet->GetParentSheet()) {
1929       topSheet = parent;
1930     }
1931     owningNode = topSheet->GetOwnerNode();
1932   }
1933 
1934   nsINode* context = nullptr;
1935   if (owningNode) {
1936     context = owningNode;
1937     MOZ_ASSERT(LoaderPrincipal() == owningNode->NodePrincipal());
1938   } else if (mDocument) {
1939     context = mDocument;
1940   }
1941 
1942   nsIPrincipal* principal = aParentSheet.Principal();
1943   nsresult rv = CheckContentPolicy(LoaderPrincipal(), principal, aURL, context,
1944                                    u""_ns, StylePreloadKind::None);
1945   if (NS_WARN_IF(NS_FAILED(rv))) {
1946     if (aParentData) {
1947       MarkLoadTreeFailed(*aParentData);
1948     }
1949     return rv;
1950   }
1951 
1952   nsCOMPtr<nsICSSLoaderObserver> observer;
1953 
1954   if (aParentData) {
1955     LOG(("  Have a parent load"));
1956     // Check for cycles
1957     if (HaveAncestorDataWithURI(*aParentData, aURL)) {
1958       // Houston, we have a loop, blow off this child and pretend this never
1959       // happened
1960       LOG_ERROR(("  @import cycle detected, dropping load"));
1961       return NS_OK;
1962     }
1963 
1964     NS_ASSERTION(aParentData->mSheet == &aParentSheet,
1965                  "Unexpected call to LoadChildSheet");
1966   } else {
1967     LOG(("  No parent load; must be CSSOM"));
1968     // No parent load data, so the sheet will need to be notified when
1969     // we finish, if it can be, if we do the load asynchronously.
1970     observer = &aParentSheet;
1971   }
1972 
1973   // Now that we know it's safe to load this (passes security check and not a
1974   // loop) do so.
1975   RefPtr<StyleSheet> sheet;
1976   SheetState state;
1977   if (aReusableSheets && aReusableSheets->FindReusableStyleSheet(aURL, sheet)) {
1978     state = SheetState::Complete;
1979   } else {
1980     // For now, use CORS_NONE for child sheets
1981     std::tie(sheet, state) = CreateSheet(
1982         aURL, nullptr, principal, aParentSheet.ParsingMode(), CORS_NONE,
1983         aParentData ? aParentData->mEncoding : nullptr,
1984         u""_ns,  // integrity is only checked on main sheet
1985         aParentData && aParentData->mSyncLoad, StylePreloadKind::None);
1986     PrepareSheet(*sheet, u""_ns, u""_ns, aMedia, IsAlternate::No,
1987                  IsExplicitlyEnabled::No);
1988   }
1989 
1990   MOZ_ASSERT(sheet);
1991   InsertChildSheet(*sheet, aParentSheet);
1992 
1993   auto data = MakeRefPtr<SheetLoadData>(
1994       this, aURL, sheet, aParentData, observer, principal,
1995       aParentSheet.GetReferrerInfo(), context);
1996 
1997   MaybeNotifyPreloadUsed(*data);
1998 
1999   if (state == SheetState::Complete) {
2000     LOG(("  Sheet already complete"));
2001     // We're completely done.  No need to notify, even, since the
2002     // @import rule addition/modification will trigger the right style
2003     // changes automatically.
2004 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2005     data->mIntentionallyDropped = true;
2006 #endif
2007     return NS_OK;
2008   }
2009 
2010   bool syncLoad = data->mSyncLoad;
2011 
2012   // Load completion will release the data
2013   rv = LoadSheet(*data, state);
2014   NS_ENSURE_SUCCESS(rv, rv);
2015 
2016   if (!syncLoad) {
2017     data->mMustNotify = true;
2018   }
2019   return rv;
2020 }
2021 
LoadSheetSync(nsIURI * aURL,SheetParsingMode aParsingMode,UseSystemPrincipal aUseSystemPrincipal)2022 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheetSync(
2023     nsIURI* aURL, SheetParsingMode aParsingMode,
2024     UseSystemPrincipal aUseSystemPrincipal) {
2025   LOG(("css::Loader::LoadSheetSync"));
2026   nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(nullptr);
2027   return InternalLoadNonDocumentSheet(
2028       aURL, StylePreloadKind::None, aParsingMode, aUseSystemPrincipal, nullptr,
2029       referrerInfo, nullptr, CORS_NONE, u""_ns);
2030 }
2031 
LoadSheet(nsIURI * aURI,SheetParsingMode aParsingMode,UseSystemPrincipal aUseSystemPrincipal,nsICSSLoaderObserver * aObserver)2032 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheet(
2033     nsIURI* aURI, SheetParsingMode aParsingMode,
2034     UseSystemPrincipal aUseSystemPrincipal, nsICSSLoaderObserver* aObserver) {
2035   nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(nullptr);
2036   return InternalLoadNonDocumentSheet(
2037       aURI, StylePreloadKind::None, aParsingMode, aUseSystemPrincipal, nullptr,
2038       referrerInfo, aObserver, CORS_NONE, u""_ns);
2039 }
2040 
LoadSheet(nsIURI * aURL,StylePreloadKind aPreloadKind,const Encoding * aPreloadEncoding,nsIReferrerInfo * aReferrerInfo,nsICSSLoaderObserver * aObserver,CORSMode aCORSMode,const nsAString & aIntegrity)2041 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheet(
2042     nsIURI* aURL, StylePreloadKind aPreloadKind,
2043     const Encoding* aPreloadEncoding, nsIReferrerInfo* aReferrerInfo,
2044     nsICSSLoaderObserver* aObserver, CORSMode aCORSMode,
2045     const nsAString& aIntegrity) {
2046   LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
2047   return InternalLoadNonDocumentSheet(
2048       aURL, aPreloadKind, eAuthorSheetFeatures, UseSystemPrincipal::No,
2049       aPreloadEncoding, aReferrerInfo, aObserver, aCORSMode, aIntegrity);
2050 }
2051 
InternalLoadNonDocumentSheet(nsIURI * aURL,StylePreloadKind aPreloadKind,SheetParsingMode aParsingMode,UseSystemPrincipal aUseSystemPrincipal,const Encoding * aPreloadEncoding,nsIReferrerInfo * aReferrerInfo,nsICSSLoaderObserver * aObserver,CORSMode aCORSMode,const nsAString & aIntegrity)2052 Result<RefPtr<StyleSheet>, nsresult> Loader::InternalLoadNonDocumentSheet(
2053     nsIURI* aURL, StylePreloadKind aPreloadKind, SheetParsingMode aParsingMode,
2054     UseSystemPrincipal aUseSystemPrincipal, const Encoding* aPreloadEncoding,
2055     nsIReferrerInfo* aReferrerInfo, nsICSSLoaderObserver* aObserver,
2056     CORSMode aCORSMode, const nsAString& aIntegrity) {
2057   MOZ_ASSERT(aURL, "Must have a URI to load");
2058   MOZ_ASSERT(aUseSystemPrincipal == UseSystemPrincipal::No || !aObserver,
2059              "Shouldn't load system-principal sheets async");
2060   MOZ_ASSERT(aReferrerInfo, "Must have referrerInfo");
2061 
2062   LOG_URI("  Non-document sheet uri: '%s'", aURL);
2063 
2064   if (!mEnabled) {
2065     LOG_WARN(("  Not enabled"));
2066     return Err(NS_ERROR_NOT_AVAILABLE);
2067   }
2068 
2069   nsIPrincipal* loadingPrincipal = LoaderPrincipal();
2070   nsIPrincipal* triggeringPrincipal = loadingPrincipal;
2071   nsresult rv = CheckContentPolicy(loadingPrincipal, triggeringPrincipal, aURL,
2072                                    mDocument, u""_ns, aPreloadKind);
2073   if (NS_FAILED(rv)) {
2074     return Err(rv);
2075   }
2076 
2077   bool syncLoad = !aObserver;
2078   auto [sheet, state] =
2079       CreateSheet(aURL, nullptr, triggeringPrincipal, aParsingMode, aCORSMode,
2080                   aPreloadEncoding, aIntegrity, syncLoad, aPreloadKind);
2081 
2082   PrepareSheet(*sheet, u""_ns, u""_ns, nullptr, IsAlternate::No,
2083                IsExplicitlyEnabled::No);
2084 
2085   auto data = MakeRefPtr<SheetLoadData>(
2086       this, aURL, sheet, syncLoad, aUseSystemPrincipal, aPreloadKind,
2087       aPreloadEncoding, aObserver, triggeringPrincipal, aReferrerInfo,
2088       mDocument);
2089   if (state == SheetState::Complete) {
2090     LOG(("  Sheet already complete"));
2091     if (aObserver || !mObservers.IsEmpty()) {
2092       rv = PostLoadEvent(std::move(data));
2093       if (NS_FAILED(rv)) {
2094         return Err(rv);
2095       }
2096     } else {
2097       // We don't have to notify anyone of this load, as it was complete, so
2098       // drop it intentionally.
2099 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2100       data->mIntentionallyDropped = true;
2101 #endif
2102     }
2103     return sheet;
2104   }
2105 
2106   rv = LoadSheet(*data, state);
2107   if (NS_FAILED(rv)) {
2108     return Err(rv);
2109   }
2110   if (aObserver) {
2111     data->mMustNotify = true;
2112   }
2113   return sheet;
2114 }
2115 
PostLoadEvent(RefPtr<SheetLoadData> aLoadData)2116 nsresult Loader::PostLoadEvent(RefPtr<SheetLoadData> aLoadData) {
2117   LOG(("css::Loader::PostLoadEvent"));
2118   mPostedEvents.AppendElement(aLoadData);
2119 
2120   nsresult rv;
2121   RefPtr<SheetLoadData> runnable(aLoadData);
2122   if (mDocument) {
2123     rv = mDocument->Dispatch(TaskCategory::Other, runnable.forget());
2124   } else if (mDocGroup) {
2125     rv = mDocGroup->Dispatch(TaskCategory::Other, runnable.forget());
2126   } else {
2127     rv = SchedulerGroup::Dispatch(TaskCategory::Other, runnable.forget());
2128   }
2129 
2130   if (NS_FAILED(rv)) {
2131     NS_WARNING("failed to dispatch stylesheet load event");
2132     mPostedEvents.RemoveElement(aLoadData);
2133   } else {
2134     if (aLoadData->BlocksLoadEvent()) {
2135       IncrementOngoingLoadCount();
2136     }
2137 
2138     // We want to notify the observer for this data.
2139     aLoadData->mMustNotify = true;
2140     aLoadData->mSheetAlreadyComplete = true;
2141 
2142     // If we get to this code, aSheet loaded correctly at some point, so
2143     // we can just schedule a load event and don't need to touch the
2144     // data's mLoadFailed.  Note that we do this here and not from
2145     // inside our SheetComplete so that we don't end up running the load
2146     // event async.
2147     MOZ_ASSERT(!aLoadData->mLoadFailed, "Why are we marked as failed?");
2148     aLoadData->ScheduleLoadEventIfNeeded();
2149   }
2150 
2151   return rv;
2152 }
2153 
HandleLoadEvent(SheetLoadData & aEvent)2154 void Loader::HandleLoadEvent(SheetLoadData& aEvent) {
2155   // XXXbz can't assert this yet.... May not have an observer because
2156   // we're unblocking the parser
2157   // NS_ASSERTION(aEvent->mObserver, "Must have observer");
2158   NS_ASSERTION(aEvent.mSheet, "Must have sheet");
2159 
2160   mPostedEvents.RemoveElement(&aEvent);
2161   SheetComplete(aEvent, NS_OK);
2162 }
2163 
Stop()2164 void Loader::Stop() {
2165   if (mSheets) {
2166     mSheets->CancelLoadsForLoader(*this);
2167   }
2168 
2169   auto arr = std::move(mPostedEvents);
2170   for (auto& data : arr) {
2171     data->mIsCancelled = true;
2172   }
2173 }
2174 
HasPendingLoads()2175 bool Loader::HasPendingLoads() { return mOngoingLoadCount; }
2176 
AddObserver(nsICSSLoaderObserver * aObserver)2177 void Loader::AddObserver(nsICSSLoaderObserver* aObserver) {
2178   MOZ_ASSERT(aObserver, "Must have observer");
2179   mObservers.AppendElementUnlessExists(aObserver);
2180 }
2181 
RemoveObserver(nsICSSLoaderObserver * aObserver)2182 void Loader::RemoveObserver(nsICSSLoaderObserver* aObserver) {
2183   mObservers.RemoveElement(aObserver);
2184 }
2185 
StartDeferredLoads()2186 void Loader::StartDeferredLoads() {
2187   if (mSheets && mPendingLoadCount) {
2188     mSheets->StartDeferredLoadsForLoader(
2189         *this, SharedStyleSheetCache::StartLoads::Always);
2190   }
2191 }
2192 
2193 NS_IMPL_CYCLE_COLLECTION_CLASS(Loader)
2194 
2195 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Loader)
2196   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheets);
2197   for (const auto& data : tmp->mInlineSheets.Values()) {
2198     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "Inline sheet cache in Loader");
2199     cb.NoteXPCOMChild(data);
2200   }
2201   for (nsCOMPtr<nsICSSLoaderObserver>& obs : tmp->mObservers.ForwardRange()) {
2202     ImplCycleCollectionTraverse(cb, obs, "mozilla::css::Loader.mObservers");
2203   }
2204   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
2205 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2206 
2207 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Loader)
2208   if (tmp->mSheets) {
2209     if (tmp->mDocument) {
2210       tmp->DeregisterFromSheetCache();
2211     }
2212     tmp->mSheets = nullptr;
2213   }
2214   tmp->mInlineSheets.Clear();
2215   tmp->mObservers.Clear();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocGroup)2216   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocGroup)
2217 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2218 
2219 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Loader, AddRef)
2220 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Loader, Release)
2221 
2222 size_t Loader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
2223   size_t n = aMallocSizeOf(this);
2224 
2225   n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
2226 
2227   n += mInlineSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
2228   for (const auto& entry : mInlineSheets) {
2229     n += entry.GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2230     // If the sheet has a parent, then its parent will report it so we don't
2231     // have to worry about it here.
2232     const StyleSheet* sheet = entry.GetWeak();
2233     MOZ_ASSERT(!sheet->GetParentSheet(),
2234                "How did an @import rule end up here?");
2235     if (!sheet->GetOwnerNode()) {
2236       n += sheet->SizeOfIncludingThis(aMallocSizeOf);
2237     }
2238   }
2239 
2240   // Measurement of the following members may be added later if DMD finds it is
2241   // worthwhile:
2242   // - mPostedEvents: transient, and should be small
2243   //
2244   // The following members aren't measured:
2245   // - mDocument, because it's a weak backpointer
2246 
2247   return n;
2248 }
2249 
LoaderPrincipal() const2250 nsIPrincipal* Loader::LoaderPrincipal() const {
2251   if (mDocument) {
2252     return mDocument->NodePrincipal();
2253   }
2254   // Loaders without a document do system loads.
2255   return nsContentUtils::GetSystemPrincipal();
2256 }
2257 
PartitionedPrincipal() const2258 nsIPrincipal* Loader::PartitionedPrincipal() const {
2259   if (mDocument && StaticPrefs::privacy_partition_network_state()) {
2260     return mDocument->PartitionedPrincipal();
2261   }
2262   return LoaderPrincipal();
2263 }
2264 
ShouldBypassCache() const2265 bool Loader::ShouldBypassCache() const {
2266   if (!mDocument) {
2267     return false;
2268   }
2269   RefPtr<nsILoadGroup> lg = mDocument->GetDocumentLoadGroup();
2270   if (!lg) {
2271     return false;
2272   }
2273   nsLoadFlags flags;
2274   if (NS_FAILED(lg->GetLoadFlags(&flags))) {
2275     return false;
2276   }
2277   return flags & (nsIRequest::LOAD_BYPASS_CACHE |
2278                   nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE);
2279 }
2280 
BlockOnload()2281 void Loader::BlockOnload() {
2282   if (mDocument) {
2283     mDocument->BlockOnload();
2284   }
2285 }
2286 
UnblockOnload(bool aFireSync)2287 void Loader::UnblockOnload(bool aFireSync) {
2288   if (mDocument) {
2289     mDocument->UnblockOnload(aFireSync);
2290   }
2291 }
2292 
DispatchTarget()2293 already_AddRefed<nsISerialEventTarget> Loader::DispatchTarget() {
2294   nsCOMPtr<nsISerialEventTarget> target;
2295   if (mDocument) {
2296     // If you change this, you may want to change StyleSheet::Replace
2297     target = mDocument->EventTargetFor(TaskCategory::Other);
2298   } else if (mDocGroup) {
2299     target = mDocGroup->EventTargetFor(TaskCategory::Other);
2300   } else {
2301     target = GetMainThreadSerialEventTarget();
2302   }
2303 
2304   return target.forget();
2305 }
2306 
2307 }  // namespace css
2308 }  // namespace mozilla
2309