[Fetch API] Response constructor should be able to take a ReadableStream as body
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Jul 2016 06:38:15 +0000 (06:38 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Jul 2016 06:38:15 +0000 (06:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=159804

Patch by Youenn Fablet <youenn@apple.com> on 2016-07-26
Reviewed by Alex Christensen.

LayoutTests/imported/w3c:

* web-platform-tests/fetch/api/response/response-consume-empty-expected.txt:
* web-platform-tests/fetch/api/response/response-consume-expected.txt:
* web-platform-tests/fetch/api/response/response-consume.html: Updating test to exercice Response coonstructor with a ReadableStream.

Source/WebCore:

Covered by existing and updated tests.

Introduced FetchBodyConsumer to encapsulate the code responsible to adapt FetchBody internal data to the requests made by user scripts.
This refactoring eases the handling of internal data coming from ReadableStream.

FetchLoader is now delegating conversion from the data to its m_consumer field.
If m_consumer is null, FetchLoader calls FetchLoaderClient::didReceiveData (streaming reception mode).
Clients of FetchLoader needs to pass a FetchBodyConsumer to the FetchLoader to do the data adaptation at loader creation time.

Added support for body data passed as a ReadableStream to Response.
This requires to set @body internal slot of the Response object in the constructor initialization JS built-in.

To actually use that data, Body accessors are also implemented as JS built-in for Response.
Since there is no need to do so for Request, FetchResponse is no longer marked as implementing FetchBody but all
FetchBody IDL description is inlined in FetchResponse.idl.

For each body accessor (arrayBuffer, blob, json, text), the corresponding JS built-in checks whether @body internal slot is set.
If that is not the case, regular handling is done through a new private method called @consume.
If @body internal slot is set, chunks are pumped from the ReadableStream using ReadableStream internal built-ins functions.
Data handling is done in C++ through the private methods @startConsumingStream, @consumeChunk and @finishConsumingStream.

To support cloning of Response with bodies, clone method is also implemented as a JS built-in.
Main clone is done through @cloneFoJS private method implemented in C++.
JS built-in clone code does the teeing of the ReadableStream using ReadableStream JS built-in internal functions.

Introducing a new ReadableStream type for FetchBody to cope with body being stored in a ReadableStream.

Introducing a new Loaded type for FetchBody to cope with body being stored in the FetchBodyConsumer owned by FetchBody.
This allows removing the conversion of data to JSC::ArrayBuffer which was done by defaut at the end of Fetch loading if user script did not request data before.
At the end of a load, the data remains in FetchBodyConsumer and the body is marked as Loaded.
If the user wants to get the data after data finished being loaded, FetchBodyConsumer will do the required conversions.

Introducing DeferredWrapper::resolveWithNewValue to handle the case of resolving promises with new objects.
This allows directly using toJSNewlyCreated instead of toJS.

* CMakeLists.txt: Adding FetchBodyConsumer.cpp. Removing WebCoreJSBuiltins.cpp from CMake list as it is not needed.
* Modules/fetch/FetchBody.cpp: Moving data adaptation code to FetchBodyConsumer.
(WebCore::FetchBody::extract):
(WebCore::FetchBody::arrayBuffer):
(WebCore::FetchBody::blob): Setting contentType in FetchBodyConsumer to handle proper creation of blob.
(WebCore::FetchBody::json):
(WebCore::FetchBody::text):
(WebCore::FetchBody::consume): Refactoring and added case of Loaded bodies.
(WebCore::FetchBody::consumeAsStream): Ditto.
(WebCore::FetchBody::consumeArrayBuffer):
(WebCore::FetchBody::consumeText):
(WebCore::FetchBody::consumeBlob):
(WebCore::FetchBody::loadingFailed):
(WebCore::FetchBody::loadingSucceeded):
(WebCore::FetchBody::loadingType): Deleted.
(WebCore::blobFromArrayBuffer): Deleted.
(WebCore::FetchBody::fulfillTextPromise): Deleted.
(WebCore::FetchBody::loadedAsArrayBuffer): Deleted.
(WebCore::FetchBody::loadedAsText): Deleted.
* Modules/fetch/FetchBody.h:
(WebCore::FetchBody::consumer):
* Modules/fetch/FetchBodyConsumer.cpp: Added, responsible of data adaptation.
(WebCore::blobFromData):
(WebCore::FetchBodyConsumer::resolveWithData):
(WebCore::FetchBodyConsumer::resolve):
(WebCore::FetchBodyConsumer::append):
(WebCore::FetchBodyConsumer::takeData):
(WebCore::FetchBodyConsumer::takeAsArrayBuffer):
(WebCore::FetchBodyConsumer::takeAsBlob):
(WebCore::FetchBodyConsumer::takeAsText):
* Modules/fetch/FetchBodyConsumer.h: Added.
(WebCore::FetchBodyConsumer::FetchBodyConsumer):
(WebCore::FetchBodyConsumer::setContentType):
(WebCore::FetchBodyConsumer::setType):
(WebCore::FetchBodyConsumer::type):
(WebCore::FetchBodyConsumer::clean):
(WebCore::FetchBodyConsumer::hasData):
* Modules/fetch/FetchBodyOwner.cpp:
(WebCore::FetchBodyOwner::loadBlob):
(WebCore::FetchBodyOwner::blobLoadingSucceeded):
(WebCore::FetchBodyOwner::loadedBlobAsText): Deleted.
* Modules/fetch/FetchBodyOwner.h:
(WebCore::FetchBodyOwner::loadedBlobAsArrayBuffer): Deleted.
* Modules/fetch/FetchInternals.js:
(consumeStream): Pump ReadableStream data and send it to FetchResponse to be converted according user need.
* Modules/fetch/FetchLoader.cpp:
(WebCore::FetchLoader::FetchLoader): FetchLoader is simplified as it has two nodes: consumer mode in which case
data is sent to the consumer and no-consumer mode in which case data is passed to loader client. The second mode
is used to conveyy data to ReadableStream source.
(WebCore::FetchLoader::stop):
(WebCore::FetchLoader::startStreaming):
(WebCore::FetchLoader::didReceiveData):
(WebCore::FetchLoader::didFinishLoading): Deleted.
* Modules/fetch/FetchLoader.h:
* Modules/fetch/FetchLoaderClient.h:
(WebCore::FetchLoaderClient::didFinishLoadingAsText): Deleted.
(WebCore::FetchLoaderClient::didFinishLoadingAsArrayBuffer): Deleted.
* Modules/fetch/FetchResponse.cpp:
(WebCore::FetchResponse::cloneForJS):
(WebCore::FetchResponse::BodyLoader::didSucceed):
(WebCore::FetchResponse::BodyLoader::start):
(WebCore::FetchResponse::consume): Introduced to consume data stored in body and not in ReadableStream.
(WebCore::FetchResponse::startConsumingStream): Introduced to start process of consuming data stored in the ReadableStream.
(WebCore::FetchResponse::consumeChunk): Reception of ReadableStream data.
(WebCore::FetchResponse::finishConsumingStream): Doing the final conversion.
(WebCore::FetchResponse::clone): Deleted.
(WebCore::FetchResponse::BodyLoader::didFinishLoadingAsArrayBuffer): Deleted.
* Modules/fetch/FetchResponse.h:
* Modules/fetch/FetchResponse.idl: Inlining of FetchBody methods/attributes and adding private methods.
* Modules/fetch/FetchResponse.js:
(initializeFetchResponse):
(clone):
(arrayBuffer):
(blob):
(formData):
(json):
(text):
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/JSDOMPromise.h:
(WebCore::DeferredWrapper::resolveWithNewValue):
* bindings/js/WebCoreBuiltinNames.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@203767 268f45cc-cd09-0410-ab3c-d52691b4dbfc

23 files changed:
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-empty-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume.html
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/Modules/fetch/FetchBody.cpp
Source/WebCore/Modules/fetch/FetchBody.h
Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp [new file with mode: 0644]
Source/WebCore/Modules/fetch/FetchBodyConsumer.h [new file with mode: 0644]
Source/WebCore/Modules/fetch/FetchBodyOwner.cpp
Source/WebCore/Modules/fetch/FetchBodyOwner.h
Source/WebCore/Modules/fetch/FetchInternals.js
Source/WebCore/Modules/fetch/FetchLoader.cpp
Source/WebCore/Modules/fetch/FetchLoader.h
Source/WebCore/Modules/fetch/FetchLoaderClient.h
Source/WebCore/Modules/fetch/FetchResponse.cpp
Source/WebCore/Modules/fetch/FetchResponse.h
Source/WebCore/Modules/fetch/FetchResponse.idl
Source/WebCore/Modules/fetch/FetchResponse.js
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/JSDOMPromise.h
Source/WebCore/bindings/js/WebCoreBuiltinNames.h

index d080d7bc50337de616619900980ee04b39a69790..afa4fb497a9f09d5f967753d79b6286122f49a7e 100644 (file)
@@ -1,3 +1,14 @@
+2016-07-26  Youenn Fablet  <youenn@apple.com>
+
+        [Fetch API] Response constructor should be able to take a ReadableStream as body
+        https://bugs.webkit.org/show_bug.cgi?id=159804
+
+        Reviewed by Alex Christensen.
+
+        * web-platform-tests/fetch/api/response/response-consume-empty-expected.txt:
+        * web-platform-tests/fetch/api/response/response-consume-expected.txt:
+        * web-platform-tests/fetch/api/response/response-consume.html: Updating test to exercice Response coonstructor with a ReadableStream.
+
 2016-07-26  Chris Dumez  <cdumez@apple.com>
 
         Move 'dir' attribute from HTMLDocument to Document
index 6704de1a442e5f1fd725b0cee22c7f0737a5e586..e9679518bd9c03094d8fffcf9fcf82dd177bc7cf 100644 (file)
@@ -3,7 +3,7 @@ PASS Consume response's body as text
 PASS Consume response's body as blob 
 PASS Consume response's body as arrayBuffer 
 PASS Consume response's body as json 
-FAIL Consume response's body as formData promise_test: Unhandled rejection with value: undefined
+FAIL Consume response's body as formData promise_test: Unhandled rejection with value: "Not implemented"
 PASS Consume empty blob response body as arrayBuffer 
 PASS Consume empty text response body as arrayBuffer 
 PASS Consume empty blob response body as text 
index d088a69a8ce42a2fa0a1c92099d86a6bfa57e6d8..416a392ce6ad6cab68e271dd04a19a2ee50203ee 100644 (file)
@@ -3,9 +3,13 @@ PASS Consume response's body as text
 PASS Consume response's body as blob 
 PASS Consume response's body as arrayBuffer 
 PASS Consume response's body as json 
-FAIL Consume response's body as formData promise_test: Unhandled rejection with value: undefined
+FAIL Consume response's body as formData promise_test: Unhandled rejection with value: "Not implemented"
 PASS Consume blob response's body as blob 
 PASS Consume blob response's body as text 
 PASS Consume blob response's body as json 
 PASS Consume blob response's body as arrayBuffer 
+PASS Consume stream response's body as blob 
+PASS Consume stream response's body as text 
+PASS Consume stream response's body as json 
+PASS Consume stream response's body as arrayBuffer 
 
index 02814ff5be926be738264ba91070bd2369d54691..99410cd448ab8bd64790187004efd34b1c99ab49 100644 (file)
     checkBlobResponseBody(blob, textData, "json", checkBodyJSON);
     checkBlobResponseBody(blob, textData, "arrayBuffer", checkBodyArrayBuffer);
 
+    function checkReadableStreamResponseBody(streamData, bodyType, checkFunction) {
+      promise_test(function(test) {
+        var stream = new ReadableStream({
+          start: function(controller) {
+            controller.enqueue((stringToArray(streamData)));
+            controller.close();
+          }
+        });
+        var response = new Response(stream);
+        assert_false(response.bodyUsed, "bodyUsed is false at init");
+        return checkFunction(response, streamData);
+      }, "Consume stream response's body as " + bodyType);
+    }
+
+    checkReadableStreamResponseBody(textData, "blob", checkBodyBlob);
+    checkReadableStreamResponseBody(textData, "text", checkBodyText);
+    checkReadableStreamResponseBody(textData, "json", checkBodyJSON);
+    checkReadableStreamResponseBody(textData, "arrayBuffer", checkBodyArrayBuffer);
+
     </script>
   </body>
 </html>
index 6dc113f8a2c602e3f5013313736ecaa366be57fd..62a7232cd8b3df5b748fa7d7481f46a308510258 100644 (file)
@@ -818,6 +818,7 @@ set(WebCore_SOURCES
 
     Modules/fetch/DOMWindowFetch.cpp
     Modules/fetch/FetchBody.cpp
+    Modules/fetch/FetchBodyConsumer.cpp
     Modules/fetch/FetchBodyOwner.cpp
     Modules/fetch/FetchHeaders.cpp
     Modules/fetch/FetchLoader.cpp
@@ -3773,7 +3774,6 @@ add_custom_command(
     DEPENDS ${BUILTINS_GENERATOR_SCRIPTS}
     COMMAND ${PYTHON_EXECUTABLE} ${JavaScriptCore_SCRIPTS_DIR}/generate-js-builtins.py --wrappers-only --framework WebCore --output-directory ${DERIVED_SOURCES_WEBCORE_DIR} ${WebCore_BUILTINS_SOURCES}
     VERBATIM)
-list(APPEND WebCore_DERIVED_SOURCES ${DERIVED_SOURCES_WEBCORE_DIR}/WebCoreJSBuiltins.cpp)
 list(APPEND WebCore_DERIVED_SOURCES ${DERIVED_SOURCES_WEBCORE_DIR}/WebCoreJSBuiltinInternals.cpp)
 list(APPEND WebCore_DERIVED_SOURCES ${DERIVED_SOURCES_WEBCORE_DIR}/WebCoreJSBuiltins.h)
 list(APPEND WebCore_DERIVED_SOURCES ${DERIVED_SOURCES_WEBCORE_DIR}/WebCoreJSBuiltinInternals.h)
index ef743db8f39eea42b0457389982499adbdfcdb9e..febf1102193dc581646dcf9a04c932c4b523433d 100644 (file)
@@ -1,3 +1,127 @@
+2016-07-26  Youenn Fablet  <youenn@apple.com>
+
+        [Fetch API] Response constructor should be able to take a ReadableStream as body
+        https://bugs.webkit.org/show_bug.cgi?id=159804
+
+        Reviewed by Alex Christensen.
+
+        Covered by existing and updated tests.
+
+        Introduced FetchBodyConsumer to encapsulate the code responsible to adapt FetchBody internal data to the requests made by user scripts.
+        This refactoring eases the handling of internal data coming from ReadableStream.
+
+        FetchLoader is now delegating conversion from the data to its m_consumer field.
+        If m_consumer is null, FetchLoader calls FetchLoaderClient::didReceiveData (streaming reception mode).
+        Clients of FetchLoader needs to pass a FetchBodyConsumer to the FetchLoader to do the data adaptation at loader creation time.
+
+        Added support for body data passed as a ReadableStream to Response.
+        This requires to set @body internal slot of the Response object in the constructor initialization JS built-in.
+
+        To actually use that data, Body accessors are also implemented as JS built-in for Response.
+        Since there is no need to do so for Request, FetchResponse is no longer marked as implementing FetchBody but all
+        FetchBody IDL description is inlined in FetchResponse.idl.
+
+        For each body accessor (arrayBuffer, blob, json, text), the corresponding JS built-in checks whether @body internal slot is set.
+        If that is not the case, regular handling is done through a new private method called @consume.
+        If @body internal slot is set, chunks are pumped from the ReadableStream using ReadableStream internal built-ins functions.
+        Data handling is done in C++ through the private methods @startConsumingStream, @consumeChunk and @finishConsumingStream.
+
+        To support cloning of Response with bodies, clone method is also implemented as a JS built-in.
+        Main clone is done through @cloneFoJS private method implemented in C++.
+        JS built-in clone code does the teeing of the ReadableStream using ReadableStream JS built-in internal functions.
+
+        Introducing a new ReadableStream type for FetchBody to cope with body being stored in a ReadableStream.
+
+        Introducing a new Loaded type for FetchBody to cope with body being stored in the FetchBodyConsumer owned by FetchBody.
+        This allows removing the conversion of data to JSC::ArrayBuffer which was done by defaut at the end of Fetch loading if user script did not request data before.
+        At the end of a load, the data remains in FetchBodyConsumer and the body is marked as Loaded.
+        If the user wants to get the data after data finished being loaded, FetchBodyConsumer will do the required conversions.
+
+        Introducing DeferredWrapper::resolveWithNewValue to handle the case of resolving promises with new objects.
+        This allows directly using toJSNewlyCreated instead of toJS.
+
+        * CMakeLists.txt: Adding FetchBodyConsumer.cpp. Removing WebCoreJSBuiltins.cpp from CMake list as it is not needed.
+        * Modules/fetch/FetchBody.cpp: Moving data adaptation code to FetchBodyConsumer.
+        (WebCore::FetchBody::extract):
+        (WebCore::FetchBody::arrayBuffer):
+        (WebCore::FetchBody::blob): Setting contentType in FetchBodyConsumer to handle proper creation of blob.
+        (WebCore::FetchBody::json):
+        (WebCore::FetchBody::text):
+        (WebCore::FetchBody::consume): Refactoring and added case of Loaded bodies.
+        (WebCore::FetchBody::consumeAsStream): Ditto.
+        (WebCore::FetchBody::consumeArrayBuffer):
+        (WebCore::FetchBody::consumeText):
+        (WebCore::FetchBody::consumeBlob):
+        (WebCore::FetchBody::loadingFailed):
+        (WebCore::FetchBody::loadingSucceeded):
+        (WebCore::FetchBody::loadingType): Deleted.
+        (WebCore::blobFromArrayBuffer): Deleted.
+        (WebCore::FetchBody::fulfillTextPromise): Deleted.
+        (WebCore::FetchBody::loadedAsArrayBuffer): Deleted.
+        (WebCore::FetchBody::loadedAsText): Deleted.
+        * Modules/fetch/FetchBody.h:
+        (WebCore::FetchBody::consumer):
+        * Modules/fetch/FetchBodyConsumer.cpp: Added, responsible of data adaptation.
+        (WebCore::blobFromData):
+        (WebCore::FetchBodyConsumer::resolveWithData):
+        (WebCore::FetchBodyConsumer::resolve):
+        (WebCore::FetchBodyConsumer::append):
+        (WebCore::FetchBodyConsumer::takeData):
+        (WebCore::FetchBodyConsumer::takeAsArrayBuffer):
+        (WebCore::FetchBodyConsumer::takeAsBlob):
+        (WebCore::FetchBodyConsumer::takeAsText):
+        * Modules/fetch/FetchBodyConsumer.h: Added.
+        (WebCore::FetchBodyConsumer::FetchBodyConsumer):
+        (WebCore::FetchBodyConsumer::setContentType):
+        (WebCore::FetchBodyConsumer::setType):
+        (WebCore::FetchBodyConsumer::type):
+        (WebCore::FetchBodyConsumer::clean):
+        (WebCore::FetchBodyConsumer::hasData):
+        * Modules/fetch/FetchBodyOwner.cpp:
+        (WebCore::FetchBodyOwner::loadBlob):
+        (WebCore::FetchBodyOwner::blobLoadingSucceeded):
+        (WebCore::FetchBodyOwner::loadedBlobAsText): Deleted.
+        * Modules/fetch/FetchBodyOwner.h:
+        (WebCore::FetchBodyOwner::loadedBlobAsArrayBuffer): Deleted.
+        * Modules/fetch/FetchInternals.js:
+        (consumeStream): Pump ReadableStream data and send it to FetchResponse to be converted according user need.
+        * Modules/fetch/FetchLoader.cpp:
+        (WebCore::FetchLoader::FetchLoader): FetchLoader is simplified as it has two nodes: consumer mode in which case
+        data is sent to the consumer and no-consumer mode in which case data is passed to loader client. The second mode
+        is used to conveyy data to ReadableStream source.
+        (WebCore::FetchLoader::stop):
+        (WebCore::FetchLoader::startStreaming):
+        (WebCore::FetchLoader::didReceiveData):
+        (WebCore::FetchLoader::didFinishLoading): Deleted.
+        * Modules/fetch/FetchLoader.h:
+        * Modules/fetch/FetchLoaderClient.h:
+        (WebCore::FetchLoaderClient::didFinishLoadingAsText): Deleted.
+        (WebCore::FetchLoaderClient::didFinishLoadingAsArrayBuffer): Deleted.
+        * Modules/fetch/FetchResponse.cpp:
+        (WebCore::FetchResponse::cloneForJS):
+        (WebCore::FetchResponse::BodyLoader::didSucceed):
+        (WebCore::FetchResponse::BodyLoader::start):
+        (WebCore::FetchResponse::consume): Introduced to consume data stored in body and not in ReadableStream.
+        (WebCore::FetchResponse::startConsumingStream): Introduced to start process of consuming data stored in the ReadableStream.
+        (WebCore::FetchResponse::consumeChunk): Reception of ReadableStream data.
+        (WebCore::FetchResponse::finishConsumingStream): Doing the final conversion.
+        (WebCore::FetchResponse::clone): Deleted.
+        (WebCore::FetchResponse::BodyLoader::didFinishLoadingAsArrayBuffer): Deleted.
+        * Modules/fetch/FetchResponse.h:
+        * Modules/fetch/FetchResponse.idl: Inlining of FetchBody methods/attributes and adding private methods.
+        * Modules/fetch/FetchResponse.js:
+        (initializeFetchResponse):
+        (clone):
+        (arrayBuffer):
+        (blob):
+        (formData):
+        (json):
+        (text):
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/JSDOMPromise.h:
+        (WebCore::DeferredWrapper::resolveWithNewValue):
+        * bindings/js/WebCoreBuiltinNames.h:
+
 2016-07-26  Youenn Fablet  <youennf@gmail.com>
 
         JS Built-ins should throw this-error messages consistently with binding generated code
index 8c479d5f8e062502d9570c8b9b585bcf0db44d3f..e0f5ae18035aaafc794b77b0dd7d6a2160550fd9 100644 (file)
 #include "HTTPParsers.h"
 #include "JSBlob.h"
 #include "JSDOMFormData.h"
+#include "JSReadableStream.h"
 #include "ReadableStreamSource.h"
 
 namespace WebCore {
 
-static Ref<Blob> blobFromArrayBuffer(ArrayBuffer*, const String&);
-
 FetchBody::FetchBody(Ref<Blob>&& blob)
     : m_type(Type::Blob)
     , m_mimeType(blob->type())
@@ -75,6 +74,8 @@ FetchBody FetchBody::extract(JSC::ExecState& state, JSC::JSValue value)
         return FetchBody(*JSDOMFormData::toWrapped(value));
     if (value.isString())
         return FetchBody(value.toWTFString(&state));
+    if (value.inherits(JSReadableStream::info()))
+        return { Type::ReadableStream };
     return { };
 }
 
@@ -89,13 +90,16 @@ FetchBody FetchBody::extractFromBody(FetchBody* body)
 void FetchBody::arrayBuffer(FetchBodyOwner& owner, DeferredWrapper&& promise)
 {
     ASSERT(m_type != Type::None);
-    consume(owner, Consumer::Type::ArrayBuffer, WTFMove(promise));
+    m_consumer.setType(FetchBodyConsumer::Type::ArrayBuffer);
+    consume(owner, WTFMove(promise));
 }
 
 void FetchBody::blob(FetchBodyOwner& owner, DeferredWrapper&& promise)
 {
     ASSERT(m_type != Type::None);
-    consume(owner, Consumer::Type::Blob, WTFMove(promise));
+    m_consumer.setType(FetchBodyConsumer::Type::Blob);
+    m_consumer.setContentType(Blob::normalizedContentType(extractMIMETypeFromMediaType(m_mimeType)));
+    consume(owner, WTFMove(promise));
 }
 
 void FetchBody::json(FetchBodyOwner& owner, DeferredWrapper&& promise)
@@ -106,7 +110,8 @@ void FetchBody::json(FetchBodyOwner& owner, DeferredWrapper&& promise)
         fulfillPromiseWithJSON(promise, m_text);
         return;
     }
-    consume(owner, Consumer::Type::JSON, WTFMove(promise));
+    m_consumer.setType(FetchBodyConsumer::Type::JSON);
+    consume(owner, WTFMove(promise));
 }
 
 void FetchBody::text(FetchBodyOwner& owner, DeferredWrapper&& promise)
@@ -117,113 +122,95 @@ void FetchBody::text(FetchBodyOwner& owner, DeferredWrapper&& promise)
         promise.resolve(m_text);
         return;
     }
