Add support for Request body stream cloning
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Aug 2017 20:03:42 +0000 (20:03 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Aug 2017 20:03:42 +0000 (20:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176148

Patch by Youenn Fablet <youenn@apple.com> on 2017-08-31
Reviewed by Alex Christensen.

Source/WebCore:

Tests: http/wpt/fetch/request-clone.html
       http/wpt/fetch/request-consume-stream.html
       http/wpt/fetch/request-stream-disturbed-1.html
       http/wpt/fetch/request-stream-disturbed-2.html
       http/wpt/fetch/request-stream-disturbed-3.html

Adding support for ReadableStream teeing for cloning fetch bodies.
Adding support for pushing request body data to its ReadableStream.
Renamed FetchResponseSource to FetchBodySource for that purpose.

Tests extracting body from a ReadableStream through JS API pass.
Tests extracting data stored in a ReadableStream to resolve fetch body promises are failing.
There is no support yet, this will be added as a follow-up.

* CMakeLists.txt:
* Modules/fetch/FetchBody.cpp:
(WebCore::FetchBody::consume):
(WebCore::FetchBody::consumeAsStream):
(WebCore::FetchBody::clone):
* Modules/fetch/FetchBody.h:
* Modules/fetch/FetchBodyConsumer.h:
* Modules/fetch/FetchBodyOwner.cpp:
(WebCore::FetchBodyOwner::cloneBody):
(WebCore::FetchBodyOwner::loadBlob):
(WebCore::FetchBodyOwner::readableStream):
(WebCore::FetchBodyOwner::consumeBodyAsStream):
* Modules/fetch/FetchBodyOwner.h:
(WebCore::FetchBodyOwner::feedStream):
(WebCore::FetchBodyOwner::cancel):
* Modules/fetch/FetchBodySource.cpp: Renamed from Source/WebCore/Modules/fetch/FetchResponseSource.cpp.
* Modules/fetch/FetchBodySource.h: Renamed from Source/WebCore/Modules/fetch/FetchResponseSource.h.
* Modules/fetch/FetchResponse.cpp:
(WebCore::FetchResponse::consumeBodyAsStream):
(WebCore::FetchResponse::createReadableStream):
* Modules/fetch/FetchResponse.h:
* Modules/fetch/FetchResponse.idl:
* Modules/fetch/FetchResponse.js:
(getter.body):
(clone):
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/ReadableStream.cpp:
(WebCore::ReadableStream::tee):
* bindings/js/ReadableStream.h:
* bindings/js/WebCoreBuiltinNames.h:

LayoutTests:

* http/wpt/fetch/request-clone-expected.txt: Added.
* http/wpt/fetch/request-clone.html: Added.
* http/wpt/fetch/request-consume-stream-expected.txt: Added.
* http/wpt/fetch/request-consume-stream.html: Added.
* http/wpt/fetch/request-stream-disturbed-1-expected.txt: Added.
* http/wpt/fetch/request-stream-disturbed-1.html: Added.
* http/wpt/fetch/request-stream-disturbed-2-expected.txt: Added.
* http/wpt/fetch/request-stream-disturbed-2.html: Added.
* http/wpt/fetch/request-stream-disturbed-3-expected.txt: Added.
* http/wpt/fetch/request-stream-disturbed-3.html: Added.

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/http/wpt/fetch/request-clone-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/fetch/request-clone.html [new file with mode: 0644]
LayoutTests/http/wpt/fetch/request-consume-stream-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/fetch/request-consume-stream.html [new file with mode: 0644]
LayoutTests/http/wpt/fetch/request-stream-disturbed-1-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/fetch/request-stream-disturbed-1.html [new file with mode: 0644]
LayoutTests/http/wpt/fetch/request-stream-disturbed-2-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/fetch/request-stream-disturbed-2.html [new file with mode: 0644]
LayoutTests/http/wpt/fetch/request-stream-disturbed-3-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/fetch/request-stream-disturbed-3.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
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/FetchBodySource.cpp [moved from Source/WebCore/Modules/fetch/FetchResponseSource.cpp with 67% similarity]
Source/WebCore/Modules/fetch/FetchBodySource.h [moved from Source/WebCore/Modules/fetch/FetchResponseSource.h with 85% similarity]
Source/WebCore/Modules/fetch/FetchResponse.cpp
Source/WebCore/Modules/fetch/FetchResponse.h
Source/WebCore/Modules/fetch/FetchResponse.idl
Source/WebCore/Modules/fetch/FetchResponse.js
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/ReadableStream.cpp
Source/WebCore/bindings/js/ReadableStream.h
Source/WebCore/bindings/js/WebCoreBuiltinNames.h

index 954a800..70cb7ed 100644 (file)
@@ -1,3 +1,21 @@
+2017-08-31  Youenn Fablet  <youenn@apple.com>
+
+        Add support for Request body stream cloning
+        https://bugs.webkit.org/show_bug.cgi?id=176148
+
+        Reviewed by Alex Christensen.
+
+        * http/wpt/fetch/request-clone-expected.txt: Added.
+        * http/wpt/fetch/request-clone.html: Added.
+        * http/wpt/fetch/request-consume-stream-expected.txt: Added.
+        * http/wpt/fetch/request-consume-stream.html: Added.
+        * http/wpt/fetch/request-stream-disturbed-1-expected.txt: Added.
+        * http/wpt/fetch/request-stream-disturbed-1.html: Added.
+        * http/wpt/fetch/request-stream-disturbed-2-expected.txt: Added.
+        * http/wpt/fetch/request-stream-disturbed-2.html: Added.
+        * http/wpt/fetch/request-stream-disturbed-3-expected.txt: Added.
+        * http/wpt/fetch/request-stream-disturbed-3.html: Added.
+
 2017-08-31  Carlos Alberto Lopez Perez  <clopez@igalia.com>
 
         [GTK][WPE] Mark test media/event-queue-crash as flaky
diff --git a/LayoutTests/http/wpt/fetch/request-clone-expected.txt b/LayoutTests/http/wpt/fetch/request-clone-expected.txt
new file mode 100644 (file)
index 0000000..6fdeb30
--- /dev/null
@@ -0,0 +1,15 @@
+
+PASS Check orginal request's body after cloning 
+PASS Check cloned request's body 
+PASS Check request clone use structureClone for teed ReadableStreams (Int8Arraychunk) 
+PASS Check request clone use structureClone for teed ReadableStreams (Int16Arraychunk) 
+PASS Check request clone use structureClone for teed ReadableStreams (Int32Arraychunk) 
+PASS Check request clone use structureClone for teed ReadableStreams (ArrayBufferchunk) 
+PASS Check request clone use structureClone for teed ReadableStreams (Uint8Arraychunk) 
+PASS Check request clone use structureClone for teed ReadableStreams (Uint8ClampedArraychunk) 
+PASS Check request clone use structureClone for teed ReadableStreams (Uint16Arraychunk) 
+PASS Check request clone use structureClone for teed ReadableStreams (Uint32Arraychunk) 
+PASS Check request clone use structureClone for teed ReadableStreams (Float32Arraychunk) 
+PASS Check request clone use structureClone for teed ReadableStreams (Float64Arraychunk) 
+PASS Check request clone use structureClone for teed ReadableStreams (DataViewchunk) 
+
diff --git a/LayoutTests/http/wpt/fetch/request-clone.html b/LayoutTests/http/wpt/fetch/request-clone.html
new file mode 100644 (file)
index 0000000..5b38009
--- /dev/null
@@ -0,0 +1,70 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Request clone</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#request">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script src="/fetch/api/resources/utils.js"></script>
+    <script>
+var body = "This is request body";
+var headersInit = { "name" : "value" };
+var requestInit  = { "body" : body,
+                     "method" : "POST",
+                     "headers" : headersInit
+};
+var request = new Request("", requestInit);
+var clonedRequest = request.clone();
+
+promise_test(function(test) {
+    return validateStreamFromString(request.body.getReader(), body);
+}, "Check orginal request's body after cloning");
+
+promise_test(function(test) {
+    return validateStreamFromString(clonedRequest.body.getReader(), body);
+}, "Check cloned request's body");
+
+function testReadableStreamClone(initialBuffer, bufferType)
+{
+    promise_test(function(test) {
+        var request = new Request("", {"method" : "POST", "body" : new ReadableStream({start : function(controller) {
+            controller.enqueue(initialBuffer);
+            controller.close();
+        }})});
+
+        var clone = request.clone();
+        var stream1 = request.body;
+        var stream2 = clone.body;
+
+        var buffer;
+        return stream1.getReader().read().then(function(data) {
+            assert_false(data.done);
+            assert_true(data.value === initialBuffer, "Buffer of being-cloned response stream is the same as the original buffer");
+            return stream2.getReader().read();
+        }).then(function(data) {
+            assert_false(data.done);
+            assert_array_equals(data.value, initialBuffer, "Cloned buffer chunks have the same content");
+            assert_equals(Object.getPrototypeOf(data.value), Object.getPrototypeOf(initialBuffer), "Cloned buffers have the same type");
+            assert_true(data.value !== initialBuffer, "Buffer of cloned response stream is a clone of the original buffer");
+        });
+    }, "Check request clone use structureClone for teed ReadableStreams (" + bufferType  + "chunk)");
+}
+
+var arrayBuffer = new ArrayBuffer(16);
+testReadableStreamClone(new Int8Array(arrayBuffer, 1), "Int8Array");
+testReadableStreamClone(new Int16Array(arrayBuffer, 2, 2), "Int16Array");
+testReadableStreamClone(new Int32Array(arrayBuffer), "Int32Array");
+testReadableStreamClone(arrayBuffer, "ArrayBuffer");
+testReadableStreamClone(new Uint8Array(arrayBuffer), "Uint8Array");
+testReadableStreamClone(new Uint8ClampedArray(arrayBuffer), "Uint8ClampedArray");
+testReadableStreamClone(new Uint16Array(arrayBuffer, 2), "Uint16Array");
+testReadableStreamClone(new Uint32Array(arrayBuffer), "Uint32Array");
+testReadableStreamClone(new Float32Array(arrayBuffer), "Float32Array");
+testReadableStreamClone(new Float64Array(arrayBuffer), "Float64Array");
+testReadableStreamClone(new DataView(arrayBuffer, 2, 8), "DataView");
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/http/wpt/fetch/request-consume-stream-expected.txt b/LayoutTests/http/wpt/fetch/request-consume-stream-expected.txt
new file mode 100644 (file)
index 0000000..6cc0648
--- /dev/null
@@ -0,0 +1,9 @@
+
+FAIL Read empty text request's body as readableStream assert_array_equals: Retrieve and verify stream lengths differ, expected 0 got 16
+FAIL Read empty blob request's body as readableStream assert_array_equals: Retrieve and verify stream lengths differ, expected 0 got 16
+FAIL Read blob request's body as readableStream assert_array_equals: Retrieve and verify stream lengths differ, expected 24 got 16
+FAIL Read text request's body as readableStream assert_array_equals: Retrieve and verify stream lengths differ, expected 24 got 16
+FAIL Read URLSearchParams request's body as readableStream assert_array_equals: Retrieve and verify stream lengths differ, expected 10 got 16
+FAIL Read array buffer request's body as readableStream assert_array_equals: Retrieve and verify stream lengths differ, expected 24 got 16
+FAIL Read form data request's body as readableStream assert_array_equals: Retrieve and verify stream lengths differ, expected 10 got 16
+
diff --git a/LayoutTests/http/wpt/fetch/request-consume-stream.html b/LayoutTests/http/wpt/fetch/request-consume-stream.html
new file mode 100644 (file)
index 0000000..a58a472
--- /dev/null
@@ -0,0 +1,67 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Request consume</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#request">
+    <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/fetch/api/resources/utils.js"></script>
+  </head>
+  <body>
+    <script>
+function createRequestWithBody(body)
+{
+    return new Request("", {body: "{\"key\": \"value\"}", method: "POST"});
+}
+
+promise_test(function(test) {
+    var body = "";
+    var request = createRequestWithBody("");
+    return validateStreamFromString(request.body.getReader(), "");
+}, "Read empty text request's body as readableStream");
+
+promise_test(function(test) {
+    var request = createRequestWithBody(new Blob([], { "type" : "text/plain" }));
+    return validateStreamFromString(request.body.getReader(), "");
+}, "Read empty blob request's body as readableStream");
+
+var formData = new FormData();
+formData.append("name", "value");
+var textData = JSON.stringify("This is request's body");
+var blob = new Blob([textData], { "type" : "text/plain" });
+var urlSearchParamsData = "name=value";
+var urlSearchParams = new URLSearchParams(urlSearchParamsData);
+
+promise_test(function(test) {
+    var request = createRequestWithBody(blob);
+    return validateStreamFromString(request.body.getReader(), textData);
+}, "Read blob request's body as readableStream");
+
+promise_test(function(test) {
+    var request = createRequestWithBody(textData);
+    return validateStreamFromString(request.body.getReader(), textData);
+}, "Read text request's body as readableStream");
+
+promise_test(function(test) {
+    var request = createRequestWithBody(urlSearchParams);
+    return validateStreamFromString(request.body.getReader(), urlSearchParamsData);
+}, "Read URLSearchParams request's body as readableStream");
+
+promise_test(function(test) {
+    var arrayBuffer = new ArrayBuffer(textData.length);
+    var int8Array = new Int8Array(arrayBuffer);
+    for (var cptr = 0; cptr < textData.length; cptr++)
+        int8Array[cptr] = textData.charCodeAt(cptr);
+
+    return validateStreamFromString(createRequestWithBody(arrayBuffer).body.getReader(), textData);
+}, "Read array buffer request's body as readableStream");
+
+promise_test(function(test) {
+    var request = createRequestWithBody(formData);
+    return validateStreamFromString(request.body.getReader(), "name=value");
+}, "Read form data request's body as readableStream");
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/http/wpt/fetch/request-stream-disturbed-1-expected.txt b/LayoutTests/http/wpt/fetch/request-stream-disturbed-1-expected.txt
new file mode 100644 (file)
index 0000000..b8546be
--- /dev/null
@@ -0,0 +1,6 @@
+
+PASS Getting blob after getting the Request body - not disturbed, not locked 
+FAIL Getting text after getting the Request body - not disturbed, not locked assert_true: expected true got false
+FAIL Getting json after getting the Request body - not disturbed, not locked promise_test: Unhandled rejection with value: object "SyntaxError: The string did not match the expected pattern."
+FAIL Getting arrayBuffer after getting the Request body - not disturbed, not locked assert_true: expected true got false
+
diff --git a/LayoutTests/http/wpt/fetch/request-stream-disturbed-1.html b/LayoutTests/http/wpt/fetch/request-stream-disturbed-1.html
new file mode 100644 (file)
index 0000000..04fe794
--- /dev/null
@@ -0,0 +1,53 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Consuming Request body after getting a ReadableStream</title>
+    <meta name="help" href="https://fetch.spec.whatwg.org/#body-mixin">
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script>
+function createRequestWithReadableStream(callback) {
+    var request = new Request("", {body: "{\"key\": \"value\"}", method: "POST"});
+    var reader = request.body.getReader();
+    reader.releaseLock();
+    return callback(request);
+}
+
+promise_test(function() {
+    return createRequestWithReadableStream(function(request) {
+        return request.blob().then(function(blob) {
+            assert_true(blob instanceof Blob);
+        });
+    });
+}, "Getting blob after getting the Request body - not disturbed, not locked");
+
+promise_test(function() {
+    return createRequestWithReadableStream(function(request) {
+        return request.text().then(function(text) {
+            assert_true(text.length > 0);
+        });
+    });
+}, "Getting text after getting the Request body - not disturbed, not locked");
+
+promise_test(function() {
+    return createRequestWithReadableStream(function(request) {
+        return request.json().then(function(json) {
+            assert_true(typeof json === "object");
+        });
+    });
+}, "Getting json after getting the Request body - not disturbed, not locked");
+
+promise_test(function() {
+    return createRequestWithReadableStream(function(request) {
+        return request.arrayBuffer().then(function(arrayBuffer) {
+            assert_true(arrayBuffer.byteLength > 0);
+        });
+    });
+}, "Getting arrayBuffer after getting the Request body - not disturbed, not locked");
+
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/http/wpt/fetch/request-stream-disturbed-2-expected.txt b/LayoutTests/http/wpt/fetch/request-stream-disturbed-2-expected.txt
new file mode 100644 (file)
index 0000000..f2b9b2e
--- /dev/null
@@ -0,0 +1,6 @@
+
+FAIL Getting blob after getting a locked Request body assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Getting text after getting a locked Request body assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Getting json after getting a locked Request body assert_throws: function "function () { throw e }" threw object "SyntaxError: The string did not match the expected pattern." ("SyntaxError") expected object "TypeError" ("TypeError")
+FAIL Getting arrayBuffer after getting a locked Request body assert_unreached: Should have rejected: undefined Reached unreachable code
+
diff --git a/LayoutTests/http/wpt/fetch/request-stream-disturbed-2.html b/LayoutTests/http/wpt/fetch/request-stream-disturbed-2.html
new file mode 100644 (file)
index 0000000..0ed03ba
--- /dev/null
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Consuming Request body after getting a ReadableStream</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script>
+function createRequestWithLockedReadableStream(callback) {
+    var request = new Request("", {body: "{\"key\": \"value\"}", method: "POST"});
+    var reader = request.body.getReader();
+    return callback(request);
+}
+
+promise_test(function(test) {
+    return createRequestWithLockedReadableStream(function(request) {
+        return promise_rejects(test, new TypeError(), request.blob());
+    });
+}, "Getting blob after getting a locked Request body");
+
+promise_test(function(test) {
+    return createRequestWithLockedReadableStream(function(request) {
+        return promise_rejects(test, new TypeError(), request.text());
+    });
+}, "Getting text after getting a locked Request body");
+
+promise_test(function(test) {
+    return createRequestWithLockedReadableStream(function(request) {
+        return promise_rejects(test, new TypeError(), request.json());
+    });
+}, "Getting json after getting a locked Request body");
+
+promise_test(function(test) {
+    return createRequestWithLockedReadableStream(function(request) {
+        return promise_rejects(test, new TypeError(), request.arrayBuffer());
+    });
+}, "Getting arrayBuffer after getting a locked Request body");
+
+    </script>
+  </body>
+</html>
diff --git a/LayoutTests/http/wpt/fetch/request-stream-disturbed-3-expected.txt b/LayoutTests/http/wpt/fetch/request-stream-disturbed-3-expected.txt
new file mode 100644 (file)
index 0000000..b93e3a6
--- /dev/null
@@ -0,0 +1,6 @@
+
+FAIL Getting blob after reading the Request body assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Getting text after reading the Request body assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Getting json after reading the Request body assert_throws: function "function () { throw e }" threw object "SyntaxError: The string did not match the expected pattern." ("SyntaxError") expected object "TypeError" ("TypeError")
+FAIL Getting arrayBuffer after reading the Request body assert_unreached: Should have rejected: undefined Reached unreachable code
+
diff --git a/LayoutTests/http/wpt/fetch/request-stream-disturbed-3.html b/LayoutTests/http/wpt/fetch/request-stream-disturbed-3.html
new file mode 100644 (file)
index 0000000..c035764
--- /dev/null
@@ -0,0 +1,45 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Consuming Request body after getting a ReadableStream</title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+  </head>
+  <body>
+    <script>
+
+function createRequestWithDisturbedReadableStream(callback) {
+    var request = new Request("", {body: "{\"key\": \"value\"}", method: "POST"});
+    var reader = request.body.getReader();
+    reader.read();
+    return callback(request);
+}
+
+promise_test(function(test) {
+    return createRequestWithDisturbedReadableStream(function(request) {
+        return promise_rejects(test, new TypeError(), request.blob());
+    });
+}, "Getting blob after reading the Request body");
+
+promise_test(function(test) {
+    return createRequestWithDisturbedReadableStream(function(request) {
+        return promise_rejects(test, new TypeError(), request.text());
+    });
+}, "Getting text after reading the Request body");
+
+promise_test(function(test) {
+    return createRequestWithDisturbedReadableStream(function(request) {
+        return promise_rejects(test, new TypeError(), request.json());
+    });
+}, "Getting json after reading the Request body");
+
+promise_test(function(test) {
+    return createRequestWithDisturbedReadableStream(function(request) {
+        return promise_rejects(test, new TypeError(), request.arrayBuffer());
+    });
+}, "Getting arrayBuffer after reading the Request body");
+
+    </script>
+  </body>
+</html>
index f112c85..c20f906 100644 (file)
@@ -936,11 +936,11 @@ set(WebCore_SOURCES
     Modules/fetch/FetchBody.cpp
     Modules/fetch/FetchBodyConsumer.cpp
     Modules/fetch/FetchBodyOwner.cpp
+    Modules/fetch/FetchBodySource.cpp
     Modules/fetch/FetchHeaders.cpp
     Modules/fetch/FetchLoader.cpp
     Modules/fetch/FetchRequest.cpp
     Modules/fetch/FetchResponse.cpp
-    Modules/fetch/FetchResponseSource.cpp
     Modules/fetch/WorkerGlobalScopeFetch.cpp
 
     Modules/geolocation/Coordinates.cpp
index e74161c..5d761dc 100644 (file)
@@ -1,3 +1,55 @@
+2017-08-31  Youenn Fablet  <youenn@apple.com>
+
+        Add support for Request body stream cloning
+        https://bugs.webkit.org/show_bug.cgi?id=176148
+
+        Reviewed by Alex Christensen.
+
+        Tests: http/wpt/fetch/request-clone.html
+               http/wpt/fetch/request-consume-stream.html
+               http/wpt/fetch/request-stream-disturbed-1.html
+               http/wpt/fetch/request-stream-disturbed-2.html
+               http/wpt/fetch/request-stream-disturbed-3.html
+
+        Adding support for ReadableStream teeing for cloning fetch bodies.
+        Adding support for pushing request body data to its ReadableStream.
+        Renamed FetchResponseSource to FetchBodySource for that purpose.
+
+        Tests extracting body from a ReadableStream through JS API pass.
+        Tests extracting data stored in a ReadableStream to resolve fetch body promises are failing.
+        There is no support yet, this will be added as a follow-up.
+
+        * CMakeLists.txt:
+        * Modules/fetch/FetchBody.cpp:
+        (WebCore::FetchBody::consume):
+        (WebCore::FetchBody::consumeAsStream):
+        (WebCore::FetchBody::clone):
+        * Modules/fetch/FetchBody.h:
+        * Modules/fetch/FetchBodyConsumer.h:
+        * Modules/fetch/FetchBodyOwner.cpp:
+        (WebCore::FetchBodyOwner::cloneBody):
+        (WebCore::FetchBodyOwner::loadBlob):
+        (WebCore::FetchBodyOwner::readableStream):
+        (WebCore::FetchBodyOwner::consumeBodyAsStream):
+        * Modules/fetch/FetchBodyOwner.h:
+        (WebCore::FetchBodyOwner::feedStream):
+        (WebCore::FetchBodyOwner::cancel):
+        * Modules/fetch/FetchBodySource.cpp: Renamed from Source/WebCore/Modules/fetch/FetchResponseSource.cpp.
+        * Modules/fetch/FetchBodySource.h: Renamed from Source/WebCore/Modules/fetch/FetchResponseSource.h.
+        * Modules/fetch/FetchResponse.cpp:
+        (WebCore::FetchResponse::consumeBodyAsStream):
+        (WebCore::FetchResponse::createReadableStream):
+        * Modules/fetch/FetchResponse.h:
+        * Modules/fetch/FetchResponse.idl:
+        * Modules/fetch/FetchResponse.js:
+        (getter.body):
+        (clone):
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/ReadableStream.cpp:
+        (WebCore::ReadableStream::tee):
+        * bindings/js/ReadableStream.h:
+        * bindings/js/WebCoreBuiltinNames.h:
+
 2017-08-31  Chris Dumez  <cdumez@apple.com>
 
         Use WTF::crossThreadCopy() in more places
index 1ac1a5f..433e94b 100644 (file)
@@ -31,8 +31,8 @@
 
 #include "Document.h"
 #include "FetchBodyOwner.h"
+#include "FetchBodySource.h"
 #include "FetchHeaders.h"
-#include "FetchResponseSource.h"
 #include "HTTPHeaderValues.h"
 #include "HTTPParsers.h"
 #include "ReadableStreamSource.h"
@@ -137,11 +137,12 @@ void FetchBody::consume(FetchBodyOwner& owner, Ref<DeferredPromise>&& promise)
         promise->reject();
         return;
     }
+
     m_consumer.resolve(WTFMove(promise));
 }
 
 #if ENABLE(STREAMS_API)
-void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchResponseSource& source)
+void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchBodySource& source)
 {
     bool closeStream = false;
     if (isArrayBuffer()) {
@@ -270,7 +271,7 @@ FetchBody::TakenData FetchBody::take()
     return nullptr;
 }
 
-FetchBody FetchBody::clone() const
+FetchBody FetchBody::clone()
 {
     FetchBody clone(m_consumer);
 
@@ -286,6 +287,12 @@ FetchBody FetchBody::clone() const
         clone.m_data = textBody();
     else if (isURLSearchParams())
         clone.m_data = urlSearchParamsBody();
+
+    if (m_readableStream) {
+        auto clones = m_readableStream->tee();
+        m_readableStream = WTFMove(clones.first);
+        clone.m_readableStream = WTFMove(clones.second);
+    }
     return clone;
 }
 
index ef9f6ed..27acc69 100644 (file)
@@ -39,7 +39,7 @@
 namespace WebCore {
 
 class FetchBodyOwner;
-class FetchResponseSource;
+class FetchBodySource;
 class ScriptExecutionContext;
 
 class FetchBody {
@@ -51,7 +51,7 @@ public:
     void formData(FetchBodyOwner&, Ref<DeferredPromise>&& promise) { promise.get().reject(NotSupportedError); }
 
 #if ENABLE(STREAMS_API)
-    void consumeAsStream(FetchBodyOwner&, FetchResponseSource&);
+    void consumeAsStream(FetchBodyOwner&, FetchBodySource&);
 #endif
 
     bool isBlob() const { return WTF::holds_alternative<Ref<const Blob>>(m_data); }
@@ -80,7 +80,7 @@ public:
     void consumeOnceLoadingFinished(FetchBodyConsumer::Type, Ref<DeferredPromise>&&, const String&);
     void cleanConsumer() { m_consumer.clean(); }
 
-    FetchBody clone() const;
+    FetchBody clone();
     ReadableStream* readableStream() { return m_readableStream.get(); }
     void setReadableStream(Ref<ReadableStream>&& stream)
     {
index 7eb4401..0fb2df3 100644 (file)
@@ -100,7 +100,7 @@ void FetchBodyOwner::blob(Ref<DeferredPromise>&& promise)
     m_body->blob(*this, WTFMove(promise), m_contentType);
 }
 
-void FetchBodyOwner::cloneBody(const FetchBodyOwner& owner)
+void FetchBodyOwner::cloneBody(FetchBodyOwner& owner)
 {
     m_contentType = owner.m_contentType;
     if (owner.isBodyNull())
@@ -200,7 +200,6 @@ void FetchBodyOwner::text(Ref<DeferredPromise>&& promise)
 void FetchBodyOwner::loadBlob(const Blob& blob, FetchBodyConsumer* consumer)
 {
     // Can only be called once for a body instance.
-    ASSERT(isDisturbed());
     ASSERT(!m_blobLoader);
     ASSERT(!isBodyNull());
 
@@ -293,10 +292,21 @@ RefPtr<ReadableStream> FetchBodyOwner::readableStream(JSC::ExecState& state)
     if (isBodyNull())
         return nullptr;
 
-    if (!m_body->hasReadableStream())
+    if (!m_body->hasReadableStream()) {
+        ASSERT(!m_readableStreamSource);
+        m_readableStreamSource = adoptRef(*new FetchBodySource(*this));
         m_body->setReadableStream(ReadableStream::create(state, m_readableStreamSource));
-
+    }
     return m_body->readableStream();
 }
 
+void FetchBodyOwner::consumeBodyAsStream()
+{
+    ASSERT(m_readableStreamSource);
+
+    body().consumeAsStream(*this, *m_readableStreamSource);
+    if (!m_readableStreamSource->isPulling())
+        m_readableStreamSource = nullptr;
+}
+
 } // namespace WebCore
index eac2232..9895c67 100644 (file)
 
 #include "ActiveDOMObject.h"
 #include "FetchBody.h"
+#include "FetchBodySource.h"
 #include "FetchHeaders.h"
 #include "FetchLoader.h"
 #include "FetchLoaderClient.h"
-#include "FetchResponseSource.h"
 
 namespace WebCore {
 
@@ -41,8 +41,6 @@ class FetchBodyOwner : public RefCounted<FetchBodyOwner>, public ActiveDOMObject
 public:
     FetchBodyOwner(ScriptExecutionContext&, std::optional<FetchBody>&&, Ref<FetchHeaders>&&);
 
-    // Exposed Body API
-    // FIXME: Missing body attribute returning a ReadableStream.
     bool bodyUsed() const { return isDisturbed(); }
     void arrayBuffer(Ref<DeferredPromise>&&);
     void blob(Ref<DeferredPromise>&&);
@@ -60,11 +58,17 @@ public:
     RefPtr<ReadableStream> readableStream(JSC::ExecState&);
     virtual bool hasReadableStreamBody() const { return m_body && m_body->hasReadableStream(); }
 
+#if ENABLE(STREAMS_API)
+    virtual void consumeBodyAsStream();
+    virtual void feedStream() { }
+    virtual void cancel() { }
+#endif
+
 protected:
     const FetchBody& body() const { return *m_body; }
     FetchBody& body() { return *m_body; }
     bool isBodyNull() const { return !m_body; }
-    void cloneBody(const FetchBodyOwner&);
+    void cloneBody(FetchBodyOwner&);
 
     void extractBody(ScriptExecutionContext&, FetchBody::Init&&);
     void updateContentType();
@@ -103,7 +107,7 @@ protected:
     String m_contentType;
     bool m_isDisturbed { false };
 #if ENABLE(STREAMS_API)
-    RefPtr<FetchResponseSource> m_readableStreamSource;
+    RefPtr<FetchBodySource> m_readableStreamSource;
 #endif
     Ref<FetchHeaders> m_headers;
 
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2016 Canon Inc.
+ * Copyright (C) 2017 Apple Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted, provided that the following conditions
@@ -10,9 +11,6 @@
  * 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
@@ -27,7 +25,7 @@
  */
 
 #include "config.h"
-#include "FetchResponseSource.h"
+#include "FetchBodySource.h"
 
 #if ENABLE(STREAMS_API)
 
 
 namespace WebCore {
 
-FetchResponseSource::FetchResponseSource(FetchResponse& response)
-    : m_response(response)
+FetchBodySource::FetchBodySource(FetchBodyOwner& bodyOwner)
+    : m_bodyOwner(bodyOwner)
 {
 }
 
-bool FetchResponseSource::isReadableStreamLocked() const
+bool FetchBodySource::isReadableStreamLocked() const
 {
     return controller().isControlledReadableStreamLocked();
 }
 
-void FetchResponseSource::setActive()
+void FetchBodySource::setActive()
 {
-    m_response.setPendingActivity(&m_response);
+    m_bodyOwner.setPendingActivity(&m_bodyOwner);
 }
 
-void FetchResponseSource::setInactive()
+void FetchBodySource::setInactive()
 {
-    m_response.unsetPendingActivity(&m_response);
+    m_bodyOwner.unsetPendingActivity(&m_bodyOwner);
 }
 
-void FetchResponseSource::doStart()
+void FetchBodySource::doStart()
 {
-    m_response.consumeBodyAsStream();
+    m_bodyOwner.consumeBodyAsStream();
 }
 
-void FetchResponseSource::doPull()
+void FetchBodySource::doPull()
 {
-    m_response.feedStream();
+    m_bodyOwner.feedStream();
 }
 
-void FetchResponseSource::doCancel()
+void FetchBodySource::doCancel()
 {
     m_isCancelling = true;
-    m_response.cancel();
+    m_bodyOwner.cancel();
 }
 
-void FetchResponseSource::close()
+void FetchBodySource::close()
 {
     controller().close();
     clean();
 }
-void FetchResponseSource::error(const String& value)
+void FetchBodySource::error(const String& value)
 {
     controller().error(value);
     clean();
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2016 Canon Inc.
+ * Copyright (C) 2017 Apple Inc.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted, provided that the following conditions
@@ -10,9 +11,6 @@
  * 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
@@ -38,11 +36,11 @@ class ArrayBuffer;
 
 namespace WebCore {
 
-class FetchResponse;
+class FetchBodyOwner;
 
-class FetchResponseSource final : public ReadableStreamSource {
+class FetchBodySource final : public ReadableStreamSource {
 public:
-    FetchResponseSource(FetchResponse&);
+    FetchBodySource(FetchBodyOwner&);
 
     bool enqueue(RefPtr<JSC::ArrayBuffer>&& chunk) { return controller().enqueue(WTFMove(chunk)); }
     void close();
@@ -60,7 +58,7 @@ private:
     void setActive() final;
     void setInactive() final;
 
-    FetchResponse& m_response;
+    FetchBodyOwner& m_bodyOwner;
     bool m_isCancelling { false };
 };
 
index 79a997f..c193ce3 100644 (file)
@@ -324,14 +324,12 @@ void FetchResponse::consumeBodyAsStream()
 {
     ASSERT(m_shouldExposeBody);
     ASSERT(m_readableStreamSource);
-    m_isDisturbed = true;
     if (!isLoading()) {
-        body().consumeAsStream(*this, *m_readableStreamSource);
-        if (!m_readableStreamSource->isPulling())
-            m_readableStreamSource = nullptr;
+        FetchBodyOwner::consumeBodyAsStream();
         return;
     }
 
+    m_isDisturbed = true;
     ASSERT(m_bodyLoader);
 
     setBodyAsReadableStream();
@@ -373,16 +371,12 @@ void FetchResponse::feedStream()
     closeStream();
 }
 
-ReadableStreamSource* FetchResponse::createReadableStreamSource()
+RefPtr<ReadableStream> FetchResponse::createReadableStream(JSC::ExecState& state)
 {
-    ASSERT(!m_readableStreamSource);
-    ASSERT(!m_isDisturbed);
-
-    if (isBodyNull() || !m_shouldExposeBody)
+    if (!m_shouldExposeBody)
         return nullptr;
 
-    m_readableStreamSource = adoptRef(*new FetchResponseSource(*this));
-    return m_readableStreamSource.get();
+    return FetchBodyOwner::readableStream(state);
 }
 
 RefPtr<SharedBuffer> FetchResponse::BodyLoader::startStreaming()
index 1b7f16c..dc9f11e 100644 (file)
@@ -85,10 +85,10 @@ public:
     Ref<FetchResponse> cloneForJS();
 
 #if ENABLE(STREAMS_API)
-    ReadableStreamSource* createReadableStreamSource();
-    void consumeBodyAsStream();
-    void feedStream();
-    void cancel();
+    RefPtr<ReadableStream> createReadableStream(JSC::ExecState&);
+    void consumeBodyAsStream() final;
+    void feedStream() final;
+    void cancel() final;
 #endif
 
     using ResponseData = Variant<std::nullptr_t, Ref<FormData>, Ref<SharedBuffer>>;
index ea17844..9510602 100644 (file)
@@ -83,7 +83,7 @@ dictionary FetchResponseInit {
     [PrivateIdentifier] boolean isLoading();
     [MayThrowException, PrivateIdentifier] void setStatus(unsigned short status, DOMString statusText);
     [PrivateIdentifier] void initializeWith(BodyInit body);
-    [NewObject, PrivateIdentifier] ReadableStreamSource createReadableStreamSource();
+    [CallWith=ScriptState, NewObject, PrivateIdentifier] ReadableStream? createReadableStream();
     [PrivateIdentifier] boolean isDisturbed();
     [PrivateIdentifier] void setBodyAsReadableStream();
 };
index 2341f0e..78fea0f 100644 (file)
@@ -77,15 +77,13 @@ function body()
     if (!(this instanceof @Response))
         throw @makeGetterTypeError("Response", "body");
 
-    if (!this.@body) {
+    if (this.@body === @undefined) {
         if (@Response.prototype.@isDisturbed.@call(this)) {
             this.@body = new @ReadableStream();
             // Get reader to lock it.
             new @ReadableStreamDefaultReader(this.@body);
-        } else {
-            var source = @Response.prototype.@createReadableStreamSource.@call(this);
-            this.@body = source ? new @ReadableStream(source) : null;
-        }
+        } else
+            this.@body = @Response.prototype.@createReadableStream.@call(this);
     }
     return this.@body;
 }
@@ -101,11 +99,8 @@ function clone()
     var cloned = @Response.prototype.@cloneForJS.@call(this);
 
     // Let's create @body if response body is loading to provide data to both clones.
-    if (@Response.prototype.@isLoading.@call(this) && !this.@body) {
-        var source = @Response.prototype.@createReadableStreamSource.@call(this);
-        @assert(!!source);
-        this.@body = new @ReadableStream(source);
-    }
+    if (@Response.prototype.@isLoading.@call(this) && this.@body === @undefined)
+        this.@body = @Response.prototype.@createReadableStream.@call(this);
 
     if (this.@body) {
         var teedReadableStreams = @readableStreamTee(this.@body, true);
index 0e30458..b198e8f 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 */; };
+               413015D91C7B571400091C6F /* FetchBodySource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 413015D51C7B570400091C6F /* FetchBodySource.cpp */; };
                41380C261F3436A600155FDA /* Cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41380C201F34368A00155FDA /* Cache.cpp */; };
                41380C271F3436AC00155FDA /* Cache.h in Headers */ = {isa = PBXBuildFile; fileRef = 41380C251F34369A00155FDA /* Cache.h */; };
                41380C281F3436AC00155FDA /* CacheStorage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41380C211F34368D00155FDA /* CacheStorage.cpp */; };
                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>"; };
+               413015D51C7B570400091C6F /* FetchBodySource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FetchBodySource.cpp; sourceTree = "<group>"; };
                413015D61C7B570400091C6E /* FetchResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FetchResponse.h; sourceTree = "<group>"; };
-               413015D61C7B570400091C6F /* FetchResponseSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FetchResponseSource.h; sourceTree = "<group>"; };
+               413015D61C7B570400091C6F /* FetchBodySource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FetchBodySource.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>"; };
                41380C201F34368A00155FDA /* Cache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Cache.cpp; path = Modules/cache/Cache.cpp; sourceTree = SOURCE_ROOT; };
                                41CF8BE51D46222000707DC9 /* FetchBodyConsumer.h */,
                                4147E2B31C89912600A7E715 /* FetchBodyOwner.cpp */,
                                4147E2B21C88337F00A7E715 /* FetchBodyOwner.h */,
+                               413015D51C7B570400091C6F /* FetchBodySource.cpp */,
+                               413015D61C7B570400091C6F /* FetchBodySource.h */,
                                41F54F821C50C4F600338488 /* FetchHeaders.cpp */,
                                41F54F831C50C4F600338488 /* FetchHeaders.h */,
                                41F54F841C50C4F600338488 /* FetchHeaders.idl */,
                                413015D61C7B570400091C6E /* FetchResponse.h */,
                                413015D71C7B570400091C6E /* FetchResponse.idl */,
                                413015D81C7B570400091C6E /* FetchResponse.js */,
-                               413015D51C7B570400091C6F /* FetchResponseSource.cpp */,
-                               413015D61C7B570400091C6F /* FetchResponseSource.h */,
                                418C39571C8DD6960051C8A3 /* WorkerGlobalScopeFetch.cpp */,
                                418C39581C8DD6960051C8A3 /* WorkerGlobalScopeFetch.h */,
                                418C39591C8DD6960051C8A3 /* WorkerGlobalScopeFetch.idl */,
                                41F54F8B1C50C50300338488 /* FetchBody.cpp in Sources */,
                                41CF8BE71D46226700707DC9 /* FetchBodyConsumer.cpp in Sources */,
                                4147E2B81C89912F00A7E715 /* FetchBodyOwner.cpp in Sources */,
+                               413015D91C7B571400091C6F /* FetchBodySource.cpp in Sources */,
                                41F54F8D1C50C50800338488 /* FetchHeaders.cpp in Sources */,
                                4147E2B71C89912C00A7E715 /* FetchLoader.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 */,
                                A871DE250A152AC800B12A68 /* HTMLHeadElement.cpp in Sources */,
                                A8EA7CAE0A192B9C00A8EF5F /* HTMLHeadingElement.cpp in Sources */,
                                A8EA7CB30A192B9C00A8EF5F /* HTMLHRElement.cpp in Sources */,
-                               E41FA9B91F5758FF0033858A /* RenderTreeUpdaterMultiColumn.cpp in Sources */,
                                A871DE260A152AC800B12A68 /* HTMLHtmlElement.cpp in Sources */,
                                A871DE240A152AC800B12A68 /* HTMLIFrameElement.cpp in Sources */,
                                A8EA7D310A19385500A8EF5F /* HTMLImageElement.cpp in Sources */,
                                E48284081F44594C00863AC3 /* RenderTreeUpdaterFirstLetter.cpp in Sources */,
                                E48E33311F47437000BAB0EF /* RenderTreeUpdaterGeneratedContent.cpp in Sources */,
                                E48E332D1F47038000BAB0EF /* RenderTreeUpdaterListItem.cpp in Sources */,
+                               E41FA9B91F5758FF0033858A /* RenderTreeUpdaterMultiColumn.cpp in Sources */,
                                E44614510CD68A3500FADA75 /* RenderVideo.cpp in Sources */,
                                BCEA4867097D93020094C9E4 /* RenderView.cpp in Sources */,
                                BE20507D18A458BF0080647E /* RenderVTTCue.cpp in Sources */,
index df4141e..722740f 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "ReadableStream.h"
 
+#include "JSDOMConvertSequences.h"
 #include "JSReadableStreamSource.h"
 #include "WebCoreJSClientData.h"
 
@@ -53,4 +54,27 @@ Ref<ReadableStream> ReadableStream::create(JSC::ExecState& execState, RefPtr<Rea
     return create(globalObject, *newReadableStream);
 }
 
+std::pair<Ref<ReadableStream>, Ref<ReadableStream>> ReadableStream::tee()
+{
+    auto& state = *m_globalObject->globalExec();
+    JSVMClientData* clientData = static_cast<JSVMClientData*>(state.vm().clientData);
+    const Identifier& privateName = clientData->builtinFunctions().readableStreamInternalsBuiltins().readableStreamTeePrivateName();
+
+    auto readableStreamTee = m_globalObject->get(&state, privateName);
+    ASSERT(readableStreamTee.isFunction());
+
+    CallData callData;
+    CallType callType = getCallData(readableStreamTee, callData);
+    ASSERT(callType != JSC::CallType::None);
+    MarkedArgumentBuffer arguments;
+    arguments.append(readableStream());
+    arguments.append(JSC::jsBoolean(true));
+    JSValue returnedValue = JSC::call(&state, readableStreamTee, callType, callData, JSC::jsUndefined(), arguments);
+
+    auto results = Detail::SequenceConverter<IDLInterface<ReadableStream>>::convert(state, returnedValue);
+
+    ASSERT(results.size() == 2);
+    return std::make_pair(results[0].releaseNonNull(), results[1].releaseNonNull());
+}
+
 }
index b1c3279..a0c26a4 100644 (file)
@@ -40,6 +40,8 @@ public:
 
     static Ref<ReadableStream> create(JSC::ExecState&, RefPtr<ReadableStreamSource>&&);
 
+    std::pair<Ref<ReadableStream>, Ref<ReadableStream>> tee();
+
     JSReadableStream* readableStream() { return guarded(); }
 
 protected:
index 09fe81b..d72d166 100644 (file)
@@ -47,7 +47,7 @@ namespace WebCore {
     macro(consumeChunk) \
     macro(controlledReadableStream) \
     macro(controller) \
-    macro(createReadableStreamSource) \
+    macro(createReadableStream) \
     macro(disturbed) \
     macro(failureKind) \
     macro(fetchRequest) \