Bug 1591579 - XHR creates the Blob response only at the first GetResponse() call, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 04 Nov 2019 15:38:14 +0000
changeset 500414 913363f089964abf0a6beb2c522ab8136ad78d3e
parent 500413 62a0c94786693fdc3612ec253fd80c9f855aeadf
child 500415 6a0f2be5eb86d78fa08a41ffe13c6b28d617b9b2
push id114164
push useraiakab@mozilla.com
push dateTue, 05 Nov 2019 10:06:15 +0000
treeherdermozilla-inbound@4d585c7edc76 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1591579
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1591579 - XHR creates the Blob response only at the first GetResponse() call, r=smaug Differential Revision: https://phabricator.services.mozilla.com/D50918
dom/xhr/XMLHttpRequestMainThread.cpp
dom/xhr/XMLHttpRequestMainThread.h
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -308,16 +308,17 @@ void XMLHttpRequestMainThread::SetClient
   mClientInfo.emplace(aClientInfo);
   mController = aController;
 }
 
 void XMLHttpRequestMainThread::ResetResponse() {
   mResponseXML = nullptr;
   mResponseBody.Truncate();
   TruncateResponseText();
+  mResponseBlobImpl = nullptr;
   mResponseBlob = nullptr;
   mBlobStorage = nullptr;
   mResultArrayBuffer = nullptr;
   mArrayBufferBuilder.reset();
   mResultJSON.setUndefined();
   mLoadTransferred = 0;
   mResponseBodyDecodedPos = 0;
   mEofDecoded = false;
@@ -347,16 +348,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread,
                                                 XMLHttpRequestEventTarget)
   tmp->mResultArrayBuffer = nullptr;
   tmp->mArrayBufferBuilder.reset();
   tmp->mResultJSON.setUndefined();
+  tmp->mResponseBlobImpl = nullptr;
+
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks)
@@ -686,21 +689,25 @@ void XMLHttpRequestMainThread::GetRespon
       return;
     }
     case XMLHttpRequestResponseType::Blob: {
       if (mState != XMLHttpRequest_Binding::DONE) {
         aResponse.setNull();
         return;
       }
 
-      if (!mResponseBlob) {
+      if (!mResponseBlobImpl) {
         aResponse.setNull();
         return;
       }
 
+      if (!mResponseBlob) {
+        mResponseBlob = Blob::Create(GetOwnerGlobal(), mResponseBlobImpl);
+      }
+
       GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse);
       return;
     }
     case XMLHttpRequestResponseType::Document: {
       if (!mResponseXML || mState != XMLHttpRequest_Binding::DONE) {
         aResponse.setNull();
         return;
       }
@@ -1644,17 +1651,17 @@ class FileCreationHandler final : public
     }
 
     RefPtr<Blob> blob;
     if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob, &aValue.toObject(), blob)))) {
       mXHR->LocalFileToBlobCompleted(nullptr);
       return;
     }
 
-    mXHR->LocalFileToBlobCompleted(blob);
+    mXHR->LocalFileToBlobCompleted(blob->Impl());
   }
 
   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
     mXHR->LocalFileToBlobCompleted(nullptr);
   }
 
  private:
   explicit FileCreationHandler(XMLHttpRequestMainThread* aXHR) : mXHR(aXHR) {
@@ -1665,20 +1672,20 @@ class FileCreationHandler final : public
 
   RefPtr<XMLHttpRequestMainThread> mXHR;
 };
 
 NS_IMPL_ISUPPORTS0(FileCreationHandler)
 
 }  // namespace
 