-    consume(owner, Consumer::Type::Text, WTFMove(promise));
+    m_consumer.setType(FetchBodyConsumer::Type::Text);
+    consume(owner, WTFMove(promise));
 }
 
-void FetchBody::consume(FetchBodyOwner& owner, Consumer::Type type, DeferredWrapper&& promise)
+void FetchBody::consume(FetchBodyOwner& owner, DeferredWrapper&& promise)
 {
-    if (m_type == Type::ArrayBuffer) {
-        consumeArrayBuffer(type, promise);
+    // This should be handled by FetchBodyOwner
+    ASSERT(m_type != Type::None);
+    // This should be handled by JS built-ins
+    ASSERT(m_type != Type::ReadableStream);
+
+    switch (m_type) {
+    case Type::ArrayBuffer:
+        consumeArrayBuffer(promise);
         return;
-    }
-    if (m_type == Type::Text) {
-        consumeText(type, promise);
+    case Type::Text:
+        consumeText(promise);
         return;
-    }
-    if (m_type == Type::Blob) {
-        consumeBlob(owner, type, WTFMove(promise));
+    case Type::Blob:
+        consumeBlob(owner, WTFMove(promise));
         return;
-    }
-    if (m_type == Type::Loading) {
-        // FIXME: We should be able to change the loading type to text if consumer type is JSON or Text.
-        m_consumer = Consumer({type, WTFMove(promise)});
+    case Type::Loading:
+        m_consumePromise = WTFMove(promise);
+        return;
+    case Type::Loaded:
+        m_consumer.resolve(promise);
         return;
+    default:
+        // FIXME: Support other types.
+        promise.reject(0);
     }
-
-    // FIXME: Support other types.
-    promise.reject(0);
 }
 
 #if ENABLE(STREAMS_API)
 void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchResponseSource& source)
 {
+    // This should be handled by FetchResponse
     ASSERT(m_type != Type::Loading);
+    // This should be handled by JS built-ins
+    ASSERT(m_type != Type::ReadableStream);
 
+    bool closeStream = false;
     switch (m_type) {
     case Type::ArrayBuffer:
         ASSERT(m_data);
-        if (source.enqueue(RefPtr<JSC::ArrayBuffer>(m_data)))
-            source.close();
-        return;
+        closeStream = source.enqueue(RefPtr<JSC::ArrayBuffer>(m_data));
+        break;
     case Type::Text: {
         Vector<uint8_t> data = extractFromText();
-        if (source.enqueue(ArrayBuffer::tryCreate(data.data(), data.size())))
-            source.close();
-        return;
+        closeStream = source.enqueue(ArrayBuffer::tryCreate(data.data(), data.size()));
+        break;
     }
     case Type::Blob:
         ASSERT(m_blob);
-        owner.loadBlob(*m_blob, FetchLoader::Type::Stream);
-        return;
+        owner.loadBlob(*m_blob, nullptr);
+        break;
     case Type::None:
-        source.close();
-        return;
+        closeStream = true;
+        break;
+    case Type::Loaded: {
+        closeStream = source.enqueue(m_consumer.takeAsArrayBuffer());
+        break;
+    }
     default:
         source.error(ASCIILiteral("not implemented"));
     }
-}
-#endif
 
