From 6c4f3e3599103eeb7dba9c29817b5200ba54fadc Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Mon, 4 Nov 2019 15:38:08 +0000 Subject: [PATCH] Bug 1591579 - XMLWorker takes data from XMLMainThread without calling GetResponse(), r=smaug Differential Revision: https://phabricator.services.mozilla.com/D50921 --HG-- extra : moz-landing-system : lando --- dom/xhr/XMLHttpRequestMainThread.cpp | 9 +- dom/xhr/XMLHttpRequestWorker.cpp | 372 ++++++++++++++------------- dom/xhr/XMLHttpRequestWorker.h | 69 +++-- 3 files changed, 249 insertions(+), 201 deletions(-) diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 154b7eeeb50c..205885f0a70d 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -546,6 +546,8 @@ nsresult XMLHttpRequestMainThread::AppendToResponseText( void XMLHttpRequestMainThread::GetResponseText(DOMString& aResponseText, ErrorResult& aRv) { + MOZ_DIAGNOSTIC_ASSERT(!mForWorker); + XMLHttpRequestStringSnapshot snapshot; GetResponseText(snapshot, aRv); if (aRv.Failed()) { @@ -660,6 +662,8 @@ void XMLHttpRequestMainThread::SetResponseType( void XMLHttpRequestMainThread::GetResponse( JSContext* aCx, JS::MutableHandle aResponse, ErrorResult& aRv) { + MOZ_DIAGNOSTIC_ASSERT(!mForWorker); + switch (mResponseType) { case XMLHttpRequestResponseType::_empty: case XMLHttpRequestResponseType::Text: { @@ -705,7 +709,10 @@ void XMLHttpRequestMainThread::GetResponse( mResponseBlob = Blob::Create(GetOwnerGlobal(), mResponseBlobImpl); } - GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse); + if (!GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse)) { + aResponse.setNull(); + } + return; } case XMLHttpRequestResponseType::Document: { diff --git a/dom/xhr/XMLHttpRequestWorker.cpp b/dom/xhr/XMLHttpRequestWorker.cpp index 7099751c6d6f..bedb2984de04 100644 --- a/dom/xhr/XMLHttpRequestWorker.cpp +++ b/dom/xhr/XMLHttpRequestWorker.cpp @@ -44,9 +44,42 @@ namespace mozilla { namespace dom { -/* static */ -void XMLHttpRequestWorker::StateData::trace(JSTracer* aTrc) { - JS::TraceEdge(aTrc, &mResponse, "XMLHttpRequestWorker::StateData::mResponse"); +void XMLHttpRequestWorker::StateData::Unlink() { + MOZ_DIAGNOSTIC_ASSERT(mResponseData); + mResponseData->Unlink(); +} + +void XMLHttpRequestWorker::ResponseData::Unlink() { + mResponseBlobImpl = nullptr; + mResponseBlob = nullptr; + mResponseArrayBufferBuilder = nullptr; + mResponseArrayBufferValue = nullptr; + mResponseJSONValue.setUndefined(); +} + +void XMLHttpRequestWorker::StateData::Trace(const TraceCallbacks& aCallbacks, + void* aClosure) { + MOZ_DIAGNOSTIC_ASSERT(mResponseData); + mResponseData->Trace(aCallbacks, aClosure); +} + +void XMLHttpRequestWorker::ResponseData::Trace(const TraceCallbacks& aCallbacks, + void* aClosure) { + ResponseData* tmp = this; + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResponseArrayBufferValue) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResponseJSONValue) +} + +void XMLHttpRequestWorker::StateData::Traverse( + nsCycleCollectionTraversalCallback& cb) { + MOZ_DIAGNOSTIC_ASSERT(mResponseData); + mResponseData->Traverse(cb); +} + +void XMLHttpRequestWorker::ResponseData::Traverse( + nsCycleCollectionTraversalCallback& cb) { + ResponseData* tmp = this; + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob) } /** @@ -131,7 +164,6 @@ class Proxy final : public nsIDOMEventListener { bool mUploadEventListenersAttached; bool mMainThreadSeenLoadStart; bool mInOpen; - bool mArrayBufferResponseWasTransferred; public: Proxy(XMLHttpRequestWorker* aXHRPrivate, const ClientInfo& aClientInfo, @@ -160,8 +192,7 @@ class Proxy final : public nsIDOMEventListener { mSeenUploadLoadStart(false), mUploadEventListenersAttached(false), mMainThreadSeenLoadStart(false), - mInOpen(false), - mArrayBufferResponseWasTransferred(false) {} + mInOpen(false) {} NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIDOMEVENTLISTENER @@ -446,12 +477,9 @@ class LoadStartDetectionRunnable final : public Runnable, ~LoadStartDetectionRunnable() { AssertIsOnMainThread(); } }; -class EventRunnable final : public MainThreadProxyRunnable, - public StructuredCloneHolder { +class EventRunnable final : public MainThreadProxyRunnable { nsString mType; - nsString mResponseType; - JS::Heap mResponse; - XMLHttpRequestStringSnapshot mResponseText; + UniquePtr mResponseData; nsString mResponseURL; nsCString mStatusText; uint64_t mLoaded; @@ -462,10 +490,7 @@ class EventRunnable final : public MainThreadProxyRunnable, 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 // reset() in PreDispatch, to ensure that it's not still linked into the // runtime once we go off-thread. @@ -476,10 +501,8 @@ class EventRunnable final : public MainThreadProxyRunnable, bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal, JS::Handle aScopeObj) : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), - StructuredCloneHolder(CloningSupported, TransferringNotSupported, - StructuredCloneScope::SameProcessDifferentThread), mType(aType), - mResponse(JS::UndefinedValue()), + mResponseData(new XMLHttpRequestWorker::ResponseData()), mLoaded(aLoaded), mTotal(aTotal), mEventStreamId(aProxy->mInnerEventStreamId), @@ -488,19 +511,14 @@ class EventRunnable final : public MainThreadProxyRunnable, 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 aScopeObj) : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), - StructuredCloneHolder(CloningSupported, TransferringNotSupported, - StructuredCloneScope::SameProcessDifferentThread), mType(aType), - mResponse(JS::UndefinedValue()), + mResponseData(new XMLHttpRequestWorker::ResponseData()), mLoaded(0), mTotal(0), mEventStreamId(aProxy->mInnerEventStreamId), @@ -509,10 +527,7 @@ class EventRunnable final : public MainThreadProxyRunnable, mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0), - mUseCachedArrayBufferResponse(false), - mResponseTextResult(NS_OK), mStatusResult(NS_OK), - mResponseResult(NS_OK), mScopeObj(RootingCx(), aScopeObj) {} private: @@ -1027,64 +1042,37 @@ bool EventRunnable::PreDispatch(WorkerPrivate* /* unused */) { RefPtr& xhr = mProxy->mXHR; MOZ_ASSERT(xhr); - mResponseType.AssignASCII( - XMLHttpRequestResponseTypeValues::GetString(xhr->ResponseType())); + mResponseData->mResponseType = xhr->ResponseType(); ErrorResult rv; - xhr->GetResponseText(mResponseText, rv); - mResponseTextResult = rv.StealNSResult(); - - if (NS_SUCCEEDED(mResponseTextResult)) { - mResponseResult = mResponseTextResult; - if (mResponseText.IsVoid()) { - mResponse.setNull(); + switch (mResponseData->mResponseType) { + case XMLHttpRequestResponseType::_empty: + case XMLHttpRequestResponseType::Text: { + xhr->GetResponseText(mResponseData->mResponseText, rv); + mResponseData->mResponseResult = rv.StealNSResult(); + break; } - } else { - JS::Rooted response(cx); - xhr->GetResponse(cx, &response, rv); - mResponseResult = rv.StealNSResult(); - if (NS_SUCCEEDED(mResponseResult)) { - if (!response.isGCThing()) { - mResponse = response; - } else { - bool doClone = true; - JS::Rooted transferable(cx); - JS::Rooted obj( - cx, response.isObject() ? &response.toObject() : nullptr); - if (obj && JS::IsArrayBufferObject(obj)) { - // Use cached response if the arraybuffer has been transfered. - if (mProxy->mArrayBufferResponseWasTransferred) { - MOZ_ASSERT(JS::IsDetachedArrayBufferObject(obj)); - mUseCachedArrayBufferResponse = true; - doClone = false; - } 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) { - mProxy->mArrayBufferResponseWasTransferred = true; - } - } else { - mResponseResult = NS_ERROR_OUT_OF_MEMORY; - doClone = false; - } - } - } - if (doClone) { - Write(cx, response, transferable, JS::CloneDataPolicy(), rv); - if (NS_WARN_IF(rv.Failed())) { - NS_WARNING("Failed to clone response!"); - mResponseResult = rv.StealNSResult(); - mProxy->mArrayBufferResponseWasTransferred = false; - } - } - } + case XMLHttpRequestResponseType::Blob: { + mResponseData->mResponseBlobImpl = xhr->GetResponseBlobImpl(); + break; } + + case XMLHttpRequestResponseType::Arraybuffer: { + mResponseData->mResponseArrayBufferBuilder = + xhr->GetResponseArrayBufferBuilder(); + break; + } + + case XMLHttpRequestResponseType::Json: { + mResponseData->mResponseResult = + xhr->GetResponseTextForJSON(mResponseData->mResponseJSON); + break; + } + + default: + MOZ_CRASH("Invalid response type"); + break; } mStatus = xhr->GetStatus(rv); @@ -1148,44 +1136,10 @@ bool EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { } } - JS::Rooted> state( - aCx, new XMLHttpRequestWorker::StateData()); + UniquePtr state( + new XMLHttpRequestWorker::StateData()); - state->mResponseTextResult = mResponseTextResult; - - state->mResponseText = mResponseText; - - if (NS_SUCCEEDED(mResponseTextResult)) { - MOZ_ASSERT(mResponse.isUndefined() || mResponse.isNull()); - state->mResponseResult = mResponseTextResult; - state->mResponse = mResponse; - } else { - state->mResponseResult = mResponseResult; - - if (NS_SUCCEEDED(mResponseResult)) { - if (HasData()) { - MOZ_ASSERT(mResponse.isUndefined()); - - ErrorResult rv; - JS::Rooted response(aCx); - - GlobalObject globalObj(aCx, - aWorkerPrivate->GlobalScope()->GetWrapper()); - nsCOMPtr global = - do_QueryInterface(globalObj.GetAsSupports()); - - Read(global, aCx, &response, rv); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); - return false; - } - - state->mResponse = response; - } else { - state->mResponse = mResponse; - } - } - } + state->mResponseData = std::move(mResponseData); state->mStatusResult = mStatusResult; state->mStatus = mStatus; @@ -1197,7 +1151,7 @@ bool EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { state->mResponseURL = mResponseURL; XMLHttpRequestWorker* xhr = mProxy->mXMLHttpRequestPrivate; - xhr->UpdateState(*state.get(), mUseCachedArrayBufferResponse); + xhr->UpdateState(std::move(state)); if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) { return true; @@ -1238,11 +1192,6 @@ bool EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { target->DispatchEvent(*event); - // After firing the event set mResponse to null for chunked response types. - if (StringBeginsWith(mResponseType, NS_LITERAL_STRING("moz-chunked-"))) { - xhr->NullResponseText(); - } - return true; } @@ -1423,8 +1372,6 @@ void SendRunnable::RunOnMainThread(ErrorResult& aRv) { } } - mProxy->mArrayBufferResponseWasTransferred = false; - mProxy->mInnerChannelId++; mProxy->mXHR->Send(nullptr, payload, aRv); @@ -1446,6 +1393,7 @@ void SendRunnable::RunOnMainThread(ErrorResult& aRv) { XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate) : mWorkerPrivate(aWorkerPrivate), mResponseType(XMLHttpRequestResponseType::_empty), + mStateData(new StateData()), mTimeout(0), mBackgroundRequest(false), mWithCredentials(false), @@ -1479,18 +1427,19 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestWorker) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload) + tmp->mStateData->Traverse(cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget) tmp->ReleaseProxy(XHRIsGoingAway); NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload) - tmp->mStateData.mResponse.setUndefined(); + tmp->mStateData->Unlink(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStateData.mResponse) + tmp->mStateData->Trace(aCallbacks, aClosure); NS_IMPL_CYCLE_COLLECTION_TRACE_END /* static */ @@ -1586,10 +1535,10 @@ void XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv) { // 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 == 1 && mStateData->mFlagSend) || + mStateData->mReadyState == 2 || mStateData->mReadyState == 3) { isStateChanged = true; - mStateData.mReadyState = 4; + mStateData->mReadyState = 4; } if (mProxy->mSeenUploadLoadStart) { @@ -1730,7 +1679,7 @@ void XMLHttpRequestWorker::SendInternal(SendRunnable* aRunnable, aRunnable->SetSyncLoopTarget(syncLoopTarget); aRunnable->SetHaveUploadListeners(hasUploadListeners); - mStateData.mFlagSend = true; + mStateData->mFlagSend = true; aRunnable->Dispatch(Canceling, aRv); if (aRv.Failed()) { @@ -1751,7 +1700,7 @@ void XMLHttpRequestWorker::SendInternal(SendRunnable* aRunnable, autoUnpin.Clear(); bool succeeded = autoSyncLoop->Run(); - mStateData.mFlagSend = false; + mStateData->mFlagSend = false; // Don't clobber an existing exception that we may have thrown on aRv // already... though can there really be one? In any case, it seems to me @@ -1946,7 +1895,7 @@ void XMLHttpRequestWorker::Send( return; } - if (!mProxy || mStateData.mFlagSend) { + if (!mProxy || mStateData->mFlagSend) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } @@ -2078,13 +2027,13 @@ void XMLHttpRequestWorker::Abort(ErrorResult& aRv) { // 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 == XMLHttpRequest_Binding::OPENED && - mStateData.mFlagSend) || - mStateData.mReadyState == XMLHttpRequest_Binding::HEADERS_RECEIVED || - mStateData.mReadyState == XMLHttpRequest_Binding::LOADING || - mStateData.mReadyState == XMLHttpRequest_Binding::DONE) { - mStateData.mStatus = 0; - mStateData.mStatusText.Truncate(); + if ((mStateData->mReadyState == XMLHttpRequest_Binding::OPENED && + mStateData->mFlagSend) || + mStateData->mReadyState == XMLHttpRequest_Binding::HEADERS_RECEIVED || + mStateData->mReadyState == XMLHttpRequest_Binding::LOADING || + mStateData->mReadyState == XMLHttpRequest_Binding::DONE) { + mStateData->mStatus = 0; + mStateData->mStatusText.Truncate(); } MaybeDispatchPrematureAbortEvents(aRv); @@ -2092,10 +2041,10 @@ void XMLHttpRequestWorker::Abort(ErrorResult& aRv) { return; } - if (mStateData.mReadyState == 4) { + if (mStateData->mReadyState == 4) { // No one did anything to us while we fired abort events, so reset our state // to "unsent" - mStateData.mReadyState = 0; + mStateData->mReadyState = 0; } mProxy->mOuterEventStreamId++; @@ -2165,8 +2114,8 @@ void XMLHttpRequestWorker::OverrideMimeType(const nsAString& aMimeType, } // We're supposed to throw if the state is LOADING or DONE. - if (mStateData.mReadyState == XMLHttpRequest_Binding::LOADING || - mStateData.mReadyState == XMLHttpRequest_Binding::DONE) { + if (mStateData->mReadyState == XMLHttpRequest_Binding::LOADING || + mStateData->mReadyState == XMLHttpRequest_Binding::DONE) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } @@ -2197,8 +2146,8 @@ void XMLHttpRequestWorker::SetResponseType( return; } - if (mStateData.mReadyState == XMLHttpRequest_Binding::LOADING || - mStateData.mReadyState == XMLHttpRequest_Binding::DONE) { + if (mStateData->mReadyState == XMLHttpRequest_Binding::LOADING || + mStateData->mReadyState == XMLHttpRequest_Binding::DONE) { aRv.Throw( NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE_RESPONSE_TYPE); return; @@ -2214,62 +2163,131 @@ void XMLHttpRequestWorker::SetResponseType( mResponseType = runnable->ResponseType(); } -void XMLHttpRequestWorker::GetResponse(JSContext* /* unused */, +void XMLHttpRequestWorker::GetResponse(JSContext* aCx, JS::MutableHandle aResponse, ErrorResult& aRv) { - if (NS_SUCCEEDED(mStateData.mResponseTextResult) && - mStateData.mResponse.isUndefined()) { - MOZ_ASSERT(NS_SUCCEEDED(mStateData.mResponseResult)); + if (NS_FAILED(mStateData->mResponseData->mResponseResult)) { + aRv.Throw(mStateData->mResponseData->mResponseResult); + return; + } - if (mStateData.mResponseText.IsEmpty()) { - mStateData.mResponse = - JS_GetEmptyStringValue(mWorkerPrivate->GetJSContext()); - } else { - XMLHttpRequestStringSnapshotReaderHelper helper(mStateData.mResponseText); + switch (mStateData->mResponseData->mResponseType) { + case XMLHttpRequestResponseType::_empty: + case XMLHttpRequestResponseType::Text: { + JSString* str; - JSString* str = JS_NewUCStringCopyN(mWorkerPrivate->GetJSContext(), - helper.Buffer(), helper.Length()); + if (mStateData->mResponseData->mResponseText.IsEmpty()) { + aResponse.set(JS_GetEmptyStringValue(aCx)); + return; + } + XMLHttpRequestStringSnapshotReaderHelper helper( + mStateData->mResponseData->mResponseText); + + str = JS_NewUCStringCopyN(aCx, helper.Buffer(), helper.Length()); if (!str) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } - mStateData.mResponse.setString(str); + aResponse.setString(str); + return; } - } - aRv = mStateData.mResponseResult; - aResponse.set(mStateData.mResponse); + case XMLHttpRequestResponseType::Arraybuffer: { + if (!mStateData->mResponseData->mResponseArrayBufferBuilder) { + aResponse.setNull(); + return; + } + + if (!mStateData->mResponseData->mResponseArrayBufferValue) { + mStateData->mResponseData->mResponseArrayBufferValue = + mStateData->mResponseData->mResponseArrayBufferBuilder + ->GetArrayBuffer(aCx); + if (!mStateData->mResponseData->mResponseArrayBufferValue) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + } + + aResponse.setObject( + *mStateData->mResponseData->mResponseArrayBufferValue); + return; + } + + case XMLHttpRequestResponseType::Blob: { + if (!mStateData->mResponseData->mResponseBlobImpl) { + aResponse.setNull(); + return; + } + + if (!mStateData->mResponseData->mResponseBlob) { + mStateData->mResponseData->mResponseBlob = Blob::Create( + GetOwnerGlobal(), mStateData->mResponseData->mResponseBlobImpl); + } + + if (!GetOrCreateDOMReflector( + aCx, mStateData->mResponseData->mResponseBlob, aResponse)) { + aResponse.setNull(); + } + + return; + } + + case XMLHttpRequestResponseType::Json: { + if (mStateData->mResponseData->mResponseJSON.IsVoid()) { + aResponse.setNull(); + return; + } + + if (mStateData->mResponseData->mResponseJSONValue.isUndefined()) { + // The Unicode converter has already zapped the BOM if there was one + JS::Rooted value(aCx); + if (!JS_ParseJSON( + aCx, mStateData->mResponseData->mResponseJSON.BeginReading(), + mStateData->mResponseData->mResponseJSON.Length(), &value)) { + JS_ClearPendingException(aCx); + mStateData->mResponseData->mResponseJSONValue.setNull(); + } else { + mStateData->mResponseData->mResponseJSONValue = value; + } + + mStateData->mResponseData->mResponseJSON.Truncate(); + } + + aResponse.set(mStateData->mResponseData->mResponseJSONValue); + return; + } + + default: + MOZ_CRASH("Invalid type"); + aResponse.setNull(); + return; + } } void XMLHttpRequestWorker::GetResponseText(DOMString& aResponseText, ErrorResult& aRv) { - aRv = mStateData.mResponseTextResult; - if (aRv.Failed()) { + MOZ_DIAGNOSTIC_ASSERT(mStateData->mResponseData); + + if (mStateData->mResponseData->mResponseType != + XMLHttpRequestResponseType::_empty && + mStateData->mResponseData->mResponseType != + XMLHttpRequestResponseType::Text) { + aRv.Throw( + NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT); return; } - if (!mStateData.mResponseText.GetAsString(aResponseText)) { + if (!mStateData->mResponseData->mResponseText.GetAsString(aResponseText)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } } -void XMLHttpRequestWorker::UpdateState(const StateData& aStateData, - bool aUseCachedArrayBufferResponse) { - if (aUseCachedArrayBufferResponse) { - MOZ_ASSERT(mStateData.mResponse.isObject() && - JS::IsArrayBufferObject(&mStateData.mResponse.toObject())); - - JS::Rooted response(mWorkerPrivate->GetJSContext(), - mStateData.mResponse); - mStateData = aStateData; - mStateData.mResponse = response; - } else { - mStateData = aStateData; - } - +void XMLHttpRequestWorker::UpdateState(UniquePtr&& aStateData) { + mStateData->Unlink(); + mStateData = std::move(aStateData); XMLHttpRequest_Binding::ClearCachedResponseTextValue(this); } diff --git a/dom/xhr/XMLHttpRequestWorker.h b/dom/xhr/XMLHttpRequestWorker.h index b23fd780d6e6..66571ceac592 100644 --- a/dom/xhr/XMLHttpRequestWorker.h +++ b/dom/xhr/XMLHttpRequestWorker.h @@ -22,28 +22,56 @@ class WorkerPrivate; class XMLHttpRequestWorker final : public XMLHttpRequest { public: - struct StateData { + // This defines the xhr.response value. + struct ResponseData { + XMLHttpRequestResponseType mResponseType; + nsresult mResponseResult; + + // responseType is empty or text. XMLHttpRequestStringSnapshot mResponseText; + + // responseType is blob + RefPtr mResponseBlobImpl; + RefPtr mResponseBlob; + + // responseType is arrayBuffer; + RefPtr mResponseArrayBufferBuilder; + JS::Heap mResponseArrayBufferValue; + + // responseType is json + nsString mResponseJSON; + JS::Heap mResponseJSONValue; + + ResponseData() + : mResponseType(XMLHttpRequestResponseType::_empty), + mResponseResult(NS_OK), + mResponseArrayBufferValue(nullptr), + mResponseJSONValue(JS::UndefinedValue()) {} + + void Unlink(); + void Trace(const TraceCallbacks& aCallbacks, void* aClosure); + void Traverse(nsCycleCollectionTraversalCallback& aCb); + }; + + struct StateData { + UniquePtr mResponseData; nsString mResponseURL; uint32_t mStatus; nsCString mStatusText; uint16_t mReadyState; bool mFlagSend; - JS::Heap mResponse; - nsresult mResponseTextResult; nsresult mStatusResult; - nsresult mResponseResult; StateData() - : mStatus(0), + : mResponseData(new ResponseData()), + mStatus(0), mReadyState(0), mFlagSend(false), - mResponse(JS::UndefinedValue()), - mResponseTextResult(NS_OK), - mStatusResult(NS_OK), - mResponseResult(NS_OK) {} + mStatusResult(NS_OK) {} - void trace(JSTracer* trc); + void Unlink(); + void Trace(const TraceCallbacks& aCallbacks, void* aClosure); + void Traverse(nsCycleCollectionTraversalCallback& aCb); }; private: @@ -52,7 +80,8 @@ class XMLHttpRequestWorker final : public XMLHttpRequest { RefPtr mWorkerRef; RefPtr mProxy; XMLHttpRequestResponseType mResponseType; - StateData mStateData; + + UniquePtr mStateData; uint32_t mTimeout; @@ -77,7 +106,7 @@ class XMLHttpRequestWorker final : public XMLHttpRequest { void Unpin(); virtual uint16_t ReadyState() const override { - return mStateData.mReadyState; + return mStateData->mReadyState; } virtual void Open(const nsACString& aMethod, const nsAString& aUrl, @@ -141,17 +170,17 @@ class XMLHttpRequestWorker final : public XMLHttpRequest { virtual void Abort(ErrorResult& aRv) override; virtual void GetResponseURL(nsAString& aUrl) override { - aUrl = mStateData.mResponseURL; + aUrl = mStateData->mResponseURL; } uint32_t GetStatus(ErrorResult& aRv) override { - aRv = mStateData.mStatusResult; - return mStateData.mStatus; + aRv = mStateData->mStatusResult; + return mStateData->mStatus; } virtual void GetStatusText(nsACString& aStatusText, ErrorResult& aRv) override { - aStatusText = mStateData.mStatusText; + aStatusText = mStateData->mStatusText; } virtual void GetResponseHeader(const nsACString& aHeader, @@ -195,13 +224,7 @@ class XMLHttpRequestWorker final : public XMLHttpRequest { XMLHttpRequestUpload* GetUploadObjectNoCreate() const { return mUpload; } - void UpdateState(const StateData& aStateData, - bool aUseCachedArrayBufferResponse); - - void NullResponseText() { - mStateData.mResponseText.SetVoid(); - mStateData.mResponse.setNull(); - } + void UpdateState(UniquePtr&& aStateData); virtual uint16_t ErrorCode() const override { return 0; // eOK