Support caching of Response with a ReadableStream body
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Sep 2017 21:25:19 +0000 (21:25 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Sep 2017 21:25:19 +0000 (21:25 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176462

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

LayoutTests/imported/w3c:

* web-platform-tests/service-workers/cache-storage/worker/cache-put.https-expected.txt:

Source/WebCore:

Tests: http/wpt/cache-storage/cache-put-stream.https.any.html
       http/wpt/cache-storage/cache-put-stream.https.any.worker.html

Making FetchResponse use more of ReadableStream:
- Cloning of its body is done through ReadableStream.
- Computation of disturbed and locked is done through ReadableStream.
- Storing of the ReadableStream body given at constructor time is done in FetchBody for FetchResponse.
This allows making FetchResponse closer to FetchRequest.
This also allows to correctly compute disturbed and locked in clone cases.

Adding the ability to consume a Response ReadableStream body as a SharedBuffer.
Using that ability in DOMCache.

* Modules/cache/DOMCache.cpp:
(WebCore::DOMCache::putWithResponseData):
(WebCore::DOMCache::put):
* Modules/cache/DOMCache.h:
* Modules/fetch/FetchBody.h:
(WebCore::FetchBody::readableStream const):
* Modules/fetch/FetchBodyConsumer.cpp:
(WebCore::FetchBodyConsumer::extract):
* Modules/fetch/FetchBodyConsumer.h:
* Modules/fetch/FetchBodyOwner.cpp:
(WebCore::FetchBodyOwner::isDisturbed const):
(WebCore::FetchBodyOwner::isDisturbedOrLocked const):
* Modules/fetch/FetchBodyOwner.h:
(WebCore::FetchBodyOwner::hasReadableStreamBody const):
* Modules/fetch/FetchBodySource.cpp:
* Modules/fetch/FetchBodySource.h:
* Modules/fetch/FetchResponse.cpp:
(WebCore::FetchResponse::consumeBodyFromReadableStream):
(WebCore::FetchResponse::consumeBodyWhenLoaded):
(WebCore::FetchResponse::consumeBodyAsStream):
* Modules/fetch/FetchResponse.h:
* Modules/fetch/FetchResponse.idl:
* Modules/fetch/FetchResponse.js:
(initializeFetchResponse):
(clone):
* bindings/js/ReadableStream.cpp:
(WebCore::callFunction):
(WebCore::ReadableStream::pipeTo):
(WebCore::ReadableStream::tee):
(WebCore::checkReadableStream):
(WebCore::ReadableStream::isLocked const):
(WebCore::ReadableStream::isDisturbed const):
(WebCore::ReadableStream::isDisturbed):
* bindings/js/ReadableStream.h:
* bindings/js/ReadableStreamDefaultController.cpp:
* bindings/js/ReadableStreamDefaultController.h:
* bindings/js/WebCoreBuiltinNames.h:
* testing/Internals.cpp:
(WebCore::Internals::isReadableStreamDisturbed):

LayoutTests:

* TestExpectations: Removing flakiness expectation for cache-put tests.
* http/wpt/cache-storage/cache-put-stream.https.any-expected.txt: Added.
* http/wpt/cache-storage/cache-put-stream.https.any.html: Added.
* http/wpt/cache-storage/cache-put-stream.https.any.js: Added.
(cache_test.):
(cache_test):
(string_appeared_here.cache_test.return.reader.read.then.):
(string_appeared_here.cache_test):
* http/wpt/cache-storage/cache-put-stream.https.any.worker-expected.txt: Added.
* http/wpt/cache-storage/cache-put-stream.https.any.worker.html: Added.

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

29 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.html [new file with mode: 0644]
LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.js [new file with mode: 0644]
LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.worker-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.worker.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/service-workers/cache-storage/worker/cache-put.https-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/Modules/cache/DOMCache.cpp
Source/WebCore/Modules/cache/DOMCache.h
Source/WebCore/Modules/fetch/FetchBody.h
Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp
Source/WebCore/Modules/fetch/FetchBodyConsumer.h
Source/WebCore/Modules/fetch/FetchBodyOwner.cpp
Source/WebCore/Modules/fetch/FetchBodyOwner.h
Source/WebCore/Modules/fetch/FetchBodySource.cpp
Source/WebCore/Modules/fetch/FetchBodySource.h
Source/WebCore/Modules/fetch/FetchResponse.cpp
Source/WebCore/Modules/fetch/FetchResponse.h
Source/WebCore/Modules/fetch/FetchResponse.idl
Source/WebCore/Modules/fetch/FetchResponse.js
Source/WebCore/bindings/js/ReadableStream.cpp
Source/WebCore/bindings/js/ReadableStream.h
Source/WebCore/bindings/js/ReadableStreamDefaultController.cpp
Source/WebCore/bindings/js/ReadableStreamDefaultController.h
Source/WebCore/bindings/js/WebCoreBuiltinNames.h
Source/WebCore/testing/Internals.cpp

index 836b481..8fd11be 100644 (file)
@@ -1,3 +1,21 @@
+2017-09-06  Youenn Fablet  <youenn@apple.com>
+
+        Support caching of Response with a ReadableStream body
+        https://bugs.webkit.org/show_bug.cgi?id=176462
+
+        Reviewed by Alex Christensen.
+
+        * TestExpectations: Removing flakiness expectation for cache-put tests.
+        * http/wpt/cache-storage/cache-put-stream.https.any-expected.txt: Added.
+        * http/wpt/cache-storage/cache-put-stream.https.any.html: Added.
+        * http/wpt/cache-storage/cache-put-stream.https.any.js: Added.
+        (cache_test.):
+        (cache_test):
+        (string_appeared_here.cache_test.return.reader.read.then.):
+        (string_appeared_here.cache_test):
+        * http/wpt/cache-storage/cache-put-stream.https.any.worker-expected.txt: Added.
+        * http/wpt/cache-storage/cache-put-stream.https.any.worker.html: Added.
+
 2017-09-06  Ryan Haddad  <ryanhaddad@apple.com>
 
         Unreviewed, rolling out r221461.
index 7d100a3..b941f20 100644 (file)
@@ -123,8 +123,6 @@ imported/w3c/web-platform-tests/service-workers [ Skip ]
 imported/w3c/web-platform-tests/service-workers/stub-4.6.2-cache.html [ Pass ]
 imported/w3c/web-platform-tests/service-workers/stub-4.6.3-cache-storage.html [ Pass ]
 imported/w3c/web-platform-tests/service-workers/cache-storage [ Pass ]
-imported/w3c/web-platform-tests/service-workers/cache-storage/window/cache-put.https.html [ Pass Failure ]
-imported/w3c/web-platform-tests/service-workers/cache-storage/worker/cache-put.https.html [ Pass Failure ]
 
 # textarea.animate is not supported
 imported/w3c/web-platform-tests/css/css-ui-3/caret-color-018.html [ Skip ]
diff --git a/LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any-expected.txt b/LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any-expected.txt
new file mode 100644 (file)
index 0000000..326c3eb
--- /dev/null
@@ -0,0 +1,4 @@
+
+PASS Cache.put should throw if response stream is locked 
+PASS Cache.put should throw if response stream is disturbed 
+
diff --git a/LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.html b/LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.html
new file mode 100644 (file)
index 0000000..2382913
--- /dev/null
@@ -0,0 +1 @@
+<!-- This file is required for WebKit test infrastructure to run the templated test -->
\ No newline at end of file
diff --git a/LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.js b/LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.js
new file mode 100644 (file)
index 0000000..ac66860
--- /dev/null
@@ -0,0 +1,31 @@
+// META: script=/service-workers/cache-storage/resources/test-helpers.js
+
+var test_url = 'https://example.com/foo';
+var test_body = 'Hello world!';
+
+cache_test(function(cache) {
+    var stream = new ReadableStream();
+    stream.getReader();
+    var response = new Response(stream);
+    return cache.put(new Request(''), response).then(assert_unreached, (e) => {
+        assert_throws(new TypeError, function() { throw e });
+    });
+}, 'Cache.put should throw if response stream is locked')
+
+cache_test(function(cache) {
+    var stream = new ReadableStream({start: (c) => {
+        c.enqueue(new Uint8Array(1));
+        c.enqueue(new Uint8Array(1));
+        c.close();
+    }});
+    var reader = stream.getReader();
+    return reader.read().then(() => {
+        reader.releaseLock();
+        var response = new Response(stream);
+        return cache.put(new Request(''), response).then(assert_unreached, (e) => {
+            assert_throws(new TypeError, function() { throw e });
+        });
+    });
+}, 'Cache.put should throw if response stream is disturbed')
+
+done();
diff --git a/LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.worker-expected.txt b/LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.worker-expected.txt
new file mode 100644 (file)
index 0000000..326c3eb
--- /dev/null
@@ -0,0 +1,4 @@
+
+PASS Cache.put should throw if response stream is locked 
+PASS Cache.put should throw if response stream is disturbed 
+
diff --git a/LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.worker.html b/LayoutTests/http/wpt/cache-storage/cache-put-stream.https.any.worker.html
new file mode 100644 (file)
index 0000000..2382913
--- /dev/null
@@ -0,0 +1 @@
+<!-- This file is required for WebKit test infrastructure to run the templated test -->
\ No newline at end of file
index 1c5b9e9..e223c6d 100644 (file)
@@ -1,3 +1,12 @@
+2017-09-06  Youenn Fablet  <youenn@apple.com>
+
+        Support caching of Response with a ReadableStream body
+        https://bugs.webkit.org/show_bug.cgi?id=176462
+
+        Reviewed by Alex Christensen.
+
+        * web-platform-tests/service-workers/cache-storage/worker/cache-put.https-expected.txt:
+
 2017-09-06  Manuel Rego Casasnovas  <rego@igalia.com>
 
         [css-grid] grid shorthand should not reset the gutter properties
index 1c82dd9..6105d67 100644 (file)
@@ -1,6 +1,6 @@
 
 PASS Cache.put called with simple Request and Response 
-FAIL Cache.put called with Request and Response from fetch() promise_test: Unhandled rejection with value: object "NotSupportedError: Caching a Response with data stored in a ReadableStream is not yet supported"
+PASS Cache.put called with Request and Response from fetch() 
 PASS Cache.put with Request without a body 
 PASS Cache.put with Response without a body 
 PASS Cache.put with a Response containing an empty URL 
index dd183f1..83b45c5 100644 (file)
@@ -1,3 +1,63 @@
+2017-09-06  Youenn Fablet  <youenn@apple.com>
+
+        Support caching of Response with a ReadableStream body
+        https://bugs.webkit.org/show_bug.cgi?id=176462
+
+        Reviewed by Alex Christensen.
+
+        Tests: http/wpt/cache-storage/cache-put-stream.https.any.html
+               http/wpt/cache-storage/cache-put-stream.https.any.worker.html
+
+        Making FetchResponse use more of ReadableStream:
+        - Cloning of its body is done through ReadableStream.
+        - Computation of disturbed and locked is done through ReadableStream.
+        - Storing of the ReadableStream body given at constructor time is done in FetchBody for FetchResponse.
+        This allows making FetchResponse closer to FetchRequest.
+        This also allows to correctly compute disturbed and locked in clone cases.
+
+        Adding the ability to consume a Response ReadableStream body as a SharedBuffer.
+        Using that ability in DOMCache.
+
+        * Modules/cache/DOMCache.cpp:
+        (WebCore::DOMCache::putWithResponseData):
+        (WebCore::DOMCache::put):
+        * Modules/cache/DOMCache.h:
+        * Modules/fetch/FetchBody.h:
+        (WebCore::FetchBody::readableStream const):
+        * Modules/fetch/FetchBodyConsumer.cpp:
+        (WebCore::FetchBodyConsumer::extract):
+        * Modules/fetch/FetchBodyConsumer.h:
+        * Modules/fetch/FetchBodyOwner.cpp:
+        (WebCore::FetchBodyOwner::isDisturbed const):
+        (WebCore::FetchBodyOwner::isDisturbedOrLocked const):
+        * Modules/fetch/FetchBodyOwner.h:
+        (WebCore::FetchBodyOwner::hasReadableStreamBody const):
+        * Modules/fetch/FetchBodySource.cpp:
+        * Modules/fetch/FetchBodySource.h:
+        * Modules/fetch/FetchResponse.cpp:
+        (WebCore::FetchResponse::consumeBodyFromReadableStream):
+        (WebCore::FetchResponse::consumeBodyWhenLoaded):
+        (WebCore::FetchResponse::consumeBodyAsStream):
+        * Modules/fetch/FetchResponse.h:
+        * Modules/fetch/FetchResponse.idl:
+        * Modules/fetch/FetchResponse.js:
+        (initializeFetchResponse):
+        (clone):
+        * bindings/js/ReadableStream.cpp:
+        (WebCore::callFunction):
+        (WebCore::ReadableStream::pipeTo):
+        (WebCore::ReadableStream::tee):
+        (WebCore::checkReadableStream):
+        (WebCore::ReadableStream::isLocked const):
+        (WebCore::ReadableStream::isDisturbed const):
+        (WebCore::ReadableStream::isDisturbed):
+        * bindings/js/ReadableStream.h:
+        * bindings/js/ReadableStreamDefaultController.cpp:
+        * bindings/js/ReadableStreamDefaultController.h:
+        * bindings/js/WebCoreBuiltinNames.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::isReadableStreamDisturbed):
+
 2017-09-06  Ryan Haddad  <ryanhaddad@apple.com>
 
         Unreviewed, rolling out r221461.
index acd339c..85a5c4b 100644 (file)
@@ -289,6 +289,21 @@ void DOMCache::addAll(Vector<RequestInfo>&& infos, DOMPromiseDeferred<void>&& pr
     }
 }
 