-void FetchBody::consumeArrayBuffer(Consumer::Type type, DeferredWrapper& promise)
-{
-    if (type == Consumer::Type::ArrayBuffer) {
-        fulfillPromiseWithArrayBuffer(promise, m_data.get());
-        return;
-    }
-    if (type == Consumer::Type::Blob) {
-        promise.resolve(blobFromArrayBuffer(m_data.get(), Blob::normalizedContentType(extractMIMETypeFromMediaType(m_mimeType))));
-        return;
-    }
-
-    ASSERT(type == Consumer::Type::Text || type == Consumer::Type::JSON);
-    // FIXME: Do we need TextResourceDecoder to create a String to decode UTF-8 data.
-    fulfillTextPromise(type, TextResourceDecoder::create(ASCIILiteral("text/plain"), "UTF-8")->decodeAndFlush(static_cast<const char*>(m_data->data()), m_data->byteLength()), promise);
+    if (closeStream)
+        source.close();
 }
+#endif
 
-void FetchBody::consumeText(Consumer::Type type, DeferredWrapper& promise)
+void FetchBody::consumeArrayBuffer(DeferredWrapper& promise)
 {
-    ASSERT(type == Consumer::Type::ArrayBuffer || type == Consumer::Type::Blob);
-
-    if (type == Consumer::Type::ArrayBuffer) {
-        Vector<uint8_t> data = extractFromText();
-        fulfillPromiseWithArrayBuffer(promise, data.data(), data.size());
-        return;
-    }
-    String contentType = Blob::normalizedContentType(extractMIMETypeFromMediaType(m_mimeType));
-    promise.resolve(Blob::create(extractFromText(), contentType));
+    m_consumer.resolveWithData(promise, static_cast<const uint8_t*>(m_data->data()), m_data->byteLength());
 }
 