-void XMLHttpRequestMainThread::LocalFileToBlobCompleted(Blob* aBlob) {
+void XMLHttpRequestMainThread::LocalFileToBlobCompleted(BlobImpl* aBlobImpl) {
   MOZ_ASSERT(mState != XMLHttpRequest_Binding::DONE);
 
-  mResponseBlob = aBlob;
+  mResponseBlobImpl = aBlobImpl;
   mBlobStorage = nullptr;
   NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
 
   ChangeStateToDone(mFlagSyncLooping);
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::OnDataAvailable(nsIRequest* request,
@@ -1695,31 +1702,26 @@ XMLHttpRequestMainThread::OnDataAvailabl
   if (mResponseType == XMLHttpRequestResponseType::Blob) {
     nsCOMPtr<nsIFile> localFile;
     nsCOMPtr<nsIURI> blobURI;
     GetBlobURIFromChannel(request, getter_AddRefs(blobURI));
     if (blobURI) {
       RefPtr<BlobImpl> blobImpl;
       rv = NS_GetBlobForBlobURI(blobURI, getter_AddRefs(blobImpl));
       if (NS_SUCCEEDED(rv)) {
-        if (blobImpl) {
-          mResponseBlob = Blob::Create(GetOwnerGlobal(), blobImpl);
-        }
-        if (!mResponseBlob) {
-          rv = NS_ERROR_FILE_NOT_FOUND;
-        }
+        mResponseBlobImpl = blobImpl;
       }
     } else {
       rv = GetLocalFileFromChannel(request, getter_AddRefs(localFile));
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    if (mResponseBlob || localFile) {
+    if (mResponseBlobImpl || localFile) {
       mBlobStorage = nullptr;
       NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
 
       // The nsIStreamListener contract mandates us to read from the stream
       // before returning.
       uint32_t totalRead;
       rv = inStr->ReadSegments(DummyStreamReaderFunc, nullptr, count,
                                &totalRead);
@@ -2077,27 +2079,27 @@ XMLHttpRequestMainThread::OnStopRequest(
     mFlagParseBody = false;
     IgnoredErrorResult rv;
     RequestErrorSteps(ProgressEventType::abort, NS_OK, rv);
     ChangeState(XMLHttpRequest_Binding::UNSENT, false);
     return NS_OK;
   }
 
   // If we were just reading a blob URL, we're already done
-  if (status == NS_ERROR_FILE_ALREADY_EXISTS && mResponseBlob) {
+  if (status == NS_ERROR_FILE_ALREADY_EXISTS && mResponseBlobImpl) {
     ChangeStateToDone(mFlagSyncLooping);
     return NS_OK;
   }
 
   bool waitingForBlobCreation = false;
 
   // If we have this error, we have to deal with a file: URL + responseType =
   // blob. We have this error because we canceled the channel. The status will
   // be set to NS_OK.
-  if (!mResponseBlob && status == NS_ERROR_FILE_ALREADY_EXISTS &&
+  if (!mResponseBlobImpl && status == NS_ERROR_FILE_ALREADY_EXISTS &&
       mResponseType == XMLHttpRequestResponseType::Blob) {
     nsCOMPtr<nsIFile> file;
     nsresult rv = GetLocalFileFromChannel(request, getter_AddRefs(file));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (file) {
@@ -3586,17 +3588,17 @@ void XMLHttpRequestMainThread::BlobStore
     MutableBlobStorage* aBlobStorage, BlobImpl* aBlobImpl, nsresult aRv) {
   // Ok, the state is changed...
   if (mBlobStorage != aBlobStorage || NS_FAILED(aRv)) {
     return;
   }
 
   MOZ_ASSERT(mState != XMLHttpRequest_Binding::DONE);
 
-  mResponseBlob = Blob::Create(GetOwnerGlobal(), aBlobImpl);
+  mResponseBlobImpl = aBlobImpl;
   mBlobStorage = nullptr;
 
   ChangeStateToDone(mFlagSyncLooping);
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::GetName(nsACString& aName) {
   aName.AssignLiteral("XMLHttpRequest");
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -431,17 +431,17 @@ class XMLHttpRequestMainThread final : p
   static bool DontWarnAboutSyncXHR() { return sDontWarnAboutSyncXHR; }
 
   virtual void SetOriginAttributes(
       const mozilla::dom::OriginAttributesDictionary& aAttrs) override;
 
   void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, BlobImpl* aBlobImpl,
                           nsresult aResult) override;
 
-  void LocalFileToBlobCompleted(Blob* aBlob);
+  void LocalFileToBlobCompleted(BlobImpl* aBlobImpl);
 
  protected:
   nsresult DetectCharset();
   nsresult AppendToResponseText(Span<const uint8_t> aBuffer,
                                 bool aLast = false);
   static nsresult StreamReaderFunc(nsIInputStream* in, void* closure,
                                    const char* fromRawSegment,
                                    uint32_t toOffset, uint32_t count,
@@ -592,19 +592,22 @@ class XMLHttpRequestMainThread final : p
   // simply feed the new data into the decoder which will handle the second
   // part of the surrogate.
   mozilla::UniquePtr<mozilla::Decoder> mDecoder;
 
   void MatchCharsetAndDecoderToResponseDocument();
 
   XMLHttpRequestResponseType mResponseType;
 
-  // It is either a cached blob-response from the last call to GetResponse,
-  // but is also explicitly set in OnStopRequest.
+  RefPtr<BlobImpl> mResponseBlobImpl;
+
+  // This is the cached blob-response, created only at the first GetResponse()
+  // call.
   RefPtr<Blob> mResponseBlob;
+
   // We stream data to mBlobStorage when response type is "blob".
   RefPtr<MutableBlobStorage> mBlobStorage;
 
   nsString mOverrideMimeType;
 
   /**
    * The notification callbacks the channel had when Send() was
    * called.  We want to forward things here as needed.