Bug 1411384 - nested sync XHR should throw, r=smaug

This commit is contained in:
Andrea Marchesini 2017-11-13 10:17:05 +01:00
Родитель 44cf6e38a4
Коммит 209fb327a0
4 изменённых файлов: 149 добавлений и 4 удалений

Просмотреть файл

@ -173,6 +173,18 @@ static void AddLoadFlags(nsIRequest *request, nsLoadFlags newFlags)
request->SetLoadFlags(flags);
}
// We are in a sync event loop.
#define NOT_CALLABLE_IN_SYNC_SEND \
if (mFlagSyncLooping) { \
return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT; \
}
#define NOT_CALLABLE_IN_SYNC_SEND_RV \
if (mFlagSyncLooping) { \
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT); \
return; \
}
/////////////////////////////////////////////
//
//
@ -699,6 +711,8 @@ void
XMLHttpRequestMainThread::SetResponseType(XMLHttpRequestResponseType aResponseType,
ErrorResult& aRv)
{
NOT_CALLABLE_IN_SYNC_SEND_RV
if (mState == State::loading || mState == State::done) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
return;
@ -1092,6 +1106,13 @@ XMLHttpRequestMainThread::RequestErrorSteps(const ProgressEventType aEventType,
void
XMLHttpRequestMainThread::Abort(ErrorResult& aRv)
{
NOT_CALLABLE_IN_SYNC_SEND_RV
AbortInternal(aRv);
}
void
XMLHttpRequestMainThread::AbortInternal(ErrorResult& aRv)
{
mFlagAborted = true;
@ -1189,6 +1210,8 @@ void
XMLHttpRequestMainThread::GetAllResponseHeaders(nsACString& aResponseHeaders,
ErrorResult& aRv)
{
NOT_CALLABLE_IN_SYNC_SEND_RV
aResponseHeaders.Truncate();
// If the state is UNSENT or OPENED,
@ -1254,6 +1277,8 @@ void
XMLHttpRequestMainThread::GetResponseHeader(const nsACString& header,
nsACString& _retval, ErrorResult& aRv)
{
NOT_CALLABLE_IN_SYNC_SEND_RV
_retval.SetIsVoid(true);
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
@ -1515,7 +1540,10 @@ XMLHttpRequestMainThread::Open(const nsACString& aMethod,
const nsACString& aUrl,
bool aAsync,
const nsAString& aUsername,
const nsAString& aPassword) {
const nsAString& aPassword)
{
NOT_CALLABLE_IN_SYNC_SEND
// Gecko-specific
if (!aAsync && !DontWarnAboutSyncXHR() && GetOwner() &&
GetOwner()->GetExtantDoc()) {
@ -2861,6 +2889,8 @@ XMLHttpRequestMainThread::Send(JSContext* aCx,
const Nullable<DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>& aData,
ErrorResult& aRv)
{
NOT_CALLABLE_IN_SYNC_SEND_RV
if (aData.IsNull()) {
aRv = SendInternal(nullptr);
return;
@ -3156,6 +3186,8 @@ NS_IMETHODIMP
XMLHttpRequestMainThread::SetRequestHeader(const nsACString& aName,
const nsACString& aValue)
{
NOT_CALLABLE_IN_SYNC_SEND
// Step 1
if (mState != State::opened) {
return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED;
@ -3219,6 +3251,8 @@ XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout)
void
XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
{
NOT_CALLABLE_IN_SYNC_SEND_RV
if (mFlagSynchronous && mState != State::unsent && HasOrHasHadOwner()) {
/* Timeout is not supported for synchronous requests with an owning window,
per XHR2 spec. */
@ -3314,8 +3348,12 @@ XMLHttpRequestMainThread::ReadyState() const
return 0;
}
void XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
void
XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType,
ErrorResult& aRv)
{
NOT_CALLABLE_IN_SYNC_SEND_RV
if (mState == State::loading || mState == State::done) {
ResetResponse();
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
@ -3395,6 +3433,8 @@ XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials)
void
XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
{
NOT_CALLABLE_IN_SYNC_SEND_RV
// Return error if we're already processing a request. Note that we can't use
// ReadyState() here, because it can't differentiate between "opened" and
// "sent", so we use mState directly.

Просмотреть файл

@ -352,8 +352,8 @@ public:
void
Abort()
{
ErrorResult rv;
Abort(rv);
IgnoredErrorResult rv;
AbortInternal(rv);
MOZ_ASSERT(!rv.Failed());
}
@ -563,6 +563,9 @@ protected:
void SuspendEventDispatching();
void ResumeEventDispatching();
void
AbortInternal(ErrorResult& aRv);
struct PendingEvent
{
RefPtr<DOMEventTargetHelper> mTarget;

Просмотреть файл

@ -113,3 +113,4 @@ support-files = test_XHR_timeout.js
[test_XHRResponseURL.html]
[test_XHRSendData.html]
[test_sync_xhr_document_write_with_iframe.html]
[test_nestedSyncXHR.html]

Просмотреть файл

@ -0,0 +1,101 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for sync XHR into sync XHRs</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript">
let xhr = new XMLHttpRequest();
let testCompleted = false;
let frame = document.createElement('frame');
frame.addEventListener('load', function() {
if (testCompleted) {
return;
}
try {
xhr.responseType = "blob";
ok(false, "xhr.responseType cannot be settable");
} catch(e) {
ok(true, "xhr.responseType cannot be settable");
}
try {
xhr.abort();
ok(false, "xhr.abort should throw");
} catch(e) {
ok(true, "xhr.abort should throw");
}
try {
xhr.getAllResponseHeaders();
ok(false, "xhr.getAllResponseHeaders should throw");
} catch(e) {
ok(true, "xhr.getAllResponseHeaders should throw");
}
try {
xhr.getResponseHeader("foo");
ok(false, "xhr.getResponseHeader should throw");
} catch(e) {
ok(true, "xhr.getResponseHeader should throw");
}
try {
xhr.open('POST', location, false);
ok(false, "xhr.open should throw");
} catch(e) {
ok(true, "xhr.open should throw");
}
try {
xhr.send();
ok(false, "xhr.send should throw");
} catch(e) {
ok(true, "xhr.send should throw");
}
try {
xhr.timeout = 42;
ok(false, "xhr.timeout cannot be settable");
} catch(e) {
ok(true, "xhr.timeout cannot be settable");
}
try {
xhr.withCredentials = false;
ok(false, "xhr.withCredentials cannot be settable");
} catch(e) {
ok(true, "xhr.withCredentials cannot be settable");
}
try {
xhr.overrideMimeType("wow")
ok(false, "xhr.overrideMimeType should throw");
} catch(e) {
ok(true, "xhr.overrideMimeType should throw");
}
}, { once: true });
// This test is racy because we try to check that the loading of the frame
// happens during a sync XHR. If the loading happens after, we still need to
// consider the test passed.
ok(xhr, "We have an XHR.");
document.documentElement.appendChild(frame);
xhr.open('POST', location, false);
xhr.send('X');
// Nothing can guarantee that the frame is loaded during the sync XHR.
testCompleted = true;
frame.remove();
</script>
</body>
</html>