-FetchLoader::Type FetchBody::loadingType(Consumer::Type type)
+void FetchBody::consumeText(DeferredWrapper& promise)
 {
-    switch (type) {
-    case Consumer::Type::JSON:
-    case Consumer::Type::Text:
-        return FetchLoader::Type::Text;
-    case Consumer::Type::Blob:
-    case Consumer::Type::ArrayBuffer:
-        return FetchLoader::Type::ArrayBuffer;
-    default:
-        ASSERT_NOT_REACHED();
-        return FetchLoader::Type::ArrayBuffer;
-    };
+    Vector<uint8_t> data = extractFromText();
+    m_consumer.resolveWithData(promise, data.data(), data.size());
 }
 
-void FetchBody::consumeBlob(FetchBodyOwner& owner, Consumer::Type type, DeferredWrapper&& promise)
+void FetchBody::consumeBlob(FetchBodyOwner& owner, DeferredWrapper&& promise)
 {
     ASSERT(m_blob);
 
-    m_consumer = Consumer({type, WTFMove(promise)});
-    owner.loadBlob(*m_blob, loadingType(type));
+    m_consumePromise = WTFMove(promise);
+    owner.loadBlob(*m_blob, &m_consumer);
 }
 
 Vector<uint8_t> FetchBody::extractFromText() const
@@ -236,63 +223,21 @@ Vector<uint8_t> FetchBody::extractFromText() const
     return value;
 }
 
