LayoutTests/imported/w3c:
authoryouenn.fablet@crf.canon.fr <youenn.fablet@crf.canon.fr@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 17 Apr 2016 18:04:20 +0000 (18:04 +0000)
committeryouenn.fablet@crf.canon.fr <youenn.fablet@crf.canon.fr@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 17 Apr 2016 18:04:20 +0000 (18:04 +0000)
[Fetch API] Consume HTTP data as a ReadableStream
https://bugs.webkit.org/show_bug.cgi?id=138968

Reviewed by Alex Christensen.

* web-platform-tests/fetch/api/basic/stream-response-expected.txt:
* web-platform-tests/fetch/api/basic/stream-response-worker-expected.txt:
* web-platform-tests/fetch/api/request/request-consume.html:
* web-platform-tests/fetch/api/resources/data.json: Added.
* web-platform-tests/fetch/api/resources/utils.js:
(validateStreamFromString):
* web-platform-tests/fetch/api/response/response-cancel-stream-expected.txt: Added.
* web-platform-tests/fetch/api/response/response-cancel-stream.html: Added.
* web-platform-tests/fetch/api/response/response-clone-expected.txt:
* web-platform-tests/fetch/api/response/response-consume-stream-expected.txt: Added.
* web-platform-tests/fetch/api/response/response-consume-stream.html: Added.
* web-platform-tests/fetch/api/response/response-init-002-expected.txt:
* web-platform-tests/fetch/api/response/response-stream-disturbed-expected-1.txt: Added.
* web-platform-tests/fetch/api/response/response-stream-disturbed-1.html: Added.
* web-platform-tests/fetch/api/response/response-stream-disturbed-expected-2.txt: Added.
* web-platform-tests/fetch/api/response/response-stream-disturbed-2.html: Added.
* web-platform-tests/fetch/api/response/response-stream-disturbed-expected-3.txt: Added.
* web-platform-tests/fetch/api/response/response-stream-disturbed-3.html: Added.
* web-platform-tests/fetch/api/response/response-stream-disturbed-expected-4.txt: Added.
* web-platform-tests/fetch/api/response/response-stream-disturbed-4.html: Added.
* web-platform-tests/fetch/api/response/response-stream-disturbed-expected-5.txt: Added.
* web-platform-tests/fetch/api/response/response-stream-disturbed-5.html: Added.

Source/WebCore:
[Fetch API] Consume HTTP data as a ReadableStream
https://bugs.webkit.org/show_bug.cgi?id=138968

Reviewed by Alex Christensen.

This patch introduces ReadableStreamSource and ReadableStreamController which allow feeding a ReadableStream from DOM classes.
ReadableStreamSource is a base class for all DOM ReadableStream sources.
ReadableStreamController is a wrapper around JSReadableStreamController that can be invoked by DOM code to enqueue/close/error a ReadableStream.
A createReadableStream function is introduced to allow DOM classes creating ReadableStream.

Added support for a FetchResponse ReadableStream source.
Both synthetic FetchResponse and loading FetchResponse are supported.
A new "Stream" FetchLoader::Type is introduced to allow receiving data as chunks and feeding them to a ReadableStream through ReadableStreamSource.

Currently, FetchResponse is consumed and marked as disturbed as soon as a ReadableStreamSource is created.
This should be changed so that consumption happens on the first read call to the ReadableStreamReader, i.e. when stream gets disturbed.

FetchResponseSource never fulfills the start promise, which allows to enqueue, error or close the stream at any time.
FetchResponseSource must therefore always ensure to close or error the stream.
Added support for locked check in FetchResponse.

Tests: imported/w3c/web-platform-tests/fetch/api/response/response-cancel-stream.html
       imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream.html
       imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-1.html
       imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-2.html
       imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-3.html
       imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-4.html
       imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-5.html
Also covered by rebased tests.

* CMakeLists.txt:
* DerivedSources.make:
* Modules/fetch/FetchBody.cpp:
(WebCore::FetchBody::consumeAsStream): Fill stream with body data.
* Modules/fetch/FetchBody.h:
(WebCore::FetchBody::type): Added accessor to body type, used for assertions.
* Modules/fetch/FetchBodyOwner.cpp:
(WebCore::FetchBodyOwner::isDisturbed): Adding stream isLocked check.
(WebCore::FetchBodyOwner::blobLoadingSucceeded): Added assertion that body type is blob. Closing stream if created.
(WebCore::FetchBodyOwner::blobLoadingFailed): Erroring the stream if created and not cancelled.
(WebCore::FetchBodyOwner::blobChunk): Filling stream with chunk.
(WebCore::FetchBodyOwner::stop): Rmoved call to finishBlobLoading as it should be called as part of FetchLoaderCLient::didFail callbacki.
* Modules/fetch/FetchBodyOwner.h:
* Modules/fetch/FetchLoader.cpp: Fixing the case of cancel being called when creating the ThreadableLoader by introducing FetchLoader::m_isStarted.
(WebCore::FetchLoader::start): Setting m_isStarted at the end of the start method.
(WebCore::FetchLoader::stop): Fixing the case that FetchLoader can be destroyed when cancelling its loader.
(WebCore::FetchLoader::startStreaming): Introduced to switch the loading type from ArayBuffer to Stream. Already buffered data is returned.
(WebCore::FetchLoader::didReceiveData): Handling of the new Stream type.
(WebCore::FetchLoader::didFinishLoading):
* Modules/fetch/FetchLoader.h:
* Modules/fetch/FetchLoaderClient.h:
(WebCore::FetchLoaderClient::didReceiveData): Callback to get data as chunks if loader is of type Stream.
* Modules/fetch/FetchResponse.cpp:
(WebCore::FetchResponse::clone): Removed m_isLocked as it is handled within isDisturbed().
(WebCore::FetchResponse::isDisturbed): Checking whether related ReadableStream is locked.
(WebCore::FetchResponse::BodyLoader::didSucceed): Introduced to handle ReadableStream case.
(WebCore::FetchResponse::BodyLoader::didFail): Ditto.
(WebCore::FetchResponse::BodyLoader::didReceiveData): Ditto.
(WebCore::FetchResponse::BodyLoader::startStreaming): Ditto.
(WebCore::FetchResponse::consumeBodyAsStream): Start filling the ReadableStream with data. Changing loader to Stream if there is one.
(WebCore::FetchResponse::createReadableStreamSource): Called by custom binding to create the source.
(WebCore::FetchResponse::stop): Fixing potential crash in case of cancelling the ibody stream.
(WebCore::FetchResponse::startFetching):
(WebCore::FetchResponse::BodyLoader::didFinishLoadingAsArrayBuffer):
* Modules/fetch/FetchResponse.h:
* Modules/fetch/FetchResponse.idl:
* Modules/fetch/FetchResponseSource.cpp: Specialization of ReadableStreamSource for FetchResponse. It is a push source that never resolves the start promise.
(WebCore::FetchResponseSource::FetchResponseSource):
(WebCore::FetchResponseSource::isReadableStreamLocked):
(WebCore::FetchResponseSource::setActive):
(WebCore::FetchResponseSource::setInactive):
(WebCore::FetchResponseSource::doStart):
(WebCore::FetchResponseSource::doCancel):
(WebCore::FetchResponseSource::close):
(WebCore::FetchResponseSource::error):
* Modules/fetch/FetchResponseSource.h: Added.
* Modules/streams/ReadableStreamController.js:
(error):
* Modules/streams/ReadableStreamSource.h: Added (base class for ReadableStream DOM sources).
(WebCore::ReadableStreamSource::~ReadableStreamSource):
(WebCore::ReadableStreamSource::isStarting):
(WebCore::ReadableStreamSource::isPulling):
(WebCore::ReadableStreamSource::isCancelling):
(WebCore::ReadableStreamSource::controller):
(WebCore::ReadableStreamSource::doStart):
(WebCore::ReadableStreamSource::doCancel):
(WebCore::ReadableStreamSource::start):
(WebCore::ReadableStreamSource::cancel):
(WebCore::ReadableStreamSource::startFinished):
(WebCore::ReadableStreamSource::clean):
* Modules/streams/ReadableStreamSource.idl: Added.
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/JSDOMGlobalObject.h:
* bindings/js/JSFetchResponseCustom.cpp: In case body is not created, call createReadableStreamSource.
(WebCore::JSFetchResponse::body):
* bindings/js/JSReadableStreamSourceCustom.cpp: Added.
(WebCore::JSReadableStreamSource::start):
(WebCore::JSReadableStreamSource::pull):
(WebCore::JSReadableStreamSource::controller):
* bindings/js/ReadableStreamController.cpp: Added.
(WebCore::callFunction):
(WebCore::ReadableStreamController::invoke):
(WebCore::ReadableStreamController::isControlledReadableStreamLocked):
(WebCore::createReadableStream):
* bindings/js/ReadableStreamController.h: The DOM wrapper for JSReadableStreamController.
(WebCore::ReadableStreamController::ReadableStreamController):
(WebCore::ReadableStreamController::close):
(WebCore::ReadableStreamController::error):
(WebCore::ReadableStreamController::enqueue):
(WebCore::ReadableStreamController::globalObject):
(WebCore::ReadableStreamController::enqueue<RefPtr<JSC::ArrayBuffer>>):
(WebCore::ReadableStreamController::error<String>):

LayoutTests:
[Streams] Consume HTTP data as a ReadableStream
https://bugs.webkit.org/show_bug.cgi?id=138968

Reviewed by Alex Christensen.

* fast/xmlhttprequest/xmlhttprequest-responsetype-stream-expected.txt: Added.
* fast/xmlhttprequest/xmlhttprequest-responsetype-stream.html: Added.
* http/tests/xmlhttprequest/streams/streams-read-api-cancelled-expected.txt: Added.
* http/tests/xmlhttprequest/streams/streams-read-api-cancelled.html: Added.
* http/tests/xmlhttprequest/streams/streams-read-api-closed-expected.txt: Added.
* http/tests/xmlhttprequest/streams/streams-read-api-closed.html: Added.
* http/tests/xmlhttprequest/streams/streams-read-api-expected.txt: Added.
* http/tests/xmlhttprequest/streams/streams-read-api.html: Added.
* http/tests/xmlhttprequest/streams/streams-read-expected.txt: Added.
* http/tests/xmlhttprequest/streams/streams-read.html: Added.

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