+void DOMCache::putWithResponseData(DOMPromiseDeferred<void>&& promise, Ref<FetchRequest>&& request, Ref<FetchResponse>&& response, ExceptionOr<RefPtr<SharedBuffer>>&& responseBody)
+{
+    if (responseBody.hasException()) {
+        promise.reject(responseBody.releaseException());
+        return;
+    }
+
+    DOMCacheEngine::ResponseBody body;
+    if (auto buffer = responseBody.releaseReturnValue())
+        body = buffer.releaseNonNull();
+    batchPutOperation(request.get(), response.get(), WTFMove(body), [promise = WTFMove(promise)](ExceptionOr<void>&& result) mutable {
+        promise.settle(WTFMove(result));
+    });
+}
+
 void DOMCache::put(RequestInfo&& info, Ref<FetchResponse>&& response, DOMPromiseDeferred<void>&& promise)
 {
     if (UNLIKELY(!scriptExecutionContext()))
@@ -312,30 +327,24 @@ void DOMCache::put(RequestInfo&& info, Ref<FetchResponse>&& response, DOMPromise
         return;
     }
 
-    // FIXME: Add support for ReadableStream.
-    if (response->hasReadableStreamBody()) {
-        promise.reject(Exception { NotSupportedError, ASCIILiteral("Caching a Response with data stored in a ReadableStream is not yet supported") });
+    if (response->isDisturbedOrLocked()) {
+        promise.reject(Exception { TypeError, ASCIILiteral("Response is disturbed or locked") });
         return;
     }
 
-    if (response->isDisturbed()) {
-        promise.reject(Exception { TypeError, ASCIILiteral("Response is disturbed or locked") });
+    if (response->hasReadableStreamBody()) {
+        setPendingActivity(this);
+        response->consumeBodyFromReadableStream([promise = WTFMove(promise), request = WTFMove(request), response = WTFMove(response), this](ExceptionOr<RefPtr<SharedBuffer>>&& result) mutable {
+            putWithResponseData(WTFMove(promise), WTFMove(request), WTFMove(response), WTFMove(result));
+            unsetPendingActivity(this);
+        });
         return;
     }
 
     if (response->isLoading()) {
         setPendingActivity(this);
         response->consumeBodyWhenLoaded([promise = WTFMove(promise), request = WTFMove(request), response = WTFMove(response), this](ExceptionOr<RefPtr<SharedBuffer>>&& result) mutable {
-            if (result.hasException())
-                promise.reject(result.releaseException());
-            else {
-                DOMCacheEngine::ResponseBody body;
-                if (auto buffer = result.releaseReturnValue())
-                    body = buffer.releaseNonNull();
-                batchPutOperation(request.get(), response.get(), WTFMove(body), [promise = WTFMove(promise)](ExceptionOr<void>&& result) mutable {
-                    promise.settle(WTFMove(result));
-                });
-            }
+            putWithResponseData(WTFMove(promise), WTFMove(request), WTFMove(response), WTFMove(result));
             unsetPendingActivity(this);
         });
         return;
index 1ad0019..20a49e9 100644 (file)
@@ -69,6 +69,8 @@ private:
     const char* activeDOMObjectName() const final;
     bool canSuspendForDocumentSuspension() const final;
 
+    void putWithResponseData(DOMPromiseDeferred<void>&&, Ref<FetchRequest>&&, Ref<FetchResponse>&&, ExceptionOr<RefPtr<SharedBuffer>>&&);
+
     void retrieveRecords(const URL&, WTF::Function<void(std::optional<Exception>&&)>&&);
     Vector<CacheStorageRecord> queryCacheWithTargetStorage(const FetchRequest&, const CacheQueryOptions&, const Vector<CacheStorageRecord>&);
     void queryCache(Ref<FetchRequest>&&, CacheQueryOptions&&, WTF::Function<void(ExceptionOr<Vector<CacheStorageRecord>>&&)>&&);
index 27acc69..c3aa7fe 100644 (file)
@@ -81,6 +81,7 @@ public:
     void cleanConsumer() { m_consumer.clean(); }
 
     FetchBody clone();
+    const ReadableStream* readableStream() const { return m_readableStream.get(); }
     ReadableStream* readableStream() { return m_readableStream.get(); }
     void setReadableStream(Ref<ReadableStream>&& stream)
     {
index b9ee1e1..d2d5260 100644 (file)
@@ -92,6 +92,13 @@ void FetchBodyConsumer::resolveWithData(Ref<DeferredPromise>&& promise, const un
     resolveWithTypeAndData(WTFMove(promise), m_type, m_contentType, data, length);
 }
 
+void FetchBodyConsumer::extract(ReadableStream& stream, ReadableStreamToSharedBufferSink::Callback&& callback)
+{
+    ASSERT(!m_sink);
+    m_sink = ReadableStreamToSharedBufferSink::create(WTFMove(callback));
+    m_sink->pipeFrom(stream);
+}
+
 void FetchBodyConsumer::resolve(Ref<DeferredPromise>&& promise, ReadableStream* stream)
 {
     if (stream) {
index dfd8fb1..a5e0182 100644 (file)
@@ -61,6 +61,7 @@ public:
 
     void clean();
 
+    void extract(ReadableStream&, ReadableStreamToSharedBufferSink::Callback&&);
     void resolve(Ref<DeferredPromise>&&, ReadableStream*);
     void resolveWithData(Ref<DeferredPromise>&&, const unsigned char*, unsigned);
 
index 0fb2df3..bc86ad2 100644 (file)
@@ -59,14 +59,33 @@ void FetchBodyOwner::stop()
     }
 }
 
-bool FetchBodyOwner::isDisturbedOrLocked() const
+bool FetchBodyOwner::isDisturbed() const
 {
+    if (isBodyNull())
+        return false;
+
     if (m_isDisturbed)
         return true;
 
 #if ENABLE(STREAMS_API)
-    if (m_readableStreamSource && m_readableStreamSource->isReadableStreamLocked())
+    if (body().readableStream())
+        return body().readableStream()->isDisturbed();
+#endif
+
+    return false;
+}
+
+bool FetchBodyOwner::isDisturbedOrLocked() const
+{
+    if (isBodyNull())
+        return false;
+
+    if (m_isDisturbed)
         return true;
+
+#if ENABLE(STREAMS_API)
+    if (body().readableStream())
+        return body().readableStream()->isDisturbed() || body().readableStream()->isLocked();
 #endif
 
     return false;
index 9895c67..3d53564 100644 (file)
@@ -48,7 +48,7 @@ public:
     void json(Ref<DeferredPromise>&&);
     void text(Ref<DeferredPromise>&&);
 
-    bool isDisturbed() const { return m_isDisturbed; };
+    bool isDisturbed() const;
     bool isDisturbedOrLocked() const;
 
     void loadBlob(const Blob&, FetchBodyConsumer*);
@@ -56,7 +56,7 @@ public:
     bool isActive() const { return !!m_blobLoader; }
 
     RefPtr<ReadableStream> readableStream(JSC::ExecState&);
-    virtual bool hasReadableStreamBody() const { return m_body && m_body->hasReadableStream(); }
+    bool hasReadableStreamBody() const { return m_body && m_body->hasReadableStream(); }
 
 #if ENABLE(STREAMS_API)
     virtual void consumeBodyAsStream();
index 2aa7cba..79459f3 100644 (file)
@@ -38,11 +38,6 @@ FetchBodySource::FetchBodySource(FetchBodyOwner& bodyOwner)
 {
 }
 
-bool FetchBodySource::isReadableStreamLocked() const
-{
-    return controller().isControlledReadableStreamLocked();
-}
-
 void FetchBodySource::setActive()
 {
     m_bodyOwner.setPendingActivity(&m_bodyOwner);
index 076c498..98159b8 100644 (file)
@@ -47,7 +47,6 @@ public:
     void error(const String&);
 
     bool isCancelling() const { return m_isCancelling; }
-    bool isReadableStreamLocked() const;
 
     void resolvePullPromise() { pullFinished(); }
 
index cce7a87..4772051 100644 (file)
@@ -79,14 +79,6 @@ void FetchResponse::initializeWith(FetchBody::Init&& body)
     updateContentType();
 }
 
-void FetchResponse::setBodyAsReadableStream()
-{
-    if (isBodyNull())
-        setBody(FetchBody::loadingBody());
-    m_isReadableStream = true;
-    updateContentType();
-}
-
 FetchResponse::FetchResponse(ScriptExecutionContext& context, std::optional<FetchBody>&& body, Ref<FetchHeaders>&& headers, ResourceResponse&& response)
     : FetchBodyOwner(context, WTFMove(body), WTFMove(headers))
     , m_response(WTFMove(response))
@@ -277,11 +269,22 @@ FetchResponse::ResponseData FetchResponse::consumeBody()
     return body().take();
 }
 
+void FetchResponse::consumeBodyFromReadableStream(ConsumeDataCallback&& callback)
+{
+    ASSERT(m_body);
+    ASSERT(m_body->readableStream());
+
+    ASSERT(!isDisturbed());
+    m_isDisturbed = true;
+
+    m_body->consumer().extract(*m_body->readableStream(), WTFMove(callback));
+}
+
 void FetchResponse::consumeBodyWhenLoaded(ConsumeDataCallback&& callback)
 {
     ASSERT(isLoading());
 
-    ASSERT(!m_isDisturbed);
+    ASSERT(!isDisturbed());
     m_isDisturbed = true;
 
     m_bodyLoader->setConsumeDataCallback(WTFMove(callback));
@@ -329,10 +332,11 @@ void FetchResponse::consumeBodyAsStream()
         return;
     }
 
-    m_isDisturbed = true;
     ASSERT(m_bodyLoader);
 
-    setBodyAsReadableStream();
+    if (isBodyNull())
+        setBody(FetchBody::loadingBody());
+    updateContentType();
 
     auto data = m_bodyLoader->startStreaming();
     if (data) {
index dc9f11e..985ee81 100644 (file)
@@ -71,7 +71,6 @@ public:
 
     ExceptionOr<void> setStatus(int status, const String& statusText);
     void initializeWith(FetchBody::Init&&);
-    void setBodyAsReadableStream();
 
     Type type() const { return m_response.type(); }
     const String& url() const;
@@ -99,12 +98,10 @@ public:
 
     using ConsumeDataCallback = WTF::Function<void(ExceptionOr<RefPtr<SharedBuffer>>&&)>;
     void consumeBodyWhenLoaded(ConsumeDataCallback&&);
+    void consumeBodyFromReadableStream(ConsumeDataCallback&&);
 
     const ResourceResponse& resourceResponse() const { return m_response; }
 
-    // FIXME: Remove this method and use FetchBodyOwner one once we have full support in DOM ReadableStream.
-    bool hasReadableStreamBody() const final { return m_isReadableStream; }
-
 private:
     FetchResponse(ScriptExecutionContext&, std::optional<FetchBody>&&, Ref<FetchHeaders>&&, ResourceResponse&&);
 
@@ -147,8 +144,6 @@ private:
     std::optional<BodyLoader> m_bodyLoader;
     mutable String m_responseURL;
     bool m_shouldExposeBody { true };
-    // FIXME: Remove that flag once we have full support in DOM ReadableStream.
-    bool m_isReadableStream { false };
 
     FetchBodyConsumer m_consumer { FetchBodyConsumer::Type::ArrayBuffer  };
 };
index 9510602..0c786c8 100644 (file)
@@ -85,5 +85,4 @@ dictionary FetchResponseInit {
     [PrivateIdentifier] void initializeWith(BodyInit body);
     [CallWith=ScriptState, NewObject, PrivateIdentifier] ReadableStream? createReadableStream();
     [PrivateIdentifier] boolean isDisturbed();
-    [PrivateIdentifier] void setBodyAsReadableStream();
 };
index 78fea0f..74130f4 100644 (file)
@@ -49,11 +49,9 @@ function initializeFetchResponse(body, init)
 
         // FIXME: Use @isReadableStream once it is no longer guarded by STREAMS_API compilation guard.
         let isBodyReadableStream = (@isObject(body) && !!body.@readableStreamController);
-        if (isBodyReadableStream) {
+        if (isBodyReadableStream)
             this.@body = body;
-            this.@setBodyAsReadableStream();
-        } else
-            this.@initializeWith(body);
+        this.@initializeWith(body);
     }
 
     return this;
@@ -96,17 +94,15 @@ function clone()
     if (@Response.prototype.@isDisturbed.@call(this) || (this.@body && @isReadableStreamLocked(this.@body)))
         @throwTypeError("Cannot clone a disturbed Response");
 
-    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 === @undefined)
         this.@body = @Response.prototype.@createReadableStream.@call(this);
 
-    if (this.@body) {
-        var teedReadableStreams = @readableStreamTee(this.@body, true);
-        this.@body = teedReadableStreams[0];
-        cloned.@body = teedReadableStreams[1];
-    }
+    var cloned = @Response.prototype.@cloneForJS.@call(this);
+
+    // Let's refresh @body with the cloned stream.
+    this.@body = @Response.prototype.@createReadableStream.@call(this);
+
     return cloned;
 }
 
index cf48697..d16105e 100644 (file)
@@ -55,6 +55,17 @@ Ref<ReadableStream> ReadableStream::create(JSC::ExecState& execState, RefPtr<Rea
     return create(globalObject, *newReadableStream);
 }
 
+static inline JSC::JSValue callFunction(JSC::ExecState& state, JSC::JSValue jsFunction, JSC::JSValue thisValue, const JSC::ArgList& arguments)
+{
+    auto scope = DECLARE_CATCH_SCOPE(state.vm());
+    JSC::CallData callData;
+    auto callType = JSC::getCallData(jsFunction, callData);
+    ASSERT(callType != JSC::CallType::None);
+    auto result = call(&state, jsFunction, callType, callData, thisValue, arguments);
+    scope.assertNoException();
+    return result;
+}
+
 void ReadableStream::pipeTo(ReadableStreamSink& sink)
 {
     auto& state = *m_globalObject->globalExec();
@@ -64,13 +75,10 @@ void ReadableStream::pipeTo(ReadableStreamSink& sink)
     auto readableStreamPipeTo = m_globalObject->get(&state, privateName);
     ASSERT(readableStreamPipeTo.isFunction());
 
-    CallData callData;
-    CallType callType = getCallData(readableStreamPipeTo, callData);
-    ASSERT(callType != JSC::CallType::None);
     MarkedArgumentBuffer arguments;
     arguments.append(readableStream());
     arguments.append(toJS(&state, m_globalObject.get(), sink));
-    JSC::call(&state, readableStreamPipeTo, callType, callData, JSC::jsUndefined(), arguments);
+    callFunction(state, readableStreamPipeTo, JSC::jsUndefined(), arguments);
 }
 
 std::pair<Ref<ReadableStream>, Ref<ReadableStream>> ReadableStream::tee()
@@ -82,13 +90,10 @@ std::pair<Ref<ReadableStream>, Ref<ReadableStream>> ReadableStream::tee()
     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 returnedValue = callFunction(state, readableStreamTee, JSC::jsUndefined(), arguments);
 
     auto results = Detail::SequenceConverter<IDLInterface<ReadableStream>>::convert(state, returnedValue);
 
@@ -96,4 +101,33 @@ std::pair<Ref<ReadableStream>, Ref<ReadableStream>> ReadableStream::tee()
     return std::make_pair(results[0].releaseNonNull(), results[1].releaseNonNull());
 }
 
+static inline bool checkReadableStream(JSDOMGlobalObject& globalObject, JSReadableStream* readableStream, JSC::JSValue function)
+{
+    auto& state = *globalObject.globalExec();
+
+    ASSERT(function);
+    JSC::MarkedArgumentBuffer arguments;
+    arguments.append(readableStream);
+    return callFunction(state, function, JSC::jsUndefined(), arguments).isTrue();
+}
+
+bool ReadableStream::isLocked() const
+{
+    return checkReadableStream(*globalObject(), readableStream(), globalObject()->builtinInternalFunctions().readableStreamInternals().m_isReadableStreamLockedFunction.get());
+}
+
+bool ReadableStream::isDisturbed() const
+{
+    return checkReadableStream(*globalObject(), readableStream(), globalObject()->builtinInternalFunctions().readableStreamInternals().m_isReadableStreamDisturbedFunction.get());
+}
+
+bool ReadableStream::isDisturbed(ExecState& state, JSValue value)
+{
+    auto& vm = state.vm();
+    auto& globalObject = *jsDynamicDowncast<JSDOMGlobalObject*>(vm, state.lexicalGlobalObject());
+    auto* readableStream = jsDynamicDowncast<JSReadableStream*>(vm, value);
+
+    return checkReadableStream(globalObject, readableStream, globalObject.builtinInternalFunctions().readableStreamInternals().m_isReadableStreamDisturbedFunction.get());
+}
+
 }