-static inline Ref<Blob> blobFromArrayBuffer(ArrayBuffer* buffer, const String& contentType)
-{
-    if (!buffer)
-        return Blob::create(Vector<uint8_t>(), contentType);
-
-    // FIXME: We should try to move buffer to Blob without doing this copy.
-    Vector<uint8_t> value(buffer->byteLength());
-    memcpy(value.data(), buffer->data(), buffer->byteLength());
-    return Blob::create(WTFMove(value), contentType);
-}
-
-void FetchBody::fulfillTextPromise(FetchBody::Consumer::Type type, const String& text, DeferredWrapper& promise)
-{
-    ASSERT(type == Consumer::Type::Text || type == Consumer::Type::JSON);
-    if (type == FetchBody::Consumer::Type::Text)
-        promise.resolve(text);
-    else
-        fulfillPromiseWithJSON(promise, text);
-}
-
 void FetchBody::loadingFailed()
 {
-    ASSERT(m_consumer);
-    m_consumer->promise.reject(0);
-    m_consumer = Nullopt;
-}
-
-void FetchBody::loadedAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer)
-{
-    if (m_type == Type::Loading) {
-        m_type = Type::ArrayBuffer;
-        m_data = buffer;
-        if (m_consumer) {
-            consumeArrayBuffer(m_consumer->type, m_consumer->promise);
-            m_consumer = Nullopt;
-        }
-        return;
-    }
-
-    ASSERT(m_consumer);
-    ASSERT(m_consumer->type == Consumer::Type::Blob || m_consumer->type == Consumer::Type::ArrayBuffer);
-    if (m_consumer->type == Consumer::Type::ArrayBuffer)
-        fulfillPromiseWithArrayBuffer(m_consumer->promise, buffer.get());
-    else {
-        ASSERT(m_blob);
-        m_consumer->promise.resolve(blobFromArrayBuffer(buffer.get(), m_blob->type()));
+    if (m_consumePromise) {
+        m_consumePromise->reject(0);
+        m_consumePromise = Nullopt;
     }
-    m_consumer = Nullopt;
 }
 
-void FetchBody::loadedAsText(String&& text)
+void FetchBody::loadingSucceeded()
 {
-    ASSERT(m_consumer);
-    ASSERT(m_consumer->type == Consumer::Type::Text || m_consumer->type == Consumer::Type::JSON);
-
-    fulfillTextPromise(m_consumer->type, text, m_consumer->promise);
-    m_consumer = Nullopt;
+    m_type = m_consumer.hasData() ? Type::Loaded : Type::None;
+    if (m_consumePromise) {
+        m_consumer.resolve(*m_consumePromise);
+        m_consumePromise = Nullopt;
+    }
 }
 
 RefPtr<FormData> FetchBody::bodyForInternalRequest() const
index 56f3dfdc8571c315112f2665c6ffda975a9df392..62d9e5a787d58330b664f8ac56ef24532cef6dc5 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "Blob.h"
 #include "DOMFormData.h"
+#include "FetchBodyConsumer.h"
 #include "FetchLoader.h"
 #include "JSDOMPromise.h"
 
@@ -70,35 +71,27 @@ public:
     FetchBody() = default;
 
     void loadingFailed();
-    void loadedAsArrayBuffer(RefPtr<ArrayBuffer>&&);
-    void loadedAsText(String&&);
+    void loadingSucceeded();
 
     RefPtr<FormData> bodyForInternalRequest() const;
 
-    enum class Type { None, ArrayBuffer, Loading, Text, Blob, FormData };
+    enum class Type { None, ArrayBuffer, Blob, FormData, Text, Loading, Loaded, ReadableStream };
     Type type() const { return m_type; }
 
+    FetchBodyConsumer& consumer() { return m_consumer; }
+
 private:
     FetchBody(Ref<Blob>&&);
     FetchBody(Ref<DOMFormData>&&);
     FetchBody(String&&);
     FetchBody(Type type) : m_type(type) { }
 
-    struct Consumer {
-        enum class Type { Text, Blob, JSON, ArrayBuffer };
-
-        Type type;
-        DeferredWrapper promise;
-    };
-    void consume(FetchBodyOwner&, Consumer::Type, DeferredWrapper&&);
+    void consume(FetchBodyOwner&, DeferredWrapper&&);
 
     Vector<uint8_t> extractFromText() const;
-    void consumeArrayBuffer(Consumer::Type, DeferredWrapper&);
-    void consumeText(Consumer::Type, DeferredWrapper&);
-    void consumeBlob(FetchBodyOwner&, Consumer::Type, DeferredWrapper&&);
-    static FetchLoader::Type loadingType(Consumer::Type);
-    static void fulfillTextPromise(FetchBody::Consumer::Type, const String&, DeferredWrapper&);
-    static void fulfillArrayBufferPromise(FetchBody::Consumer::Type, const String&, DeferredWrapper&);
+    void consumeArrayBuffer(DeferredWrapper&);
+    void consumeText(DeferredWrapper&);
+    void consumeBlob(FetchBodyOwner&, DeferredWrapper&&);
 
     Type m_type { Type::None };
     String m_mimeType;
@@ -109,7 +102,8 @@ private:
     RefPtr<ArrayBuffer> m_data;
     String m_text;
 
-    Optional<Consumer> m_consumer;
+    FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::None };
+    Optional<DeferredWrapper> m_consumePromise;
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp b/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp
new file mode 100644 (file)
index 0000000..d93251b
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 Apple Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Inc. nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "FetchBodyConsumer.h"
+
+#if ENABLE(FETCH_API)
+
+#include "JSBlob.h"
+#include "JSDOMPromise.h"
+
+namespace WebCore {
+
+static inline Ref<Blob> blobFromData(const unsigned char* data, unsigned length, const String& contentType)
+{
+    Vector<uint8_t> value(length);
+    memcpy(value.data(), data, length);
+    return Blob::create(WTFMove(value), contentType);
+}
+
+void FetchBodyConsumer::resolveWithData(DeferredWrapper& promise, const unsigned char* data, unsigned length)
+{
+    switch (m_type) {
+    case Type::ArrayBuffer:
+        fulfillPromiseWithArrayBuffer(promise, data, length);
+        return;
+    case Type::Blob:
+        promise.resolveWithNewlyCreated(blobFromData(data, length, m_contentType));
+        return;
+    case Type::JSON:
+        fulfillPromiseWithJSON(promise, String(data, length));
+        return;
+    case Type::Text:
+        promise.resolve(String(data, length));
+        return;
+    case Type::None:
+        ASSERT_NOT_REACHED();
+        return;
+    }
+}
+
+void FetchBodyConsumer::resolve(DeferredWrapper& promise)
+{
+    ASSERT(m_type != Type::None);
+    switch (m_type) {
+    case Type::ArrayBuffer:
+        fulfillPromiseWithArrayBuffer(promise, takeAsArrayBuffer().get());
+        return;
+    case Type::Blob:
+        promise.resolveWithNewlyCreated(takeAsBlob());
+        return;
+    case Type::JSON:
+        fulfillPromiseWithJSON(promise, takeAsText());
+        return;
+    case Type::Text:
+        promise.resolve(takeAsText());
+        return;
+    case Type::None:
+        ASSERT_NOT_REACHED();
+        return;
+    }
+}
+
+void FetchBodyConsumer::append(const char* data, unsigned length)
+{
+    if (!m_buffer) {
+        m_buffer = SharedBuffer::create(data, length);
+        return;
+    }
+    m_buffer->append(data, length);
+}
+
+void FetchBodyConsumer::append(const unsigned char* data, unsigned length)
+{
+    append(reinterpret_cast<const char*>(data), length);
+}
+
+RefPtr<SharedBuffer> FetchBodyConsumer::takeData()
+{
+    return WTFMove(m_buffer);
+}
+
+RefPtr<JSC::ArrayBuffer> FetchBodyConsumer::takeAsArrayBuffer()
+{
+    if (!m_buffer)
+        return ArrayBuffer::tryCreate(nullptr, 0);
+
+    auto arrayBuffer = m_buffer->createArrayBuffer();
+    m_buffer = nullptr;
+    return arrayBuffer;
+}
+
+Ref<Blob> FetchBodyConsumer::takeAsBlob()
+{
+    ASSERT(m_buffer);
+    if (!m_buffer)
+        return Blob::create();
+
+    // FIXME: We should try to move m_buffer to Blob without doing extra copy.
+    return blobFromData(reinterpret_cast<const unsigned char*>(m_buffer->data()), m_buffer->size(), m_contentType);
+}
+
+String FetchBodyConsumer::takeAsText()
+{
+    if (!m_buffer)
+        return String();
+
+    auto text = String(m_buffer->data(), m_buffer->size());
+    m_buffer = nullptr;
+    return text;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(FETCH_API)
diff --git a/Source/WebCore/Modules/fetch/FetchBodyConsumer.h b/Source/WebCore/Modules/fetch/FetchBodyConsumer.h
new file mode 100644 (file)
index 0000000..faa1fe7
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 Apple Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Inc. nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(FETCH_API)
+
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+class Blob;
+class DeferredWrapper;
+
+class FetchBodyConsumer {
+public:
+    // Type is used in FetchResponse.js and should be kept synchronized with it.
+    enum class Type { None, ArrayBuffer, Blob, JSON, Text };
+
+    FetchBodyConsumer(Type type) : m_type(type) { }
+
+    void append(const char* data, unsigned);
+    void append(const unsigned char* data, unsigned);
+
+    RefPtr<SharedBuffer> takeData();
+    RefPtr<JSC::ArrayBuffer> takeAsArrayBuffer();
+    Ref<Blob> takeAsBlob();
+    String takeAsText();
+
+    void setContentType(const String& contentType) { m_contentType = contentType; }
+    void setType(Type type) { m_type = type; }
+
+    void clean() { m_buffer = nullptr; }
+
+    void resolve(DeferredWrapper&);
+    void resolveWithData(DeferredWrapper&, const unsigned char*, unsigned);
+
+    bool hasData() const { return !!m_buffer; }
+
+private:
+    Type m_type;
+    String m_contentType;
+    RefPtr<SharedBuffer> m_buffer;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(FETCH_API)
index 0f45794a069e2ee608b18378e3df1f4bce7353a5..964be771104da67197d3d504c69881418cc85689 100644 (file)
@@ -138,7 +138,7 @@ void FetchBodyOwner::text(DeferredWrapper&& promise)
     m_body.text(*this, WTFMove(promise));
 }
 
-void FetchBodyOwner::loadBlob(Blob& blob, FetchLoader::Type type)
+void FetchBodyOwner::loadBlob(Blob& blob, FetchBodyConsumer* consumer)
 {
     // Can only be called once for a body instance.
     ASSERT(isDisturbed());
@@ -150,7 +150,7 @@ void FetchBodyOwner::loadBlob(Blob& blob, FetchLoader::Type type)
     }
 
     m_blobLoader = { *this };
-    m_blobLoader->loader = std::make_unique<FetchLoader>(type, *m_blobLoader);
+    m_blobLoader->loader = std::make_unique<FetchLoader>(*m_blobLoader, consumer);
 
     m_blobLoader->loader->start(*scriptExecutionContext(), blob);
     if (!m_blobLoader->loader->isStarted()) {
@@ -169,11 +169,6 @@ void FetchBodyOwner::finishBlobLoading()
     unsetPendingActivity(this);
 }
 
-void FetchBodyOwner::loadedBlobAsText(String&& text)
-{
-    m_body.loadedAsText(WTFMove(text));
-}
-
 void FetchBodyOwner::blobLoadingSucceeded()
 {
     ASSERT(m_body.type() == FetchBody::Type::Blob);
@@ -184,7 +179,7 @@ void FetchBodyOwner::blobLoadingSucceeded()
         m_readableStreamSource = nullptr;
     }
 #endif
-
+    m_body.loadingSucceeded();
     finishBlobLoading();
 }
 
index 06a212665c39fc3907d38e13f4cc133cc872c7b4..87b72f0a499f9336a40fa48633a18909223b1d96 100644 (file)
@@ -52,7 +52,7 @@ public:
     void json(DeferredWrapper&&);
     void text(DeferredWrapper&&);
 
-    void loadBlob(Blob&, FetchLoader::Type);
+    void loadBlob(Blob&, FetchBodyConsumer*);
 
     bool isActive() const { return !!m_blobLoader; }
 
@@ -67,8 +67,6 @@ protected:
 
 private:
     // Blob loading routines
-    void loadedBlobAsText(String&&);
-    void loadedBlobAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer) { m_body.loadedAsArrayBuffer(WTFMove(buffer)); }
     void blobChunk(const char*, size_t);
     void blobLoadingSucceeded();
     void blobLoadingFailed();