47 files changed:
LayoutTests/ChangeLog
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/fetch/api/basic/stream-response-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/basic/stream-response-worker-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-consume.html
LayoutTests/imported/w3c/web-platform-tests/fetch/api/resources/data.json [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/resources/utils.js
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-cancel-stream-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-cancel-stream.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-clone-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-init-002-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-1-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-1.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-2-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-2.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-3-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-3.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-4-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-4.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-5-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-5.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/DerivedSources.make
Source/WebCore/Modules/fetch/FetchBody.cpp
Source/WebCore/Modules/fetch/FetchBody.h
Source/WebCore/Modules/fetch/FetchBodyOwner.cpp
Source/WebCore/Modules/fetch/FetchBodyOwner.h
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/FetchResponseSource.cpp [new file with mode: 0644]
Source/WebCore/Modules/fetch/FetchResponseSource.h [new file with mode: 0644]
Source/WebCore/Modules/streams/ReadableStreamController.js
Source/WebCore/Modules/streams/ReadableStreamSource.h [new file with mode: 0644]
Source/WebCore/Modules/streams/ReadableStreamSource.idl [new file with mode: 0644]
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/JSDOMGlobalObject.h
Source/WebCore/bindings/js/JSFetchResponseCustom.cpp [new file with mode: 0644]
Source/WebCore/bindings/js/JSReadableStreamSourceCustom.cpp [new file with mode: 0644]
Source/WebCore/bindings/js/ReadableStreamController.cpp [new file with mode: 0644]
Source/WebCore/bindings/js/ReadableStreamController.h [new file with mode: 0644]

index 6b9aace8ac2b80e3cf5a97f7c2aa4eb53b9d5bcf..4c8e8e4f5f0ff481fdc058c9a8232bf6ffb59bbc 100644 (file)
@@ -1,3 +1,21 @@
+2016-04-17  Youenn Fablet  <youenn.fablet@crf.canon.fr>
+
+        [Streams] Consume HTTP data as a ReadableStream
+        https://bugs.webkit.org/show_bug.cgi?id=138968
+
+        Reviewed by Alex Christensen.
+
+        * fast/xmlhttprequest/xmlhttprequest-responsetype-stream-expected.txt: Added.
+        * fast/xmlhttprequest/xmlhttprequest-responsetype-stream.html: Added.
+        * http/tests/xmlhttprequest/streams/streams-read-api-cancelled-expected.txt: Added.
+        * http/tests/xmlhttprequest/streams/streams-read-api-cancelled.html: Added.
+        * http/tests/xmlhttprequest/streams/streams-read-api-closed-expected.txt: Added.
+        * http/tests/xmlhttprequest/streams/streams-read-api-closed.html: Added.
+        * http/tests/xmlhttprequest/streams/streams-read-api-expected.txt: Added.
+        * http/tests/xmlhttprequest/streams/streams-read-api.html: Added.
+        * http/tests/xmlhttprequest/streams/streams-read-expected.txt: Added.
+        * http/tests/xmlhttprequest/streams/streams-read.html: Added.
+
 2016-04-16  Matt Baker  <mattbaker@apple.com>
 
         Web Inspector: Adopt Number.prototype.toLocaleString For All Sizes and Times
index 78ea0078c1b8dd881c344e7a480c05d2ad230317..48083dcd4942918f91b80520d34ca9db3dead72b 100644 (file)
@@ -1,3 +1,33 @@
+2016-04-17  Youenn Fablet  <youenn.fablet@crf.canon.fr>
+
+        [Fetch API] Consume HTTP data as a ReadableStream
+        https://bugs.webkit.org/show_bug.cgi?id=138968
+
+        Reviewed by Alex Christensen.
+
+        * web-platform-tests/fetch/api/basic/stream-response-expected.txt:
+        * web-platform-tests/fetch/api/basic/stream-response-worker-expected.txt:
+        * web-platform-tests/fetch/api/request/request-consume.html:
+        * web-platform-tests/fetch/api/resources/data.json: Added.
+        * web-platform-tests/fetch/api/resources/utils.js:
+        (validateStreamFromString):
+        * web-platform-tests/fetch/api/response/response-cancel-stream-expected.txt: Added.
+        * web-platform-tests/fetch/api/response/response-cancel-stream.html: Added.
+        * web-platform-tests/fetch/api/response/response-clone-expected.txt:
+        * web-platform-tests/fetch/api/response/response-consume-stream-expected.txt: Added.
+        * web-platform-tests/fetch/api/response/response-consume-stream.html: Added.
+        * web-platform-tests/fetch/api/response/response-init-002-expected.txt:
+        * web-platform-tests/fetch/api/response/response-stream-disturbed-expected-1.txt: Added.
+        * web-platform-tests/fetch/api/response/response-stream-disturbed-1.html: Added.
+        * web-platform-tests/fetch/api/response/response-stream-disturbed-expected-2.txt: Added.
+        * web-platform-tests/fetch/api/response/response-stream-disturbed-2.html: Added.
+        * web-platform-tests/fetch/api/response/response-stream-disturbed-expected-3.txt: Added.
+        * web-platform-tests/fetch/api/response/response-stream-disturbed-3.html: Added.
+        * web-platform-tests/fetch/api/response/response-stream-disturbed-expected-4.txt: Added.
+        * web-platform-tests/fetch/api/response/response-stream-disturbed-4.html: Added.
+        * web-platform-tests/fetch/api/response/response-stream-disturbed-expected-5.txt: Added.
+        * web-platform-tests/fetch/api/response/response-stream-disturbed-5.html: Added.
+
 2016-04-11  Chris Dumez  <cdumez@apple.com>
 
         DOMTokenList.contains() should not throw
index d584d0103a6d37bf09b875ef3993dae3fea9f253..585775e6c14e57c06cdfdac25c89e52f228f1788 100644 (file)
@@ -1,3 +1,3 @@
 
-FAIL Stream response's body assert_unreached: Body does not exist in response Reached unreachable code
+PASS Stream response's body 
 
index d584d0103a6d37bf09b875ef3993dae3fea9f253..585775e6c14e57c06cdfdac25c89e52f228f1788 100644 (file)
@@ -1,3 +1,3 @@
 
-FAIL Stream response's body assert_unreached: Body does not exist in response Reached unreachable code
+PASS Stream response's body 
 
index 2b12b63ef157623cd2f7cf2ce025984703395db2..47c3b6f95c7481e80ed50d981dd86ca6a55b0c68 100644 (file)
@@ -11,6 +11,7 @@
     <script src="../resources/utils.js"></script>
   </head>
   <body>
+    <script src="../resources/utils.js"></script>
     <script>
     function checkBodyText(request, expectedBody) {
       return request.text().then( function(bodyAsText) {
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/resources/data.json b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/resources/data.json
new file mode 100644 (file)
index 0000000..76519fa
--- /dev/null
@@ -0,0 +1 @@
+{"key": "value"}
index f97050dc879755ae88032738c685baaf51e8e1af..c5489c0ead645428bbf1065f15d1e2e08501b81c 100644 (file)
@@ -61,14 +61,16 @@ function validateStreamFromString(reader, expectedValue, retrievedArrayBuffer) {
     if (!data.done) {
       var newBuffer;
       if (retrievedArrayBuffer) {
-        newBuffer =  new ArrayBuffer(data.value.length + retrievedArrayBuffer.length);
+        newBuffer =  new ArrayBuffer(data.value.byteLength + retrievedArrayBuffer.byteLength);
         newBuffer.set(retrievedArrayBuffer, 0);
-        newBuffer.set(data.value, retrievedArrayBuffer.length);
+        newBuffer.set(data.value, retrievedArrayBuffer.byteLength);
       } else {
         newBuffer = data.value;
       }
       return validateStreamFromString(reader, expectedValue, newBuffer);
     }
+    if (!retrievedArrayBuffer)
+        retrievedArrayBuffer = new Uint8Array();
     validateBufferFromString(retrievedArrayBuffer, expectedValue, "Retrieve and verify stream");
   });
 }
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-cancel-stream-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-cancel-stream-expected.txt
new file mode 100644 (file)
index 0000000..a74c17b
--- /dev/null
@@ -0,0 +1,8 @@
+
+PASS Cancelling a starting blob Response stream 
+PASS Cancelling a loading blob Response stream 
+PASS Cancelling a closed blob Response stream 
+PASS Cancelling a starting Response stream 
+PASS Cancelling a loading Response stream 
+PASS Cancelling a closed Response stream 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-cancel-stream.html b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-cancel-stream.html
new file mode 100644 (file)
index 0000000..ab24dd7
--- /dev/null
@@ -0,0 +1,71 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Response consume empty bodies</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#response">
+    <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin">
+    <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script src="../resources/utils.js"></script>
+    <script>
+
+promise_test(function(test) {
+    return new Response(new Blob([], { "type" : "text/plain" })).body.cancel();
+}, "Cancelling a starting blob Response stream");
+
+promise_test(function(test) {
+    var response = new Response(new Blob(["This is data"], { "type" : "text/plain" }));
+    var reader = response.body.getReader();
+    reader.read();
+    return reader.cancel();
+}, "Cancelling a loading blob Response stream");
+
+promise_test(function(test) {
+    var response = new Response(new Blob(["T"], { "type" : "text/plain" }));
+    var reader = response.body.getReader();
+    var resolve;
+    var promise = new Promise(function(_resolve, _reject) {
+        resolve = _resolve;
+    });
+    promise.then(function() {
+        return reader.read().then(function(value) {
+        return reader.cancel();
+        });
+    });
+    setTimeout(resolve, 100);
+    return promise;
+}, "Cancelling a closed blob Response stream");
+
+promise_test(function(test) {
+    return fetch(RESOURCES_DIR + "trickle.py?ms=30&count=100").then(function(response) {
+        return response.body.cancel();
+    });
+}, "Cancelling a starting Response stream");
+
+promise_test(function() {
+    return fetch(RESOURCES_DIR + "trickle.py?ms=30&count=100").then(function(response) {
+        var reader = response.body.getReader();
+        return reader.read().then(function() {
+            return reader.cancel();
+        });
+    });
+}, "Cancelling a loading Response stream");
+
+promise_test(function() {
+    return fetch(RESOURCES_DIR + "top.txt").then(function(response) {
+        var reader = response.body.getReader();
+        var closedPromise = reader.closed.then(function() {
+            return reader.cancel();
+        });
+        reader.read();
+        return closedPromise;
+    });
+}, "Cancelling a closed Response stream");
+
+    </script>
+  </body>
+</html>
index 5fa8fa44d3f5a96d45b844e5737d2b2af64fc554..2986d8707e62db9a4aa6becfd1b78553903bb6f8 100644 (file)
@@ -1,7 +1,7 @@
 
 PASS Check Response's clone with default values, without body 
 PASS Check Response's clone has the expected attribute values 
-FAIL Check orginal response's body after cloning null is not an object (evaluating 'response.body.getReader')
-FAIL Check cloned response's body null is not an object (evaluating 'clonedResponse.body.getReader')
+PASS Check orginal response's body after cloning 
+PASS Check cloned response's body 
 FAIL Cannot clone a disturbed response assert_true: response is disturbed expected true got false
 
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream-expected.txt
new file mode 100644 (file)
index 0000000..01a3bd0
--- /dev/null
@@ -0,0 +1,9 @@
+
+PASS Read empty text response's body as readableStream 
+PASS Read empty blob response's body as readableStream 
+PASS Read blob response's body as readableStream 
+PASS Read text response's body as readableStream 
+FAIL Read form data response's body as readableStream promise_test: Unhandled rejection with value: "not implemented"
+PASS Getting an error Response stream 
+PASS Getting a redirect Response stream 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream.html b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream.html
new file mode 100644 (file)
index 0000000..be33935
--- /dev/null
@@ -0,0 +1,56 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Response consume</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#response">
+    <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin">
+    <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="../resources/utils.js"></script>
+  </head>
+  <body>
+    <script>
+    promise_test(function(test) {
+      var body = "";
+      var response = new Response("");
+      return validateStreamFromString(response.body.getReader(), "");
+    }, "Read empty text response's body as readableStream");
+
+    promise_test(function(test) {
+      var response = new Response(new Blob([], { "type" : "text/plain" }));
+      return validateStreamFromString(response.body.getReader(), "");
+    }, "Read empty blob response's body as readableStream");
+
+    var formData = new FormData();
+    formData.append("name", "value");
+    var textData = JSON.stringify("This is response's body");
+    var blob = new Blob([textData], { "type" : "text/plain" });
+
+    promise_test(function(test) {
+      var response = new Response(blob);
+      return validateStreamFromString(response.body.getReader(), textData);
+    }, "Read blob response's body as readableStream");
+
+    promise_test(function(test) {
+      var response = new Response(textData);
+      return validateStreamFromString(response.body.getReader(), textData);
+    }, "Read text response's body as readableStream");
+
+    promise_test(function(test) {
+      var response = new Response(formData);
+      return validateStreamFromString(response.body.getReader(), "name=value");
+    }, "Read form data response's body as readableStream");
+
+    test(function() {
+        assert_equals(Response.error().body, null);
+    }, "Getting an error Response stream");
+
+    promise_test(function(test) {
+        assert_equals(Response.redirect(301).body, null);
+    }, "Getting a redirect Response stream");
+
+    </script>
+  </body>
+</html>
index 4167d0f2d78b3f3b6cf57f13482f2c198fec195d..3d6b73f99e6a94d3f352b66300fdc1de469edd62 100644 (file)
@@ -4,5 +4,5 @@ PASS Initialize Response's body with application/octet-binary
 FAIL Initialize Response's body with multipart/form-data promise_test: Unhandled rejection with value: undefined
 FAIL Initialize Response's body with application/x-www-form-urlencoded;charset=UTF-8 assert_true: Content-Type header should be "application/x-www-form-urlencoded;charset=UTF-8"  expected true got false
 PASS Initialize Response's body with text/plain;charset=UTF-8 
-FAIL Read Response's body as readableStream null is not an object (evaluating 'response.body.getReader')
+PASS Read Response's body as readableStream 
 
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-1-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-1-expected.txt
new file mode 100644 (file)
index 0000000..72bcec2
--- /dev/null
@@ -0,0 +1,6 @@
+
+FAIL Getting blob after getting the Response body - not disturbed, not locked promise_test: Unhandled rejection with value: object "TypeError: Type error"
+FAIL Getting text after getting the Response body - not disturbed, not locked promise_test: Unhandled rejection with value: object "TypeError: Type error"
+FAIL Getting json after getting the Response body - not disturbed, not locked promise_test: Unhandled rejection with value: object "TypeError: Type error"
+FAIL Getting arrayBuffer after getting the Response body - not disturbed, not locked promise_test: Unhandled rejection with value: object "TypeError: Type error"
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-1.html b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-1.html
new file mode 100644 (file)
index 0000000..dda81d6
--- /dev/null
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Consuming Response body after getting a ReadableStream</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#response">
+    <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin">
+    <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script>
+
+function createResponseWithReadableStream(callback) {
+    return fetch("../resources/data.json").then(function(response) {
+        var reader = response.body.getReader();
+        reader.releaseLock();
+        return callback(response);
+    });
+}
+
+function createResponseWithCancelledReadableStream(callback) {
+    return fetch("../resources/data.json").then(function(response) {
+        response.body.cancel();
+        return callback(response);
+    });
+}
+
+promise_test(function() {
+    return createResponseWithReadableStream(function(response) {
+        return response.blob().then(function(blob) {
+            assert_true(blob instanceof Blob);
+        });
+    });
+}, "Getting blob after getting the Response body - not disturbed, not locked");
+
+promise_test(function() {
+    return createResponseWithReadableStream(function(response) {
+        return response.text().then(function(text) {
+            assert_true(text.length > 0);
+        });
+    });
+}, "Getting text after getting the Response body - not disturbed, not locked");
+
+promise_test(function() {
+    return createResponseWithReadableStream(function(response) {
+        return response.json().then(function(json) {
+            assert_true(typeof json === "object");
+        });
+    });
+}, "Getting json after getting the Response body - not disturbed, not locked");
+
+promise_test(function() {
+    return createResponseWithReadableStream(function(response) {
+        return response.arrayBuffer().then(function(arrayBuffer) {
+            assert_true(arrayBuffer.byteLength > 0);
+        });
+    });
+}, "Getting arrayBuffer after getting the Response body - not disturbed, not locked");
+
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-2-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-2-expected.txt
new file mode 100644 (file)
index 0000000..b977165
--- /dev/null
@@ -0,0 +1,6 @@
+
+FAIL Getting blob after getting a locked Response body Can't find variable: createResponseWithReadableStream
+FAIL Getting text after getting a locked Response body Can't find variable: createResponseWithReadableStream
+FAIL Getting json after getting a locked Response body Can't find variable: createResponseWithReadableStream
+FAIL Getting arraybuffer after getting a locked Response body Can't find variable: createResponseWithReadableStream
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-2.html b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-2.html
new file mode 100644 (file)
index 0000000..5192901
--- /dev/null
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Consuming Response body after getting a ReadableStream</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#response">
+    <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin">
+    <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script>
+
+function createResponseWithLockedReadableStream(callback) {
+    return fetch("../resources/data.json").then(function(response) {
+        var reader = response.body.getReader();
+        return callback(response);
+    });
+}
+
+function createResponseWithCancelledReadableStream(callback) {
+    return fetch("../resources/data.json").then(function(response) {
+        response.body.cancel();
+        return callback(response);
+    });
+}
+
+promise_test(function() {
+    return createResponseWithReadableStream(function(response) {
+        return response.blob().then(function(blob) {
+            assert_true(blob instanceof Blob);
+        });
+    });
+}, "Getting blob after getting a locked Response body");
+
+promise_test(function() {
+    return createResponseWithReadableStream(function(response) {
+        return response.text().then(function(text) {
+            assert_true(text.length > 0);
+        });
+    });
+}, "Getting text after getting a locked Response body");
+
+promise_test(function() {
+    return createResponseWithReadableStream(function(response) {
+        return response.json().then(function(json) {
+            assert_true(typeof json === "object");
+        });
+    });
+}, "Getting json after getting a locked Response body");
+
+promise_test(function() {
+    return createResponseWithReadableStream(function(response) {
+        return response.arrayBuffer().then(function(arrayBuffer) {
+            assert_true(arrayBuffer.byteLength > 0);
+        });
+    });
+}, "Getting arraybuffer after getting a locked Response body");
+
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-3-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-3-expected.txt
new file mode 100644 (file)
index 0000000..e0944db
--- /dev/null
@@ -0,0 +1,6 @@
+
+PASS Getting blob after reading the Response body 
+PASS Getting text after reading the Response body 
+PASS Getting json after reading the Response body 
+PASS Getting arrayBuffer after reading the Response body 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-3.html b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-3.html
new file mode 100644 (file)
index 0000000..8d92125
--- /dev/null
@@ -0,0 +1,49 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Consuming Response body after getting a ReadableStream</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#response">
+    <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin">
+    <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script>
+
+function createResponseWithDisturbedReadableStream(callback) {
+    return fetch("../resources/data.json").then(function(response) {
+        var reader = response.body.getReader();
+        reader.read();
+        return callback(response);
+    });
+}
+
+promise_test(function(test) {
+    return createResponseWithDisturbedReadableStream(function(response) {
+        return promise_rejects(test, new TypeError(), response.blob());
+    });
+}, "Getting blob after reading the Response body");
+
+promise_test(function(test) {
+    return createResponseWithDisturbedReadableStream(function(response) {
+        return promise_rejects(test, new TypeError(), response.text());
+    });
+}, "Getting text after reading the Response body");
+
+promise_test(function(test) {
+    return createResponseWithDisturbedReadableStream(function(response) {
+        return promise_rejects(test, new TypeError(), response.json());
+    });
+}, "Getting json after reading the Response body");
+
+promise_test(function(test) {
+    return createResponseWithDisturbedReadableStream(function(response) {
+        return promise_rejects(test, new TypeError(), response.arrayBuffer());
+    });
+}, "Getting arrayBuffer after reading the Response body");
+
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-4-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-4-expected.txt
new file mode 100644 (file)
index 0000000..c2673c2
--- /dev/null
@@ -0,0 +1,6 @@
+
+PASS Getting blob after cancelling the Response body 
+PASS Getting text after cancelling the Response body 
+PASS Getting json after cancelling the Response body 
+PASS Getting arrayBuffer after cancelling the Response body 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-4.html b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-4.html
new file mode 100644 (file)
index 0000000..5bc9413
--- /dev/null
@@ -0,0 +1,50 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Consuming Response body after getting a ReadableStream</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#response">
+    <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin">
+    <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script>
+
+function createResponseWithCancelledReadableStream(callback) {
+    return fetch("../resources/data.json").then(function(response) {
+        response.body.cancel();
+        return callback(response);
+    });
+}
+
+promise_test(function(test) {
+    return createResponseWithCancelledReadableStream(function(response) {
+        return promise_rejects(test, new TypeError(), response.blob());
+    });
+}, "Getting blob after cancelling the Response body");
+
+promise_test(function(test) {
+    return createResponseWithCancelledReadableStream(function(response) {
+        return promise_rejects(test, new TypeError(), response.text());
+    });
+    return promise_rejects(test, new TypeError(), createResponseWithCancelledReadableStream().text());
+}, "Getting text after cancelling the Response body");
+
+promise_test(function(test) {
+    return createResponseWithCancelledReadableStream(function(response) {
+        return promise_rejects(test, new TypeError(), response.json());
+    });
+    return promise_rejects(test, new TypeError(), createResponseWithCancelledReadableStream().json());
+}, "Getting json after cancelling the Response body");
+
+promise_test(function(test) {
+    return createResponseWithCancelledReadableStream(function(response) {
+        return promise_rejects(test, new TypeError(), response.arrayBuffer());
+    });
+}, "Getting arrayBuffer after cancelling the Response body");
+
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-5-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-5-expected.txt
new file mode 100644 (file)
index 0000000..70b64f4
--- /dev/null
@@ -0,0 +1,6 @@
+
+PASS Getting a null body after consuming as blob 
+PASS Getting a null body after consuming as text 
+PASS Getting a null body after consuming as json 
+PASS Getting a null body after consuming as arrayBuffer 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-5.html b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-5.html
new file mode 100644 (file)
index 0000000..42f6747
--- /dev/null
@@ -0,0 +1,45 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Consuming Response body after getting a ReadableStream</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#response">
+    <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin">
+    <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script>
+        
+promise_test(function() {
+    return fetch("../resources/data.json").then(function(response) {
+        response.blob();
+        assert_equals(response.body, null);
+    });
+}, "Getting a null body after consuming as blob");
+
+promise_test(function() {
+    return fetch("../resources/data.json").then(function(response) {
+        response.text();
+        assert_equals(response.body, null);
+    });
+}, "Getting a null body after consuming as text");
+
+promise_test(function() {
+    return fetch("../resources/data.json").then(function(response) {
+        response.json();
+        assert_equals(response.body, null);
+    });
+}, "Getting a null body after consuming as json");
+
+promise_test(function() {
+    return fetch("../resources/data.json").then(function(response) {
+        response.arrayBuffer();
+        assert_equals(response.body, null);
+    });
+}, "Getting a null body after consuming as arrayBuffer");
+
+    </script>
+  </body>
+</html>
index 5e621efbf46688c4e5c92ae956a557b89b591e7d..884451cbad2028e77007da7f099c224a6811f6a3 100644 (file)
@@ -264,6 +264,7 @@ set(WebCore_NON_SVG_IDL_FILES
     Modules/streams/ReadableStream.idl
     Modules/streams/ReadableStreamController.idl
     Modules/streams/ReadableStreamReader.idl
+    Modules/streams/ReadableStreamSource.idl
     Modules/streams/WritableStream.idl
 
     Modules/vibration/NavigatorVibration.idl
@@ -822,6 +823,7 @@ set(WebCore_SOURCES
     Modules/fetch/FetchLoader.cpp
     Modules/fetch/FetchRequest.cpp
     Modules/fetch/FetchResponse.cpp
+    Modules/fetch/FetchResponseSource.cpp
     Modules/fetch/WorkerGlobalScopeFetch.cpp
 
     Modules/geolocation/Coordinates.cpp
@@ -1143,6 +1145,7 @@ set(WebCore_SOURCES
     bindings/js/JSEventListener.cpp
     bindings/js/JSEventTargetCustom.cpp
     bindings/js/JSExceptionBase.cpp
+    bindings/js/JSFetchResponseCustom.cpp
     bindings/js/JSFileReaderCustom.cpp
     bindings/js/JSGeolocationCustom.cpp
     bindings/js/JSHTMLAllCollectionCustom.cpp
@@ -1195,6 +1198,7 @@ set(WebCore_SOURCES
     bindings/js/JSPluginElementFunctions.cpp
     bindings/js/JSPopStateEventCustom.cpp
     bindings/js/JSReadableStreamPrivateConstructors.cpp
+    bindings/js/JSReadableStreamSourceCustom.cpp
     bindings/js/JSRTCIceCandidateCustom.cpp
     bindings/js/JSRTCPeerConnectionCustom.cpp
     bindings/js/JSRTCSessionDescriptionCustom.cpp
@@ -1225,6 +1229,7 @@ set(WebCore_SOURCES
     bindings/js/JSXMLHttpRequestCustom.cpp
     bindings/js/JSXPathResultCustom.cpp
     bindings/js/JSXSLTProcessorCustom.cpp
+    bindings/js/ReadableStreamController.cpp
     bindings/js/ScheduledAction.cpp
     bindings/js/ScriptCachedFrameData.cpp
     bindings/js/ScriptController.cpp
index 30213771980d351a2b3c84cf1d194fee6391d812..1a15eca8a25297c550a97c45f0eca0b511afa840 100644 (file)
@@ -1,3 +1,118 @@
+2016-04-17  Youenn Fablet  <youenn.fablet@crf.canon.fr>
+
+        [Fetch API] Consume HTTP data as a ReadableStream
+        https://bugs.webkit.org/show_bug.cgi?id=138968
+
+        Reviewed by Alex Christensen.
+
+        This patch introduces ReadableStreamSource and ReadableStreamController which allow feeding a ReadableStream from DOM classes.
+        ReadableStreamSource is a base class for all DOM ReadableStream sources.
+        ReadableStreamController is a wrapper around JSReadableStreamController that can be invoked by DOM code to enqueue/close/error a ReadableStream.
+        A createReadableStream function is introduced to allow DOM classes creating ReadableStream.
+
+        Added support for a FetchResponse ReadableStream source.
+        Both synthetic FetchResponse and loading FetchResponse are supported.
+        A new "Stream" FetchLoader::Type is introduced to allow receiving data as chunks and feeding them to a ReadableStream through ReadableStreamSource.
+
+        Currently, FetchResponse is consumed and marked as disturbed as soon as a ReadableStreamSource is created.
+        This should be changed so that consumption happens on the first read call to the ReadableStreamReader, i.e. when stream gets disturbed.
+
+        FetchResponseSource never fulfills the start promise, which allows to enqueue, error or close the stream at any time.
+        FetchResponseSource must therefore always ensure to close or error the stream.
+        Added support for locked check in FetchResponse.
+
+        Tests: imported/w3c/web-platform-tests/fetch/api/response/response-cancel-stream.html
+               imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream.html
+               imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-1.html
+               imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-2.html
+               imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-3.html
+               imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-4.html
+               imported/w3c/web-platform-tests/fetch/api/response/response-stream-disturbed-5.html
+        Also covered by rebased tests.
+
+        * CMakeLists.txt:
+        * DerivedSources.make:
+        * Modules/fetch/FetchBody.cpp:
+        (WebCore::FetchBody::consumeAsStream): Fill stream with body data.
+        * Modules/fetch/FetchBody.h:
+        (WebCore::FetchBody::type): Added accessor to body type, used for assertions.
+        * Modules/fetch/FetchBodyOwner.cpp:
+        (WebCore::FetchBodyOwner::isDisturbed): Adding stream isLocked check.
+        (WebCore::FetchBodyOwner::blobLoadingSucceeded): Added assertion that body type is blob. Closing stream if created.
+        (WebCore::FetchBodyOwner::blobLoadingFailed): Erroring the stream if created and not cancelled.
+        (WebCore::FetchBodyOwner::blobChunk): Filling stream with chunk.
+        (WebCore::FetchBodyOwner::stop): Rmoved call to finishBlobLoading as it should be called as part of FetchLoaderCLient::didFail callbacki.
+        * Modules/fetch/FetchBodyOwner.h:
+        * Modules/fetch/FetchLoader.cpp: Fixing the case of cancel being called when creating the ThreadableLoader by introducing FetchLoader::m_isStarted.
+        (WebCore::FetchLoader::start): Setting m_isStarted at the end of the start method.
+        (WebCore::FetchLoader::stop): Fixing the case that FetchLoader can be destroyed when cancelling its loader.
+        (WebCore::FetchLoader::startStreaming): Introduced to switch the loading type from ArayBuffer to Stream. Already buffered data is returned.
+        (WebCore::FetchLoader::didReceiveData): Handling of the new Stream type.
+        (WebCore::FetchLoader::didFinishLoading):
+        * Modules/fetch/FetchLoader.h:
+        * Modules/fetch/FetchLoaderClient.h:
+        (WebCore::FetchLoaderClient::didReceiveData): Callback to get data as chunks if loader is of type Stream.
+        * Modules/fetch/FetchResponse.cpp:
+        (WebCore::FetchResponse::clone): Removed m_isLocked as it is handled within isDisturbed().
+        (WebCore::FetchResponse::isDisturbed): Checking whether related ReadableStream is locked.
+        (WebCore::FetchResponse::BodyLoader::didSucceed): Introduced to handle ReadableStream case.
+        (WebCore::FetchResponse::BodyLoader::didFail): Ditto.
+        (WebCore::FetchResponse::BodyLoader::didReceiveData): Ditto.
+        (WebCore::FetchResponse::BodyLoader::startStreaming): Ditto.
+        (WebCore::FetchResponse::consumeBodyAsStream): Start filling the ReadableStream with data. Changing loader to Stream if there is one.
+        (WebCore::FetchResponse::createReadableStreamSource): Called by custom binding to create the source.
+        (WebCore::FetchResponse::stop): Fixing potential crash in case of cancelling the ibody stream.
+        (WebCore::FetchResponse::startFetching):
+        (WebCore::FetchResponse::BodyLoader::didFinishLoadingAsArrayBuffer):
+        * Modules/fetch/FetchResponse.h:
+        * Modules/fetch/FetchResponse.idl:
+        * Modules/fetch/FetchResponseSource.cpp: Specialization of ReadableStreamSource for FetchResponse. It is a push source that never resolves the start promise.
+        (WebCore::FetchResponseSource::FetchResponseSource):
+        (WebCore::FetchResponseSource::isReadableStreamLocked):
+        (WebCore::FetchResponseSource::setActive):
+        (WebCore::FetchResponseSource::setInactive):
+        (WebCore::FetchResponseSource::doStart):
+        (WebCore::FetchResponseSource::doCancel):
+        (WebCore::FetchResponseSource::close):
+        (WebCore::FetchResponseSource::error):
+        * Modules/fetch/FetchResponseSource.h: Added.
+        * Modules/streams/ReadableStreamController.js:
+        (error):
+        * Modules/streams/ReadableStreamSource.h: Added (base class for ReadableStream DOM sources).
+        (WebCore::ReadableStreamSource::~ReadableStreamSource):
+        (WebCore::ReadableStreamSource::isStarting):
+        (WebCore::ReadableStreamSource::isPulling):
+        (WebCore::ReadableStreamSource::isCancelling):
+        (WebCore::ReadableStreamSource::controller):
+        (WebCore::ReadableStreamSource::doStart):
+        (WebCore::ReadableStreamSource::doCancel):
+        (WebCore::ReadableStreamSource::start):
+        (WebCore::ReadableStreamSource::cancel):
+        (WebCore::ReadableStreamSource::startFinished):
+        (WebCore::ReadableStreamSource::clean):
+        * Modules/streams/ReadableStreamSource.idl: Added.
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/JSDOMGlobalObject.h:
+        * bindings/js/JSFetchResponseCustom.cpp: In case body is not created, call createReadableStreamSource.
+        (WebCore::JSFetchResponse::body):
+        * bindings/js/JSReadableStreamSourceCustom.cpp: Added.
+        (WebCore::JSReadableStreamSource::start):
+        (WebCore::JSReadableStreamSource::pull):
+        (WebCore::JSReadableStreamSource::controller):
+        * bindings/js/ReadableStreamController.cpp: Added.
+        (WebCore::callFunction):
+        (WebCore::ReadableStreamController::invoke):
+        (WebCore::ReadableStreamController::isControlledReadableStreamLocked):
+        (WebCore::createReadableStream):
+        * bindings/js/ReadableStreamController.h: The DOM wrapper for JSReadableStreamController.
+        (WebCore::ReadableStreamController::ReadableStreamController):
+        (WebCore::ReadableStreamController::close):
+        (WebCore::ReadableStreamController::error):
+        (WebCore::ReadableStreamController::enqueue):
+        (WebCore::ReadableStreamController::globalObject):
+        (WebCore::ReadableStreamController::enqueue<RefPtr<JSC::ArrayBuffer>>):
+        (WebCore::ReadableStreamController::error<String>):
+
 2016-04-16  Antti Koivisto  <antti@apple.com>
 
         Element should be const in StyleResolver
index 72dd719eb7dc69c9d602b8ce70e4869d533dfa63..267a97e5f2755f535c638880c2248aa1513067d8 100644 (file)
@@ -182,6 +182,7 @@ NON_SVG_BINDING_IDLS = \
     $(WebCore)/Modules/streams/ReadableStream.idl \
     $(WebCore)/Modules/streams/ReadableStreamController.idl \
     $(WebCore)/Modules/streams/ReadableStreamReader.idl \
+    $(WebCore)/Modules/streams/ReadableStreamSource.idl \
     $(WebCore)/Modules/streams/WritableStream.idl \
     $(WebCore)/Modules/webaudio/AudioBuffer.idl \
     $(WebCore)/Modules/webaudio/AudioBufferCallback.idl \
index 579ded964a1ff6219ef4082e49c92d6c6ac4946e..10a11f155ab7241606ecefc24e39974d74b628b4 100644 (file)
 #include "DOMRequestState.h"
 #include "Dictionary.h"
 #include "FetchBodyOwner.h"
+#include "FetchResponseSource.h"
 #include "FormData.h"
 #include "HTTPParsers.h"
 #include "JSBlob.h"
 #include "JSDOMFormData.h"
+#include "ReadableStreamSource.h"
 
 namespace WebCore {
 
@@ -142,6 +144,36 @@ void FetchBody::consume(FetchBodyOwner& owner, Consumer::Type type, DeferredWrap
     promise.reject<ExceptionCode>(0);
 }
 
+#if ENABLE(STREAMS_API)
+void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchResponseSource& source)
+{
+    ASSERT(m_type != Type::Loading);
+
+    switch (m_type) {
+    case Type::ArrayBuffer:
+        source.enqueue(m_data);
+        source.close();
+        return;
+    case Type::Text: {
+        Vector<uint8_t> data = extractFromText();
+        // FIXME: We should not close the source if ArrayBuffer;;tryCreate returns null.
+        source.enqueue(ArrayBuffer::tryCreate(data.data(), data.size()));
+        source.close();
+        return;
+    }
+    case Type::Blob:
+        ASSERT(m_blob);
+        owner.loadBlob(*m_blob, FetchLoader::Type::Stream);
+        return;
+    case Type::None:
+        source.close();
+        return;
+    default:
+        source.error(ASCIILiteral("not implemented"));
+    }
+}
+#endif
+
 void FetchBody::consumeArrayBuffer(Consumer::Type type, DeferredWrapper& promise)
 {
     if (type == Consumer::Type::ArrayBuffer) {
index 7059cd22a96792b7cd97150ef591bd5a3e6fac4e..046010a0fb92b266850eea526bee0ed76ce18922 100644 (file)
@@ -44,6 +44,7 @@ class JSValue;
 namespace WebCore {
 
 class FetchBodyOwner;
+class FetchResponseSource;
 class FormData;
 
 class FetchBody {
@@ -54,6 +55,10 @@ public:
     void text(FetchBodyOwner&, DeferredWrapper&&);
     void formData(FetchBodyOwner&, DeferredWrapper&& promise) { promise.reject<ExceptionCode>(0); }
 
+#if ENABLE(STREAMS_API)
+    void consumeAsStream(FetchBodyOwner&, FetchResponseSource&);
+#endif
+
     bool isEmpty() const { return m_type == Type::None; }
 
     void setMimeType(const String& mimeType) { m_mimeType = mimeType; }
@@ -70,9 +75,10 @@ public:
 
     RefPtr<FormData> bodyForInternalRequest() const;
 
-private:
     enum class Type { None, ArrayBuffer, Loading, Text, Blob, FormData };
+    Type type() const { return m_type; }
 
+private:
     FetchBody(Ref<Blob>&&);
     FetchBody(Ref<DOMFormData>&&);
     FetchBody(String&&);
index f75253baee9c3579d65d02e8deef6fc6957970a0..caf4ac8b5ccad1435ce8fa673614254ba2e48c69 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "ExceptionCode.h"
 #include "FetchLoader.h"
+#include "FetchResponseSource.h"
 #include "JSBlob.h"
 #include "ResourceResponse.h"
 
@@ -50,11 +51,23 @@ void FetchBodyOwner::stop()
     if (m_blobLoader) {
         if (m_blobLoader->loader)
             m_blobLoader->loader->stop();
-        finishBlobLoading();
     }
     ASSERT(!m_blobLoader);
 }
 
+bool FetchBodyOwner::isDisturbed() const
+{
+    if (m_isDisturbed)
+        return true;
+
+#if ENABLE(STREAMS_API)
+    if (m_readableStreamSource && m_readableStreamSource->isReadableStreamLocked())
+        return true;
+#endif
+
+    return false;
+}
+
 void FetchBodyOwner::arrayBuffer(DeferredWrapper&& promise)
 {
     if (m_body.isEmpty()) {
@@ -161,12 +174,46 @@ void FetchBodyOwner::loadedBlobAsText(String&& text)
     m_body.loadedAsText(WTFMove(text));
 }
 
+void FetchBodyOwner::blobLoadingSucceeded()
+{
+    ASSERT(m_body.type() == FetchBody::Type::Blob);
+
+#if ENABLE(STREAMS_API)
+    if (m_readableStreamSource) {
+        m_readableStreamSource->close();
+        m_readableStreamSource = nullptr;
+    }
+#endif
+
+    finishBlobLoading();
+}
+
 void FetchBodyOwner::blobLoadingFailed()
 {
-    m_body.loadingFailed();
+#if ENABLE(STREAMS_API)
+    if (m_readableStreamSource) {
+        if (!m_readableStreamSource->isCancelling())
+            m_readableStreamSource->error(ASCIILiteral("Blob loading failed"));
+        m_readableStreamSource = nullptr;
+    } else
+#endif
+        m_body.loadingFailed();
+
     finishBlobLoading();
 }
 
+void FetchBodyOwner::blobChunk(const char* data, size_t size)
+{
+#if ENABLE(STREAMS_API)
+    ASSERT(m_readableStreamSource);
+    // FIXME: If ArrayBuffer::tryCreate returns null, we should probably cancel the load.
+    m_readableStreamSource->enqueue(ArrayBuffer::tryCreate(data, size));
+#else
+    UNUSED_PARAM(data);
+    UNUSED_PARAM(size);
+#endif
+}
+
 FetchBodyOwner::BlobLoader::BlobLoader(FetchBodyOwner& owner)
     : owner(owner)
 {
index 8eae9d9f3a3f63ac58891539f2b4babb8278d911..06a212665c39fc3907d38e13f4cc133cc872c7b4 100644 (file)
@@ -35,6 +35,7 @@
 #include "FetchBody.h"
 #include "FetchLoader.h"
 #include "FetchLoaderClient.h"
+#include "FetchResponseSource.h"
 
 namespace WebCore {
 
@@ -43,7 +44,7 @@ public:
     FetchBodyOwner(ScriptExecutionContext&, FetchBody&&);
 
     // Exposed Body API
-    bool isDisturbed() const { return m_isDisturbed; }
+    bool isDisturbed() const;
 
     void arrayBuffer(DeferredWrapper&&);
     void blob(DeferredWrapper&&);
@@ -68,7 +69,8 @@ private:
     // Blob loading routines
     void loadedBlobAsText(String&&);
     void loadedBlobAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer) { m_body.loadedAsArrayBuffer(WTFMove(buffer)); }
-    void blobLoadingSucceeded() { finishBlobLoading(); }
+    void blobChunk(const char*, size_t);
+    void blobLoadingSucceeded();
     void blobLoadingFailed();
     void finishBlobLoading();
 
@@ -79,6 +81,7 @@ private:
         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;
         void didSucceed() final { owner.blobLoadingSucceeded(); }
 
@@ -89,6 +92,9 @@ private:
 protected:
     FetchBody m_body;
     bool m_isDisturbed { false };
+#if ENABLE(STREAMS_API)
+    RefPtr<FetchResponseSource> m_readableStreamSource;
+#endif
 
 private:
     Optional<BlobLoader> m_blobLoader;
index a760baf6d85c2ce44ce89164c56d385f5455a392..c21525f79c9060264d600831aca2f46edda2d0df 100644 (file)
@@ -68,6 +68,7 @@ void FetchLoader::start(ScriptExecutionContext& context, Blob& blob)
     options.contentSecurityPolicyEnforcement = ContentSecurityPolicyEnforcement::DoNotEnforce;
 
     m_loader = ThreadableLoader::create(&context, this, request, options);
+    m_isStarted = true;
 }
 
 void FetchLoader::start(ScriptExecutionContext& context, const FetchRequest& request)
@@ -83,6 +84,7 @@ void FetchLoader::start(ScriptExecutionContext& context, const FetchRequest& req
     options.contentSecurityPolicyEnforcement = ContentSecurityPolicyEnforcement::DoNotEnforce;
 
     m_loader = ThreadableLoader::create(&context, this, request.internalRequest(), options);
+    m_isStarted = true;
 }
 
 FetchLoader::FetchLoader(Type type, FetchLoaderClient& client)
@@ -93,11 +95,18 @@ FetchLoader::FetchLoader(Type type, FetchLoaderClient& client)
 
 void FetchLoader::stop()
 {
+    m_data = nullptr;
     if (m_loader) {
-        m_loader->cancel();
-        m_loader = nullptr;
+        RefPtr<ThreadableLoader> loader = WTFMove(m_loader);
+        loader->cancel();
     }
-    m_data = nullptr;
+}
+
+RefPtr<SharedBuffer> FetchLoader::startStreaming()
+{
+    ASSERT(m_type == Type::ArrayBuffer);
+    m_type = Type::Stream;
+    return WTFMove(m_data);
 }
 
 void FetchLoader::didReceiveResponse(unsigned long, const ResourceResponse& response)
@@ -109,6 +118,10 @@ void FetchLoader::didReceiveResponse(unsigned long, const ResourceResponse& resp
 // We might also want to merge this class with FileReaderLoader.
 void FetchLoader::didReceiveData(const char* value, int size)
 {
+    if (m_type == Type::Stream) {
+        m_client.didReceiveData(value, size);
+        return;
+    }
     if (!m_data) {
         m_data = SharedBuffer::create(value, size);
         return;
@@ -120,7 +133,7 @@ void FetchLoader::didFinishLoading(unsigned long, double)
 {
     if (m_type == Type::ArrayBuffer)
         m_client.didFinishLoadingAsArrayBuffer(m_data ? m_data->createArrayBuffer() : ArrayBuffer::tryCreate(nullptr, 0));
-    else
+    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;
 
index 8466fd757f33defc832a248fba345beba4e0b643..b725e351aebf7969e6ce4ae52bba9ab53c6a9902 100644 (file)
@@ -44,15 +44,18 @@ class ScriptExecutionContext;
 
 class FetchLoader final : public ThreadableLoaderClient {
 public:
-    enum class Type { ArrayBuffer, Text };
+    enum class Type { ArrayBuffer, Stream, Text };
 
     FetchLoader(Type, FetchLoaderClient&);
 
+    RefPtr<SharedBuffer> startStreaming();
+
     void start(ScriptExecutionContext&, const FetchRequest&);
     void start(ScriptExecutionContext&, Blob&);
     void stop();
 
-    bool isStarted() const { return !!m_loader; }
+    bool isStarted() const { return m_isStarted; }
+
 private:
     // ThreadableLoaderClient API.
     void didReceiveResponse(unsigned long, const ResourceResponse&) final;
@@ -61,11 +64,14 @@ private:
     void didFail(const ResourceError&) final;
     void didFailRedirectCheck() final;
 
+    Type type() const { return m_type; }
+
 private:
     Type m_type { Type::ArrayBuffer };
     FetchLoaderClient& m_client;
     RefPtr<ThreadableLoader> m_loader;
     RefPtr<SharedBuffer> m_data;
+    bool m_isStarted { false };
 };
 
 } // namespace WebCore
index dd35cbb94007c7d2284bb9955b83c17d76188ecc..d09fad53e27e6d8ac22adb7ca9f4510278da4150 100644 (file)
@@ -49,6 +49,7 @@ public:
 
     virtual void didFinishLoadingAsText(String&&) { }
     virtual void didFinishLoadingAsArrayBuffer(RefPtr<JSC::ArrayBuffer>&&) { }
+    virtual void didReceiveData(const char*, size_t) { }
 
     virtual void didSucceed() = 0;
     virtual void didFail() = 0;
index 1e3d87d78000f37b3c645adb8f87cefa92d0b1b0..f75553a32bcdddd12282b989c1845ba6fde2fc3e 100644 (file)
@@ -119,7 +119,7 @@ FetchResponse::FetchResponse(ScriptExecutionContext& context, Type type, FetchBo
 
 RefPtr<FetchResponse> FetchResponse::clone(ScriptExecutionContext& context, ExceptionCode& ec)
 {
-    if (isDisturbed() || m_isLocked) {
+    if (isDisturbed()) {
         ec = TypeError;
         return nullptr;
     }
@@ -148,12 +148,6 @@ String FetchResponse::type() const
     return String();
 }
 
-// FIXME: Implement this, as a custom or through binding generator.
-JSC::JSValue JSFetchResponse::body(JSC::ExecState&) const
-{
-    return JSC::jsNull();
-}
-
 void FetchResponse::startFetching(ScriptExecutionContext& context, const FetchRequest& request, FetchPromise&& promise)
 {
     Ref<FetchResponse> response = adoptRef(*new FetchResponse(context, Type::Basic, FetchBody::loadingBody(), FetchHeaders::create(FetchHeaders::Guard::Immutable), ResourceResponse()));
@@ -192,15 +186,31 @@ void FetchResponse::fetch(ScriptExecutionContext& context, const String& url, co
 
 void FetchResponse::BodyLoader::didSucceed()
 {
+    ASSERT(m_response.hasPendingActivity());
+#if ENABLE(STREAMS_API)
+    if (m_response.m_readableStreamSource) {
+        m_response.m_readableStreamSource->close();
+        m_response.m_readableStreamSource = nullptr;
+    }
+#endif
     m_response.m_bodyLoader = Nullopt;
     m_response.unsetPendingActivity(&m_response);
 }
 
 void FetchResponse::BodyLoader::didFail()
 {
+    ASSERT(m_response.hasPendingActivity());
     if (m_promise)
         std::exchange(m_promise, Nullopt)->reject(TypeError);
 
+#if ENABLE(STREAMS_API)
+    if (m_response.m_readableStreamSource) {
+        if (!m_response.m_readableStreamSource->isCancelling())
+            m_response.m_readableStreamSource->error(ASCIILiteral("Loading failed"));
+        m_response.m_readableStreamSource = nullptr;
+    }
+#endif
+
     // Check whether didFail is called as part of FetchLoader::start.
     if (m_loader->isStarted())
         m_response.m_bodyLoader = Nullopt;
@@ -226,6 +236,19 @@ void FetchResponse::BodyLoader::didReceiveResponse(const ResourceResponse& resou
     std::exchange(m_promise, Nullopt)->resolve(&m_response);
 }
 
+void FetchResponse::BodyLoader::didReceiveData(const char* data, size_t size)
+{
+#if ENABLE(STREAMS_API)
+    ASSERT(m_response.m_readableStreamSource);
+
+    // FIXME: If ArrayBuffer::tryCreate returns null, we should probably cancel the load.
+    m_response.m_readableStreamSource->enqueue(ArrayBuffer::tryCreate(data, size));
+#else
+    UNUSED_PARAM(data);
+    UNUSED_PARAM(size);
+#endif
+}
+
 void FetchResponse::BodyLoader::didFinishLoadingAsArrayBuffer(RefPtr<ArrayBuffer>&& buffer)
 {
     m_response.body().loadedAsArrayBuffer(WTFMove(buffer));
@@ -244,13 +267,53 @@ void FetchResponse::BodyLoader::stop()
         m_loader->stop();
 }
 
+#if ENABLE(STREAMS_API)
+void FetchResponse::consumeBodyAsStream()
+{
+    ASSERT(m_readableStreamSource);
+    m_isDisturbed = true;
+    if (body().type() != FetchBody::Type::Loading) {
+        body().consumeAsStream(*this, *m_readableStreamSource);
+        if (!m_readableStreamSource->isStarting())
+            m_readableStreamSource = nullptr;        
+        return;
+    }
+
+    ASSERT(m_bodyLoader);
+
+    RefPtr<SharedBuffer> data = m_bodyLoader->startStreaming();
+    if (data) {
+        // FIXME: We might want to enqueue each internal SharedBuffer chunk as an individual ArrayBuffer.
+        // Also, createArrayBuffer might return nullptr which will lead to erroring the stream.
+        // We might want to cancel the load and rename createArrayBuffer to tryCreateArrayBuffer.
+        m_readableStreamSource->enqueue(data->createArrayBuffer());
+    }
+}
+
+ReadableStreamSource* FetchResponse::createReadableStreamSource()
+{
+    ASSERT(!m_readableStreamSource);
+    if (body().isEmpty() || isDisturbed())
+        return nullptr;
+
+    m_readableStreamSource = adoptRef(*new FetchResponseSource(*this));
+    return m_readableStreamSource.get();
+}
+
+RefPtr<SharedBuffer> FetchResponse::BodyLoader::startStreaming()
+{
+    ASSERT(m_loader);
+    return m_loader->startStreaming();
+}
+#endif
+
 void FetchResponse::stop()
 {
+    RefPtr<FetchResponse> protect(this);
     FetchBodyOwner::stop();
     if (m_bodyLoader) {
-        RefPtr<FetchResponse> protect(this);
         m_bodyLoader->stop();
-        m_bodyLoader = Nullopt;
+        ASSERT(!m_bodyLoader);
     }
 }
 
index 7800fa7eef43686e0511f2f1611926b99c379bfa..58c23504ccbc02be16c378a3c5f7b8c55b0aee79 100644 (file)
@@ -43,6 +43,7 @@ namespace WebCore {
 
 class Dictionary;
 class FetchRequest;
+class ReadableStreamSource;
 
 typedef int ExceptionCode;
 
@@ -70,6 +71,11 @@ public:
     FetchHeaders& headers() { return m_headers; }
     RefPtr<FetchResponse> clone(ScriptExecutionContext&, ExceptionCode&);
 
+#if ENABLE(STREAMS_API)
+    ReadableStreamSource* createReadableStreamSource();
+    void consumeBodyAsStream();
+#endif
+
 private:
     enum class Type { Basic, Cors, Default, Error, Opaque, OpaqueRedirect };
 
@@ -89,11 +95,16 @@ private:
         bool start(ScriptExecutionContext&, const FetchRequest&);
         void stop();
 
+#if ENABLE(STREAMS_API)
+        RefPtr<SharedBuffer> startStreaming();
+#endif
+
     private:
         // FetchLoaderClient API
         void didSucceed() final;
         void didFail() final;
         void didReceiveResponse(const ResourceResponse&);
+        void didReceiveData(const char*, size_t) final;
         void didFinishLoadingAsArrayBuffer(RefPtr<ArrayBuffer>&&) final;
 
         FetchResponse& m_response;
@@ -104,7 +115,6 @@ private:
     Type m_type;
     ResourceResponse m_response;
     Ref<FetchHeaders> m_headers;
-    bool m_isLocked = false;
     bool m_isRedirected = false;
     Optional<BodyLoader> m_bodyLoader;
 };
index 37feded876ea501f4b32e773131da8dd797cdea5..2ad3ab2a1619e4214fb41d9a81a2625c04b26b6b 100644 (file)
@@ -50,7 +50,7 @@ interface FetchResponse {
     readonly attribute DOMString statusText;
     // FIXME: Add support for SameObject keyword for headers
     readonly attribute FetchHeaders headers;
-    [Custom, RaisesException] readonly attribute ReadableStream? body;
+    [Custom, CachedAttribute] readonly attribute ReadableStream? body;
 
     [NewObject, CallWith=ScriptExecutionContext, RaisesException] FetchResponse clone();
 
diff --git a/Source/WebCore/Modules/fetch/FetchResponseSource.cpp b/Source/WebCore/Modules/fetch/FetchResponseSource.cpp
new file mode 100644 (file)
index 0000000..873f712
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 Canon 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 Canon 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 CANON 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 CANON 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 "FetchResponseSource.h"
+
+#if ENABLE(FETCH_API) && ENABLE(STREAMS_API)
+
+#include "FetchResponse.h"
+
+namespace WebCore {
+
+FetchResponseSource::FetchResponseSource(FetchResponse& response)
+    : m_response(response)
+{
+}
+
+bool FetchResponseSource::isReadableStreamLocked() const
+{
+    return controller().isControlledReadableStreamLocked();
+}
+
+void FetchResponseSource::setActive()
+{
+    m_response.setPendingActivity(&m_response);
+}
+
+void FetchResponseSource::setInactive()
+{
+    m_response.unsetPendingActivity(&m_response);
+}
+
+void FetchResponseSource::doStart()
+{
+    // FIXME: We should consume body only if stream reader requested data, i.e. is disturbed.
+    // We might need a callback to be notified of the stream being disturbed.
+    m_response.consumeBodyAsStream();
+}
+
+void FetchResponseSource::doCancel()
+{
+    m_isCancelling = true;
+    static_cast<ActiveDOMObject&>(m_response).stop();
+}
+
+void FetchResponseSource::close()
+{
+    ASSERT(isStarting());
+    controller().close();
+    clean();
+}
+void FetchResponseSource::error(const String& value)
+{
+    ASSERT(isStarting());
+    controller().error(value);
+    clean();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(FETCH_API) && ENABLE(STREAMS_API)
diff --git a/Source/WebCore/Modules/fetch/FetchResponseSource.h b/Source/WebCore/Modules/fetch/FetchResponseSource.h
new file mode 100644 (file)
index 0000000..53b10e4
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 Canon 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 Canon 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 CANON 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 CANON 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.
+ */
+
+#ifndef FetchResponseSource_h
+#define FetchResponseSource_h
+
+#if ENABLE(FETCH_API) && ENABLE(STREAMS_API)
+
+#include "ReadableStreamSource.h"
+
+namespace WebCore {
+
+class FetchResponse;
+
+class FetchResponseSource final : public ReadableStreamSource {
+public:
+    FetchResponseSource(FetchResponse&);
+
+    template<typename T> void enqueue(const T& t) { controller().enqueue(t); }
+    void close();
+    void error(const String&);
+
+    bool isCancelling() const { return m_isCancelling; }
+    bool isReadableStreamLocked() const;
+
+private:
+    void doStart() final;
+    void doCancel() final;
+    void setActive() final;
+    void setInactive() final;
+
+    FetchResponse& m_response;
+    bool m_isCancelling { false };
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(FETCH_API) && ENABLE(STREAMS_API)
+
+#endif // FetchResponseSource_h
index 9c8c34ea4c4816b97ad428e012d7253e0b173760..5fde9ccf0ff099f363bc74b0301be0e338deae41 100644 (file)
@@ -51,7 +51,7 @@ function error(error)
 
     const stream = this.@controlledReadableStream;
     if (stream.@state !== @streamReadable)
-        throw new @TypeError("ReaableStream is not readable");
+        throw new @TypeError("ReadableStream is not readable");
 
     @errorReadableStream(stream, error);
 }
diff --git a/Source/WebCore/Modules/streams/ReadableStreamSource.h b/Source/WebCore/Modules/streams/ReadableStreamSource.h
new file mode 100644 (file)
index 0000000..5929494
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 Canon 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 Canon 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 CANON 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 CANON 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.
+ */
+
+
+#ifndef ReadableStreamSource_h
+#define ReadableStreamSource_h
+
+#if ENABLE(STREAMS_API)
+
+#include "JSDOMPromise.h"
+#include "ReadableStreamController.h"
+#include <runtime/ArrayBuffer.h>
+#include <wtf/Optional.h>
+
+namespace Deprecated {
+class ScriptValue;
+}
+
+namespace WebCore {
+
+typedef int ExceptionCode;
+
+class ReadableStreamSource : public RefCounted<ReadableStreamSource> {
+public:
+    virtual ~ReadableStreamSource() { }
+
+    typedef DOMPromise<std::nullptr_t, ExceptionCode> Promise;
+
+    void start(ReadableStreamController&&, Promise&&);
+    void cancel(const Deprecated::ScriptValue&);
+
+    bool isStarting() const { return !!m_startPromise; }
+
+protected:
+    ReadableStreamController& controller() { return m_controller.value(); }
+    const ReadableStreamController& controller() const { return m_controller.value(); }
+
+    void startFinished();
+    void cancelFinished();
+    void clean();
+
+    virtual void setActive() = 0;
+    virtual void setInactive() = 0;
+
+    virtual void doStart() { startFinished(); }
+    virtual void doCancel() { }
+
+private:
+    Optional<Promise> m_startPromise;
+
+    Optional<ReadableStreamController> m_controller;
+};
+
+inline void ReadableStreamSource::start(ReadableStreamController&& controller, Promise&& promise)
+{
+    m_startPromise = WTFMove(promise);
+    m_controller = WTFMove(controller);
+
+    setActive();
+    doStart();
+}
+
+inline void ReadableStreamSource::startFinished()
+{
+    ASSERT(m_startPromise);
+    std::exchange(m_startPromise, Nullopt).value().resolve(nullptr);
+    setInactive();
+}
+
+inline void ReadableStreamSource::cancel(const Deprecated::ScriptValue&)
+{
+    clean();
+    doCancel();
+}
+
+inline void ReadableStreamSource::clean()
+{
+    if (m_startPromise) {
+        m_startPromise = Nullopt;
+        setInactive();
+    }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(STREAMS_API)
+
+#endif // ReadableStreamSource_h
diff --git a/Source/WebCore/Modules/streams/ReadableStreamSource.idl b/Source/WebCore/Modules/streams/ReadableStreamSource.idl
new file mode 100644 (file)
index 0000000..f4e6255
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 Canon 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 Canon 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 CANON 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 CANON 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.
+ */
+
+[
+    NoInterfaceObject,
+    Conditional=STREAMS_API,
+    SkipVTableValidation
+] interface ReadableStreamSource {
+    // Methods to support push sources. To support all sources, pull method might need to be added, and cancel may return a promise.
+    [Custom] Promise start(ReadableStreamController controller);
+    void cancel(any reason);
+
+    // Place holder to keep the controller linked to the source.
+    [CachedAttribute, CustomGetter] readonly attribute any controller;
+};
index 4dea47c363eeff70f475298c83b436110246b198..2855c687887f35cdae8c8e42396e8e84291299f2 100644 (file)
                4129DF851BB5B80700322A16 /* JSReadableStreamPrivateConstructors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4129DF831BB5B7F700322A16 /* JSReadableStreamPrivateConstructors.cpp */; };
                4129DF861BB5B80C00322A16 /* JSReadableStreamPrivateConstructors.h in Headers */ = {isa = PBXBuildFile; fileRef = 4129DF841BB5B7F700322A16 /* JSReadableStreamPrivateConstructors.h */; settings = {ATTRIBUTES = (Private, ); }; };
                413015D91C7B571400091C6E /* FetchResponse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 413015D51C7B570400091C6E /* FetchResponse.cpp */; };
+               413015D91C7B571400091C6F /* FetchResponseSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 413015D51C7B570400091C6F /* FetchResponseSource.cpp */; };
                4138D3351244054800323D33 /* EventContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 4138D3331244054800323D33 /* EventContext.h */; };
                4138D3361244054800323D33 /* EventContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4138D3341244054800323D33 /* EventContext.cpp */; };
                413C2C341BC29A8F0075204C /* JSDOMConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 413C2C331BC29A7B0075204C /* JSDOMConstructor.h */; };
                418A06D1133C04D500CD379C /* EventDispatcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 418A06CF133C04D500CD379C /* EventDispatcher.cpp */; };
                418C39561C8DAC7F0051C8A3 /* DOMWindowFetch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 418C39521C8DAC7B0051C8A3 /* DOMWindowFetch.cpp */; };
                418C395A1C8DD6990051C8A3 /* WorkerGlobalScopeFetch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 418C39571C8DD6960051C8A3 /* WorkerGlobalScopeFetch.cpp */; };
+               418C39601C8F0AAE0051C8A3 /* JSReadableStreamSourceCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 418C395D1C8F0AAB0051C8A3 /* JSReadableStreamSourceCustom.cpp */; };
+               418C39611C8F0AB10051C8A3 /* ReadableStreamController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 418C395E1C8F0AAB0051C8A3 /* ReadableStreamController.cpp */; };
+               418C39631C8F129B0051C8A3 /* JSFetchResponseCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 418C39621C8F12970051C8A3 /* JSFetchResponseCustom.cpp */; };
                418F88040FF957AE0080F045 /* JSAbstractWorker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 418F88020FF957AE0080F045 /* JSAbstractWorker.cpp */; };
                418F88050FF957AF0080F045 /* JSAbstractWorker.h in Headers */ = {isa = PBXBuildFile; fileRef = 418F88030FF957AE0080F045 /* JSAbstractWorker.h */; };
                419BC2DE1685329900D64D6D /* VisitedLinkState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 419BC2DC1685329900D64D6D /* VisitedLinkState.cpp */; };
                7E474E1E12494DC900235364 /* SQLiteDatabaseTrackerClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E474E1B12494DC900235364 /* SQLiteDatabaseTrackerClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7E474E1F12494DC900235364 /* SQLiteDatabaseTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E474E1C12494DC900235364 /* SQLiteDatabaseTracker.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7E474E2012494DC900235364 /* SQLiteDatabaseTracker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7E474E1D12494DC900235364 /* SQLiteDatabaseTracker.cpp */; };
+               7E4C96DC1AD4483500365A51 /* JSReadableStreamSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7E4C96D81AD4483500365A51 /* JSReadableStreamSource.cpp */; };
+               7E4C96DD1AD4483500365A51 /* JSReadableStreamSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E4C96D91AD4483500365A51 /* JSReadableStreamSource.h */; };
                7E4C96DC1AD4483500365A50 /* JSFetchRequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7E4C96D81AD4483500365A50 /* JSFetchRequest.cpp */; };
                7E4C96DD1AD4483500365A50 /* JSFetchRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E4C96D91AD4483500365A50 /* JSFetchRequest.h */; };
                7E4DE10D198B10B60051CB02 /* DiskCacheMonitorCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7E4DE10C198B10B60051CB02 /* DiskCacheMonitorCocoa.mm */; };
                410D33231C19984100F7FDE2 /* WebCoreJSBuiltinInternals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebCoreJSBuiltinInternals.cpp; sourceTree = "<group>"; };
                41189EF71AD8232800B90A0D /* ReadableStreamController.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReadableStreamController.idl; sourceTree = "<group>"; };
                4127D5360F8AAB1D00E424F5 /* ScriptState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptState.cpp; sourceTree = "<group>"; };
-               4129DF811BB5B79B00322A16 /* ReadableStreamController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReadableStreamController.h; sourceTree = "<group>"; };
-               4129DF821BB5B7A600322A16 /* ReadableStreamReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReadableStreamReader.h; sourceTree = "<group>"; };
                4129DF831BB5B7F700322A16 /* JSReadableStreamPrivateConstructors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSReadableStreamPrivateConstructors.cpp; sourceTree = "<group>"; };
                4129DF841BB5B7F700322A16 /* JSReadableStreamPrivateConstructors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSReadableStreamPrivateConstructors.h; sourceTree = "<group>"; };
                413015D51C7B570400091C6E /* FetchResponse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FetchResponse.cpp; sourceTree = "<group>"; };
+               413015D51C7B570400091C6F /* FetchResponseSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FetchResponseSource.cpp; sourceTree = "<group>"; };
+               413015D61C7B570400091C6F /* FetchResponseSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FetchResponseSource.h; sourceTree = "<group>"; };
                413015D61C7B570400091C6E /* FetchResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FetchResponse.h; sourceTree = "<group>"; };
                413015D71C7B570400091C6E /* FetchResponse.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = FetchResponse.idl; sourceTree = "<group>"; };
                413015D81C7B570400091C6E /* FetchResponse.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = FetchResponse.js; sourceTree = "<group>"; };
                418C39571C8DD6960051C8A3 /* WorkerGlobalScopeFetch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WorkerGlobalScopeFetch.cpp; sourceTree = "<group>"; };
                418C39581C8DD6960051C8A3 /* WorkerGlobalScopeFetch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WorkerGlobalScopeFetch.h; sourceTree = "<group>"; };
                418C39591C8DD6960051C8A3 /* WorkerGlobalScopeFetch.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = WorkerGlobalScopeFetch.idl; sourceTree = "<group>"; };
+               418C395B1C8F0A610051C8A3 /* ReadableStreamSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReadableStreamSource.h; sourceTree = "<group>"; };
+               418C395C1C8F0A610051C8A3 /* ReadableStreamSource.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReadableStreamSource.idl; sourceTree = "<group>"; };
+               418C395D1C8F0AAB0051C8A3 /* JSReadableStreamSourceCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSReadableStreamSourceCustom.cpp; sourceTree = "<group>"; };
+               418C395E1C8F0AAB0051C8A3 /* ReadableStreamController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReadableStreamController.cpp; sourceTree = "<group>"; };
+               418C395F1C8F0AAB0051C8A3 /* ReadableStreamController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReadableStreamController.h; sourceTree = "<group>"; };
+               418C39621C8F12970051C8A3 /* JSFetchResponseCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFetchResponseCustom.cpp; sourceTree = "<group>"; };
                418F88020FF957AE0080F045 /* JSAbstractWorker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSAbstractWorker.cpp; sourceTree = "<group>"; };
                418F88030FF957AE0080F045 /* JSAbstractWorker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSAbstractWorker.h; sourceTree = "<group>"; };
                419BC2DC1685329900D64D6D /* VisitedLinkState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VisitedLinkState.cpp; sourceTree = "<group>"; };
                7E474E1B12494DC900235364 /* SQLiteDatabaseTrackerClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLiteDatabaseTrackerClient.h; sourceTree = "<group>"; };
                7E474E1C12494DC900235364 /* SQLiteDatabaseTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLiteDatabaseTracker.h; sourceTree = "<group>"; };
                7E474E1D12494DC900235364 /* SQLiteDatabaseTracker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SQLiteDatabaseTracker.cpp; sourceTree = "<group>"; };
+               7E4C96D81AD4483500365A51 /* JSReadableStreamSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSReadableStreamSource.cpp; sourceTree = "<group>"; };
+               7E4C96D91AD4483500365A51 /* JSReadableStreamSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSReadableStreamSource.h; sourceTree = "<group>"; };
                7E4C96D81AD4483500365A50 /* JSFetchRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFetchRequest.cpp; sourceTree = "<group>"; };
                7E4C96D91AD4483500365A50 /* JSFetchRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFetchRequest.h; sourceTree = "<group>"; };
                7E4DE10C198B10B60051CB02 /* DiskCacheMonitorCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DiskCacheMonitorCocoa.mm; sourceTree = "<group>"; };
                                9908B0EE1BCACF9100ED0F65 /* CountQueuingStrategy.js */,
                                41A023ED1A39DB7900F722CF /* ReadableStream.idl */,
                                9908B0EF1BCACF9100ED0F65 /* ReadableStream.js */,
-                               4129DF811BB5B79B00322A16 /* ReadableStreamController.h */,
                                41189EF71AD8232800B90A0D /* ReadableStreamController.idl */,
                                9908B0F01BCACF9100ED0F65 /* ReadableStreamController.js */,
                                9908B0F11BCACF9100ED0F65 /* ReadableStreamInternals.js */,
-                               4129DF821BB5B7A600322A16 /* ReadableStreamReader.h */,
                                419FAFAD1ABABCD5005B828B /* ReadableStreamReader.idl */,
                                9908B0F21BCACF9100ED0F65 /* ReadableStreamReader.js */,
+                               418C395B1C8F0A610051C8A3 /* ReadableStreamSource.h */,
+                               418C395C1C8F0A610051C8A3 /* ReadableStreamSource.idl */,
                                9908B0F11BCACF9100ED0F55 /* StreamInternals.js */,
                                41A023ED1A39DB7900F722DF /* WritableStream.idl */,
                                9908B0EF1BCACF9100ED0F75 /* WritableStream.js */,
                                41F54F891C50C4F600338488 /* FetchRequest.idl */,
                                413015D51C7B570400091C6E /* FetchResponse.cpp */,
                                413015D61C7B570400091C6E /* FetchResponse.h */,
+                               413015D51C7B570400091C6F /* FetchResponseSource.cpp */,
+                               413015D61C7B570400091C6F /* FetchResponseSource.h */,
                                413015D71C7B570400091C6E /* FetchResponse.idl */,
                                413015D81C7B570400091C6E /* FetchResponse.js */,
                                418C39571C8DD6960051C8A3 /* WorkerGlobalScopeFetch.cpp */,
                                7E4C96D91AD4483500365A50 /* JSFetchRequest.h */,
                                8E4C96D81AD4483500365A50 /* JSFetchResponse.cpp */,
                                8E4C96D91AD4483500365A50 /* JSFetchResponse.h */,
+                               7E4C96D81AD4483500365A51 /* JSReadableStreamSource.cpp */,
+                               7E4C96D91AD4483500365A51 /* JSReadableStreamSource.h */,
                        );
                        name = FetchAPI;
                        sourceTree = "<group>";
                                46B63F6B1C6E8CDF002E914B /* JSEventTargetCustom.h */,
                                3314ACE910892086000F0E56 /* JSExceptionBase.cpp */,
                                3314ACEA10892086000F0E56 /* JSExceptionBase.h */,
+                               418C39621C8F12970051C8A3 /* JSFetchResponseCustom.cpp */,
                                8F934D841189F1EE00508D5D /* JSMainThreadExecState.cpp */,
                                8F934D831189F1EE00508D5D /* JSMainThreadExecState.h */,
                                B56576E417DA599F00A56BDC /* JSMainThreadExecStateInstrumentation.h */,
                                93B70D5009EB0C7C009D8468 /* JSPluginElementFunctions.h */,
                                4129DF831BB5B7F700322A16 /* JSReadableStreamPrivateConstructors.cpp */,
                                4129DF841BB5B7F700322A16 /* JSReadableStreamPrivateConstructors.h */,
+                               418C395D1C8F0AAB0051C8A3 /* JSReadableStreamSourceCustom.cpp */,
                                E1C36D320EB0A094007410BC /* JSWorkerGlobalScopeBase.cpp */,
                                E1C36D330EB0A094007410BC /* JSWorkerGlobalScopeBase.h */,
+                               418C395E1C8F0AAB0051C8A3 /* ReadableStreamController.cpp */,
+                               418C395F1C8F0AAB0051C8A3 /* ReadableStreamController.h */,
                                BCA378BA0D15F64200B793D6 /* ScheduledAction.cpp */,
                                BCA378BB0D15F64200B793D6 /* ScheduledAction.h */,
                                41F1D21E0EF35C2A00DA8753 /* ScriptCachedFrameData.cpp */,
                                7F4C96DD1AD4483500365A50 /* JSFetchBody.h in Headers */,
                                7D4C96DD1AD4483500365A50 /* JSFetchHeaders.h in Headers */,
                                7E4C96DD1AD4483500365A50 /* JSFetchRequest.h in Headers */,
+                               7E4C96DD1AD4483500365A51 /* JSReadableStreamSource.h in Headers */,
                                8E4C96DD1AD4483500365A50 /* JSFetchResponse.h in Headers */,
                                BC00F0150E0A189500FD04E3 /* JSFile.h in Headers */,
                                2E3BC0CB117D3E0800B9409A /* JSFileError.h in Headers */,
                                CD3E252318046BCD00E27F56 /* CSSGridTemplateAreasValue.cpp in Sources */,
                                FBF89045169E9F1F0052D86E /* CSSGroupingRule.cpp in Sources */,
                                BC23E76C0DAE88A9009FDC91 /* CSSImageGeneratorValue.cpp in Sources */,
+                               418C39601C8F0AAE0051C8A3 /* JSReadableStreamSourceCustom.cpp in Sources */,
                                9393E5FF151A99F200066F06 /* CSSImageSetValue.cpp in Sources */,
                                A80E6CFE0A1989CA007FB8C5 /* CSSImageValue.cpp in Sources */,
                                A80E6CEB0A1989CA007FB8C5 /* CSSImportRule.cpp in Sources */,
                                41F54F8D1C50C50800338488 /* FetchHeaders.cpp in Sources */,
                                41F54F8E1C50C50C00338488 /* FetchRequest.cpp in Sources */,
                                413015D91C7B571400091C6E /* FetchResponse.cpp in Sources */,
+                               413015D91C7B571400091C6F /* FetchResponseSource.cpp in Sources */,
                                84730D8A1248F0B300D3A9C9 /* FETile.cpp in Sources */,
                                84730D8C1248F0B300D3A9C9 /* FETurbulence.cpp in Sources */,
                                FD31609412B026F700C1A359 /* FFTConvolver.cpp in Sources */,
                                A8EA79FC0A1916DF00A8EF5F /* HTMLLIElement.cpp in Sources */,
                                A871DC210A15205700B12A68 /* HTMLLinkElement.cpp in Sources */,
                                A8EA7D320A19385500A8EF5F /* HTMLMapElement.cpp in Sources */,
+                               418C39611C8F0AB10051C8A3 /* ReadableStreamController.cpp in Sources */,
                                A8EA7CAC0A192B9C00A8EF5F /* HTMLMarqueeElement.cpp in Sources */,
                                E44613A40CD6331000FADA75 /* HTMLMediaElement.cpp in Sources */,
                                7AD3CDD91C8A002F00F12698 /* ResourceLoadStatisticsStore.cpp in Sources */,
                                7F4C96DC1AD4483500365A50 /* JSFetchBody.cpp in Sources */,
                                7D4C96DC1AD4483500365A50 /* JSFetchHeaders.cpp in Sources */,
                                7E4C96DC1AD4483500365A50 /* JSFetchRequest.cpp in Sources */,
+                               7E4C96DC1AD4483500365A51 /* JSReadableStreamSource.cpp in Sources */,
                                8E4C96DC1AD4483500365A50 /* JSFetchResponse.cpp in Sources */,
                                BC00F0140E0A189500FD04E3 /* JSFile.cpp in Sources */,
                                2E3BC0CA117D3E0800B9409A /* JSFileError.cpp in Sources */,
                                078E08FE17D14CEE00420AA1 /* MediaConstraintsImpl.cpp in Sources */,
                                073794E119EE2D1B00E5A045 /* MediaConstraintsMock.cpp in Sources */,
                                417253AA1354BBBC00360F2A /* MediaControlElements.cpp in Sources */,
+                               418C39631C8F129B0051C8A3 /* JSFetchResponseCustom.cpp in Sources */,
                                DEBCCDD516646EB200A452E1 /* MediaControlElementTypes.cpp in Sources */,
                                CD27F6E7145770D30078207D /* MediaController.cpp in Sources */,
                                1F3C3BEA135CAF3C00B8C1AC /* MediaControls.cpp in Sources */,
index 0f77bf435b7678c19a65f4540c7c700af9f049fa..15582aa88ac25b6638ad835424d82f58d6df6180 100644 (file)
@@ -69,6 +69,8 @@ namespace WebCore {
         DOMWrapperWorld& world() { return *m_world; }
         bool worldIsNormal() const { return m_worldIsNormal; }
 
+        JSBuiltinInternalFunctions& builtinInternalFunctions() { return m_builtinInternalFunctions; }
+
     protected:
         static const JSC::ClassInfo s_info;
 
diff --git a/Source/WebCore/bindings/js/JSFetchResponseCustom.cpp b/Source/WebCore/bindings/js/JSFetchResponseCustom.cpp
new file mode 100644 (file)
index 0000000..cdb06e4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 Canon 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 Canon 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 CANON 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 CANON 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 "JSFetchResponse.h"
+
+#if ENABLE(FETCH_API)
+
+#include "JSReadableStreamSource.h"
+
+namespace WebCore {
+
+JSC::JSValue JSFetchResponse::body(JSC::ExecState& state) const
+{
+#if ENABLE(STREAMS_API)
+    if (!m_body)
+        m_body.set(state.vm(), this, createReadableStream(state, globalObject(), wrapped().createReadableStreamSource()));
+    return m_body.get();
+#else
+    UNUSED_PARAM(state);
+    return JSC::jsNull();
+#endif
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(FETCH_API)
diff --git a/Source/WebCore/bindings/js/JSReadableStreamSourceCustom.cpp b/Source/WebCore/bindings/js/JSReadableStreamSourceCustom.cpp
new file mode 100644 (file)
index 0000000..58999ce
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Canon 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 Canon 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 CANON 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 CANON 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 "JSReadableStreamSource.h"
+
+#if ENABLE(STREAMS_API)
+
+using namespace JSC;
+
+namespace WebCore {
+
+JSValue JSReadableStreamSource::start(ExecState& state)
+{
+    JSReadableStreamController* controller = jsDynamicCast<JSReadableStreamController*>(state.argument(0));
+    ASSERT(controller);
+
+    JSReadableStreamSource* jsSource = const_cast<JSReadableStreamSource*>(this);
+    m_controller.set(state.vm(), jsSource, state.argument(0));
+
+    JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, globalObject());
+    wrapped().start(ReadableStreamController(controller), DeferredWrapper(&state, globalObject(), promiseDeferred));
+    return promiseDeferred->promise();
+}
+
+JSValue JSReadableStreamSource::controller(ExecState&) const
+{
+    ASSERT_NOT_REACHED();
+    return jsUndefined();
+}
+
+}
+
+#endif // ENABLE(STREAMS_API)
diff --git a/Source/WebCore/bindings/js/ReadableStreamController.cpp b/Source/WebCore/bindings/js/ReadableStreamController.cpp
new file mode 100644 (file)
index 0000000..577c647
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 Canon 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 Canon 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 CANON 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 CANON 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 "ReadableStreamController.h"
+
+#if ENABLE(STREAMS_API)
+
+#include "JSReadableStream.h"
+#include "JSReadableStreamSource.h"
+#include "WebCoreJSClientData.h"
+
+namespace WebCore {
+
+static inline JSC::JSValue callFunction(JSC::ExecState& state, JSC::JSValue jsFunction, JSC::JSValue thisValue, const JSC::ArgList& arguments)
+{
+    JSC::CallData callData;
+    JSC::CallType callType = JSC::getCallData(jsFunction, callData);
+    return JSC::call(&state, jsFunction, callType, callData, thisValue, arguments);
+}
+
+JSC::JSValue ReadableStreamController::invoke(JSC::ExecState& state, JSC::JSObject& object, const char* propertyName, JSC::JSValue parameter)
+{
+    JSC::JSLockHolder lock(&state);
+
+    JSC::JSValue function = getPropertyFromObject(state, object, propertyName);
+    if (state.hadException())
+        return JSC::jsUndefined();
+
+    if (!function.isFunction()) {
+        if (!function.isUndefined())
+            throwVMError(&state, createTypeError(&state, ASCIILiteral("ReadableStream trying to call a property that is not callable")));
+        return JSC::jsUndefined();
+    }
+
+    JSC::MarkedArgumentBuffer arguments;
+    arguments.append(parameter);
+
+    return callFunction(state, function, &object, arguments);
+}
+
+bool ReadableStreamController::isControlledReadableStreamLocked() const
+{
+    JSC::ExecState& state = *globalObject()->globalExec();
+    JSC::JSLockHolder lock(&state);
+
+    JSVMClientData& clientData = *static_cast<JSVMClientData*>(state.vm().clientData);
+    JSC::JSValue readableStream = m_jsController->get(&state, clientData.builtinNames().controlledReadableStreamPrivateName());
+    ASSERT(!state.hadException());
+
+    JSC::JSValue isLocked = globalObject()->builtinInternalFunctions().readableStreamInternals().m_isReadableStreamLockedFunction.get();
+
+    JSC::MarkedArgumentBuffer arguments;
+    arguments.append(readableStream);
+    JSC::JSValue result = callFunction(state, isLocked, JSC::jsUndefined(), arguments);
+    ASSERT(!state.hadException());
+
+    return result.isTrue();
+}
+
+JSC::JSValue createReadableStream(JSC::ExecState& state, JSDOMGlobalObject* globalObject, ReadableStreamSource* source)
+{
+    if (!source)
+        return JSC::jsNull();
+
+    JSC::JSLockHolder lock(&state);
+
+    JSC::JSValue jsSource = toJS(&state, globalObject, source);
+    JSC::Strong<JSC::Unknown> protect(state.vm(), jsSource);
+
+    JSC::MarkedArgumentBuffer arguments;
+    arguments.append(jsSource);
+
+    JSC::JSValue constructor = JSReadableStream::getConstructor(state.vm(), globalObject);
+
+    JSC::ConstructData constructData;
+    JSC::ConstructType constructType = JSC::getConstructData(constructor, constructData);
+    ASSERT(constructType != JSC::ConstructType::None);
+
+    return JSC::construct(&state, constructor, constructType, constructData, arguments);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(STREAMS_API)
diff --git a/Source/WebCore/bindings/js/ReadableStreamController.h b/Source/WebCore/bindings/js/ReadableStreamController.h
new file mode 100644 (file)
index 0000000..5bc31e6
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 Canon 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 Canon 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 CANON 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 CANON 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.
+ */
+
+#ifndef ReadableStreamController_h
+#define ReadableStreamController_h
+
+#if ENABLE(STREAMS_API)
+
+#include "JSDOMBinding.h"
+#include "JSReadableStreamController.h"
+#include <runtime/JSCJSValue.h>
+#include <runtime/JSCJSValueInlines.h>
+
+namespace WebCore {
+
+class ReadableStreamSource;
+
+class ReadableStreamController {
+public:
+    explicit ReadableStreamController(JSReadableStreamController* controller) : m_jsController(controller) { }
+
+    static JSC::JSValue invoke(JSC::ExecState&, JSC::JSObject&, const char*, JSC::JSValue);
+
+    template<class ResolveResultType>
+    void enqueue(const ResolveResultType&);
+
+    template<class ResolveResultType>
+    void error(const ResolveResultType&);
+
+    void close() { invoke(*globalObject()->globalExec(), *m_jsController, "close", JSC::jsUndefined()); }
+
+    bool isControlledReadableStreamLocked() const;
+
+private:
+    void error(JSC::ExecState& state, JSC::JSValue value) { invoke(state, *m_jsController, "error", value); }
+    void enqueue(JSC::ExecState& state, JSC::JSValue value) { invoke(state, *m_jsController, "enqueue", value); }
+
+    JSDOMGlobalObject* globalObject() const;
+
+    // The owner of ReadableStreamController is responsible to keep uncollected the JSReadableStreamController.
+    JSReadableStreamController* m_jsController { nullptr };
+};
+
+JSC::JSValue createReadableStream(JSC::ExecState&, JSDOMGlobalObject*, ReadableStreamSource*);
+
+inline JSDOMGlobalObject* ReadableStreamController::globalObject() const
+{
+    ASSERT(m_jsController);
+    return static_cast<JSDOMGlobalObject*>(m_jsController->globalObject());
+}
+
+template<>
+inline void ReadableStreamController::enqueue<RefPtr<JSC::ArrayBuffer>>(const RefPtr<JSC::ArrayBuffer>& result)
+{
+    JSC::ExecState& state = *globalObject()->globalExec();
+    JSC::JSLockHolder locker(&state);
+
+    if (result)
+        enqueue(state, toJS(&state, globalObject(), result.get()));
+    else
+        error(state, createOutOfMemoryError(&state));
+}
+
+template<>
+inline void ReadableStreamController::error<String>(const String& result)
+{
+    JSC::ExecState* state = globalObject()->globalExec();
+    JSC::JSLockHolder locker(state);
+    error(*state, jsString(state, result));
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(STREAMS_API)
+
+#endif // ReadableStreamController_h