Bug 697151 - distinguish between SYNC and ASYNC XMLHttpRequests in nsIContentPolicy types, and have ChannelEventQueue::MaybeSuspendIfEventsAreSuppressed only suspend async ones; a=dmeehan

Original Revision: https://phabricator.services.mozilla.com/D213516

Differential Revision: https://phabricator.services.mozilla.com/D237613
This commit is contained in:
Thomas Wisniewski 2025-02-11 14:44:28 +00:00
parent f44b9c9921
commit 79e4b8e576
15 changed files with 56 additions and 42 deletions

View file

@ -121,7 +121,7 @@ inline const char* NS_CP_ContentTypeName(nsContentPolicyType contentType) {
CASE_RETURN(TYPE_INTERNAL_AUDIO);
CASE_RETURN(TYPE_INTERNAL_VIDEO);
CASE_RETURN(TYPE_INTERNAL_TRACK);
CASE_RETURN(TYPE_INTERNAL_XMLHTTPREQUEST);
CASE_RETURN(TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC);
CASE_RETURN(TYPE_INTERNAL_EVENTSOURCE);
CASE_RETURN(TYPE_INTERNAL_SERVICE_WORKER);
CASE_RETURN(TYPE_INTERNAL_SCRIPT_PRELOAD);
@ -148,6 +148,7 @@ inline const char* NS_CP_ContentTypeName(nsContentPolicyType contentType) {
CASE_RETURN(TYPE_PROXIED_WEBRTC_MEDIA);
CASE_RETURN(TYPE_WEB_IDENTITY);
CASE_RETURN(TYPE_WEB_TRANSPORT);
CASE_RETURN(TYPE_INTERNAL_XMLHTTPREQUEST_SYNC);
CASE_RETURN(TYPE_END);
case nsIContentPolicy::TYPE_INVALID:
break;

View file

@ -3718,7 +3718,8 @@ nsContentUtils::InternalContentPolicyTypeToExternal(nsContentPolicyType aType) {
case nsIContentPolicy::TYPE_INTERNAL_TRACK:
return ExtContentPolicy::TYPE_MEDIA;
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC:
case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
return ExtContentPolicy::TYPE_XMLHTTPREQUEST;

View file

@ -232,11 +232,11 @@ interface nsIContentPolicy : nsISupports
TYPE_INTERNAL_TRACK = 32,
/**
* Indicates an internal constant for an XMLHttpRequest.
* Indicates an internal constant for an asynchronous XMLHttpRequest.
*
* This will be mapped to TYPE_XMLHTTPREQUEST.
*/
TYPE_INTERNAL_XMLHTTPREQUEST = 33,
TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC = 33,
/**
* Indicates an internal constant for EventSource.
@ -434,13 +434,20 @@ interface nsIContentPolicy : nsISupports
*/
TYPE_WEB_TRANSPORT = 59,
/**
* Indicates an internal constant for a synchronous XMLHttpRequest.
*
* This will be mapped to TYPE_XMLHTTPREQUEST.
*/
TYPE_INTERNAL_XMLHTTPREQUEST_SYNC = 60,
/**
* Used to indicate the end of this list, not a content policy. If you want
* to add a new content policy type, place it before this sentinel value
* TYPE_END, have it use TYPE_END's current value, and increment TYPE_END by
* one. (TYPE_END should always have the highest numerical value.)
*/
TYPE_END = 60,
TYPE_END = 61,
/* When adding new content types, please update

View file

@ -365,7 +365,7 @@ static_assert(
nsIContentPolicy::TYPE_INTERNAL_AUDIO == 30 &&
nsIContentPolicy::TYPE_INTERNAL_VIDEO == 31 &&
nsIContentPolicy::TYPE_INTERNAL_TRACK == 32 &&
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST == 33 &&
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC == 33 &&
nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE == 34 &&
nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER == 35 &&
nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD == 36 &&
@ -391,7 +391,8 @@ static_assert(
nsIContentPolicy::TYPE_WEB_IDENTITY == 57 &&
nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE == 58 &&
nsIContentPolicy::TYPE_WEB_TRANSPORT == 59 &&
nsIContentPolicy::TYPE_END == 60,
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC == 60 &&
nsIContentPolicy::TYPE_END == 61,
"nsContentPolicyType values are as expected");
namespace {

View file

@ -305,7 +305,8 @@ RequestDestination InternalRequest::MapContentPolicyTypeToRequestDestination(
case nsIContentPolicy::TYPE_PING:
return RequestDestination::_empty;
case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC:
return RequestDestination::_empty;
case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
return RequestDestination::_empty;

View file

@ -66,7 +66,8 @@ nsCString MapInternalContentPolicyTypeToDest(nsContentPolicyType aType) {
case nsIContentPolicy::TYPE_PING:
return "empty"_ns;
case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC:
return "empty"_ns;
case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
return "empty"_ns;

View file

@ -339,7 +339,8 @@ CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType) {
case nsIContentPolicy::TYPE_BEACON:
case nsIContentPolicy::TYPE_PING:
case nsIContentPolicy::TYPE_FETCH:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC:
case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC:
case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
case nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD:
case nsIContentPolicy::TYPE_WEB_IDENTITY:

View file

@ -2591,11 +2591,13 @@ nsresult XMLHttpRequestMainThread::CreateChannel() {
// where it will be the parent document, which is not the one we want to use.
nsresult rv;
nsCOMPtr<Document> responsibleDocument = GetDocumentIfCurrent();
auto contentPolicyType =
mFlagSynchronous ? nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC
: nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC;
if (responsibleDocument &&
responsibleDocument->NodePrincipal() == mPrincipal) {
rv = NS_NewChannel(getter_AddRefs(mChannel), mRequestURL,
responsibleDocument, secFlags,
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
responsibleDocument, secFlags, contentPolicyType,
nullptr, // aPerformanceStorage
loadGroup,
nullptr, // aCallbacks
@ -2603,8 +2605,7 @@ nsresult XMLHttpRequestMainThread::CreateChannel() {
} else if (mClientInfo.isSome()) {
rv = NS_NewChannel(getter_AddRefs(mChannel), mRequestURL, mPrincipal,
mClientInfo.ref(), mController, secFlags,
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
mCookieJarSettings,
contentPolicyType, mCookieJarSettings,
mPerformanceStorage, // aPerformanceStorage
loadGroup,
nullptr, // aCallbacks
@ -2612,8 +2613,7 @@ nsresult XMLHttpRequestMainThread::CreateChannel() {
} else {
// Otherwise use the principal.
rv = NS_NewChannel(getter_AddRefs(mChannel), mRequestURL, mPrincipal,
secFlags, nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
mCookieJarSettings,
secFlags, contentPolicyType, mCookieJarSettings,
mPerformanceStorage, // aPerformanceStorage
loadGroup,
nullptr, // aCallbacks

View file

@ -36,7 +36,7 @@ nsresult txParseDocumentFromURI(const nsAString& aHref,
nsAutoSyncOperation sync(loaderDocument,
SyncOperationBehavior::eSuspendInput);
rv = nsSyncLoadService::LoadDocument(
documentURI, nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
documentURI, nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC,
loaderDocument->NodePrincipal(),
nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT, loadGroup,
loaderDocument->CookieJarSettings(), true,

View file

@ -185,7 +185,7 @@ bool ChannelEventQueue::MaybeSuspendIfEventsAreSuppressed() {
// Only suppress events for queues associated with XHRs, as these can cause
// content scripts to run.
if (mHasCheckedForXMLHttpRequest && !mForXMLHttpRequest) {
if (mHasCheckedForAsyncXMLHttpRequest && !mForAsyncXMLHttpRequest) {
return false;
}
@ -196,25 +196,24 @@ bool ChannelEventQueue::MaybeSuspendIfEventsAreSuppressed() {
}
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
// Figure out if this is for an XHR, if we haven't done so already.
if (!mHasCheckedForXMLHttpRequest) {
// Figure out if this is for an Async XHR, if we haven't done so already.
// We don't want to suspend Sync XHRs, as they'll always suspend event
// handling on the document, but we still need to process events for them.
if (!mHasCheckedForAsyncXMLHttpRequest) {
nsContentPolicyType contentType = loadInfo->InternalContentPolicyType();
mForXMLHttpRequest =
(contentType == nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST);
mHasCheckedForXMLHttpRequest = true;
mForAsyncXMLHttpRequest =
contentType == nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC;
mHasCheckedForAsyncXMLHttpRequest = true;
if (!mForXMLHttpRequest) {
if (!mForAsyncXMLHttpRequest) {
return false;
}
}
// Suspend the queue if the associated document has suppressed event handling,
// *and* it is not in the middle of a synchronous operation that might require
// XHR events to be processed (such as a synchronous XHR).
// Suspend the queue if the associated document has suppressed event handling.
RefPtr<dom::Document> document;
loadInfo->GetLoadingDocument(getter_AddRefs(document));
if (document && document->EventHandlingSuppressed() &&
!document->IsInSyncOperation()) {
if (document && document->EventHandlingSuppressed()) {
document->AddSuspendedChannelEventQueue(this);
SuspendInternal();
return true;

View file

@ -114,8 +114,8 @@ class ChannelEventQueue final {
mSuspended(false),
mForcedCount(0),
mFlushing(false),
mHasCheckedForXMLHttpRequest(false),
mForXMLHttpRequest(false),
mHasCheckedForAsyncXMLHttpRequest(false),
mForAsyncXMLHttpRequest(false),
mOwner(owner),
mMutex("ChannelEventQueue::mMutex"),
mRunningMutex("ChannelEventQueue::mRunningMutex") {}
@ -184,10 +184,11 @@ class ChannelEventQueue final {
MOZ_GUARDED_BY(mMutex);
bool mFlushing MOZ_GUARDED_BY(mMutex);
// Whether the queue is associated with an XHR. This is lazily instantiated
// the first time it is needed. These are MainThread-only.
bool mHasCheckedForXMLHttpRequest;
bool mForXMLHttpRequest;
// Whether the queue is associated with an ssync XHR.
// This is lazily instantiated the first time it is needed.
// These are MainThread-only.
bool mHasCheckedForAsyncXMLHttpRequest;
bool mForAsyncXMLHttpRequest;
// Keep ptr to avoid refcount cycle: only grab ref during flushing.
nsISupports* mOwner MOZ_GUARDED_BY(mMutex);

View file

@ -3991,7 +3991,9 @@ nsresult nsHttpChannel::OpenCacheEntryInternal(bool isHttps) {
mLoadInfo->InternalContentPolicyType() ==
nsIContentPolicy::TYPE_XMLHTTPREQUEST ||
mLoadInfo->InternalContentPolicyType() ==
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST)) {
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC ||
mLoadInfo->InternalContentPolicyType() ==
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC)) {
mCacheIdExtension.Append("FETCH");
}

View file

@ -171,7 +171,7 @@ static int FuzzingRunNetworkHttp(const uint8_t* data, size_t size) {
nsContentUtils::GetSystemPrincipal(), // loading principal
nsContentUtils::GetSystemPrincipal(), // triggering principal
nullptr, // Context
secFlags, nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
secFlags, nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC,
Maybe<mozilla::dom::ClientInfo>(),
Maybe<mozilla::dom::ServiceWorkerDescriptor>(), sandboxFlags);
@ -186,7 +186,7 @@ static int FuzzingRunNetworkHttp(const uint8_t* data, size_t size) {
} else {
rv = NS_NewChannel(getter_AddRefs(channel), url,
nsContentUtils::GetSystemPrincipal(), secFlags,
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC,
nullptr, // aCookieJarSettings
nullptr, // aPerformanceStorage
nullptr, // loadGroup

View file

@ -1,5 +1,3 @@
[send-sync-blocks-async.htm]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[XMLHttpRequest: sync requests should block events on pending async requests]
expected: FAIL

View file

@ -2054,7 +2054,7 @@ enum nsContentPolicyType {
TYPE_INTERNAL_AUDIO = 30,
TYPE_INTERNAL_VIDEO = 31,
TYPE_INTERNAL_TRACK = 32,
TYPE_INTERNAL_XMLHTTPREQUEST = 33,
TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC = 33,
TYPE_INTERNAL_EVENTSOURCE = 34,
TYPE_INTERNAL_SERVICE_WORKER = 35,
TYPE_INTERNAL_SCRIPT_PRELOAD = 36,
@ -2081,7 +2081,8 @@ enum nsContentPolicyType {
TYPE_WEB_IDENTITY = 57,
TYPE_INTERNAL_WORKER_STATIC_MODULE = 58,
TYPE_WEB_TRANSPORT = 59,
TYPE_END = 60,
TYPE_INTERNAL_XMLHTTPREQUEST_SYNC = 60,
TYPE_END = 61,
}
}