index ae5b0d5..78425cf 100644 (file)
@@ -41,11 +41,15 @@ public:
 
     static Ref<ReadableStream> create(JSC::ExecState&, RefPtr<ReadableStreamSource>&&);
 
+    WEBCORE_EXPORT static bool isDisturbed(JSC::ExecState&, JSC::JSValue);
+
     std::pair<Ref<ReadableStream>, Ref<ReadableStream>> tee();
 
     void pipeTo(ReadableStreamSink&);
+    bool isLocked() const;
+    bool isDisturbed() const;
 
-    JSReadableStream* readableStream() { return guarded(); }
+    JSReadableStream* readableStream() const { return guarded(); }
 
 protected:
     ReadableStream(JSDOMGlobalObject& globalObject, JSReadableStream& readableStream) : DOMGuarded<JSReadableStream>(globalObject, readableStream) { }
index 71c55e4..1659caf 100644 (file)
@@ -69,29 +69,6 @@ JSC::JSValue ReadableStreamDefaultController::invoke(JSC::ExecState& state, JSC:
     return callFunction(state, function, &object, arguments);
 }
 
-bool ReadableStreamDefaultController::isControlledReadableStreamLocked() const
-{
-    auto& globalObject = this->globalObject();
-    JSC::VM& vm = globalObject.vm();
-    JSC::JSLockHolder lock(vm);
-    auto scope = DECLARE_CATCH_SCOPE(vm);
-    auto& state = globalExec();
-
-    auto& clientData = *static_cast<JSVMClientData*>(vm.clientData);
-    auto readableStream = m_jsController->get(&state, clientData.builtinNames().controlledReadableStreamPrivateName());
-    scope.assertNoException();
-
-    auto* isLocked = globalObject.builtinInternalFunctions().readableStreamInternals().m_isReadableStreamLockedFunction.get();
-    ASSERT(isLocked);
-
-    JSC::MarkedArgumentBuffer arguments;
-    arguments.append(readableStream);
-    auto result = callFunction(state, isLocked, JSC::jsUndefined(), arguments);
-    scope.assertNoException();
-
-    return result.isTrue();
-}
-
 } // namespace WebCore
 
 #endif // ENABLE(STREAMS_API)
