tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Loader.cpp (86226B)


      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 "MainThreadUtils.h"
     12 #include "ReferrerInfo.h"
     13 #include "mozilla/AsyncEventDispatcher.h"
     14 #include "mozilla/AutoRestore.h"
     15 #include "mozilla/ConsoleReportCollector.h"
     16 #include "mozilla/Encoding.h"
     17 #include "mozilla/IntegerPrintfMacros.h"
     18 #include "mozilla/LoadInfo.h"
     19 #include "mozilla/Logging.h"
     20 #include "mozilla/MemoryReporting.h"
     21 #include "mozilla/PreloadHashKey.h"
     22 #include "mozilla/ProfilerLabels.h"
     23 #include "mozilla/ResultExtensions.h"
     24 #include "mozilla/ServoBindings.h"
     25 #include "mozilla/SharedStyleSheetCache.h"
     26 #include "mozilla/StaticPrefs_layout.h"
     27 #include "mozilla/StaticPrefs_network.h"
     28 #include "mozilla/StyleSheet.h"
     29 #include "mozilla/StyleSheetInlines.h"
     30 #include "mozilla/TimeStamp.h"
     31 #include "mozilla/Try.h"
     32 #include "mozilla/URLPreloader.h"
     33 #include "mozilla/css/ErrorReporter.h"
     34 #include "mozilla/css/StreamLoader.h"
     35 #include "mozilla/dom/DocGroup.h"
     36 #include "mozilla/dom/Document.h"
     37 #include "mozilla/dom/FetchPriority.h"
     38 #include "mozilla/dom/MediaList.h"
     39 #include "mozilla/dom/SRICheck.h"
     40 #include "mozilla/dom/SRILogHelper.h"
     41 #include "mozilla/dom/ShadowRoot.h"
     42 #include "mozilla/dom/URL.h"
     43 #include "mozilla/glean/LayoutMetrics.h"
     44 #include "mozilla/net/UrlClassifierFeatureFactory.h"
     45 #include "nsCOMPtr.h"
     46 #include "nsContentPolicyUtils.h"
     47 #include "nsContentSecurityManager.h"
     48 #include "nsContentUtils.h"
     49 #include "nsError.h"
     50 #include "nsHttpChannel.h"
     51 #include "nsICSSLoaderObserver.h"
     52 #include "nsICachingChannel.h"
     53 #include "nsIClassOfService.h"
     54 #include "nsIClassifiedChannel.h"
     55 #include "nsIContent.h"
     56 #include "nsIContentInlines.h"
     57 #include "nsICookieJarSettings.h"
     58 #include "nsIHttpChannel.h"
     59 #include "nsIHttpChannelInternal.h"
     60 #include "nsIPrincipal.h"
     61 #include "nsIScriptError.h"
     62 #include "nsIScriptSecurityManager.h"
     63 #include "nsISupportsPriority.h"
     64 #include "nsITimedChannel.h"
     65 #include "nsIURI.h"
     66 #include "nsMimeTypes.h"
     67 #include "nsQueryActor.h"
     68 #include "nsQueryObject.h"
     69 #include "nsString.h"
     70 #include "nsStringStream.h"
     71 #include "nsSyncLoadService.h"
     72 #include "nsThreadUtils.h"
     73 #include "nsXULPrototypeCache.h"
     74 
     75 using namespace mozilla::dom;
     76 using namespace mozilla::net;
     77 
     78 /**
     79 * OVERALL ARCHITECTURE
     80 *
     81 * The CSS Loader gets requests to load various sorts of style sheets:
     82 * inline style from <style> elements, linked style, @import-ed child
     83 * sheets, non-document sheets.  The loader handles the following tasks:
     84 * 1) Creation of the actual style sheet objects: CreateSheet()
     85 * 2) setting of the right media, title, enabled state, etc on the
     86 *    sheet: PrepareSheet()
     87 * 3) Insertion of the sheet in the proper cascade order:
     88 *    InsertSheetInTree() and InsertChildSheet()
     89 * 4) Load of the sheet: LoadSheet() including security checks
     90 * 5) Parsing of the sheet: ParseSheet()
     91 * 6) Cleanup: SheetComplete()
     92 *
     93 * The detailed documentation for these functions is found with the
     94 * function implementations.
     95 *
     96 * The following helper object is used:
     97 *    SheetLoadData -- a small class that is used to store all the
     98 *                     information needed for the loading of a sheet;
     99 *                     this class handles listening for the stream
    100 *                     loader completion and also handles charset
    101 *                     determination.
    102 */
    103 
    104 extern mozilla::LazyLogModule sCssLoaderLog;
    105 mozilla::LazyLogModule sCssLoaderLog("nsCSSLoader");
    106 
    107 static mozilla::LazyLogModule gSriPRLog("SRI");
    108 
    109 static bool IsPrivilegedURI(nsIURI* aURI) {
    110  return aURI->SchemeIs("chrome") || aURI->SchemeIs("resource");
    111 }
    112 
    113 #define LOG_ERROR(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Error, args)
    114 #define LOG_WARN(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Warning, args)
    115 #define LOG_DEBUG(args) MOZ_LOG(sCssLoaderLog, mozilla::LogLevel::Debug, args)
    116 #define LOG(args) LOG_DEBUG(args)
    117 
    118 #define LOG_ERROR_ENABLED() \
    119  MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Error)
    120 #define LOG_WARN_ENABLED() \
    121  MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Warning)
    122 #define LOG_DEBUG_ENABLED() \
    123  MOZ_LOG_TEST(sCssLoaderLog, mozilla::LogLevel::Debug)
    124 #define LOG_ENABLED() LOG_DEBUG_ENABLED()
    125 
    126 #define LOG_URI(format, uri)                      \
    127  PR_BEGIN_MACRO                                  \
    128  NS_ASSERTION(uri, "Logging null uri");          \
    129  if (LOG_ENABLED()) {                            \
    130    LOG((format, uri->GetSpecOrDefault().get())); \
    131  }                                               \
    132  PR_END_MACRO
    133 
    134 // And some convenience strings...
    135 static const char* const gStateStrings[] = {"NeedsParser", "Pending", "Loading",
    136                                            "Complete"};
    137 
    138 namespace mozilla {
    139 
    140 SheetLoadDataHashKey::SheetLoadDataHashKey(const css::SheetLoadData& aLoadData)
    141    : mURI(aLoadData.mURI),
    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      mIsLinkRelPreloadOrEarlyHint(aLoadData.IsLinkRelPreloadOrEarlyHint()) {
    149  MOZ_COUNT_CTOR(SheetLoadDataHashKey);
    150  MOZ_ASSERT(mURI);
    151  MOZ_ASSERT(mLoaderPrincipal);
    152  MOZ_ASSERT(mPartitionPrincipal);
    153  aLoadData.mSheet->GetIntegrity(mSRIMetadata);
    154 }
    155 
    156 bool SheetLoadDataHashKey::KeyEquals(const SheetLoadDataHashKey& aKey) const {
    157  {
    158    bool eq;
    159    if (NS_FAILED(mURI->Equals(aKey.mURI, &eq)) || !eq) {
    160      return false;
    161    }
    162  }
    163 
    164  LOG_URI("KeyEquals(%s)\n", mURI);
    165 
    166  if (mParsingMode != aKey.mParsingMode) {
    167    LOG((" > Parsing mode mismatch\n"));
    168    return false;
    169  }
    170 
    171  // Chrome URIs ignore everything else.
    172  if (IsPrivilegedURI(mURI)) {
    173    return true;
    174  }
    175 
    176  if (!mPartitionPrincipal->Equals(aKey.mPartitionPrincipal)) {
    177    LOG((" > Partition principal mismatch\n"));
    178    return false;
    179  }
    180 
    181  if (mCORSMode != aKey.mCORSMode) {
    182    LOG((" > CORS mismatch\n"));
    183    return false;
    184  }
    185 
    186  if (mCompatMode != aKey.mCompatMode) {
    187    LOG((" > Quirks mismatch\n"));
    188    return false;
    189  }
    190 
    191  // If encoding differs, then don't reuse the cache.
    192  //
    193  // TODO(emilio): When the encoding is determined from the request (either
    194  // BOM or Content-Length or @charset), we could do a bit better,
    195  // theoretically.
    196  if (mEncodingGuess != aKey.mEncodingGuess) {
    197    LOG((" > Encoding guess mismatch\n"));
    198    return false;
    199  }
    200 
    201  // Consuming stylesheet tags must never coalesce to <link preload> initiated
    202  // speculative loads with a weaker SRI hash or its different value.  This
    203  // check makes sure that regular loads will never find such a weaker preload
    204  // and rather start a new, independent load with new, stronger SRI checker
    205  // set up, so that integrity is ensured.
    206  if (mIsLinkRelPreloadOrEarlyHint != aKey.mIsLinkRelPreloadOrEarlyHint) {
    207    const auto& linkPreloadMetadata =
    208        mIsLinkRelPreloadOrEarlyHint ? mSRIMetadata : aKey.mSRIMetadata;
    209    const auto& consumerPreloadMetadata =
    210        mIsLinkRelPreloadOrEarlyHint ? aKey.mSRIMetadata : mSRIMetadata;
    211    if (!consumerPreloadMetadata.CanTrustBeDelegatedTo(linkPreloadMetadata)) {
    212      LOG((" > Preload SRI metadata mismatch\n"));
    213      return false;
    214    }
    215  }
    216 
    217  return true;
    218 }
    219 
    220 namespace css {
    221 
    222 static NotNull<const Encoding*> GetFallbackEncoding(
    223    Loader& aLoader, nsINode* aOwningNode,
    224    const Encoding* aPreloadOrParentDataEncoding) {
    225  const Encoding* encoding;
    226  // Now try the charset on the <link> or processing instruction
    227  // that loaded us
    228  if (aOwningNode) {
    229    nsAutoString label16;
    230    LinkStyle::FromNode(*aOwningNode)->GetCharset(label16);
    231    encoding = Encoding::ForLabel(label16);
    232    if (encoding) {
    233      return WrapNotNull(encoding);
    234    }
    235  }
    236 
    237  // Try preload or parent sheet encoding.
    238  if (aPreloadOrParentDataEncoding) {
    239    return WrapNotNull(aPreloadOrParentDataEncoding);
    240  }
    241 
    242  if (auto* doc = aLoader.GetDocument()) {
    243    // Use the document charset.
    244    return doc->GetDocumentCharacterSet();
    245  }
    246 
    247  return UTF_8_ENCODING;
    248 }
    249 
    250 /********************************
    251 * SheetLoadData implementation *
    252 ********************************/
    253 NS_IMPL_ISUPPORTS(SheetLoadData, nsISupports)
    254 
    255 SheetLoadData::SheetLoadData(
    256    css::Loader* aLoader, const nsAString& aTitle, nsIURI* aURI,
    257    StyleSheet* aSheet, SyncLoad aSyncLoad, nsINode* aOwningNode,
    258    IsAlternate aIsAlternate, MediaMatched aMediaMatches,
    259    StylePreloadKind aPreloadKind, nsICSSLoaderObserver* aObserver,
    260    nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
    261    const nsAString& aNonce, FetchPriority aFetchPriority,
    262    already_AddRefed<SubResourceNetworkMetadataHolder>&& aNetworkMetadata)
    263    : mLoader(aLoader),
    264      mTitle(aTitle),
    265      mEncoding(nullptr),
    266      mURI(aURI),
    267      mSheet(aSheet),
    268      mPendingChildren(0),
    269      mSyncLoad(aSyncLoad == SyncLoad::Yes),
    270      mIsNonDocumentSheet(false),
    271      mIsChildSheet(aSheet->GetParentSheet()),
    272      mIsBeingParsed(false),
    273      mIsLoading(false),
    274      mIsCancelled(false),
    275      mMustNotify(false),
    276      mHadOwnerNode(!!aOwningNode),
    277      mWasAlternate(aIsAlternate == IsAlternate::Yes),
    278      mMediaMatched(aMediaMatches == MediaMatched::Yes),
    279      mUseSystemPrincipal(false),
    280      mSheetAlreadyComplete(false),
    281      mLoadFailed(false),
    282      mShouldEmulateNotificationsForCachedLoad(false),
    283      mPreloadKind(aPreloadKind),
    284      mObserver(aObserver),
    285      mTriggeringPrincipal(aTriggeringPrincipal),
    286      mReferrerInfo(aReferrerInfo),
    287      mNonce(aNonce),
    288      mFetchPriority{aFetchPriority},
    289      mGuessedEncoding(GetFallbackEncoding(*aLoader, aOwningNode, nullptr)),
    290      mCompatMode(aLoader->CompatMode(aPreloadKind)),
    291      mRecordErrors(
    292          aLoader && aLoader->GetDocument() &&
    293          css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())),
    294      mNetworkMetadata(std::move(aNetworkMetadata)) {
    295  MOZ_ASSERT(!aOwningNode || dom::LinkStyle::FromNode(*aOwningNode),
    296             "Must implement LinkStyle");
    297  MOZ_ASSERT(mTriggeringPrincipal);
    298  MOZ_ASSERT(mLoader, "Must have a loader!");
    299 }
    300 
    301 SheetLoadData::SheetLoadData(
    302    css::Loader* aLoader, nsIURI* aURI, StyleSheet* aSheet,
    303    SheetLoadData* aParentData, nsICSSLoaderObserver* aObserver,
    304    nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
    305    already_AddRefed<SubResourceNetworkMetadataHolder>&& aNetworkMetadata)
    306    : mLoader(aLoader),
    307      mEncoding(nullptr),
    308      mURI(aURI),
    309      mSheet(aSheet),
    310      mParentData(aParentData),
    311      mPendingChildren(0),
    312      mSyncLoad(aParentData && aParentData->mSyncLoad),
    313      mIsNonDocumentSheet(aParentData && aParentData->mIsNonDocumentSheet),
    314      mIsChildSheet(aSheet->GetParentSheet()),
    315      mIsBeingParsed(false),
    316      mIsLoading(false),
    317      mIsCancelled(false),
    318      mMustNotify(false),
    319      mHadOwnerNode(false),
    320      mWasAlternate(false),
    321      mMediaMatched(true),
    322      mUseSystemPrincipal(aParentData && aParentData->mUseSystemPrincipal),
    323      mSheetAlreadyComplete(false),
    324      mLoadFailed(false),
    325      mShouldEmulateNotificationsForCachedLoad(false),
    326      mPreloadKind(StylePreloadKind::None),
    327      mObserver(aObserver),
    328      mTriggeringPrincipal(aTriggeringPrincipal),
    329      mReferrerInfo(aReferrerInfo),
    330      mNonce(u""_ns),
    331      mFetchPriority(FetchPriority::Auto),
    332      mGuessedEncoding(GetFallbackEncoding(
    333          *aLoader, nullptr, aParentData ? aParentData->mEncoding : nullptr)),
    334      mCompatMode(aLoader->CompatMode(mPreloadKind)),
    335      mRecordErrors(
    336          aLoader && aLoader->GetDocument() &&
    337          css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())),
    338      mNetworkMetadata(std::move(aNetworkMetadata)) {
    339  MOZ_ASSERT(mLoader, "Must have a loader!");
    340  MOZ_ASSERT(mTriggeringPrincipal);
    341  MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
    342             "Shouldn't use system principal for async loads");
    343  MOZ_ASSERT_IF(aParentData, mIsChildSheet);
    344 }
    345 
    346 SheetLoadData::SheetLoadData(
    347    css::Loader* aLoader, nsIURI* aURI, StyleSheet* aSheet, SyncLoad aSyncLoad,
    348    UseSystemPrincipal aUseSystemPrincipal, StylePreloadKind aPreloadKind,
    349    const Encoding* aPreloadEncoding, nsICSSLoaderObserver* aObserver,
    350    nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
    351    const nsAString& aNonce, FetchPriority aFetchPriority,
    352    already_AddRefed<SubResourceNetworkMetadataHolder>&& aNetworkMetadata)
    353    : mLoader(aLoader),
    354      mEncoding(nullptr),
    355      mURI(aURI),
    356      mSheet(aSheet),
    357      mPendingChildren(0),
    358      mSyncLoad(aSyncLoad == SyncLoad::Yes),
    359      mIsNonDocumentSheet(true),
    360      mIsChildSheet(false),
    361      mIsBeingParsed(false),
    362      mIsLoading(false),
    363      mIsCancelled(false),
    364      mMustNotify(false),
    365      mHadOwnerNode(false),
    366      mWasAlternate(false),
    367      mMediaMatched(true),
    368      mUseSystemPrincipal(aUseSystemPrincipal == UseSystemPrincipal::Yes),
    369      mSheetAlreadyComplete(false),
    370      mLoadFailed(false),
    371      mShouldEmulateNotificationsForCachedLoad(false),
    372      mPreloadKind(aPreloadKind),
    373      mObserver(aObserver),
    374      mTriggeringPrincipal(aTriggeringPrincipal),
    375      mReferrerInfo(aReferrerInfo),
    376      mNonce(aNonce),
    377      mFetchPriority(aFetchPriority),
    378      mGuessedEncoding(
    379          GetFallbackEncoding(*aLoader, nullptr, aPreloadEncoding)),
    380      mCompatMode(aLoader->CompatMode(aPreloadKind)),
    381      mRecordErrors(
    382          aLoader && aLoader->GetDocument() &&
    383          css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())),
    384      mNetworkMetadata(std::move(aNetworkMetadata)) {
    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 
    392 SheetLoadData::~SheetLoadData() {
    393  MOZ_RELEASE_ASSERT(mSheetCompleteCalled || mIntentionallyDropped,
    394                     "Should always call SheetComplete, except when "
    395                     "dropping the load");
    396 }
    397 
    398 void SheetLoadData::StartLoading() {
    399  MOZ_ASSERT(!mIsLoading, "Already loading? How?");
    400  mIsLoading = true;
    401  mLoadStart = TimeStamp::Now();
    402 }
    403 
    404 void SheetLoadData::SetLoadCompleted() {
    405  MOZ_ASSERT(mIsLoading, "Not loading?");
    406  MOZ_ASSERT(!mLoadStart.IsNull());
    407  mIsLoading = false;
    408 }
    409 
    410 void SheetLoadData::OnCoalescedTo(const SheetLoadData& aExistingLoad) {
    411  if (&aExistingLoad.Loader() != &Loader()) {
    412    mShouldEmulateNotificationsForCachedLoad = true;
    413  }
    414  mLoadStart = TimeStamp::Now();
    415 }
    416 
    417 RefPtr<StyleSheet> SheetLoadData::ValueForCache() const {
    418  // We need to clone the sheet on insertion to the cache because otherwise the
    419  // stylesheets can keep full windows alive via either their JS wrapper, or via
    420  // StyleSheet::mRelevantGlobal.
    421  //
    422  // If this ever changes, then you also need to fix up the memory reporting in
    423  // both SizeOfIncludingThis and nsXULPrototypeCache::CollectMemoryReports.
    424  return mSheet->Clone(nullptr, nullptr);
    425 }
    426 
    427 void SheetLoadData::PrioritizeAsPreload(nsIChannel* aChannel) {
    428  if (nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(aChannel)) {
    429    sp->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
    430  }
    431 }
    432 
    433 void SheetLoadData::StartPendingLoad() {
    434  mLoader->LoadSheet(*this, Loader::SheetState::NeedsParser, 0,
    435                     Loader::PendingLoad::Yes);
    436 }
    437 
    438 already_AddRefed<AsyncEventDispatcher>
    439 SheetLoadData::PrepareLoadEventIfNeeded() {
    440  nsCOMPtr<nsINode> node = mSheet->GetOwnerNode();
    441  if (!node) {
    442    return nullptr;
    443  }
    444  MOZ_ASSERT(!RootLoadData().IsLinkRelPreloadOrEarlyHint(),
    445             "rel=preload handled elsewhere");
    446  RefPtr<AsyncEventDispatcher> dispatcher;
    447  if (BlocksLoadEvent()) {
    448    dispatcher = new LoadBlockingAsyncEventDispatcher(
    449        node, mLoadFailed ? u"error"_ns : u"load"_ns, CanBubble::eNo,
    450        ChromeOnlyDispatch::eNo);
    451  } else {
    452    // Fire the load event on the link, but don't block the document load.
    453    dispatcher =
    454        new AsyncEventDispatcher(node, mLoadFailed ? u"error"_ns : u"load"_ns,
    455                                 CanBubble::eNo, ChromeOnlyDispatch::eNo);
    456  }
    457  return dispatcher.forget();
    458 }
    459 
    460 nsINode* SheetLoadData::GetRequestingNode() const {
    461  if (nsINode* node = mSheet->GetOwnerNodeOfOutermostSheet()) {
    462    return node;
    463  }
    464  return mLoader->GetDocument();
    465 }
    466 
    467 /*********************
    468 * Style sheet reuse *
    469 *********************/
    470 
    471 bool LoaderReusableStyleSheets::FindReusableStyleSheet(
    472    nsIURI* aURL, RefPtr<StyleSheet>& aResult) {
    473  MOZ_ASSERT(aURL);
    474  for (size_t i = mReusableSheets.Length(); i > 0; --i) {
    475    size_t index = i - 1;
    476    bool sameURI;
    477    MOZ_ASSERT(mReusableSheets[index]->GetOriginalURI());
    478    nsresult rv =
    479        aURL->Equals(mReusableSheets[index]->GetOriginalURI(), &sameURI);
    480    if (!NS_FAILED(rv) && sameURI) {
    481      aResult = mReusableSheets[index];
    482      mReusableSheets.RemoveElementAt(index);
    483      return true;
    484    }
    485  }
    486  return false;
    487 }
    488 /*************************
    489 * Loader Implementation *
    490 *************************/
    491 
    492 Loader::Loader()
    493    : mDocument(nullptr),
    494      mDocumentCompatMode(eCompatibility_FullStandards),
    495      mReporter(new ConsoleReportCollector()) {}
    496 
    497 Loader::Loader(DocGroup* aDocGroup) : Loader() { mDocGroup = aDocGroup; }
    498 
    499 Loader::Loader(Document* aDocument) : Loader() {
    500  MOZ_ASSERT(aDocument, "We should get a valid document from the caller!");
    501  mDocument = aDocument;
    502  mIsDocumentAssociated = true;
    503  mDocumentCompatMode = aDocument->GetCompatibilityMode();
    504  mSheets = SharedStyleSheetCache::Get();
    505  RegisterInSheetCache();
    506 }
    507 
    508 // Note: no real need to revoke our stylesheet loaded events -- they hold strong
    509 // references to us, so if we're going away that means they're all done.
    510 Loader::~Loader() = default;
    511 
    512 void Loader::RegisterInSheetCache() {
    513  MOZ_ASSERT(mDocument);
    514  MOZ_ASSERT(mSheets);
    515 
    516  mSheets->RegisterLoader(*this);
    517 }
    518 
    519 void Loader::DeregisterFromSheetCache() {
    520  MOZ_ASSERT(mDocument);
    521  MOZ_ASSERT(mSheets);
    522 
    523  mSheets->CancelLoadsForLoader(*this);
    524  mSheets->UnregisterLoader(*this);
    525 }
    526 
    527 void Loader::DropDocumentReference() {
    528  // Flush out pending datas just so we don't leak by accident.
    529  if (mSheets) {
    530    DeregisterFromSheetCache();
    531  }
    532  mDocument = nullptr;
    533 }
    534 
    535 void Loader::DocumentStyleSheetSetChanged() {
    536  MOZ_ASSERT(mDocument);
    537 
    538  // start any pending alternates that aren't alternates anymore
    539  mSheets->StartPendingLoadsForLoader(*this, [&](const SheetLoadData& aData) {
    540    return IsAlternateSheet(aData.mTitle, true) != IsAlternate::Yes;
    541  });
    542 }
    543 
    544 static const char kCharsetSym[] = "@charset \"";
    545 
    546 static bool GetCharsetFromData(const char* aStyleSheetData,
    547                               uint32_t aDataLength, nsACString& aCharset) {
    548  aCharset.Truncate();
    549  if (aDataLength <= sizeof(kCharsetSym) - 1) {
    550    return false;
    551  }
    552 
    553  if (strncmp(aStyleSheetData, kCharsetSym, sizeof(kCharsetSym) - 1)) {
    554    return false;
    555  }
    556 
    557  for (uint32_t i = sizeof(kCharsetSym) - 1; i < aDataLength; ++i) {
    558    char c = aStyleSheetData[i];
    559    if (c == '"') {
    560      ++i;
    561      if (i < aDataLength && aStyleSheetData[i] == ';') {
    562        return true;
    563      }
    564      // fail
    565      break;
    566    }
    567    aCharset.Append(c);
    568  }
    569 
    570  // Did not see end quote or semicolon
    571  aCharset.Truncate();
    572  return false;
    573 }
    574 
    575 NotNull<const Encoding*> SheetLoadData::DetermineNonBOMEncoding(
    576    const nsACString& aSegment, nsIChannel* aChannel) const {
    577  // 1024 bytes is specified in https://drafts.csswg.org/css-syntax/
    578  constexpr size_t kSniffingBufferSize = 1024;
    579  nsAutoCString label;
    580  // Check HTTP
    581  if (aChannel && NS_SUCCEEDED(aChannel->GetContentCharset(label))) {
    582    if (const auto* encoding = Encoding::ForLabel(label)) {
    583      return WrapNotNull(encoding);
    584    }
    585  }
    586 
    587  // Check @charset
    588  auto sniffingLength = std::min(aSegment.Length(), kSniffingBufferSize);
    589  if (GetCharsetFromData(aSegment.BeginReading(), sniffingLength, label)) {
    590    if (const auto* encoding = Encoding::ForLabel(label)) {
    591      if (encoding == UTF_16BE_ENCODING || encoding == UTF_16LE_ENCODING) {
    592        return UTF_8_ENCODING;
    593      }
    594      return WrapNotNull(encoding);
    595    }
    596  }
    597  return mGuessedEncoding;
    598 }
    599 
    600 static nsresult VerifySheetIntegrity(const SRIMetadata& aMetadata,
    601                                     nsIChannel* aChannel,
    602                                     LoadTainting aTainting,
    603                                     const nsACString& aFirst,
    604                                     const nsACString& aSecond,
    605                                     nsIConsoleReportCollector* aReporter) {
    606  NS_ENSURE_ARG_POINTER(aReporter);
    607  MOZ_LOG(SRILogHelper::GetSriLog(), LogLevel::Debug,
    608          ("VerifySheetIntegrity (unichar stream)"));
    609 
    610  SRICheckDataVerifier verifier(aMetadata, aChannel, aReporter);
    611  MOZ_TRY(verifier.Update(aFirst));
    612  MOZ_TRY(verifier.Update(aSecond));
    613  return verifier.Verify(aMetadata, aChannel, aTainting, aReporter);
    614 }
    615 
    616 static bool AllLoadsCanceled(const SheetLoadData& aData) {
    617  const SheetLoadData* data = &aData;
    618  do {
    619    if (!data->IsCancelled()) {
    620      return false;
    621    }
    622  } while ((data = data->mNext));
    623  return true;
    624 }
    625 
    626 void SheetLoadData::OnStartRequest(nsIRequest* aRequest) {
    627  MOZ_ASSERT(NS_IsMainThread());
    628  NotifyStart(aRequest);
    629 
    630  SetMinimumExpirationTime(
    631      nsContentUtils::GetSubresourceCacheExpirationTime(aRequest, mURI));
    632 
    633  // We need to block resolution of parse promise until we receive OnStopRequest
    634  // on Main thread. This is necessary because parse promise resolution fires
    635  // OnLoad event must not be dispatched until OnStopRequest in main thread is
    636  // processed, for stuff like performance resource entries.
    637  mSheet->BlockParsePromise();
    638 
    639  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
    640  if (!channel) {
    641    return;
    642  }
    643 
    644  nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
    645  mTainting = loadInfo->GetTainting();
    646 
    647  nsCOMPtr<nsIURI> originalURI;
    648  channel->GetOriginalURI(getter_AddRefs(originalURI));
    649  MOZ_DIAGNOSTIC_ASSERT(originalURI,
    650                        "Someone just violated the nsIRequest contract");
    651  nsCOMPtr<nsIURI> finalURI;
    652  NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI));
    653  MOZ_DIAGNOSTIC_ASSERT(finalURI,
    654                        "Someone just violated the nsIRequest contract");
    655  nsCOMPtr<nsIPrincipal> principal;
    656  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
    657  if (mUseSystemPrincipal) {
    658    secMan->GetSystemPrincipal(getter_AddRefs(principal));
    659  } else {
    660    secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal));
    661  }
    662  MOZ_DIAGNOSTIC_ASSERT(principal);
    663 
    664  nsCOMPtr<nsIReferrerInfo> referrerInfo =
    665      ReferrerInfo::CreateForExternalCSSResources(
    666          mSheet, finalURI,
    667          nsContentUtils::GetReferrerPolicyFromChannel(channel));
    668  mSheet->SetURIs(originalURI, finalURI, referrerInfo, principal);
    669  mSheet->SetOriginClean([&] {
    670    if (mParentData && !mParentData->mSheet->IsOriginClean()) {
    671      return false;
    672    }
    673    if (mSheet->GetCORSMode() != CORS_NONE) {
    674      return true;
    675    }
    676    if (!mLoader->LoaderPrincipal()->Subsumes(mSheet->Principal())) {
    677      return false;
    678    }
    679    if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel)) {
    680      bool allRedirectsSameOrigin = false;
    681      bool hadCrossOriginRedirects =
    682          NS_SUCCEEDED(timedChannel->GetAllRedirectsSameOrigin(
    683              &allRedirectsSameOrigin)) &&
    684          !allRedirectsSameOrigin;
    685      if (hadCrossOriginRedirects) {
    686        return false;
    687      }
    688    }
    689    return true;
    690  }());
    691  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
    692    nsCString sourceMapURL;
    693    if (nsContentUtils::GetSourceMapURL(httpChannel, sourceMapURL)) {
    694      mSheet->SetSourceMapURL(std::move(sourceMapURL));
    695    }
    696  }
    697 }
    698 
    699 /*
    700 * Here we need to check that the load did not give us an http error
    701 * page and check the mimetype on the channel to make sure we're not
    702 * loading non-text/css data in standards mode.
    703 */
    704 nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
    705                                                const nsACString& aBytes1,
    706                                                const nsACString& aBytes2,
    707                                                nsIChannel* aChannel) {
    708  LOG(("SheetLoadData::VerifySheetReadyToParse"));
    709  NS_ASSERTION((!NS_IsMainThread() || !mLoader->mSyncCallback),
    710               "Synchronous callback from necko");
    711  MOZ_DIAGNOSTIC_ASSERT_IF(mRecordErrors, NS_IsMainThread());
    712  MOZ_DIAGNOSTIC_ASSERT(aChannel);
    713 
    714  if (AllLoadsCanceled(*this)) {
    715    return NS_BINDING_ABORTED;
    716  }
    717 
    718  if (NS_FAILED(aStatus)) {
    719    return aStatus;
    720  }
    721 
    722  // If it's an HTTP channel, we want to make sure this is not an
    723  // error document we got.
    724  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
    725    bool requestSucceeded;
    726    nsresult result = httpChannel->GetRequestSucceeded(&requestSucceeded);
    727    if (NS_SUCCEEDED(result) && !requestSucceeded) {
    728      return NS_ERROR_NOT_AVAILABLE;
    729    }
    730  }
    731 
    732  nsAutoCString contentType;
    733  aChannel->GetContentType(contentType);
    734 
    735  // In standards mode, a style sheet must have one of these MIME
    736  // types to be processed at all.  In quirks mode, we accept any
    737  // MIME type, but only if the style sheet is same-origin with the
    738  // requesting document or parent sheet.  See bug 524223.
    739 
    740  const bool validType = contentType.EqualsLiteral("text/css") ||
    741                         contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE) ||
    742                         contentType.IsEmpty();
    743 
    744  if (!validType) {
    745    // FIXME(emilio, bug 1995647): This should arguably use IsOriginClean(),
    746    // though test_css_cross_domain_no_orb.html tests precisely this behavior
    747    // intentionally, and this is quirks-only...
    748    const bool sameOrigin =
    749        mLoader->LoaderPrincipal()->Subsumes(mSheet->Principal());
    750    const auto flag = sameOrigin && mCompatMode == eCompatibility_NavQuirks
    751                          ? nsIScriptError::warningFlag
    752                          : nsIScriptError::errorFlag;
    753    const auto errorMessage = flag == nsIScriptError::errorFlag
    754                                  ? "MimeNotCss"_ns
    755                                  : "MimeNotCssWarn"_ns;
    756    NS_ConvertUTF8toUTF16 sheetUri(mURI->GetSpecOrDefault());
    757    NS_ConvertUTF8toUTF16 contentType16(contentType);
    758 
    759    nsAutoCString referrerSpec;
    760    if (nsCOMPtr<nsIURI> referrer = ReferrerInfo()->GetOriginalReferrer()) {
    761      referrer->GetSpec(referrerSpec);
    762    }
    763    mLoader->mReporter->AddConsoleReport(
    764        flag, "CSS Loader"_ns, nsContentUtils::eCSS_PROPERTIES, referrerSpec, 0,
    765        0, errorMessage, {sheetUri, contentType16});
    766    if (flag == nsIScriptError::errorFlag) {
    767      LOG_WARN(
    768          ("  Ignoring sheet with improper MIME type %s", contentType.get()));
    769      return NS_ERROR_NOT_AVAILABLE;
    770    }
    771  }
    772 
    773  SRIMetadata sriMetadata;
    774  mSheet->GetIntegrity(sriMetadata);
    775  if (!sriMetadata.IsEmpty()) {
    776    nsresult rv = VerifySheetIntegrity(sriMetadata, aChannel, mTainting,
    777                                       aBytes1, aBytes2, mLoader->mReporter);
    778    if (NS_FAILED(rv)) {
    779      LOG(("  Load was blocked by SRI"));
    780      MOZ_LOG(gSriPRLog, LogLevel::Debug,
    781              ("css::Loader::OnStreamComplete, bad metadata"));
    782      return NS_ERROR_SRI_CORRUPT;
    783    }
    784  }
    785  return NS_OK_PARSE_SHEET;
    786 }
    787 
    788 Loader::IsAlternate Loader::IsAlternateSheet(const nsAString& aTitle,
    789                                             bool aHasAlternateRel) {
    790  // A sheet is alternate if it has a nonempty title that doesn't match the
    791  // currently selected style set.  But if there _is_ no currently selected
    792  // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
    793  // is nonempty, we should select the style set corresponding to aTitle, since
    794  // that's a preferred sheet.
    795  if (aTitle.IsEmpty()) {
    796    return IsAlternate::No;
    797  }
    798 
    799  if (mDocument) {
    800    const nsString& currentSheetSet = mDocument->GetCurrentStyleSheetSet();
    801    if (!aHasAlternateRel && currentSheetSet.IsEmpty()) {
    802      // There's no preferred set yet, and we now have a sheet with a title.
    803      // Make that be the preferred set.
    804      // FIXME(emilio): This is kinda wild, can we do it somewhere else?
    805      mDocument->SetPreferredStyleSheetSet(aTitle);
    806      // We're definitely not an alternate. Also, beware that at this point
    807      // currentSheetSet may dangle.
    808      return IsAlternate::No;
    809    }
    810 
    811    if (aTitle.Equals(currentSheetSet)) {
    812      return IsAlternate::No;
    813    }
    814  }
    815 
    816  return IsAlternate::Yes;
    817 }
    818 
    819 static nsSecurityFlags ComputeSecurityFlags(CORSMode aCORSMode) {
    820  nsSecurityFlags securityFlags =
    821      nsContentSecurityManager::ComputeSecurityFlags(
    822          aCORSMode, nsContentSecurityManager::CORSSecurityMapping::
    823                         CORS_NONE_MAPS_TO_INHERITED_CONTEXT);
    824  securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
    825  return securityFlags;
    826 }
    827 
    828 static nsContentPolicyType ComputeContentPolicyType(
    829    StylePreloadKind aPreloadKind) {
    830  return aPreloadKind == StylePreloadKind::None
    831             ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET
    832             : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD;
    833 }
    834 
    835 nsresult Loader::CheckContentPolicy(
    836    nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal,
    837    nsIURI* aTargetURI, nsINode* aRequestingNode, const nsAString& aNonce,
    838    StylePreloadKind aPreloadKind, CORSMode aCORSMode,
    839    const nsAString& aIntegrity) {
    840  // When performing a system load don't consult content policies.
    841  if (!mDocument) {
    842    return NS_OK;
    843  }
    844 
    845  nsContentPolicyType contentPolicyType =
    846      ComputeContentPolicyType(aPreloadKind);
    847 
    848  nsCOMPtr<nsILoadInfo> secCheckLoadInfo = MOZ_TRY(net::LoadInfo::Create(
    849      aLoadingPrincipal, aTriggeringPrincipal, aRequestingNode,
    850      nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, contentPolicyType));
    851  secCheckLoadInfo->SetCspNonce(aNonce);
    852 
    853  // Required for IntegrityPolicy checks.
    854  RequestMode requestMode = nsContentSecurityManager::SecurityModeToRequestMode(
    855      nsContentSecurityManager::ComputeSecurityMode(
    856          ComputeSecurityFlags(aCORSMode)));
    857  secCheckLoadInfo->SetRequestMode(Some(requestMode));
    858 
    859  // Required for IntegrityPolicy checks.
    860  secCheckLoadInfo->SetIntegrityMetadata(aIntegrity);
    861 
    862  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
    863  nsresult rv =
    864      NS_CheckContentLoadPolicy(aTargetURI, secCheckLoadInfo, &shouldLoad,
    865                                nsContentUtils::GetContentPolicy());
    866  if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
    867    // Asynchronously notify observers (e.g devtools) of CSP failure.
    868    nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
    869        "Loader::NotifyOnFailedCheckPolicy",
    870        [targetURI = RefPtr<nsIURI>(aTargetURI),
    871         requestingNode = RefPtr<nsINode>(aRequestingNode),
    872         contentPolicyType]() {
    873          nsCOMPtr<nsIChannel> channel;
    874          NS_NewChannel(
    875              getter_AddRefs(channel), targetURI, requestingNode,
    876              nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT,
    877              contentPolicyType);
    878          NS_SetRequestBlockingReason(
    879              channel, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_GENERAL);
    880          nsCOMPtr<nsIObserverService> obsService =
    881              services::GetObserverService();
    882          if (obsService) {
    883            obsService->NotifyObservers(
    884                channel, "http-on-failed-opening-request", nullptr);
    885          }
    886        }));
    887    return NS_ERROR_CONTENT_BLOCKED;
    888  }
    889  return NS_OK;
    890 }
    891 
    892 bool Loader::MaybePutIntoLoadsPerformed(SheetLoadData& aLoadData) {
    893  if (!aLoadData.mURI) {
    894    // Inline style sheet is not tracked.
    895    return false;
    896  }
    897 
    898  return mLoadsPerformed.EnsureInserted(SheetLoadDataHashKey(aLoadData));
    899 }
    900 
    901 /**
    902 * CreateSheet() creates a StyleSheet object for the given URI.
    903 *
    904 * We check for an existing style sheet object for that uri in various caches
    905 * and clone it if we find it.  Cloned sheets will have the title/media/enabled
    906 * state of the sheet they are clones off; make sure to call PrepareSheet() on
    907 * the result of CreateSheet().
    908 */
    909 std::tuple<RefPtr<StyleSheet>, Loader::SheetState,
    910           RefPtr<SubResourceNetworkMetadataHolder>>
    911 Loader::CreateSheet(nsIURI* aURI, nsIContent* aLinkingContent,
    912                    nsIPrincipal* aTriggeringPrincipal,
    913                    css::SheetParsingMode aParsingMode, CORSMode aCORSMode,
    914                    const Encoding* aPreloadOrParentDataEncoding,
    915                    const nsAString& aIntegrity, bool aSyncLoad,
    916                    StylePreloadKind aPreloadKind) {
    917  MOZ_ASSERT(aURI, "This path is not taken for inline stylesheets");
    918  LOG(("css::Loader::CreateSheet(%s)", aURI->GetSpecOrDefault().get()));
    919 
    920  SRIMetadata sriMetadata;
    921  if (!aIntegrity.IsEmpty()) {
    922    MOZ_LOG(gSriPRLog, LogLevel::Debug,
    923            ("css::Loader::CreateSheet, integrity=%s",
    924             NS_ConvertUTF16toUTF8(aIntegrity).get()));
    925    nsAutoCString sourceUri;
    926    if (mDocument && mDocument->GetDocumentURI()) {
    927      mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
    928    }
    929    SRICheck::IntegrityMetadata(aIntegrity, sourceUri, mReporter, &sriMetadata);
    930  }
    931 
    932  if (mSheets) {
    933    SheetLoadDataHashKey key(aURI, LoaderPrincipal(), PartitionedPrincipal(),
    934                             GetFallbackEncoding(*this, aLinkingContent,
    935                                                 aPreloadOrParentDataEncoding),
    936                             aCORSMode, aParsingMode, CompatMode(aPreloadKind),
    937                             sriMetadata, aPreloadKind);
    938    auto cacheResult = mSheets->Lookup(*this, key, aSyncLoad);
    939    if (cacheResult.mState != CachedSubResourceState::Miss) {
    940      SheetState sheetState = SheetState::Complete;
    941      RefPtr<StyleSheet> sheet;
    942      RefPtr<SubResourceNetworkMetadataHolder> networkMetadata;
    943      if (cacheResult.mCompleteValue) {
    944        sheet = cacheResult.mCompleteValue->Clone(nullptr, nullptr);
    945        networkMetadata = cacheResult.mNetworkMetadata;
    946        mDocument->SetDidHitCompleteSheetCache();
    947      } else {
    948        MOZ_ASSERT(cacheResult.mLoadingOrPendingValue);
    949        sheet = cacheResult.mLoadingOrPendingValue->ValueForCache();
    950        sheetState = cacheResult.mState == CachedSubResourceState::Loading
    951                         ? SheetState::Loading
    952                         : SheetState::Pending;
    953      }
    954      LOG(("  Hit cache with state: %s", gStateStrings[size_t(sheetState)]));
    955      return {std::move(sheet), sheetState, std::move(networkMetadata)};
    956    }
    957  }
    958  auto sheet = MakeRefPtr<StyleSheet>(aParsingMode, aCORSMode, sriMetadata);
    959  nsCOMPtr<nsIReferrerInfo> referrerInfo =
    960      ReferrerInfo::CreateForExternalCSSResources(sheet, aURI);
    961  // NOTE: If the sheet is loaded, then SetURIs gets called again with the right
    962  // principal / final uri / etc.
    963  sheet->SetURIs(aURI, aURI, referrerInfo, LoaderPrincipal());
    964  // Load errors should be origin-dirty, even if same-origin.
    965  sheet->SetOriginClean(false);
    966  LOG(("  Needs parser"));
    967  return {std::move(sheet), SheetState::NeedsParser, nullptr};
    968 }
    969 
    970 static Loader::MediaMatched MediaListMatches(const MediaList* aMediaList,
    971                                             const Document* aDocument) {
    972  if (!aMediaList || !aDocument) {
    973    return Loader::MediaMatched::Yes;
    974  }
    975 
    976  if (aMediaList->Matches(*aDocument)) {
    977    return Loader::MediaMatched::Yes;
    978  }
    979 
    980  return Loader::MediaMatched::No;
    981 }
    982 
    983 /**
    984 * PrepareSheet() handles setting the media and title on the sheet, as
    985 * well as setting the enabled state based on the title and whether
    986 * the sheet had "alternate" in its rel.
    987 */
    988 Loader::MediaMatched Loader::PrepareSheet(
    989    StyleSheet& aSheet, const nsAString& aTitle, const nsAString& aMediaString,
    990    MediaList* aMediaList, IsAlternate aIsAlternate,
    991    IsExplicitlyEnabled aIsExplicitlyEnabled) {
    992  RefPtr<MediaList> mediaList(aMediaList);
    993 
    994  if (!aMediaString.IsEmpty()) {
    995    NS_ASSERTION(!aMediaList,
    996                 "must not provide both aMediaString and aMediaList");
    997    mediaList = MediaList::Create(NS_ConvertUTF16toUTF8(aMediaString));
    998  }
    999 
   1000  aSheet.SetMedia(do_AddRef(mediaList));
   1001 
   1002  aSheet.SetTitle(aTitle);
   1003  aSheet.SetEnabled(aIsAlternate == IsAlternate::No ||
   1004                    aIsExplicitlyEnabled == IsExplicitlyEnabled::Yes);
   1005  return MediaListMatches(mediaList, mDocument);
   1006 }
   1007 
   1008 /**
   1009 * InsertSheetInTree handles ordering of sheets in the document or shadow root.
   1010 *
   1011 * Here we have two types of sheets -- those with linking elements and
   1012 * those without.  The latter are loaded by Link: headers, and are only added to
   1013 * the document.
   1014 *
   1015 * The following constraints are observed:
   1016 * 1) Any sheet with a linking element comes after all sheets without
   1017 *    linking elements
   1018 * 2) Sheets without linking elements are inserted in the order in
   1019 *    which the inserting requests come in, since all of these are
   1020 *    inserted during header data processing in the content sink
   1021 * 3) Sheets with linking elements are ordered based on document order
   1022 *    as determined by CompareDocumentPosition.
   1023 */
   1024 void Loader::InsertSheetInTree(StyleSheet& aSheet) {
   1025  LOG(("css::Loader::InsertSheetInTree"));
   1026  MOZ_ASSERT(mDocument, "Must have a document to insert into");
   1027 
   1028  // If our owning node is null, we come from a link header.
   1029  nsINode* owningNode = aSheet.GetOwnerNode();
   1030  MOZ_ASSERT_IF(owningNode, owningNode->OwnerDoc() == mDocument);
   1031  DocumentOrShadowRoot* target =
   1032      owningNode ? owningNode->GetContainingDocumentOrShadowRoot() : mDocument;
   1033  MOZ_ASSERT(target, "Where should we insert it?");
   1034 
   1035  size_t insertionPoint = target->FindSheetInsertionPointInTree(aSheet);
   1036  if (auto* shadow = ShadowRoot::FromNode(target->AsNode())) {
   1037    shadow->InsertSheetAt(insertionPoint, aSheet);
   1038  } else {
   1039    MOZ_ASSERT(&target->AsNode() == mDocument);
   1040    mDocument->InsertSheetAt(insertionPoint, aSheet);
   1041  }
   1042 
   1043  LOG(("  Inserting into target (doc: %d) at position %zu",
   1044       target->AsNode().IsDocument(), insertionPoint));
   1045 }
   1046 
   1047 /**
   1048 * InsertChildSheet handles ordering of @import-ed sheet in their
   1049 * parent sheets.  Here we want to just insert based on order of the
   1050 * @import rules that imported the sheets.  In theory we can't just
   1051 * append to the end because the CSSOM can insert @import rules.  In
   1052 * practice, we get the call to load the child sheet before the CSSOM
   1053 * has finished inserting the @import rule, so we have no idea where
   1054 * to put it anyway.  So just append for now.  (In the future if we
   1055 * want to insert the sheet at the correct position, we'll need to
   1056 * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
   1057 * bug 1220506.)
   1058 */
   1059 void Loader::InsertChildSheet(StyleSheet& aSheet, StyleSheet& aParentSheet) {
   1060  LOG(("css::Loader::InsertChildSheet"));
   1061 
   1062  // child sheets should always start out enabled, even if they got
   1063  // cloned off of top-level sheets which were disabled
   1064  aSheet.SetEnabled(true);
   1065  aParentSheet.AppendStyleSheet(aSheet);
   1066 
   1067  LOG(("  Inserting into parent sheet"));
   1068 }
   1069 
   1070 nsresult Loader::NewStyleSheetChannel(SheetLoadData& aLoadData,
   1071                                      CORSMode aCorsMode,
   1072                                      UsePreload aUsePreload,
   1073                                      UseLoadGroup aUseLoadGroup,
   1074                                      nsIChannel** aOutChannel) {
   1075  nsCOMPtr<nsILoadGroup> loadGroup;
   1076  nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
   1077  net::ClassificationFlags triggeringClassificationFlags;
   1078  if (aUseLoadGroup == UseLoadGroup::Yes && mDocument) {
   1079    loadGroup = mDocument->GetDocumentLoadGroup();
   1080    // load for a document with no loadgrup indicates that something is
   1081    // completely bogus, let's bail out early.
   1082    if (!loadGroup) {
   1083      LOG_ERROR(("  Failed to query loadGroup from document"));
   1084      return NS_ERROR_UNEXPECTED;
   1085    }
   1086 
   1087    cookieJarSettings = mDocument->CookieJarSettings();
   1088 
   1089    // If the script context is tracking, we use the flags from the script
   1090    // tracking flags. Otherwise, we fallback to use the flags from the
   1091    // document.
   1092    triggeringClassificationFlags = mDocument->GetScriptTrackingFlags();
   1093  }
   1094 
   1095  nsSecurityFlags securityFlags = ComputeSecurityFlags(aCorsMode);
   1096 
   1097  nsContentPolicyType contentPolicyType =
   1098      ComputeContentPolicyType(aLoadData.mPreloadKind);
   1099 
   1100  nsINode* requestingNode = aLoadData.GetRequestingNode();
   1101 
   1102  nsIPrincipal* triggeringPrincipal = aLoadData.mTriggeringPrincipal;
   1103 
   1104  // Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both
   1105  // a node and a principal.
   1106  // This is because of a case where the node is the document being styled and
   1107  // the principal is the stylesheet (perhaps from a different origin) that is
   1108  // applying the styles.
   1109  if (requestingNode) {
   1110    return NS_NewChannelWithTriggeringPrincipal(
   1111        aOutChannel, aLoadData.mURI, requestingNode, triggeringPrincipal,
   1112        securityFlags, contentPolicyType,
   1113        /* aPerformanceStorage = */ nullptr, loadGroup);
   1114  }
   1115 
   1116  MOZ_ASSERT(triggeringPrincipal->Equals(LoaderPrincipal()));
   1117 
   1118  if (aUsePreload == UsePreload::Yes) {
   1119    auto result = URLPreloader::ReadURI(aLoadData.mURI);
   1120    if (result.isOk()) {
   1121      nsCOMPtr<nsIInputStream> stream;
   1122      MOZ_TRY(
   1123          NS_NewCStringInputStream(getter_AddRefs(stream), result.unwrap()));
   1124 
   1125      return NS_NewInputStreamChannel(aOutChannel, aLoadData.mURI,
   1126                                      stream.forget(), triggeringPrincipal,
   1127                                      securityFlags, contentPolicyType);
   1128    }
   1129  }
   1130 
   1131  MOZ_TRY(NS_NewChannel(aOutChannel, aLoadData.mURI, triggeringPrincipal,
   1132                        securityFlags, contentPolicyType, cookieJarSettings,
   1133                        /* aPerformanceStorage = */ nullptr, loadGroup));
   1134 
   1135  nsCOMPtr<nsILoadInfo> loadInfo = (*aOutChannel)->LoadInfo();
   1136  loadInfo->SetTriggeringFirstPartyClassificationFlags(
   1137      triggeringClassificationFlags.firstPartyFlags);
   1138  loadInfo->SetTriggeringThirdPartyClassificationFlags(
   1139      triggeringClassificationFlags.thirdPartyFlags);
   1140 
   1141  return NS_OK;
   1142 }
   1143 
   1144 nsresult Loader::LoadSheetSyncInternal(SheetLoadData& aLoadData,
   1145                                       SheetState aSheetState) {
   1146  LOG(("  Synchronous load"));
   1147  MOZ_ASSERT(!aLoadData.mObserver, "Observer for a sync load?");
   1148  MOZ_ASSERT(aSheetState == SheetState::NeedsParser,
   1149             "Sync loads can't reuse existing async loads");
   1150 
   1151  // Create a StreamLoader instance to which we will feed
   1152  // the data from the sync load.  Do this before creating the
   1153  // channel to make error recovery simpler.
   1154  auto streamLoader = MakeRefPtr<StreamLoader>(aLoadData);
   1155 
   1156  // Synchronous loads should only be used internally. Therefore no CORS
   1157  // policy is needed.
   1158  nsCOMPtr<nsIChannel> channel;
   1159  nsresult rv =
   1160      NewStyleSheetChannel(aLoadData, CORSMode::CORS_NONE, UsePreload::Yes,
   1161                           UseLoadGroup::No, getter_AddRefs(channel));
   1162  if (NS_FAILED(rv)) {
   1163    LOG_ERROR(("  Failed to create channel"));
   1164    streamLoader->ChannelOpenFailed(rv);
   1165    SheetComplete(aLoadData, rv);
   1166    return rv;
   1167  }
   1168 
   1169  nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
   1170  loadInfo->SetCspNonce(aLoadData.Nonce());
   1171 
   1172 #ifdef DEBUG
   1173  {
   1174    nsCOMPtr<nsIInterfaceRequestor> prevCallback;
   1175    channel->GetNotificationCallbacks(getter_AddRefs(prevCallback));
   1176    MOZ_ASSERT(!prevCallback);
   1177  }
   1178 #endif
   1179  channel->SetNotificationCallbacks(streamLoader);
   1180 
   1181  nsCOMPtr<nsIInputStream> stream;
   1182  rv = channel->Open(getter_AddRefs(stream));
   1183 
   1184  if (NS_FAILED(rv)) {
   1185    LOG_ERROR(("  Failed to open URI synchronously"));
   1186    streamLoader->ChannelOpenFailed(rv);
   1187    channel->SetNotificationCallbacks(nullptr);
   1188    SheetComplete(aLoadData, rv);
   1189    return rv;
   1190  }
   1191 
   1192  // Force UA sheets to be UTF-8.
   1193  // XXX this is only necessary because the default in
   1194  // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
   1195  channel->SetContentCharset("UTF-8"_ns);
   1196 
   1197  // Manually feed the streamloader the contents of the stream.
   1198  // This will call back into OnStreamComplete
   1199  // and thence to ParseSheet.  Regardless of whether this fails,
   1200  // SheetComplete has been called.
   1201  return nsSyncLoadService::PushSyncStreamToListener(stream.forget(),
   1202                                                     streamLoader, channel);
   1203 }
   1204 
   1205 bool Loader::MaybeDeferLoad(SheetLoadData& aLoadData, SheetState aSheetState,
   1206                            PendingLoad aPendingLoad,
   1207                            const SheetLoadDataHashKey& aKey) {
   1208  MOZ_ASSERT(mSheets);
   1209 
   1210  // If we have at least one other load ongoing, then we can defer it until
   1211  // all non-pending loads are done.
   1212  if (aSheetState == SheetState::NeedsParser &&
   1213      aPendingLoad == PendingLoad::No && aLoadData.ShouldDefer() &&
   1214      mOngoingLoadCount > mPendingLoadCount + 1) {
   1215    LOG(("  Deferring sheet load"));
   1216    ++mPendingLoadCount;
   1217    mSheets->DeferLoad(aKey, aLoadData);
   1218    return true;
   1219  }
   1220  return false;
   1221 }
   1222 
   1223 bool Loader::MaybeCoalesceLoadAndNotifyOpen(SheetLoadData& aLoadData,
   1224                                            SheetState aSheetState,
   1225                                            const SheetLoadDataHashKey& aKey,
   1226                                            const PreloadHashKey& aPreloadKey) {
   1227  bool coalescedLoad = false;
   1228  auto cacheState = [&aSheetState] {
   1229    switch (aSheetState) {
   1230      case SheetState::Complete:
   1231        return CachedSubResourceState::Complete;
   1232      case SheetState::Pending:
   1233        return CachedSubResourceState::Pending;
   1234      case SheetState::Loading:
   1235        return CachedSubResourceState::Loading;
   1236      case SheetState::NeedsParser:
   1237        return CachedSubResourceState::Miss;
   1238    }
   1239    MOZ_ASSERT_UNREACHABLE("wat");
   1240    return CachedSubResourceState::Miss;
   1241  }();
   1242 
   1243  if ((coalescedLoad = mSheets->CoalesceLoad(aKey, aLoadData, cacheState))) {
   1244    if (aSheetState == SheetState::Pending) {
   1245      ++mPendingLoadCount;
   1246    } else {
   1247      // TODO: why not just `IsPreload()`?
   1248      aLoadData.NotifyOpen(aPreloadKey, mDocument,
   1249                           aLoadData.IsLinkRelPreloadOrEarlyHint());
   1250    }
   1251  }
   1252  return coalescedLoad;
   1253 }
   1254 
   1255 /**
   1256 * LoadSheet handles the actual load of a sheet.  If the load is
   1257 * supposed to be synchronous it just opens a channel synchronously
   1258 * using the given uri, wraps the resulting stream in a converter
   1259 * stream and calls ParseSheet.  Otherwise it tries to look for an
   1260 * existing load for this URI and piggyback on it.  Failing all that,
   1261 * a new load is kicked off asynchronously.
   1262 */
   1263 nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState,
   1264                           uint64_t aEarlyHintPreloaderId,
   1265                           PendingLoad aPendingLoad) {
   1266  LOG(("css::Loader::LoadSheet"));
   1267  MOZ_ASSERT(aLoadData.mURI, "Need a URI to load");
   1268  MOZ_ASSERT(aLoadData.mSheet, "Need a sheet to load into");
   1269  MOZ_ASSERT(aSheetState != SheetState::Complete, "Why bother?");
   1270  MOZ_ASSERT(!aLoadData.mUseSystemPrincipal || aLoadData.mSyncLoad,
   1271             "Shouldn't use system principal for async loads");
   1272 
   1273  LOG_URI("  Load from: '%s'", aLoadData.mURI);
   1274 
   1275  // If we're firing a pending load, this load is already accounted for the
   1276  // first time it went through this function.
   1277  if (aPendingLoad == PendingLoad::No) {
   1278    if (aLoadData.BlocksLoadEvent()) {
   1279      IncrementOngoingLoadCountAndMaybeBlockOnload();
   1280    }
   1281 
   1282    // We technically never defer non-top-level sheets, so this condition could
   1283    // be outside the branch, but conceptually it should be here.
   1284    if (aLoadData.mParentData) {
   1285      ++aLoadData.mParentData->mPendingChildren;
   1286    }
   1287  }
   1288 
   1289  if (!mDocument && !aLoadData.mIsNonDocumentSheet) {
   1290    // No point starting the load; just release all the data and such.
   1291    LOG_WARN(("  No document and not non-document sheet; pre-dropping load"));
   1292    SheetComplete(aLoadData, NS_BINDING_ABORTED);
   1293    return NS_BINDING_ABORTED;
   1294  }
   1295 
   1296  if (aLoadData.mSyncLoad) {
   1297    return LoadSheetSyncInternal(aLoadData, aSheetState);
   1298  }
   1299 
   1300  SheetLoadDataHashKey key(aLoadData);
   1301 
   1302  auto preloadKey = PreloadHashKey::CreateAsStyle(aLoadData);
   1303  if (mSheets) {
   1304    if (MaybeDeferLoad(aLoadData, aSheetState, aPendingLoad, key)) {
   1305      return NS_OK;
   1306    }
   1307 
   1308    if (MaybeCoalesceLoadAndNotifyOpen(aLoadData, aSheetState, key,
   1309                                       preloadKey)) {
   1310      // All done here; once the load completes we'll be marked complete
   1311      // automatically.
   1312      return NS_OK;
   1313    }
   1314  }
   1315 
   1316  aLoadData.NotifyOpen(preloadKey, mDocument,
   1317                       aLoadData.IsLinkRelPreloadOrEarlyHint());
   1318 
   1319  return LoadSheetAsyncInternal(aLoadData, aEarlyHintPreloaderId, key);
   1320 }
   1321 
   1322 void Loader::AdjustPriority(const SheetLoadData& aLoadData,
   1323                            nsIChannel* aChannel) {
   1324  if (!aLoadData.ShouldDefer() && aLoadData.IsLinkRelPreloadOrEarlyHint()) {
   1325    SheetLoadData::PrioritizeAsPreload(aChannel);
   1326  }
   1327 
   1328  if (!StaticPrefs::network_fetchpriority_enabled()) {
   1329    return;
   1330  }
   1331 
   1332  nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(aChannel);
   1333 
   1334  if (!sp) {
   1335    return;
   1336  }
   1337 
   1338  // Adjusting priorites is specified as implementation-defined.
   1339  // See corresponding preferences in StaticPrefList.yaml for more context.
   1340  const int32_t supportsPriorityDelta = [&]() {
   1341    if (aLoadData.ShouldDefer()) {
   1342      return FETCH_PRIORITY_ADJUSTMENT_FOR(deferred_style,
   1343                                           aLoadData.mFetchPriority);
   1344    }
   1345    if (aLoadData.IsLinkRelPreloadOrEarlyHint()) {
   1346      return FETCH_PRIORITY_ADJUSTMENT_FOR(link_preload_style,
   1347                                           aLoadData.mFetchPriority);
   1348    }
   1349    return FETCH_PRIORITY_ADJUSTMENT_FOR(non_deferred_style,
   1350                                         aLoadData.mFetchPriority);
   1351  }();
   1352 
   1353  sp->AdjustPriority(supportsPriorityDelta);
   1354 #ifdef DEBUG
   1355  int32_t adjustedPriority;
   1356  sp->GetPriority(&adjustedPriority);
   1357  LogPriorityMapping(sCssLoaderLog, aLoadData.mFetchPriority, adjustedPriority);
   1358 #endif
   1359 
   1360  if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(aChannel)) {
   1361    cos->SetFetchPriorityDOM(aLoadData.mFetchPriority);
   1362  }
   1363 }
   1364 
   1365 nsresult Loader::LoadSheetAsyncInternal(SheetLoadData& aLoadData,
   1366                                        uint64_t aEarlyHintPreloaderId,
   1367                                        const SheetLoadDataHashKey& aKey) {
   1368  SRIMetadata sriMetadata;
   1369  aLoadData.mSheet->GetIntegrity(sriMetadata);
   1370 
   1371 #ifdef DEBUG
   1372  AutoRestore<bool> syncCallbackGuard(mSyncCallback);
   1373  mSyncCallback = true;
   1374 #endif
   1375 
   1376  nsCOMPtr<nsIChannel> channel;
   1377  nsresult rv = NewStyleSheetChannel(aLoadData, aLoadData.mSheet->GetCORSMode(),
   1378                                     UsePreload::No, UseLoadGroup::Yes,
   1379                                     getter_AddRefs(channel));
   1380  if (NS_FAILED(rv)) {
   1381    LOG_ERROR(("  Failed to create channel"));
   1382    SheetComplete(aLoadData, rv);
   1383    return rv;
   1384  }
   1385 
   1386  nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
   1387  loadInfo->SetCspNonce(aLoadData.Nonce());
   1388  loadInfo->SetIntegrityMetadata(sriMetadata.GetIntegrityString());
   1389 
   1390  if (!aLoadData.ShouldDefer()) {
   1391    if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(channel)) {
   1392      cos->AddClassFlags(nsIClassOfService::Leader);
   1393    }
   1394 
   1395    if (!aLoadData.BlocksLoadEvent()) {
   1396      SheetLoadData::AddLoadBackgroundFlag(channel);
   1397    }
   1398  }
   1399 
   1400  AdjustPriority(aLoadData, channel);
   1401 
   1402  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
   1403    if (nsCOMPtr<nsIReferrerInfo> referrerInfo = aLoadData.ReferrerInfo()) {
   1404      rv = httpChannel->SetReferrerInfo(referrerInfo);
   1405      (void)NS_WARN_IF(NS_FAILED(rv));
   1406    }
   1407 
   1408    // Set the initiator type
   1409    if (nsCOMPtr<nsITimedChannel> timedChannel =
   1410            do_QueryInterface(httpChannel)) {
   1411      timedChannel->SetInitiatorType(aLoadData.InitiatorTypeString());
   1412      if (aLoadData.mParentData &&
   1413          !aLoadData.mParentData->mSheet->IsOriginClean()) {
   1414        // This is a child sheet load of a cross-origin stylesheet.
   1415        //
   1416        // The resource timing of the sub-resources that a document loads
   1417        // should normally be reported to the document.  One exception is any
   1418        // sub-resources of any cross-origin resources that are loaded.  We
   1419        // don't mind reporting timing data for a direct child cross-origin
   1420        // resource since the resource that linked to it (and hence potentially
   1421        // anything in that parent origin) is aware that the cross-origin
   1422        // resources is to be loaded.  However, we do not want to report
   1423        // timings for any sub-resources that a cross-origin resource may load
   1424        // since that obviously leaks information about what the cross-origin
   1425        // resource loads, which is bad.
   1426        // Mark the channel so PerformanceMainThread::AddEntry will not
   1427        // report the resource.
   1428        timedChannel->SetReportResourceTiming(false);
   1429      }
   1430    }
   1431  }
   1432 
   1433  // Now tell the channel we expect text/css data back....  We do
   1434  // this before opening it, so it's only treated as a hint.
   1435  channel->SetContentType("text/css"_ns);
   1436 
   1437  // We don't have to hold on to the stream loader.  The ownership
   1438  // model is: Necko owns the stream loader, which owns the load data,
   1439  // which owns us
   1440  auto streamLoader = MakeRefPtr<StreamLoader>(aLoadData);
   1441 
   1442 #ifdef DEBUG
   1443  {
   1444    nsCOMPtr<nsIInterfaceRequestor> prevCallback;
   1445    channel->GetNotificationCallbacks(getter_AddRefs(prevCallback));
   1446    MOZ_ASSERT(!prevCallback);
   1447  }
   1448 #endif
   1449  channel->SetNotificationCallbacks(streamLoader);
   1450 
   1451  if (aEarlyHintPreloaderId) {
   1452    nsCOMPtr<nsIHttpChannelInternal> channelInternal =
   1453        do_QueryInterface(channel);
   1454    NS_ENSURE_TRUE(channelInternal != nullptr, NS_ERROR_FAILURE);
   1455 
   1456    rv = channelInternal->SetEarlyHintPreloaderId(aEarlyHintPreloaderId);
   1457    NS_ENSURE_SUCCESS(rv, rv);
   1458  }
   1459  rv = channel->AsyncOpen(streamLoader);
   1460  if (NS_FAILED(rv)) {
   1461    LOG_ERROR(("  Failed to create stream loader"));
   1462    streamLoader->ChannelOpenFailed(rv);
   1463    channel->SetNotificationCallbacks(nullptr);
   1464    // NOTE: NotifyStop will be done in SheetComplete -> NotifyObservers.
   1465    aLoadData.NotifyStart(channel);
   1466    SheetComplete(aLoadData, rv);
   1467    return rv;
   1468  }
   1469 
   1470 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   1471  if (nsCOMPtr<nsIHttpChannelInternal> hci = do_QueryInterface(channel)) {
   1472    hci->DoDiagnosticAssertWhenOnStopNotCalledOnDestroy();
   1473  }
   1474 #endif
   1475 
   1476  if (mSheets) {
   1477    mSheets->LoadStarted(aKey, aLoadData);
   1478  }
   1479  return NS_OK;
   1480 }
   1481 
   1482 /**
   1483 * ParseSheet handles parsing the data stream.
   1484 */
   1485 Loader::Completed Loader::ParseSheet(
   1486    const nsACString& aBytes, const RefPtr<SheetLoadDataHolder>& aLoadData,
   1487    AllowAsyncParse aAllowAsync) {
   1488  LOG(("css::Loader::ParseSheet"));
   1489  SheetLoadData* loadData = aLoadData->get();
   1490  MOZ_ASSERT(loadData);
   1491 
   1492  if (loadData->mURI) {
   1493    LOG_URI("  Load succeeded for URI: '%s', parsing", loadData->mURI);
   1494  }
   1495  AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(LAYOUT_CSSParsing);
   1496 
   1497  ++mParsedSheetCount;
   1498 
   1499  loadData->mIsBeingParsed = true;
   1500 
   1501  StyleSheet* sheet = loadData->mSheet;
   1502  MOZ_ASSERT(sheet);
   1503 
   1504  // Some cases, like inline style and UA stylesheets, need to be parsed
   1505  // synchronously. The former may trigger child loads, the latter must not.
   1506  if (loadData->mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
   1507    sheet->ParseSheetSync(this, aBytes, loadData);
   1508    loadData->mIsBeingParsed = false;
   1509 
   1510    bool noPendingChildren = loadData->mPendingChildren == 0;
   1511    MOZ_ASSERT_IF(loadData->mSyncLoad, noPendingChildren);
   1512    if (noPendingChildren) {
   1513      SheetComplete(*loadData, NS_OK);
   1514      return Completed::Yes;
   1515    }
   1516    return Completed::No;
   1517  }
   1518 
   1519  // This parse does not need to be synchronous. \o/
   1520  //
   1521  // Note that load is already blocked from
   1522  // IncrementOngoingLoadCountAndMaybeBlockOnload(), and will be unblocked from
   1523  // SheetFinishedParsingAsync which will end up in NotifyObservers as needed.
   1524  sheet->ParseSheet(*this, aBytes, aLoadData)
   1525      ->Then(
   1526          GetMainThreadSerialEventTarget(), __func__,
   1527          [loadData = aLoadData](bool aDummy) {
   1528            MOZ_ASSERT(NS_IsMainThread());
   1529            loadData->get()->SheetFinishedParsingAsync();
   1530          },
   1531          [] { MOZ_CRASH("rejected parse promise"); });
   1532  return Completed::No;
   1533 }
   1534 
   1535 void Loader::AddPerformanceEntryForCachedSheet(SheetLoadData& aLoadData) {
   1536  MOZ_ASSERT(aLoadData.mURI);
   1537 
   1538  if (!aLoadData.mNetworkMetadata) {
   1539    return;
   1540  }
   1541  if (!mDocument) {
   1542    return;
   1543  }
   1544 
   1545  nsAutoCString name;
   1546  aLoadData.mURI->GetSpec(name);
   1547  NS_ConvertUTF8toUTF16 entryName(name);
   1548 
   1549  auto end = TimeStamp::Now();
   1550  auto start = aLoadData.mLoadStart;
   1551  if (start.IsNull()) {
   1552    start = end;
   1553  }
   1554 
   1555  SharedSubResourceCacheUtils::AddPerformanceEntryForCache(
   1556      entryName, aLoadData.InitiatorTypeString(), aLoadData.mNetworkMetadata,
   1557      start, end, mDocument);
   1558 }
   1559 
   1560 void Loader::NotifyObservers(SheetLoadData& aData, nsresult aStatus) {
   1561  aData.mSheet->PropagateUseCountersTo(mDocument);
   1562  if (MaybePutIntoLoadsPerformed(aData) &&
   1563      aData.mShouldEmulateNotificationsForCachedLoad) {
   1564    NotifyObserversForCachedSheet(aData);
   1565    AddPerformanceEntryForCachedSheet(aData);
   1566  }
   1567 
   1568  RefPtr loadDispatcher = aData.PrepareLoadEventIfNeeded();
   1569  if (aData.mURI) {
   1570    aData.NotifyStop(aStatus);
   1571    // NOTE(emilio): This needs to happen before notifying observers, as
   1572    // FontFaceSet for example checks for pending sheet loads from the
   1573    // StyleSheetLoaded callback.
   1574    if (aData.BlocksLoadEvent()) {
   1575      DecrementOngoingLoadCountAndMaybeUnblockOnload();
   1576      if (mPendingLoadCount && mPendingLoadCount == mOngoingLoadCount) {
   1577        LOG(("  No more loading sheets; starting deferred loads"));
   1578        StartDeferredLoads();
   1579      }
   1580    }
   1581  }
   1582  if (!aData.mTitle.IsEmpty() && NS_SUCCEEDED(aStatus)) {
   1583    nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
   1584        "Loader::NotifyObservers - Create PageStyle actor",
   1585        [doc = RefPtr{mDocument}] {
   1586          // Force creating the page style actor, if available.
   1587          // This will no-op if no actor with this name is registered (outside
   1588          // of desktop Firefox).
   1589          nsCOMPtr<nsISupports> pageStyleActor =
   1590              do_QueryActor("PageStyle", doc);
   1591          (void)pageStyleActor;
   1592        }));
   1593  }
   1594  if (aData.mMustNotify) {
   1595    if (nsCOMPtr<nsICSSLoaderObserver> observer = std::move(aData.mObserver)) {
   1596      LOG(("  Notifying observer %p for data %p.  deferred: %d", observer.get(),
   1597           &aData, aData.ShouldDefer()));
   1598      observer->StyleSheetLoaded(aData.mSheet, aData.ShouldDefer(), aStatus);
   1599    }
   1600 
   1601    for (nsCOMPtr<nsICSSLoaderObserver> obs : mObservers.ForwardRange()) {
   1602      LOG(("  Notifying global observer %p for data %p.  deferred: %d",
   1603           obs.get(), &aData, aData.ShouldDefer()));
   1604      obs->StyleSheetLoaded(aData.mSheet, aData.ShouldDefer(), aStatus);
   1605    }
   1606 
   1607    if (loadDispatcher) {
   1608      loadDispatcher->RunDOMEventWhenSafe();
   1609    }
   1610  } else if (loadDispatcher) {
   1611    loadDispatcher->PostDOMEvent();
   1612  }
   1613 }
   1614 
   1615 /**
   1616 * SheetComplete is the do-it-all cleanup function.  It removes the
   1617 * load data from the "loading" hashtable, adds the sheet to the
   1618 * "completed" hashtable, massages the XUL cache, handles siblings of
   1619 * the load data (other loads for the same URI), handles unblocking
   1620 * blocked parent loads as needed, and most importantly calls
   1621 * NS_RELEASE on the load data to destroy the whole mess.
   1622 */
   1623 void Loader::SheetComplete(SheetLoadData& aLoadData, nsresult aStatus) {
   1624  LOG(("css::Loader::SheetComplete, status: 0x%" PRIx32,
   1625       static_cast<uint32_t>(aStatus)));
   1626  if (aLoadData.mURI) {
   1627    mReporter->FlushConsoleReports(mDocument);
   1628  }
   1629  SharedStyleSheetCache::LoadCompleted(mSheets.get(), aLoadData, aStatus);
   1630 }
   1631 
   1632 // static
   1633 void Loader::MarkLoadTreeFailed(SheetLoadData& aLoadData,
   1634                                Loader* aOnlyForLoader) {
   1635  if (aLoadData.mURI) {
   1636    LOG_URI("  Load failed: '%s'", aLoadData.mURI);
   1637  }
   1638 
   1639  SheetLoadData* data = &aLoadData;
   1640  do {
   1641    if (!aOnlyForLoader || aOnlyForLoader == data->mLoader) {
   1642      data->mLoadFailed = true;
   1643      data->mSheet->MaybeRejectReplacePromise();
   1644    }
   1645 
   1646    if (data->mParentData) {
   1647      MarkLoadTreeFailed(*data->mParentData, aOnlyForLoader);
   1648    }
   1649 
   1650    data = data->mNext;
   1651  } while (data);
   1652 }
   1653 
   1654 static bool URIsEqual(nsIURI* aA, nsIURI* aB) {
   1655  if (aA == aB) {
   1656    return true;
   1657  }
   1658  if (!aA || !aB) {
   1659    return false;
   1660  }
   1661  bool equal = false;
   1662  return NS_SUCCEEDED(aA->Equals(aB, &equal)) && equal;
   1663 }
   1664 
   1665 // The intention is that this would return true for inputs like
   1666 //   (https://example.com/a, https://example.com/b)
   1667 // but not for:
   1668 //   (https://example.com/a, https://example.com/b/c)
   1669 // where "regular" relative URIs would resolve differently.
   1670 static bool BaseURIsArePathCompatible(nsIURI* aA, nsIURI* aB) {
   1671  if (!aA || !aB) {
   1672    return false;
   1673  }
   1674  constexpr auto kDummyPath = "foo.css"_ns;
   1675  nsAutoCString resultA;
   1676  nsAutoCString resultB;
   1677  aA->Resolve(kDummyPath, resultA);
   1678  aB->Resolve(kDummyPath, resultB);
   1679  return resultA == resultB;
   1680 }
   1681 
   1682 static bool CanReuseInlineSheet(SharedStyleSheetCache::InlineSheetEntry& aEntry,
   1683                                nsIURI* aNewBaseURI, bool aIsImage) {
   1684  auto dependency = aEntry.mSheet->OriginalContentsUriDependency();
   1685  if (dependency == StyleNonLocalUriDependency::No) {
   1686    // If there are no non-local uris, then we can reuse.
   1687    return true;
   1688  }
   1689  if (aIsImage != aEntry.mWasLoadedAsImage) {
   1690    // Even with the same base uri, if the document was loaded as an image on
   1691    // one document, but wasn't on another, we can't cache the sheet, since
   1692    // non-local URIs should not load in those, see bug 1982344.
   1693    return false;
   1694  }
   1695  if (dependency == StyleNonLocalUriDependency::Absolute) {
   1696    // If there are only absolute uris, then we don't need to worry about the
   1697    // base.
   1698    return true;
   1699  }
   1700  nsIURI* oldBase = aEntry.mSheet->GetBaseURI();
   1701  if (URIsEqual(oldBase, aNewBaseURI)) {
   1702    return true;
   1703  }
   1704  switch (dependency) {
   1705    case StyleNonLocalUriDependency::Absolute:
   1706    case StyleNonLocalUriDependency::No:
   1707      MOZ_ASSERT_UNREACHABLE("How?");
   1708      break;
   1709    case StyleNonLocalUriDependency::Path:
   1710      if (BaseURIsArePathCompatible(oldBase, aNewBaseURI)) {
   1711        break;
   1712      }
   1713      [[fallthrough]];
   1714    case StyleNonLocalUriDependency::Full:
   1715      LOG(("  Can't reuse due to base URI dependency"));
   1716      return false;
   1717  }
   1718  return true;
   1719 }
   1720 
   1721 RefPtr<StyleSheet> Loader::LookupInlineSheetInCache(
   1722    const nsAString& aBuffer, nsIPrincipal* aSheetPrincipal, nsIURI* aBaseURI) {
   1723  MOZ_ASSERT(mDocument);
   1724  MOZ_ASSERT(mSheets, "Document associated loader should have sheet cache");
   1725  auto result = mSheets->LookupInline(LoaderPrincipal(), aBuffer);
   1726  if (!result) {
   1727    return nullptr;
   1728  }
   1729  MOZ_ASSERT(!result.Data().IsEmpty());
   1730  const bool asImage = mDocument->IsBeingUsedAsImage();
   1731  for (auto& candidate : result.Data()) {
   1732    auto* sheet = candidate.mSheet.get();
   1733    MOZ_ASSERT(!sheet->HasModifiedRules(),
   1734               "How did we end up with a dirty sheet?");
   1735    if (NS_WARN_IF(!sheet->Principal()->Equals(aSheetPrincipal))) {
   1736      // If the sheet is going to have different access rights, don't return it
   1737      // from the cache. XXX can this happen now that we eagerly clone?
   1738      continue;
   1739    }
   1740    if (!CanReuseInlineSheet(candidate, aBaseURI, asImage)) {
   1741      continue;
   1742    }
   1743    return sheet->Clone(nullptr, nullptr);
   1744  }
   1745  return nullptr;
   1746 }
   1747 
   1748 void Loader::MaybeNotifyPreloadUsed(SheetLoadData& aData) {
   1749  if (!mDocument) {
   1750    return;
   1751  }
   1752 
   1753  auto key = PreloadHashKey::CreateAsStyle(aData);
   1754  RefPtr<PreloaderBase> preload = mDocument->Preloads().LookupPreload(key);
   1755  if (!preload) {
   1756    return;
   1757  }
   1758 
   1759  preload->NotifyUsage(mDocument);
   1760 }
   1761 
   1762 Result<Loader::LoadSheetResult, nsresult> Loader::LoadInlineStyle(
   1763    const SheetInfo& aInfo, const nsAString& aBuffer,
   1764    nsICSSLoaderObserver* aObserver) {
   1765  LOG(("css::Loader::LoadInlineStyle"));
   1766  MOZ_ASSERT(aInfo.mContent);
   1767 
   1768  if (!mEnabled) {
   1769    LOG_WARN(("  Not enabled"));
   1770    return Err(NS_ERROR_NOT_AVAILABLE);
   1771  }
   1772 
   1773  if (!mDocument) {
   1774    return Err(NS_ERROR_NOT_INITIALIZED);
   1775  }
   1776 
   1777  MOZ_ASSERT(LinkStyle::FromNodeOrNull(aInfo.mContent),
   1778             "Element is not a style linking element!");
   1779 
   1780  // Since we're not planning to load a URI, no need to hand a principal to the
   1781  // load data or to CreateSheet().
   1782 
   1783  // Check IsAlternateSheet now, since it can mutate our document.
   1784  auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
   1785  LOG(("  Sheet is alternate: %d", static_cast<int>(isAlternate)));
   1786 
   1787  // Use the document's base URL so that @import in the inline sheet picks up
   1788  // the right base.
   1789  nsIURI* baseURI = aInfo.mContent->GetBaseURI();
   1790  MOZ_ASSERT(aInfo.mIntegrity.IsEmpty());
   1791  nsIPrincipal* loadingPrincipal = LoaderPrincipal();
   1792  nsIPrincipal* principal = aInfo.mTriggeringPrincipal
   1793                                ? aInfo.mTriggeringPrincipal.get()
   1794                                : loadingPrincipal;
   1795  nsIPrincipal* sheetPrincipal = [&] {
   1796    // The triggering principal may be an expanded principal, which is safe to
   1797    // use for URL security checks, but not as the loader principal for a
   1798    // stylesheet. So treat this as principal inheritance, and downgrade if
   1799    // necessary.
   1800    //
   1801    // FIXME(emilio): Why doing this for inline sheets but not for links?
   1802    if (aInfo.mTriggeringPrincipal) {
   1803      return BasePrincipal::Cast(aInfo.mTriggeringPrincipal)
   1804          ->PrincipalToInherit();
   1805    }
   1806    return LoaderPrincipal();
   1807  }();
   1808 
   1809  RefPtr<StyleSheet> sheet =
   1810      LookupInlineSheetInCache(aBuffer, sheetPrincipal, baseURI);
   1811  const bool isSheetFromCache = !!sheet;
   1812  if (!isSheetFromCache) {
   1813    sheet = MakeRefPtr<StyleSheet>(eAuthorSheetFeatures, aInfo.mCORSMode,
   1814                                   SRIMetadata{});
   1815    // If an extension creates an inline stylesheet, we don't want to consider
   1816    // it same-origin with the page.
   1817    // FIXME(emilio): That's rather odd.
   1818    sheet->SetOriginClean(LoaderPrincipal()->Subsumes(sheetPrincipal));
   1819  }
   1820  // We allow sharing inline sheets with e.g. different base URIs, iff there's
   1821  // no dependency on that base URI. However, we still need to keep track of the
   1822  // right URIs in case the sheet is then mutated. EnsureUniqueInner will make
   1823  // sure the StylesheetContents get fixed up.
   1824  nsIReferrerInfo* referrerInfo =
   1825      aInfo.mContent->OwnerDoc()->ReferrerInfoForInternalCSSAndSVGResources();
   1826  sheet->SetURIs(nullptr, baseURI, referrerInfo, sheetPrincipal);
   1827 
   1828  auto matched = PrepareSheet(*sheet, aInfo.mTitle, aInfo.mMedia, nullptr,
   1829                              isAlternate, aInfo.mIsExplicitlyEnabled);
   1830  if (auto* linkStyle = LinkStyle::FromNode(*aInfo.mContent)) {
   1831    linkStyle->SetStyleSheet(sheet);
   1832  }
   1833  MOZ_ASSERT(sheet->IsComplete() == isSheetFromCache);
   1834 
   1835  Completed completed;
   1836  auto data = MakeRefPtr<SheetLoadData>(
   1837      this, aInfo.mTitle, /* aURI = */ nullptr, sheet, SyncLoad::No,
   1838      aInfo.mContent, isAlternate, matched, StylePreloadKind::None, aObserver,
   1839      principal, aInfo.mReferrerInfo, aInfo.mNonce, aInfo.mFetchPriority,
   1840      nullptr);
   1841  MOZ_ASSERT(data->GetRequestingNode() == aInfo.mContent);
   1842  if (isSheetFromCache) {
   1843    MOZ_ASSERT(sheet->IsComplete());
   1844    MOZ_ASSERT(sheet->GetOwnerNode() == aInfo.mContent);
   1845    completed = Completed::Yes;
   1846    InsertSheetInTree(*sheet);
   1847    NotifyOfCachedLoad(std::move(data));
   1848  } else {
   1849    // Parse completion releases the load data.
   1850    //
   1851    // Note that we need to parse synchronously, since the web expects that the
   1852    // effects of inline stylesheets are visible immediately (aside from
   1853    // @imports).
   1854    NS_ConvertUTF16toUTF8 utf8(aBuffer);
   1855    RefPtr<SheetLoadDataHolder> holder(
   1856        new nsMainThreadPtrHolder<css::SheetLoadData>(__func__, data.get(),
   1857                                                      true));
   1858    completed = ParseSheet(utf8, holder, AllowAsyncParse::No);
   1859    if (completed == Completed::Yes) {
   1860      mSheets->InsertInline(
   1861          LoaderPrincipal(), aBuffer,
   1862          {data->ValueForCache(), mDocument->IsBeingUsedAsImage()});
   1863    } else {
   1864      data->mMustNotify = true;
   1865    }
   1866  }
   1867 
   1868  return LoadSheetResult{completed, isAlternate, matched};
   1869 }
   1870 
   1871 nsLiteralString SheetLoadData::InitiatorTypeString() {
   1872  MOZ_ASSERT(mURI, "Inline sheet doesn't have the initiator type string");
   1873 
   1874  if (mPreloadKind == StylePreloadKind::FromEarlyHintsHeader) {
   1875    return u"early-hints"_ns;
   1876  }
   1877 
   1878  if (mParentData) {
   1879    // @import rule.
   1880    return u"css"_ns;
   1881  }
   1882 
   1883  // <link>.
   1884  return u"link"_ns;
   1885 }
   1886 
   1887 Result<Loader::LoadSheetResult, nsresult> Loader::LoadStyleLink(
   1888    const SheetInfo& aInfo, nsICSSLoaderObserver* aObserver) {
   1889  MOZ_ASSERT(aInfo.mURI, "Must have URL to load");
   1890  LOG(("css::Loader::LoadStyleLink"));
   1891  LOG_URI("  Link uri: '%s'", aInfo.mURI);
   1892  LOG(("  Link title: '%s'", NS_ConvertUTF16toUTF8(aInfo.mTitle).get()));
   1893  LOG(("  Link media: '%s'", NS_ConvertUTF16toUTF8(aInfo.mMedia).get()));
   1894  LOG(("  Link alternate rel: %d", aInfo.mHasAlternateRel));
   1895 
   1896  if (!mEnabled) {
   1897    LOG_WARN(("  Not enabled"));
   1898    return Err(NS_ERROR_NOT_AVAILABLE);
   1899  }
   1900 
   1901  if (!mDocument) {
   1902    return Err(NS_ERROR_NOT_INITIALIZED);
   1903  }
   1904 
   1905  MOZ_ASSERT_IF(aInfo.mContent,
   1906                aInfo.mContent->NodePrincipal() == mDocument->NodePrincipal());
   1907  nsIPrincipal* loadingPrincipal = LoaderPrincipal();
   1908  nsIPrincipal* principal = aInfo.mTriggeringPrincipal
   1909                                ? aInfo.mTriggeringPrincipal.get()
   1910                                : loadingPrincipal;
   1911 
   1912  nsINode* requestingNode =
   1913      aInfo.mContent ? static_cast<nsINode*>(aInfo.mContent) : mDocument;
   1914  const bool syncLoad = [&] {
   1915    if (!aInfo.mContent) {
   1916      return false;
   1917    }
   1918    const bool privilegedShadowTree =
   1919        aInfo.mContent->IsInShadowTree() &&
   1920        (aInfo.mContent->ChromeOnlyAccess() ||
   1921         aInfo.mContent->OwnerDoc()->ChromeRulesEnabled());
   1922    if (!privilegedShadowTree) {
   1923      return false;
   1924    }
   1925    if (!IsPrivilegedURI(aInfo.mURI)) {
   1926      return false;
   1927    }
   1928    // We're loading a chrome/resource URI in a chrome doc shadow tree or UA
   1929    // widget. Load synchronously to avoid FOUC.
   1930    return true;
   1931  }();
   1932  LOG(("  Link sync load: '%s'", syncLoad ? "true" : "false"));
   1933  MOZ_ASSERT_IF(syncLoad, !aObserver);
   1934 
   1935  nsresult rv = CheckContentPolicy(
   1936      loadingPrincipal, principal, aInfo.mURI, requestingNode, aInfo.mNonce,
   1937      StylePreloadKind::None, aInfo.mCORSMode, aInfo.mIntegrity);
   1938  if (NS_FAILED(rv)) {
   1939    // Don't fire the error event if our document is loaded as data.  We're
   1940    // supposed to not even try to do loads in that case... Unfortunately, we
   1941    // implement that via nsDataDocumentContentPolicy, which doesn't have a good
   1942    // way to communicate back to us that _it_ is the thing that blocked the
   1943    // load.
   1944    if (aInfo.mContent && !mDocument->IsLoadedAsData()) {
   1945      // Fire an async error event on it.
   1946      RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
   1947          new LoadBlockingAsyncEventDispatcher(aInfo.mContent, u"error"_ns,
   1948                                               CanBubble::eNo,
   1949                                               ChromeOnlyDispatch::eNo);
   1950      loadBlockingAsyncDispatcher->PostDOMEvent();
   1951    }
   1952    return Err(rv);
   1953  }
   1954 
   1955  // Check IsAlternateSheet now, since it can mutate our document and make
   1956  // pending sheets go to the non-pending state.
   1957  auto isAlternate = IsAlternateSheet(aInfo.mTitle, aInfo.mHasAlternateRel);
   1958  auto [sheet, state, networkMetadata] = CreateSheet(
   1959      aInfo, eAuthorSheetFeatures, syncLoad, StylePreloadKind::None);
   1960 
   1961  LOG(("  Sheet is alternate: %d", static_cast<int>(isAlternate)));
   1962 
   1963  auto matched = PrepareSheet(*sheet, aInfo.mTitle, aInfo.mMedia, nullptr,
   1964                              isAlternate, aInfo.mIsExplicitlyEnabled);
   1965 
   1966  if (auto* linkStyle = LinkStyle::FromNodeOrNull(aInfo.mContent)) {
   1967    linkStyle->SetStyleSheet(sheet);
   1968  }
   1969  MOZ_ASSERT(sheet->IsComplete() == (state == SheetState::Complete));
   1970 
   1971  // We may get here with no content for Link: headers for example.
   1972  MOZ_ASSERT(!aInfo.mContent || LinkStyle::FromNode(*aInfo.mContent),
   1973             "If there is any node, it should be a LinkStyle");
   1974  auto data = MakeRefPtr<SheetLoadData>(
   1975      this, aInfo.mTitle, aInfo.mURI, sheet, SyncLoad(syncLoad), aInfo.mContent,
   1976      isAlternate, matched, StylePreloadKind::None, aObserver, principal,
   1977      aInfo.mReferrerInfo, aInfo.mNonce, aInfo.mFetchPriority,
   1978      networkMetadata.forget());
   1979 
   1980  MOZ_ASSERT(data->GetRequestingNode() == requestingNode);
   1981 
   1982  MaybeNotifyPreloadUsed(*data);
   1983 
   1984  if (state == SheetState::Complete) {
   1985    LOG(("  Sheet already complete: 0x%p", sheet.get()));
   1986    MOZ_ASSERT(sheet->GetOwnerNode() == aInfo.mContent);
   1987    InsertSheetInTree(*sheet);
   1988    NotifyOfCachedLoad(std::move(data));
   1989    return LoadSheetResult{Completed::Yes, isAlternate, matched};
   1990  }
   1991 
   1992  // Now we need to actually load it.
   1993  auto result = LoadSheetResult{Completed::No, isAlternate, matched};
   1994 
   1995  MOZ_ASSERT(result.ShouldBlock() == !data->ShouldDefer(),
   1996             "These should better match!");
   1997 
   1998  // Load completion will free the data
   1999  rv = LoadSheet(*data, state, 0);
   2000  if (NS_FAILED(rv)) {
   2001    return Err(rv);
   2002  }
   2003 
   2004  if (!syncLoad) {
   2005    data->mMustNotify = true;
   2006  }
   2007  return result;
   2008 }
   2009 
   2010 static bool HaveAncestorDataWithURI(SheetLoadData& aData, nsIURI* aURI) {
   2011  if (!aData.mURI) {
   2012    // Inline style; this won't have any ancestors
   2013    MOZ_ASSERT(!aData.mParentData, "How does inline style have a parent?");
   2014    return false;
   2015  }
   2016 
   2017  bool equal;
   2018  if (NS_FAILED(aData.mURI->Equals(aURI, &equal)) || equal) {
   2019    return true;
   2020  }
   2021 
   2022  // Datas down the mNext chain have the same URI as aData, so we
   2023  // don't have to compare to them.  But they might have different
   2024  // parents, and we have to check all of those.
   2025  SheetLoadData* data = &aData;
   2026  do {
   2027    if (data->mParentData &&
   2028        HaveAncestorDataWithURI(*data->mParentData, aURI)) {
   2029      return true;
   2030    }
   2031 
   2032    data = data->mNext;
   2033  } while (data);
   2034 
   2035  return false;
   2036 }
   2037 
   2038 nsresult Loader::LoadChildSheet(StyleSheet& aParentSheet,
   2039                                SheetLoadData* aParentData, nsIURI* aURL,
   2040                                dom::MediaList* aMedia,
   2041                                LoaderReusableStyleSheets* aReusableSheets) {
   2042  LOG(("css::Loader::LoadChildSheet"));
   2043  MOZ_ASSERT(aURL, "Must have a URI to load");
   2044 
   2045  if (!mEnabled) {
   2046    LOG_WARN(("  Not enabled"));
   2047    return NS_ERROR_NOT_AVAILABLE;
   2048  }
   2049 
   2050  LOG_URI("  Child uri: '%s'", aURL);
   2051 
   2052  nsCOMPtr<nsINode> owningNode;
   2053 
   2054  nsINode* requestingNode = aParentSheet.GetOwnerNodeOfOutermostSheet();
   2055  if (requestingNode) {
   2056    MOZ_ASSERT(LoaderPrincipal() == requestingNode->NodePrincipal());
   2057  } else {
   2058    requestingNode = mDocument;
   2059  }
   2060 
   2061  nsIPrincipal* principal = aParentSheet.Principal();
   2062  nsresult rv = CheckContentPolicy(
   2063      LoaderPrincipal(), principal, aURL, requestingNode,
   2064      /* aNonce = */ u""_ns, StylePreloadKind::None, CORS_NONE, u""_ns);
   2065  if (NS_WARN_IF(NS_FAILED(rv))) {
   2066    if (aParentData) {
   2067      MarkLoadTreeFailed(*aParentData);
   2068    }
   2069    return rv;
   2070  }
   2071 
   2072  nsCOMPtr<nsICSSLoaderObserver> observer;
   2073 
   2074  if (aParentData) {
   2075    LOG(("  Have a parent load"));
   2076    // Check for cycles
   2077    if (HaveAncestorDataWithURI(*aParentData, aURL)) {
   2078      // Houston, we have a loop, blow off this child and pretend this never
   2079      // happened
   2080      LOG_ERROR(("  @import cycle detected, dropping load"));
   2081      return NS_OK;
   2082    }
   2083 
   2084    NS_ASSERTION(aParentData->mSheet == &aParentSheet,
   2085                 "Unexpected call to LoadChildSheet");
   2086  } else {
   2087    LOG(("  No parent load; must be CSSOM"));
   2088    // No parent load data, so the sheet will need to be notified when
   2089    // we finish, if it can be, if we do the load asynchronously.
   2090    observer = &aParentSheet;
   2091  }
   2092 
   2093  // Now that we know it's safe to load this (passes security check and not a
   2094  // loop) do so.
   2095  RefPtr<StyleSheet> sheet;
   2096  RefPtr<SubResourceNetworkMetadataHolder> networkMetadata;
   2097  SheetState state;
   2098  bool isReusableSheet = false;
   2099  if (aReusableSheets && aReusableSheets->FindReusableStyleSheet(aURL, sheet)) {
   2100    state = SheetState::Complete;
   2101    isReusableSheet = true;
   2102  } else {
   2103    // For now, use CORS_NONE for child sheets
   2104    std::tie(sheet, state, networkMetadata) = CreateSheet(
   2105        aURL, nullptr, principal, aParentSheet.ParsingMode(), CORS_NONE,
   2106        aParentData ? aParentData->mEncoding : nullptr,
   2107        u""_ns,  // integrity is only checked on main sheet
   2108        aParentData && aParentData->mSyncLoad, StylePreloadKind::None);
   2109    PrepareSheet(*sheet, u""_ns, u""_ns, aMedia, IsAlternate::No,
   2110                 IsExplicitlyEnabled::No);
   2111  }
   2112 
   2113  MOZ_ASSERT(sheet);
   2114  InsertChildSheet(*sheet, aParentSheet);
   2115 
   2116  auto data = MakeRefPtr<SheetLoadData>(
   2117      this, aURL, sheet, aParentData, observer, principal,
   2118      aParentSheet.GetReferrerInfo(), networkMetadata.forget());
   2119  MOZ_ASSERT(data->GetRequestingNode() == requestingNode);
   2120 
   2121  MaybeNotifyPreloadUsed(*data);
   2122 
   2123  if (state == SheetState::Complete) {
   2124    LOG(("  Sheet already complete"));
   2125    // We're completely done.  No need to notify, even, since the
   2126    // @import rule addition/modification will trigger the right style
   2127    // changes automatically.
   2128    if (!isReusableSheet) {
   2129      // Child sheets are not handled by NotifyObservers, and these need to be
   2130      // performed here if the sheet comes from the SharedStyleSheetCache.
   2131      data->mSheet->PropagateUseCountersTo(mDocument);
   2132      if (MaybePutIntoLoadsPerformed(*data)) {
   2133        NotifyObserversForCachedSheet(*data);
   2134        AddPerformanceEntryForCachedSheet(*data);
   2135      }
   2136    }
   2137    data->mIntentionallyDropped = true;
   2138    return NS_OK;
   2139  }
   2140 
   2141  bool syncLoad = data->mSyncLoad;
   2142 
   2143  // Load completion will release the data
   2144  rv = LoadSheet(*data, state, 0);
   2145  NS_ENSURE_SUCCESS(rv, rv);
   2146 
   2147  if (!syncLoad) {
   2148    data->mMustNotify = true;
   2149  }
   2150  return rv;
   2151 }
   2152 
   2153 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheetSync(
   2154    nsIURI* aURL, SheetParsingMode aParsingMode,
   2155    UseSystemPrincipal aUseSystemPrincipal) {
   2156  LOG(("css::Loader::LoadSheetSync"));
   2157  nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(nullptr);
   2158  return InternalLoadNonDocumentSheet(
   2159      aURL, StylePreloadKind::None, aParsingMode, aUseSystemPrincipal, nullptr,
   2160      referrerInfo, nullptr, CORS_NONE, u""_ns, u""_ns, 0, FetchPriority::Auto);
   2161 }
   2162 
   2163 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheet(
   2164    nsIURI* aURI, SheetParsingMode aParsingMode,
   2165    UseSystemPrincipal aUseSystemPrincipal, nsICSSLoaderObserver* aObserver) {
   2166  nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo(nullptr);
   2167  return InternalLoadNonDocumentSheet(
   2168      aURI, StylePreloadKind::None, aParsingMode, aUseSystemPrincipal, nullptr,
   2169      referrerInfo, aObserver, CORS_NONE, u""_ns, u""_ns, 0,
   2170      FetchPriority::Auto);
   2171 }
   2172 
   2173 Result<RefPtr<StyleSheet>, nsresult> Loader::LoadSheet(
   2174    nsIURI* aURL, StylePreloadKind aPreloadKind,
   2175    const Encoding* aPreloadEncoding, nsIReferrerInfo* aReferrerInfo,
   2176    nsICSSLoaderObserver* aObserver, uint64_t aEarlyHintPreloaderId,
   2177    CORSMode aCORSMode, const nsAString& aNonce, const nsAString& aIntegrity,
   2178    FetchPriority aFetchPriority) {
   2179  LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
   2180  return InternalLoadNonDocumentSheet(
   2181      aURL, aPreloadKind, eAuthorSheetFeatures, UseSystemPrincipal::No,
   2182      aPreloadEncoding, aReferrerInfo, aObserver, aCORSMode, aNonce, aIntegrity,
   2183      aEarlyHintPreloaderId, aFetchPriority);
   2184 }
   2185 
   2186 Result<RefPtr<StyleSheet>, nsresult> Loader::InternalLoadNonDocumentSheet(
   2187    nsIURI* aURL, StylePreloadKind aPreloadKind, SheetParsingMode aParsingMode,
   2188    UseSystemPrincipal aUseSystemPrincipal, const Encoding* aPreloadEncoding,
   2189    nsIReferrerInfo* aReferrerInfo, nsICSSLoaderObserver* aObserver,
   2190    CORSMode aCORSMode, const nsAString& aNonce, const nsAString& aIntegrity,
   2191    uint64_t aEarlyHintPreloaderId, FetchPriority aFetchPriority) {
   2192  MOZ_ASSERT(aURL, "Must have a URI to load");
   2193  MOZ_ASSERT(aUseSystemPrincipal == UseSystemPrincipal::No || !aObserver,
   2194             "Shouldn't load system-principal sheets async");
   2195  MOZ_ASSERT(aReferrerInfo, "Must have referrerInfo");
   2196 
   2197  LOG_URI("  Non-document sheet uri: '%s'", aURL);
   2198 
   2199  if (!mEnabled) {
   2200    LOG_WARN(("  Not enabled"));
   2201    return Err(NS_ERROR_NOT_AVAILABLE);
   2202  }
   2203 
   2204  nsIPrincipal* loadingPrincipal = LoaderPrincipal();
   2205  nsIPrincipal* triggeringPrincipal = loadingPrincipal;
   2206  nsresult rv =
   2207      CheckContentPolicy(loadingPrincipal, triggeringPrincipal, aURL, mDocument,
   2208                         aNonce, aPreloadKind, aCORSMode, aIntegrity);
   2209  if (NS_FAILED(rv)) {
   2210    return Err(rv);
   2211  }
   2212 
   2213  bool syncLoad = !aObserver;
   2214  auto [sheet, state, networkMetadata] =
   2215      CreateSheet(aURL, nullptr, triggeringPrincipal, aParsingMode, aCORSMode,
   2216                  aPreloadEncoding, aIntegrity, syncLoad, aPreloadKind);
   2217 
   2218  PrepareSheet(*sheet, u""_ns, u""_ns, nullptr, IsAlternate::No,
   2219               IsExplicitlyEnabled::No);
   2220 
   2221  auto data = MakeRefPtr<SheetLoadData>(
   2222      this, aURL, sheet, SyncLoad(syncLoad), aUseSystemPrincipal, aPreloadKind,
   2223      aPreloadEncoding, aObserver, triggeringPrincipal, aReferrerInfo, aNonce,
   2224      aFetchPriority, networkMetadata.forget());
   2225  MOZ_ASSERT(data->GetRequestingNode() == mDocument);
   2226  if (state == SheetState::Complete) {
   2227    LOG(("  Sheet already complete"));
   2228    NotifyOfCachedLoad(std::move(data));
   2229    return sheet;
   2230  }
   2231 
   2232  rv = LoadSheet(*data, state, aEarlyHintPreloaderId);
   2233  if (NS_FAILED(rv)) {
   2234    return Err(rv);
   2235  }
   2236  if (aObserver) {
   2237    data->mMustNotify = true;
   2238  }
   2239  return sheet;
   2240 }
   2241 
   2242 void Loader::NotifyOfCachedLoad(RefPtr<SheetLoadData> aLoadData) {
   2243  LOG(("css::Loader::PostLoadEvent"));
   2244  MOZ_ASSERT(aLoadData->mSheet->IsComplete(),
   2245             "Only expected to be used for cached sheets");
   2246  // If we get to this code, the stylesheet loaded correctly at some point, so
   2247  // we can just schedule a load event and don't need to touch the data's
   2248  // mLoadFailed.
   2249  // Note that we do this here and not from inside our SheetComplete so that we
   2250  // don't end up running the load event more async than needed.
   2251  MOZ_ASSERT(!aLoadData->mLoadFailed, "Why are we marked as failed?");
   2252  aLoadData->mSheetAlreadyComplete = true;
   2253 
   2254  if (aLoadData->mURI) {
   2255    aLoadData->mShouldEmulateNotificationsForCachedLoad = true;
   2256  }
   2257 
   2258  // We need to check mURI to match
   2259  // DecrementOngoingLoadCountAndMaybeUnblockOnload().
   2260  if (aLoadData->mURI && aLoadData->BlocksLoadEvent()) {
   2261    IncrementOngoingLoadCountAndMaybeBlockOnload();
   2262  }
   2263  SheetComplete(*aLoadData, NS_OK);
   2264 }
   2265 
   2266 void Loader::NotifyObserversForCachedSheet(SheetLoadData& aLoadData) {
   2267  nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
   2268 
   2269  if (!obsService->HasObservers("http-on-resource-cache-response")) {
   2270    return;
   2271  }
   2272 
   2273  nsCOMPtr<nsIChannel> channel;
   2274  nsresult rv = NewStyleSheetChannel(aLoadData, aLoadData.mSheet->GetCORSMode(),
   2275                                     UsePreload::No, UseLoadGroup::No,
   2276                                     getter_AddRefs(channel));
   2277  if (NS_FAILED(rv)) {
   2278    return;
   2279  }
   2280 
   2281  RefPtr<HttpBaseChannel> httpBaseChannel = do_QueryObject(channel);
   2282  if (httpBaseChannel) {
   2283    const net::nsHttpResponseHead* responseHead = nullptr;
   2284    if (aLoadData.GetNetworkMetadata()) {
   2285      responseHead = aLoadData.GetNetworkMetadata()->GetResponseHead();
   2286    }
   2287    httpBaseChannel->SetDummyChannelForCachedResource(responseHead);
   2288  }
   2289 
   2290  channel->SetContentType("text/css"_ns);
   2291 
   2292  // TODO: Populate other fields (bug 1915626).
   2293 
   2294  obsService->NotifyObservers(channel, "http-on-resource-cache-response",
   2295                              nullptr);
   2296 }
   2297 
   2298 void Loader::Stop() {
   2299  if (mSheets) {
   2300    mSheets->CancelLoadsForLoader(*this);
   2301  }
   2302 }
   2303 
   2304 bool Loader::HasPendingLoads() { return mOngoingLoadCount; }
   2305 
   2306 void Loader::AddObserver(nsICSSLoaderObserver* aObserver) {
   2307  MOZ_ASSERT(aObserver, "Must have observer");
   2308  mObservers.AppendElementUnlessExists(aObserver);
   2309 }
   2310 
   2311 void Loader::RemoveObserver(nsICSSLoaderObserver* aObserver) {
   2312  mObservers.RemoveElement(aObserver);
   2313 }
   2314 
   2315 void Loader::StartDeferredLoads() {
   2316  if (mSheets && mPendingLoadCount) {
   2317    mSheets->StartPendingLoadsForLoader(
   2318        *this, [](const SheetLoadData&) { return true; });
   2319  }
   2320 }
   2321 
   2322 NS_IMPL_CYCLE_COLLECTION_CLASS(Loader)
   2323 
   2324 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Loader)
   2325  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheets);
   2326  for (nsCOMPtr<nsICSSLoaderObserver>& obs : tmp->mObservers.ForwardRange()) {
   2327    ImplCycleCollectionTraverse(cb, obs, "mozilla::css::Loader.mObservers");
   2328  }
   2329  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
   2330 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   2331 
   2332 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Loader)
   2333  if (tmp->mSheets) {
   2334    if (tmp->mDocument) {
   2335      tmp->DeregisterFromSheetCache();
   2336    }
   2337    tmp->mSheets = nullptr;
   2338  }
   2339  tmp->mObservers.Clear();
   2340  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocGroup)
   2341 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   2342 
   2343 size_t Loader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
   2344  size_t n = aMallocSizeOf(this);
   2345 
   2346  n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
   2347 
   2348  // Measurement of the following members may be added later if DMD finds it is
   2349  // worthwhile:
   2350  // The following members aren't measured:
   2351  // - mDocument, because it's a weak backpointer
   2352 
   2353  return n;
   2354 }
   2355 
   2356 nsIPrincipal* Loader::LoaderPrincipal() const {
   2357  if (mDocument) {
   2358    return mDocument->NodePrincipal();
   2359  }
   2360  // Loaders without a document do system loads.
   2361  return nsContentUtils::GetSystemPrincipal();
   2362 }
   2363 
   2364 nsIPrincipal* Loader::PartitionedPrincipal() const {
   2365  return mDocument ? mDocument->PartitionedPrincipal() : LoaderPrincipal();
   2366 }
   2367 
   2368 bool Loader::ShouldBypassCache() const {
   2369  return mDocument && nsContentUtils::ShouldBypassSubResourceCache(mDocument);
   2370 }
   2371 
   2372 void Loader::BlockOnload() {
   2373  if (mDocument) {
   2374    mDocument->BlockOnload();
   2375  }
   2376 }
   2377 
   2378 void Loader::UnblockOnload(bool aFireSync) {
   2379  if (mDocument) {
   2380    mDocument->UnblockOnload(aFireSync);
   2381  }
   2382 }
   2383 
   2384 }  // namespace css
   2385 }  // namespace mozilla