<!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 &quot;Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the
source&quot; was moved to its own file because it causes timing issues.

* streams/reference-implementation/pipe-to.html: Moved &quot;Piping to a writable stream that does not consume the
writes fast enough exerts backpressure on the source&quot; test to its own file.
* streams/pipe-to.html: Added with &quot;Piping to a writable stream that does not consume the writes fast enough
exerts backpressure on the source&quot; 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  &lt;calvaris@igalia.com&gt;
+
+        [Streams API] Implement pipeTo method in readable Stream
+        https://bugs.webkit.org/show_bug.cgi?id=151588
+
+        Reviewed by Darin Adler.
+
+        Test &quot;Piping to a writable stream that does not consume the writes fast enough exerts backpressure on the
+        source&quot; was moved to its own file because it causes timing issues.
+
+        * streams/reference-implementation/pipe-to.html: Moved &quot;Piping to a writable stream that does not consume the
+        writes fast enough exerts backpressure on the source&quot; test to its own file.
+        * streams/pipe-to.html: Added with &quot;Piping to a writable stream that does not consume the writes fast enough
+        exerts backpressure on the source&quot; 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  &lt;antti@apple.com&gt;
</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>+&lt;!DOCTYPE html&gt;
+&lt;script src='../resources/testharness.js'&gt;&lt;/script&gt;
+&lt;script src='../resources/testharnessreport.js'&gt;&lt;/script&gt;
+&lt;script src='reference-implementation/resources/streams-utils.js'&gt;&lt;/script&gt;
+&lt;script&gt;
+// 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);
+    }));
+});
+&lt;/script&gt;
</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 &quot;writable stream&quot; 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 &quot;writable stream&quot; 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"> &lt;/script&gt;
</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  &lt;calvaris@igalia.com&gt;
+
+        [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  &lt;antti@apple.com&gt;
</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">     &quot;use strict&quot;;
</span><span class="cx"> 
</span><del>-    throw new @TypeError(&quot;pipeTo is not implemented&quot;);
</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) &amp;&amp; !!options.preventClose;
+    const preventAbort = @isObject(options) &amp;&amp; !!options.preventAbort;
+    const preventCancel = @isObject(options) &amp;&amp; !!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 === &quot;writable&quot;) {
+                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 &amp;&amp; (destinationState === &quot;waiting&quot; || destinationState === &quot;writable&quot;)) {
+            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>