@@ -78,8 +76,6 @@ private:
         BlobLoader(FetchBodyOwner&);
 
         // FetchLoaderClient API
-        void didFinishLoadingAsText(String&& text) final { owner.loadedBlobAsText(WTFMove(text)); }
-        void didFinishLoadingAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer) final { owner.loadedBlobAsArrayBuffer(WTFMove(buffer)); }
         void didReceiveResponse(const ResourceResponse&) final;
         void didReceiveData(const char* data, size_t size) final { owner.blobChunk(data, size); }
         void didFail() final;
index 452ec763535a809ea8d8298e06935ee5b3076e4b..d75004e2f8085b15c710070015bedd1b5abb22c6 100644 (file)
@@ -51,3 +51,27 @@ function fillFetchHeaders(headers, headersInit)
         @Headers.prototype.@appendFromJS.@call(headers, name, headersInit[name]);
     }
 }
+
+function consumeStream(response, type)
+{
+    @assert(response instanceof @Response);
+    @assert(response.@body instanceof @ReadableStream);
+
+    if (@isReadableStreamDisturbed(response.@body))
+        return @Promise.@reject(new @TypeError("Cannot consume a disturbed Response body ReadableStream"));
+
+    try {
+        let reader = new @ReadableStreamReader(response.@body);
+
+        @Response.prototype.@startConsumingStream.@call(response, type);
+        let pull = (result) => {
+            if (result.done)
+                return @Response.prototype.@finishConsumingStream.@call(response);
+            @Response.prototype.@consumeChunk.@call(response, result.value);
+            return @Promise.prototype.@then.@call(@readFromReadableStreamReader(reader), pull);
+        }
+        return @Promise.prototype.@then.@call(@readFromReadableStreamReader(reader), pull);
+    } catch(e) {
+        return @Promise.@reject(e);
+    }
+}
index 4d7b2ccbd449eaca0f2e8a1bfb33dedd16122b3a..5d8b7583a08b2fb3eea2a946bd7588f372e6e152 100644 (file)
@@ -91,24 +91,26 @@ void FetchLoader::start(ScriptExecutionContext& context, const FetchRequest& req
     m_isStarted = m_loader;
 }
 
-FetchLoader::FetchLoader(Type type, FetchLoaderClient& client)
-    : m_type(type)
-    , m_client(client)
+FetchLoader::FetchLoader(FetchLoaderClient& client, FetchBodyConsumer* consumer)
+    : m_client(client)
+    , m_consumer(consumer)
 {
 }
 
 void FetchLoader::stop()
 {
-    m_data = nullptr;
+    if (m_consumer)
+        m_consumer->clean();
     if (m_loader)
         m_loader->cancel();
 }
 
 RefPtr<SharedBuffer> FetchLoader::startStreaming()
 {
-    ASSERT(m_type == Type::ArrayBuffer);
-    m_type = Type::Stream;
-    return WTFMove(m_data);
+    ASSERT(m_consumer);
+    auto firstChunk = m_consumer->takeData();
+    m_consumer = nullptr;
+    return firstChunk;
 }
 
 void FetchLoader::didReceiveResponse(unsigned long, const ResourceResponse& response)
@@ -116,29 +118,17 @@ void FetchLoader::didReceiveResponse(unsigned long, const ResourceResponse& resp
     m_client.didReceiveResponse(response);
 }
 
-// FIXME: We should make text and blob creation more efficient.
-// We might also want to merge this class with FileReaderLoader.
 void FetchLoader::didReceiveData(const char* value, int size)
 {
-    if (m_type == Type::Stream) {
+    if (!m_consumer) {
         m_client.didReceiveData(value, size);
         return;
     }
-    if (!m_data) {
-        m_data = SharedBuffer::create(value, size);
-        return;
-    }
-    m_data->append(value, size);
+    m_consumer->append(value, size);
 }
 
 void FetchLoader::didFinishLoading(unsigned long, double)
 {
-    if (m_type == Type::ArrayBuffer)
-        m_client.didFinishLoadingAsArrayBuffer(m_data ? m_data->createArrayBuffer() : ArrayBuffer::tryCreate(nullptr, 0));
-    else if (m_type == Type::Text)
-        m_client.didFinishLoadingAsText(m_data ? TextResourceDecoder::create(ASCIILiteral("text/plain"), "UTF-8")->decodeAndFlush(m_data->data(), m_data->size()): String());
-    m_data = nullptr;
-
     m_client.didSucceed();
 }
 
index d473ff17451fca5c1a713fb543bd0b6a0b1de02b..dbb82fbebd559e77d1ce1b616bc6d0b4f1aef3c3 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef FetchLoader_h
-#define FetchLoader_h
+#pragma once
 
 #if ENABLE(FETCH_API)
 
-#include "SharedBuffer.h"
+#include "FetchBodyConsumer.h"
 #include "ThreadableLoader.h"
 #include "ThreadableLoaderClient.h"
 
@@ -44,9 +43,7 @@ class ScriptExecutionContext;
 
 class FetchLoader final : public ThreadableLoaderClient {
 public:
-    enum class Type { ArrayBuffer, Stream, Text };
-
-    FetchLoader(Type, FetchLoaderClient&);
+    FetchLoader(FetchLoaderClient&, FetchBodyConsumer*);
 
     RefPtr<SharedBuffer> startStreaming();
 
@@ -63,18 +60,13 @@ private:
     void didFinishLoading(unsigned long, double) final;
     void didFail(const ResourceError&) final;
 
-    Type type() const { return m_type; }
-
 private:
-    Type m_type { Type::ArrayBuffer };
     FetchLoaderClient& m_client;
     RefPtr<ThreadableLoader> m_loader;
-    RefPtr<SharedBuffer> m_data;
+    FetchBodyConsumer* m_consumer;
     bool m_isStarted { false };
 };
 
 } // namespace WebCore
 
 #endif // ENABLE(FETCH_API)
-
-#endif // FetchLoader_h
index d09fad53e27e6d8ac22adb7ca9f4510278da4150..e88e99418cd9914a1ee481877d61adcb6e04eaf4 100644 (file)
@@ -47,8 +47,6 @@ public:
 
     virtual void didReceiveResponse(const ResourceResponse&) { }
 
-    virtual void didFinishLoadingAsText(String&&) { }
-    virtual void didFinishLoadingAsArrayBuffer(RefPtr<JSC::ArrayBuffer>&&) { }
     virtual void didReceiveData(const char*, size_t) { }
 
     virtual void didSucceed() = 0;
index c2e8abaab11a201540d8bd12850e51e70b2ea301..6738fa3183c961159112ce18cd050cb880da8403 100644 (file)
@@ -34,6 +34,7 @@
 #include "ExceptionCode.h"
 #include "FetchRequest.h"
 #include "HTTPParsers.h"
+#include "JSBlob.h"
 #include "JSFetchResponse.h"
 #include "ScriptExecutionContext.h"
 
