Bug 1273279 - Changes in preparation for FlyWeb landing. Change InternalResponse handling to track body size. r=baku f=bkelly
authorJonas Sicking <jonas@sicking.cc>
Wed, 01 Jun 2016 17:02:29 -0400
changeset 341026 9020ac239e2a971a5fb5a93df50b89879b713912
parent 341025 7217724754491b5d40002d35012b73f670f76498
child 341027 724b6e4ac4e45247463de0f6050f356c5260781f
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1273279
milestone49.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 1273279 - Changes in preparation for FlyWeb landing. Change InternalResponse handling to track body size. r=baku f=bkelly
dom/cache/TypeUtils.cpp
dom/fetch/Fetch.cpp
dom/fetch/Fetch.h
dom/fetch/FetchDriver.cpp
dom/fetch/InternalResponse.cpp
dom/fetch/InternalResponse.h
dom/fetch/Request.cpp
dom/fetch/Response.cpp
dom/fetch/Response.h
dom/workers/ScriptLoader.cpp
dom/workers/ServiceWorkerScriptCache.cpp
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -276,17 +276,17 @@ TypeUtils::ToResponse(const CacheRespons
 
   ir->InitChannelInfo(aIn.channelInfo());
   if (aIn.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
     UniquePtr<mozilla::ipc::PrincipalInfo> info(new mozilla::ipc::PrincipalInfo(aIn.principalInfo().get_PrincipalInfo()));
     ir->SetPrincipalInfo(Move(info));
   }
 
   nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
-  ir->SetBody(stream);
+  ir->SetBody(stream, InternalResponse::UNKNOWN_BODY_SIZE);
 
   switch (aIn.type())
   {
     case ResponseType::Basic:
       ir = ir->BasicResponse();
       break;
     case ResponseType::Cors:
       ir = ir->CORSResponse();
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -444,67 +444,80 @@ WorkerFetchResolver::OnResponseEnd()
       NS_WARNING("Failed to dispatch WorkerFetchResponseEndControlRunnable");
     }
   }
 }
 
 namespace {
 nsresult
 ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,
-                       nsIInputStream** aStream)
+                       nsIInputStream** aStream,
+                       uint64_t& aContentLength)
 {
   aBuffer.ComputeLengthAndData();
+  aContentLength = aBuffer.Length();
   //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
   return NS_NewByteInputStream(aStream,
                                reinterpret_cast<char*>(aBuffer.Data()),
                                aBuffer.Length(), NS_ASSIGNMENT_COPY);
 }
 
 nsresult
 ExtractFromArrayBufferView(const ArrayBufferView& aBuffer,
-                           nsIInputStream** aStream)
+                           nsIInputStream** aStream,
+                           uint64_t& aContentLength)
 {
   aBuffer.ComputeLengthAndData();
+  aContentLength = aBuffer.Length();
   //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
   return NS_NewByteInputStream(aStream,
                                reinterpret_cast<char*>(aBuffer.Data()),
                                aBuffer.Length(), NS_ASSIGNMENT_COPY);
 }
 
 nsresult
