Source/WebCore/ChangeLog

 12015-06-18 Xabier Rodriguez Calvar <calvaris@igalia.com> and Youenn Fablet <youenn.fablet@crf.canon.fr>
 2
 3 [Streams API] Implement ReadableStream cancel (abstract part)
 4 https://bugs.webkit.org/show_bug.cgi?id=146111
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 This patch implements ReadableStream and ReadableStreamReader cancel.
 9 The reader delegates cancellation to its stream.
 10
 11 This patch also ensures that controller.close() will not throw in case cancellation is on-going.
 12
 13 A follow-up patch will implement the calling of 'cancel' JS callback for JS sources.
 14
 15 Covered by rebased tests.
 16
 17 * Modules/streams/ReadableStream.cpp:
 18 (WebCore::ReadableStream::cancel): Checks whether locked or not before cancelling.
 19 (WebCore::ReadableStream::cancelNoCheck): Cancel without lock check.
 20 (WebCore::ReadableStream::notifyCancelSucceeded): Async cancel callback.
 21 (WebCore::ReadableStream::notifyCancelFailed): Ditto.
 22 * Modules/streams/ReadableStream.h:
 23 * Modules/streams/ReadableStream.idl: Cleaned up IDL.
 24 * Modules/streams/ReadableStreamReader.cpp:
 25 (WebCore::ReadableStreamReader::cancel):
 26 * Modules/streams/ReadableStreamReader.h:
 27 * Modules/streams/ReadableStreamReader.idl: Cleaned up IDL
 28 * bindings/js/JSReadableStreamControllerCustom.cpp:
 29 (WebCore::JSReadableStreamController::close):
 30 * bindings/js/JSReadableStreamCustom.cpp:
 31 (WebCore::JSReadableStream::cancel):
 32 * bindings/js/JSReadableStreamReaderCustom.cpp:
 33 (WebCore::JSReadableStreamReader::cancel):
 34 * bindings/js/ReadableJSStream.cpp:
 35 (WebCore::ReadableJSStream::doCancel):
 36 * bindings/js/ReadableJSStream.h:
 37
1382015-06-19 Youenn Fablet <youenn.fablet@crf.canon.fr>
239
340 Bindings generator should generate code to catch exception and reject promises for Promise-based APIs

Source/WebCore/Modules/streams/ReadableStream.cpp

3232
3333#if ENABLE(STREAMS_API)
3434
 35#include "ExceptionCode.h"
3536#include "ReadableStreamReader.h"
3637#include <runtime/JSCJSValueInlines.h>
3738#include <wtf/RefCountedLeakCounter.h>

@@ReadableStreamReader& ReadableStream::getReader()
162163 return reader;
163164}
164165
 166void ReadableStream::cancel(JSC::JSValue reason, CancelPromise&& promise, ExceptionCode& ec)
 167{
 168 if (locked()) {
 169 ec = TypeError;
 170 return;
 171 }
 172 cancelNoCheck(reason, WTF::move(promise));
 173}
 174
 175void ReadableStream::cancelNoCheck(JSC::JSValue reason, CancelPromise&& promise)
 176{
 177 if (m_state == State::Closed) {
 178 promise.resolve(nullptr);
 179 return;
 180 }
 181 if (m_state == State::Errored) {
 182 promise.reject(error());
 183 return;
 184 }
 185 ASSERT(m_state == State::Readable);
 186
 187 m_cancelPromise = std::make_unique<CancelPromise>(WTF::move(promise));
 188
 189 close();
 190
 191 if (doCancel(reason))
 192 error() ? notifyCancelFailed() : notifyCancelSucceeded();
 193}
 194
 195void ReadableStream::notifyCancelSucceeded()
 196{
 197 ASSERT(m_state == State::Closed);
 198 ASSERT(m_cancelPromise);
 199
 200 m_cancelPromise->resolve(nullptr);
 201 m_cancelPromise.reset();
 202}
 203
 204void ReadableStream::notifyCancelFailed()
 205{
 206 ASSERT(m_state == State::Closed);
 207 ASSERT(m_cancelPromise);
 208
 209 m_cancelPromise->reject(error());
 210 m_cancelPromise.reset();
 211}
 212