@@ -93,13 +94,11 @@ FetchResponse::FetchResponse(ScriptExecutionContext& context, FetchBody&& body,
 {
 }
 
-RefPtr<FetchResponse> FetchResponse::clone(ScriptExecutionContext& context, ExceptionCode& ec)
+Ref<FetchResponse> FetchResponse::cloneForJS()
 {
-    if (isDisturbed()) {
-        ec = TypeError;
-        return nullptr;
-    }
-    return adoptRef(*new FetchResponse(context, FetchBody(m_body), FetchHeaders::create(headers()), ResourceResponse(m_response)));
+    ASSERT(scriptExecutionContext());
+    ASSERT(!isDisturbed());
+    return adoptRef(*new FetchResponse(*scriptExecutionContext(), FetchBody(m_body), FetchHeaders::create(headers()), ResourceResponse(m_response)));
 }
 
 void FetchResponse::fetch(ScriptExecutionContext& context, FetchRequest& request, FetchPromise&& promise)
@@ -130,6 +129,8 @@ void FetchResponse::BodyLoader::didSucceed()
         m_response.m_readableStreamSource = nullptr;
     }
 #endif
+    m_response.m_body.loadingSucceeded();
+
     if (m_loader->isStarted())
         m_response.m_bodyLoader = Nullopt;
     m_response.unsetPendingActivity(&m_response);
@@ -187,14 +188,9 @@ void FetchResponse::BodyLoader::didReceiveData(const char* data, size_t size)
 #endif
 }
 
-void FetchResponse::BodyLoader::didFinishLoadingAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer)
-{
-    m_response.body().loadedAsArrayBuffer(WTFMove(buffer));
-}
-
 bool FetchResponse::BodyLoader::start(ScriptExecutionContext& context, const FetchRequest& request)
 {
-    m_loader = std::make_unique<FetchLoader>(FetchLoader::Type::ArrayBuffer, *this);
+    m_loader = std::make_unique<FetchLoader>(*this, &m_response.m_body.consumer());
     m_loader->start(context, request);
     return m_loader->isStarted();
 }
@@ -205,7 +201,46 @@ void FetchResponse::BodyLoader::stop()
         m_loader->stop();
 }
 
+void FetchResponse::consume(unsigned type, DeferredWrapper&& wrapper)
+{
+    ASSERT(type <= static_cast<unsigned>(FetchBodyConsumer::Type::Text));
+
+    switch (static_cast<FetchBodyConsumer::Type>(type)) {
+    case FetchBodyConsumer::Type::ArrayBuffer:
+        arrayBuffer(WTFMove(wrapper));
+        return;
+    case FetchBodyConsumer::Type::Blob:
+        blob(WTFMove(wrapper));
+        return;
+    case FetchBodyConsumer::Type::JSON:
+        json(WTFMove(wrapper));
+        return;
+    case FetchBodyConsumer::Type::Text:
+        text(WTFMove(wrapper));
+        return;
+    case FetchBodyConsumer::Type::None:
+        ASSERT_NOT_REACHED();
+        return;
+    }
+}
+
 #if ENABLE(STREAMS_API)
+void FetchResponse::startConsumingStream(unsigned type)
+{
+    m_isDisturbed = true;
+    m_consumer.setType(static_cast<FetchBodyConsumer::Type>(type));
+}
+
+void FetchResponse::consumeChunk(Ref<JSC::Uint8Array>&& chunk)
+{
+    m_consumer.append(chunk->data(), chunk->byteLength());
+}
+
+void FetchResponse::finishConsumingStream(DeferredWrapper&& promise)
+{
+    m_consumer.resolve(promise);
+}
+
 void FetchResponse::consumeBodyAsStream()
 {
     ASSERT(m_readableStreamSource);
index 573c2708d8dc94dda8105b4c960c51596854204f..848625140507d80c836d71955a1b3b6fdb28b26d 100644 (file)
@@ -33,6 +33,7 @@
 #include "FetchBodyOwner.h"
 #include "FetchHeaders.h"
 #include "ResourceResponse.h"
+#include <runtime/TypedArrays.h>
 
 namespace JSC {
 class ArrayBuffer;
@@ -59,6 +60,13 @@ public:
     using FetchPromise = DOMPromise<FetchResponse>;
     static void fetch(ScriptExecutionContext&, FetchRequest&, FetchPromise&&);
 
+    void consume(unsigned, DeferredWrapper&&);
+#if ENABLE(STREAMS_API)
+    void startConsumingStream(unsigned);
+    void consumeChunk(Ref<JSC::Uint8Array>&&);
+    void finishConsumingStream(DeferredWrapper&&);
+#endif
+
     void setStatus(int, const String&, ExceptionCode&);
     void initializeWith(JSC::ExecState&, JSC::JSValue);
 
@@ -70,7 +78,7 @@ public:
     const String& statusText() const { return m_response.httpStatusText(); }
 
     FetchHeaders& headers() { return m_headers; }
-    RefPtr<FetchResponse> clone(ScriptExecutionContext&, ExceptionCode&);
+    Ref<FetchResponse> cloneForJS();
 
 #if ENABLE(STREAMS_API)
     ReadableStreamSource* createReadableStreamSource();
@@ -105,7 +113,6 @@ private:
         void didFail() final;
         void didReceiveResponse(const ResourceResponse&) final;
         void didReceiveData(const char*, size_t) final;
-        void didFinishLoadingAsArrayBuffer(RefPtr<ArrayBuffer>&&) final;
 
         FetchResponse& m_response;
         Optional<FetchPromise> m_promise;
@@ -116,6 +123,8 @@ private:
     Ref<FetchHeaders> m_headers;
     Optional<BodyLoader> m_bodyLoader;
     mutable String m_responseURL;
+
+    FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::ArrayBuffer  };
 };
 
 } // namespace WebCore
index 3fb148814354ce7d1f840180bc990dfb23d0a92b..be3c2ce02937f6238f865d9219226b6fb1d1701c 100644 (file)
@@ -53,11 +53,25 @@ interface FetchResponse {
     readonly attribute FetchHeaders headers;
     [JSBuiltin] readonly attribute ReadableStream? body;
 
-    [NewObject, CallWith=ScriptExecutionContext, RaisesException] FetchResponse clone();
+    // Copy of FetchBody IDL as we want to implement some of these as built-ins.
+    [ImplementedAs=isDisturbed] readonly attribute boolean bodyUsed;
+    [JSBuiltin] Promise arrayBuffer();
+    [JSBuiltin] Promise blob();
+    [JSBuiltin] Promise formData();
+    [JSBuiltin] Promise json();
+    [JSBuiltin] Promise text();
 
+    [JSBuiltin] FetchResponse clone();
+
+    [PrivateIdentifier, NewObject] FetchResponse cloneForJS();
+
+    [Conditional=STREAMS_API, PrivateIdentifier] void startConsumingStream(unsigned short type);
+    [Conditional=STREAMS_API, PrivateIdentifier] void consumeChunk(Uint8Array chunk);
+    [Conditional=STREAMS_API, PrivateIdentifier] Promise finishConsumingStream();
+
+    [PrivateIdentifier] Promise consume(unsigned short type);
     [PrivateIdentifier, RaisesException] void setStatus(unsigned short status, DOMString statusText);
     [CallWith=ScriptState, PrivateIdentifier] void initializeWith(any body);
     [PrivateIdentifier, NewObject] ReadableStreamSource createReadableStreamSource();
     [PrivateIdentifier] boolean isDisturbed();
 };
-FetchResponse implements FetchBody;
index 3b6d95fe9428a7555bab8c417954ef9b8a82f297..ac328f33e9aabaef7773b5b0841080264a4cb895 100644 (file)
@@ -48,6 +48,12 @@ function initializeFetchResponse(body, init)
     if (body !== @undefined && body !== null) {
         if (status == 101 || status == 204 || status == 205 || status == 304)
             throw new @TypeError("Response cannot have a body with the given status");
+
+        // FIXME: Use @isReadableStream once it is no longer guarded by STREAMS_API guard.
+        let isBodyReadableStream = (@isObject(body) && !!body.@underlyingSource);
+        if (isBodyReadableStream)
+          this.@body = body;
+
         this.@initializeWith(body);
     }
 
@@ -71,3 +77,77 @@ function body()
     }
     return this.@body;
 }
