<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[206857] trunk/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/206857">206857</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2016-10-06 02:43:52 -0700 (Thu, 06 Oct 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Fetch API] Use ReadableStream pull to transfer binary data to stream when application needs it
https://bugs.webkit.org/show_bug.cgi?id=162892

Patch by Youenn Fablet &lt;youenn@apple.com&gt; on 2016-10-06
Reviewed by Alex Christensen.

Covered by existing tests.

Before this patch, FetchResponse was never resolving the start promise.
This way, it could enqueue data, error or close the stream whenever desired.

With this patch, FetchResponse will feed the stream when being asked to.
This allows keeping the data in WebCore until the application needs it.
This is only implemented for network data.
For other data owned by response (blob, text...), data will be enqueued like previously as fast as possible.

Note that FetchResponse can enqueue/error/close the stream at any time since JSFetchResponse has a reference to the stream.
And the stream has a reference to the controller.

In addition to transfer binary chunks to ReadableStream only when needed, WebCore is now aware of the data
stored in the response, which may allow applying backpressure to the network source in the future.

* Modules/fetch/FetchResponse.cpp:
(WebCore::FetchResponse::BodyLoader::didSucceed):
(WebCore::FetchResponse::BodyLoader::didReceiveData): Enqueuing only if stream is pulling.
Otherwise, storing in FetchBodyConsumer. If stream is pulling, we enqueue both buffered data and received chunk.
(WebCore::FetchResponse::consumeBodyAsStream): Resolving pull promise if we enqueued some buffered data.
(WebCore::FetchResponse::closeStream):
(WebCore::FetchResponse::feedStream): If we have some buffered data, we enqueue it. If there is no loader, the stream can be closed.
* Modules/fetch/FetchResponse.h:
* Modules/fetch/FetchResponseSource.cpp:
(WebCore::FetchResponseSource::doPull):
(WebCore::FetchResponseSource::close):
(WebCore::FetchResponseSource::error):
* Modules/fetch/FetchResponseSource.h:
* Modules/streams/ReadableStreamSource.h:
(WebCore::ReadableStreamSource::isPulling): Renamed from isStarting.
(WebCore::ReadableStreamSource::isStarting): Deleted.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesfetchFetchResponsecpp">trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesfetchFetchResponseh">trunk/Source/WebCore/Modules/fetch/FetchResponse.h</a></li>
<li><a href="#trunkSourceWebCoreModulesfetchFetchResponseSourcecpp">trunk/Source/WebCore/Modules/fetch/FetchResponseSource.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesfetchFetchResponseSourceh">trunk/Source/WebCore/Modules/fetch/FetchResponseSource.h</a></li>
<li><a href="#trunkSourceWebCoreModulesstreamsReadableStreamSourceh">trunk/Source/WebCore/Modules/streams/ReadableStreamSource.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (206856 => 206857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-10-06 08:54:49 UTC (rev 206856)
+++ trunk/Source/WebCore/ChangeLog        2016-10-06 09:43:52 UTC (rev 206857)
</span><span class="lines">@@ -1,3 +1,43 @@
</span><ins>+2016-10-06  Youenn Fablet  &lt;youenn@apple.com&gt;
+
+        [Fetch API] Use ReadableStream pull to transfer binary data to stream when application needs it
+        https://bugs.webkit.org/show_bug.cgi?id=162892
+
+        Reviewed by Alex Christensen.
+
+        Covered by existing tests.
+
+        Before this patch, FetchResponse was never resolving the start promise.
+        This way, it could enqueue data, error or close the stream whenever desired.
+
+        With this patch, FetchResponse will feed the stream when being asked to.
+        This allows keeping the data in WebCore until the application needs it.
+        This is only implemented for network data.
+        For other data owned by response (blob, text...), data will be enqueued like previously as fast as possible.
+
+        Note that FetchResponse can enqueue/error/close the stream at any time since JSFetchResponse has a reference to the stream.
+        And the stream has a reference to the controller.
+
+        In addition to transfer binary chunks to ReadableStream only when needed, WebCore is now aware of the data
+        stored in the response, which may allow applying backpressure to the network source in the future.
+
+        * Modules/fetch/FetchResponse.cpp:
+        (WebCore::FetchResponse::BodyLoader::didSucceed):
+        (WebCore::FetchResponse::BodyLoader::didReceiveData): Enqueuing only if stream is pulling.
+        Otherwise, storing in FetchBodyConsumer. If stream is pulling, we enqueue both buffered data and received chunk.
+        (WebCore::FetchResponse::consumeBodyAsStream): Resolving pull promise if we enqueued some buffered data.
+        (WebCore::FetchResponse::closeStream):
+        (WebCore::FetchResponse::feedStream): If we have some buffered data, we enqueue it. If there is no loader, the stream can be closed.
+        * Modules/fetch/FetchResponse.h:
+        * Modules/fetch/FetchResponseSource.cpp:
+        (WebCore::FetchResponseSource::doPull):
+        (WebCore::FetchResponseSource::close):
+        (WebCore::FetchResponseSource::error):
+        * Modules/fetch/FetchResponseSource.h:
+        * Modules/streams/ReadableStreamSource.h:
+        (WebCore::ReadableStreamSource::isPulling): Renamed from isStarting.
+        (WebCore::ReadableStreamSource::isStarting): Deleted.
+
</ins><span class="cx"> 2016-10-06  Adam Bergkvist  &lt;adam.bergkvist@ericsson.com&gt;
</span><span class="cx"> 
</span><span class="cx">         WebRTC: Add support for the icecandidate event in MediaEndpointPeerConnection
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesfetchFetchResponsecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp (206856 => 206857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp        2016-10-06 08:54:49 UTC (rev 206856)
+++ trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp        2016-10-06 09:43:52 UTC (rev 206857)
</span><span class="lines">@@ -128,12 +128,8 @@
</span><span class="cx">     m_response.m_body-&gt;loadingSucceeded();
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(READABLE_STREAM_API)
</span><del>-    if (m_response.m_readableStreamSource &amp;&amp; !m_response.body().consumer().hasData()) {
-        // We only close the stream if FetchBody already enqueued all data.
-        // Otherwise, FetchBody will close the stream after enqueuing the data.
-        m_response.m_readableStreamSource-&gt;close();
-        m_response.m_readableStreamSource = nullptr;
-    }
</del><ins>+    if (m_response.m_readableStreamSource &amp;&amp; !m_response.body().consumer().hasData())
+        m_response.closeStream();
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     if (m_loader-&gt;isStarted())
</span><span class="lines">@@ -182,9 +178,22 @@
</span><span class="cx"> {
</span><span class="cx"> #if ENABLE(READABLE_STREAM_API)
</span><span class="cx">     ASSERT(m_response.m_readableStreamSource);
</span><ins>+    auto&amp; source = *m_response.m_readableStreamSource;
</ins><span class="cx"> 
</span><del>-    if (!m_response.m_readableStreamSource-&gt;enqueue(ArrayBuffer::tryCreate(data, size)))
</del><ins>+    if (!source.isPulling()) {
+        m_response.body().consumer().append(data, size);
+        return;
+    }
+
+    if (m_response.body().consumer().hasData() &amp;&amp; !source.enqueue(m_response.body().consumer().takeAsArrayBuffer())) {
</ins><span class="cx">         stop();
</span><ins>+        return;
+    }
+    if (!source.enqueue(ArrayBuffer::tryCreate(data, size))) {
+        stop();
+        return;
+    }
+    source.resolvePullPromise();
</ins><span class="cx"> #else
</span><span class="cx">     UNUSED_PARAM(data);
</span><span class="cx">     UNUSED_PARAM(size);
</span><span class="lines">@@ -257,7 +266,7 @@
</span><span class="cx">     m_isDisturbed = true;
</span><span class="cx">     if (!isLoading()) {
</span><span class="cx">         body().consumeAsStream(*this, *m_readableStreamSource);
</span><del>-        if (!m_readableStreamSource-&gt;isStarting())
</del><ins>+        if (!m_readableStreamSource-&gt;isPulling())
</ins><span class="cx">             m_readableStreamSource = nullptr;
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="lines">@@ -266,12 +275,41 @@
</span><span class="cx"> 
</span><span class="cx">     RefPtr&lt;SharedBuffer&gt; data = m_bodyLoader-&gt;startStreaming();
</span><span class="cx">     if (data) {
</span><del>-        // FIXME: We might want to enqueue each internal SharedBuffer chunk as an individual ArrayBuffer.
-        if (!m_readableStreamSource-&gt;enqueue(data-&gt;createArrayBuffer()))
</del><ins>+        if (!m_readableStreamSource-&gt;enqueue(data-&gt;createArrayBuffer())) {
</ins><span class="cx">             stop();
</span><ins>+            return;
+        }
+        m_readableStreamSource-&gt;resolvePullPromise();
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void FetchResponse::closeStream()
+{
+    ASSERT(m_readableStreamSource);
+    m_readableStreamSource-&gt;close();
+    m_readableStreamSource = nullptr;
+}
+
+void FetchResponse::feedStream()
+{
+    ASSERT(m_readableStreamSource);
+    bool shouldCloseStream = !m_bodyLoader;
+
+    if (body().consumer().hasData()) {
+        if (!m_readableStreamSource-&gt;enqueue(body().consumer().takeAsArrayBuffer())) {
+            stop();
+            return;
+        }
+        if (!shouldCloseStream) {
+            m_readableStreamSource-&gt;resolvePullPromise();
+            return;
+        }
+    } else if (!shouldCloseStream)
+        return;
+
+    closeStream();
+}
+
</ins><span class="cx"> ReadableStreamSource* FetchResponse::createReadableStreamSource()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!m_readableStreamSource);
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesfetchFetchResponseh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/fetch/FetchResponse.h (206856 => 206857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/fetch/FetchResponse.h        2016-10-06 08:54:49 UTC (rev 206856)
+++ trunk/Source/WebCore/Modules/fetch/FetchResponse.h        2016-10-06 09:43:52 UTC (rev 206857)
</span><span class="lines">@@ -82,6 +82,7 @@
</span><span class="cx"> #if ENABLE(READABLE_STREAM_API)
</span><span class="cx">     ReadableStreamSource* createReadableStreamSource();
</span><span class="cx">     void consumeBodyAsStream();
</span><ins>+    void feedStream();
</ins><span class="cx">     void cancel();
</span><span class="cx"> #endif
</span><span class="cx">     bool isLoading() const { return !!m_bodyLoader; }
</span><span class="lines">@@ -96,6 +97,10 @@
</span><span class="cx">     const char* activeDOMObjectName() const final;
</span><span class="cx">     bool canSuspendForDocumentSuspension() const final;
</span><span class="cx"> 
</span><ins>+#if ENABLE(READABLE_STREAM_API)
+    void closeStream();
+#endif
+
</ins><span class="cx">     class BodyLoader final : public FetchLoaderClient {
</span><span class="cx">     public:
</span><span class="cx">         BodyLoader(FetchResponse&amp;, FetchPromise&amp;&amp;);
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesfetchFetchResponseSourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/fetch/FetchResponseSource.cpp (206856 => 206857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/fetch/FetchResponseSource.cpp        2016-10-06 08:54:49 UTC (rev 206856)
+++ trunk/Source/WebCore/Modules/fetch/FetchResponseSource.cpp        2016-10-06 09:43:52 UTC (rev 206857)
</span><span class="lines">@@ -62,7 +62,7 @@
</span><span class="cx"> 
</span><span class="cx"> void FetchResponseSource::doPull()
</span><span class="cx"> {
</span><del>-    ASSERT_NOT_REACHED();
</del><ins>+    m_response.feedStream();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void FetchResponseSource::doCancel()
</span><span class="lines">@@ -73,13 +73,11 @@
</span><span class="cx"> 
</span><span class="cx"> void FetchResponseSource::close()
</span><span class="cx"> {
</span><del>-    ASSERT(isStarting());
</del><span class="cx">     controller().close();
</span><span class="cx">     clean();
</span><span class="cx"> }
</span><span class="cx"> void FetchResponseSource::error(const String&amp; value)
</span><span class="cx"> {
</span><del>-    ASSERT(isStarting());
</del><span class="cx">     controller().error(value);
</span><span class="cx">     clean();
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesfetchFetchResponseSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/fetch/FetchResponseSource.h (206856 => 206857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/fetch/FetchResponseSource.h        2016-10-06 08:54:49 UTC (rev 206856)
+++ trunk/Source/WebCore/Modules/fetch/FetchResponseSource.h        2016-10-06 09:43:52 UTC (rev 206857)
</span><span class="lines">@@ -51,6 +51,8 @@
</span><span class="cx">     bool isCancelling() const { return m_isCancelling; }
</span><span class="cx">     bool isReadableStreamLocked() const;
</span><span class="cx"> 
</span><ins>+    void resolvePullPromise() { pullFinished(); }
+
</ins><span class="cx"> private:
</span><span class="cx">     void doStart() final;
</span><span class="cx">     void doPull() final;
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesstreamsReadableStreamSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/streams/ReadableStreamSource.h (206856 => 206857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/streams/ReadableStreamSource.h        2016-10-06 08:54:49 UTC (rev 206856)
+++ trunk/Source/WebCore/Modules/streams/ReadableStreamSource.h        2016-10-06 09:43:52 UTC (rev 206857)
</span><span class="lines">@@ -48,7 +48,7 @@
</span><span class="cx">     void pull(Promise&amp;&amp;);
</span><span class="cx">     void cancel(JSC::JSValue);
</span><span class="cx"> 
</span><del>-    bool isStarting() const { return !!m_promise; }
</del><ins>+    bool isPulling() const { return !!m_promise; }
</ins><span class="cx"> 
</span><span class="cx"> protected:
</span><span class="cx">     ReadableStreamDefaultController&amp; controller() { return m_controller.value(); }
</span></span></pre>
</div>
</div>

</body>
</html>