https://bugs.webkit.org/show_bug.cgi?id=145210
Reviewed by Darin Adler.
Source/WebCore:
Added possibility to enqueue any JSValue within ReadableJSStream.
They are stored in a Vector of strongified JSValue.
Added support for streams that contain data but are asked to close.
This is done through m_closeRequested boolean and splitting actual closing of the stream from changeStateToClosed().
Chunk size and backpressure mechanism is not yet implemented.
Neither is pulling once enqueued data is processed.
Covered by rebased tests.
* Modules/streams/ReadableStream.cpp:
(WebCore::ReadableStream::changeStateToClosed): Split method with newly added close().
(WebCore::ReadableStream::close): Does the actual closing of stream once stream has no more values.
(WebCore::ReadableStream::read): Close the stream when stream is emptied and close is requested.
(WebCore::ReadableStream::resolveReadCallback): Added to enable ReadableJSStream to resolve read callbacks immediatly at enqueue time.
* Modules/streams/ReadableStream.h:
(WebCore::ReadableStream::isErrored): Getter added for the custom binding code.
(WebCore::ReadableStream::isCloseRequested): Ditto.
* bindings/js/JSReadableStreamControllerCustom.cpp:
(WebCore::JSReadableStreamController::enqueue): binding code for enqueue, taking care of raising exception if readable stream cannot enqueue.
* bindings/js/ReadableJSStream.cpp:
(WebCore::ReadableJSStream::hasValue):
(WebCore::ReadableJSStream::read):
(WebCore::ReadableJSStream::enqueue):
* bindings/js/ReadableJSStream.h:
LayoutTests:
* streams/reference-implementation/bad-underlying-sources-expected.txt:
* streams/reference-implementation/count-queuing-strategy-expected.txt:
* streams/reference-implementation/count-queuing-strategy.html:
* 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@185197
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2015-06-04 Xabier Rodriguez Calvar <calvaris@igalia.com> and Youenn Fablet <youenn.fablet@crf.canon.fr>
+
+ [Streams API] Implement ReadableStreamController enqueue
+ https://bugs.webkit.org/show_bug.cgi?id=145210
+
+ Reviewed by Darin Adler.
+
+ * streams/reference-implementation/bad-underlying-sources-expected.txt:
+ * streams/reference-implementation/count-queuing-strategy-expected.txt:
+ * streams/reference-implementation/count-queuing-strategy.html:
+ * 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-03 Zalan Bujtas <zalan@apple.com>
Use borderBoxRect instead of contentBoxRect for backdrop filter.
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 assert_object_equals: read() should read the enqueued chunk property "done" expected object "[object Object]" got object "[object Object]"
+PASS Underlying source: calling close twice on a non-empty stream should throw the second time
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
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
-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
+FAIL Correctly governs the return value of a ReadableStream's enqueue function (HWM = 0) assert_equals: After 0 reads, 1st enqueue should return false (queue now contains 1 chunk) expected (boolean) false but got (undefined) undefined
+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 (boolean) true but got (undefined) undefined
+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 (boolean) true but got (undefined) undefined
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)', { timeout: 50 });
+var test1 = async_test('Correctly governs the return value of a ReadableStream\'s enqueue function (HWM = 0)');
test1.step(function() {
var enqueue;
var rs = new ReadableStream({
PASS ReadableStream constructor can get initial garbage as strategy argument
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. assert_object_equals: value read should be the one enqueued property "done" expected object "[object Object]" got object "[object Object]"
+PASS ReadableStream should be able to enqueue different objects.
PASS ReadableStream: if start throws an error, it should be re-thrown
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
+FAIL ReadableStream: should not call pull until the previous pull call's promise fulfills assert_equals: pull should have been called once after start, but not yet have been called a second time expected 1 but got 0
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
-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: enqueue should throw when the stream is readable but draining assert_equals: the first enqueue should return true expected (boolean) true but got (undefined) undefined
+PASS ReadableStream: enqueue should throw when the stream is closed
+PASS ReadableStream: enqueue should throw the stored error when the stream is errored
+FAIL ReadableStream: should call underlying source methods as methods releaseLock is not implemented
+FAIL ReadableStream strategies: the default strategy should return false for all but the first enqueue call assert_equals: first enqueue should return true expected (boolean) true but got (undefined) undefined
+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 (boolean) true but got (undefined) undefined
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
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
-TIMEOUT Reading from a reader for an empty stream will wait until a chunk is available Test timed out
+PASS Reading from a reader for an empty stream will wait until a chunk is available
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
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', { timeout: 50 });
+var test1 = async_test('Reading from a reader for an empty stream will wait until a chunk is available');
test1.step(function() {
var controller;
var rs = new ReadableStream({
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 templatedRSTwoChunksOpenReader with ReadableStream (two chunks enqueued, still open) reader
-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 calling read() twice without waiting will eventually give both chunks
+PASS calling read() twice with waiting will eventually give both chunks
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 } 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 third read(), without waiting, should give { value: undefined, done: true }
+PASS third read, with waiting, should give { value: undefined, done: true }
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
test(function() {
}, 'Running templatedRSTwoChunksOpenReader with ' + label);
- var test1 = async_test('calling read() twice without waiting will eventually give both chunks', { timeout: 50 });
+ var test1 = async_test('calling read() twice without waiting will eventually give both chunks');
test1.step(function() {
var { reader } = factory();
var promiseCount = 0;
// }), standardTimeout);
// });
-var test11 = async_test('ReadableStream: should not call pull until the previous pull call\'s promise fulfills', { timeout: 50 });
+var test11 = async_test('ReadableStream: should not call pull until the previous pull call\'s promise fulfills');
test11.step(function() {
var resolve;
var returnedPromise;
});
}, 'ReadableStream: enqueue should throw the stored error when the stream is errored');
-var test16 = async_test('ReadableStream: should call underlying source methods as methods', { timeout: 50 });
+var test16 = async_test('ReadableStream: should call underlying source methods as methods');
test16.step(function() {
var startCalled = 0;
var pullCalled = 0;
2015-06-04 Xabier Rodriguez Calvar <calvaris@igalia.com> and Youenn Fablet <youenn.fablet@crf.canon.fr>
+ [Streams API] Implement ReadableStreamController enqueue
+ https://bugs.webkit.org/show_bug.cgi?id=145210
+
+ Reviewed by Darin Adler.
+
+ Added possibility to enqueue any JSValue within ReadableJSStream.
+ They are stored in a Vector of strongified JSValue.
+
+ Added support for streams that contain data but are asked to close.
+ This is done through m_closeRequested boolean and splitting actual closing of the stream from changeStateToClosed().
+
+ Chunk size and backpressure mechanism is not yet implemented.
+ Neither is pulling once enqueued data is processed.
+
+ Covered by rebased tests.
+
+ * Modules/streams/ReadableStream.cpp:
+ (WebCore::ReadableStream::changeStateToClosed): Split method with newly added close().
+ (WebCore::ReadableStream::close): Does the actual closing of stream once stream has no more values.
+ (WebCore::ReadableStream::read): Close the stream when stream is emptied and close is requested.
+ (WebCore::ReadableStream::resolveReadCallback): Added to enable ReadableJSStream to resolve read callbacks immediatly at enqueue time.
+ * Modules/streams/ReadableStream.h:
+ (WebCore::ReadableStream::isErrored): Getter added for the custom binding code.
+ (WebCore::ReadableStream::isCloseRequested): Ditto.
+ * bindings/js/JSReadableStreamControllerCustom.cpp:
+ (WebCore::JSReadableStreamController::enqueue): binding code for enqueue, taking care of raising exception if readable stream cannot enqueue.
+ * bindings/js/ReadableJSStream.cpp:
+ (WebCore::ReadableJSStream::hasValue):
+ (WebCore::ReadableJSStream::read):
+ (WebCore::ReadableJSStream::enqueue):
+ * bindings/js/ReadableJSStream.h:
+
+2015-06-04 Xabier Rodriguez Calvar <calvaris@igalia.com> and Youenn Fablet <youenn.fablet@crf.canon.fr>
+
[Streams API] ReadableJSStream does not need a ReadableStreamSource
https://bugs.webkit.org/show_bug.cgi?id=145601
void ReadableStream::changeStateToClosed()
{
- if (m_state != State::Readable)
+ ASSERT(!m_closeRequested);
+ ASSERT(m_state != State::Errored);
+
+ m_closeRequested = true;
+
+ if (m_state != State::Readable || hasValue())
return;
+ close();
+}
+
+void ReadableStream::close()
+{
m_state = State::Closed;
if (m_reader)
}
if (hasValue()) {
successCallback(read());
+ if (m_closeRequested && !hasValue())
+ close();
return;
}
m_readRequests.append({ WTF::move(successCallback), WTF::move(endCallback), WTF::move(failureCallback) });
// FIXME: We should try to pull.
}
+bool ReadableStream::resolveReadCallback(JSC::JSValue value)
+{
+ if (m_readRequests.isEmpty())
+ return false;
+
+ m_readRequests.first().successCallback(value);
+ m_readRequests.remove(0);
+ return true;
+}
+
void ReadableStream::start()
{
notImplemented();
const ReadableStreamReader* reader() const { return m_reader.get(); }
bool isLocked() const { return !!m_reader; }
+ bool isErrored() const { return m_state == State::Errored; }
bool isReadable() const { return m_state == State::Readable; }
+ bool isCloseRequested() const { return m_closeRequested; }
virtual JSC::JSValue error() = 0;
protected:
explicit ReadableStream(ScriptExecutionContext&);
+ bool resolveReadCallback(JSC::JSValue);
+
private:
// ActiveDOMObject API.
const char* activeDOMObjectName() const override;
bool canSuspendForPageCache() const override;
void clearCallbacks();
+ void close();
virtual bool hasValue() const = 0;
virtual JSC::JSValue read() = 0;
};
Vector<ReadCallbacks> m_readRequests;
+ bool m_closeRequested { false };
State m_state { State::Readable };
};
#if ENABLE(STREAMS_API)
#include "JSDOMBinding.h"
-#include "NotImplemented.h"
#include "ReadableJSStream.h"
#include <runtime/Error.h>
return jsUndefined();
}
-JSValue JSReadableStreamController::enqueue(ExecState*)
+JSValue JSReadableStreamController::enqueue(ExecState* exec)
{
- notImplemented();
- return jsBoolean(false);
+ ReadableJSStream& stream = impl().stream();
+ if (stream.isErrored())
+ return exec->vm().throwException(exec, stream.error());
+ if (stream.isCloseRequested())
+ return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Calling enqueue on a stream which is closing")));
+ stream.enqueue(*exec);
+ return jsUndefined();
}
JSValue JSReadableStreamController::error(ExecState* exec)
bool ReadableJSStream::hasValue() const
{
- notImplemented();
- return false;
+ return m_chunkQueue.size();
}
JSValue ReadableJSStream::read()
{
- notImplemented();
- return jsUndefined();
+ ASSERT(hasValue());
+
+ return m_chunkQueue.takeFirst().get();
+}
+
+void ReadableJSStream::enqueue(ExecState& exec)
+{
+ ASSERT(!isCloseRequested());
+
+ if (!isReadable())
+ return;
+
+ JSValue chunk = exec.argumentCount() ? exec.argument(0) : jsUndefined();
+ if (resolveReadCallback(chunk))
+ return;
+
+ m_chunkQueue.append(JSC::Strong<JSC::Unknown>(exec.vm(), chunk));
+ // FIXME: Compute chunk size.
+ // FIXME: Add pulling of data here and also when data is passed to resolve callback.
}
} // namespace WebCore
#include <heap/StrongInlines.h>
#include <runtime/JSCJSValue.h>
#include <runtime/PrivateName.h>
+#include <wtf/Deque.h>
#include <wtf/Ref.h>
namespace WebCore {
void storeError(JSC::ExecState&);
JSC::JSValue error() { return m_error.get(); }
+ void enqueue(JSC::ExecState&);
+
private:
ReadableJSStream(ScriptExecutionContext&, JSC::ExecState&, JSC::JSObject*);
std::unique_ptr<ReadableStreamController> m_controller;
JSC::Strong<JSC::Unknown> m_error;
JSC::Strong<JSC::JSObject> m_source;
+ Deque<JSC::Strong<JSC::Unknown>> m_chunkQueue;
};
} // namespace WebCore