summaryrefslogtreecommitdiffstats
path: root/layout/style/StreamLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/StreamLoader.cpp')
-rw-r--r--layout/style/StreamLoader.cpp114
1 files changed, 93 insertions, 21 deletions
diff --git a/layout/style/StreamLoader.cpp b/layout/style/StreamLoader.cpp
index 3e8bd37d76..3c614d7d0c 100644
--- a/layout/style/StreamLoader.cpp
+++ b/layout/style/StreamLoader.cpp
@@ -5,8 +5,9 @@
* 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/glean/GleanMetrics.h"
#include "mozilla/TaskQueue.h"
#include "nsContentUtils.h"
#include "nsIChannel.h"
@@ -14,16 +15,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 +40,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 +59,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 +86,13 @@ 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 processed, for stuff like performance
+ // resource entries.
+ mSheetLoadData->mSheet->BlockParsePromise();
+
return NS_OK;
}
@@ -81,39 +101,80 @@ 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()) {
+ if (mOnDataFinishedTime) {
+ // collect telemetry for the delta between OnDataFinished and
+ // OnStopRequest
+ TimeDuration delta = (TimeStamp::Now() - mOnDataFinishedTime);
+ glean::networking::http_content_cssloader_ondatafinished_to_onstop_delay
+ .AccumulateRawDuration(delta);
+ }
+ mSheetLoadData->mSheet->UnblockParsePromise();
+ }
+
+ 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 +203,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 +222,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 +236,18 @@ void StreamLoader::HandleBOM() {
mBOMBytes.Truncate(bomLength);
}
+NS_IMETHODIMP
+StreamLoader::OnDataFinished(nsresult aResult) {
+ if (StaticPrefs::network_send_OnDataFinished_cssLoader()) {
+ MOZ_ASSERT(mOnDataFinishedTime.IsNull(),
+ "OnDataFinished should only be called once");
+ mOnDataFinishedTime = TimeStamp::Now();
+ return OnStopRequest(mRequest, aResult);
+ }
+
+ return NS_OK;
+}
+
nsresult StreamLoader::WriteSegmentFun(nsIInputStream*, void* aClosure,
const char* aSegment, uint32_t,
uint32_t aCount, uint32_t* aWriteCount) {