Bug 1285036 - Part 2: Split mState up into an enum and a set of bool flags. r=baku

This commit is contained in:
Thomas Wisniewski 2016-07-08 12:46:12 -04:00
Родитель c603d18df1
Коммит d6255ae33c
2 изменённых файлов: 144 добавлений и 175 удалений

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

@ -115,37 +115,6 @@ namespace {
}
// CIDs
// State
#define XML_HTTP_REQUEST_UNSENT (1 << 0) // 0 UNSENT
#define XML_HTTP_REQUEST_OPENED (1 << 1) // 1 OPENED
#define XML_HTTP_REQUEST_HEADERS_RECEIVED (1 << 2) // 2 HEADERS_RECEIVED
#define XML_HTTP_REQUEST_LOADING (1 << 3) // 3 LOADING
#define XML_HTTP_REQUEST_DONE (1 << 4) // 4 DONE
#define XML_HTTP_REQUEST_SENT (1 << 5) // Internal, corresponds to
// "OPENED and the send()
// flag is set" in spec
// terms.
// The above states are mutually exclusive, change with ChangeState() only.
// The states below can be combined.
#define XML_HTTP_REQUEST_ABORTED (1 << 7) // Internal
#define XML_HTTP_REQUEST_ASYNC (1 << 8) // Internal
#define XML_HTTP_REQUEST_PARSEBODY (1 << 9) // Internal
#define XML_HTTP_REQUEST_SYNCLOOPING (1 << 10) // Internal
#define XML_HTTP_REQUEST_BACKGROUND (1 << 13) // Internal
#define XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND (1 << 14) // Internal
#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 15) // Internal
#define XML_HTTP_REQUEST_TIMED_OUT (1 << 16) // Internal
#define XML_HTTP_REQUEST_DELETED (1 << 17) // Internal
#define XML_HTTP_REQUEST_LOADSTATES \
(XML_HTTP_REQUEST_UNSENT | \
XML_HTTP_REQUEST_OPENED | \
XML_HTTP_REQUEST_HEADERS_RECEIVED | \
XML_HTTP_REQUEST_LOADING | \
XML_HTTP_REQUEST_DONE | \
XML_HTTP_REQUEST_SENT)
#define NS_BADCERTHANDLER_CONTRACTID \
"@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
@ -191,7 +160,11 @@ XMLHttpRequestMainThread::XMLHttpRequestMainThread()
: mResponseBodyDecodedPos(0),
mResponseType(XMLHttpRequestResponseType::_empty),
mRequestObserver(nullptr),
mState(XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC),
mState(State::unsent),
mFlagAsynchronous(true), mFlagAborted(false), mFlagParseBody(false),
mFlagSyncLooping(false), mFlagBackgroundRequest(false),
mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
mFlagTimedOut(false), mFlagDeleted(false),
mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
mProgressSinceLastProgressEvent(false),
mRequestSentTime(0), mTimeoutMilliseconds(0),
@ -213,15 +186,14 @@ XMLHttpRequestMainThread::XMLHttpRequestMainThread()
XMLHttpRequestMainThread::~XMLHttpRequestMainThread()
{
mState |= XML_HTTP_REQUEST_DELETED;
mFlagDeleted = true;
if (mState & (XML_HTTP_REQUEST_SENT |
XML_HTTP_REQUEST_LOADING)) {
if (mState == State::sent || mState == State::loading) {
Abort();
}
MOZ_ASSERT(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
MOZ_ASSERT(!mFlagSyncLooping, "we rather crash than hang");
mFlagSyncLooping = false;
mResultJSON.setUndefined();
mResultArrayBuffer = nullptr;
@ -507,7 +479,7 @@ XMLHttpRequestMainThread::GetResponseXML(ErrorResult& aRv)
mWarnAboutSyncHtml = false;
LogMessage("HTMLSyncXHRWarning", GetOwner());
}
if (!(XML_HTTP_REQUEST_DONE & mState)) {
if (mState != State::done) {
return nullptr;
}
return mResponseXML;
@ -623,7 +595,7 @@ XMLHttpRequestMainThread::GetResponseText(nsAString& aResponseText,
return;
}
if (!(mState & (XML_HTTP_REQUEST_DONE | XML_HTTP_REQUEST_LOADING))) {
if (mState != State::loading && mState != State::done) {
return;
}
@ -653,7 +625,7 @@ XMLHttpRequestMainThread::GetResponseText(nsAString& aResponseText,
mResponseBodyDecodedPos = mResponseBody.Length();
if (mState & XML_HTTP_REQUEST_DONE) {
if (mState == State::done) {
// Free memory buffer which we no longer need
mResponseBody.Truncate();
mResponseBodyDecodedPos = 0;
@ -741,20 +713,19 @@ XMLHttpRequestMainThread::SetResponseType(XMLHttpRequestResponseType aResponseTy
{
// If the state is LOADING or DONE raise an INVALID_STATE_ERR exception
// and terminate these steps.
if ((mState & (XML_HTTP_REQUEST_LOADING | XML_HTTP_REQUEST_DONE))) {
if (mState == State::loading || mState == State::done) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
// sync request is not allowed setting responseType in window context
if (HasOrHasHadOwner() &&
!(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
if (HasOrHasHadOwner() && mState != State::unsent && !mFlagAsynchronous) {
LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return;
}
if (!(mState & XML_HTTP_REQUEST_ASYNC) &&
if (!mFlagAsynchronous &&
(aResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
@ -798,7 +769,7 @@ XMLHttpRequestMainThread::GetResponse(JSContext* aCx,
case XMLHttpRequestResponseType::Moz_chunked_arraybuffer:
{
if (!(mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
mState & XML_HTTP_REQUEST_DONE) &&
mState == State::done) &&
!(mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer &&
mInLoadProgressEvent)) {
aResponse.setNull();
@ -821,7 +792,7 @@ XMLHttpRequestMainThread::GetResponse(JSContext* aCx,
case XMLHttpRequestResponseType::Blob:
case XMLHttpRequestResponseType::Moz_blob:
{
if (!(mState & XML_HTTP_REQUEST_DONE)) {
if (mState != State::done) {
if (mResponseType != XMLHttpRequestResponseType::Moz_blob) {
aResponse.setNull();
return;
@ -842,7 +813,7 @@ XMLHttpRequestMainThread::GetResponse(JSContext* aCx,
}
case XMLHttpRequestResponseType::Document:
{
if (!(mState & XML_HTTP_REQUEST_DONE) || !mResponseXML) {
if (!mResponseXML || mState != State::done) {
aResponse.setNull();
return;
}
@ -852,7 +823,7 @@ XMLHttpRequestMainThread::GetResponse(JSContext* aCx,
}
case XMLHttpRequestResponseType::Json:
{
if (!(mState & XML_HTTP_REQUEST_DONE)) {
if (mState != State::done) {
aResponse.setNull();
return;
}
@ -1031,8 +1002,7 @@ XMLHttpRequestMainThread::GetStatusText(nsACString& aStatusText,
}
void
XMLHttpRequestMainThread::CloseRequestWithError(const ProgressEventType aType,
const uint32_t aFlag)
XMLHttpRequestMainThread::CloseRequestWithError(const ProgressEventType aType)
{
if (mChannel) {
mChannel->Cancel(NS_BINDING_ABORTED);
@ -1042,20 +1012,19 @@ XMLHttpRequestMainThread::CloseRequestWithError(const ProgressEventType aType,
}
uint32_t responseLength = mResponseBody.Length();
ResetResponse();
mState |= aFlag;
// If we're in the destructor, don't risk dispatching an event.
if (mState & XML_HTTP_REQUEST_DELETED) {
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
if (mFlagDeleted) {
mFlagSyncLooping = false;
return;
}
if (!(mState & (XML_HTTP_REQUEST_UNSENT |
XML_HTTP_REQUEST_OPENED |
XML_HTTP_REQUEST_DONE))) {
ChangeState(XML_HTTP_REQUEST_DONE, true);
if (mState != State::unsent &&
mState != State::opened &&
mState != State::done) {
ChangeState(State::done, true);
if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) {
if (!mFlagSyncLooping) {
DispatchProgressEvent(this, aType, mLoadLengthComputable, responseLength,
mLoadTotal);
if (mUpload && !mUploadComplete) {
@ -1069,17 +1038,18 @@ XMLHttpRequestMainThread::CloseRequestWithError(const ProgressEventType aType,
// 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 (mState & XML_HTTP_REQUEST_ABORTED) {
ChangeState(XML_HTTP_REQUEST_UNSENT, false); // IE seems to do it
if (mFlagAborted) {
ChangeState(State::unsent, false); // IE seems to do it
}
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
mFlagSyncLooping = false;
}
void
XMLHttpRequestMainThread::Abort(ErrorResult& arv)
{
CloseRequestWithError(ProgressEventType::abort, XML_HTTP_REQUEST_ABORTED);
mFlagAborted = true;
CloseRequestWithError(ProgressEventType::abort);
}
NS_IMETHODIMP
@ -1162,8 +1132,9 @@ XMLHttpRequestMainThread::GetAllResponseHeaders(nsACString& aResponseHeaders,
// If the state is UNSENT or OPENED,
// return the empty string and terminate these steps.
if (mState & (XML_HTTP_REQUEST_UNSENT |
XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
if (mState == State::unsent ||
mState == State::opened ||
mState == State::sent) {
return;
}
@ -1227,8 +1198,9 @@ XMLHttpRequestMainThread::GetResponseHeader(const nsACString& header,
if (!httpChannel) {
// If the state is UNSENT or OPENED,
// return null and terminate these steps.
if (mState & (XML_HTTP_REQUEST_UNSENT |
XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
if (mState == State::unsent ||
mState == State::opened ||
mState == State::sent) {
return;
}
@ -1285,7 +1257,7 @@ XMLHttpRequestMainThread::GetResponseHeader(const nsACString& header,
already_AddRefed<nsILoadGroup>
XMLHttpRequestMainThread::GetLoadGroup() const
{
if (mState & XML_HTTP_REQUEST_BACKGROUND) {
if (mFlagBackgroundRequest) {
return nullptr;
}
@ -1433,10 +1405,8 @@ XMLHttpRequestMainThread::Open(const nsACString& inMethod, const nsACString& url
nsCOMPtr<nsIURI> uri;
if (mState & (XML_HTTP_REQUEST_OPENED |
XML_HTTP_REQUEST_HEADERS_RECEIVED |
XML_HTTP_REQUEST_LOADING |
XML_HTTP_REQUEST_SENT)) {
if (mState == State::opened || mState == State::headers_received ||
mState == State::loading || mState == State::sent) {
// IE aborts as well
Abort();
@ -1447,14 +1417,11 @@ XMLHttpRequestMainThread::Open(const nsACString& inMethod, const nsACString& url
// why things didn't work.
}
// Unset any pre-existing aborted and timed-out states.
mState &= ~XML_HTTP_REQUEST_ABORTED & ~XML_HTTP_REQUEST_TIMED_OUT;
// Unset any pre-existing aborted and timed-out flags.
mFlagAborted = false;
mFlagTimedOut = false;
if (async) {
mState |= XML_HTTP_REQUEST_ASYNC;
} else {
mState &= ~XML_HTTP_REQUEST_ASYNC;
}
mFlagAsynchronous = async;
nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
if (!doc) {
@ -1560,7 +1527,7 @@ XMLHttpRequestMainThread::Open(const nsACString& inMethod, const nsACString& url
NS_ENSURE_SUCCESS(rv, rv);
mState &= ~XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;
mFlagHadUploadListenersOnSend = false;
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
if (httpChannel) {
@ -1574,7 +1541,7 @@ XMLHttpRequestMainThread::Open(const nsACString& inMethod, const nsACString& url
}
}
ChangeState(XML_HTTP_REQUEST_OPENED);
ChangeState(State::opened);
return NS_OK;
}
@ -1648,7 +1615,7 @@ XMLHttpRequestMainThread::StreamReaderFunc(nsIInputStream* in,
xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
}
if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
if (xmlHttpRequest->mFlagParseBody) {
// Give the same data to the parser.
// We need to wrap the data in a new lightweight stream and pass that
@ -1667,7 +1634,7 @@ XMLHttpRequestMainThread::StreamReaderFunc(nsIInputStream* in,
// No use to continue parsing if we failed here, but we
// should still finish reading the stream
if (NS_FAILED(parsingResult)) {
xmlHttpRequest->mState &= ~XML_HTTP_REQUEST_PARSEBODY;
xmlHttpRequest->mFlagParseBody = false;
}
}
}
@ -1737,13 +1704,13 @@ XMLHttpRequestMainThread::OnDataAvailable(nsIRequest *request,
return error.StealNSResult();
}
ChangeState(XML_HTTP_REQUEST_LOADING);
ChangeState(State::loading);
return request->Cancel(NS_OK);
}
mDataAvailable += totalRead;
ChangeState(XML_HTTP_REQUEST_LOADING);
ChangeState(State::loading);
MaybeDispatchProgressEvents(false);
@ -1768,19 +1735,20 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
}
// Don't do anything if we have been aborted
if (mState & XML_HTTP_REQUEST_UNSENT)
if (mState == State::unsent) {
return NS_OK;
}
/* Apparently, Abort() should set XML_HTTP_REQUEST_UNSENT. See bug 361773.
/* Apparently, Abort() should set State::unsent. See bug 361773.
XHR2 spec says this is correct. */
if (mState & XML_HTTP_REQUEST_ABORTED) {
if (mFlagAborted) {
NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
return NS_ERROR_UNEXPECTED;
}
// Don't do anything if we have timed out.
if (mState & XML_HTTP_REQUEST_TIMED_OUT) {
if (mFlagTimedOut) {
return NS_OK;
}
@ -1791,8 +1759,7 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
request->GetStatus(&status);
mErrorLoad = mErrorLoad || NS_FAILED(status);
if (mUpload && !mUploadComplete && !mErrorLoad &&
(mState & XML_HTTP_REQUEST_ASYNC)) {
if (mUpload && !mUploadComplete && !mErrorLoad && mFlagAsynchronous) {
if (mProgressTimerIsActive) {
mProgressTimerIsActive = false;
mProgressNotifier->Cancel();
@ -1809,8 +1776,8 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
}
mContext = ctxt;
mState |= XML_HTTP_REQUEST_PARSEBODY;
ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
mFlagParseBody = true;
ChangeState(State::headers_received);
ResetResponse();
@ -1897,11 +1864,11 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
// legacy users of XHR who use responseType == "" for retrieving the
// responseText of text/html resources. This legacy case is so common
// that it's not useful to emit a warning about it.
if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
if (!mFlagAsynchronous) {
// We don't make cool new features available in the bad synchronous
// mode. The synchronous mode is for legacy only.
mWarnAboutSyncHtml = true;
mState &= ~XML_HTTP_REQUEST_PARSEBODY;
mFlagParseBody = false;
} else {
mIsHtml = true;
}
@ -1911,14 +1878,14 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
// Follow https://xhr.spec.whatwg.org/
// If final MIME type is not null, text/html, text/xml, application/xml,
// or does not end in +xml, return null.
mState &= ~XML_HTTP_REQUEST_PARSEBODY;
mFlagParseBody = false;
}
} else {
// The request failed, so we shouldn't be parsing anyway
mState &= ~XML_HTTP_REQUEST_PARSEBODY;
mFlagParseBody = false;
}
if (mState & XML_HTTP_REQUEST_PARSEBODY) {
if (mFlagParseBody) {
nsCOMPtr<nsIURI> baseURI, docURI;
rv = mChannel->GetURI(getter_AddRefs(docURI));
NS_ENSURE_SUCCESS(rv, rv);
@ -1991,7 +1958,7 @@ XMLHttpRequestMainThread::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
// We won't get any progress events anyway if we didn't have progress
// events when starting the request - so maybe no need to start timer here.
if (NS_SUCCEEDED(rv) &&
(mState & XML_HTTP_REQUEST_ASYNC) &&
mFlagAsynchronous &&
HasListenersFor(nsGkAtoms::onprogress)) {
StartProgressEventTimer();
}
@ -2020,16 +1987,15 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
// make sure to notify the listener if we were aborted
// XXX in fact, why don't we do the cleanup below in this case??
// XML_HTTP_REQUEST_UNSENT is for abort calls. See OnStartRequest above.
if ((mState & XML_HTTP_REQUEST_UNSENT) ||
(mState & XML_HTTP_REQUEST_TIMED_OUT)) {
// State::unsent is for abort calls. See OnStartRequest above.
if (mState == State::unsent || mFlagTimedOut) {
if (mXMLParserStreamListener)
(void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
return NS_OK;
}
// Is this good enough here?
if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
if (mXMLParserStreamListener && mFlagParseBody) {
mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
}
@ -2093,7 +2059,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
mChannelEventSink = nullptr;
mProgressEventSink = nullptr;
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
mFlagSyncLooping = false;
if (NS_FAILED(status)) {
// This can happen if the server is unreachable. Other possible
@ -2106,8 +2072,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
// 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 & (XML_HTTP_REQUEST_UNSENT |
XML_HTTP_REQUEST_DONE)) {
if (mState == State::unsent || mState == State::done) {
return NS_OK;
}
@ -2116,7 +2081,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
return NS_OK;
}
if (mIsHtml) {
NS_ASSERTION(!(mState & XML_HTTP_REQUEST_SYNCLOOPING),
NS_ASSERTION(!mFlagSyncLooping,
"We weren't supposed to support HTML parsing with XHR!");
nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mResponseXML);
EventListenerManager* manager =
@ -2146,7 +2111,8 @@ XMLHttpRequestMainThread::ChangeStateToDone()
MaybeDispatchProgressEvents(true);
}
ChangeState(XML_HTTP_REQUEST_DONE, true);
ChangeState(State::done, true);
if (mTimeoutTimer) {
mTimeoutTimer->Cancel();
}
@ -2462,12 +2428,12 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant, const Nullable<RequestBody>
NS_ENSURE_SUCCESS(rv, rv);
// Return error if we're already processing a request
if (XML_HTTP_REQUEST_SENT & mState) {
if (mState == State::sent) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
// Make sure we've been opened
if (!mChannel || !(XML_HTTP_REQUEST_OPENED & mState)) {
if (!mChannel || mState != State::opened) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
@ -2656,8 +2622,7 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant, const Nullable<RequestBody>
ResetResponse();
if (!IsSystemXHR() && !mIsAnon &&
(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS)) {
if (!IsSystemXHR() && !mIsAnon && mFlagACwithCredentials) {
// This is quite sad. We have to create the channel in .open(), since the
// chrome-only xhr.channel API depends on that. However .withCredentials
// can be modified after, so we don't know what to set the
@ -2731,14 +2696,14 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant, const Nullable<RequestBody>
// Check if we should enabled cross-origin upload listeners.
if (mUpload && mUpload->HasListeners()) {
mState |= XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;
mFlagHadUploadListenersOnSend = true;
}
// Set up the preflight if needed
if (!IsSystemXHR()) {
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
loadInfo->SetCorsPreflightInfo(mCORSUnsafeHeaders,
mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
mFlagHadUploadListenersOnSend);
}
mIsMappedArrayBuffer = false;
@ -2788,8 +2753,8 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant, const Nullable<RequestBody>
mWaitingForOnStopRequest = true;
// If we're synchronous, spin an event loop here and wait
if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
mState |= XML_HTTP_REQUEST_SYNCLOOPING;
if (!mFlagAsynchronous) {
mFlagSyncLooping = true;
nsCOMPtr<nsIDocument> suspendedDoc;
nsCOMPtr<nsIRunnable> resumeTimeoutRunnable;
@ -2806,14 +2771,13 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant, const Nullable<RequestBody>
}
}
ChangeState(XML_HTTP_REQUEST_SENT);
ChangeState(State::sent);
{
nsAutoSyncOperation sync(suspendedDoc);
// Note, calling ChangeState may have cleared
// XML_HTTP_REQUEST_SYNCLOOPING flag.
// Note, calling ChangeState may have cleared mFlagSyncLooping
nsIThread *thread = NS_GetCurrentThread();
while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
while (mFlagSyncLooping) {
if (!NS_ProcessNextEvent(thread)) {
rv = NS_ERROR_UNEXPECTED;
break;
@ -2834,7 +2798,7 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant, const Nullable<RequestBody>
// that this needs to come after the AsyncOpen() and rv check, because this
// can run script that would try to restart this request, and that could end
// up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
ChangeState(XML_HTTP_REQUEST_SENT);
ChangeState(State::sent);
if (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress)) {
StartProgressEventTimer();
}
@ -2859,7 +2823,7 @@ XMLHttpRequestMainThread::SetRequestHeader(const nsACString& header,
const nsACString& value)
{
// Step 1 and 2
if (!(mState & XML_HTTP_REQUEST_OPENED)) {
if (mState != State::opened) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
NS_ASSERTION(mChannel, "mChannel must be valid if we're OPENED.");
@ -2968,8 +2932,7 @@ XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout)
void
XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
{
if (!(mState & (XML_HTTP_REQUEST_ASYNC | XML_HTTP_REQUEST_UNSENT)) &&
HasOrHasHadOwner()) {
if (!mFlagAsynchronous && mState != State::unsent && HasOrHasHadOwner()) {
/* Timeout is not supported for synchronous requests with an owning window,
per XHR2 spec. */
LogMessage("TimeoutSyncXHRWarning", GetOwner());
@ -2988,7 +2951,7 @@ XMLHttpRequestMainThread::StartTimeoutTimer()
{
MOZ_ASSERT(mRequestSentTime,
"StartTimeoutTimer mustn't be called before the request was sent!");
if (mState & XML_HTTP_REQUEST_DONE) {
if (mState == State::done) {
// do nothing!
return;
}
@ -3024,25 +2987,25 @@ uint16_t
XMLHttpRequestMainThread::ReadyState() const
{
// Translate some of our internal states for external consumers
if (mState & XML_HTTP_REQUEST_UNSENT) {
if (mState == State::unsent) {
return UNSENT;
}
if (mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
if (mState == State::opened || mState == State::sent) {
return OPENED;
}
if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
if (mState == State::headers_received) {
return HEADERS_RECEIVED;
}
if (mState & XML_HTTP_REQUEST_LOADING) {
if (mState == State::loading) {
return LOADING;
}
MOZ_ASSERT(mState & XML_HTTP_REQUEST_DONE);
MOZ_ASSERT(mState == State::done);
return DONE;
}
void XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
{
if ((mState & XML_HTTP_REQUEST_LOADING) || (mState & XML_HTTP_REQUEST_DONE)) {
if (mState == State::loading || mState == State::done) {
ResetResponse();
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
@ -3069,7 +3032,7 @@ XMLHttpRequestMainThread::GetMozBackgroundRequest(bool *_retval)
bool
XMLHttpRequestMainThread::MozBackgroundRequest() const
{
return !!(mState & XML_HTTP_REQUEST_BACKGROUND);
return mFlagBackgroundRequest;
}
NS_IMETHODIMP
@ -3079,16 +3042,12 @@ XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest)
return NS_ERROR_DOM_SECURITY_ERR;
}
if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
if (mState != State::unsent) {
// Can't change this while we're in the middle of something.
return NS_ERROR_IN_PROGRESS;
}
if (aMozBackgroundRequest) {
mState |= XML_HTTP_REQUEST_BACKGROUND;
} else {
mState &= ~XML_HTTP_REQUEST_BACKGROUND;
}
mFlagBackgroundRequest = aMozBackgroundRequest;
return NS_OK;
}
@ -3111,7 +3070,7 @@ XMLHttpRequestMainThread::GetWithCredentials(bool *_retval)
bool
XMLHttpRequestMainThread::WithCredentials() const
{
return !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
return mFlagACwithCredentials;
}
NS_IMETHODIMP
@ -3128,43 +3087,33 @@ XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials, ErrorResult&
// 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 & XML_HTTP_REQUEST_UNSENT) &&
!(mState & XML_HTTP_REQUEST_OPENED)) ||
mIsAnon) {
if ((mState != State::unsent && mState != State::opened) || mIsAnon) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (aWithCredentials) {
mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
} else {
mState &= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
}
mFlagACwithCredentials = aWithCredentials;
}
nsresult
XMLHttpRequestMainThread::ChangeState(uint32_t aState, bool aBroadcast)
XMLHttpRequestMainThread::ChangeState(State aState, bool aBroadcast)
{
// If we are setting one of the mutually exclusive states,
// unset those state bits first.
if (aState & XML_HTTP_REQUEST_LOADSTATES) {
mState &= ~XML_HTTP_REQUEST_LOADSTATES;
}
mState |= aState;
bool droppingFromSENTtoOPENED = mState == State::sent &&
aState == State::opened;
mState = aState;
nsresult rv = NS_OK;
if (mProgressNotifier &&
!(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
aState != State::headers_received && aState != State::loading) {
mProgressTimerIsActive = false;
mProgressNotifier->Cancel();
}
if ((aState & XML_HTTP_REQUEST_LOADSTATES) && // Broadcast load states only
aState != XML_HTTP_REQUEST_SENT && // And not internal ones
if (aState != State::sent && // SENT is internal
!droppingFromSENTtoOPENED && // since SENT is essentially OPENED
aBroadcast &&
(mState & XML_HTTP_REQUEST_ASYNC ||
aState & XML_HTTP_REQUEST_OPENED ||
aState & XML_HTTP_REQUEST_DONE)) {
(mFlagAsynchronous || aState == State::opened || aState == State::done)) {
nsCOMPtr<nsIDOMEvent> event;
rv = CreateReadystatechangeEvent(getter_AddRefs(event));
NS_ENSURE_SUCCESS(rv, rv);
@ -3256,7 +3205,7 @@ XMLHttpRequestMainThread::MaybeDispatchProgressEvents(bool aFinalProgress)
if (mProgressTimerIsActive ||
!mProgressSinceLastProgressEvent ||
mErrorLoad ||
!(mState & XML_HTTP_REQUEST_ASYNC)) {
!mFlagAsynchronous) {
return;
}
@ -3264,9 +3213,8 @@ XMLHttpRequestMainThread::MaybeDispatchProgressEvents(bool aFinalProgress)
StartProgressEventTimer();
}
// We're uploading if our state is XML_HTTP_REQUEST_OPENED or
// XML_HTTP_REQUEST_SENT
if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) {
// We're uploading if our state is State::opened or State::sent
if (mState == State::opened || mState == State::sent) {
if (mUpload && !mUploadComplete) {
DispatchProgressEvent(mUpload, ProgressEventType::progress,
mUploadLengthComputable, mUploadTransferred,
@ -3296,9 +3244,8 @@ XMLHttpRequestMainThread::MaybeDispatchProgressEvents(bool aFinalProgress)
NS_IMETHODIMP
XMLHttpRequestMainThread::OnProgress(nsIRequest *aRequest, nsISupports *aContext, int64_t aProgress, int64_t aProgressMax)
{
// We're uploading if our state is XML_HTTP_REQUEST_OPENED or
// XML_HTTP_REQUEST_SENT
bool upload = !!((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState);
// We're uploading if our state is State::opened or State::sent
bool upload = mState == State::opened || mState == State::sent;
// When uploading, OnProgress reports also headers in aProgress and aProgressMax.
// So, try to remove the headers, if possible.
bool lengthComputable = (aProgressMax != -1);
@ -3343,7 +3290,7 @@ bool
XMLHttpRequestMainThread::AllowUploadProgress()
{
return !IsCrossSiteCORSRequest() ||
(mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
mFlagHadUploadListenersOnSend;
}
/////////////////////////////////////////////////////
@ -3378,7 +3325,7 @@ XMLHttpRequestMainThread::GetInterface(const nsIID & aIID, void **aResult)
}
}
if (mState & XML_HTTP_REQUEST_BACKGROUND) {
if (mFlagBackgroundRequest) {
nsCOMPtr<nsIInterfaceRequestor> badCertHandler(do_CreateInstance(NS_BADCERTHANDLER_CONTRACTID, &rv));
// Ignore failure to get component, we may not have all its dependencies
@ -3480,13 +3427,14 @@ XMLHttpRequestMainThread::GetMozSystem(bool* aSystem)
void
XMLHttpRequestMainThread::HandleTimeoutCallback()
{
if (mState & XML_HTTP_REQUEST_DONE) {
if (mState == State::done) {
NS_NOTREACHED("XMLHttpRequestMainThread::HandleTimeoutCallback with completed request");
// do nothing!
return;
}
CloseRequestWithError(ProgressEventType::timeout, XML_HTTP_REQUEST_TIMED_OUT);
mFlagTimedOut = true;
CloseRequestWithError(ProgressEventType::timeout);
}
NS_IMETHODIMP

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

@ -7,6 +7,7 @@
#ifndef mozilla_dom_XMLHttpRequestMainThread_h
#define mozilla_dom_XMLHttpRequestMainThread_h
#include <bitset>
#include "nsAutoPtr.h"
#include "nsIXMLHttpRequest.h"
#include "nsISupportsUtils.h"
@ -558,6 +559,18 @@ public:
return sDontWarnAboutSyncXHR;
}
protected:
// XHR states are meant to mirror the XHR2 spec:
// https://xhr.spec.whatwg.org/#states
// Note that we currently have an extra pseudo-state called sent.
enum class State : uint8_t {
unsent, // object has been constructed.
opened, // open() has been successfully invoked.
sent, // non-spec, corresponds with "opened and the send() flag is set".
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 NS_METHOD StreamReaderFunc(nsIInputStream* in,
@ -571,7 +584,7 @@ protected:
bool CreateDOMBlob(nsIRequest *request);
// Change the state of the object with this. The broadcast argument
// determines if the onreadystatechange listener should be called.
nsresult ChangeState(uint32_t aState, bool aBroadcast = true);
nsresult ChangeState(State aState, bool aBroadcast = true);
already_AddRefed<nsILoadGroup> GetLoadGroup() const;
nsIURI *GetBaseURI();
@ -675,7 +688,17 @@ protected:
nsCOMPtr<nsIURI> mBaseURI;
nsCOMPtr<nsILoadGroup> mLoadGroup;
uint32_t mState;
State mState;
bool mFlagAsynchronous;
bool mFlagAborted;
bool mFlagParseBody;
bool mFlagSyncLooping;
bool mFlagBackgroundRequest;
bool mFlagHadUploadListenersOnSend;
bool mFlagACwithCredentials;
bool mFlagTimedOut;
bool mFlagDeleted;
RefPtr<XMLHttpRequestUpload> mUpload;
int64_t mUploadTransferred;
@ -725,10 +748,8 @@ protected:
* events.
*
* @param aType The progress event type.
* @param aFlag A XML_HTTP_REQUEST_* state flag defined in
* XMLHttpRequestMainthread.cpp.
*/
void CloseRequestWithError(const ProgressEventType aType, const uint32_t aFlag);
void CloseRequestWithError(const ProgressEventType aType);
bool mFirstStartRequestSeen;
bool mInLoadProgressEvent;