--- a/dom/xhr/XMLHttpRequest.h
+++ b/dom/xhr/XMLHttpRequest.h
@@ -20,16 +20,26 @@ class Blob;
class DOMString;
class FormData;
class URLSearchParams;
class XMLHttpRequestUpload;
class XMLHttpRequest : public XMLHttpRequestEventTarget
{
public:
+ // XHR states are meant to mirror the XHR2 spec:
+ // https://xhr.spec.whatwg.org/#states
+ enum class State : uint8_t {
+ unsent, // object has been constructed.
+ opened, // open() has been successfully invoked.
+ headers_received, // redirects followed and response headers received.
+ loading, // response body is being received.
+ done, // data transfer concluded, whether success or error.
+ };
+
static already_AddRefed<XMLHttpRequest>
Constructor(const GlobalObject& aGlobal,
const MozXMLHttpRequestParameters& aParams,
ErrorResult& aRv);
static already_AddRefed<XMLHttpRequest>
Constructor(const GlobalObject& aGlobal, const nsAString& ignored,
ErrorResult& aRv)
@@ -44,16 +54,19 @@ public:
return Constructor(aGlobal, params, aRv);
}
IMPL_EVENT_HANDLER(readystatechange)
virtual uint16_t
ReadyState() const = 0;
+ virtual State
+ GetReadyState() const = 0;
+
virtual void
Open(const nsACString& aMethod, const nsAString& aUrl, ErrorResult& aRv) = 0;
virtual void
Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
const nsAString& aUser, const nsAString& aPassword, ErrorResult& aRv) = 0;
virtual void
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -181,17 +181,17 @@ static void AddLoadFlags(nsIRequest *req
bool
XMLHttpRequestMainThread::sDontWarnAboutSyncXHR = false;
XMLHttpRequestMainThread::XMLHttpRequestMainThread()
: mResponseBodyDecodedPos(0),
mResponseCharset(nullptr),
mResponseType(XMLHttpRequestResponseType::_empty),
mRequestObserver(nullptr),
- mState(State::unsent),
+ mState(XMLHttpRequest::State::unsent),
mStyleBackend(StyleBackendType::None),
mFlagSynchronous(false), mFlagAborted(false), mFlagParseBody(false),
mFlagSyncLooping(false), mFlagBackgroundRequest(false),
mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
mFlagTimedOut(false), mFlagDeleted(false), mFlagSend(false),
mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
mProgressSinceLastProgressEvent(false),
mRequestSentTime(0), mTimeoutMilliseconds(0),
@@ -213,18 +213,18 @@ XMLHttpRequestMainThread::XMLHttpRequest
{
mozilla::HoldJSObjects(this);
}
XMLHttpRequestMainThread::~XMLHttpRequestMainThread()
{
mFlagDeleted = true;
- if ((mState == State::opened && mFlagSend) ||
- mState == State::loading) {
+ if ((mState == XMLHttpRequest::State::opened && mFlagSend) ||
+ mState == XMLHttpRequest::State::loading) {
Abort();
}
mParseEndListener = nullptr;
MOZ_ASSERT(!mFlagSyncLooping, "we rather crash than hang");
mFlagSyncLooping = false;
@@ -430,17 +430,17 @@ XMLHttpRequestMainThread::GetResponseXML
mResponseType != XMLHttpRequestResponseType::Document) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSEXML);
return nullptr;
}
if (mWarnAboutSyncHtml) {
mWarnAboutSyncHtml = false;
LogMessage("HTMLSyncXHRWarning", GetOwner());
}
- if (mState != State::done) {
+ if (mState != XMLHttpRequest::State::done) {
return nullptr;
}
return mResponseXML;
}
/*
* This piece copied from XMLDocument, we try to get the charset
* from HTTP headers.
@@ -544,17 +544,18 @@ XMLHttpRequestMainThread::GetResponseTex
aSnapshot.Reset();
if (mResponseType != XMLHttpRequestResponseType::_empty &&
mResponseType != XMLHttpRequestResponseType::Text) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT);
return;
}
- if (mState != State::loading && mState != State::done) {
+ if (mState != XMLHttpRequest::State::loading &&
+ mState != XMLHttpRequest::State::done) {
return;
}
// Main Fetch step 18 requires to ignore body for head/connect methods.
if (mRequestMethod.EqualsLiteral("HEAD") ||
mRequestMethod.EqualsLiteral("CONNECT")) {
return;
}
@@ -575,17 +576,17 @@ XMLHttpRequestMainThread::GetResponseTex
aRv = AppendToResponseText(mResponseBody.get() + mResponseBodyDecodedPos,
mResponseBody.Length() - mResponseBodyDecodedPos);
if (aRv.Failed()) {
return;
}
mResponseBodyDecodedPos = mResponseBody.Length();
- if (mState == State::done) {
+ if (mState == XMLHttpRequest::State::done) {
// Free memory buffer which we no longer need
mResponseBody.Truncate();
mResponseBodyDecodedPos = 0;
}
mResponseText.CreateSnapshot(aSnapshot);
}
@@ -610,23 +611,25 @@ XMLHttpRequestMainThread::CreateResponse
mResultJSON = value;
return NS_OK;
}
void
XMLHttpRequestMainThread::SetResponseType(XMLHttpRequestResponseType aResponseType,
ErrorResult& aRv)
{
- if (mState == State::loading || mState == State::done) {
+ if (mState == XMLHttpRequest::State::loading ||
+ mState == XMLHttpRequest::State::done) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
return;
}
// sync request is not allowed setting responseType in window context
- if (HasOrHasHadOwner() && mState != State::unsent && mFlagSynchronous) {
+ if (HasOrHasHadOwner() &&
+ mState != XMLHttpRequest::State::unsent && mFlagSynchronous) {
LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC);
return;
}
if (mFlagSynchronous &&
aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC);
@@ -661,17 +664,17 @@ XMLHttpRequestMainThread::GetResponse(JS
}
return;
}
case XMLHttpRequestResponseType::Arraybuffer:
case XMLHttpRequestResponseType::Moz_chunked_arraybuffer:
{
if (!(mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
- mState == State::done) &&
+ mState == XMLHttpRequest::State::done) &&
!(mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer &&
mInLoadProgressEvent)) {
aResponse.setNull();
return;
}
if (!mResultArrayBuffer) {
mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
@@ -680,42 +683,42 @@ XMLHttpRequestMainThread::GetResponse(JS
return;
}
}
aResponse.setObject(*mResultArrayBuffer);
return;
}
case XMLHttpRequestResponseType::Blob:
{
- if (mState != State::done) {
+ if (mState != XMLHttpRequest::State::done) {
aResponse.setNull();
return;
}
if (!mResponseBlob) {
aResponse.setNull();
return;
}
GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse);
return;
}
case XMLHttpRequestResponseType::Document:
{
- if (!mResponseXML || mState != State::done) {
+ if (!mResponseXML || mState != XMLHttpRequest::State::done) {
aResponse.setNull();
return;
}
aRv = nsContentUtils::WrapNative(aCx, mResponseXML, aResponse);
return;
}
case XMLHttpRequestResponseType::Json:
{
- if (mState != State::done) {
+ if (mState != XMLHttpRequest::State::done) {
aResponse.setNull();
return;
}
if (mResultJSON.isUndefined()) {
aRv = CreateResponseParsedJSON(aCx);
TruncateResponseText();
if (aRv.Failed()) {
@@ -767,17 +770,18 @@ XMLHttpRequestMainThread::IsDeniedCrossS
return false;
}
void
XMLHttpRequestMainThread::GetResponseURL(nsAString& aUrl)
{
aUrl.Truncate();
- if ((mState == State::unsent || mState == State::opened) || !mChannel) {
+ if ((mState == XMLHttpRequest::State::unsent ||
+ mState == XMLHttpRequest::State::opened) || !mChannel) {
return;
}
// Make sure we don't leak responseURL information from denied cross-site
// requests.
if (IsDeniedCrossSiteCORSRequest()) {
return;
}
@@ -798,17 +802,18 @@ uint32_t
XMLHttpRequestMainThread::GetStatus(ErrorResult& aRv)
{
// Make sure we don't leak status information from denied cross-site
// requests.
if (IsDeniedCrossSiteCORSRequest()) {
return 0;
}
- if (mState == State::unsent || mState == State::opened) {
+ if (mState == XMLHttpRequest::State::unsent ||
+ mState == XMLHttpRequest::State::opened) {
return 0;
}
if (mErrorLoad != ErrorType::eOK) {
// Let's simulate the http protocol for jar/app requests:
nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
if (jarChannel) {
nsresult status;
@@ -851,17 +856,18 @@ XMLHttpRequestMainThread::GetStatusText(
if (IsDeniedCrossSiteCORSRequest()) {
return;
}
// Check the current XHR state to see if it is valid to obtain the statusText
// value. This check is to prevent the status text for redirects from being
// available before all the redirects have been followed and HTTP headers have
// been received.
- if (mState == State::unsent || mState == State::opened) {
+ if (mState == XMLHttpRequest::State::unsent ||
+ mState == XMLHttpRequest::State::opened) {
return;
}
if (mErrorLoad != ErrorType::eOK) {
return;
}
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
@@ -869,18 +875,19 @@ XMLHttpRequestMainThread::GetStatusText(
Unused << httpChannel->GetResponseStatusText(aStatusText);
} else {
aStatusText.AssignLiteral("OK");
}
}
void
XMLHttpRequestMainThread::TerminateOngoingFetch() {
- if ((mState == State::opened && mFlagSend) ||
- mState == State::headers_received || mState == State::loading) {
+ if ((mState == XMLHttpRequest::State::opened && mFlagSend) ||
+ mState == XMLHttpRequest::State::headers_received ||
+ mState == XMLHttpRequest::State::loading) {
CloseRequest();
}
}
void
XMLHttpRequestMainThread::CloseRequest()
{
mWaitingForOnStopRequest = false;
@@ -901,47 +908,47 @@ XMLHttpRequestMainThread::CloseRequestWi
ResetResponse();
// If we're in the destructor, don't risk dispatching an event.
if (mFlagDeleted) {
mFlagSyncLooping = false;
return;
}
- if (mState != State::unsent &&
- !(mState == State::opened && !mFlagSend) &&
- mState != State::done) {
- ChangeState(State::done, true);
+ if (mState != XMLHttpRequest::State::unsent &&
+ !(mState == XMLHttpRequest::State::opened && !mFlagSend) &&
+ mState != XMLHttpRequest::State::done) {
+ ChangeState(XMLHttpRequest::State::done, true);
if (!mFlagSyncLooping) {
if (mUpload && !mUploadComplete) {
mUploadComplete = true;
DispatchProgressEvent(mUpload, aType, 0, -1);
}
DispatchProgressEvent(this, aType, 0, -1);
}
}
// The ChangeState call above calls onreadystatechange handlers which
// if they load a new url will cause XMLHttpRequestMainThread::Open to clear
// the abort state bit. If this occurs we're not uninitialized (bug 361773).
if (mFlagAborted) {
- ChangeState(State::unsent, false); // IE seems to do it
+ ChangeState(XMLHttpRequest::State::unsent, false); // IE seems to do it
}
mFlagSyncLooping = false;
}
void
XMLHttpRequestMainThread::RequestErrorSteps(const ProgressEventType aEventType,
const nsresult aOptionalException,
ErrorResult& aRv)
{
// Step 1
- mState = State::done;
+ mState = XMLHttpRequest::State::done;
StopProgressEventTimer();
// Step 2
mFlagSend = false;
// Step 3
ResetResponse();
@@ -983,25 +990,25 @@ void
XMLHttpRequestMainThread::Abort(ErrorResult& aRv)
{
mFlagAborted = true;
// Step 1
TerminateOngoingFetch();
// Step 2
- if ((mState == State::opened && mFlagSend) ||
- mState == State::headers_received ||
- mState == State::loading) {
+ if ((mState == XMLHttpRequest::State::opened && mFlagSend) ||
+ mState == XMLHttpRequest::State::headers_received ||
+ mState == XMLHttpRequest::State::loading) {
RequestErrorSteps(ProgressEventType::abort, NS_OK, aRv);
}
// Step 3
- if (mState == State::done) {
- ChangeState(State::unsent, false); // no ReadystateChange event
+ if (mState == XMLHttpRequest::State::done) {
+ ChangeState(XMLHttpRequest::State::unsent, false); // no ReadystateChange event
}
mFlagSyncLooping = false;
}
/*Method that checks if it is safe to expose a header value to the client.
It is used to check what headers are exposed for CORS requests.*/
bool
@@ -1062,17 +1069,18 @@ XMLHttpRequestMainThread::IsSafeHeader(c
void
XMLHttpRequestMainThread::GetAllResponseHeaders(nsACString& aResponseHeaders,
ErrorResult& aRv)
{
aResponseHeaders.Truncate();
// If the state is UNSENT or OPENED,
// return the empty string and terminate these steps.
- if (mState == State::unsent || mState == State::opened) {
+ if (mState == XMLHttpRequest::State::unsent ||
+ mState == XMLHttpRequest::State::opened) {
return;
}
if (mErrorLoad != ErrorType::eOK) {
return;
}
if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
@@ -1121,17 +1129,18 @@ XMLHttpRequestMainThread::GetResponseHea
{
_retval.SetIsVoid(true);
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
if (!httpChannel) {
// If the state is UNSENT or OPENED,
// return null and terminate these steps.
- if (mState == State::unsent || mState == State::opened) {
+ if (mState == XMLHttpRequest::State::unsent ||
+ mState == XMLHttpRequest::State::opened) {
return;
}
// Even non-http channels supply content type and content length.
// Remember we don't leak header information from denied cross-site
// requests.
nsresult status;
if (!mChannel ||
@@ -1198,17 +1207,17 @@ XMLHttpRequestMainThread::GetLoadGroup()
}
return nullptr;
}
nsresult
XMLHttpRequestMainThread::FireReadystatechangeEvent()
{
- MOZ_ASSERT(mState != State::unsent);
+ MOZ_ASSERT(mState != XMLHttpRequest::State::unsent);
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
event->InitEvent(kLiteralString_readystatechange, false, false);
// We assume anyone who managed to call CreateReadystatechangeEvent is trusted
event->SetTrusted(true);
DispatchOrStoreEvent(this, event);
return NS_OK;
}
@@ -1332,17 +1341,17 @@ XMLHttpRequestMainThread::IsSystemXHR()
{
return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
}
bool
XMLHttpRequestMainThread::InUploadPhase() const
{
// We're in the upload phase while our state is State::opened.
- return mState == State::opened;
+ return mState == XMLHttpRequest::State::opened;
}
// This case is hit when the async parameter is outright omitted, which
// should set it to true (and the username and password to null).
void
XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsAString& aUrl,
ErrorResult& aRv)
{
@@ -1468,28 +1477,28 @@ XMLHttpRequestMainThread::Open(const nsA
// Per spec we should only create the channel on send(), but we have internal
// code that relies on the channel being created now, and that code is not
// always IsSystemXHR(). However, we're not supposed to throw channel-creation
// errors during open(), so we silently ignore those here.
CreateChannel();
// Step 12
- if (mState != State::opened) {
- mState = State::opened;
+ if (mState != XMLHttpRequest::State::opened) {
+ mState = XMLHttpRequest::State::opened;
FireReadystatechangeEvent();
}
return NS_OK;
}
void
XMLHttpRequestMainThread::SetOriginAttributes(const OriginAttributesDictionary& aAttrs)
{
- MOZ_ASSERT((mState == State::opened) && !mFlagSend);
+ MOZ_ASSERT((mState == XMLHttpRequest::State::opened) && !mFlagSend);
OriginAttributes attrs(aAttrs);
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
MOZ_ASSERT(loadInfo);
if (loadInfo) {
loadInfo->SetOriginAttributes(attrs);
}
@@ -1684,17 +1693,17 @@ private:
NS_IMPL_ISUPPORTS0(FileCreationHandler)
} // namespace
void
XMLHttpRequestMainThread::LocalFileToBlobCompleted(Blob* aBlob)
{
- MOZ_ASSERT(mState != State::done);
+ MOZ_ASSERT(mState != XMLHttpRequest::State::done);
mResponseBlob = aBlob;
mBlobStorage = nullptr;
NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
ChangeStateToDone();
}
@@ -1727,33 +1736,33 @@ XMLHttpRequestMainThread::OnDataAvailabl
// The nsIStreamListener contract mandates us to read from the stream
// before returning.
uint32_t totalRead;
rv =
inStr->ReadSegments(DummyStreamReaderFunc, nullptr, count, &totalRead);
NS_ENSURE_SUCCESS(rv, rv);
- ChangeState(State::loading);
+ ChangeState(XMLHttpRequest::State::loading);
// Cancel() must be called with an error. We use
// NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
// just because we can retrieve the File from the channel directly.
return request->Cancel(NS_ERROR_FILE_ALREADY_EXISTS);
}
}
uint32_t totalRead;
rv = inStr->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc,
(void*)this, count, &totalRead);
NS_ENSURE_SUCCESS(rv, rv);
// Fire the first progress event/loading state change
- if (mState == State::headers_received) {
- ChangeState(State::loading);
+ if (mState == XMLHttpRequest::State::headers_received) {
+ ChangeState(XMLHttpRequest::State::loading);
if (!mFlagSynchronous) {
DispatchProgressEvent(this, ProgressEventType::progress,
mLoadTransferred, mLoadTotal);
}
mProgressSinceLastProgressEvent = false;
}
if (!mFlagSynchronous && !mProgressTimerIsActive) {
@@ -1775,17 +1784,17 @@ XMLHttpRequestMainThread::OnStartRequest
}
if (request != mChannel) {
// Can this still happen?
return NS_OK;
}
// Don't do anything if we have been aborted
- if (mState == State::unsent) {
+ if (mState == XMLHttpRequest::State::unsent) {
return NS_OK;
}
/* Apparently, Abort() should set State::unsent. See bug 361773.
XHR2 spec says this is correct. */
if (mFlagAborted) {
NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
@@ -1821,17 +1830,17 @@ XMLHttpRequestMainThread::OnStartRequest
mUploadComplete = true;
DispatchProgressEvent(mUpload, ProgressEventType::load,
mUploadTotal, mUploadTotal);
}
mContext = ctxt;
mFlagParseBody = true;
- ChangeState(State::headers_received);
+ ChangeState(XMLHttpRequest::State::headers_received);
ResetResponse();
if (!mOverrideMimeType.IsEmpty()) {
channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
}
// Fallback to 'application/octet-stream'
@@ -2052,17 +2061,17 @@ XMLHttpRequestMainThread::OnStopRequest(
NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
mFirstStartRequestSeen = false;
mRequestObserver->OnStopRequest(request, ctxt, status);
}
// make sure to notify the listener if we were aborted
// XXX in fact, why don't we do the cleanup below in this case??
// State::unsent is for abort calls. See OnStartRequest above.
- if (mState == State::unsent || mFlagTimedOut) {
+ if (mState == XMLHttpRequest::State::unsent || mFlagTimedOut) {
if (mXMLParserStreamListener)
(void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
return NS_OK;
}
// Is this good enough here?
if (mXMLParserStreamListener && mFlagParseBody) {
mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
@@ -2161,17 +2170,18 @@ XMLHttpRequestMainThread::OnStopRequest(
mErrorLoad = ErrorType::eUnreachable;
mResponseXML = nullptr;
}
// If we're uninitialized at this point, we encountered an error
// earlier and listeners have already been notified. Also we do
// not want to do this if we already completed.
- if (mState == State::unsent || mState == State::done) {
+ if (mState == XMLHttpRequest::State::unsent ||
+ mState == XMLHttpRequest::State::done) {
return NS_OK;
}
if (!mResponseXML) {
mFlagParseBody = false;
//We postpone the 'done' until the creation of the Blob is completed.
if (!waitingForBlobCreation) {
@@ -2247,17 +2257,17 @@ XMLHttpRequestMainThread::ChangeStateToD
if (!mFlagSynchronous &&
(!mLoadTransferred || mProgressSinceLastProgressEvent)) {
DispatchProgressEvent(this, ProgressEventType::progress,
mLoadTransferred, mLoadTotal);
mProgressSinceLastProgressEvent = false;
}
// Per spec, fire readystatechange=4/done before final error events.
- ChangeState(State::done, true);
+ ChangeState(XMLHttpRequest::State::done, true);
// Per spec, if we failed in the upload phase, fire a final error
// and loadend events for the upload after readystatechange=4/done.
if (!mFlagSynchronous && mUpload && !mUploadComplete) {
DispatchProgressEvent(mUpload, ProgressEventType::error, 0, -1);
}
// Per spec, fire download's load/error and loadend events after
@@ -2626,17 +2636,17 @@ XMLHttpRequestMainThread::InitiateFetch(
// ref to us to be extra safe.
mChannel->SetNotificationCallbacks(mNotificationCallbacks);
mChannel = nullptr;
mErrorLoad = ErrorType::eChannelOpen;
// Per spec, we throw on sync errors, but not async.
if (mFlagSynchronous) {
- mState = State::done;
+ mState = XMLHttpRequest::State::done;
return NS_ERROR_DOM_NETWORK_ERR;
}
}
return NS_OK;
}
void
@@ -2714,17 +2724,17 @@ XMLHttpRequestMainThread::Send(JSContext
nsresult
XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase* aBody)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
// Step 1
- if (mState != State::opened) {
+ if (mState != XMLHttpRequest::State::opened) {
return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED;
}
// Step 2
if (mFlagSend) {
return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
}
@@ -2887,17 +2897,17 @@ XMLHttpRequestMainThread::SendInternal(c
DispatchProgressEvent(mUpload, ProgressEventType::loadstart,
0, mUploadTotal);
}
}
if (!mChannel) {
// Per spec, silently fail on async request failures; throw for sync.
if (mFlagSynchronous) {
- mState = State::done;
+ mState = XMLHttpRequest::State::done;
return NS_ERROR_DOM_NETWORK_ERR;
} else {
// Defer the actual sending of async events just in case listeners
// are attached after the send() method is called.
return DispatchToMainThread(NewRunnableMethod<ProgressEventType>(
"dom::XMLHttpRequestMainThread::CloseRequestWithError",
this,
&XMLHttpRequestMainThread::CloseRequestWithError,
@@ -2944,17 +2954,17 @@ XMLHttpRequestMainThread::IsLowercaseRes
// http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
void
XMLHttpRequestMainThread::SetRequestHeader(const nsACString& aName,
const nsACString& aValue,
ErrorResult& aRv)
{
// Step 1
- if (mState != State::opened) {
+ if (mState != XMLHttpRequest::State::opened) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED);
return;
}
// Step 2
if (mFlagSend) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING);
return;
@@ -2992,17 +3002,18 @@ XMLHttpRequestMainThread::SetRequestHead
} else {
mAuthorRequestHeaders.MergeOrSet(aName, value);
}
}
void
XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
{
- if (mFlagSynchronous && mState != State::unsent && HasOrHasHadOwner()) {
+ if (mFlagSynchronous && mState != XMLHttpRequest::State::unsent &&
+ HasOrHasHadOwner()) {
/* Timeout is not supported for synchronous requests with an owning window,
per XHR2 spec. */
LogMessage("TimeoutSyncXHRWarning", GetOwner());
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC);
return;
}
mTimeoutMilliseconds = aTimeout;
@@ -3033,17 +3044,17 @@ XMLHttpRequestMainThread::DispatchToMain
return NS_DispatchToMainThread(Move(aRunnable));
}
void
XMLHttpRequestMainThread::StartTimeoutTimer()
{
MOZ_ASSERT(mRequestSentTime,
"StartTimeoutTimer mustn't be called before the request was sent!");
- if (mState == State::done) {
+ if (mState == XMLHttpRequest::State::done) {
// do nothing!
return;
}
if (mTimeoutTimer) {
mTimeoutTimer->Cancel();
}
@@ -3059,25 +3070,32 @@ XMLHttpRequestMainThread::StartTimeoutTi
(uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
mTimeoutTimer->InitWithCallback(
this,
mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
nsITimer::TYPE_ONE_SHOT
);
}
+XMLHttpRequest::State
+XMLHttpRequestMainThread::GetReadyState() const
+{
+ return mState;
+}
+
uint16_t
XMLHttpRequestMainThread::ReadyState() const
{
return static_cast<uint16_t>(mState);
}
void XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
{
- if (mState == State::loading || mState == State::done) {
+ if (mState == XMLHttpRequest::State::loading ||
+ mState == XMLHttpRequest::State::done) {
ResetResponse();
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
return;
}
mOverrideMimeType = aMimeType;
}
@@ -3089,17 +3107,17 @@ XMLHttpRequestMainThread::MozBackgroundR
nsresult
XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest)
{
if (!IsSystemXHR()) {
return NS_ERROR_DOM_SECURITY_ERR;
}
- if (mState != State::unsent) {
+ if (mState != XMLHttpRequest::State::unsent) {
// Can't change this while we're in the middle of something.
return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
}
mFlagBackgroundRequest = aMozBackgroundRequest;
return NS_OK;
}
@@ -3120,39 +3138,41 @@ XMLHttpRequestMainThread::WithCredential
void
XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
{
// 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.
- if ((mState != State::unsent && mState != State::opened) ||
+ if ((mState != XMLHttpRequest::State::unsent &&
+ mState != XMLHttpRequest::State::opened) ||
mFlagSend || mIsAnon) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING);
return;
}
mFlagACwithCredentials = aWithCredentials;
}
nsresult
XMLHttpRequestMainThread::ChangeState(State aState, bool aBroadcast)
{
mState = aState;
nsresult rv = NS_OK;
- if (aState != State::headers_received && aState != State::loading) {
+ if (aState != XMLHttpRequest::State::headers_received &&
+ aState != XMLHttpRequest::State::loading) {
StopProgressEventTimer();
}
if (aBroadcast && (!mFlagSynchronous ||
- aState == State::opened ||
- aState == State::done)) {
+ aState == XMLHttpRequest::State::opened ||
+ aState == XMLHttpRequest::State::done)) {
rv = FireReadystatechangeEvent();
}
return rv;
}
/////////////////////////////////////////////////////
// nsIChannelEventSink methods:
@@ -3375,17 +3395,17 @@ bool
XMLHttpRequestMainThread::MozSystem() const
{
return IsSystemXHR();
}
void
XMLHttpRequestMainThread::HandleTimeoutCallback()
{
- if (mState == State::done) {
+ if (mState == XMLHttpRequest::State::done) {
NS_NOTREACHED("XMLHttpRequestMainThread::HandleTimeoutCallback with completed request");
// do nothing!
return;
}
mFlagTimedOut = true;
CloseRequestWithError(ProgressEventType::timeout);
}
@@ -3617,17 +3637,17 @@ void
XMLHttpRequestMainThread::BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
Blob* aBlob, nsresult aRv)
{
// Ok, the state is changed...
if (mBlobStorage != aBlobStorage || NS_FAILED(aRv)) {
return;
}
- MOZ_ASSERT(mState != State::done);
+ MOZ_ASSERT(mState != XMLHttpRequest::State::done);
mResponseBlob = aBlob;
mBlobStorage = nullptr;
ChangeStateToDone();
}
NS_IMETHODIMP
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -246,16 +246,17 @@ public:
// nsISizeOfEventTarget
virtual size_t
SizeOfEventTargetIncludingThis(MallocSizeOf aMallocSizeOf) const override;
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(XMLHttpRequestEventTarget)
// states
virtual uint16_t ReadyState() const override;
+ virtual State GetReadyState() const override;
// request
nsresult CreateChannel();
nsresult InitiateFetch(nsIInputStream* aUploadStream,
int64_t aUploadLength,
nsACString& aUploadContentType);
virtual void
@@ -495,26 +496,16 @@ public:
void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
Blob* aBlob,
nsresult aResult) override;
void
LocalFileToBlobCompleted(Blob* aBlob);
protected:
- // XHR states are meant to mirror the XHR2 spec:
- // https://xhr.spec.whatwg.org/#states
- enum class State : uint8_t {
- unsent, // object has been constructed.
- opened, // open() has been successfully invoked.
- headers_received, // redirects followed and response headers received.
- loading, // response body is being received.
- done, // data transfer concluded, whether success or error.
- };
-
nsresult DetectCharset();
nsresult AppendToResponseText(const char * aBuffer, uint32_t aBufferLen);
static nsresult StreamReaderFunc(nsIInputStream* in,
void* closure,
const char* fromRawSegment,
uint32_t toOffset,
uint32_t count,
uint32_t *writeCount);
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -482,17 +482,17 @@ class EventRunnable final : public MainT
JS::Heap<JS::Value> mResponse;
XMLHttpRequestStringSnapshot mResponseText;
nsString mResponseURL;
nsCString mStatusText;
uint64_t mLoaded;
uint64_t mTotal;
uint32_t mEventStreamId;
uint32_t mStatus;
- uint16_t mReadyState;
+ XMLHttpRequest::State mReadyState;
bool mUploadEvent;
bool mProgressEvent;
bool mLengthComputable;
bool mUseCachedArrayBufferResponse;
nsresult mResponseTextResult;
nsresult mStatusResult;
nsresult mResponseResult;
// mScopeObj is used in PreDispatch only. We init it in our constructor, and
@@ -504,30 +504,32 @@ public:
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal,
JS::Handle<JSObject*> aScopeObj)
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
StructuredCloneHolder(CloningSupported, TransferringNotSupported,
StructuredCloneScope::SameProcessDifferentThread),
mType(aType), mResponse(JS::UndefinedValue()), mLoaded(aLoaded),
mTotal(aTotal), mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0),
- mReadyState(0), mUploadEvent(aUploadEvent), mProgressEvent(true),
- mLengthComputable(aLengthComputable), mUseCachedArrayBufferResponse(false),
- mResponseTextResult(NS_OK), mStatusResult(NS_OK), mResponseResult(NS_OK),
+ mReadyState(XMLHttpRequest::State::unsent), mUploadEvent(aUploadEvent),
+ mProgressEvent(true), mLengthComputable(aLengthComputable),
+ mUseCachedArrayBufferResponse(false), mResponseTextResult(NS_OK),
+ mStatusResult(NS_OK), mResponseResult(NS_OK),
mScopeObj(RootingCx(), aScopeObj)
{ }
EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
JS::Handle<JSObject*> aScopeObj)
: MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
StructuredCloneHolder(CloningSupported, TransferringNotSupported,
StructuredCloneScope::SameProcessDifferentThread),
mType(aType), mResponse(JS::UndefinedValue()), mLoaded(0), mTotal(0),
- mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
- mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
+ mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0),
+ mReadyState(XMLHttpRequest::State::unsent), mUploadEvent(aUploadEvent),
+ mProgressEvent(false), mLengthComputable(0),
mUseCachedArrayBufferResponse(false), mResponseTextResult(NS_OK),
mStatusResult(NS_OK), mResponseResult(NS_OK),
mScopeObj(RootingCx(), aScopeObj)
{ }
private:
~EventRunnable()
{ }
@@ -999,17 +1001,17 @@ Proxy::HandleEvent(nsIDOMEvent* aEvent)
NS_WARNING("Failed to get target!");
return NS_ERROR_FAILURE;
}
bool isUploadTarget = mXHR != target;
ProgressEvent* progressEvent = aEvent->InternalDOMEvent()->AsProgressEvent();
if (mInOpen && type.EqualsASCII(sEventStrings[STRING_readystatechange])) {
- if (mXHR->ReadyState() == 1) {
+ if (mXHR->GetReadyState() == XMLHttpRequest::State::opened) {
mInnerEventStreamId++;
}
}
{
AutoSafeJSContext cx;
JSAutoRequest ar(cx);
@@ -1163,17 +1165,17 @@ EventRunnable::PreDispatch(WorkerPrivate
} else {
MOZ_ASSERT(!JS_IsDetachedArrayBufferObject(obj));
JS::AutoValueArray<1> argv(cx);
argv[0].set(response);
obj = JS_NewArrayObject(cx, argv);
if (obj) {
transferable.setObject(*obj);
// Only cache the response when the readyState is DONE.
- if (xhr->ReadyState() == 4) {
+ if (xhr->GetReadyState() == XMLHttpRequest::State::done) {
mProxy->mArrayBufferResponseWasTransferred = true;
}
} else {
mResponseResult = NS_ERROR_OUT_OF_MEMORY;
doClone = false;
}
}
}
@@ -1191,17 +1193,17 @@ EventRunnable::PreDispatch(WorkerPrivate
}
mStatus = xhr->GetStatus(rv);
mStatusResult = rv.StealNSResult();
xhr->GetStatusText(mStatusText, rv);
MOZ_ASSERT(!rv.Failed());
- mReadyState = xhr->ReadyState();
+ mReadyState = xhr->GetReadyState();
xhr->GetResponseURL(mResponseURL);
return true;
}
bool
EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
@@ -1305,17 +1307,18 @@ EventRunnable::WorkerRun(JSContext* aCx,
state->mReadyState = mReadyState;
state->mResponseURL = mResponseURL;
XMLHttpRequestWorker* xhr = mProxy->mXMLHttpRequestPrivate;
xhr->UpdateState(*state.get(), mUseCachedArrayBufferResponse);
if (mType.EqualsASCII(sEventStrings[STRING_readystatechange])) {
- if (mReadyState == 4 && !mUploadEvent && !mProxy->mSeenLoadStart) {
+ if (mReadyState == XMLHttpRequest::State::done && !mUploadEvent &&
+ !mProxy->mSeenLoadStart) {
// We've already dispatched premature abort events.
return true;
}
}
if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
return true;
}
@@ -1672,21 +1675,22 @@ XMLHttpRequestWorker::MaybePin(ErrorResu
void
XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mProxy);
// Only send readystatechange event when state changed.
bool isStateChanged = false;
- if ((mStateData.mReadyState == 1 && mStateData.mFlagSend) ||
- mStateData.mReadyState == 2 ||
- mStateData.mReadyState == 3) {
+ if ((mStateData.mReadyState == XMLHttpRequest::State::opened &&
+ mStateData.mFlagSend) ||
+ mStateData.mReadyState == XMLHttpRequest::State::headers_received ||
+ mStateData.mReadyState == XMLHttpRequest::State::loading) {
isStateChanged = true;
- mStateData.mReadyState = 4;
+ mStateData.mReadyState = XMLHttpRequest::State::done;
}
if (mProxy->mSeenUploadLoadStart) {
MOZ_ASSERT(mUpload);
DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("abort"), true,
aRv);
if (aRv.Failed()) {
@@ -2178,33 +2182,34 @@ XMLHttpRequestWorker::Abort(ErrorResult&
if (!mProxy) {
return;
}
// Set our status to 0 and statusText to "" if we
// will be aborting an ongoing fetch, so the upcoming
// abort events we dispatch have the correct info.
- if ((mStateData.mReadyState == State::opened && mStateData.mFlagSend) ||
- mStateData.mReadyState == State::headers_received ||
- mStateData.mReadyState == State::loading ||
- mStateData.mReadyState == State::done) {
+ if ((mStateData.mReadyState == XMLHttpRequest::State::opened &&
+ mStateData.mFlagSend) ||
+ mStateData.mReadyState == XMLHttpRequest::State::headers_received ||
+ mStateData.mReadyState == XMLHttpRequest::State::loading ||
+ mStateData.mReadyState == XMLHttpRequest::State::done) {
mStateData.mStatus = 0;
mStateData.mStatusText.Truncate();
}
MaybeDispatchPrematureAbortEvents(aRv);
if (aRv.Failed()) {
return;
}
- if (mStateData.mReadyState == 4) {
+ if (mStateData.mReadyState == XMLHttpRequest::State::done) {
// No one did anything to us while we fired abort events, so reset our state
// to "unsent"
- mStateData.mReadyState = 0;
+ mStateData.mReadyState = XMLHttpRequest::State::unsent;
}
mProxy->mOuterEventStreamId++;
RefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
runnable->Dispatch(Terminating, aRv);
}
@@ -2274,17 +2279,17 @@ XMLHttpRequestWorker::OverrideMimeType(c
// We're supposed to throw if the state is not OPENED or HEADERS_RECEIVED. We
// can detect OPENED really easily but we can't detect HEADERS_RECEIVED in a
// non-racy way until the XHR state machine actually runs on this thread
// (bug 671047). For now we're going to let this work only if the Send()
// method has not been called, unless the send has been aborted.
if (!mProxy || (SendInProgress() &&
(mProxy->mSeenLoadStart ||
- mStateData.mReadyState > 1))) {
+ mStateData.mReadyState > XMLHttpRequest::State::opened))) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
RefPtr<OverrideMimeTypeRunnable> runnable =
new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
runnable->Dispatch(Terminating, aRv);
}
@@ -2310,17 +2315,17 @@ XMLHttpRequestWorker::SetResponseType(XM
// Open() has not been called yet. We store the responseType and we will use
// it later in Open().
mResponseType = aResponseType;
return;
}
if (SendInProgress() &&
(mProxy->mSeenLoadStart ||
- mStateData.mReadyState > 1)) {
+ mStateData.mReadyState > XMLHttpRequest::State::opened)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
RefPtr<SetResponseTypeRunnable> runnable =
new SetResponseTypeRunnable(mWorkerPrivate, mProxy, aResponseType);
runnable->Dispatch(Terminating, aRv);
if (aRv.Failed()) {
--- a/dom/xhr/XMLHttpRequestWorker.h
+++ b/dom/xhr/XMLHttpRequestWorker.h
@@ -28,25 +28,25 @@ class XMLHttpRequestWorker final : publi
{
public:
struct StateData
{
XMLHttpRequestStringSnapshot mResponseText;
nsString mResponseURL;
uint32_t mStatus;
nsCString mStatusText;
- uint16_t mReadyState;
+ State mReadyState;
bool mFlagSend;
JS::Heap<JS::Value> mResponse;
nsresult mResponseTextResult;
nsresult mStatusResult;
nsresult mResponseResult;
StateData()
- : mStatus(0), mReadyState(0), mFlagSend(false),
+ : mStatus(0), mReadyState(State::unsent), mFlagSend(false),
mResponse(JS::UndefinedValue()), mResponseTextResult(NS_OK),
mStatusResult(NS_OK), mResponseResult(NS_OK)
{ }
void trace(JSTracer* trc);
};
private:
@@ -81,16 +81,22 @@ public:
Unpin();
bool
Notify(workers::Status aStatus) override;
virtual uint16_t
ReadyState() const override
{
+ return static_cast<uint16_t>(mStateData.mReadyState);
+ }
+
+ virtual State
+ GetReadyState() const override
+ {
return mStateData.mReadyState;
}
virtual void
Open(const nsACString& aMethod, const nsAString& aUrl,
ErrorResult& aRv) override
{
Open(aMethod, aUrl, true, Optional<nsAString>(),