Source/WebCore:
authoryouenn.fablet@crf.canon.fr <youenn.fablet@crf.canon.fr@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Jun 2015 19:28:11 +0000 (19:28 +0000)
committeryouenn.fablet@crf.canon.fr <youenn.fablet@crf.canon.fr@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Jun 2015 19:28:11 +0000 (19:28 +0000)
[Streams API] Implement ReadableStreamReader read method in closed and errored state
https://bugs.webkit.org/show_bug.cgi?id=144790

Reviewed by Darin Adler.

Reader delegates read() promise handling to its stream except if reader is no longer locking the stream and stream is readable.
Storing of reader read() promise callbacks as a Vector in ReadableStream.
Added resolution/rejection of read() promises in case of errored/closed streams.

Test: streams/readable-stream-reader-read.html

* ForwardingHeaders/runtime/IteratorOperations.h: Added.
* Modules/streams/ReadableStream.cpp:
(WebCore::ReadableStream::cleanCallbacks): Clean the read requests.
(WebCore::ReadableStream::changeStateToClosed): Run success callbacks with undefined for read requests.
(WebCore::ReadableStream::changeStateToErrored): Run failure callbacks with the errors for read requests.
(WebCore::ReadableStream::closed): Parameter name changed.
(WebCore::ReadableStream::read): Added. Succeeds with empty when closed, fails with error when errored, reads a
value if there is one and pushes the callbacks to the queue otherwise.
* Modules/streams/ReadableStream.h:
(WebCore::ReadableStream::ReadCallbacks::ReadCallbacks): Struct containing success and failure callbacks.
* Modules/streams/ReadableStreamReader.cpp:
(WebCore::ReadableStreamReader::closed): Parameter name changed.
(WebCore::ReadableStreamReader::read): Invoke success with empty if we streams if we don't have the right reader
and call the stream otherwise to read.
* Modules/streams/ReadableStreamReader.h:
* bindings/js/JSReadableStreamReaderCustom.cpp:
(WebCore::JSReadableStreamReader::read): Create the callback lambdas and invoke read. Failure rejects the
promise and success creates the result from the read value.
* bindings/js/ReadableJSStream.cpp:
(WebCore::ReadableJSStream::hasValue):
(WebCore::ReadableJSStream::read): Not implemented yet.
* bindings/js/ReadableJSStream.h:

Source/WTF:
[Streams API] Delegate ReadableStreamReader reference counting to ReadableStream
https://bugs.webkit.org/show_bug.cgi?id=144907

Reviewed by Darin Adler.

* wtf/Vector.h:
(WTF::Vector::append): Adding not templated append method, forwarding to ValueType templated append method.

LayoutTests:
[Streams API] Implement ReadableStreamReader read method in closed and errored state
https://bugs.webkit.org/show_bug.cgi?id=144790

Reviewed by Darin Adler.

Added new test. Rebased reference tests (expectations and timeouting/untimeouting tests).
Removed temporarily some tests in streams/reference-implementation/readable-stream-templated.html.
These tests try to check handling promise returned in start method which is not yet supported.
If we did not comment these tests, they would be flaky.

* streams/readable-stream-reader-read-expected.txt: Added.
* streams/readable-stream-reader-read.html: Added.
* streams/reference-implementation/bad-underlying-sources-expected.txt:
* streams/reference-implementation/bad-underlying-sources.html:
* streams/reference-implementation/count-queuing-strategy-expected.txt:
* streams/reference-implementation/count-queuing-strategy.html:
* streams/reference-implementation/readable-stream-cancel-expected.txt:
* streams/reference-implementation/readable-stream-expected.txt:
* streams/reference-implementation/readable-stream-reader-expected.txt:
* streams/reference-implementation/readable-stream-reader.html:
* streams/reference-implementation/readable-stream-templated-expected.txt:
* streams/reference-implementation/readable-stream-templated.html:
* streams/reference-implementation/readable-stream.html:

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

25 files changed:
LayoutTests/ChangeLog
LayoutTests/streams/readable-stream-reader-read-expected.txt [new file with mode: 0644]
LayoutTests/streams/readable-stream-reader-read.html [new file with mode: 0644]
LayoutTests/streams/reference-implementation/bad-underlying-sources-expected.txt
LayoutTests/streams/reference-implementation/bad-underlying-sources.html
LayoutTests/streams/reference-implementation/count-queuing-strategy-expected.txt
LayoutTests/streams/reference-implementation/count-queuing-strategy.html
LayoutTests/streams/reference-implementation/readable-stream-cancel-expected.txt
LayoutTests/streams/reference-implementation/readable-stream-expected.txt
LayoutTests/streams/reference-implementation/readable-stream-reader-expected.txt
LayoutTests/streams/reference-implementation/readable-stream-reader.html
LayoutTests/streams/reference-implementation/readable-stream-templated-expected.txt
LayoutTests/streams/reference-implementation/readable-stream-templated.html
LayoutTests/streams/reference-implementation/readable-stream.html
Source/WTF/ChangeLog
Source/WTF/wtf/Vector.h
Source/WebCore/ChangeLog
Source/WebCore/ForwardingHeaders/runtime/IteratorOperations.h [new file with mode: 0644]
Source/WebCore/Modules/streams/ReadableStream.cpp
Source/WebCore/Modules/streams/ReadableStream.h
Source/WebCore/Modules/streams/ReadableStreamReader.cpp
Source/WebCore/Modules/streams/ReadableStreamReader.h
Source/WebCore/bindings/js/JSReadableStreamReaderCustom.cpp
Source/WebCore/bindings/js/ReadableJSStream.cpp
Source/WebCore/bindings/js/ReadableJSStream.h