-ExtractFromBlob(const Blob& aBlob, nsIInputStream** aStream,
-                nsCString& aContentType)
+ExtractFromBlob(const Blob& aBlob,
+                nsIInputStream** aStream,
+                nsCString& aContentType,
+                uint64_t& aContentLength)
 {
   RefPtr<BlobImpl> impl = aBlob.Impl();
   ErrorResult rv;
+  aContentLength = impl->GetSize(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
   impl->GetInternalStream(aStream, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   nsAutoString type;
   impl->GetType(type);
   aContentType = NS_ConvertUTF16toUTF8(type);
   return NS_OK;
 }
 
 nsresult
-ExtractFromFormData(FormData& aFormData, nsIInputStream** aStream,
-                    nsCString& aContentType)
+ExtractFromFormData(FormData& aFormData,
+                    nsIInputStream** aStream,
+                    nsCString& aContentType,
+                    uint64_t& aContentLength)
 {
-  uint64_t unusedContentLength;
   nsAutoCString unusedCharset;
-  return aFormData.GetSendInfo(aStream, &unusedContentLength,
+  return aFormData.GetSendInfo(aStream, &aContentLength,
                                aContentType, unusedCharset);
 }
 
 nsresult
 ExtractFromUSVString(const nsString& aStr,
                      nsIInputStream** aStream,
-                     nsCString& aContentType)
+                     nsCString& aContentType,
+                     uint64_t& aContentLength)
 {
   nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
   if (!encoder) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   int32_t destBufferLen;
   nsresult rv = encoder->GetMaxLength(aStr.get(), aStr.Length(), &destBufferLen);
@@ -524,90 +537,106 @@ ExtractFromUSVString(const nsString& aSt
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(outLen <= destBufferLen);
   encoded.SetLength(outLen);
 
   aContentType = NS_LITERAL_CSTRING("text/plain;charset=UTF-8");
+  aContentLength = outLen;
 
   return NS_NewCStringInputStream(aStream, encoded);
 }
 
 nsresult
 ExtractFromURLSearchParams(const URLSearchParams& aParams,
                            nsIInputStream** aStream,
-                           nsCString& aContentType)
+                           nsCString& aContentType,
+                           uint64_t& aContentLength)
 {
   nsAutoString serialized;
   aParams.Stringify(serialized);
   aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8");
+  aContentLength = serialized.Length();
   return NS_NewCStringInputStream(aStream, NS_ConvertUTF16toUTF8(serialized));
 }
 } // namespace
 
 nsresult
 ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
-                          nsCString& aContentType)
+                          nsCString& aContentType,
+                          uint64_t& aContentLength)
 {
   MOZ_ASSERT(aStream);
 
   if (aBodyInit.IsArrayBuffer()) {
     const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
-    return ExtractFromArrayBuffer(buf, aStream);
-  } else if (aBodyInit.IsArrayBufferView()) {
+    return ExtractFromArrayBuffer(buf, aStream, aContentLength);
+  }
+  if (aBodyInit.IsArrayBufferView()) {
     const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
-    return ExtractFromArrayBufferView(buf, aStream);
-  } else if (aBodyInit.IsBlob()) {
+    return ExtractFromArrayBufferView(buf, aStream, aContentLength);
+  }
+  if (aBodyInit.IsBlob()) {
     const Blob& blob = aBodyInit.GetAsBlob();
-    return ExtractFromBlob(blob, aStream, aContentType);
-  } else if (aBodyInit.IsFormData()) {
+    return ExtractFromBlob(blob, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsFormData()) {
     FormData& form = aBodyInit.GetAsFormData();
-    return ExtractFromFormData(form, aStream, aContentType);
-  } else if (aBodyInit.IsUSVString()) {
+    return ExtractFromFormData(form, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsUSVString()) {
     nsAutoString str;
     str.Assign(aBodyInit.GetAsUSVString());
-    return ExtractFromUSVString(str, aStream, aContentType);
-  } else if (aBodyInit.IsURLSearchParams()) {
+    return ExtractFromUSVString(str, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsURLSearchParams()) {
     URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
-    return ExtractFromURLSearchParams(params, aStream, aContentType);
+    return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength);
   }
 
   NS_NOTREACHED("Should never reach here");
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
-                          nsCString& aContentType)
+                          nsCString& aContentType,
+                          uint64_t& aContentLength)
 {
   MOZ_ASSERT(aStream);
+  MOZ_ASSERT(!*aStream);
 
   if (aBodyInit.IsArrayBuffer()) {
     const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
-    return ExtractFromArrayBuffer(buf, aStream);
-  } else if (aBodyInit.IsArrayBufferView()) {
+    return ExtractFromArrayBuffer(buf, aStream, aContentLength);
+  }
+  if (aBodyInit.IsArrayBufferView()) {
     const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
-    return ExtractFromArrayBufferView(buf, aStream);
-  } else if (aBodyInit.IsBlob()) {
+    return ExtractFromArrayBufferView(buf, aStream, aContentLength);
+  }
+  if (aBodyInit.IsBlob()) {
     const Blob& blob = aBodyInit.GetAsBlob();
-    return ExtractFromBlob(blob, aStream, aContentType);
-  } else if (aBodyInit.IsFormData()) {
+    return ExtractFromBlob(blob, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsFormData()) {
     FormData& form = aBodyInit.GetAsFormData();
-    return ExtractFromFormData(form, aStream, aContentType);
-  } else if (aBodyInit.IsUSVString()) {
+    return ExtractFromFormData(form, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsUSVString()) {
     nsAutoString str;
     str.Assign(aBodyInit.GetAsUSVString());
-    return ExtractFromUSVString(str, aStream, aContentType);
-  } else if (aBodyInit.IsURLSearchParams()) {
+    return ExtractFromUSVString(str, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsURLSearchParams()) {
     URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
-    return ExtractFromURLSearchParams(params, aStream, aContentType);
+    return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength);
   }
 
   NS_NOTREACHED("Should never reach here");
   return NS_ERROR_FAILURE;
 }
 
 namespace {
 /*
--- a/dom/fetch/Fetch.h
+++ b/dom/fetch/Fetch.h
@@ -45,25 +45,27 @@ UpdateRequestReferrer(nsIGlobalObject* a
 /*
  * Creates an nsIInputStream based on the fetch specifications 'extract a byte
  * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
  * Stores content type in out param aContentType.
  */
 nsresult
 ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
-                          nsCString& aContentType);
+                          nsCString& aContentType,
+                          uint64_t& aContentLength);
 
 /*
  * Non-owning version.
  */
 nsresult
 ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
-                          nsCString& aContentType);
+                          nsCString& aContentType,
+                          uint64_t& aContentLength);
 
 template <class Derived> class FetchBodyFeature;
 
 /*
  * FetchBody's body consumption uses nsIInputStreamPump to read from the
  * underlying stream to a block of memory, which is then adopted by
  * ContinueConsumeBody() and converted to the right type based on the JS
  * function called.
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -483,16 +483,20 @@ FetchDriver::OnStartRequest(nsIRequest* 
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
 
   // On a successful redirect we perform the following substeps of HTTP Fetch,
   // step 5, "redirect status", step 11.
 
   bool foundOpaqueRedirect = false;
 
+  int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
+  rv = channel->GetContentLength(&contentLength);
+  MOZ_ASSERT_IF(NS_FAILED(rv), contentLength == InternalResponse::UNKNOWN_BODY_SIZE);
+
   if (httpChannel) {
     uint32_t responseStatus;
     httpChannel->GetResponseStatus(&responseStatus);
 
     if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
       if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
         FailWithNetworkError();
         return NS_BINDING_FAILED;
@@ -507,16 +511,27 @@ FetchDriver::OnStartRequest(nsIRequest* 
 
     response = new InternalResponse(responseStatus, statusText);
 
     RefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
     rv = httpChannel->VisitResponseHeaders(visitor);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       NS_WARNING("Failed to visit all headers.");
     }
+
+    // If Content-Encoding or Transfer-Encoding headers are set, then the actual
+    // Content-Length (which refer to the decoded data) is obscured behind the encodings.
+    ErrorResult result;
+    if (response->Headers()->Has(NS_LITERAL_CSTRING("content-encoding"), result) ||
+        response->Headers()->Has(NS_LITERAL_CSTRING("transfer-encoding"), result)) {
+      NS_WARNING("Cannot know response Content-Length due to presence of Content-Encoding "
+                 "or Transfer-Encoding headers.");
+      contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
+    }
+    MOZ_ASSERT(!result.Failed());
   } else {
     response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
 
     ErrorResult result;
     nsAutoCString contentType;
     rv = channel->GetContentType(contentType);
     if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
       nsAutoCString contentCharset;
@@ -526,19 +541,17 @@ FetchDriver::OnStartRequest(nsIRequest* 
       }
 
       response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
                                   contentType,
                                   result);
       MOZ_ASSERT(!result.Failed());
     }
 
-    int64_t contentLength;
-    rv = channel->GetContentLength(&contentLength);
-    if (NS_SUCCEEDED(rv) && contentLength) {
+    if (contentLength > 0) {
       nsAutoCString contentLenStr;
       contentLenStr.AppendInt(contentLength);
       response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
                                   contentLenStr,
                                   result);
       MOZ_ASSERT(!result.Failed());
     }
   }
@@ -556,17 +569,17 @@ FetchDriver::OnStartRequest(nsIRequest* 
                   UINT32_MAX /* infinite pipe */,
                   true /* non-blocking input, otherwise you deadlock */,
                   false /* blocking output, since the pipe is 'in'finite */ );
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     // Cancel request.
     return rv;
   }
