<!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>[192765] trunk</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/192765">192765</a></dd>
<dt>Author</dt> <dd>calvaris@igalia.com</dd>
<dt>Date</dt> <dd>2015-11-24 10:47:19 -0800 (Tue, 24 Nov 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>[Streams API] Implement pipeTo method in readable Stream
https://bugs.webkit.org/show_bug.cgi?id=151588
Reviewed by Darin Adler.
Source/WebCore:
Implemented pipeTo method according to the reference implementation in the spec as the spec is not written
yet. It can be found at https://github.com/whatwg/streams/blob/632b26a05f3106650b1ec91239ad5b012e6c64af/reference-implementation/lib/readable-stream.js#L75.
Tests: streams/pipe-to.html
streams/reference-implementation/brand-checks.html
streams/reference-implementation/pipe-through.html
streams/reference-implementation/pipe-to.html
streams/reference-implementation/pipe-to-options.html
streams/reference-implementation/readable-stream-templated
* Modules/streams/ReadableStream.js:
(doPipe): Internal function of pipeTo.
(closeDest): Internal function of pipeTo.
(abortDest): Internal function of pipeTo.
(pipeTo): Implemented as per spec with some other internal functions as helpers.
LayoutTests:
Test "Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the
source" was moved to its own file because it causes timing issues.
* streams/reference-implementation/pipe-to.html: Moved "Piping to a writable stream that does not consume the
writes fast enough exerts backpressure on the source" test to its own file.
* streams/pipe-to.html: Added with "Piping to a writable stream that does not consume the writes fast enough
exerts backpressure on the source" test.
* streams/reference-implementation/brand-checks.html: Fixed issue with the creation of a ReadableStreamReader.
* streams/pipe-to-expected.txt:
* streams/reference-implementation/brand-checks-expected.txt:
* streams/reference-implementation/pipe-through-expected.txt:
* streams/reference-implementation/pipe-to-expected.txt:
* streams/reference-implementation/pipe-to-options-expected.txt:
* streams/reference-implementation/readable-stream-templated-expected.txt: Expectations.
* platform/mac/TestExpectations:
* platform/win/TestExpectations: Flagged pipe-to test because of webkit.org/b/147933.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsplatformmacTestExpectations">trunk/LayoutTests/platform/mac/TestExpectations</a></li>
<li><a href="#trunkLayoutTestsplatformwinTestExpectations">trunk/LayoutTests/platform/win/TestExpectations</a></li>
<li><a href="#trunkLayoutTestsstreamsreferenceimplementationbrandchecksexpectedtxt">trunk/LayoutTests/streams/reference-implementation/brand-checks-expected.txt</a></li>
<li><a href="#trunkLayoutTestsstreamsreferenceimplementationbrandcheckshtml">trunk/LayoutTests/streams/reference-implementation/brand-checks.html</a></li>
<li><a href="#trunkLayoutTestsstreamsreferenceimplementationpipethroughexpectedtxt">trunk/LayoutTests/streams/reference-implementation/pipe-through-expected.txt</a></li>
<li><a href="#trunkLayoutTestsstreamsreferenceimplementationpipetoexpectedtxt">trunk/LayoutTests/streams/reference-implementation/pipe-to-expected.txt</a></li>
<li><a href="#trunkLayoutTestsstreamsreferenceimplementationpipetooptionsexpectedtxt">trunk/LayoutTests/streams/reference-implementation/pipe-to-options-expected.txt</a></li>
<li><a href="#trunkLayoutTestsstreamsreferenceimplementationpipetohtml">trunk/LayoutTests/streams/reference-implementation/pipe-to.html</a></li>
<li><a href="#trunkLayoutTestsstreamsreferenceimplementationreadablestreamtemplatedexpectedtxt">trunk/LayoutTests/streams/reference-implementation/readable-stream-templated-expected.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesstreamsReadableStreamjs">trunk/Source/WebCore/Modules/streams/ReadableStream.js</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsstreamspipetoexpectedtxt">trunk/LayoutTests/streams/pipe-to-expected.txt</a></li>
<li><a href="#trunkLayoutTestsstreamspipetohtml">trunk/LayoutTests/streams/pipe-to.html</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/LayoutTests/ChangeLog        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -1,3 +1,27 @@
</span><ins>+2015-11-24 Xabier Rodriguez Calvar <calvaris@igalia.com>
+
+ [Streams API] Implement pipeTo method in readable Stream
+ https://bugs.webkit.org/show_bug.cgi?id=151588
+
+ Reviewed by Darin Adler.
+
+ Test "Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the
+ source" was moved to its own file because it causes timing issues.
+
+ * streams/reference-implementation/pipe-to.html: Moved "Piping to a writable stream that does not consume the
+ writes fast enough exerts backpressure on the source" test to its own file.
+ * streams/pipe-to.html: Added with "Piping to a writable stream that does not consume the writes fast enough
+ exerts backpressure on the source" test.
+ * streams/reference-implementation/brand-checks.html: Fixed issue with the creation of a ReadableStreamReader.
+ * streams/pipe-to-expected.txt:
+ * streams/reference-implementation/brand-checks-expected.txt:
+ * streams/reference-implementation/pipe-through-expected.txt:
+ * streams/reference-implementation/pipe-to-expected.txt:
+ * streams/reference-implementation/pipe-to-options-expected.txt:
+ * streams/reference-implementation/readable-stream-templated-expected.txt: Expectations.
+ * platform/mac/TestExpectations:
+ * platform/win/TestExpectations: Flagged pipe-to test because of webkit.org/b/147933.
+
</ins><span class="cx"> 2015-11-24 Antti Koivisto <antti@apple.com>
</span><span class="cx">
</span><span class="cx"> REGRESSION (r190983): Non-element, non-text nodes should not be distributed to slots
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmacTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac/TestExpectations (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/TestExpectations        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/LayoutTests/platform/mac/TestExpectations        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -1380,6 +1380,7 @@
</span><span class="cx">
</span><span class="cx"> # Promises callbacks issues create problems in the writable stream tests
</span><span class="cx"> webkit.org/b/147933 streams/reference-implementation/count-queuing-strategy.html [ Pass Failure ]
</span><ins>+webkit.org/b/147933 streams/reference-implementation/pipe-to.html [ Pass Failure ]
</ins><span class="cx"> webkit.org/b/147933 streams/reference-implementation/writable-stream-abort.html [ Pass Failure ]
</span><span class="cx">
</span><span class="cx"> webkit.org/b/150806 imported/w3c/web-platform-tests/XMLHttpRequest/send-timeout-events.htm [ Pass Failure ]
</span></span></pre></div>
<a id="trunkLayoutTestsplatformwinTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/win/TestExpectations (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/win/TestExpectations        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/LayoutTests/platform/win/TestExpectations        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -3297,8 +3297,9 @@
</span><span class="cx">
</span><span class="cx"> webkit.org/b/150808 fast/text/multiple-feature-properties.html [ ImageOnlyFailure ]
</span><span class="cx">
</span><ins>+webkit.org/b/147933 streams/reference-implementation/count-queuing-strategy.html [ Pass Failure ]
+webkit.org/b/147933 streams/reference-implementation/pipe-to.html [ Pass Failure ]
</ins><span class="cx"> webkit.org/b/147933 streams/reference-implementation/writable-stream-abort.html [ Pass Failure ]
</span><del>-webkit.org/b/147933 streams/reference-implementation/count-queuing-strategy.html [ Pass Failure ]
</del><span class="cx">
</span><span class="cx"> webkit.org/b/150946 [ Debug ] scrollbars/custom-scrollbar-appearance-property.html [ Crash ]
</span><span class="cx">
</span></span></pre></div>
<a id="trunkLayoutTestsstreamspipetoexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/streams/pipe-to-expected.txt (0 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/streams/pipe-to-expected.txt         (rev 0)
+++ trunk/LayoutTests/streams/pipe-to-expected.txt        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -0,0 +1,3 @@
</span><ins>+
+PASS Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the source
+
</ins></span></pre></div>
<a id="trunkLayoutTestsstreamspipetohtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/streams/pipe-to.html (0 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/streams/pipe-to.html         (rev 0)
+++ trunk/LayoutTests/streams/pipe-to.html        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -0,0 +1,103 @@
</span><ins>+<!DOCTYPE html>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<script src='reference-implementation/resources/streams-utils.js'></script>
+<script>
+// This is updated till https://github.com/whatwg/streams/commit/ec5ffa036308d9f6350d2946560d48cdbf090939
+
+// This test is alone here for timing reasons though it should be at streams/reference-implementation/pipe-to.html.
+var test24 = async_test('Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the source');
+test24.step(function() {
+ const timeoutMultiplier = 5;
+ var desiredSizes = [];
+ var rs = new ReadableStream({
+ start: function(c) {
+ setTimeout(test24.step_func(function() { enqueue('a'); }), 100 * timeoutMultiplier);
+ setTimeout(test24.step_func(function() { enqueue('b'); }), 200 * timeoutMultiplier);
+ setTimeout(test24.step_func(function() { enqueue('c'); }), 300 * timeoutMultiplier);
+ setTimeout(test24.step_func(function() { enqueue('d'); }), 400 * timeoutMultiplier);
+ setTimeout(test24.step_func(function() { c.close(); }), 500 * timeoutMultiplier);
+
+ function enqueue(chunk) {
+ c.enqueue(chunk);
+ desiredSizes.push(c.desiredSize);
+ }
+ }
+ });
+
+ var chunksGivenToWrite = [];
+ var chunksFinishedWriting = [];
+ var startPromise = Promise.resolve();
+ var ws = new WritableStream({
+ start: function() {
+ return startPromise;
+ },
+ write: function(chunk) {
+ chunksGivenToWrite.push(chunk);
+ return new Promise(test24.step_func(function(resolve) {
+ setTimeout(test24.step_func(function() {
+ chunksFinishedWriting.push(chunk);
+ resolve();
+ }), 350 * timeoutMultiplier);
+ }));
+ }
+ });
+
+ startPromise.then(test24.step_func(function() {
+ rs.pipeTo(ws).then(test24.step_func(function() {
+ assert_array_equals(desiredSizes, [1, 1, 0, -1], 'backpressure was correctly exerted at the source');
+ assert_array_equals(chunksFinishedWriting, ['a', 'b', 'c', 'd'], 'all chunks were written');
+ test24.done();
+ }));
+
+ assert_equals(ws.state, 'writable', 'at t = 0 ms, ws should be writable');
+
+ setTimeout(test24.step_func(function() {
+ assert_equals(ws.state, 'waiting', 'at t = 125 ms, ws should be waiting');
+ assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 125 ms, ws.write should have been called with one chunk');
+ assert_array_equals(chunksFinishedWriting, [], 'at t = 125 ms, no chunks should have finished writing');
+
+ // When 'a' (the very first chunk) was enqueued, it was immediately used to fulfill the outstanding read request
+ // promise, leaving room in the queue
+ assert_array_equals(desiredSizes, [1], 'at t = 125 ms, the one enqueued chunk in rs did not cause backpressure');
+ }), 125 * timeoutMultiplier);
+
+ setTimeout(test24.step_func(function() {
+ assert_equals(ws.state, 'waiting', 'at t = 225 ms, ws should be waiting');
+ assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 225 ms, ws.write should have been called with one chunk');
+ assert_array_equals(chunksFinishedWriting, [], 'at t = 225 ms, no chunks should have finished writing');
+
+ // When 'b' was enqueued at 200 ms, the queue was also empty, since immediately after enqueuing 'a' at
+ // t = 100 ms, it was dequeued in order to fulfill the read() call that was made at time t = 0.
+ assert_array_equals(desiredSizes, [1, 1], 'at t = 225 ms, the two enqueued chunks in rs did not cause backpressure');
+ }), 225 * timeoutMultiplier);
+
+ setTimeout(test24.step_func(function() {
+ assert_equals(ws.state, 'waiting', 'at t = 325 ms, ws should be waiting');
+ assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 325 ms, ws.write should have been called with one chunk');
+ assert_array_equals(chunksFinishedWriting, [], 'at t = 325 ms, no chunks should have finished writing');
+
+ // When 'c' was enqueued at 300 ms, the queue was again empty, since at time t = 200 ms when 'b' was enqueued,
+ // it was immediately dequeued in order to fulfill the second read() call that was made at time t = 0.
+ // However, this time there was no pending read request to whisk it away, so after the enqueue desired size is 0.
+ assert_array_equals(desiredSizes, [1, 1, 0], 'at t = 325 ms, the three enqueued chunks in rs did not cause backpressure');
+ }), 325 * timeoutMultiplier);
+
+ setTimeout(test24.step_func(function() {
+ assert_equals(ws.state, 'waiting', 'at t = 425 ms, ws should be waiting');
+ assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 425 ms, ws.write should have been called with one chunk');
+ assert_array_equals(chunksFinishedWriting, [], 'at t = 425 ms, no chunks should have finished writing');
+
+ // When 'd' was enqueued at 400 ms, the queue was *not* empty. 'c' was still in it, since the write() of 'b' will
+ // not finish until t = 100 ms + 350 ms = 450 ms. Thus backpressure should have been exerted.
+ assert_array_equals(desiredSizes, [1, 1, 0, -1], 'at t = 425 ms, the fourth enqueued chunks in rs did cause backpressure');
+ }), 425 * timeoutMultiplier);
+
+ setTimeout(test24.step_func(function() {
+ assert_equals(ws.state, 'waiting', 'at t = 475 ms, ws should be waiting');
+ assert_array_equals(chunksGivenToWrite, ['a', 'b'], 'at t = 475 ms, ws.write should have been called with two chunks');
+ assert_array_equals(chunksFinishedWriting, ['a'], 'at t = 475 ms, one chunk should have finished writing');
+ }), 475 * timeoutMultiplier);
+ }));
+});
+</script>
</ins></span></pre></div>
<a id="trunkLayoutTestsstreamsreferenceimplementationbrandchecksexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/streams/reference-implementation/brand-checks-expected.txt (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/streams/reference-implementation/brand-checks-expected.txt        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/LayoutTests/streams/reference-implementation/brand-checks-expected.txt        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -4,7 +4,7 @@
</span><span class="cx"> PASS ReadableStream.prototype.cancel enforces a brand check
</span><span class="cx"> PASS ReadableStream.prototype.getReader enforces a brand check
</span><span class="cx"> PASS ReadableStream.prototype.pipeThrough works generically on its this and its arguments
</span><del>-FAIL ReadableStream.prototype.pipeTo works generically on its this and its arguments pipeTo is not implemented
</del><ins>+PASS ReadableStream.prototype.pipeTo works generically on its this and its arguments
</ins><span class="cx"> PASS ReadableStream.prototype.tee enforces a brand check
</span><span class="cx"> PASS ReadableStreamReader enforces a brand check on its argument
</span><span class="cx"> PASS ReadableStreamReader.prototype.closed enforces a brand check
</span></span></pre></div>
<a id="trunkLayoutTestsstreamsreferenceimplementationbrandcheckshtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/streams/reference-implementation/brand-checks.html (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/streams/reference-implementation/brand-checks.html        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/LayoutTests/streams/reference-implementation/brand-checks.html        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -25,7 +25,7 @@
</span><span class="cx"> function fakeReadableStream() {
</span><span class="cx"> return {
</span><span class="cx"> cancel: function(reason) { return Promise.resolve(); },
</span><del>- getReader: function() { return new ReadableStream(new ReadableStream()); },
</del><ins>+ getReader: function() { return new ReadableStreamReader(new ReadableStream()); },
</ins><span class="cx"> pipeThrough: function(obj, options) { return obj.readable; },
</span><span class="cx"> pipeTo: function() { return Promise.resolve(); },
</span><span class="cx"> tee: function() { return [realReadableStream(), realReadableStream()]; }
</span></span></pre></div>
<a id="trunkLayoutTestsstreamsreferenceimplementationpipethroughexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/streams/reference-implementation/pipe-through-expected.txt (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/streams/reference-implementation/pipe-through-expected.txt        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/LayoutTests/streams/reference-implementation/pipe-through-expected.txt        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><span class="cx">
</span><del>-FAIL Piping through a duck-typed pass-through transform stream works pipeTo is not implemented
</del><ins>+PASS Piping through a duck-typed pass-through transform stream works
</ins><span class="cx"> FAIL Piping through an identity transform stream will close the destination when the source closes Can't find variable: TransformStream
</span><span class="cx">
</span></span></pre></div>
<a id="trunkLayoutTestsstreamsreferenceimplementationpipetoexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/streams/reference-implementation/pipe-to-expected.txt (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/streams/reference-implementation/pipe-to-expected.txt        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/LayoutTests/streams/reference-implementation/pipe-to-expected.txt        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -1,26 +1,25 @@
</span><span class="cx">
</span><del>-FAIL Piping from a ReadableStream from which lots of data are readable synchronously pipeTo is not implemented
-FAIL Piping from a ReadableStream in readable state to a WritableStream in closing state pipeTo is not implemented
-FAIL Piping from a ReadableStream in readable state to a WritableStream in errored state pipeTo is not implemented
-FAIL Piping from a ReadableStream in the readable state which becomes closed after pipeTo call to a WritableStream in the writable state pipeTo is not implemented
-FAIL Piping from a ReadableStream in the readable state which becomes errored after pipeTo call to a WritableStream in the writable state pipeTo is not implemented
-FAIL Piping from an empty ReadableStream which becomes non-empty after pipeTo call to a WritableStream in the writable state pipeTo is not implemented
-FAIL Piping from an empty ReadableStream which becomes errored after pipeTo call to a WritableStream in the writable state pipeTo is not implemented
-FAIL Piping from an empty ReadableStream to a WritableStream in the writable state which becomes errored after a pipeTo call pipeTo is not implemented
-FAIL Piping from a non-empty ReadableStream to a WritableStream in the waiting state which becomes writable after a pipeTo call pipeTo is not implemented
-FAIL Piping from a non-empty ReadableStream to a WritableStream in waiting state which becomes errored after a pipeTo call pipeTo is not implemented
-FAIL Piping from a non-empty ReadableStream which becomes errored after pipeTo call to a WritableStream in the waiting state pipeTo is not implemented
-FAIL Piping from a non-empty ReadableStream to a WritableStream in the waiting state where both become ready after a pipeTo pipeTo is not implemented
-FAIL Piping from an empty ReadableStream to a WritableStream in the waiting state which becomes writable after a pipeTo call pipeTo is not implemented
-FAIL Piping from an empty ReadableStream which becomes closed after a pipeTo call to a WritableStream in the waiting state whose writes never complete pipeTo is not implemented
-FAIL Piping from an empty ReadableStream which becomes errored after a pipeTo call to a WritableStream in the waiting state pipeTo is not implemented
-FAIL Piping to a duck-typed asynchronous "writable stream" works pipeTo is not implemented
-FAIL Piping to a stream that has been aborted passes through the error as the cancellation reason pipeTo is not implemented
-FAIL Piping to a stream and then aborting it passes through the error as the cancellation reason pipeTo is not implemented
-FAIL Piping to a stream that has been closed propagates a TypeError cancellation reason backward pipeTo is not implemented
-FAIL Piping to a stream and then closing it propagates a TypeError cancellation reason backward pipeTo is not implemented
-FAIL Piping to a stream that errors on write should pass through the error as the cancellation reason pipeTo is not implemented
-FAIL Piping to a stream that errors on write should not pass through the error if the stream is already closed pipeTo is not implemented
-FAIL Piping to a stream that errors soon after writing should pass through the error as the cancellation reason pipeTo is not implemented
-FAIL Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the source pipeTo is not implemented
</del><ins>+PASS Piping from a ReadableStream from which lots of data are readable synchronously
+PASS Piping from a ReadableStream in readable state to a WritableStream in closing state
+PASS Piping from a ReadableStream in readable state to a WritableStream in errored state
+PASS Piping from a ReadableStream in the readable state which becomes closed after pipeTo call to a WritableStream in the writable state
+PASS Piping from a ReadableStream in the readable state which becomes errored after pipeTo call to a WritableStream in the writable state
+PASS Piping from an empty ReadableStream which becomes non-empty after pipeTo call to a WritableStream in the writable state
+PASS Piping from an empty ReadableStream which becomes errored after pipeTo call to a WritableStream in the writable state
+PASS Piping from an empty ReadableStream to a WritableStream in the writable state which becomes errored after a pipeTo call
+PASS Piping from a non-empty ReadableStream to a WritableStream in the waiting state which becomes writable after a pipeTo call
+PASS Piping from a non-empty ReadableStream to a WritableStream in waiting state which becomes errored after a pipeTo call
+PASS Piping from a non-empty ReadableStream which becomes errored after pipeTo call to a WritableStream in the waiting state
+PASS Piping from a non-empty ReadableStream to a WritableStream in the waiting state where both become ready after a pipeTo
+PASS Piping from an empty ReadableStream to a WritableStream in the waiting state which becomes writable after a pipeTo call
+PASS Piping from an empty ReadableStream which becomes closed after a pipeTo call to a WritableStream in the waiting state whose writes never complete
+PASS Piping from an empty ReadableStream which becomes errored after a pipeTo call to a WritableStream in the waiting state
+PASS Piping to a duck-typed asynchronous "writable stream" works
+PASS Piping to a stream that has been aborted passes through the error as the cancellation reason
+PASS Piping to a stream and then aborting it passes through the error as the cancellation reason
+PASS Piping to a stream that has been closed propagates a TypeError cancellation reason backward
+PASS Piping to a stream and then closing it propagates a TypeError cancellation reason backward
+PASS Piping to a stream that errors on write should pass through the error as the cancellation reason
+PASS Piping to a stream that errors on write should not pass through the error if the stream is already closed
+PASS Piping to a stream that errors soon after writing should pass through the error as the cancellation reason
</ins><span class="cx">
</span></span></pre></div>
<a id="trunkLayoutTestsstreamsreferenceimplementationpipetooptionsexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/streams/reference-implementation/pipe-to-options-expected.txt (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/streams/reference-implementation/pipe-to-options-expected.txt        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/LayoutTests/streams/reference-implementation/pipe-to-options-expected.txt        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx">
</span><del>-FAIL Piping with no options and a destination error pipeTo is not implemented
-FAIL Piping with { preventCancel: false } and a destination error pipeTo is not implemented
-FAIL Piping with { preventCancel: true } and a destination error pipeTo is not implemented
</del><ins>+PASS Piping with no options and a destination error
+PASS Piping with { preventCancel: false } and a destination error
+PASS Piping with { preventCancel: true } and a destination error
</ins><span class="cx">
</span></span></pre></div>
<a id="trunkLayoutTestsstreamsreferenceimplementationpipetohtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/streams/reference-implementation/pipe-to.html (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/streams/reference-implementation/pipe-to.html        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/LayoutTests/streams/reference-implementation/pipe-to.html        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -960,97 +960,6 @@
</span><span class="cx"> rs.pipeTo(ws);
</span><span class="cx"> });
</span><span class="cx">
</span><del>-var test24 = async_test('Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the source');
-test24.step(function() {
- var desiredSizes = [];
- var rs = new ReadableStream({
- start: function(c) {
- setTimeout(test24.step_func(function() { enqueue('a'); }), 200);
- setTimeout(test24.step_func(function() { enqueue('b'); }), 400);
- setTimeout(test24.step_func(function() { enqueue('c'); }), 600);
- setTimeout(test24.step_func(function() { enqueue('d'); }), 800);
- setTimeout(test24.step_func(function() { c.close(); }), 1000);
-
- function enqueue(chunk) {
- c.enqueue(chunk);
- desiredSizes.push(c.desiredSize);
- }
- }
- });
-
- var chunksGivenToWrite = [];
- var chunksFinishedWriting = [];
- var startPromise = Promise.resolve();
- var ws = new WritableStream({
- start: function() {
- return startPromise;
- },
- write: function(chunk) {
- chunksGivenToWrite.push(chunk);
- return new Promise(test24.step_func(function(resolve) {
- setTimeout(test24.step_func(function() {
- chunksFinishedWriting.push(chunk);
- resolve();
- }), 700);
- }));
- }
- });
-
- startPromise.then(test24.step_func(function() {
- rs.pipeTo(ws).then(test24.step_func(function() {
- assert_array_equals(desiredSizes, [1, 1, 0, -1], 'backpressure was correctly exerted at the source');
- assert_array_equals(chunksFinishedWriting, ['a', 'b', 'c', 'd'], 'all chunks were written');
- test24.done();
- }));
-
- assert_equals(ws.state, 'writable', 'at t = 0 ms, ws should be writable');
-
- setTimeout(test24.step_func(function() {
- assert_equals(ws.state, 'waiting', 'at t = 250 ms, ws should be waiting');
- assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 250 ms, ws.write should have been called with one chunk');
- assert_array_equals(chunksFinishedWriting, [], 'at t = 250 ms, no chunks should have finished writing');
-
- // When 'a' (the very first chunk) was enqueued, it was immediately used to fulfill the outstanding read request
- // promise, leaving room in the queue
- assert_array_equals(desiredSizes, [1], 'at t = 250 ms, the one enqueued chunk in rs did not cause backpressure');
- }), 250);
-
- setTimeout(test24.step_func(function() {
- assert_equals(ws.state, 'waiting', 'at t = 450 ms, ws should be waiting');
- assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 450 ms, ws.write should have been called with one chunk');
- assert_array_equals(chunksFinishedWriting, [], 'at t = 450 ms, no chunks should have finished writing');
-
- // When 'b' was enqueued at 200 ms, the queue was also empty, since immediately after enqueuing 'a' at
- // t = 100 ms, it was dequeued in order to fulfill the read() call that was made at time t = 0.
- assert_array_equals(desiredSizes, [1, 1], 'at t = 450 ms, the two enqueued chunks in rs did not cause backpressure');
- }), 450);
-
- setTimeout(test24.step_func(function() {
- assert_equals(ws.state, 'waiting', 'at t = 650 ms, ws should be waiting');
- assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 650 ms, ws.write should have been called with one chunk');
- assert_array_equals(chunksFinishedWriting, [], 'at t = 650 ms, no chunks should have finished writing');
-
- // When 'c' was enqueued at 300 ms, the queue was again empty, since at time t = 200 ms when 'b' was enqueued,
- // it was immediately dequeued in order to fulfill the second read() call that was made at time t = 0.
- // However, this time there was no pending read request to whisk it away, so after the enqueue desired size is 0.
- assert_array_equals(desiredSizes, [1, 1, 0], 'at t = 650 ms, the three enqueued chunks in rs did not cause backpressure');
- }), 650);
-
- setTimeout(test24.step_func(function() {
- assert_equals(ws.state, 'waiting', 'at t = 850 ms, ws should be waiting');
- assert_array_equals(chunksGivenToWrite, ['a'], 'at t = 850 ms, ws.write should have been called with one chunk');
- assert_array_equals(chunksFinishedWriting, [], 'at t = 850 ms, no chunks should have finished writing');
-
- // When 'd' was enqueued at 400 ms, the queue was *not* empty. 'c' was still in it, since the write() of 'b' will
- // not finish until t = 100 ms + 350 ms = 450 ms. Thus backpressure should have been exerted.
- assert_array_equals(desiredSizes, [1, 1, 0, -1], 'at t = 850 ms, the fourth enqueued chunks in rs did cause backpressure');
- }), 850);
-
- setTimeout(test24.step_func(function() {
- assert_equals(ws.state, 'waiting', 'at t = 950 ms, ws should be waiting');
- assert_array_equals(chunksGivenToWrite, ['a', 'b'], 'at t = 950 ms, ws.write should have been called with two chunks');
- assert_array_equals(chunksFinishedWriting, ['a'], 'at t = 950 ms, one chunk should have finished writing');
- }), 950);
- }));
-});
</del><ins>+// This test is alone under streams/pipe-to.html for timing reasons.
+// var test24 = async_test('Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the source');
</ins><span class="cx"> </script>
</span></span></pre></div>
<a id="trunkLayoutTestsstreamsreferenceimplementationreadablestreamtemplatedexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/streams/reference-implementation/readable-stream-templated-expected.txt (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/streams/reference-implementation/readable-stream-templated-expected.txt        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/LayoutTests/streams/reference-implementation/readable-stream-templated-expected.txt        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -18,8 +18,8 @@
</span><span class="cx"> PASS cancel() should return a distinct fulfilled promise each time
</span><span class="cx"> PASS locked should be false
</span><span class="cx"> PASS getReader() should be OK
</span><del>-FAIL piping to a WritableStream in the writable state should close the writable stream pipeTo is not implemented
-FAIL piping to a WritableStream in the writable state with { preventClose: true } should do nothing pipeTo is not implemented
</del><ins>+PASS piping to a WritableStream in the writable state should close the writable stream
+PASS piping to a WritableStream in the writable state with { preventClose: true } should do nothing
</ins><span class="cx"> PASS should be able to acquire multiple readers if they are released in succession
</span><span class="cx"> PASS should not be able to acquire a second reader if we don't release the first one
</span><span class="cx"> PASS Running templatedRSClosedReader with ReadableStream reader (closed before getting reader)
</span><span class="lines">@@ -40,8 +40,8 @@
</span><span class="cx"> PASS cancel() should return a distinct fulfilled promise each time
</span><span class="cx"> PASS locked should be false
</span><span class="cx"> PASS getReader() should be OK
</span><del>-FAIL piping to a WritableStream in the writable state should close the writable stream pipeTo is not implemented
-FAIL piping to a WritableStream in the writable state with { preventClose: true } should do nothing pipeTo is not implemented
</del><ins>+PASS piping to a WritableStream in the writable state should close the writable stream
+PASS piping to a WritableStream in the writable state with { preventClose: true } should do nothing
</ins><span class="cx"> PASS should be able to acquire multiple readers if they are released in succession
</span><span class="cx"> PASS should not be able to acquire a second reader if we don't release the first one
</span><span class="cx"> PASS Running templatedRSClosedReader with ReadableStream reader (closed via cancel after getting reader)
</span><span class="lines">@@ -52,7 +52,7 @@
</span><span class="cx"> PASS releasing the lock should cause closed to reject and change identity
</span><span class="cx"> PASS cancel() should return a distinct fulfilled promise each time
</span><span class="cx"> PASS Running templatedRSErrored with ReadableStream (errored via call in start)
</span><del>-FAIL piping to a WritableStream in the writable state should abort the writable stream pipeTo is not implemented
</del><ins>+PASS piping to a WritableStream in the writable state should abort the writable stream
</ins><span class="cx"> PASS getReader() should return a reader that acts errored
</span><span class="cx"> PASS read() twice should give the error each time
</span><span class="cx"> PASS locked should be false
</span><span class="lines">@@ -62,14 +62,14 @@
</span><span class="cx"> PASS cancel() should return a distinct rejected promise each time
</span><span class="cx"> PASS reader cancel() should return a distinct rejected promise each time
</span><span class="cx"> PASS Running templatedRSErrored with ReadableStream (errored via returning a rejected promise in start)
</span><del>-FAIL piping to a WritableStream in the writable state should abort the writable stream pipeTo is not implemented
</del><ins>+PASS piping to a WritableStream in the writable state should abort the writable stream
</ins><span class="cx"> PASS getReader() should return a reader that acts errored
</span><span class="cx"> PASS read() twice should give the error each time
</span><span class="cx"> PASS locked should be false
</span><span class="cx"> PASS Running templatedRSErroredAsyncOnly with ReadableStream (errored via returning a rejected promise in start) reader
</span><del>-FAIL piping with no options pipeTo is not implemented
-FAIL piping with { preventAbort: false } pipeTo is not implemented
-FAIL piping with { preventAbort: true } pipeTo is not implemented
</del><ins>+PASS piping with no options
+PASS piping with { preventAbort: false }
+PASS piping with { preventAbort: true }
</ins><span class="cx"> PASS Running templatedRSErroredReader with ReadableStream (errored via returning a rejected promise in start) reader
</span><span class="cx"> PASS closed should reject with the error
</span><span class="cx"> PASS releasing the lock should cause closed to reject and change identity
</span><span class="lines">@@ -88,26 +88,26 @@
</span><span class="cx"> PASS read() should return distinct promises each time
</span><span class="cx"> PASS cancel() after a read() should still give that single read result
</span><span class="cx"> PASS Running templatedRSTwoChunksClosed with ReadableStream (two chunks enqueued, then closed)
</span><del>-FAIL piping with no options and no destination errors pipeTo is not implemented
-FAIL piping with { preventClose: false } and no destination errors pipeTo is not implemented
-FAIL piping with { preventClose: true } and no destination errors pipeTo is not implemented
-FAIL piping with { preventClose: false } and a destination with that errors synchronously pipeTo is not implemented
-FAIL piping with { preventClose: true } and a destination with that errors synchronously pipeTo is not implemented
-FAIL piping with { preventClose: true } and a destination that errors on the last chunk pipeTo is not implemented
</del><ins>+PASS piping with no options and no destination errors
+PASS piping with { preventClose: false } and no destination errors
+PASS piping with { preventClose: true } and no destination errors
+PASS piping with { preventClose: false } and a destination with that errors synchronously
+PASS piping with { preventClose: true } and a destination with that errors synchronously
+PASS piping with { preventClose: true } and a destination that errors on the last chunk
</ins><span class="cx"> PASS Running templatedRSTwoChunksClosed with ReadableStream (two chunks enqueued async, then closed)
</span><del>-FAIL piping with no options and no destination errors pipeTo is not implemented
-FAIL piping with { preventClose: false } and no destination errors pipeTo is not implemented
-FAIL piping with { preventClose: true } and no destination errors pipeTo is not implemented
-FAIL piping with { preventClose: false } and a destination with that errors synchronously pipeTo is not implemented
-FAIL piping with { preventClose: true } and a destination with that errors synchronously pipeTo is not implemented
-FAIL piping with { preventClose: true } and a destination that errors on the last chunk pipeTo is not implemented
</del><ins>+PASS piping with no options and no destination errors
+PASS piping with { preventClose: false } and no destination errors
+PASS piping with { preventClose: true } and no destination errors
+PASS piping with { preventClose: false } and a destination with that errors synchronously
+PASS piping with { preventClose: true } and a destination with that errors synchronously
+PASS piping with { preventClose: true } and a destination that errors on the last chunk
</ins><span class="cx"> PASS Running templatedRSTwoChunksClosed with ReadableStream (two chunks enqueued via pull, then closed)
</span><del>-FAIL piping with no options and no destination errors pipeTo is not implemented
-FAIL piping with { preventClose: false } and no destination errors pipeTo is not implemented
-FAIL piping with { preventClose: true } and no destination errors pipeTo is not implemented
-FAIL piping with { preventClose: false } and a destination with that errors synchronously pipeTo is not implemented
-FAIL piping with { preventClose: true } and a destination with that errors synchronously pipeTo is not implemented
-FAIL piping with { preventClose: true } and a destination that errors on the last chunk pipeTo is not implemented
</del><ins>+PASS piping with no options and no destination errors
+PASS piping with { preventClose: false } and no destination errors
+PASS piping with { preventClose: true } and no destination errors
+PASS piping with { preventClose: false } and a destination with that errors synchronously
+PASS piping with { preventClose: true } and a destination with that errors synchronously
+PASS piping with { preventClose: true } and a destination that errors on the last chunk
</ins><span class="cx"> PASS Running templatedRSTwoChunksClosedReader with ReadableStream (two chunks enqueued, then closed) reader
</span><span class="cx"> PASS third read(), without waiting, should give { value: undefined, done: true }
</span><span class="cx"> PASS third read, with waiting, should give { value: undefined, done: true }
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/Source/WebCore/ChangeLog        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -1,3 +1,26 @@
</span><ins>+2015-11-24 Xabier Rodriguez Calvar <calvaris@igalia.com>
+
+ [Streams API] Implement pipeTo method in readable Stream
+ https://bugs.webkit.org/show_bug.cgi?id=151588
+
+ Reviewed by Darin Adler.
+
+ Implemented pipeTo method according to the reference implementation in the spec as the spec is not written
+ yet. It can be found at https://github.com/whatwg/streams/blob/632b26a05f3106650b1ec91239ad5b012e6c64af/reference-implementation/lib/readable-stream.js#L75.
+
+ Tests: streams/pipe-to.html
+ streams/reference-implementation/brand-checks.html
+ streams/reference-implementation/pipe-through.html
+ streams/reference-implementation/pipe-to.html
+ streams/reference-implementation/pipe-to-options.html
+ streams/reference-implementation/readable-stream-templated
+
+ * Modules/streams/ReadableStream.js:
+ (doPipe): Internal function of pipeTo.
+ (closeDest): Internal function of pipeTo.
+ (abortDest): Internal function of pipeTo.
+ (pipeTo): Implemented as per spec with some other internal functions as helpers.
+
</ins><span class="cx"> 2015-11-24 Antti Koivisto <antti@apple.com>
</span><span class="cx">
</span><span class="cx"> REGRESSION (r190983): Non-element, non-text nodes should not be distributed to slots
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesstreamsReadableStreamjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/streams/ReadableStream.js (192764 => 192765)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/streams/ReadableStream.js        2015-11-24 18:22:03 UTC (rev 192764)
+++ trunk/Source/WebCore/Modules/streams/ReadableStream.js        2015-11-24 18:47:19 UTC (rev 192765)
</span><span class="lines">@@ -99,11 +99,90 @@
</span><span class="cx"> return streams.readable;
</span><span class="cx"> }
</span><span class="cx">
</span><del>-function pipeTo(dest)
</del><ins>+function pipeTo(destination, options)
</ins><span class="cx"> {
</span><span class="cx"> "use strict";
</span><span class="cx">
</span><del>- throw new @TypeError("pipeTo is not implemented");
</del><ins>+ // We are not shielding against methods and attributes of the reader and destination as those objects don't have to
+ // be necessarily ReadableStreamReader and WritableStream.
+
+ const preventClose = @isObject(options) && !!options.preventClose;
+ const preventAbort = @isObject(options) && !!options.preventAbort;
+ const preventCancel = @isObject(options) && !!options.preventCancel;
+
+ const source = this;
+
+ let reader;
+ let lastRead;
+ let lastWrite;
+ let closedPurposefully = false;
+ let promiseCapability;
+
+ function doPipe() {
+ lastRead = reader.read();
+ @Promise.prototype.@then.@call(@Promise.all([lastRead, destination.ready]), function([{ value, done }]) {
+ if (done)
+ closeDestination();
+ else if (destination.state === "writable") {
+ lastWrite = destination.write(value);
+ doPipe();
+ }
+ }, function(e) {
+ throw e;
+ });
+ }
+
+ function cancelSource(reason) {
+ if (!preventCancel) {
+ reader.cancel(reason);
+ reader.releaseLock();
+ promiseCapability.@reject.@call(undefined, reason);
+ } else {
+ @Promise.prototype.@then.@call(lastRead, function() {
+ reader.releaseLock();
+ promiseCapability.@reject.@call(undefined, reason);
+ });
+ }
+ }
+
+ function closeDestination() {
+ reader.releaseLock();
+
+ const destinationState = destination.state;
+ if (!preventClose && (destinationState === "waiting" || destinationState === "writable")) {
+ closedPurposefully = true;
+ @Promise.prototype.@then.@call(destination.close(), promiseCapability.@resolve, promiseCapability.@reject);
+ } else if (lastWrite !== undefined)
+ @Promise.prototype.@then.@call(lastWrite, promiseCapability.@resolve, promiseCapability.@reject);
+ else
+ promiseCapability.@resolve.@call();
+
+ }
+
+ function abortDestination(reason) {
+ reader.releaseLock();
+
+ if (!preventAbort)
+ destination.abort(reason);
+ promiseCapability.@reject.@call(undefined, reason);
+ }
+
+ promiseCapability = @newPromiseCapability(@Promise);
+
+ reader = source.getReader();
+
+ @Promise.prototype.@catch.@call(reader.closed, abortDestination);
+ @Promise.prototype.@then.@call(destination.closed,
+ function() {
+ if (!closedPurposefully)
+ cancelSource(new @TypeError('destination is closing or closed and cannot be piped to anymore'));
+ },
+ cancelSource
+ );
+
+ doPipe();
+
+ return promiseCapability.@promise;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> function tee()
</span></span></pre>
</div>
</div>
</body>
</html>