index 5b3c792..1305e80 100644 (file)
@@ -1,3 +1,29 @@
+2015-06-02  Xabier Rodriguez Calvar  <calvaris@igalia.com> and Youenn Fablet <youenn.fablet@crf.canon.fr>
+
+        [Streams API] Implement ReadableStreamReader read method in closed and errored state
+        https://bugs.webkit.org/show_bug.cgi?id=144790
+
+        Reviewed by Darin Adler.
+
+        Added new test. Rebased reference tests (expectations and timeouting/untimeouting tests).
+        Removed temporarily some tests in streams/reference-implementation/readable-stream-templated.html.
+        These tests try to check handling promise returned in start method which is not yet supported.
+        If we did not comment these tests, they would be flaky.
+
+        * streams/readable-stream-reader-read-expected.txt: Added.
+        * streams/readable-stream-reader-read.html: Added.
+        * streams/reference-implementation/bad-underlying-sources-expected.txt:
+        * streams/reference-implementation/bad-underlying-sources.html:
+        * streams/reference-implementation/count-queuing-strategy-expected.txt:
+        * streams/reference-implementation/count-queuing-strategy.html:
+        * streams/reference-implementation/readable-stream-cancel-expected.txt:
+        * streams/reference-implementation/readable-stream-expected.txt:
+        * streams/reference-implementation/readable-stream-reader-expected.txt:
+        * streams/reference-implementation/readable-stream-reader.html:
+        * streams/reference-implementation/readable-stream-templated-expected.txt:
+        * streams/reference-implementation/readable-stream-templated.html:
+        * streams/reference-implementation/readable-stream.html:
+
 2015-06-02  Brady Eidson  <beidson@apple.com>
 
         WebKit policy delegate should suggest if a navigation should be allowed to open URLs externally.
diff --git a/LayoutTests/streams/readable-stream-reader-read-expected.txt b/LayoutTests/streams/readable-stream-reader-read-expected.txt
new file mode 100644 (file)
index 0000000..3af43e1
--- /dev/null
@@ -0,0 +1,7 @@
+
+PASS Reading twice on a stream that gets closed 
+PASS Reading twice on a closed stream 
+PASS Reading twice on an errored stream 
+PASS Reading twice on a stream that gets errored 
+PASS Reading within a read promise resolve callback on a stream that gets closed 
+
diff --git a/LayoutTests/streams/readable-stream-reader-read.html b/LayoutTests/streams/readable-stream-reader-read.html
new file mode 100644 (file)
index 0000000..82417b7
--- /dev/null
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<script>
+var t1 = async_test('Reading twice on a stream that gets closed');
+t1.step(function() {
+    var controller;
+    var rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        }
+    });
+    var counter = 0;
+    var reader = rs.getReader();
+
+    reader.read().then(t1.step_func(function(result) {
+        assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close');
+        assert_equals(counter, 1);
+        counter++;
+    }));
+    reader.read().then(t1.step_func(function(result) {
+        assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close');
+        assert_equals(counter, 2);
+        counter++;
+        t1.done();
+    }));
+    reader.closed.then(t1.step_func(function() {
+        assert_equals(counter, 0);
+        counter++;
+    }));
+
+    controller.close();
+});
+
+var t2 = async_test('Reading twice on a closed stream');
+t2.step(function() {
+    var controller;
+    var rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        }
+    });
+
+    controller.close();
+
+    var counter = 0;
+    var reader = rs.getReader();
+
+    reader.read().then(t2.step_func(function(result) {
+        assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close');
+        assert_equals(counter, 0);
+        counter++;
+    }));
+    reader.read().then(t2.step_func(function(result) {
+        assert_object_equals(result, { value: undefined, done: true }, 'read() should fulfill with close');
+        assert_equals(counter, 1);
+        counter++;
+    }));
+    reader.closed.then(t2.step_func(function() {
+        assert_equals(counter, 2);
+        counter++;
+        t2.done();
+    }));
+});
+
+var t3 = async_test('Reading twice on an errored stream');
+t3.step(function() {
+    var controller;
+    var rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        }
+    });
+
+    var myError = { potato: "mashed" };
+    controller.error(myError);
+
+    var counter = 0;
+    var reader = rs.getReader();
+
+    reader.read().then(t3.step_func(function() {
+        assert_unreached('read() should reject on an errored stream');
+    }), t3.step_func(function(err) {
+        assert_equals(myError, err);
+        assert_equals(counter, 0);
+        counter++;
+    }));
+    reader.read().then(t3.step_func(function() {
+        assert_unreached('read() should reject on an errored stream');
+    }), t3.step_func(function(err) {
+        assert_equals(myError, err);
+        assert_equals(counter, 1);
+        counter++;
+    }));
+    reader.closed.then(t3.step_func(function() {
+        assert_unreached('read() should reject on an errored stream');
+    }), t3.step_func(function(err) {
+        assert_equals(myError, err);
+        assert_equals(counter, 2);
+        counter++;
+        t3.done();
+    }));
+});
+
+var t4 = async_test('Reading twice on a stream that gets errored');
+t4.step(function() {
+    var controller;
+    var rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        }
+    });
+
+    var counter = 0;
+    var reader = rs.getReader();
+
+    reader.read().then(t4.step_func(function() {
+        assert_unreached('read() should reject on an errored stream');
+    }), t4.step_func(function(err) {
+        assert_equals(myError, err);
+        assert_equals(counter, 1);
+        counter++;
+    }));
+    reader.read().then(t4.step_func(function() {
+        assert_unreached('read() should reject on an errored stream');
+    }), t4.step_func(function(err) {
+        assert_equals(myError, err);
+        assert_equals(counter, 2);
+        counter++;
+        t4.done();
+    }));
+    reader.closed.then(t4.step_func(function() {
+        assert_unreached('read() should reject on an errored stream');
+    }), t4.step_func(function(err) {
+        assert_equals(myError, err);
+        assert_equals(counter, 0);
+        counter++;
+    }));
+
+    var myError = { potato: 'mashed' };
+    controller.error(myError);
+ });
+
+var t5 = async_test('Reading within a read promise resolve callback on a stream that gets closed');
+t5.step(function() {
+    var controller;
+    var rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        }
+    });
+
+    var reader = rs.getReader();
+
+    reader.read().then(t5.step_func(function() {
+        reader.read().then(t5.step_func(function() {
+            t5.done();
+        }));
+    }));
+    controller.close();
+});
+</script>
index 19ca355..b75581b 100644 (file)
@@ -5,8 +5,8 @@ FAIL Underlying source start: throwing getter assert_throws: constructing the st
 PASS Underlying source start: throwing method 
 TIMEOUT Underlying source: throwing pull getter (initial pull) Test timed out
 TIMEOUT Underlying source: throwing pull method (initial pull) Test timed out