-  response->SetBody(pipeInputStream);
+  response->SetBody(pipeInputStream, contentLength);
 
   response->InitChannelInfo(channel);
 
   nsCOMPtr<nsIURI> channelURI;
   rv = channel->GetURI(getter_AddRefs(channelURI));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     // Cancel request.
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -16,16 +16,17 @@
 namespace mozilla {
 namespace dom {
 
 InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
   : mType(ResponseType::Default)
   , mStatus(aStatus)
   , mStatusText(aStatusText)
   , mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
+  , mBodySize(UNKNOWN_BODY_SIZE)
 {
 }
 
 InternalResponse::~InternalResponse()
 {
 }
 
 already_AddRefed<InternalResponse>
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -171,47 +171,60 @@ public:
     if (mWrappedResponse) {
       return mWrappedResponse->Headers();
     };
 
     return Headers();
   }
 
   void
-  GetUnfilteredBody(nsIInputStream** aStream)
+  GetUnfilteredBody(nsIInputStream** aStream, int64_t* aBodySize = nullptr)
   {
     if (mWrappedResponse) {
       MOZ_ASSERT(!mBody);
-      return mWrappedResponse->GetBody(aStream);
+      return mWrappedResponse->GetBody(aStream, aBodySize);
     }
     nsCOMPtr<nsIInputStream> stream = mBody;
     stream.forget(aStream);
+    if (aBodySize) {
+      *aBodySize = mBodySize;
+    }
   }
 
   void
-  GetBody(nsIInputStream** aStream)
+  GetBody(nsIInputStream** aStream, int64_t* aBodySize = nullptr)
   {
     if (Type() == ResponseType::Opaque ||
         Type() == ResponseType::Opaqueredirect) {
       *aStream = nullptr;
+      if (aBodySize) {
+        *aBodySize = UNKNOWN_BODY_SIZE;
+      }
       return;
     }
 
-    return GetUnfilteredBody(aStream);
+    return GetUnfilteredBody(aStream, aBodySize);
   }
 
   void
-  SetBody(nsIInputStream* aBody)
+  SetBody(nsIInputStream* aBody, int64_t aBodySize)
   {
     if (mWrappedResponse) {
-      return mWrappedResponse->SetBody(aBody);
+      return mWrappedResponse->SetBody(aBody, aBodySize);
     }
     // A request's body may not be reset once set.
     MOZ_ASSERT(!mBody);
+    MOZ_ASSERT(mBodySize == UNKNOWN_BODY_SIZE);
+    // Check arguments.
+    MOZ_ASSERT(aBodySize == UNKNOWN_BODY_SIZE || aBodySize >= 0);
+    // If body is not given, then size must be unknown.
+    MOZ_ASSERT_IF(!aBody, aBodySize == UNKNOWN_BODY_SIZE);
+
     mBody = aBody;
+    mBodySize = aBodySize;
   }
 
   void
   InitChannelInfo(nsIChannel* aChannel)
   {
     mChannelInfo.InitFromChannel(aChannel);
   }
 
@@ -271,16 +284,20 @@ private:
   // A response has an associated url list (a list of zero or more fetch URLs).
   // Unless stated otherwise, it is the empty list. The current url is the last
   // element in mURLlist
   nsTArray<nsCString> mURLList;
   const uint16_t mStatus;
   const nsCString mStatusText;
   RefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBody;
+  int64_t mBodySize;
+public:
+  static const int64_t UNKNOWN_BODY_SIZE = -1;
+private:
   ChannelInfo mChannelInfo;
   UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
 
   // For filtered responses.
   // Cache, and SW interception should always serialize/access the underlying
   // unfiltered headers and when deserializing, create an InternalResponse
   // with the unfiltered headers followed by wrapping it.
   RefPtr<InternalResponse> mWrappedResponse;
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -33,16 +33,19 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 Request::Request(nsIGlobalObject* aOwner, InternalRequest* aRequest)
   : FetchBody<Request>()
   , mOwner(aOwner)
   , mRequest(aRequest)
 {
+  MOZ_ASSERT(aRequest->Headers()->Guard() == HeadersGuardEnum::Immutable ||
+             aRequest->Headers()->Guard() == HeadersGuardEnum::Request ||
+             aRequest->Headers()->Guard() == HeadersGuardEnum::Request_no_cors);
   SetMimeType();
 }
 
 Request::~Request()
 {
 }
 
 // static
@@ -508,18 +511,21 @@ Request::Constructor(const GlobalObject&
   if (aInit.mBody.WasPassed()) {
     const Nullable<OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& bodyInitNullable =
       aInit.mBody.Value();
     if (!bodyInitNullable.IsNull()) {
       const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit =
         bodyInitNullable.Value();
       nsCOMPtr<nsIInputStream> stream;
       nsAutoCString contentType;
+      uint64_t contentLengthUnused;
       aRv = ExtractByteStreamFromBody(bodyInit,
-                                      getter_AddRefs(stream), contentType);
+                                      getter_AddRefs(stream),
+                                      contentType,
+                                      contentLengthUnused);
       if (NS_WARN_IF(aRv.Failed())) {
         return nullptr;
       }
 
       temporaryBody = stream;
 
       if (!contentType.IsVoid() &&
           !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
--- a/dom/fetch/Response.cpp
+++ b/dom/fetch/Response.cpp
@@ -34,16 +34,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse)
   : FetchBody<Response>()
   , mOwner(aGlobal)
   , mInternalResponse(aInternalResponse)
 {
+  MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable ||
+             aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response);
   SetMimeType();
 }
 
 Response::~Response()
 {
 }
 
 /* static */ already_AddRefed<Response>
@@ -200,18 +202,25 @@ Response::Constructor(const GlobalObject
   if (aBody.WasPassed()) {
     if (aInit.mStatus == 204 || aInit.mStatus == 205 || aInit.mStatus == 304) {
       aRv.ThrowTypeError<MSG_RESPONSE_NULL_STATUS_WITH_BODY>();
       return nullptr;
     }
 
     nsCOMPtr<nsIInputStream> bodyStream;
     nsCString contentType;
-    aRv = ExtractByteStreamFromBody(aBody.Value(), getter_AddRefs(bodyStream), contentType);
-    internalResponse->SetBody(bodyStream);
+    uint64_t bodySize = 0;
+    aRv = ExtractByteStreamFromBody(aBody.Value(),
+                                    getter_AddRefs(bodyStream),
+                                    contentType,
+                                    bodySize);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+    internalResponse->SetBody(bodyStream, bodySize);
 
     if (!contentType.IsVoid() &&
         !internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
       // Ignore Append() failing here.
       ErrorResult error;
       internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, error);
       error.SuppressException();
     }
@@ -248,20 +257,20 @@ Response::CloneUnfiltered(ErrorResult& a
 
   RefPtr<InternalResponse> clone = mInternalResponse->Clone();
   RefPtr<InternalResponse> ir = clone->Unfiltered();
   RefPtr<Response> ref = new Response(mOwner, ir);
   return ref.forget();
 }
 
 void
-Response::SetBody(nsIInputStream* aBody)
+Response::SetBody(nsIInputStream* aBody, int64_t aBodySize)
 {
   MOZ_ASSERT(!BodyUsed());
-  mInternalResponse->SetBody(aBody);
+  mInternalResponse->SetBody(aBody, aBodySize);
 }
 
 already_AddRefed<InternalResponse>
 Response::GetInternalResponse() const
 {
   RefPtr<InternalResponse> ref = mInternalResponse;
   return ref.forget();
 }
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -129,17 +129,17 @@ public:
 
   already_AddRefed<Response>
   Clone(ErrorResult& aRv) const;
 
   already_AddRefed<Response>
   CloneUnfiltered(ErrorResult& aRv) const;
 
   void
-  SetBody(nsIInputStream* aBody);
+  SetBody(nsIInputStream* aBody, int64_t aBodySize);
 
   already_AddRefed<InternalResponse>
   GetInternalResponse() const;
 
 private:
   ~Response();
 
   nsCOMPtr<nsIGlobalObject> mOwner;
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -652,17 +652,17 @@ private:
     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
 
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     MOZ_ASSERT(channel == loadInfo.mChannel);
 
     // We synthesize the result code, but its never exposed to content.
     RefPtr<mozilla::dom::InternalResponse> ir =
       new mozilla::dom::InternalResponse(200, NS_LITERAL_CSTRING("OK"));
-    ir->SetBody(loadInfo.mCacheReadStream);
+    ir->SetBody(loadInfo.mCacheReadStream, InternalResponse::UNKNOWN_BODY_SIZE);
     // Drop our reference to the stream now that we've passed it along, so it
     // doesn't hang around once the cache is done with it and keep data alive.
     loadInfo.mCacheReadStream = nullptr;
 
     // Set the channel info of the channel on the response so that it's
     // saved in the cache.
     ir->InitChannelInfo(channel);
 
--- a/dom/workers/ServiceWorkerScriptCache.cpp
+++ b/dom/workers/ServiceWorkerScriptCache.cpp
@@ -542,17 +542,17 @@ private:
     if (NS_WARN_IF(result.Failed())) {
       MOZ_ASSERT(!result.IsErrorWithMessage());
       Fail(result.StealNSResult());
       return;
     }
 
     RefPtr<InternalResponse> ir =
       new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
-    ir->SetBody(body);
+    ir->SetBody(body, mCN->Buffer().Length());
 
     ir->InitChannelInfo(mChannelInfo);
     if (mPrincipalInfo) {
       ir->SetPrincipalInfo(Move(mPrincipalInfo));
     }
 
     RefPtr<Response> response = new Response(aCache->GetGlobalObject(), ir);