165213void ReadableStream::closed(ClosedSuccessCallback&& successCallback, FailureCallback&& failureCallback)
166214{
167215 if (m_state == State::Closed) {

Source/WebCore/Modules/streams/ReadableStream.h

3333#if ENABLE(STREAMS_API)
3434
3535#include "ActiveDOMObject.h"
 36#include "JSDOMPromise.h"
3637#include "ScriptWrappable.h"
3738#include <functional>
3839#include <wtf/Deque.h>

@@namespace WebCore {
4748
4849class ReadableStreamReader;
4950class ScriptExecutionContext;
 51typedef int ExceptionCode;
5052
5153// ReadableStream implements the core of the streams API ReadableStream functionality.
5254// It handles in particular the backpressure according the queue size.

@@public:
8082 void changeStateToClosed();
8183 void changeStateToErrored();
8284 void finishPulling();
 85 void notifyCancelSucceeded();
 86 void notifyCancelFailed();
8387
8488 typedef std::function<void(JSC::JSValue)> FailureCallback;
8589
 90 typedef DOMPromise<std::nullptr_t, JSC::JSValue> CancelPromise;
 91 void cancel(JSC::JSValue, CancelPromise&&, ExceptionCode&);
 92 void cancelNoCheck(JSC::JSValue, CancelPromise&&);
 93
8694 typedef std::function<void()> ClosedSuccessCallback;
8795 void closed(ClosedSuccessCallback&&, FailureCallback&&);
8896

@@private:
107115 virtual bool hasValue() const = 0;
108116 virtual JSC::JSValue read() = 0;
109117 virtual bool doPull() = 0;
 118 virtual bool doCancel(JSC::JSValue) = 0;
110119
111120 std::unique_ptr<ReadableStreamReader> m_reader;
112121 Vector<std::unique_ptr<ReadableStreamReader>> m_releasedReaders;
113122
 123 std::unique_ptr<CancelPromise> m_cancelPromise;
 124
114125 ClosedSuccessCallback m_closedSuccessCallback;
115126 FailureCallback m_closedFailureCallback;
116127

Source/WebCore/Modules/streams/ReadableStream.idl

3232 Conditional=STREAMS_API,
3333 SkipVTableValidation
3434] interface ReadableStream {
35  // FIXME: Remove RaisesException once methods are actually implemented.
36  [Custom, RaisesException] Promise cancel(DOMString reason);
37  [Custom, RaisesException] ReadableStreamReader getReader();
38  [Custom, RaisesException] Promise pipeTo(any streams, any options);
39  [Custom, RaisesException] Object pipeThrough(any dest, any options);
 35 [Custom] Promise cancel(any reason);
 36 [Custom] ReadableStreamReader getReader();
 37 [Custom] Promise pipeTo(any streams, any options);
 38 [Custom] Object pipeThrough(any dest, any options);
4039
4140 readonly attribute boolean locked;
4241};

Source/WebCore/Modules/streams/ReadableStreamReader.cpp

3737
3838namespace WebCore {
3939
 40void ReadableStreamReader::cancel(JSC::JSValue reason, ReadableStream::CancelPromise&& promise)
 41{
 42 if (m_stream.isReadable() && m_stream.reader() != this) {
 43 promise.resolve(nullptr);
 44 return;
 45 }
 46 m_stream.cancelNoCheck(reason, WTF::move(promise));
 47}
 48
4049void ReadableStreamReader::closed(ReadableStream::ClosedSuccessCallback&& successCallback, ReadableStream::FailureCallback&& failureCallback)
4150{
4251 if (m_stream.isReadable() && m_stream.reader() != this) {

Source/WebCore/Modules/streams/ReadableStreamReader.h

@@public:
5353 ReadableStreamReader(ReadableStream& stream)
5454 : m_stream(stream) { }
5555
 56 void cancel(JSC::JSValue, ReadableStream::CancelPromise&&);
5657 void closed(ReadableStream::ClosedSuccessCallback&&, ReadableStream::FailureCallback&&);
5758 void read(ReadableStream::ReadSuccessCallback&&, ReadableStream::ReadEndCallback&&, ReadableStream::FailureCallback&&);
5859 void releaseLock(ExceptionCode&);

Source/WebCore/Modules/streams/ReadableStreamReader.idl

3333 NoInterfaceObject,
3434 SkipVTableValidation
3535] interface ReadableStreamReader {
36  // FIXME: Remove RaisesException once methods are implemented.
37  [Custom, RaisesException] Object read();
38  [Custom, RaisesException] Promise cancel(DOMString reason);
 36 [Custom] Object read();
 37 [Custom] Promise cancel(any reason);
3938 [RaisesException] void releaseLock();
4039
4140 [CustomGetter] readonly attribute Promise closed;

Source/WebCore/bindings/js/JSReadableStreamControllerCustom.cpp

@@namespace WebCore {
4343JSValue JSReadableStreamController::close(ExecState* exec)
4444{
4545 ReadableJSStream& stream = impl().stream();
46  // FIXME: Handle the case of draining.
47  if (!stream.isReadable())
48  return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Calling close on a stream which is not readable")));
 46 if (stream.isCloseRequested())
 47 return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Calling enqueue on a stream which is closing")));
 48 if (stream.isErrored())
 49 return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Calling close on a stream which is errored")));
4950 stream.changeStateToClosed();
5051 return jsUndefined();
5152}

Source/WebCore/bindings/js/JSReadableStreamCustom.cpp

@@namespace WebCore {
4949
5050JSValue JSReadableStream::cancel(ExecState* exec)
5151{
52  JSValue error = createError(exec, ASCIILiteral("cancel is not implemented"));
53  return exec->vm().throwException(exec, error);
 52 // FIXME: We should be able to remove this custom binding, once we can pass a JSValue or a ScriptValue.
 53 ExceptionCode ec = 0;
 54 JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(exec, globalObject());
 55 impl().cancel(exec->argument(0), DeferredWrapper(exec, globalObject(), promiseDeferred), ec);
 56
 57 if (ec)
 58 DeferredWrapper(exec, globalObject(), promiseDeferred).reject(ec);
 59
 60 return promiseDeferred->promise();
5461}
5562
5663JSValue JSReadableStream::getReader(ExecState* exec)

Source/WebCore/bindings/js/JSReadableStreamReaderCustom.cpp

@@JSValue JSReadableStreamReader::closed(ExecState* exec) const
8888
8989JSValue JSReadableStreamReader::cancel(ExecState* exec)
9090{
91  JSValue error = createError(exec, ASCIILiteral("cancel is not implemented"));
92  return exec->vm().throwException(exec, error);
 91 // FIXME: We should be able to remove this custom binding, once we can pass a JSValue or a ScriptValue.
 92 JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(exec, globalObject());
 93 impl().cancel(exec->argument(0), DeferredWrapper(exec, globalObject(), promiseDeferred));
 94 return promiseDeferred->promise();
9395}
9496
9597EncodedJSValue JSC_HOST_CALL constructJSReadableStreamReader(ExecState* exec)

Source/WebCore/bindings/js/ReadableJSStream.cpp

@@RefPtr<ReadableJSStream> ReadableJSStream::create(ExecState& exec, ScriptExecuti
182182 return readableStream;
183183}
184184
 185bool ReadableJSStream::doCancel(JSValue)
 186{
 187 // FIXME: Implement it.
 188 return true;
 189}
 190
185191ReadableJSStream::ReadableJSStream(ScriptExecutionContext& scriptExecutionContext, ExecState& state, JSObject* source)
186192 : ReadableStream(scriptExecutionContext)
187193{

Source/WebCore/bindings/js/ReadableJSStream.h

@@private:
7272 virtual bool hasValue() const override;
7373 virtual JSC::JSValue read() override;
7474 virtual bool doPull() override;
 75 virtual bool doCancel(JSC::JSValue) override;
7576
7677 JSDOMGlobalObject* globalObject();
7778

LayoutTests/ChangeLog

 12015-06-18 Xabier Rodriguez Calvar <calvaris@igalia.com> and Youenn Fablet <youenn.fablet@crf.canon.fr>
 2
 3 [Streams API] Implement ReadableStream cancel (abstract part)
 4 https://bugs.webkit.org/show_bug.cgi?id=146111
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Rebasing expectations.
 9
 10 * streams/reference-implementation/bad-underlying-sources-expected.txt:
 11 * streams/reference-implementation/readable-stream-cancel-expected.txt:
 12 * streams/reference-implementation/readable-stream-expected.txt:
 13 * streams/reference-implementation/readable-stream-reader-expected.txt:
 14 * streams/reference-implementation/readable-stream-templated-expected.txt:
 15
1162015-06-19 Joseph Pecoraro <pecoraro@apple.com>
217
318 Web Inspector: Avoid getOwnPropertyNames/Symbols on very large lists

LayoutTests/streams/reference-implementation/bad-underlying-sources-expected.txt

@@PASS Underlying source: throwing pull getter (initial pull)
55PASS Underlying source: throwing pull method (initial pull)
66PASS Underlying source: throwing pull getter (second pull)
77PASS Underlying source: throwing pull method (second pull)
8 FAIL Underlying source: throwing cancel getter cancel is not implemented
9 FAIL Underlying source: throwing cancel method cancel is not implemented
10 FAIL Underlying source: calling enqueue on an empty canceled stream should not throw cancel is not implemented
11 FAIL Underlying source: calling enqueue on a non-empty canceled stream should not throw cancel is not implemented
 8FAIL Underlying source: throwing cancel getter assert_unreached: cancel should not fulfill Reached unreachable code
 9FAIL Underlying source: throwing cancel method assert_unreached: cancel should not fulfill Reached unreachable code
 10PASS Underlying source: calling enqueue on an empty canceled stream should not throw
 11PASS Underlying source: calling enqueue on a non-empty canceled stream should not throw
1212PASS Underlying source: calling enqueue on a closed stream should throw
1313PASS Underlying source: calling enqueue on an errored stream should throw
1414PASS Underlying source: calling close twice on an empty stream should throw the second time
1515PASS Underlying source: calling close twice on a non-empty stream should throw the second time
16 FAIL Underlying source: calling close on an empty canceled stream should not throw cancel is not implemented
17 FAIL Underlying source: calling close on a non-empty canceled stream should not throw cancel is not implemented
 16PASS Underlying source: calling close on an empty canceled stream should not throw
 17PASS Underlying source: calling close on a non-empty canceled stream should not throw
1818PASS Underlying source: calling close after error should throw
1919PASS Underlying source: calling error twice should throw the second time
2020PASS Underlying source: calling error after close should throw

LayoutTests/streams/reference-implementation/readable-stream-cancel-expected.txt

11
2 FAIL ReadableStream cancellation: integration test on an infinite stream derived from a random push source cancel is not implemented
3 FAIL ReadableStream cancellation: cancel(reason) should pass through the given reason to the underlying source cancel is not implemented
4 FAIL ReadableStream cancellation: cancel() on a locked stream should fail and not call the underlying source cancel cancel is not implemented
5 FAIL ReadableStream cancellation: should fulfill promise when cancel callback went fine cancel is not implemented
6 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
7 FAIL ReadableStream cancellation: should reject promise when cancel callback raises an exception cancel is not implemented
8 FAIL ReadableStream cancellation: if the underlying source's cancel method returns a promise, the promise returned by the stream's cancel should fulfill when that one does (1) cancel is not implemented
9 FAIL ReadableStream cancellation: if the underlying source's cancel method returns a promise, the promise returned by the stream's cancel should fulfill when that one does (2) cancel is not implemented
10 FAIL ReadableStream cancellation: if the underlying source's cancel method returns a promise, the promise returned by the stream's cancel should reject when that one does cancel is not implemented
11 FAIL ReadableStream cancellation: cancelling before start finishes should prevent pull() from being called cancel is not implemented
 2FAIL ReadableStream cancellation: integration test on an infinite stream derived from a random push source assert_equals: it returns a promise that is fulfilled when the cancellation finishes expected true but got false
 3FAIL ReadableStream cancellation: cancel(reason) should pass through the given reason to the underlying source assert_equals: the error passed to the underlying source's cancel method should equal the one passed to the stream's cancel expected (object) object "Error: Sorry, it just wasn't meant to be." but got (undefined) undefined
 4PASS ReadableStream cancellation: cancel() on a locked stream should fail and not call the underlying source cancel
 5FAIL ReadableStream cancellation: should fulfill promise when cancel callback went fine assert_true: expected true got false
 6PASS 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
 7FAIL ReadableStream cancellation: should reject promise when cancel callback raises an exception assert_unreached: cancel should reject Reached unreachable code
 8PASS ReadableStream cancellation: if the underlying source's cancel method returns a promise, the promise returned by the stream's cancel should fulfill when that one does (1)
 9FAIL ReadableStream cancellation: if the underlying source's cancel method returns a promise, the promise returned by the stream's cancel should fulfill when that one does (2) assert_true: cancel() return value should be fulfilled only after the promise returned by the underlying source's cancel expected true got false
 10FAIL ReadableStream cancellation: if the underlying source's cancel method returns a promise, the promise returned by the stream's cancel should reject when that one does assert_unreached: cancel() return value should not be rejected Reached unreachable code
 11PASS ReadableStream cancellation: cancelling before start finishes should prevent pull() from being called
1212

LayoutTests/streams/reference-implementation/readable-stream-expected.txt

@@PASS ReadableStream pull should be able to close a stream.
2525PASS ReadableStream: enqueue should throw when the stream is readable but draining
2626PASS ReadableStream: enqueue should throw when the stream is closed
2727PASS ReadableStream: enqueue should throw the stored error when the stream is errored
28 FAIL ReadableStream: should call underlying source methods as methods cancel is not implemented
 28FAIL ReadableStream: should call underlying source methods as methods assert_equals: expected 1 but got 0
2929FAIL ReadableStream strategies: the default strategy should give desiredSize of 1 to start, decreasing by 1 per enqueue assert_equals: expected (number) 1 but got (undefined) undefined
3030FAIL ReadableStream strategies: the default strategy should continue giving desiredSize of 1 if the chunks are read immediately assert_equals: desiredSize should start at 1 expected (number) 1 but got (undefined) undefined
3131PASS ReadableStream integration test: adapting a random push source

LayoutTests/streams/reference-implementation/readable-stream-reader-expected.txt

@@PASS Getting a ReadableStreamReader via getReader should fail if the stream is a
1010PASS Constructing a ReadableStreamReader directly should be OK if the stream is closed
1111PASS Constructing a ReadableStreamReader directly should be OK if the stream is errored
1212PASS Reading from a reader for an empty stream will wait until a chunk is available
13 FAIL cancel() on a reader releases the reader before calling through cancel is not implemented
 13FAIL cancel() on a reader releases the reader before calling through assert_true: expected true got false
1414PASS closed should be fulfilled after stream is closed (.closed access before acquiring)
1515PASS closed should be fulfilled after reader releases its lock (multiple stream locks)
1616PASS Multiple readers can access the stream in sequence
1717PASS Cannot use an already-released reader to unlock a stream again
18 FAIL cancel() on a released reader is a no-op and does not pass through cancel is not implemented
 18PASS cancel() on a released reader is a no-op and does not pass through
1919PASS Getting a second reader after erroring the stream should succeed
2020PASS Garbage-collecting a ReadableStreamReader should not unlock its stream
2121

LayoutTests/streams/reference-implementation/readable-stream-templated-expected.txt

@@FAIL releasing the lock with pending read requests should throw but the read req
1212PASS releasing the lock should cause further read() calls to resolve as if the stream is closed
1313PASS releasing the lock should cause closed to fulfill
1414PASS releasing the lock should cause locked to become false
15 FAIL canceling via the reader should cause the reader to act closed cancel is not implemented
16 FAIL canceling via the stream should fail cancel is not implemented
 15PASS canceling via the reader should cause the reader to act closed
 16PASS canceling via the stream should fail
1717PASS Running templatedRSClosed with ReadableStream (closed via call in start)
18 FAIL cancel() should return a distinct fulfilled promise each time cancel is not implemented
 18PASS cancel() should return a distinct fulfilled promise each time
1919PASS locked should be false
2020PASS getReader() should be OK
2121PASS should be able to acquire multiple readers, since they are all auto-released
2222PASS Running templatedRSClosedReader with ReadableStream (closed via call in start) reader
2323PASS read() should fulfill with { value: undefined, done: true }
2424PASS closed should fulfill with undefined
25 FAIL cancel() should return a distinct fulfilled promise each time cancel is not implemented
 25PASS cancel() should return a distinct fulfilled promise each time
2626PASS Running templatedRSClosed with ReadableStream (closed via cancel)
27 FAIL cancel() should return a distinct fulfilled promise each time cancel is not implemented
28 FAIL locked should be false cancel is not implemented
29 FAIL getReader() should be OK cancel is not implemented
30 FAIL should be able to acquire multiple readers, since they are all auto-released cancel is not implemented
 27PASS cancel() should return a distinct fulfilled promise each time
 28PASS locked should be false
 29PASS getReader() should be OK
 30PASS should be able to acquire multiple readers, since they are all auto-released
3131PASS Running templatedRSClosedReader with ReadableStream (closed via cancel) reader
32 FAIL read() should fulfill with { value: undefined, done: true } cancel is not implemented
33 FAIL closed should fulfill with undefined cancel is not implemented
34 FAIL cancel() should return a distinct fulfilled promise each time cancel is not implemented
 32PASS read() should fulfill with { value: undefined, done: true }
 33PASS closed should fulfill with undefined
 34PASS cancel() should return a distinct fulfilled promise each time
3535PASS Running templatedRSErrored with ReadableStream (errored via call in start)
3636PASS getReader() should return a reader that acts errored
3737PASS locked should be false
3838PASS Running templatedRSErroredSyncOnly with ReadableStream (errored via call in start)
39 FAIL cancel() should return a distinct rejected promise each time cancel is not implemented
40 FAIL reader cancel() should return a distinct rejected promise each time cancel is not implemented
 39PASS cancel() should return a distinct rejected promise each time
 40PASS reader cancel() should return a distinct rejected promise each time
4141PASS should be able to acquire multiple readers, since they are all auto-released
4242PASS Running templatedRSErrored with ReadableStream (errored via returning a rejected promise in start)
4343PASS getReader() should return a reader that acts errored

@@PASS Running templatedRSTwoChunksOpenReader with ReadableStream (two chunks enqu
4949PASS calling read() twice without waiting will eventually give both chunks
5050PASS calling read() twice with waiting will eventually give both chunks
5151PASS read() should return distinct promises each time
52 FAIL cancel() after a read() should still give that single read result cancel is not implemented
 52PASS cancel() after a read() should still give that single read result
5353PASS Running templatedRSTwoChunksClosedReader with ReadableStream (two chunks enqueued, then closed) reader
5454PASS third read(), without waiting, should give { value: undefined, done: true }
5555PASS third read, with waiting, should give { value: undefined, done: true }