-FAIL Underlying source: throwing pull getter (second pull) read is not implemented
-FAIL Underlying source: throwing pull method (second pull) read is not implemented
+TIMEOUT Underlying source: throwing pull getter (second pull) Test timed out
+TIMEOUT Underlying source: throwing pull method (second pull) Test timed out
 FAIL Underlying source: throwing cancel getter cancel is not implemented
 FAIL Underlying source: throwing cancel method cancel is not implemented
 FAIL Underlying source: throwing strategy getter assert_throws: enqueue should throw the error function "function () { c.enqueue('a'); }" did not throw
@@ -18,7 +18,7 @@ FAIL Underlying source: strategy.size returning NaN assert_unreached: enqueue di
 FAIL Underlying source: strategy.size returning -Infinity assert_unreached: enqueue didn't throw Reached unreachable code
 FAIL Underlying source: strategy.size returning +Infinity assert_unreached: enqueue didn't throw Reached unreachable code
 PASS Underlying source: calling close twice on an empty stream should throw the second time 
-FAIL Underlying source: calling close twice on a non-empty stream should throw the second time read is not implemented
+FAIL Underlying source: calling close twice on a non-empty stream should throw the second time assert_object_equals: read() should read the enqueued chunk property "done" expected object "[object Object]" got object "[object Object]"
 FAIL Underlying source: calling close on an empty canceled stream should not throw cancel is not implemented
 FAIL Underlying source: calling close on a non-empty canceled stream should not throw cancel is not implemented
 PASS Underlying source: calling close after error should throw 
index bc41709..c85d53c 100644 (file)
@@ -61,7 +61,7 @@ test2.step(function() {
         }));
 });
 
-var test3 = async_test('Underlying source: throwing pull getter (second pull)');
+var test3 = async_test('Underlying source: throwing pull getter (second pull)', { timeout: 50 });
 test3.step(function() {
     var theError = new Error('a unique string');
     var counter = 0;
@@ -89,7 +89,7 @@ test3.step(function() {
         }));
 });
 
-var test4 = async_test('Underlying source: throwing pull method (second pull)');
+var test4 = async_test('Underlying source: throwing pull method (second pull)', { timeout: 50 });
 test4.step(function() {
     var theError = new Error('a unique string');
     var counter = 0;
@@ -437,7 +437,7 @@ test18.step(function() {
     }));
 });
 
-var test19 = async_test('Underlying source: calling close after error should throw', { timeout: 50 });
+var test19 = async_test('Underlying source: calling close after error should throw');
 test19.step(function() {
     var theError = new Error('boo');
     var startCalled = false;
@@ -454,7 +454,7 @@ test19.step(function() {
     }));
 });
 
-var test20 = async_test('Underlying source: calling error twice should throw the second time', { timeout: 50 });
+var test20 = async_test('Underlying source: calling error twice should throw the second time');
 test20.step(function() {
     var theError = new Error('boo');
     var startCalled = false;
@@ -471,7 +471,7 @@ test20.step(function() {
     }));
 });
 
