/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* loading of CSS style sheets using the network APIs */
/** * OVERALL ARCHITECTURE * * The CSS Loader gets requests to load various sorts of style sheets: * inline style from <style> elements, linked style, @import-ed child * sheets, non-document sheets. The loader handles the following tasks: * 1) Creation of the actual style sheet objects: CreateSheet() * 2) setting of the right media, title, enabled state, etc on the * sheet: PrepareSheet() * 3) Insertion of the sheet in the proper cascade order: * InsertSheetInTree() and InsertChildSheet() * 4) Load of the sheet: LoadSheet() including security checks * 5) Parsing of the sheet: ParseSheet() * 6) Cleanup: SheetComplete() * * The detailed documentation for these functions is found with the * function implementations. * * The following helper object is used: * SheetLoadData -- a small class that is used to store all the * information needed for the loading of a sheet; * this class handles listening for the stream * loader completion and also handles charset * determination.
*/
if (!mPrincipal->Equals(aKey.mPrincipal)) {
LOG((" > Principal mismatch\n")); returnfalse;
}
// We only check for partition principal equality if any of the loads are // triggered by a document rather than e.g. an extension (which have different // origins than the loader principal). if (mPrincipal->Equals(mLoaderPrincipal) ||
aKey.mPrincipal->Equals(aKey.mLoaderPrincipal)) { if (!mPartitionPrincipal->Equals(aKey.mPartitionPrincipal)) {
LOG((" > Partition principal mismatch\n")); returnfalse;
}
}
if (mCORSMode != aKey.mCORSMode) {
LOG((" > CORS mismatch\n")); returnfalse;
}
// If encoding differs, then don't reuse the cache. // // TODO(emilio): When the encoding is determined from the request (either // BOM or Content-Length or @charset), we could do a bit better, // theoretically. if (mEncodingGuess != aKey.mEncodingGuess) {
LOG((" > Encoding guess mismatch\n")); returnfalse;
}
// Consuming stylesheet tags must never coalesce to <link preload> initiated // speculative loads with a weaker SRI hash or its different value. This // check makes sure that regular loads will never find such a weaker preload // and rather start a new, independent load with new, stronger SRI checker // set up, so that integrity is ensured. if (mIsLinkRelPreloadOrEarlyHint != aKey.mIsLinkRelPreloadOrEarlyHint) { constauto& linkPreloadMetadata =
mIsLinkRelPreloadOrEarlyHint ? mSRIMetadata : aKey.mSRIMetadata; constauto& consumerPreloadMetadata =
mIsLinkRelPreloadOrEarlyHint ? aKey.mSRIMetadata : mSRIMetadata; if (!consumerPreloadMetadata.CanTrustBeDelegatedTo(linkPreloadMetadata)) {
LOG((" > Preload SRI metadata mismatch\n")); returnfalse;
}
}
returntrue;
}
namespace css {
static NotNull<const Encoding*> GetFallbackEncoding(
Loader& aLoader, nsINode* aOwningNode, const Encoding* aPreloadOrParentDataEncoding) { const Encoding* encoding; // Now try the charset on the <link> or processing instruction // that loaded us if (aOwningNode) {
nsAutoString label16;
LinkStyle::FromNode(*aOwningNode)->GetCharset(label16);
encoding = Encoding::ForLabel(label16); if (encoding) { return WrapNotNull(encoding);
}
}
// Try preload or parent sheet encoding. if (aPreloadOrParentDataEncoding) { return WrapNotNull(aPreloadOrParentDataEncoding);
}
if (auto* doc = aLoader.GetDocument()) { // Use the document charset. return doc->GetDocumentCharacterSet();
}
RefPtr<StyleSheet> SheetLoadData::ValueForCache() const { // We need to clone the sheet on insertion to the cache because otherwise the // stylesheets can keep full windows alive via either their JS wrapper, or via // StyleSheet::mRelevantGlobal. // // If this ever changes, then you also need to fix up the memory reporting in // both SizeOfIncludingThis and nsXULPrototypeCache::CollectMemoryReports. return mSheet->Clone(nullptr, nullptr);
}
Loader::Loader(Document* aDocument) : Loader() {
MOZ_ASSERT(aDocument, "We should get a valid document from the caller!");
mDocument = aDocument;
mIsDocumentAssociated = true;
mDocumentCompatMode = aDocument->GetCompatibilityMode();
mSheets = SharedStyleSheetCache::Get();
RegisterInSheetCache();
}
// Note: no real need to revoke our stylesheet loaded events -- they hold strong // references to us, so if we're going away that means they're all done.
Loader::~Loader() = default;
void Loader::DropDocumentReference() { // Flush out pending datas just so we don't leak by accident. if (mSheets) {
DeregisterFromSheetCache();
}
mDocument = nullptr;
}
// We need to block resolution of parse promise until we receive OnStopRequest // on Main thread. This is necessary because parse promise resolution fires // OnLoad event must not be dispatched until OnStopRequest in main thread is // processed, for stuff like performance resource entries.
mSheet->BlockParsePromise();
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); if (!channel) { return;
}
nsCOMPtr<nsIURI> originalURI;
channel->GetOriginalURI(getter_AddRefs(originalURI));
MOZ_DIAGNOSTIC_ASSERT(originalURI, "Someone just violated the nsIRequest contract");
nsCOMPtr<nsIURI> finalURI;
NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI));
MOZ_DIAGNOSTIC_ASSERT(finalURI, "Someone just violated the nsIRequest contract");
mSheet->SetURIs(finalURI, originalURI, finalURI);
/* * Here we need to check that the load did not give us an http error * page and check the mimetype on the channel to make sure we're not * loading non-text/css data in standards mode.
*/
nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus, const nsACString& aBytes1, const nsACString& aBytes2,
nsIChannel* aChannel) {
LOG(("SheetLoadData::VerifySheetReadyToParse"));
NS_ASSERTION((!NS_IsMainThread() || !mLoader->mSyncCallback), "Synchronous callback from necko");
if (AllLoadsCanceled(*this)) { if (NS_IsMainThread()) {
LOG_WARN((" All loads are canceled, dropping"));
mLoader->SheetComplete(*this, NS_BINDING_ABORTED);
} return NS_OK;
}
if (!NS_IsMainThread() && mRecordErrors) { // we cannot parse sheet OMT if we need to record errors return NS_OK;
}
if (NS_FAILED(aStatus)) { if (NS_IsMainThread()) {
LOG_WARN(
(" Load failed: status 0x%" PRIx32, static_cast<uint32_t>(aStatus))); // Handle sheet not loading error because source was a tracking URL (or // fingerprinting, cryptomining, etc). // We make a note of this sheet node by including it in a dedicated // array of blocked tracking nodes under its parent document. // // Multiple sheet load instances might be tied to this request, // we annotate each one linked to a valid owning element (node). if (net::UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(
aStatus)) { if (Document* doc = mLoader->GetDocument()) { for (SheetLoadData* data = this; data; data = data->mNext) { // owner node may be null but AddBlockTrackingNode can cope
doc->AddBlockedNodeByClassifier(data->mSheet->GetOwnerNode());
}
}
}
mLoader->SheetComplete(*this, aStatus);
} return NS_OK;
}
if (!aChannel) {
MOZ_ASSERT(NS_IsMainThread());
mLoader->SheetComplete(*this, NS_OK); return NS_OK;
}
// If it's an HTTP channel, we want to make sure this is not an // error document we got. if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) { bool requestSucceeded;
nsresult result = httpChannel->GetRequestSucceeded(&requestSucceeded); if (NS_SUCCEEDED(result) && !requestSucceeded) { if (NS_IsMainThread()) {
LOG((" Load returned an error page"));
mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
} return NS_OK;
}
}
// In standards mode, a style sheet must have one of these MIME // types to be processed at all. In quirks mode, we accept any // MIME type, but only if the style sheet is same-origin with the // requesting document or parent sheet. See bug 524223.
if (errorFlag == nsIScriptError::errorFlag) {
LOG_WARN(
(" Ignoring sheet with improper MIME type %s", contentType.get()));
mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE); return NS_OK;
}
}
SRIMetadata sriMetadata;
mSheet->GetIntegrity(sriMetadata); if (!sriMetadata.IsEmpty()) { if (!NS_IsMainThread()) { // We dont process any further in OMT. // This is because we have some main-thread only accesses below. // We need to find a way to optimize this handling. // See Bug 1882046. return NS_OK;
}
nsAutoCString sourceUri; if (mLoader->mDocument && mLoader->mDocument->GetDocumentURI()) {
mLoader->mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
}
nsresult rv = VerifySheetIntegrity(sriMetadata, aChannel, aBytes1, aBytes2,
sourceUri, mLoader->mReporter);
if (NS_FAILED(rv)) {
LOG((" Load was blocked by SRI"));
MOZ_LOG(gSriPRLog, LogLevel::Debug,
("css::Loader::OnStreamComplete, bad metadata"));
mLoader->SheetComplete(*this, NS_ERROR_SRI_CORRUPT); return NS_OK;
}
} return NS_OK_PARSE_SHEET;
}
Loader::IsAlternate Loader::IsAlternateSheet(const nsAString& aTitle, bool aHasAlternateRel) { // A sheet is alternate if it has a nonempty title that doesn't match the // currently selected style set. But if there _is_ no currently selected // style set, the sheet wasn't marked as an alternate explicitly, and aTitle // is nonempty, we should select the style set corresponding to aTitle, since // that's a preferred sheet. if (aTitle.IsEmpty()) { return IsAlternate::No;
}
if (mDocument) { const nsString& currentSheetSet = mDocument->GetCurrentStyleSheetSet(); if (!aHasAlternateRel && currentSheetSet.IsEmpty()) { // There's no preferred set yet, and we now have a sheet with a title. // Make that be the preferred set. // FIXME(emilio): This is kinda wild, can we do it somewhere else?
mDocument->SetPreferredStyleSheetSet(aTitle); // We're definitely not an alternate. Also, beware that at this point // currentSheetSet may dangle. return IsAlternate::No;
}
if (aTitle.Equals(currentSheetSet)) { return IsAlternate::No;
}
}
/** * CreateSheet() creates a StyleSheet object for the given URI. * * We check for an existing style sheet object for that uri in various caches * and clone it if we find it. Cloned sheets will have the title/media/enabled * state of the sheet they are clones off; make sure to call PrepareSheet() on * the result of CreateSheet().
*/
std::tuple<RefPtr<StyleSheet>, Loader::SheetState,
RefPtr<SubResourceNetworkMetadataHolder>>
Loader::CreateSheet(nsIURI* aURI, nsIContent* aLinkingContent,
nsIPrincipal* aTriggeringPrincipal,
css::SheetParsingMode aParsingMode, CORSMode aCORSMode, const Encoding* aPreloadOrParentDataEncoding, const nsAString& aIntegrity, bool aSyncLoad,
StylePreloadKind aPreloadKind) {
MOZ_ASSERT(aURI, "This path is not taken for inline stylesheets");
LOG(("css::Loader::CreateSheet(%s)", aURI->GetSpecOrDefault().get()));
if (aMediaList->Matches(*aDocument)) { return Loader::MediaMatched::Yes;
}
return Loader::MediaMatched::No;
}
/** * PrepareSheet() handles setting the media and title on the sheet, as * well as setting the enabled state based on the title and whether * the sheet had "alternate" in its rel.
*/
Loader::MediaMatched Loader::PrepareSheet(
StyleSheet& aSheet, const nsAString& aTitle, const nsAString& aMediaString,
MediaList* aMediaList, IsAlternate aIsAlternate,
IsExplicitlyEnabled aIsExplicitlyEnabled) {
RefPtr<MediaList> mediaList(aMediaList);
if (!aMediaString.IsEmpty()) {
NS_ASSERTION(!aMediaList, "must not provide both aMediaString and aMediaList");
mediaList = MediaList::Create(NS_ConvertUTF16toUTF8(aMediaString));
}
/** * InsertSheetInTree handles ordering of sheets in the document or shadow root. * * Here we have two types of sheets -- those with linking elements and * those without. The latter are loaded by Link: headers, and are only added to * the document. * * The following constraints are observed: * 1) Any sheet with a linking element comes after all sheets without * linking elements * 2) Sheets without linking elements are inserted in the order in * which the inserting requests come in, since all of these are * inserted during header data processing in the content sink * 3) Sheets with linking elements are ordered based on document order * as determined by CompareDocumentPosition.
*/ void Loader::InsertSheetInTree(StyleSheet& aSheet) {
LOG(("css::Loader::InsertSheetInTree"));
MOZ_ASSERT(mDocument, "Must have a document to insert into");
nsINode* owningNode = aSheet.GetOwnerNode();
MOZ_ASSERT(!owningNode || owningNode->IsInUncomposedDoc() ||
owningNode->IsInShadowTree(), "Why would we insert it anywhere?");
ShadowRoot* shadow = owningNode ? owningNode->GetContainingShadow() : nullptr;
// XXX Need to cancel pending sheet loads for this element, if any
int32_t sheetCount = target.SheetCount();
/* * Start the walk at the _end_ of the list, since in the typical * case we'll just want to append anyway. We want to break out of * the loop when insertionPoint points to just before the index we * want to insert at. In other words, when we leave the loop * insertionPoint is the index of the stylesheet that immediately * precedes the one we're inserting.
*/
int32_t insertionPoint = sheetCount - 1; for (; insertionPoint >= 0; --insertionPoint) {
nsINode* sheetOwner = target.SheetAt(insertionPoint)->GetOwnerNode(); if (sheetOwner && !owningNode) { // Keep moving; all sheets with a sheetOwner come after all // sheets without a linkingNode continue;
}
if (!sheetOwner) { // Aha! The current sheet has no sheet owner, so we want to insert after // it no matter whether we have a linking content or not. break;
}
MOZ_ASSERT(owningNode != sheetOwner, "Why do we still have our old sheet?");
// Have to compare if (nsContentUtils::PositionIsBefore(sheetOwner, owningNode)) { // The current sheet comes before us, and it better be the first // such, because now we break break;
}
}
LOG((" Inserting into target (doc: %d) at position %d",
target.AsNode().IsDocument(), insertionPoint));
}
/** * InsertChildSheet handles ordering of @import-ed sheet in their * parent sheets. Here we want to just insert based on order of the * @import rules that imported the sheets. In theory we can't just * append to the end because the CSSOM can insert @import rules. In * practice, we get the call to load the child sheet before the CSSOM * has finished inserting the @import rule, so we have no idea where * to put it anyway. So just append for now. (In the future if we * want to insert the sheet at the correct position, we'll need to * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in * bug 1220506.)
*/ void Loader::InsertChildSheet(StyleSheet& aSheet, StyleSheet& aParentSheet) {
LOG(("css::Loader::InsertChildSheet"));
// child sheets should always start out enabled, even if they got // cloned off of top-level sheets which were disabled
aSheet.SetEnabled(true);
aParentSheet.AppendStyleSheet(aSheet);
LOG((" Inserting into parent sheet"));
}
nsresult Loader::NewStyleSheetChannel(SheetLoadData& aLoadData,
CORSMode aCorsMode,
UsePreload aUsePreload,
UseLoadGroup aUseLoadGroup,
nsIChannel** aOutChannel) {
nsCOMPtr<nsILoadGroup> loadGroup;
nsCOMPtr<nsICookieJarSettings> cookieJarSettings; if (aUseLoadGroup == UseLoadGroup::Yes && mDocument) {
loadGroup = mDocument->GetDocumentLoadGroup(); // load for a document with no loadgrup indicates that something is // completely bogus, let's bail out early. if (!loadGroup) {
LOG_ERROR((" Failed to query loadGroup from document")); return NS_ERROR_UNEXPECTED;
}
// Note that we are calling NS_NewChannelWithTriggeringPrincipal() with both // a node and a principal. // This is because of a case where the node is the document being styled and // the principal is the stylesheet (perhaps from a different origin) that is // applying the styles. if (requestingNode) { return NS_NewChannelWithTriggeringPrincipal(
aOutChannel, aLoadData.mURI, requestingNode, triggeringPrincipal,
securityFlags, contentPolicyType, /* aPerformanceStorage = */ nullptr, loadGroup);
}
if (aUsePreload == UsePreload::Yes) { auto result = URLPreloader::ReadURI(aLoadData.mURI); if (result.isOk()) {
nsCOMPtr<nsIInputStream> stream;
MOZ_TRY(
NS_NewCStringInputStream(getter_AddRefs(stream), result.unwrap()));
// Create a StreamLoader instance to which we will feed // the data from the sync load. Do this before creating the // channel to make error recovery simpler. auto streamLoader = MakeRefPtr<StreamLoader>(aLoadData);
if (mDocument) {
net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, mDocument);
}
// Synchronous loads should only be used internally. Therefore no CORS // policy is needed.
nsCOMPtr<nsIChannel> channel;
nsresult rv =
NewStyleSheetChannel(aLoadData, CORSMode::CORS_NONE, UsePreload::Yes,
UseLoadGroup::No, getter_AddRefs(channel)); if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to create channel"));
streamLoader->ChannelOpenFailed(rv);
SheetComplete(aLoadData, rv); return rv;
}
if (NS_FAILED(rv)) {
LOG_ERROR((" Failed to open URI synchronously"));
streamLoader->ChannelOpenFailed(rv);
channel->SetNotificationCallbacks(nullptr);
SheetComplete(aLoadData, rv); return rv;
}
// Force UA sheets to be UTF-8. // XXX this is only necessary because the default in // SheetLoadData::OnDetermineCharset is wrong (bug 521039).
channel->SetContentCharset("UTF-8"_ns);
// Manually feed the streamloader the contents of the stream. // This will call back into OnStreamComplete // and thence to ParseSheet. Regardless of whether this fails, // SheetComplete has been called. return nsSyncLoadService::PushSyncStreamToListener(stream.forget(),
streamLoader, channel);
}
// If we have at least one other load ongoing, then we can defer it until // all non-pending loads are done. if (aSheetState == SheetState::NeedsParser &&
aPendingLoad == PendingLoad::No && aLoadData.ShouldDefer() &&
mOngoingLoadCount > mPendingLoadCount + 1) {
LOG((" Deferring sheet load"));
++mPendingLoadCount;
mSheets->DeferLoad(aKey, aLoadData); returntrue;
} returnfalse;
}
bool Loader::MaybeCoalesceLoadAndNotifyOpen(SheetLoadData& aLoadData,
SheetState aSheetState, const SheetLoadDataHashKey& aKey, const PreloadHashKey& aPreloadKey) { bool coalescedLoad = false; auto cacheState = [&aSheetState] { switch (aSheetState) { case SheetState::Complete: return CachedSubResourceState::Complete; case SheetState::Pending: return CachedSubResourceState::Pending; case SheetState::Loading: return CachedSubResourceState::Loading; case SheetState::NeedsParser: return CachedSubResourceState::Miss;
}
MOZ_ASSERT_UNREACHABLE("wat"); return CachedSubResourceState::Miss;
}();
if ((coalescedLoad = mSheets->CoalesceLoad(aKey, aLoadData, cacheState))) { if (aSheetState == SheetState::Pending) {
++mPendingLoadCount;
} else { // TODO: why not just `IsPreload()`?
aLoadData.NotifyOpen(aPreloadKey, mDocument,
aLoadData.IsLinkRelPreloadOrEarlyHint());
}
} return coalescedLoad;
}
/** * LoadSheet handles the actual load of a sheet. If the load is * supposed to be synchronous it just opens a channel synchronously * using the given uri, wraps the resulting stream in a converter * stream and calls ParseSheet. Otherwise it tries to look for an * existing load for this URI and piggyback on it. Failing all that, * a new load is kicked off asynchronously.
*/
nsresult Loader::LoadSheet(SheetLoadData& aLoadData, SheetState aSheetState,
uint64_t aEarlyHintPreloaderId,
PendingLoad aPendingLoad) {
LOG(("css::Loader::LoadSheet"));
MOZ_ASSERT(aLoadData.mURI, "Need a URI to load");
MOZ_ASSERT(aLoadData.mSheet, "Need a sheet to load into");
MOZ_ASSERT(aSheetState != SheetState::Complete, "Why bother?");
MOZ_ASSERT(!aLoadData.mUseSystemPrincipal || aLoadData.mSyncLoad, "Shouldn't use system principal for async loads");
LOG_URI(" Load from: '%s'", aLoadData.mURI);
// If we're firing a pending load, this load is already accounted for the // first time it went through this function. if (aPendingLoad == PendingLoad::No) { if (aLoadData.BlocksLoadEvent()) {
IncrementOngoingLoadCountAndMaybeBlockOnload();
}
// We technically never defer non-top-level sheets, so this condition could // be outside the branch, but conceptually it should be here. if (aLoadData.mParentData) {
++aLoadData.mParentData->mPendingChildren;
}
}
if (!mDocument && !aLoadData.mIsNonDocumentSheet) { // No point starting the load; just release all the data and such.
LOG_WARN((" No document and not non-document sheet; pre-dropping load"));
SheetComplete(aLoadData, NS_BINDING_ABORTED); return NS_BINDING_ABORTED;
}
if (aLoadData.mSyncLoad) { return LoadSheetSyncInternal(aLoadData, aSheetState);
}
SheetLoadDataHashKey key(aLoadData);
auto preloadKey = PreloadHashKey::CreateAsStyle(aLoadData); if (mSheets) { if (MaybeDeferLoad(aLoadData, aSheetState, aPendingLoad, key)) { return NS_OK;
}
if (MaybeCoalesceLoadAndNotifyOpen(aLoadData, aSheetState, key,
preloadKey)) { // All done here; once the load completes we'll be marked complete // automatically. return NS_OK;
}
}
// Adjusting priorites is specified as implementation-defined. // See corresponding preferences in StaticPrefList.yaml for more context. const int32_t supportsPriorityDelta = [&]() { if (aLoadData.ShouldDefer()) { return FETCH_PRIORITY_ADJUSTMENT_FOR(deferred_style,
aLoadData.mFetchPriority);
} if (aLoadData.IsLinkRelPreloadOrEarlyHint()) { return FETCH_PRIORITY_ADJUSTMENT_FOR(link_preload_style,
aLoadData.mFetchPriority);
} return FETCH_PRIORITY_ADJUSTMENT_FOR(non_deferred_style,
aLoadData.mFetchPriority);
}();
// Set the initiator type if (nsCOMPtr<nsITimedChannel> timedChannel =
do_QueryInterface(httpChannel)) {
timedChannel->SetInitiatorType(aLoadData.InitiatorTypeString());
if (aLoadData.mParentData) { // This is a child sheet load. // // The resource timing of the sub-resources that a document loads // should normally be reported to the document. One exception is any // sub-resources of any cross-origin resources that are loaded. We // don't mind reporting timing data for a direct child cross-origin // resource since the resource that linked to it (and hence potentially // anything in that parent origin) is aware that the cross-origin // resources is to be loaded. However, we do not want to report // timings for any sub-resources that a cross-origin resource may load // since that obviously leaks information about what the cross-origin // resource loads, which is bad. // // In addition to checking whether we're an immediate child resource of // a cross-origin resource (by checking if mIsCrossOriginNoCORS is set // to true on our parent), we also check our parent to see whether it // itself is a sub-resource of a cross-origin resource by checking // mBlockResourceTiming. If that is set then we too are such a // sub-resource and so we set the flag on ourself too to propagate it // on down. if (aLoadData.mParentData->mIsCrossOriginNoCORS ||
aLoadData.mParentData->mBlockResourceTiming) { // Set a flag so any other stylesheet triggered by this one will // not be reported
aLoadData.mBlockResourceTiming = true;
// Mark the channel so PerformanceMainThread::AddEntry will not // report the resource.
timedChannel->SetReportResourceTiming(false);
}
}
}
}
// Now tell the channel we expect text/css data back.... We do // this before opening it, so it's only treated as a hint.
channel->SetContentType("text/css"_ns);
// We don't have to hold on to the stream loader. The ownership // model is: Necko owns the stream loader, which owns the load data, // which owns us auto streamLoader = MakeRefPtr<StreamLoader>(aLoadData); if (mDocument) {
net::PredictorLearn(aLoadData.mURI, mDocument->GetDocumentURI(),
nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, mDocument);
}
// Some cases, like inline style and UA stylesheets, need to be parsed // synchronously. The former may trigger child loads, the latter must not. if (loadData->mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
sheet->ParseSheetSync(this, aBytes, loadData);
loadData->mIsBeingParsed = false;
// This parse does not need to be synchronous. \o/ // // Note that load is already blocked from // IncrementOngoingLoadCountAndMaybeBlockOnload(), and will be unblocked from // SheetFinishedParsingAsync which will end up in NotifyObservers as needed.
sheet->ParseSheet(*this, aBytes, aLoadData)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[loadData = aLoadData](bool aDummy) {
MOZ_ASSERT(NS_IsMainThread());
loadData->get()->SheetFinishedParsingAsync();
},
[] { MOZ_CRASH("rejected parse promise"); }); return Completed::No;
}
RefPtr loadDispatcher = aData.PrepareLoadEventIfNeeded(); if (aData.mURI) {
aData.NotifyStop(aStatus); // NOTE(emilio): This needs to happen before notifying observers, as // FontFaceSet for example checks for pending sheet loads from the // StyleSheetLoaded callback. if (aData.BlocksLoadEvent()) {
DecrementOngoingLoadCountAndMaybeUnblockOnload(); if (mPendingLoadCount && mPendingLoadCount == mOngoingLoadCount) {
LOG((" No more loading sheets; starting deferred loads"));
StartDeferredLoads();
}
}
} if (!aData.mTitle.IsEmpty() && NS_SUCCEEDED(aStatus)) {
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( "Loader::NotifyObservers - Create PageStyle actor",
[doc = RefPtr{mDocument}] { // Force creating the page style actor, if available. // This will no-op if no actor with this name is registered (outside // of desktop Firefox).
nsCOMPtr<nsISupports> pageStyleActor =
do_QueryActor("PageStyle", doc);
Unused << pageStyleActor;
}));
} if (aData.mMustNotify) { if (nsCOMPtr<nsICSSLoaderObserver> observer = std::move(aData.mObserver)) {
LOG((" Notifying observer %p for data %p. deferred: %d", observer.get(),
&aData, aData.ShouldDefer()));
observer->StyleSheetLoaded(aData.mSheet, aData.ShouldDefer(), aStatus);
}
for (nsCOMPtr<nsICSSLoaderObserver> obs : mObservers.ForwardRange()) {
LOG((" Notifying global observer %p for data %p. deferred: %d",
obs.get(), &aData, aData.ShouldDefer()));
obs->StyleSheetLoaded(aData.mSheet, aData.ShouldDefer(), aStatus);
}
/** * SheetComplete is the do-it-all cleanup function. It removes the * load data from the "loading" hashtable, adds the sheet to the * "completed" hashtable, massages the XUL cache, handles siblings of * the load data (other loads for the same URI), handles unblocking * blocked parent loads as needed, and most importantly calls * NS_RELEASE on the load data to destroy the whole mess.
*/ void Loader::SheetComplete(SheetLoadData& aLoadData, nsresult aStatus) {
LOG(("css::Loader::SheetComplete, status: 0x%" PRIx32, static_cast<uint32_t>(aStatus)));
SharedStyleSheetCache::LoadCompleted(mSheets.get(), aLoadData, aStatus);
}
SheetLoadData* data = &aLoadData; do { if (!aOnlyForLoader || aOnlyForLoader == data->mLoader) {
data->mLoadFailed = true;
data->mSheet->MaybeRejectReplacePromise();
}
if (data->mParentData) {
MarkLoadTreeFailed(*data->mParentData, aOnlyForLoader);
}
data = data->mNext;
} while (data);
}
RefPtr<StyleSheet> Loader::LookupInlineSheetInCache( const nsAString& aBuffer, nsIPrincipal* aSheetPrincipal) { auto result = mInlineSheets.Lookup(aBuffer); if (!result) { return nullptr;
}
StyleSheet* sheet = result.Data(); if (NS_WARN_IF(sheet->HasModifiedRules())) { // Remove it now that we know that we're never going to use this stylesheet // again.
result.Remove(); return nullptr;
} if (NS_WARN_IF(!sheet->Principal()->Equals(aSheetPrincipal))) {
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.