index 3634c4b..afaa177 100644 (file)
@@ -54,8 +54,6 @@ public:
 
     void close() { invoke(*globalObject().globalExec(), jsController(), "close", JSC::jsUndefined()); }
 
-    bool isControlledReadableStreamLocked() const;
-
 private:
     void error(JSC::ExecState& state, JSC::JSValue value) { invoke(state, jsController(), "error", value); }
     void enqueue(JSC::ExecState& state, JSC::JSValue value) { invoke(state, jsController(), "enqueue", value); }
index 572b7c7..5a6424b 100644 (file)
@@ -232,7 +232,6 @@ namespace WebCore {
     macro(retrieveResponse) \
     macro(self) \
     macro(setBody) \
-    macro(setBodyAsReadableStream) \
     macro(setBodyFromInputRequest) \
     macro(setStatus) \
     macro(showModalDialog) \
index 7b49031..7b158df 100644 (file)
 #include "PrintContext.h"
 #include "PseudoElement.h"
 #include "Range.h"
+#include "ReadableStream.h"
 #include "RenderEmbeddedObject.h"
 #include "RenderLayerBacking.h"
 #include "RenderLayerCompositor.h"
@@ -3796,25 +3797,7 @@ void Internals::setShowAllPlugins(bool show)
 
 bool Internals::isReadableStreamDisturbed(JSC::ExecState& state, JSValue stream)
 {
-    JSGlobalObject* globalObject = state.vmEntryGlobalObject();
-    JSVMClientData* clientData = static_cast<JSVMClientData*>(state.vm().clientData);
-    const Identifier& privateName = clientData->builtinFunctions().readableStreamInternalsBuiltins().isReadableStreamDisturbedPrivateName();
-    JSValue value;
-    PropertySlot propertySlot(value, PropertySlot::InternalMethodType::Get);
-    globalObject->methodTable()->getOwnPropertySlot(globalObject, &state, privateName, propertySlot);
-    value = propertySlot.getValue(&state, privateName);
-    ASSERT(value.isFunction());
-
-    JSObject* function = value.getObject();
-    CallData callData;
-    CallType callType = JSC::getCallData(function, callData);
-    ASSERT(callType != JSC::CallType::None);
-    MarkedArgumentBuffer arguments;
-    arguments.append(stream);
-    JSValue returnedValue = JSC::call(&state, function, callType, callData, JSC::jsUndefined(), arguments);
-    ASSERT(returnedValue.isBoolean());
-
-    return returnedValue.asBoolean();
+    return ReadableStream::isDisturbed(state, stream);
 }
 
 JSValue Internals::cloneArrayBuffer(JSC::ExecState& state, JSValue buffer, JSValue srcByteOffset, JSValue srcLength)