-var test21 = async_test('Underlying source: calling error after close should throw', { timeout: 50 });
+var test21 = async_test('Underlying source: calling error after close should throw');
 test21.step(function() {
     var startCalled = false;
     new ReadableStream({
index 7bdf3de..d7cbb75 100644 (file)
@@ -2,7 +2,7 @@
 PASS Can construct a CountQueuingStrategy with a valid high water mark 
 PASS Gives a RangeError when the number is negative 
 PASS Can construct a readable stream with a valid CountQueuingStrategy 
-FAIL Correctly governs the return value of a ReadableStream's enqueue function (HWM = 0) read is not implemented
+TIMEOUT Correctly governs the return value of a ReadableStream's enqueue function (HWM = 0) Test timed out
 FAIL Correctly governs the return value of a ReadableStream's enqueue function (HWM = 1) assert_equals: After 0 reads, 1st enqueue should return true (queue now contains 1 chunk) expected true but got false
 FAIL Correctly governs the return value of a ReadableStream's enqueue function (HWM = 4) assert_equals: After 0 reads, 1st enqueue should return true (queue now contains 1 chunk) expected true but got false
 
index 3b9645b..82c387d 100644 (file)
@@ -17,7 +17,7 @@ test(function() {
     new ReadableStream({ strategy: new CountQueuingStrategy({ "highWaterMark": 4 }) });
 }, 'Can construct a readable stream with a valid CountQueuingStrategy');
 
-var test1 = async_test('Correctly governs the return value of a ReadableStream\'s enqueue function (HWM = 0)');
+var test1 = async_test('Correctly governs the return value of a ReadableStream\'s enqueue function (HWM = 0)', { timeout: 50 });
 test1.step(function() {
     var enqueue;
     var rs = new ReadableStream({
index edf9017..9b75542 100644 (file)
@@ -1,5 +1,5 @@
 
-FAIL ReadableStream cancellation: integration test on an infinite stream derived from a random push source read is not implemented
+FAIL ReadableStream cancellation: integration test on an infinite stream derived from a random push source cancel is not implemented
 FAIL ReadableStream cancellation: cancel(reason) should pass through the given reason to the underlying source cancel is not implemented
 FAIL ReadableStream cancellation: cancel() on a locked stream should fail and not call the underlying source cancel cancel is not implemented
 FAIL ReadableStream cancellation: returning a value from the underlying source's cancel should not affect the fulfillment value of the promise returned by the stream's cancel cancel is not implemented
index e9d8495..89ae49d 100644 (file)
@@ -5,22 +5,22 @@ PASS ReadableStream constructor should throw for non-function start arguments
 PASS ReadableStream constructor can get initial garbage as cancel argument 
 PASS ReadableStream constructor can get initial garbage as pull argument 
 PASS ReadableStream constructor can get initial garbage as strategy argument 
-FAIL ReadableStream start should be able to return a promise read is not implemented
+TIMEOUT ReadableStream start should be able to return a promise Test timed out
 TIMEOUT ReadableStream start should be able to return a promise and reject it Test timed out
-FAIL ReadableStream should be able to enqueue different objects. read is not implemented
+FAIL ReadableStream should be able to enqueue different objects. assert_object_equals: value read should be the one enqueued property "done" expected object "[object Object]" got object "[object Object]"
 PASS ReadableStream: if start throws an error, it should be re-thrown 
-FAIL ReadableStream: if pull rejects, it should error the stream read is not implemented
-FAIL ReadableStream: should not call pull until the previous pull call's promise fulfills read is not implemented
-FAIL ReadableStream: should not call pull after start if the stream is now closed read is not implemented
+TIMEOUT ReadableStream: if pull rejects, it should error the stream Test timed out
+TIMEOUT ReadableStream: should not call pull until the previous pull call's promise fulfills Test timed out
+PASS ReadableStream: should not call pull after start if the stream is now closed 
 FAIL ReadableStream: should call pull after enqueueing from inside pull (with no read requests), if strategy allows assert_equals: pull() should have been called four times expected 4 but got 0
 TIMEOUT ReadableStream pull should be able to close a stream. Test timed out
 FAIL ReadableStream: enqueue should throw when the stream is readable but draining assert_equals: the first enqueue should return true expected true but got false
 FAIL ReadableStream: enqueue should throw when the stream is closed assert_throws: enqueue after close should throw a TypeError function "function () { c.enqueue('a'); }" did not throw
 FAIL ReadableStream: enqueue should throw the stored error when the stream is errored assert_throws: enqueue after error should throw that error function "function () { c.enqueue('a'); }" did not throw
-FAIL ReadableStream: should call underlying source methods as methods read is not implemented
+TIMEOUT ReadableStream: should call underlying source methods as methods Test timed out
 FAIL ReadableStream strategies: the default strategy should return false for all but the first enqueue call assert_equals: first enqueue should return true expected true but got false
 FAIL ReadableStream strategies: the default strategy should continue returning true from enqueue if the chunks are read immediately assert_equals: first enqueue should return true expected true but got false
-FAIL ReadableStream integration test: adapting a random push source read is not implemented
-FAIL ReadableStream integration test: adapting a sync pull source read is not implemented
-FAIL ReadableStream integration test: adapting an async pull source read is not implemented
+TIMEOUT ReadableStream integration test: adapting a random push source Test timed out
+TIMEOUT ReadableStream integration test: adapting a sync pull source Test timed out
+TIMEOUT ReadableStream integration test: adapting an async pull source Test timed out
 
index 759f13c..07c5d74 100644 (file)
@@ -9,10 +9,10 @@ PASS Constructing a ReadableStreamReader directly should fail if the stream is a
 PASS Getting a ReadableStreamReader via getReader should fail if the stream is already locked (via direct getReader) 
 PASS Constructing a ReadableStreamReader directly should be OK if the stream is closed 
 PASS Constructing a ReadableStreamReader directly should be OK if the stream is errored 
-FAIL Reading from a reader for an empty stream will wait until a chunk is available read is not implemented
+TIMEOUT Reading from a reader for an empty stream will wait until a chunk is available Test timed out
 FAIL cancel() on a reader releases the reader before calling through cancel is not implemented
 PASS closed should be fulfilled after stream is closed (.closed access before acquiring) 
 FAIL closed should be fulfilled after reader releases its lock (multiple stream locks) releaseLock is not implemented
 FAIL Cannot use an already-released reader to unlock a stream again releaseLock is not implemented
-FAIL Getting a second reader after erroring the stream should succeed read is not implemented
+PASS Getting a second reader after erroring the stream should succeed 
 
index 358343a..813e01e 100644 (file)
@@ -107,7 +107,7 @@ test(function() {
     new ReadableStreamReader(rs); // Constructing directly should not throw.
 }, 'Constructing a ReadableStreamReader directly should be OK if the stream is errored');
 
-var test1 = async_test('Reading from a reader for an empty stream will wait until a chunk is available');
+var test1 = async_test('Reading from a reader for an empty stream will wait until a chunk is available', { timeout: 50 });
 test1.step(function() {
     var controller;
     var rs = new ReadableStream({
index 238b0f7..3e8c2c1 100644 (file)
@@ -3,7 +3,7 @@ PASS Running templatedRSEmpty with ReadableStream (empty)
 PASS instances have the correct methods and properties 
 PASS Running templatedRSEmptyReader with ReadableStream (empty) reader 
 PASS instances have the correct methods and properties 
-FAIL read() should return distinct promises each time read is not implemented
+PASS read() should return distinct promises each time 
 PASS getReader() again on the stream should fail 
 FAIL releasing the lock should cause further read() calls to resolve as if the stream is closed releaseLock is not implemented
 FAIL releasing the lock should cause closed to fulfill releaseLock is not implemented
@@ -14,7 +14,7 @@ FAIL cancel() should return a distinct fulfilled promise each time cancel is not
 PASS getReader() should be OK 
 PASS should be able to acquire multiple readers, since they are all auto-released 
 PASS Running templatedRSClosedReader with ReadableStream (closed via call in start) reader 
-FAIL read() should fulfill with { value: undefined, done: true } read is not implemented
+PASS read() should fulfill with { value: undefined, done: true } 
 PASS closed should fulfill with undefined 
 PASS Running templatedRSClosed with ReadableStream (closed via cancel) 
 FAIL cancel() should return a distinct fulfilled promise each time cancel is not implemented
@@ -24,26 +24,21 @@ PASS Running templatedRSClosedReader with ReadableStream (closed via cancel) rea
 FAIL read() should fulfill with { value: undefined, done: true } cancel is not implemented
 FAIL closed should fulfill with undefined cancel is not implemented
 PASS Running templatedRSErrored with ReadableStream (errored via call in start) 
-FAIL getReader() should return a reader that acts errored read is not implemented
+PASS getReader() should return a reader that acts errored 
 PASS Running templatedRSErroredSyncOnly with ReadableStream (errored via call in start) 
 FAIL cancel() should return a distinct rejected promise each time cancel is not implemented
 FAIL reader cancel() should return a distinct rejected promise each time cancel is not implemented
 PASS should be able to acquire multiple readers, since they are all auto-released 
-PASS Running templatedRSErrored with ReadableStream (errored via returning a rejected promise in start) 
-FAIL getReader() should return a reader that acts errored read is not implemented
-PASS Running templatedRSErroredReader with ReadableStream (errored via returning a rejected promise in start) reader 
-TIMEOUT closed should reject with the error Test timed out
-FAIL read() should reject with the error read is not implemented
 PASS Running templatedRSTwoChunksOpenReader with ReadableStream (two chunks enqueued, still open) reader 
-FAIL calling read() twice without waiting will eventually give both chunks read is not implemented
-FAIL calling read() twice with waiting will eventually give both chunks read is not implemented
-FAIL read() should return distinct promises each time read is not implemented
-FAIL cancel() after a read() should still give that single read result read is not implemented
+TIMEOUT calling read() twice without waiting will eventually give both chunks Test timed out
+TIMEOUT calling read() twice with waiting will eventually give both chunks Test timed out
+PASS read() should return distinct promises each time 
+FAIL cancel() after a read() should still give that single read result cancel is not implemented
 PASS Running templatedRSTwoChunksClosedReader with ReadableStream (two chunks enqueued, then closed) reader 
-FAIL third read(), without waiting, should give { value: undefined, done: true } read is not implemented
-FAIL third read, with waiting, should give { value: undefined, done: true } read is not implemented
-FAIL draining the stream via read() should cause the reader closed promise to fulfill read is not implemented
-FAIL releasing the lock after the stream is closed should do nothing read is not implemented
+FAIL third read(), without waiting, should give { value: undefined, done: true } assert_object_equals: first result should be correct property "done" expected object "[object Object]" got object "[object Object]"
+FAIL third read, with waiting, should give { value: undefined, done: true } assert_object_equals: first result should be correct property "done" expected object "[object Object]" got object "[object Object]"
+PASS draining the stream via read() should cause the reader closed promise to fulfill 
+FAIL releasing the lock after the stream is closed should do nothing releaseLock is not implemented
 FAIL releasing the lock should cause read() to act as if the stream is closed releaseLock is not implemented
-FAIL reader's closed property always returns the same promise read is not implemented
+FAIL reader's closed property always returns the same promise releaseLock is not implemented
 
index 8c1bf58..ac2c4c2 100644 (file)
@@ -369,7 +369,7 @@ function templatedRSTwoChunksOpenReader(label, factory, chunks) {
     test(function() {
     }, 'Running templatedRSTwoChunksOpenReader with ' + label);
 
-    var test1 = async_test('calling read() twice without waiting will eventually give both chunks');
+    var test1 = async_test('calling read() twice without waiting will eventually give both chunks', { timeout: 50 });
     test1.step(function() {
         var { reader } = factory();
         var promiseCount = 0;
@@ -385,7 +385,7 @@ function templatedRSTwoChunksOpenReader(label, factory, chunks) {
         }));
     });
 
-    var test2 = async_test('calling read() twice with waiting will eventually give both chunks');
+    var test2 = async_test('calling read() twice with waiting will eventually give both chunks', { timeout: 50 });
     test2.step(function() {
         var { reader } = factory();
 
@@ -615,23 +615,24 @@ templatedRSErroredSyncOnly('ReadableStream (errored via call in start)', functio
     theError
 );
 
-templatedRSErrored('ReadableStream (errored via returning a rejected promise in start)', function() {
-    return new ReadableStream({
-        start: function() {
-            return Promise.reject(theError);
-        }
-    })},
-    theError
-);
-
-templatedRSErroredReader('ReadableStream (errored via returning a rejected promise in start) reader', function() {
-    return streamAndDefaultReader(new ReadableStream({
-        start: function() {
-            return Promise.reject(theError);
-        }
-    }))},
-    theError
-);
+// FIXME: Activate these tests once Promise handling is supported in start function.
+// templatedRSErrored('ReadableStream (errored via returning a rejected promise in start)', function() {
+//     return new ReadableStream({
+//         start: function() {
+//             return Promise.reject(theError);
+//         }
+//     })},
+//     theError
+// );
+
+// templatedRSErroredReader('ReadableStream (errored via returning a rejected promise in start) reader', function() {
+//     return streamAndDefaultReader(new ReadableStream({
+//         start: function() {
+//             return Promise.reject(theError);
+//         }
+//     }))},
+//     theError
+// );
 
 var chunks = ['a', 'b'];
 
index d0725e4..16ce8c4 100644 (file)
@@ -53,7 +53,7 @@ test(function() {
     new ReadableStream({ strategy: 2 }); // Constructor should not throw when strategy is not an object.
 }, 'ReadableStream constructor can get initial garbage as strategy argument');
 
-var test1 = async_test('ReadableStream start should be able to return a promise');
+var test1 = async_test('ReadableStream start should be able to return a promise', { timeout: 50 });
 test1.step(function()
 {
     var readCalled = false;
@@ -146,7 +146,7 @@ test(function() {
     assert_throws(error, function() { new ReadableStream({ start() { throw error; } }) }, 'error should be re-thrown');
 }, 'ReadableStream: if start throws an error, it should be re-thrown');
 
-var test4 = async_test('ReadableStream: if pull rejects, it should error the stream');
+var test4 = async_test('ReadableStream: if pull rejects, it should error the stream', { timeout: 50 });
 test4.step(function() {
     var error = new Error('pull failure');
     var rs = new ReadableStream({
@@ -368,7 +368,7 @@ test4.step(function() {
 //     }), standardTimeout);
 // });
 
-var test11 = async_test('ReadableStream: should not call pull until the previous pull call\'s promise fulfills');
+var test11 = async_test('ReadableStream: should not call pull until the previous pull call\'s promise fulfills', { timeout: 50 });
 test11.step(function() {
     var resolve;
     var returnedPromise;
@@ -565,7 +565,7 @@ test(function() {
     });
 }, 'ReadableStream: enqueue should throw the stored error when the stream is errored');
 
-var test16 = async_test('ReadableStream: should call underlying source methods as methods');
+var test16 = async_test('ReadableStream: should call underlying source methods as methods', { timeout: 50 });
 test16.step(function() {
     var startCalled = 0;
     var pullCalled = 0;
@@ -658,7 +658,7 @@ test17.step(function() {
     })).catch(test17.step_func(function(e) { assert_unreached(e); } ));
 });
 
-var test18 = async_test('ReadableStream integration test: adapting a random push source');
+var test18 = async_test('ReadableStream integration test: adapting a random push source', { timeout: 50 });
 test18.step(function() {
     var pullChecked = false;
     var randomSource = new RandomPushSource(8);
@@ -702,7 +702,7 @@ test18.step(function() {
     }), test18.step_func(function(e) { assert_reached(e); }));
 });
 
-var test19 = async_test('ReadableStream integration test: adapting a sync pull source');
+var test19 = async_test('ReadableStream integration test: adapting a sync pull source', { timeout: 50 });
 test19.step(function() {
     var rs = sequentialReadableStream(10);
 
@@ -714,7 +714,7 @@ test19.step(function() {
     }));
 });
 
-var test20 = async_test('ReadableStream integration test: adapting an async pull source');
+var test20 = async_test('ReadableStream integration test: adapting an async pull source', { timeout: 50 });
 test20.step(function() {
     var rs = sequentialReadableStream(10, { async: true });
 
index 5d63dd2..aebafc7 100644 (file)
@@ -1,3 +1,13 @@
+2015-06-02  Xabier Rodriguez Calvar  <calvaris@igalia.com> and Youenn Fablet <youenn.fablet@crf.canon.fr>
+
+        [Streams API] Delegate ReadableStreamReader reference counting to ReadableStream
+        https://bugs.webkit.org/show_bug.cgi?id=144907
+
+        Reviewed by Darin Adler.
+
+        * wtf/Vector.h:
+        (WTF::Vector::append): Adding not templated append method, forwarding to ValueType templated append method.
+
 2015-06-02  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         Heap-use-after-free read of size 4 in JavaScriptCore: WTF::StringImpl::isSymbol() (StringImpl.h:496)
index 4257d8c..d61c5ff 100644 (file)
@@ -723,6 +723,7 @@ public:
 
     void clear() { shrinkCapacity(0); }
 
+    void append(ValueType&& value) { append<ValueType>(std::forward<ValueType>(value)); }
     template<typename U> void append(const U*, size_t);
     template<typename U> void append(U&&);
     template<typename U> void uncheckedAppend(U&& val);
index fa639d5..a8434e5 100644 (file)
@@ -1,3 +1,39 @@
+2015-06-02  Xabier Rodriguez Calvar  <calvaris@igalia.com> and Youenn Fablet <youenn.fablet@crf.canon.fr>
+
+        [Streams API] Implement ReadableStreamReader read method in closed and errored state
+        https://bugs.webkit.org/show_bug.cgi?id=144790
+
+        Reviewed by Darin Adler.
+
+        Reader delegates read() promise handling to its stream except if reader is no longer locking the stream and stream is readable.
+        Storing of reader read() promise callbacks as a Vector in ReadableStream.
+        Added resolution/rejection of read() promises in case of errored/closed streams.
+
+        Test: streams/readable-stream-reader-read.html
+
+        * ForwardingHeaders/runtime/IteratorOperations.h: Added.
+        * Modules/streams/ReadableStream.cpp:
+        (WebCore::ReadableStream::cleanCallbacks): Clean the read requests.
+        (WebCore::ReadableStream::changeStateToClosed): Run success callbacks with undefined for read requests.
+        (WebCore::ReadableStream::changeStateToErrored): Run failure callbacks with the errors for read requests.
+        (WebCore::ReadableStream::closed): Parameter name changed.
+        (WebCore::ReadableStream::read): Added. Succeeds with empty when closed, fails with error when errored, reads a
+        value if there is one and pushes the callbacks to the queue otherwise.
+        * Modules/streams/ReadableStream.h:
+        (WebCore::ReadableStream::ReadCallbacks::ReadCallbacks): Struct containing success and failure callbacks.
+        * Modules/streams/ReadableStreamReader.cpp:
+        (WebCore::ReadableStreamReader::closed): Parameter name changed.
+        (WebCore::ReadableStreamReader::read): Invoke success with empty if we streams if we don't have the right reader
+        and call the stream otherwise to read.
+        * Modules/streams/ReadableStreamReader.h:
+        * bindings/js/JSReadableStreamReaderCustom.cpp:
+        (WebCore::JSReadableStreamReader::read): Create the callback lambdas and invoke read. Failure rejects the
+        promise and success creates the result from the read value.
+        * bindings/js/ReadableJSStream.cpp:
+        (WebCore::ReadableJSStream::hasValue):
+        (WebCore::ReadableJSStream::read): Not implemented yet.
+        * bindings/js/ReadableJSStream.h:
+
 2015-06-01  Myles C. Maxfield  <mmaxfield@apple.com>
 
         [Cocoa] FontPlatformData's equality check should always use reference URLs
diff --git a/Source/WebCore/ForwardingHeaders/runtime/IteratorOperations.h b/Source/WebCore/ForwardingHeaders/runtime/IteratorOperations.h
new file mode 100644 (file)
index 0000000..bce06a1
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef WebCore_FWD_IteratorOperations_h
+#define WebCore_FWD_IteratorOperations_h
+#include <JavaScriptCore/IteratorOperations.h>
+#endif
index bec3d30..f019b44 100644 (file)
@@ -34,7 +34,7 @@
 
 #include "NotImplemented.h"
 #include "ReadableStreamReader.h"
-#include <runtime/JSCJSValue.h>
+#include <runtime/JSCJSValueInlines.h>
 #include <wtf/RefCountedLeakCounter.h>
 
 namespace WebCore {
@@ -62,6 +62,8 @@ void ReadableStream::clearCallbacks()
 {
     m_closedSuccessCallback = nullptr;
     m_closedFailureCallback = nullptr;
+
+    m_readRequests.clear();
 }
 
 void ReadableStream::changeStateToClosed()
@@ -76,6 +78,9 @@ void ReadableStream::changeStateToClosed()
     if (m_closedSuccessCallback)
         m_closedSuccessCallback();
 
+    for (auto& request : m_readRequests)
+        request.endCallback();
+
     clearCallbacks();
 }
 
@@ -88,8 +93,12 @@ void ReadableStream::changeStateToErrored()
     if (m_reader)
         m_releasedReaders.append(WTF::move(m_reader));
 
+    JSC::JSValue error = this->error();
     if (m_closedFailureCallback)
-        m_closedFailureCallback(error());
+        m_closedFailureCallback(error);
+
+    for (auto& request : m_readRequests)
+        request.failureCallback(error);
 
     clearCallbacks();
 }
@@ -110,7 +119,7 @@ ReadableStreamReader& ReadableStream::getReader()
     return reader;
 }
 
-void ReadableStream::closed(ClosedSuccessCallback successCallback, ClosedFailureCallback failureCallback)
+void ReadableStream::closed(ClosedSuccessCallback&& successCallback, FailureCallback&& failureCallback)
 {
     if (m_state == State::Closed) {
         successCallback();
@@ -124,6 +133,24 @@ void ReadableStream::closed(ClosedSuccessCallback successCallback, ClosedFailure
     m_closedFailureCallback = WTF::move(failureCallback);
 }
 
+void ReadableStream::read(ReadSuccessCallback&& successCallback, ReadEndCallback&& endCallback, FailureCallback&& failureCallback)
+{
+    if (m_state == State::Closed) {
+        endCallback();
+        return;
+    }
+    if (m_state == State::Errored) {
+        failureCallback(error());
+        return;
+    }
+    if (hasValue()) {
+        successCallback(read());
+        return;
+    }
+    m_readRequests.append({ WTF::move(successCallback), WTF::move(endCallback), WTF::move(failureCallback) });
+    // FIXME: We should try to pull.
+}
+
 void ReadableStream::start()
 {
     notImplemented();
index c874365..2bd2ca5 100644 (file)
@@ -75,9 +75,14 @@ public:
 
     ReadableStreamSource& source() { return m_source.get(); }
 
+    typedef std::function<void(JSC::JSValue)> FailureCallback;
+
     typedef std::function<void()> ClosedSuccessCallback;
-    typedef std::function<void(JSC::JSValue)> ClosedFailureCallback;
-    void closed(ClosedSuccessCallback, ClosedFailureCallback);
+    void closed(ClosedSuccessCallback&&, FailureCallback&&);
+
+    typedef std::function<void(JSC::JSValue)> ReadSuccessCallback;
+    typedef std::function<void()> ReadEndCallback;
+    void read(ReadSuccessCallback&&, ReadEndCallback&&, FailureCallback&&);
 
 protected:
     ReadableStream(ScriptExecutionContext&, Ref<ReadableStreamSource>&&);
@@ -89,11 +94,21 @@ private:
 
     void clearCallbacks();
 
+    virtual bool hasValue() const = 0;
+    virtual JSC::JSValue read() = 0;
+
     std::unique_ptr<ReadableStreamReader> m_reader;
     Vector<std::unique_ptr<ReadableStreamReader>> m_releasedReaders;
 
     ClosedSuccessCallback m_closedSuccessCallback;
-    ClosedFailureCallback m_closedFailureCallback;
+    FailureCallback m_closedFailureCallback;
+
+    struct ReadCallbacks {
+        ReadSuccessCallback successCallback;
+        ReadEndCallback endCallback;
+        FailureCallback failureCallback;
+    };
+    Vector<ReadCallbacks> m_readRequests;
 
     State m_state { State::Readable };
     Ref<ReadableStreamSource> m_source;
index 4428dab..10810fe 100644 (file)
 #include "config.h"
 #include "ReadableStreamReader.h"
 
+#include <runtime/JSCJSValueInlines.h>
+
 #if ENABLE(STREAMS_API)
 
 namespace WebCore {
 
-void ReadableStreamReader::closed(ReadableStream::ClosedSuccessCallback successCallback, ReadableStream::ClosedFailureCallback failureCallback)
+void ReadableStreamReader::closed(ReadableStream::ClosedSuccessCallback&& successCallback, ReadableStream::FailureCallback&& failureCallback)
 {
     if (m_stream.isReadable() && m_stream.reader() != this) {
         successCallback();
@@ -43,6 +45,15 @@ void ReadableStreamReader::closed(ReadableStream::ClosedSuccessCallback successC
     m_stream.closed(WTF::move(successCallback), WTF::move(failureCallback));
 }
 
+void ReadableStreamReader::read(ReadableStream::ReadSuccessCallback&& successCallback, ReadableStream::ReadEndCallback&& endCallback, ReadableStream::FailureCallback&& failureCallback)
+{
+    if (m_stream.isReadable() && m_stream.reader() != this) {
+        successCallback(JSC::JSValue());
+        return;
+    }
+    m_stream.read(WTF::move(successCallback), WTF::move(endCallback), WTF::move(failureCallback));
+}
+
 }
 
 #endif
index 4ff42d2..ed20b73 100644 (file)
@@ -51,7 +51,8 @@ public:
     ReadableStreamReader(ReadableStream& stream)
         : m_stream(stream) { }
 
-    void closed(ReadableStream::ClosedSuccessCallback, ReadableStream::ClosedFailureCallback);
+    void closed(ReadableStream::ClosedSuccessCallback&&, ReadableStream::FailureCallback&&);
+    void read(ReadableStream::ReadSuccessCallback&&, ReadableStream::ReadEndCallback&&, ReadableStream::FailureCallback&&);
 
     void ref() { m_stream.ref(); }
     void deref() { m_stream.deref(); }
index a77c988..5d5c344 100644 (file)
@@ -37,6 +37,7 @@
 #include "JSReadableStream.h"
 #include "ReadableJSStream.h"
 #include <runtime/Error.h>
+#include <runtime/IteratorOperations.h>
 #include <wtf/NeverDestroyed.h>
 
 using namespace JSC;
@@ -45,8 +46,22 @@ namespace WebCore {
 
 JSValue JSReadableStreamReader::read(ExecState* exec)
 {
-    JSValue error = createError(exec, ASCIILiteral("read is not implemented"));
-    return exec->vm().throwException(exec, error);
+    DeferredWrapper wrapper(exec, globalObject());
+    auto successCallback = [wrapper](JSValue value) mutable {
+        JSValue result = createIteratorResultObject(wrapper.promise()->globalObject()->globalExec(), value, false);
+        wrapper.resolve(result);
+    };
+    auto endCallback = [wrapper]() mutable {
+        JSValue result = createIteratorResultObject(wrapper.promise()->globalObject()->globalExec(), JSC::jsUndefined(), true);
+        wrapper.resolve(result);
+    };
+    auto failureCallback = [wrapper](JSValue value) mutable {
+        wrapper.reject(value);
+    };
+
+    impl().read(WTF::move(successCallback), WTF::move(endCallback), WTF::move(failureCallback));
+
+    return wrapper.promise();
 }
 
 JSValue JSReadableStreamReader::closed(ExecState* exec) const
index 367385f..d487314 100644 (file)
@@ -148,6 +148,18 @@ void ReadableJSStream::storeError(JSC::ExecState& exec)
     changeStateToErrored();
 }
 
+bool ReadableJSStream::hasValue() const
+{
+    notImplemented();
+    return false;
+}
+
+JSValue ReadableJSStream::read()
+{
+    notImplemented();
+    return jsUndefined();
+}
+
 } // namespace WebCore
 
 #endif
index 4b73b89..68ab89a 100644 (file)
@@ -73,6 +73,9 @@ public:
 private:
     ReadableJSStream(ScriptExecutionContext&, Ref<ReadableJSStream::Source>&&);
 
+    virtual bool hasValue() const override;
+    virtual JSC::JSValue read() override;
+
     std::unique_ptr<ReadableStreamController> m_controller;
     JSC::Strong<JSC::Unknown> m_error;
 };