+
+function clone()
+{
+    if (!this instanceof @Response)
+        throw new @TypeError("Function should be called on a Response");
+
+    if (@Response.prototype.@isDisturbed.@call(this))
+        throw new @TypeError("Cannot clone a disturbed Response");
+
+    var cloned = @Response.prototype.@cloneForJS.@call(this);
+    if (this.@body) {
+        var teedReadableStreams = @teeReadableStream(this.@body, false);
+        this.@body = teedReadableStreams[0];
+        cloned.@body = teedReadableStreams[1];
+    }
+    return cloned;
+}
+
+// consume and consumeStream single parameter should be kept in sync with FetchBodyConsumer::Type.
+function arrayBuffer()
+{
+    if (!this instanceof @Response)
+        throw new @TypeError("Function should be called on a Response");
+
+    const arrayBufferConsumerType = 1;
+    if (!this.@body)
+        return @Response.prototype.@consume.@call(this, arrayBufferConsumerType);
+
+    return @consumeStream(this, arrayBufferConsumerType);
+}
+
+function blob()
+{
+    if (!this instanceof @Response)
+        throw new @TypeError("Function should be called on a Response");
+
+    const blobConsumerType = 2;
+    if (!this.@body)
+        return @Response.prototype.@consume.@call(this, blobConsumerType);
+
+    return @consumeStream(this, blobConsumerType);
+}
+
+function formData()
+{
+    if (!this instanceof @Response)
+        throw new @TypeError("Function should be called on a Response");
+
+    return @Promise.@reject("Not implemented");
+}
+
+function json()
+{
+    if (!this instanceof @Response)
+        throw new @TypeError("Function should be called on a Response");
+
+    const jsonConsumerType = 3;
+    if (!this.@body)
+        return @Response.prototype.@consume.@call(this, jsonConsumerType);
+
+    return @consumeStream(this, jsonConsumerType);
+}
+
+function text()
+{
+    if (!this instanceof @Response)
+        throw new @TypeError("Function should be called on a Response");
+
+    const textConsumerType = 4;
+    if (!this.@body)
+        return @Response.prototype.@consume.@call(this, textConsumerType);
+
+    return @consumeStream(this, textConsumerType);
+}
index 67d2badf7f179b60d221613025cf64ba2e42ac45..3b8a366d6b8bf7ed79ab161c6655dd07a487c883 100644 (file)
                41BF700F0FE86F61005E8DEC /* PlatformMessagePortChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41BF700D0FE86F61005E8DEC /* PlatformMessagePortChannel.cpp */; };
                41BF70100FE86F61005E8DEC /* PlatformMessagePortChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 41BF700E0FE86F61005E8DEC /* PlatformMessagePortChannel.h */; };
                41C760B10EDE03D300C1655F /* ScriptState.h in Headers */ = {isa = PBXBuildFile; fileRef = 41C760B00EDE03D300C1655F /* ScriptState.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               41CF8BE71D46226700707DC9 /* FetchBodyConsumer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41CF8BE41D46222000707DC9 /* FetchBodyConsumer.cpp */; };
                41D015CA0F4B5C71004A662F /* ContentType.h in Headers */ = {isa = PBXBuildFile; fileRef = 41D015C80F4B5C71004A662F /* ContentType.h */; };
                41D015CB0F4B5C71004A662F /* ContentType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41D015C90F4B5C71004A662F /* ContentType.cpp */; };
                41E1B1D00FF5986900576B3B /* AbstractWorker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41E1B1CA0FF5986900576B3B /* AbstractWorker.cpp */; };
                41BF700D0FE86F61005E8DEC /* PlatformMessagePortChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PlatformMessagePortChannel.cpp; path = default/PlatformMessagePortChannel.cpp; sourceTree = "<group>"; };
                41BF700E0FE86F61005E8DEC /* PlatformMessagePortChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlatformMessagePortChannel.h; path = default/PlatformMessagePortChannel.h; sourceTree = "<group>"; };
                41C760B00EDE03D300C1655F /* ScriptState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptState.h; sourceTree = "<group>"; };
+               41CF8BE41D46222000707DC9 /* FetchBodyConsumer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FetchBodyConsumer.cpp; sourceTree = "<group>"; };
+               41CF8BE51D46222000707DC9 /* FetchBodyConsumer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FetchBodyConsumer.h; sourceTree = "<group>"; };
+               41CF8BE61D46222C00707DC9 /* FetchInternals.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = FetchInternals.js; sourceTree = "<group>"; };
                41D015C80F4B5C71004A662F /* ContentType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentType.h; sourceTree = "<group>"; };
                41D015C90F4B5C71004A662F /* ContentType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContentType.cpp; sourceTree = "<group>"; };
                41E1B1CA0FF5986900576B3B /* AbstractWorker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AbstractWorker.cpp; sourceTree = "<group>"; };
                                41F54F7D1C50C4F600338488 /* FetchBody.cpp */,
                                41F54F7E1C50C4F600338488 /* FetchBody.h */,
                                41F54F7F1C50C4F600338488 /* FetchBody.idl */,
+                               41CF8BE41D46222000707DC9 /* FetchBodyConsumer.cpp */,
+                               41CF8BE51D46222000707DC9 /* FetchBodyConsumer.h */,
                                4147E2B31C89912600A7E715 /* FetchBodyOwner.cpp */,
                                4147E2B21C88337F00A7E715 /* FetchBodyOwner.h */,
                                41F54F821C50C4F600338488 /* FetchHeaders.cpp */,
                                41F54F831C50C4F600338488 /* FetchHeaders.h */,
                                41F54F841C50C4F600338488 /* FetchHeaders.idl */,
                                41F54F851C50C4F600338488 /* FetchHeaders.js */,
+                               41CF8BE61D46222C00707DC9 /* FetchInternals.js */,
                                4147E2B41C89912600A7E715 /* FetchLoader.cpp */,
                                4147E2B51C89912600A7E715 /* FetchLoader.h */,
                                4147E2B61C89912600A7E715 /* FetchLoaderClient.h */,
                                BC06EDE30BFD6D0D00856E9D /* JSHTMLTableCellElement.cpp in Sources */,
                                BC06ED9D0BFD660600856E9D /* JSHTMLTableColElement.cpp in Sources */,
                                BC06EE040BFD71AA00856E9D /* JSHTMLTableElement.cpp in Sources */,
+                               41CF8BE71D46226700707DC9 /* FetchBodyConsumer.cpp in Sources */,
                                BC06ED9F0BFD660600856E9D /* JSHTMLTableRowElement.cpp in Sources */,
                                BC06ED060BFD5BAE00856E9D /* JSHTMLTableSectionElement.cpp in Sources */,
                                D6489D25166FFCF1007C031B /* JSHTMLTemplateElement.cpp in Sources */,
index 1275a32e678ddd8beca254bd1d6d4d19f024c287..4e22e334ff376ad3849bf0d10999b97bd9d9bc80 100644 (file)
@@ -118,6 +118,8 @@ public:
     template<class RejectResultType> typename std::enable_if<PromiseResultInspector<RejectResultType>::passByConstRef, void>::type
     reject(const RejectResultType& result) { rejectWithValue(result); }
 
+    template<class ResolveResultType> void resolveWithNewlyCreated(Ref<ResolveResultType>&&);
+
     void reject(ExceptionCode, const String& = { });
 
     JSDOMGlobalObject& globalObject() const;
@@ -181,6 +183,16 @@ inline void DeferredWrapper::resolveWithValue(ResolveResultType&& result)
     resolve(*exec, toJS(exec, m_globalObject.get(), std::forward<ResolveResultType>(result)));
 }
 
+template<class ResolveResultType>
+inline void DeferredWrapper::resolveWithNewlyCreated(Ref<ResolveResultType>&& result)
+{
+    ASSERT(m_deferred);
+    ASSERT(m_globalObject);
+    JSC::ExecState* exec = m_globalObject->globalExec();
+    JSC::JSLockHolder locker(exec);
+    resolve(*exec, toJSNewlyCreated(exec, m_globalObject.get(), WTFMove(result)));
+}
+
 template<class RejectResultType>
 inline void DeferredWrapper::rejectWithValue(RejectResultType&& result)
 {
index 1f455725954ec0a6da03b3542b7d36911daf8cc3..a266af60485d0c0b4c112de34207c8dc9bf52de2 100644 (file)
@@ -35,14 +35,18 @@ namespace WebCore {
     macro(addTrack) \
     macro(appendFromJS) \
     macro(body) \
+    macro(cloneForJS) \
     macro(closeRequested) \
     macro(closedPromiseCapability) \
+    macro(consume) \
+    macro(consumeChunk) \
     macro(controlledReadableStream) \
     macro(controller) \
     macro(createReadableStreamSource) \
     macro(disturbed) \
     macro(fetchRequest) \
     macro(fillFromJS) \
+    macro(finishConsumingStream) \
     macro(firstReadCallback) \
     macro(getUserMediaFromJS) \
     macro(getRemoteStreams) \
@@ -74,6 +78,7 @@ namespace WebCore {
     macro(setBody) \
     macro(setStatus) \
     macro(state) \
+    macro(startConsumingStream) \
     macro(started) \
     macro(startedPromise) \
     macro(storedError) \