Backed out 7 changesets (bug 1591579) for Hazzard failure on dom/xhr/XMLHttpRequestWorker.cpp. CLOSED TREE
authorDorel Luca <dluca@mozilla.com>
Mon, 04 Nov 2019 17:33:15 +0200
changeset 500395 6c358363b281222f619014f75e734846ad7f41c6
parent 500394 7fa59747bc5df3fb62a8672a53f5c575ae652701
child 500396 9d297847a45226ba0a642510e547cf5952d7a991
push id36763
push userrmaries@mozilla.com
push dateMon, 04 Nov 2019 21:44:06 +0000
treeherdermozilla-central@75a7a3400888 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1591579
milestone72.0a1
backs out9a1cf12c3de167578445f539ac744018536fd5b4
4a228aa6e526336dba96a8a0cb643c4673c671f4
b19e6eba3b127f7414aa017e623b93b7673dd241
d7c095dce497f3a30841a14bbba00ce45824bd93
dfb0fcebe51e2bf7aeae5029ef5591ca2bec2ae4
9d525c1c558dec40bee5380bc663ae1032e98411
f4bcdcefe2d18b22a2e89a986fa8ac186c8d58c6
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
Backed out 7 changesets (bug 1591579) for Hazzard failure on dom/xhr/XMLHttpRequestWorker.cpp. CLOSED TREE Backed out changeset 9a1cf12c3de1 (bug 1591579) Backed out changeset 4a228aa6e526 (bug 1591579) Backed out changeset b19e6eba3b12 (bug 1591579) Backed out changeset d7c095dce497 (bug 1591579) Backed out changeset dfb0fcebe51e (bug 1591579) Backed out changeset 9d525c1c558d (bug 1591579) Backed out changeset f4bcdcefe2d1 (bug 1591579)
dom/xhr/XMLHttpRequestMainThread.cpp
dom/xhr/XMLHttpRequestMainThread.h
dom/xhr/XMLHttpRequestWorker.cpp
dom/xhr/XMLHttpRequestWorker.h
dom/xhr/tests/mochitest.ini
dom/xhr/tests/test_sharedworker_xhr.html
dom/xhr/tests/xhr_sharedworker.js
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -224,17 +224,16 @@ XMLHttpRequestMainThread::XMLHttpRequest
       mWarnAboutSyncHtml(false),
       mLoadTotal(-1),
       mLoadTransferred(0),
       mIsSystem(false),
       mIsAnon(false),
       mFirstStartRequestSeen(false),
       mInLoadProgressEvent(false),
       mResultJSON(JS::UndefinedValue()),
-      mArrayBufferBuilder(new ArrayBufferBuilder()),
       mResultArrayBuffer(nullptr),
       mIsMappedArrayBuffer(false),
       mXPCOMifier(nullptr),
       mEventDispatchingSuspended(false),
       mEofDecoded(false),
       mDelayedDoneNotifier(nullptr) {
   mozilla::HoldJSObjects(this);
 }
@@ -309,21 +308,20 @@ 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 = new ArrayBufferBuilder();
+  mArrayBufferBuilder.reset();
   mResultJSON.setUndefined();
   mLoadTransferred = 0;
   mResponseBodyDecodedPos = 0;
   mEofDecoded = false;
 }
 
 void XMLHttpRequestMainThread::SetRequestObserver(
     nsIRequestObserver* aObserver) {
@@ -347,20 +345,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
 
   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 = 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)
