mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 04:09:50 +00:00
Bug 1864817 - implement OnDataFinished for CSSLoader. r=jesup,necko-reviewers,emilio
Differential Revision: https://phabricator.services.mozilla.com/D189403
This commit is contained in:
parent
693d0e730b
commit
b72114e181
@ -8,7 +8,9 @@
|
||||
|
||||
#include "mozilla/css/Loader.h"
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/css/ErrorReporter.h"
|
||||
#include "mozilla/dom/DocGroup.h"
|
||||
#include "mozilla/dom/FetchPriority.h"
|
||||
#include "mozilla/dom/SRILogHelper.h"
|
||||
@ -22,6 +24,7 @@
|
||||
#include "mozilla/SchedulerGroup.h"
|
||||
#include "mozilla/URLPreloader.h"
|
||||
#include "nsIChildChannel.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
#include "nsITimedChannel.h"
|
||||
#include "nsICachingChannel.h"
|
||||
@ -34,7 +37,6 @@
|
||||
#include "nsICookieJarSettings.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
@ -64,6 +66,7 @@
|
||||
#include "mozilla/css/StreamLoader.h"
|
||||
#include "mozilla/SharedStyleSheetCache.h"
|
||||
#include "mozilla/StaticPrefs_layout.h"
|
||||
#include "mozilla/StaticPrefs_network.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/StaticPrefs_network.h"
|
||||
#include "mozilla/Try.h"
|
||||
@ -308,7 +311,10 @@ SheetLoadData::SheetLoadData(
|
||||
mNonce(aNonce),
|
||||
mFetchPriority{aFetchPriority},
|
||||
mGuessedEncoding(GetFallbackEncoding(*aLoader, aOwningNode, nullptr)),
|
||||
mCompatMode(aLoader->CompatMode(aPreloadKind)) {
|
||||
mCompatMode(aLoader->CompatMode(aPreloadKind)),
|
||||
mRecordErrors(
|
||||
aLoader && aLoader->GetDocument() &&
|
||||
css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())) {
|
||||
MOZ_ASSERT(!aOwningNode || dom::LinkStyle::FromNode(*aOwningNode),
|
||||
"Must implement LinkStyle");
|
||||
MOZ_ASSERT(mTriggeringPrincipal);
|
||||
@ -349,7 +355,10 @@ SheetLoadData::SheetLoadData(css::Loader* aLoader, nsIURI* aURI,
|
||||
mFetchPriority(FetchPriority::Auto),
|
||||
mGuessedEncoding(GetFallbackEncoding(
|
||||
*aLoader, nullptr, aParentData ? aParentData->mEncoding : nullptr)),
|
||||
mCompatMode(aLoader->CompatMode(mPreloadKind)) {
|
||||
mCompatMode(aLoader->CompatMode(mPreloadKind)),
|
||||
mRecordErrors(
|
||||
aLoader && aLoader->GetDocument() &&
|
||||
css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())) {
|
||||
MOZ_ASSERT(mLoader, "Must have a loader!");
|
||||
MOZ_ASSERT(mTriggeringPrincipal);
|
||||
MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
|
||||
@ -391,7 +400,10 @@ SheetLoadData::SheetLoadData(
|
||||
mFetchPriority(aFetchPriority),
|
||||
mGuessedEncoding(
|
||||
GetFallbackEncoding(*aLoader, nullptr, aPreloadEncoding)),
|
||||
mCompatMode(aLoader->CompatMode(aPreloadKind)) {
|
||||
mCompatMode(aLoader->CompatMode(aPreloadKind)),
|
||||
mRecordErrors(
|
||||
aLoader && aLoader->GetDocument() &&
|
||||
css::ErrorReporter::ShouldReportErrors(*aLoader->GetDocument())) {
|
||||
MOZ_ASSERT(mTriggeringPrincipal);
|
||||
MOZ_ASSERT(mLoader, "Must have a loader!");
|
||||
MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad,
|
||||
@ -639,43 +651,54 @@ static bool AllLoadsCanceled(const SheetLoadData& aData) {
|
||||
* 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) {
|
||||
nsresult SheetLoadData::VerifySheetReadyToParse(
|
||||
nsresult aStatus, const nsACString& aBytes1, const nsACString& aBytes2,
|
||||
nsIChannel* aChannel, nsIURI* aFinalChannelURI,
|
||||
nsIPrincipal* aChannelResultPrincipal) {
|
||||
LOG(("SheetLoadData::VerifySheetReadyToParse"));
|
||||
NS_ASSERTION(!mLoader->mSyncCallback, "Synchronous callback from necko");
|
||||
NS_ASSERTION((!NS_IsMainThread() || !mLoader->mSyncCallback),
|
||||
"Synchronous callback from necko");
|
||||
|
||||
if (AllLoadsCanceled(*this)) {
|
||||
LOG_WARN((" All loads are canceled, dropping"));
|
||||
mLoader->SheetComplete(*this, NS_BINDING_ABORTED);
|
||||
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)) {
|
||||
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());
|
||||
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);
|
||||
}
|
||||
mLoader->SheetComplete(*this, aStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!aChannel) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mLoader->SheetComplete(*this, NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -688,10 +711,8 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
|
||||
// having a chrome URI. (Whether or not chrome stylesheets come through
|
||||
// this codepath seems nondeterministic.)
|
||||
// Otherwise we want the potentially-HTTP-redirected URI.
|
||||
nsCOMPtr<nsIURI> channelURI;
|
||||
NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
|
||||
|
||||
if (!channelURI || !originalURI) {
|
||||
if (!aFinalChannelURI || !originalURI) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ERROR("Someone just violated the nsIRequest contract");
|
||||
LOG_WARN((" Channel without a URI. Bad!"));
|
||||
mLoader->SheetComplete(*this, NS_ERROR_UNEXPECTED);
|
||||
@ -705,32 +726,33 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
|
||||
if (mUseSystemPrincipal) {
|
||||
result = secMan->GetSystemPrincipal(getter_AddRefs(principal));
|
||||
} else {
|
||||
result = secMan->GetChannelResultPrincipal(aChannel,
|
||||
getter_AddRefs(principal));
|
||||
if (aChannelResultPrincipal) {
|
||||
principal = aChannelResultPrincipal;
|
||||
result = NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(result)) {
|
||||
LOG_WARN((" Couldn't get principal"));
|
||||
mLoader->SheetComplete(*this, result);
|
||||
if (NS_IsMainThread()) {
|
||||
LOG_WARN((" Couldn't get principal"));
|
||||
mLoader->SheetComplete(*this, result);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mSheet->SetPrincipal(principal);
|
||||
|
||||
if (mSheet->GetCORSMode() == CORS_NONE &&
|
||||
!mTriggeringPrincipal->Subsumes(principal)) {
|
||||
mIsCrossOriginNoCORS = true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
result = httpChannel->GetRequestSucceeded(&requestSucceeded);
|
||||
if (NS_SUCCEEDED(result) && !requestSucceeded) {
|
||||
LOG((" Load returned an error page"));
|
||||
mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
|
||||
if (NS_IsMainThread()) {
|
||||
LOG((" Load returned an error page"));
|
||||
mLoader->SheetComplete(*this, NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -753,6 +775,9 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
|
||||
contentType.IsEmpty();
|
||||
|
||||
if (!validType) {
|
||||
if (!NS_IsMainThread()) {
|
||||
return NS_OK;
|
||||
}
|
||||
const char* errorMessage;
|
||||
uint32_t errorFlag;
|
||||
bool sameOrigin = true;
|
||||
@ -772,7 +797,8 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
|
||||
}
|
||||
|
||||
AutoTArray<nsString, 2> strings;
|
||||
CopyUTF8toUTF16(channelURI->GetSpecOrDefault(), *strings.AppendElement());
|
||||
CopyUTF8toUTF16(aFinalChannelURI->GetSpecOrDefault(),
|
||||
*strings.AppendElement());
|
||||
CopyASCIItoUTF16(contentType, *strings.AppendElement());
|
||||
|
||||
nsCOMPtr<nsIURI> referrer = ReferrerInfo()->GetOriginalReferrer();
|
||||
@ -791,6 +817,13 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
|
||||
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);
|
||||
@ -815,9 +848,13 @@ nsresult SheetLoadData::VerifySheetReadyToParse(nsresult aStatus,
|
||||
}
|
||||
}
|
||||
|
||||
if (mSheet->GetCORSMode() == CORS_NONE &&
|
||||
!mTriggeringPrincipal->Subsumes(principal)) {
|
||||
mIsCrossOriginNoCORS = true;
|
||||
}
|
||||
// Enough to set the URIs on mSheet, since any sibling datas we have share
|
||||
// the same mInner as mSheet and will thus get the same URI.
|
||||
mSheet->SetURIs(channelURI, originalURI, channelURI);
|
||||
mSheet->SetURIs(aFinalChannelURI, originalURI, aFinalChannelURI);
|
||||
|
||||
ReferrerPolicy policy =
|
||||
nsContentUtils::GetReferrerPolicyFromChannel(aChannel);
|
||||
@ -1577,32 +1614,35 @@ nsresult Loader::LoadSheetAsyncInternal(SheetLoadData& aLoadData,
|
||||
/**
|
||||
* ParseSheet handles parsing the data stream.
|
||||
*/
|
||||
Loader::Completed Loader::ParseSheet(const nsACString& aBytes,
|
||||
SheetLoadData& aLoadData,
|
||||
AllowAsyncParse aAllowAsync) {
|
||||
Loader::Completed Loader::ParseSheet(
|
||||
const nsACString& aBytes, const RefPtr<SheetLoadDataHolder>& aLoadData,
|
||||
AllowAsyncParse aAllowAsync) {
|
||||
LOG(("css::Loader::ParseSheet"));
|
||||
if (aLoadData.mURI) {
|
||||
LOG_URI(" Load succeeded for URI: '%s', parsing", aLoadData.mURI);
|
||||
SheetLoadData* loadData = aLoadData->get();
|
||||
MOZ_ASSERT(loadData);
|
||||
|
||||
if (loadData->mURI) {
|
||||
LOG_URI(" Load succeeded for URI: '%s', parsing", loadData->mURI);
|
||||
}
|
||||
AUTO_PROFILER_LABEL_CATEGORY_PAIR_RELEVANT_FOR_JS(LAYOUT_CSSParsing);
|
||||
|
||||
++mParsedSheetCount;
|
||||
|
||||
aLoadData.mIsBeingParsed = true;
|
||||
loadData->mIsBeingParsed = true;
|
||||
|
||||
StyleSheet* sheet = aLoadData.mSheet;
|
||||
StyleSheet* sheet = loadData->mSheet;
|
||||
MOZ_ASSERT(sheet);
|
||||
|
||||
// Some cases, like inline style and UA stylesheets, need to be parsed
|
||||
// synchronously. The former may trigger child loads, the latter must not.
|
||||
if (aLoadData.mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
|
||||
sheet->ParseSheetSync(this, aBytes, &aLoadData);
|
||||
aLoadData.mIsBeingParsed = false;
|
||||
if (loadData->mSyncLoad || aAllowAsync == AllowAsyncParse::No) {
|
||||
sheet->ParseSheetSync(this, aBytes, loadData);
|
||||
loadData->mIsBeingParsed = false;
|
||||
|
||||
bool noPendingChildren = aLoadData.mPendingChildren == 0;
|
||||
MOZ_ASSERT_IF(aLoadData.mSyncLoad, noPendingChildren);
|
||||
bool noPendingChildren = loadData->mPendingChildren == 0;
|
||||
MOZ_ASSERT_IF(loadData->mSyncLoad, noPendingChildren);
|
||||
if (noPendingChildren) {
|
||||
SheetComplete(aLoadData, NS_OK);
|
||||
SheetComplete(*loadData, NS_OK);
|
||||
return Completed::Yes;
|
||||
}
|
||||
return Completed::No;
|
||||
@ -1616,9 +1656,9 @@ Loader::Completed Loader::ParseSheet(const nsACString& aBytes,
|
||||
sheet->ParseSheet(*this, aBytes, aLoadData)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[loadData = RefPtr<SheetLoadData>(&aLoadData)](bool aDummy) {
|
||||
[loadData = aLoadData](bool aDummy) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
loadData->SheetFinishedParsingAsync();
|
||||
loadData->get()->SheetFinishedParsingAsync();
|
||||
},
|
||||
[] { MOZ_CRASH("rejected parse promise"); });
|
||||
return Completed::No;
|
||||
@ -1842,7 +1882,10 @@ Result<Loader::LoadSheetResult, nsresult> Loader::LoadInlineStyle(
|
||||
// effects of inline stylesheets are visible immediately (aside from
|
||||
// @imports).
|
||||
NS_ConvertUTF16toUTF8 utf8(aBuffer);
|
||||
completed = ParseSheet(utf8, *data, AllowAsyncParse::No);
|
||||
RefPtr<SheetLoadDataHolder> holder(
|
||||
new nsMainThreadPtrHolder<css::SheetLoadData>(__func__, data.get(),
|
||||
true));
|
||||
completed = ParseSheet(utf8, holder, AllowAsyncParse::No);
|
||||
if (completed == Completed::Yes) {
|
||||
if (isWorthCaching) {
|
||||
mInlineSheets.InsertOrUpdate(aBuffer, std::move(sheet));
|
||||
|
@ -590,7 +590,8 @@ class Loader final {
|
||||
//
|
||||
// If this function returns Completed::Yes, then ParseSheet also called
|
||||
// SheetComplete on aLoadData.
|
||||
Completed ParseSheet(const nsACString&, SheetLoadData&, AllowAsyncParse);
|
||||
Completed ParseSheet(const nsACString&, const RefPtr<SheetLoadDataHolder>&,
|
||||
AllowAsyncParse);
|
||||
|
||||
// The load of the sheet in the load data is done, one way or another.
|
||||
// Do final cleanup.
|
||||
@ -646,7 +647,7 @@ class Loader final {
|
||||
uint32_t mPendingLoadCount = 0;
|
||||
|
||||
// The number of stylesheets that we have parsed, for testing purposes.
|
||||
uint32_t mParsedSheetCount = 0;
|
||||
Atomic<uint32_t, MemoryOrdering::Relaxed> mParsedSheetCount{0};
|
||||
|
||||
bool mEnabled = true;
|
||||
|
||||
|
@ -90,7 +90,9 @@ class SheetLoadData final
|
||||
// so aBytes1 and aBytes2 refer to those pieces.
|
||||
nsresult VerifySheetReadyToParse(nsresult aStatus, const nsACString& aBytes1,
|
||||
const nsACString& aBytes2,
|
||||
nsIChannel* aChannel);
|
||||
nsIChannel* aChannel,
|
||||
nsIURI* aFinalChannelURI,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
@ -237,6 +239,8 @@ class SheetLoadData final
|
||||
// listening for the load.
|
||||
bool mIntentionallyDropped = false;
|
||||
|
||||
const bool mRecordErrors;
|
||||
|
||||
bool ShouldDefer() const { return mWasAlternate || !mMediaMatched; }
|
||||
|
||||
RefPtr<StyleSheet> ValueForCache() const;
|
||||
|
@ -5,7 +5,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/css/StreamLoader.h"
|
||||
|
||||
#include "mozilla/StaticPrefs_network.h"
|
||||
#include "mozilla/Encoding.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
#include "nsContentUtils.h"
|
||||
@ -14,16 +14,21 @@
|
||||
#include "nsIThreadRetargetableRequest.h"
|
||||
#include "nsIStreamTransportService.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla::css {
|
||||
|
||||
StreamLoader::StreamLoader(SheetLoadData& aSheetLoadData)
|
||||
: mSheetLoadData(&aSheetLoadData), mStatus(NS_OK) {}
|
||||
: mSheetLoadData(&aSheetLoadData),
|
||||
mStatus(NS_OK),
|
||||
mMainThreadSheetLoadData(new nsMainThreadPtrHolder<SheetLoadData>(
|
||||
"StreamLoader::SheetLoadData", mSheetLoadData, false)) {}
|
||||
|
||||
StreamLoader::~StreamLoader() {
|
||||
#ifdef NIGHTLY_BUILD
|
||||
MOZ_RELEASE_ASSERT(mOnStopRequestCalled || mChannelOpenFailed);
|
||||
MOZ_RELEASE_ASSERT(mOnStopProcessingDone || mChannelOpenFailed);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -34,6 +39,7 @@ NS_IMPL_ISUPPORTS(StreamLoader, nsIStreamListener,
|
||||
NS_IMETHODIMP
|
||||
StreamLoader::OnStartRequest(nsIRequest* aRequest) {
|
||||
MOZ_ASSERT(aRequest);
|
||||
mRequest = aRequest;
|
||||
mSheetLoadData->NotifyStart(aRequest);
|
||||
|
||||
// It's kinda bad to let Web content send a number that results
|
||||
@ -52,6 +58,12 @@ StreamLoader::OnStartRequest(nsIRequest* aRequest) {
|
||||
return (mStatus = NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
}
|
||||
NS_GetFinalChannelURI(channel, getter_AddRefs(mFinalChannelURI));
|
||||
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
|
||||
// we dont return on error here as the error is handled in
|
||||
// SheetLoadData::VerifySheetReadyToParse
|
||||
Unused << secMan->GetChannelResultPrincipal(
|
||||
channel, getter_AddRefs(mChannelResultPrincipal));
|
||||
}
|
||||
if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
|
||||
nsCOMPtr<nsIEventTarget> sts =
|
||||
@ -73,6 +85,12 @@ StreamLoader::OnStartRequest(nsIRequest* aRequest) {
|
||||
return *info.mExpirationTime;
|
||||
}();
|
||||
|
||||
// we need to block block resolution of parse promise until we receive
|
||||
// OnStopRequest on Main thread. This is necessary because, parse promise
|
||||
// resolution fires OnLoad event OnLoad event must not be dispatched until
|
||||
// OnStopRequest in main thread is dispatched.
|
||||
mSheetLoadData->mSheet->BlockOrUnblockParsePromise(true);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -81,39 +99,73 @@ StreamLoader::CheckListenerChain() { return NS_OK; }
|
||||
|
||||
NS_IMETHODIMP
|
||||
StreamLoader::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
|
||||
#ifdef NIGHTLY_BUILD
|
||||
MOZ_RELEASE_ASSERT(!mOnStopRequestCalled);
|
||||
mOnStopRequestCalled = true;
|
||||
#endif
|
||||
MOZ_ASSERT_IF(!StaticPrefs::network_send_OnDataFinished_cssLoader(),
|
||||
!mOnStopProcessingDone);
|
||||
|
||||
// StreamLoader::OnStopRequest can get triggered twice for a request.
|
||||
// Once from the path
|
||||
// nsIThreadRetargetableStreamListener::OnDataFinished->StreamLoader::OnDataFinished
|
||||
// (non-main thread) and
|
||||
// once from nsIRequestObserver::OnStopRequest path (main thread). It is
|
||||
// guaranteed that we will always get
|
||||
// nsIThreadRetargetableStreamListener::OnDataFinished trigger first and this
|
||||
// is always followed by nsIRequestObserver::OnStopRequest
|
||||
|
||||
// If we are executing OnStopRequest OMT, we need to block resolution of parse
|
||||
// promise and unblock again if we are executing this in main thread.
|
||||
// Resolution of parse promise fires onLoadEvent and this should not happen
|
||||
// before main thread OnStopRequest is dispatched.
|
||||
if (NS_IsMainThread()) {
|
||||
mSheetLoadData->mSheet->BlockOrUnblockParsePromise(false);
|
||||
}
|
||||
|
||||
if (mOnStopProcessingDone) {
|
||||
return NS_OK;
|
||||
}
|
||||
mOnStopProcessingDone = true;
|
||||
|
||||
nsresult rv = mStatus;
|
||||
// Decoded data
|
||||
nsCString utf8String;
|
||||
{
|
||||
// Hold the nsStringBuffer for the bytes from the stack to ensure release
|
||||
// no matter which return branch is taken.
|
||||
nsCString bytes = std::move(mBytes);
|
||||
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
|
||||
if (NS_FAILED(mStatus)) {
|
||||
mSheetLoadData->VerifySheetReadyToParse(mStatus, ""_ns, ""_ns, channel);
|
||||
mSheetLoadData->VerifySheetReadyToParse(mStatus, ""_ns, ""_ns, channel,
|
||||
mFinalChannelURI,
|
||||
mChannelResultPrincipal);
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
// When processing OMT, we have code paths in VerifySheetReadyToParse
|
||||
// that are main-thread only. We bail on such scenarios and continue
|
||||
// processing them on main thread OnStopRequest.
|
||||
mOnStopProcessingDone = false;
|
||||
}
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
rv = mSheetLoadData->VerifySheetReadyToParse(aStatus, mBOMBytes, bytes,
|
||||
channel);
|
||||
rv = mSheetLoadData->VerifySheetReadyToParse(aStatus, mBOMBytes, mBytes,
|
||||
channel, mFinalChannelURI,
|
||||
mChannelResultPrincipal);
|
||||
if (rv != NS_OK_PARSE_SHEET) {
|
||||
if (!NS_IsMainThread()) {
|
||||
mOnStopProcessingDone = false;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
// BOM detection generally happens during the write callback, but that won't
|
||||
// have happened if fewer than three bytes were received.
|
||||
// At this point all the conditions that requires us to run on main
|
||||
// are checked in VerifySheetReadyToParse
|
||||
|
||||
// BOM detection generally happens during the write callback, but that
|
||||
// won't have happened if fewer than three bytes were received.
|
||||
if (mEncodingFromBOM.isNothing()) {
|
||||
HandleBOM();
|
||||
MOZ_ASSERT(mEncodingFromBOM.isSome());
|
||||
}
|
||||
|
||||
// Hold the nsStringBuffer for the bytes from the stack to ensure release
|
||||
// after its scope ends
|
||||
nsCString bytes = std::move(mBytes);
|
||||
// The BOM handling has happened, but we still may not have an encoding if
|
||||
// there was no BOM. Ensure we have one.
|
||||
const Encoding* encoding = mEncodingFromBOM.value();
|
||||
@ -142,9 +194,11 @@ StreamLoader::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
|
||||
// For reasons I don't understand, factoring the below lines into
|
||||
// a method on SheetLoadData resulted in a linker error. Hence,
|
||||
// accessing fields of mSheetLoadData from here.
|
||||
mSheetLoadData->mLoader->ParseSheet(utf8String, *mSheetLoadData,
|
||||
mSheetLoadData->mLoader->ParseSheet(utf8String, mMainThreadSheetLoadData,
|
||||
Loader::AllowAsyncParse::Yes);
|
||||
|
||||
mRequest = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -159,9 +213,6 @@ StreamLoader::OnDataAvailable(nsIRequest*, nsIInputStream* aInputStream,
|
||||
return aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StreamLoader::OnDataFinished(nsresult aStatus) { return NS_OK; }
|
||||
|
||||
void StreamLoader::HandleBOM() {
|
||||
MOZ_ASSERT(mEncodingFromBOM.isNothing());
|
||||
MOZ_ASSERT(mBytes.IsEmpty());
|
||||
@ -176,6 +227,15 @@ void StreamLoader::HandleBOM() {
|
||||
mBOMBytes.Truncate(bomLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StreamLoader::OnDataFinished(nsresult aResult) {
|
||||
if (StaticPrefs::network_send_OnDataFinished_cssLoader()) {
|
||||
return OnStopRequest(mRequest, aResult);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult StreamLoader::WriteSegmentFun(nsIInputStream*, void* aClosure,
|
||||
const char* aSegment, uint32_t,
|
||||
uint32_t aCount, uint32_t* aWriteCount) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIThreadRetargetableStreamListener.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/css/SheetLoadData.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
@ -52,10 +53,15 @@ class StreamLoader : public nsIThreadRetargetableStreamListener {
|
||||
// mBytes, and store all subsequent data in that buffer.
|
||||
nsCString mBytes;
|
||||
nsAutoCStringN<3> mBOMBytes;
|
||||
nsCOMPtr<nsIRequest> mRequest;
|
||||
nsCOMPtr<nsIURI> mFinalChannelURI;
|
||||
nsCOMPtr<nsIPrincipal> mChannelResultPrincipal;
|
||||
// flag to indicate that we can skip processing of data in OnStopRequest
|
||||
bool mOnStopProcessingDone{false};
|
||||
RefPtr<SheetLoadDataHolder> mMainThreadSheetLoadData;
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
bool mChannelOpenFailed = false;
|
||||
bool mOnStopRequestCalled = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include "mozilla/css/SheetLoadData.h"
|
||||
|
||||
#include "mozAutoDocUpdate.h"
|
||||
#include "SheetLoadData.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -72,6 +71,10 @@ StyleSheet::StyleSheet(const StyleSheet& aCopy, StyleSheet* aParentSheetToUse,
|
||||
State::ModifiedRulesForDevtools);
|
||||
}
|
||||
|
||||
// we could have cloned sheet which has not been parsed yet
|
||||
// reset the parsing state flag
|
||||
mState &= ~State::AsyncParseOngoing;
|
||||
|
||||
if (aCopy.mMedia) {
|
||||
// XXX This is wrong; we should be keeping @import rules and
|
||||
// sheets in sync!
|
||||
@ -740,7 +743,9 @@ already_AddRefed<dom::Promise> StyleSheet::Replace(const nsACString& aText,
|
||||
loadData->mIsBeingParsed = true;
|
||||
MOZ_ASSERT(!mReplacePromise);
|
||||
mReplacePromise = promise;
|
||||
ParseSheet(*loader, aText, *loadData)
|
||||
RefPtr<css::SheetLoadDataHolder> holder(
|
||||
new css::SheetLoadDataHolder(__func__, loadData, false));
|
||||
ParseSheet(*loader, aText, holder)
|
||||
->Then(
|
||||
target, __func__,
|
||||
[loadData] { loadData->SheetFinishedParsingAsync(); },
|
||||
@ -1159,28 +1164,20 @@ already_AddRefed<StyleSheet> StyleSheet::CreateEmptyChildSheet(
|
||||
return child.forget();
|
||||
}
|
||||
|
||||
// We disable parallel stylesheet parsing if the browser is recording CSS errors
|
||||
// (which parallel parsing can't handle).
|
||||
static bool AllowParallelParse(css::Loader& aLoader, URLExtraData* aUrlData) {
|
||||
Document* doc = aLoader.GetDocument();
|
||||
if (doc && css::ErrorReporter::ShouldReportErrors(*doc)) {
|
||||
return false;
|
||||
}
|
||||
// Otherwise we can parse in parallel.
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
|
||||
css::Loader& aLoader, const nsACString& aBytes,
|
||||
css::SheetLoadData& aLoadData) {
|
||||
const RefPtr<css::SheetLoadDataHolder>& aLoadData) {
|
||||
MOZ_ASSERT(mParsePromise.IsEmpty());
|
||||
RefPtr<StyleSheetParsePromise> p = mParsePromise.Ensure(__func__);
|
||||
if (!aLoadData.ShouldDefer()) {
|
||||
if (!aLoadData->get()->ShouldDefer()) {
|
||||
mParsePromise.SetTaskPriority(nsIRunnablePriority::PRIORITY_RENDER_BLOCKING,
|
||||
__func__);
|
||||
}
|
||||
SetURLExtraData();
|
||||
MOZ_ASSERT(!IsAsyncParseOngoing());
|
||||
mState |= State::AsyncParseOngoing;
|
||||
|
||||
MOZ_ASSERT_IF(NS_IsMainThread(), !HasParsePromiseResolutionBlocked());
|
||||
// @import rules are disallowed due to this decision:
|
||||
// https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-588352418
|
||||
// We may allow @import rules again in the future.
|
||||
@ -1191,26 +1188,26 @@ RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
|
||||
const bool shouldRecordCounters =
|
||||
aLoader.GetDocument() && aLoader.GetDocument()->GetStyleUseCounters() &&
|
||||
!urlData->ChromeRulesEnabled();
|
||||
if (!AllowParallelParse(aLoader, urlData)) {
|
||||
|
||||
if (aLoadData->get()->mRecordErrors) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
UniquePtr<StyleUseCounters> counters;
|
||||
if (shouldRecordCounters) {
|
||||
counters.reset(Servo_UseCounters_Create());
|
||||
}
|
||||
|
||||
RefPtr<StyleStylesheetContents> contents =
|
||||
Servo_StyleSheet_FromUTF8Bytes(
|
||||
&aLoader, this, &aLoadData, &aBytes, mParsingMode, urlData,
|
||||
aLoadData.mCompatMode,
|
||||
&aLoader, this, aLoadData->get(), &aBytes, mParsingMode, urlData,
|
||||
aLoadData->get()->mCompatMode,
|
||||
/* reusable_sheets = */ nullptr, counters.get(), allowImportRules,
|
||||
StyleSanitizationKind::None,
|
||||
/* sanitized_output = */ nullptr)
|
||||
.Consume();
|
||||
FinishAsyncParse(contents.forget(), std::move(counters));
|
||||
} else {
|
||||
auto holder = MakeRefPtr<css::SheetLoadDataHolder>(__func__, &aLoadData);
|
||||
Servo_StyleSheet_FromUTF8BytesAsync(holder, urlData, &aBytes, mParsingMode,
|
||||
aLoadData.mCompatMode,
|
||||
shouldRecordCounters, allowImportRules);
|
||||
Servo_StyleSheet_FromUTF8BytesAsync(
|
||||
aLoadData, urlData, &aBytes, mParsingMode,
|
||||
aLoadData->get()->mCompatMode, shouldRecordCounters, allowImportRules);
|
||||
}
|
||||
|
||||
return p;
|
||||
@ -1219,12 +1216,13 @@ RefPtr<StyleSheetParsePromise> StyleSheet::ParseSheet(
|
||||
void StyleSheet::FinishAsyncParse(
|
||||
already_AddRefed<StyleStylesheetContents> aSheetContents,
|
||||
UniquePtr<StyleUseCounters> aUseCounters) {
|
||||
mState &= ~State::AsyncParseOngoing;
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mParsePromise.IsEmpty());
|
||||
Inner().mContents = aSheetContents;
|
||||
Inner().mUseCounters = std::move(aUseCounters);
|
||||
FixUpRuleListAfterContentsChangeIfNeeded();
|
||||
mParsePromise.Resolve(true, __func__);
|
||||
MayBeResolveParsePromise();
|
||||
}
|
||||
|
||||
void StyleSheet::ParseSheetSync(
|
||||
|
@ -15,11 +15,13 @@
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/ServoBindingTypes.h"
|
||||
#include "mozilla/ServoTypes.h"
|
||||
#include "mozilla/StaticPrefs_network.h"
|
||||
#include "mozilla/StyleSheetInfo.h"
|
||||
#include "nsICSSLoaderObserver.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsStringFwd.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
class nsINode;
|
||||
@ -44,6 +46,7 @@ class Loader;
|
||||
class LoaderReusableStyleSheets;
|
||||
class Rule;
|
||||
class SheetLoadData;
|
||||
using SheetLoadDataHolder = nsMainThreadPtrHolder<SheetLoadData>;
|
||||
} // namespace css
|
||||
|
||||
namespace dom {
|
||||
@ -79,6 +82,12 @@ enum class StyleSheetState : uint8_t {
|
||||
// This flag is set during the async Replace() function to ensure
|
||||
// that the sheet is not modified until the promise is resolved.
|
||||
ModificationDisallowed = 1 << 5,
|
||||
// Flag to indicate if resolution of parse promise is blocked.
|
||||
// Parse promise resolution should be blocked if we are parsing sheet data
|
||||
// before main thread OnStopRequest is dispatched.
|
||||
ParsePromiseResolutionBlocked = 1 << 6,
|
||||
// Flag to indicate if Parse has been completed
|
||||
AsyncParseOngoing = 1 << 7
|
||||
};
|
||||
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StyleSheetState)
|
||||
@ -112,9 +121,9 @@ class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
|
||||
// SheetLoadData for this stylesheet.
|
||||
// NOTE: ParseSheet can run synchronously or asynchronously
|
||||
// based on the result of `AllowParallelParse`
|
||||
RefPtr<StyleSheetParsePromise> ParseSheet(css::Loader&,
|
||||
const nsACString& aBytes,
|
||||
css::SheetLoadData&);
|
||||
RefPtr<StyleSheetParsePromise> ParseSheet(
|
||||
css::Loader&, const nsACString& aBytes,
|
||||
const RefPtr<css::SheetLoadDataHolder>& aLoadData);
|
||||
|
||||
// Common code that needs to be called after servo finishes parsing. This is
|
||||
// shared between the parallel and sequential paths.
|
||||
@ -220,6 +229,14 @@ class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
|
||||
return bool(mState & State::ModifiedRulesForDevtools);
|
||||
}
|
||||
|
||||
bool IsAsyncParseOngoing() const {
|
||||
return bool(mState & State::AsyncParseOngoing);
|
||||
}
|
||||
|
||||
bool HasParsePromiseResolutionBlocked() const {
|
||||
return bool(mState & State::ParsePromiseResolutionBlocked);
|
||||
}
|
||||
|
||||
bool HasUniqueInner() const { return Inner().mSheets.Length() == 1; }
|
||||
|
||||
void AssertHasUniqueInner() const { MOZ_ASSERT(HasUniqueInner()); }
|
||||
@ -301,7 +318,7 @@ class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
|
||||
*/
|
||||
void SetPrincipal(nsIPrincipal* aPrincipal) {
|
||||
StyleSheetInfo& info = Inner();
|
||||
MOZ_ASSERT(!info.mPrincipalSet, "Should only set principal once");
|
||||
MOZ_ASSERT_IF(info.mPrincipalSet, info.mPrincipal == aPrincipal);
|
||||
if (aPrincipal) {
|
||||
info.mPrincipal = aPrincipal;
|
||||
#ifdef DEBUG
|
||||
@ -465,9 +482,29 @@ class StyleSheet final : public nsICSSLoaderObserver, public nsWrapperCache {
|
||||
// Rejects mReplacePromise with a NetworkError.
|
||||
void MaybeRejectReplacePromise();
|
||||
|
||||
// Resolves mParsePromise with this sheet
|
||||
void MayBeResolveParsePromise() {
|
||||
if (!IsAsyncParseOngoing() && !HasParsePromiseResolutionBlocked() &&
|
||||
!mParsePromise.IsEmpty()) {
|
||||
mParsePromise.Resolve(true, __func__);
|
||||
}
|
||||
};
|
||||
|
||||
// Gets the relevant global if exists.
|
||||
nsISupports* GetRelevantGlobal() const;
|
||||
|
||||
// Blocks/Unblocks resolution of parse promise
|
||||
void BlockOrUnblockParsePromise(bool aBlock) {
|
||||
MOZ_ASSERT_IF(aBlock, !HasParsePromiseResolutionBlocked());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (aBlock) {
|
||||
mState |= State::ParsePromiseResolutionBlocked;
|
||||
} else {
|
||||
mState &= ~State::ParsePromiseResolutionBlocked;
|
||||
MayBeResolveParsePromise();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void SetModifiedRules() {
|
||||
mState |= State::ModifiedRules | State::ModifiedRulesForDevtools;
|
||||
|
@ -12184,12 +12184,18 @@
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
# Whether we can send OnDataAvailable to content process directly.
|
||||
# Whether we can send OnDataFinished to content process directly.
|
||||
- name: network.send_OnDataFinished.nsInputStreamPump
|
||||
type: RelaxedAtomicBool
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
# Whether we can send OnDataFinished to cssLoader in content process.
|
||||
- name: network.send_OnDataFinished.cssLoader
|
||||
type: RelaxedAtomicBool
|
||||
value: @IS_EARLY_BETA_OR_EARLIER@
|
||||
mirror: always
|
||||
|
||||
# Whether we can send send OnDataFinished only after dispatching
|
||||
# all the progress events on the main thread
|
||||
- name: network.send_OnDataFinished_after_progress_updates
|
||||
|
Loading…
x
Reference in New Issue
Block a user