@@ -541,18 +537,16 @@ nsresult XMLHttpRequestMainThread::Appen
     mDecoder = nullptr;
     mEofDecoded = true;
   }
   return NS_OK;
 }
 
 void XMLHttpRequestMainThread::GetResponseText(DOMString& aResponseText,
                                                ErrorResult& aRv) {
-  MOZ_DIAGNOSTIC_ASSERT(!mForWorker);
-
   XMLHttpRequestStringSnapshot snapshot;
   GetResponseText(snapshot, aRv);
   if (aRv.Failed()) {
     return;
   }
 
   if (!snapshot.GetAsString(aResponseText)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
@@ -616,19 +610,18 @@ void XMLHttpRequestMainThread::GetRespon
 }
 
 nsresult XMLHttpRequestMainThread::CreateResponseParsedJSON(JSContext* aCx) {
   if (!aCx) {
     return NS_ERROR_FAILURE;
   }
 
   nsAutoString string;
-  nsresult rv = GetResponseTextForJSON(string);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  if (!mResponseText.GetAsString(string)) {
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // The Unicode converter has already zapped the BOM if there was one
   JS::Rooted<JS::Value> value(aCx);
   if (!JS_ParseJSON(aCx, string.BeginReading(), string.Length(), &value)) {
     return NS_ERROR_FAILURE;
   }
 
@@ -657,18 +650,16 @@ void XMLHttpRequestMainThread::SetRespon
   }
 
   // Set the responseType attribute's value to the given value.
   SetResponseTypeRaw(aResponseType);
 }
 
 void XMLHttpRequestMainThread::GetResponse(
     JSContext* aCx, JS::MutableHandle<JS::Value> aResponse, ErrorResult& aRv) {
-  MOZ_DIAGNOSTIC_ASSERT(!mForWorker);
-
   switch (mResponseType) {
     case XMLHttpRequestResponseType::_empty:
     case XMLHttpRequestResponseType::Text: {
       DOMString str;
       GetResponseText(str, aRv);
       if (aRv.Failed()) {
         return;
       }
@@ -680,44 +671,37 @@ void XMLHttpRequestMainThread::GetRespon
 
     case XMLHttpRequestResponseType::Arraybuffer: {
       if (mState != XMLHttpRequest_Binding::DONE) {
         aResponse.setNull();
         return;
       }
 
       if (!mResultArrayBuffer) {
-        mResultArrayBuffer = mArrayBufferBuilder->TakeArrayBuffer(aCx);
+        mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
         if (!mResultArrayBuffer) {
           aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
           return;
         }
       }
       aResponse.setObject(*mResultArrayBuffer);
       return;
     }
     case XMLHttpRequestResponseType::Blob: {
       if (mState != XMLHttpRequest_Binding::DONE) {
         aResponse.setNull();
         return;
       }
 
-      if (!mResponseBlobImpl) {
+      if (!mResponseBlob) {
         aResponse.setNull();
         return;
       }
 
-      if (!mResponseBlob) {
-        mResponseBlob = Blob::Create(GetOwnerGlobal(), mResponseBlobImpl);
-      }
-
-      if (!GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse)) {
-        aResponse.setNull();
-      }
-
+      GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse);
       return;
     }
     case XMLHttpRequestResponseType::Document: {
       if (!mResponseXML || mState != XMLHttpRequest_Binding::DONE) {
         aResponse.setNull();
         return;
       }
 
@@ -748,55 +732,16 @@ void XMLHttpRequestMainThread::GetRespon
     }
     default:
       NS_ERROR("Should not happen");
   }
 
   aResponse.setNull();
 }
 
-already_AddRefed<BlobImpl> XMLHttpRequestMainThread::GetResponseBlobImpl() {
-  MOZ_DIAGNOSTIC_ASSERT(mForWorker);
-  MOZ_DIAGNOSTIC_ASSERT(mResponseType == XMLHttpRequestResponseType::Blob);
-
-  if (mState != XMLHttpRequest_Binding::DONE) {
-    return nullptr;
-  }
-
-  RefPtr<BlobImpl> blobImpl = mResponseBlobImpl;
-  return blobImpl.forget();
-}
-
-already_AddRefed<ArrayBufferBuilder>
-XMLHttpRequestMainThread::GetResponseArrayBufferBuilder() {
-  MOZ_DIAGNOSTIC_ASSERT(mForWorker);
-  MOZ_DIAGNOSTIC_ASSERT(mResponseType ==
-                        XMLHttpRequestResponseType::Arraybuffer);
-
-  if (mState != XMLHttpRequest_Binding::DONE) {
-    return nullptr;
-  }
-
-  RefPtr<ArrayBufferBuilder> builder = mArrayBufferBuilder;
-  return builder.forget();
-}
-
-nsresult XMLHttpRequestMainThread::GetResponseTextForJSON(nsAString& aString) {
-  if (mState != XMLHttpRequest_Binding::DONE) {
-    aString.SetIsVoid(true);
-    return NS_OK;
-  }
-
-  if (!mResponseText.GetAsString(aString)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return NS_OK;
-}
-
 bool XMLHttpRequestMainThread::IsCrossSiteCORSRequest() const {
   if (!mChannel) {
     return false;
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
   return loadInfo->GetTainting() == LoadTainting::CORS;
 }
@@ -1555,21 +1500,21 @@ nsresult XMLHttpRequestMainThread::Strea
   if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Blob) {
     xmlHttpRequest->MaybeCreateBlobStorage();
     rv = xmlHttpRequest->mBlobStorage->Append(fromRawSegment, count);
   } else if (xmlHttpRequest->mResponseType ==
                  XMLHttpRequestResponseType::Arraybuffer &&
              !xmlHttpRequest->mIsMappedArrayBuffer) {
     // get the initial capacity to something reasonable to avoid a bunch of
     // reallocs right at the start
-    if (xmlHttpRequest->mArrayBufferBuilder->Capacity() == 0)
-      xmlHttpRequest->mArrayBufferBuilder->SetCapacity(
+    if (xmlHttpRequest->mArrayBufferBuilder.capacity() == 0)
+      xmlHttpRequest->mArrayBufferBuilder.setCapacity(
           std::max(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));
 
-    if (NS_WARN_IF(!xmlHttpRequest->mArrayBufferBuilder->Append(
+    if (NS_WARN_IF(!xmlHttpRequest->mArrayBufferBuilder.append(
             reinterpret_cast<const uint8_t*>(fromRawSegment), count,
             XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
   } else if (xmlHttpRequest->mResponseType ==
                  XMLHttpRequestResponseType::_empty &&
              xmlHttpRequest->mResponseXML) {
@@ -1699,17 +1644,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->Impl());
+    mXHR->LocalFileToBlobCompleted(blob);
   }
 
   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
     mXHR->LocalFileToBlobCompleted(nullptr);
   }
 
  private:
   explicit FileCreationHandler(XMLHttpRequestMainThread* aXHR) : mXHR(aXHR) {
@@ -1720,20 +1665,20 @@ class FileCreationHandler final : public
 
   RefPtr<XMLHttpRequestMainThread> mXHR;
 };
 
 NS_IMPL_ISUPPORTS0(FileCreationHandler)
 
 }  // namespace
 
-void XMLHttpRequestMainThread::LocalFileToBlobCompleted(BlobImpl* aBlobImpl) {
+void XMLHttpRequestMainThread::LocalFileToBlobCompleted(Blob* aBlob) {
   MOZ_ASSERT(mState != XMLHttpRequest_Binding::DONE);
 
-  mResponseBlobImpl = aBlobImpl;
+  mResponseBlob = aBlob;
   mBlobStorage = nullptr;
   NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
 
   ChangeStateToDone(mFlagSyncLooping);
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::OnDataAvailable(nsIRequest* request,
@@ -1750,26 +1695,31 @@ 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)) {
-        mResponseBlobImpl = blobImpl;
+        if (blobImpl) {
+          mResponseBlob = Blob::Create(GetOwnerGlobal(), blobImpl);
+        }
+        if (!mResponseBlob) {
+          rv = NS_ERROR_FILE_NOT_FOUND;
+        }
       }
     } else {
       rv = GetLocalFileFromChannel(request, getter_AddRefs(localFile));
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    if (mResponseBlobImpl || localFile) {
+    if (mResponseBlob || 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);
@@ -1904,17 +1854,17 @@ XMLHttpRequestMainThread::OnStartRequest
               jarURI->GetJAREntry(file);
             }
           }
           nsCOMPtr<nsIFile> jarFile;
           jarChannel->GetJarFile(getter_AddRefs(jarFile));
           if (!jarFile) {
             mIsMappedArrayBuffer = false;
           } else {
-            rv = mArrayBufferBuilder->MapToFileInPackage(file, jarFile);
+            rv = mArrayBufferBuilder.mapToFileInPackage(file, jarFile);
             // This can happen legitimately if there are compressed files
             // in the jarFile. See bug #1357219. No need to warn on the error.
             if (NS_FAILED(rv)) {
               mIsMappedArrayBuffer = false;
             } else {
               channel->SetContentType(
                   NS_LITERAL_CSTRING("application/mem-mapped"));
             }
@@ -1924,17 +1874,17 @@ XMLHttpRequestMainThread::OnStartRequest
     }
     // If memory mapping failed, mIsMappedArrayBuffer would be set to false,
     // and we want it fallback to the malloc way.
     if (!mIsMappedArrayBuffer) {
       int64_t contentLength;
       rv = channel->GetContentLength(&contentLength);
       if (NS_SUCCEEDED(rv) && contentLength > 0 &&
           contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
-        mArrayBufferBuilder->SetCapacity(static_cast<int32_t>(contentLength));
+        mArrayBufferBuilder.setCapacity(static_cast<int32_t>(contentLength));
       }
     }
   }
 
   // Set up responseXML
   // Note: Main Fetch step 18 requires to ignore body for head/connect methods.
   bool parseBody = (mResponseType == XMLHttpRequestResponseType::_empty ||
                     mResponseType == XMLHttpRequestResponseType::Document) &&
@@ -2127,27 +2077,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 && mResponseBlobImpl) {
+  if (status == NS_ERROR_FILE_ALREADY_EXISTS && mResponseBlob) {
     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 (!mResponseBlobImpl && status == NS_ERROR_FILE_ALREADY_EXISTS &&
+  if (!mResponseBlob && 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) {
@@ -2197,17 +2147,17 @@ XMLHttpRequestMainThread::OnStopRequest(
     waitingForBlobCreation = true;
 
     NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
     NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
   } else if (NS_SUCCEEDED(status) && !mIsMappedArrayBuffer &&
              mResponseType == XMLHttpRequestResponseType::Arraybuffer) {
     // set the capacity down to the actual length, to realloc back
     // down to the actual size
-    if (!mArrayBufferBuilder->SetCapacity(mArrayBufferBuilder->Length())) {
+    if (!mArrayBufferBuilder.setCapacity(mArrayBufferBuilder.length())) {
       // this should never happen!
       status = NS_ERROR_UNEXPECTED;
     }
   }
 
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
 
@@ -3636,17 +3586,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);
 
-  mResponseBlobImpl = aBlobImpl;
+  mResponseBlob = Blob::Create(GetOwnerGlobal(), aBlobImpl);
   mBlobStorage = nullptr;
 
   ChangeStateToDone(mFlagSyncLooping);
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::GetName(nsACString& aName) {
   aName.AssignLiteral("XMLHttpRequest");
@@ -3694,46 +3644,36 @@ nsXMLHttpRequestXPCOMifier::GetInterface
       return rv;
     }
   }
 
   return mXHR->GetInterface(aIID, aResult);
 }
 
 ArrayBufferBuilder::ArrayBufferBuilder()
-    : mMutex("ArrayBufferBuilder"),
-      mDataPtr(nullptr),
-      mCapacity(0),
-      mLength(0),
-      mMapPtr(nullptr),
-      mNeutered(false) {}
-
-ArrayBufferBuilder::~ArrayBufferBuilder() {
+    : mDataPtr(nullptr), mCapacity(0), mLength(0), mMapPtr(nullptr) {}
+
+ArrayBufferBuilder::~ArrayBufferBuilder() { reset(); }
+
+void ArrayBufferBuilder::reset() {
   if (mDataPtr) {
     JS_free(nullptr, mDataPtr);
   }
 
   if (mMapPtr) {
     JS::ReleaseMappedArrayBufferContents(mMapPtr, mLength);
     mMapPtr = nullptr;
   }
 
   mDataPtr = nullptr;
   mCapacity = mLength = 0;
 }
 
-bool ArrayBufferBuilder::SetCapacity(uint32_t aNewCap) {
-  MutexAutoLock lock(mMutex);
-  return SetCapacityInternal(aNewCap, lock);
-}
-
-bool ArrayBufferBuilder::SetCapacityInternal(
-    uint32_t aNewCap, const MutexAutoLock& aProofOfLock) {
+bool ArrayBufferBuilder::setCapacity(uint32_t aNewCap) {
   MOZ_ASSERT(!mMapPtr);
-  MOZ_ASSERT(!mNeutered);
 
   // To ensure that realloc won't free mDataPtr, use a size of 1
   // instead of 0.
   uint8_t* newdata = (uint8_t*)js_realloc(mDataPtr, aNewCap ? aNewCap : 1);
 
   if (!newdata) {
     return false;
   }
@@ -3746,21 +3686,19 @@ bool ArrayBufferBuilder::SetCapacityInte
   mCapacity = aNewCap;
   if (mLength > aNewCap) {
     mLength = aNewCap;
   }
 
   return true;
 }
 
-bool ArrayBufferBuilder::Append(const uint8_t* aNewData, uint32_t aDataLen,
+bool ArrayBufferBuilder::append(const uint8_t* aNewData, uint32_t aDataLen,
                                 uint32_t aMaxGrowth) {
-  MutexAutoLock lock(mMutex);
   MOZ_ASSERT(!mMapPtr);
-  MOZ_ASSERT(!mNeutered);
 
   CheckedUint32 neededCapacity = mLength;
   neededCapacity += aDataLen;
   if (!neededCapacity.isValid()) {
     return false;
   }
   if (mLength + aDataLen > mCapacity) {
     CheckedUint32 newcap = mCapacity;
@@ -3775,87 +3713,63 @@ bool ArrayBufferBuilder::Append(const ui
       return false;
     }
 
     // But make sure there's always enough to satisfy our request.
     if (newcap.value() < neededCapacity.value()) {
       newcap = neededCapacity;
     }
 
-    if (!SetCapacityInternal(newcap.value(), lock)) {
+    if (!setCapacity(newcap.value())) {
       return false;
     }
   }
 
   // Assert that the region isn't overlapping so we can memcpy.
   MOZ_ASSERT(
-      !AreOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength, aDataLen));
+      !areOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength, aDataLen));
 
   memcpy(mDataPtr + mLength, aNewData, aDataLen);
   mLength += aDataLen;
 
   return true;
 }
 
-uint32_t ArrayBufferBuilder::Length() {
-  MutexAutoLock lock(mMutex);
-  MOZ_ASSERT(!mNeutered);
-  return mLength;
-}
-
-uint32_t ArrayBufferBuilder::Capacity() {
-  MutexAutoLock lock(mMutex);
-  MOZ_ASSERT(!mNeutered);
-  return mCapacity;
-}
-
-JSObject* ArrayBufferBuilder::TakeArrayBuffer(JSContext* aCx) {
-  MutexAutoLock lock(mMutex);
-  MOZ_DIAGNOSTIC_ASSERT(!mNeutered);
-
+JSObject* ArrayBufferBuilder::getArrayBuffer(JSContext* aCx) {
   if (mMapPtr) {
     JSObject* obj = JS::NewMappedArrayBufferWithContents(aCx, mLength, mMapPtr);
     if (!obj) {
       JS::ReleaseMappedArrayBufferContents(mMapPtr, mLength);
     }
-
     mMapPtr = nullptr;
-    mNeutered = true;
 
     // The memory-mapped contents will be released when the ArrayBuffer becomes
     // detached or is GC'd.
     return obj;
   }
 
   // we need to check for mLength == 0, because nothing may have been
   // added
   if (mCapacity > mLength || mLength == 0) {
-    if (!SetCapacityInternal(mLength, lock)) {
+    if (!setCapacity(mLength)) {
       return nullptr;
     }
   }
 
   JSObject* obj = JS::NewArrayBufferWithContents(aCx, mLength, mDataPtr);
+  mLength = mCapacity = 0;
   if (!obj) {
-    return nullptr;
-  }
-
+    js_free(mDataPtr);
+  }
   mDataPtr = nullptr;
-  mCapacity = mLength = 0;
-
-  mNeutered = true;
   return obj;
 }
 
-nsresult ArrayBufferBuilder::MapToFileInPackage(const nsCString& aFile,
+nsresult ArrayBufferBuilder::mapToFileInPackage(const nsCString& aFile,
                                                 nsIFile* aJarFile) {
-  MutexAutoLock lock(mMutex);
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mNeutered);
-
   nsresult rv;
 
   // Open Jar file to get related attributes of target file.
   RefPtr<nsZipArchive> zip = new nsZipArchive();
   rv = zip->OpenArchive(aJarFile);
   if (NS_FAILED(rv)) {
     return rv;
   }
@@ -3880,17 +3794,17 @@ nsresult ArrayBufferBuilder::MapToFileIn
       mLength = size;
       return NS_OK;
     }
   }
   return NS_ERROR_FAILURE;
 }
 
 /* static */
-bool ArrayBufferBuilder::AreOverlappingRegions(const uint8_t* aStart1,
+bool ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
                                                uint32_t aLength1,
                                                const uint8_t* aStart2,
                                                uint32_t aLength2) {
   const uint8_t* end1 = aStart1 + aLength1;
   const uint8_t* end2 = aStart2 + aLength2;
 
   const uint8_t* max_start = aStart1 > aStart2 ? aStart1 : aStart2;
   const uint8_t* min_end = end1 < end2 ? end1 : end2;
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -68,73 +68,61 @@ class DOMString;
 class XMLHttpRequestUpload;
 class SerializedStackHolder;
 struct OriginAttributesDictionary;
 
 // A helper for building up an ArrayBuffer object's data
 // before creating the ArrayBuffer itself.  Will do doubling
 // based reallocation, up to an optional maximum growth given.
 //
-// When all the data has been appended, call GetArrayBuffer,
+// When all the data has been appended, call getArrayBuffer,
 // passing in the JSContext* for which the ArrayBuffer object
-// is to be created.  This also implicitly resets the builder.
+// is to be created.  This also implicitly resets the builder,
+// or it can be reset explicitly at any point by calling reset().
 class ArrayBufferBuilder {
+  uint8_t* mDataPtr;
+  uint32_t mCapacity;
+  uint32_t mLength;
+  void* mMapPtr;
+
  public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ArrayBufferBuilder);
+  ArrayBufferBuilder();
+  ~ArrayBufferBuilder();
 
-  ArrayBufferBuilder();
+  void reset();
 
-  // Will truncate if aNewCap is < Length().
-  bool SetCapacity(uint32_t aNewCap);
+  // Will truncate if aNewCap is < length().
+  bool setCapacity(uint32_t aNewCap);
 
   // Append aDataLen bytes from data to the current buffer.  If we
   // need to grow the buffer, grow by doubling the size up to a
   // maximum of aMaxGrowth (if given).  If aDataLen is greater than
   // what the new capacity would end up as, then grow by aDataLen.
   //
   // The data parameter must not overlap with anything beyond the
   // builder's current valid contents [0..length)
-  bool Append(const uint8_t* aNewData, uint32_t aDataLen,
+  bool append(const uint8_t* aNewData, uint32_t aDataLen,
               uint32_t aMaxGrowth = 0);
 
-  uint32_t Length();
-  uint32_t Capacity();
+  uint32_t length() { return mLength; }
+  uint32_t capacity() { return mCapacity; }
 
-  JSObject* TakeArrayBuffer(JSContext* aCx);
+  JSObject* getArrayBuffer(JSContext* aCx);
 
   // Memory mapping to starting position of file(aFile) in the zip
   // package(aJarFile).
   //
   // The file in the zip package has to be uncompressed and the starting
   // position of the file must be aligned according to array buffer settings
   // in JS engine.
-  nsresult MapToFileInPackage(const nsCString& aFile, nsIFile* aJarFile);
-
- private:
-  ~ArrayBufferBuilder();
-
-  ArrayBufferBuilder(const ArrayBufferBuilder&) = delete;
-  ArrayBufferBuilder& operator=(const ArrayBufferBuilder&) = delete;
-  ArrayBufferBuilder& operator=(const ArrayBufferBuilder&&) = delete;
-
-  bool SetCapacityInternal(uint32_t aNewCap, const MutexAutoLock& aProofOfLock);
+  nsresult mapToFileInPackage(const nsCString& aFile, nsIFile* aJarFile);
 
-  static bool AreOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1,
+ protected:
+  static bool areOverlappingRegions(const uint8_t* aStart1, uint32_t aLength1,
                                     const uint8_t* aStart2, uint32_t aLength2);
-
-  Mutex mMutex;
-
-  // All of these are protected by mMutex.
-  uint8_t* mDataPtr;
-  uint32_t mCapacity;
-  uint32_t mLength;
-  void* mMapPtr;
-
-  // This is used in assertions only.
-  bool mNeutered;
 };
 
 class nsXMLHttpRequestXPCOMifier;
 
 class RequestHeaders {
   struct RequestHeader {
     nsCString mName;
     nsCString mValue;
@@ -384,20 +372,16 @@ class XMLHttpRequestMainThread final : p
 
   virtual void GetResponse(JSContext* aCx,
                            JS::MutableHandle<JS::Value> aResponse,
                            ErrorResult& aRv) override;
 
   virtual void GetResponseText(DOMString& aResponseText,
                                ErrorResult& aRv) override;
 
-  // GetResponse* for workers:
-  already_AddRefed<BlobImpl> GetResponseBlobImpl();
-  already_AddRefed<ArrayBufferBuilder> GetResponseArrayBufferBuilder();
-  nsresult GetResponseTextForJSON(nsAString& aString);
   void GetResponseText(XMLHttpRequestStringSnapshot& aSnapshot,
                        ErrorResult& aRv);
 
   virtual Document* GetResponseXML(ErrorResult& aRv) override;
 
   virtual bool MozBackgroundRequest() const override;
 
   nsresult SetMozBackgroundRequest(bool aMozBackgroundRequest);
@@ -447,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(BlobImpl* aBlobImpl);
+  void LocalFileToBlobCompleted(Blob* aBlob);
 
  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,
@@ -608,22 +592,19 @@ 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;
 
-  RefPtr<BlobImpl> mResponseBlobImpl;
-
-  // This is the cached blob-response, created only at the first GetResponse()
-  // call.
+  // It is either a cached blob-response from the last call to GetResponse,
+  // but is also explicitly set in OnStopRequest.
   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.
@@ -729,17 +710,17 @@ class XMLHttpRequestMainThread final : p
   bool mFirstStartRequestSeen;
   bool mInLoadProgressEvent;
 
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
   nsCOMPtr<nsIChannel> mNewRedirectChannel;
 
   JS::Heap<JS::Value> mResultJSON;
 
-  RefPtr<ArrayBufferBuilder> mArrayBufferBuilder;
+  ArrayBufferBuilder mArrayBufferBuilder;
   JS::Heap<JSObject*> mResultArrayBuffer;
   bool mIsMappedArrayBuffer;
 
   void ResetResponse();
 
   bool ShouldBlockAuthPrompt();
 
   RequestHeaders mAuthorRequestHeaders;
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -39,35 +39,19 @@
 
 #include "XMLHttpRequestUpload.h"
 
 #include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 namespace dom {
 
-void XMLHttpRequestWorker::ResponseData::Unlink() {
-  mResponseBlobImpl = nullptr;
-  mResponseBlob = nullptr;
-  mResponseArrayBufferBuilder = nullptr;
-  mResponseArrayBufferValue = nullptr;
-  mResponseJSONValue.setUndefined();
-}
-
-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::ResponseData::Traverse(
-    nsCycleCollectionTraversalCallback& cb) {
-  ResponseData* tmp = this;
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
+/* static */
+void XMLHttpRequestWorker::StateData::trace(JSTracer* aTrc) {
+  JS::TraceEdge(aTrc, &mResponse, "XMLHttpRequestWorker::StateData::mResponse");
 }
 
 /**
  *  XMLHttpRequest in workers
  *
  *  XHR in workers is implemented by proxying calls/events/etc between the
  *  worker thread and an XMLHttpRequest on the main thread.  The glue
  *  object here is the Proxy, which lives on both threads.  All other objects
@@ -142,16 +126,17 @@ class Proxy final : public nsIDOMEventLi
   bool mLastUploadLengthComputable;
   bool mSeenLoadStart;
   bool mSeenUploadLoadStart;
 
   // Only touched on the main thread.
   bool mUploadEventListenersAttached;
   bool mMainThreadSeenLoadStart;
   bool mInOpen;
+  bool mArrayBufferResponseWasTransferred;
 
  public:
   Proxy(XMLHttpRequestWorker* aXHRPrivate, const ClientInfo& aClientInfo,
         const Maybe<ServiceWorkerDescriptor>& aController, bool aMozAnon,
         bool aMozSystem)
       : mWorkerPrivate(nullptr),
         mXMLHttpRequestPrivate(aXHRPrivate),
         mClientInfo(aClientInfo),
@@ -170,17 +155,18 @@ class Proxy final : public nsIDOMEventLi
         mLastUploadTotal(0),
         mIsSyncXHR(false),
         mLastLengthComputable(false),
         mLastUploadLengthComputable(false),
         mSeenLoadStart(false),
         mSeenUploadLoadStart(false),
         mUploadEventListenersAttached(false),
         mMainThreadSeenLoadStart(false),
-        mInOpen(false) {}
+        mInOpen(false),
+        mArrayBufferResponseWasTransferred(false) {}
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIDOMEVENTLISTENER
 
   bool Init();
 
   void Teardown(bool aSendUnpin);
 
@@ -455,67 +441,83 @@ class LoadStartDetectionRunnable final :
 
     return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(this));
   }
 
  private:
   ~LoadStartDetectionRunnable() { AssertIsOnMainThread(); }
 };
 
-class EventRunnable final : public MainThreadProxyRunnable {
+class EventRunnable final : public MainThreadProxyRunnable,
+                            public StructuredCloneHolder {
   nsString mType;
-  UniquePtr<XMLHttpRequestWorker::ResponseData> mResponseData;
+  nsString mResponseType;
+  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;
   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.
   JS::PersistentRooted<JSObject*> mScopeObj;
 
  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),
-        mResponseData(new XMLHttpRequestWorker::ResponseData()),
+        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),
         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),
-        mResponseData(new XMLHttpRequestWorker::ResponseData()),
+        mResponse(JS::UndefinedValue()),
         mLoaded(0),
         mTotal(0),
         mEventStreamId(aProxy->mInnerEventStreamId),
         mStatus(0),
         mReadyState(0),
         mUploadEvent(aUploadEvent),
         mProgressEvent(false),
         mLengthComputable(0),
+        mUseCachedArrayBufferResponse(false),
+        mResponseTextResult(NS_OK),
         mStatusResult(NS_OK),
+        mResponseResult(NS_OK),
         mScopeObj(RootingCx(), aScopeObj) {}
 
  private:
   ~EventRunnable() {}
 
   bool PreDispatch(WorkerPrivate* /* unused */) final;
   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
 };
@@ -1020,50 +1022,73 @@ bool EventRunnable::PreDispatch(WorkerPr
   JS::Rooted<JSObject*> scopeObj(cx, mScopeObj);
   // And reset mScopeObj now, before we have a chance to run its destructor on
   // some background thread.
   mScopeObj.reset();
 
   RefPtr<XMLHttpRequestMainThread>& xhr = mProxy->mXHR;
   MOZ_ASSERT(xhr);
 
-  ErrorResult rv;
-
-  XMLHttpRequestResponseType type = xhr->ResponseType();
+  mResponseType.AssignASCII(
+      XMLHttpRequestResponseTypeValues::GetString(xhr->ResponseType()));
 
-  // We want to take the result data only if this is available.
-  if (mType.EqualsASCII(sEventStrings[STRING_readystatechange])) {
-    switch (type) {
-      case XMLHttpRequestResponseType::_empty:
-      case XMLHttpRequestResponseType::Text: {
-        xhr->GetResponseText(mResponseData->mResponseText, rv);
-        mResponseData->mResponseResult = rv.StealNSResult();
-        break;
-      }
+  ErrorResult rv;
+  xhr->GetResponseText(mResponseText, rv);
+  mResponseTextResult = rv.StealNSResult();
 
-      case XMLHttpRequestResponseType::Blob: {
-        mResponseData->mResponseBlobImpl = xhr->GetResponseBlobImpl();
-        break;
-      }
+  if (NS_SUCCEEDED(mResponseTextResult)) {
+    mResponseResult = mResponseTextResult;
+    if (mResponseText.IsVoid()) {
+      mResponse.setNull();
+    }
+  } else {
+    JS::Rooted<JS::Value> 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<JS::Value> transferable(cx);
+        JS::Rooted<JSObject*> 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;
+            }
+          }
+        }
 
-      case XMLHttpRequestResponseType::Arraybuffer: {
-        mResponseData->mResponseArrayBufferBuilder =
-            xhr->GetResponseArrayBufferBuilder();
-        break;
+        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::Json: {
-        mResponseData->mResponseResult =
-            xhr->GetResponseTextForJSON(mResponseData->mResponseJSON);
-        break;
-      }
-
-      default:
-        MOZ_CRASH("Invalid response type");
-        break;
     }
   }
 
   mStatus = xhr->GetStatus(rv);
   mStatusResult = rv.StealNSResult();
 
   xhr->GetStatusText(mStatusText, rv);
   MOZ_ASSERT(!rv.Failed());
@@ -1118,33 +1143,66 @@ bool EventRunnable::WorkerRun(JSContext*
       mProxy->mLastUploadTotal = mTotal;
     } else {
       mProxy->mLastLengthComputable = mLengthComputable;
       mProxy->mLastLoaded = mLoaded;
       mProxy->mLastTotal = mTotal;
     }
   }
 
-  UniquePtr<XMLHttpRequestWorker::StateData> state(
-      new XMLHttpRequestWorker::StateData());
+  JS::Rooted<UniquePtr<XMLHttpRequestWorker::StateData>> state(
+      aCx, 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<JS::Value> response(aCx);
+
+        GlobalObject globalObj(aCx,
+                               aWorkerPrivate->GlobalScope()->GetWrapper());
+        nsCOMPtr<nsIGlobalObject> 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->mStatusResult = mStatusResult;
   state->mStatus = mStatus;
 
   state->mStatusText = mStatusText;
 
   state->mReadyState = mReadyState;
 
   state->mResponseURL = mResponseURL;
 
   XMLHttpRequestWorker* xhr = mProxy->mXMLHttpRequestPrivate;
-  xhr->UpdateState(std::move(state),
-                   mType.EqualsASCII(sEventStrings[STRING_readystatechange])
-                       ? std::move(mResponseData)
-                       : nullptr);
+  xhr->UpdateState(*state.get(), mUseCachedArrayBufferResponse);
 
   if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
     return true;
   }
 
   XMLHttpRequestEventTarget* target;
   if (mUploadEvent) {
     target = xhr->GetUploadObjectNoCreate();
@@ -1175,16 +1233,21 @@ bool EventRunnable::WorkerRun(JSContext*
   if (!event) {
     return false;
   }
 
   event->SetTrusted(true);
 
   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;
 }
 
 bool WorkerThreadProxySyncRunnable::MainThreadRun() {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsIEventTarget> tempTarget = mSyncLoopTarget;
 
@@ -1355,16 +1418,18 @@ void SendRunnable::RunOnMainThread(Error
     // Send() can be called more than once before failure,
     // so don't attach the upload listeners more than once.
     if (!mProxy->mUploadEventListenersAttached &&
         !mProxy->AddRemoveEventListeners(true, true)) {
       MOZ_ASSERT(false, "This should never fail!");
     }
   }
 
+  mProxy->mArrayBufferResponseWasTransferred = false;
+
   mProxy->mInnerChannelId++;
 
   mProxy->mXHR->Send(nullptr, payload, aRv);
 
   if (!aRv.Failed()) {
     mProxy->mOutstandingSendCount++;
 
     if (!mHasUploadListeners) {
@@ -1376,18 +1441,16 @@ void SendRunnable::RunOnMainThread(Error
       }
     }
   }
 }
 
 XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate)
     : mWorkerPrivate(aWorkerPrivate),
       mResponseType(XMLHttpRequestResponseType::_empty),
-      mResponseData(new ResponseData()),
-      mStateData(new StateData()),
       mTimeout(0),
       mBackgroundRequest(false),
       mWithCredentials(false),
       mCanceled(false),
       mMozAnon(false),
       mMozSystem(false),
       mMimeTypeOverride(VoidString()) {
   mWorkerPrivate->AssertIsOnWorkerThread();
@@ -1411,29 +1474,28 @@ NS_IMPL_RELEASE_INHERITED(XMLHttpRequest
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestWorker)
 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestWorker)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestWorker,
                                                   XMLHttpRequestEventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
-  tmp->mResponseData->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->mResponseData->Unlink();
+  tmp->mStateData.mResponse.setUndefined();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestWorker,
                                                XMLHttpRequestEventTarget)
-  tmp->mResponseData->Trace(aCallbacks, aClosure);
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStateData.mResponse)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 /* static */
 already_AddRefed<XMLHttpRequest> XMLHttpRequestWorker::Construct(
     const GlobalObject& aGlobal, const MozXMLHttpRequestParameters& aParams,
     ErrorResult& aRv) {
   JSContext* cx = aGlobal.Context();
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
@@ -1519,20 +1581,20 @@ void XMLHttpRequestWorker::MaybePin(Erro
 }
 
 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 == 1 && mStateData.mFlagSend) ||
+      mStateData.mReadyState == 2 || mStateData.mReadyState == 3) {
     isStateChanged = true;
-    mStateData->mReadyState = 4;
+    mStateData.mReadyState = 4;
   }
 
   if (mProxy->mSeenUploadLoadStart) {
     MOZ_ASSERT(mUpload);
 
     DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("abort"), true, aRv);
     if (aRv.Failed()) {
       return;
@@ -1663,17 +1725,17 @@ void XMLHttpRequestWorker::SendInternal(
     }
   }
 
   mProxy->mOuterChannelId++;
 
   aRunnable->SetSyncLoopTarget(syncLoopTarget);
   aRunnable->SetHaveUploadListeners(hasUploadListeners);
 
-  mStateData->mFlagSend = true;
+  mStateData.mFlagSend = true;
 
   aRunnable->Dispatch(Canceling, aRv);
   if (aRv.Failed()) {
     // Dispatch() may have spun the event loop and we may have already unrooted.
     // If so we don't want autoUnpin to try again.
     if (!mWorkerRef) {
       autoUnpin.Clear();
     }
@@ -1684,17 +1746,17 @@ void XMLHttpRequestWorker::SendInternal(
     autoUnpin.Clear();
     MOZ_ASSERT(!autoSyncLoop);
     return;
   }
 
   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
   // that this autoSyncLoop->Run() can never fail, since the StopSyncLoop call
   // for it will come from ProxyCompleteRunnable and that always passes true for
   // the second arg.
   if (!succeeded && !aRv.Failed()) {
     aRv.Throw(NS_ERROR_FAILURE);
@@ -1879,17 +1941,17 @@ void XMLHttpRequestWorker::Send(
     ErrorResult& aRv) {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
     aRv.ThrowUncatchableException();
     return;
   }
 
-  if (!mProxy || mStateData->mFlagSend) {
+  if (!mProxy || mStateData.mFlagSend) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   RefPtr<SendRunnable> sendRunnable;
 
   if (aData.IsNull()) {
     sendRunnable = new SendRunnable(mWorkerPrivate, mProxy, VoidString());
@@ -2011,34 +2073,34 @@ void XMLHttpRequestWorker::Abort(ErrorRe
 
   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 == 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);
   if (aRv.Failed()) {
     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++;
 
   RefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
   runnable->Dispatch(Canceling, aRv);
 }
 
@@ -2098,18 +2160,18 @@ void XMLHttpRequestWorker::OverrideMimeT
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
     aRv.ThrowUncatchableException();
     return;
   }
 
   // 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;
   }
 
   mMimeTypeOverride = aMimeType;
 
   if (mProxy) {
     RefPtr<OverrideMimeTypeRunnable> runnable =
@@ -2130,157 +2192,86 @@ void XMLHttpRequestWorker::SetResponseTy
 
   if (!mProxy) {
     // Open() has not been called yet. We store the responseType and we will use
     // it later in Open().
     mResponseType = aResponseType;
     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;
   }
 
   RefPtr<SetResponseTypeRunnable> runnable =
       new SetResponseTypeRunnable(mWorkerPrivate, mProxy, aResponseType);
   runnable->Dispatch(Canceling, aRv);
   if (aRv.Failed()) {
     return;
   }
 
   mResponseType = runnable->ResponseType();
 }
 
-void XMLHttpRequestWorker::GetResponse(JSContext* aCx,
+void XMLHttpRequestWorker::GetResponse(JSContext* /* unused */,
                                        JS::MutableHandle<JS::Value> aResponse,
                                        ErrorResult& aRv) {
-  if (NS_FAILED(mResponseData->mResponseResult)) {
-    aRv.Throw(mResponseData->mResponseResult);
-    return;
-  }
-
-  switch (mResponseType) {
-    case XMLHttpRequestResponseType::_empty:
-    case XMLHttpRequestResponseType::Text: {
-      JSString* str;
+  if (NS_SUCCEEDED(mStateData.mResponseTextResult) &&
+      mStateData.mResponse.isUndefined()) {
+    MOZ_ASSERT(NS_SUCCEEDED(mStateData.mResponseResult));
 
-      if (mResponseData->mResponseText.IsEmpty()) {
-        aResponse.set(JS_GetEmptyStringValue(aCx));
-        return;
-      }
+    if (mStateData.mResponseText.IsEmpty()) {
+      mStateData.mResponse =
+          JS_GetEmptyStringValue(mWorkerPrivate->GetJSContext());
+    } else {
+      XMLHttpRequestStringSnapshotReaderHelper helper(mStateData.mResponseText);
 
-      XMLHttpRequestStringSnapshotReaderHelper helper(
-          mResponseData->mResponseText);
+      JSString* str = JS_NewUCStringCopyN(mWorkerPrivate->GetJSContext(),
+                                          helper.Buffer(), helper.Length());
 
-      str = JS_NewUCStringCopyN(aCx, helper.Buffer(), helper.Length());
       if (!str) {
         aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
         return;
       }
 
-      aResponse.setString(str);
-      return;
+      mStateData.mResponse.setString(str);
     }
-
-    case XMLHttpRequestResponseType::Arraybuffer: {
-      if (!mResponseData->mResponseArrayBufferBuilder) {
-        aResponse.setNull();
-        return;
-      }
-
-      if (!mResponseData->mResponseArrayBufferValue) {
-        mResponseData->mResponseArrayBufferValue =
-            mResponseData->mResponseArrayBufferBuilder->TakeArrayBuffer(aCx);
-        if (!mResponseData->mResponseArrayBufferValue) {
-          aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-          return;
-        }
-      }
-
-      aResponse.setObject(*mResponseData->mResponseArrayBufferValue);
-      return;
-    }
-
-    case XMLHttpRequestResponseType::Blob: {
-      if (!mResponseData->mResponseBlobImpl) {
-        aResponse.setNull();
-        return;
-      }
-
-      if (!mResponseData->mResponseBlob) {
-        mResponseData->mResponseBlob =
-            Blob::Create(GetOwnerGlobal(), mResponseData->mResponseBlobImpl);
-      }
+  }
 
-      if (!GetOrCreateDOMReflector(aCx, mResponseData->mResponseBlob,
-                                   aResponse)) {
-        aResponse.setNull();
-      }
-
-      return;
-    }
-
-    case XMLHttpRequestResponseType::Json: {
-      if (mResponseData->mResponseJSON.IsVoid()) {
-        aResponse.setNull();
-        return;
-      }
-
-      if (mResponseData->mResponseJSONValue.isUndefined()) {
-        // The Unicode converter has already zapped the BOM if there was one
-        JS::Rooted<JS::Value> value(aCx);
-        if (!JS_ParseJSON(aCx, mResponseData->mResponseJSON.BeginReading(),
-                          mResponseData->mResponseJSON.Length(), &value)) {
-          JS_ClearPendingException(aCx);
-          mResponseData->mResponseJSONValue.setNull();
-        } else {
-          mResponseData->mResponseJSONValue = value;
-        }
-
-        mResponseData->mResponseJSON.Truncate();
-      }
-
-      aResponse.set(mResponseData->mResponseJSONValue);
-      return;
-    }
-
-    default:
-      MOZ_CRASH("Invalid type");
-      aResponse.setNull();
-      return;
-  }
+  aRv = mStateData.mResponseResult;
+  aResponse.set(mStateData.mResponse);
 }
 
 void XMLHttpRequestWorker::GetResponseText(DOMString& aResponseText,
                                            ErrorResult& aRv) {
-  MOZ_DIAGNOSTIC_ASSERT(mResponseData);
-
-  if (mResponseType != XMLHttpRequestResponseType::_empty &&
-      mResponseType != XMLHttpRequestResponseType::Text) {
-    aRv.Throw(
-        NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT);
+  aRv = mStateData.mResponseTextResult;
+  if (aRv.Failed()) {
     return;
   }
 
-  if (!mResponseData->mResponseText.GetAsString(aResponseText)) {
+  if (!mStateData.mResponseText.GetAsString(aResponseText)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 }
 
-void XMLHttpRequestWorker::UpdateState(
-    UniquePtr<StateData>&& aStateData,
-    UniquePtr<ResponseData>&& aResponseData) {
-  mStateData = std::move(aStateData);
+void XMLHttpRequestWorker::UpdateState(const StateData& aStateData,
+                                       bool aUseCachedArrayBufferResponse) {
+  if (aUseCachedArrayBufferResponse) {
+    MOZ_ASSERT(mStateData.mResponse.isObject() &&
+               JS::IsArrayBufferObject(&mStateData.mResponse.toObject()));
 
-  UniquePtr<ResponseData> responseData = std::move(aResponseData);
-  if (responseData) {
-    mResponseData = std::move(responseData);
+    JS::Rooted<JS::Value> response(mWorkerPrivate->GetJSContext(),
+                                   mStateData.mResponse);
+    mStateData = aStateData;
+    mStateData.mResponse = response;
+  } else {
+    mStateData = aStateData;
   }
 
   XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/xhr/XMLHttpRequestWorker.h
+++ b/dom/xhr/XMLHttpRequestWorker.h
@@ -17,67 +17,47 @@ namespace dom {
 class Proxy;
 class DOMString;
 class SendRunnable;
 class StrongWorkerRef;
 class WorkerPrivate;
 
 class XMLHttpRequestWorker final : public XMLHttpRequest {
  public:
-  // This defines the xhr.response value.
-  struct ResponseData {
-    nsresult mResponseResult;
-
-    // responseType is empty or text.
+  struct StateData {
     XMLHttpRequestStringSnapshot mResponseText;
-
-    // responseType is blob
-    RefPtr<BlobImpl> mResponseBlobImpl;
-    RefPtr<Blob> mResponseBlob;
-
-    // responseType is arrayBuffer;
-    RefPtr<ArrayBufferBuilder> mResponseArrayBufferBuilder;
-    JS::Heap<JSObject*> mResponseArrayBufferValue;
-
-    // responseType is json
-    nsString mResponseJSON;
-    JS::Heap<JS::Value> mResponseJSONValue;
-
-    ResponseData()
-        : mResponseResult(NS_OK),
-          mResponseArrayBufferValue(nullptr),
-          mResponseJSONValue(JS::UndefinedValue()) {}
-
-    void Unlink();
-    void Trace(const TraceCallbacks& aCallbacks, void* aClosure);
-    void Traverse(nsCycleCollectionTraversalCallback& aCb);
-  };
-
-  struct StateData {
     nsString mResponseURL;
     uint32_t mStatus;
     nsCString mStatusText;
     uint16_t mReadyState;
     bool mFlagSend;
+    JS::Heap<JS::Value> mResponse;
+    nsresult mResponseTextResult;
     nsresult mStatusResult;
+    nsresult mResponseResult;
 
     StateData()
-        : mStatus(0), mReadyState(0), mFlagSend(false), mStatusResult(NS_OK) {}
+        : mStatus(0),
+          mReadyState(0),
+          mFlagSend(false),
+          mResponse(JS::UndefinedValue()),
+          mResponseTextResult(NS_OK),
+          mStatusResult(NS_OK),
+          mResponseResult(NS_OK) {}
+
+    void trace(JSTracer* trc);
   };
 
  private:
   RefPtr<XMLHttpRequestUpload> mUpload;
   WorkerPrivate* mWorkerPrivate;
   RefPtr<StrongWorkerRef> mWorkerRef;
   RefPtr<Proxy> mProxy;
-
   XMLHttpRequestResponseType mResponseType;
-
-  UniquePtr<ResponseData> mResponseData;
-  UniquePtr<StateData> mStateData;
+  StateData mStateData;
 
   uint32_t mTimeout;
 
   bool mBackgroundRequest;
   bool mWithCredentials;
   bool mCanceled;
 
   bool mMozAnon;
@@ -92,17 +72,17 @@ class XMLHttpRequestWorker final : publi
 
   static already_AddRefed<XMLHttpRequest> Construct(
       const GlobalObject& aGlobal, const MozXMLHttpRequestParameters& aParams,
       ErrorResult& aRv);
 
   void Unpin();
 
   virtual uint16_t ReadyState() const override {
-    return mStateData->mReadyState;
+    return mStateData.mReadyState;
   }
 
   virtual void Open(const nsACString& aMethod, const nsAString& aUrl,
                     ErrorResult& aRv) override {
     Open(aMethod, aUrl, true, Optional<nsAString>(), Optional<nsAString>(),
          aRv);
   }
 
@@ -156,27 +136,27 @@ class XMLHttpRequestWorker final : publi
   virtual void SendInputStream(nsIInputStream* aInputStream,
                                ErrorResult& aRv) override {
     MOZ_CRASH("nsIInputStream is not a valid argument for XHR in workers.");
   }
 
   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,
                                  nsACString& aResponseHeader,
                                  ErrorResult& aRv) override;
 
   virtual void GetAllResponseHeaders(nsACString& aResponseHeaders,
                                      ErrorResult& aRv) override;
@@ -210,18 +190,23 @@ class XMLHttpRequestWorker final : publi
 
   virtual void SetOriginAttributes(
       const mozilla::dom::OriginAttributesDictionary& aAttrs) override {
     MOZ_CRASH("This method cannot be called on workers.");
   }
 
   XMLHttpRequestUpload* GetUploadObjectNoCreate() const { return mUpload; }
 
-  void UpdateState(UniquePtr<StateData>&& aStateData,
-                   UniquePtr<ResponseData>&& aResponseData);
+  void UpdateState(const StateData& aStateData,
+                   bool aUseCachedArrayBufferResponse);
+
+  void NullResponseText() {
+    mStateData.mResponseText.SetVoid();
+    mStateData.mResponse.setNull();
+  }
 
   virtual uint16_t ErrorCode() const override {
     return 0;  // eOK
   }
 
   virtual bool MozAnon() const override { return mMozAnon; }
 
   virtual bool MozSystem() const override { return mMozSystem; }
--- a/dom/xhr/tests/mochitest.ini
+++ b/dom/xhr/tests/mochitest.ini
@@ -108,10 +108,8 @@ support-files = test_XHR_timeout.js
 [test_XHRSendData.html]
 [test_sync_xhr_document_write_with_iframe.html]
 skip-if = toolkit == "android" && debug && !is_fennec
 [test_nestedSyncXHR.html]
 [test_event_listener_leaks.html]
 skip-if = (os == "win" && processor == "aarch64") #bug 1535784
 [test_worker_xhr_doubleSend.html]
 support-files = xhr_worker_doubleSend.js
-[test_sharedworker_xhr.html]
-support-files = xhr_sharedworker.js
deleted file mode 100644
--- a/dom/xhr/tests/test_sharedworker_xhr.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for SharedWorker Threads XHR</title>
-  <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<script class="testbody" type="text/javascript">
-
-var worker = new SharedWorker("xhr_sharedworker.js");
-
-worker.port.onmessage = function(event) {
-  is(event.data, "done", "Got correct result");
-  SimpleTest.finish();
-}
-worker.port.postMessage("worker_testXHR.txt");
-
-SimpleTest.waitForExplicitFinish();
-
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/xhr/tests/xhr_sharedworker.js
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-/* eslint-env worker */
-onconnect = e => {
-  e.ports[0].onmessage = event => {
-    const url = event.data;
-
-    var xhr = new XMLHttpRequest();
-    xhr.open("GET", url, false);
-    xhr.send();
-
-    const refText = xhr.responseText;
-
-    function getResponse(type) {
-      xhr = new XMLHttpRequest();
-      xhr.open("GET", url, false);
-      if (type !== undefined) {
-        xhr.responseType = type;
-      }
-      xhr.send();
-      return xhr.response;
-    }
-
-    if (getResponse() != refText) {
-      throw new Error("unset responseType failed");
-    }
-
-    if (getResponse("") != refText) {
-      throw new Error("'' responseType failed");
-    }
-
-    if (getResponse("text") != refText) {
-      throw new Error("'text' responseType failed");
-    }
-
-    var array = new Uint8Array(getResponse("arraybuffer"));
-    if (String.fromCharCode.apply(String, array) != refText) {
-      throw new Error("'arraybuffer' responseType failed");
-    }
-
-    var blob = getResponse("blob");
-    if (new FileReaderSync().readAsText(blob) != refText) {
-      throw new Error("'blob' responseType failed");
-    }
-
-    // Make sure that we get invalid state exceptions when getting the wrong
-    // property.
-
-    function testResponseTextException(type) {
-      xhr = new XMLHttpRequest();
-      xhr.open("GET", url, false);
-      xhr.responseType = type;
-      xhr.send();
-
-      var exception;
-
-      try {
-        xhr.responseText;
-      } catch (ex) {
-        exception = ex;
-      }
-
-      if (!exception) {
-        throw new Error(
-          `Failed to throw when getting responseText on ${type} type`
-        );
-      }
-
-      if (exception.name != "InvalidStateError") {
-        throw new Error(
-          `Unexpected error when getting responseText on ${type} type`
-        );
-      }
-
-      if (exception.code != DOMException.INVALID_STATE_ERR) {
-        throw new Error(
-          `Unexpected error code when getting responseText on ${type} type`
-        );
-      }
-    }
-
-    testResponseTextException("arraybuffer");
-    testResponseTextException("blob");
-
-    // Make sure "document" works, but returns text.
-    xhr = new XMLHttpRequest();
-
-    if (xhr.responseType != "") {
-      throw new Error("Default value for responseType is wrong!");
-    }
-
-    xhr.open("GET", url, false);
-    xhr.responseType = "document";
-    xhr.send();
-
-    if (xhr.responseText != refText) {
-      throw new Error("'document' type not working correctly");
-    }
-
-    e.ports[0].postMessage("done");
-  };
-};