[Readable Streams API] Implement ReadableByteStreamController enqueue()
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 7 Feb 2017 09:00:10 +0000 (09:00 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 7 Feb 2017 09:00:10 +0000 (09:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=167786

Patch by Romain Bellessort <romain.bellessort@crf.canon.fr> on 2017-02-07
Reviewed by Youenn Fablet.

Source/WebCore:

Implemented ReadableByteStreamController enqueue() method. Also replaced
@queue by an Array instead of a dedicated queue object (said object is
useful for ReadableStreamDefaultController, but not for ReadableByteStreamController).

Added 8 tests to cover newly added code as well as code that was previously unreachable.

* Modules/streams/ReadableByteStreamController.js:
(enqueue): Implemented as defined by spec.
* Modules/streams/ReadableByteStreamInternals.js:
(privateInitializeReadableByteStreamController): Updated @queue.
(readableByteStreamControllerCancel): Updated @queue.
(readableByteStreamControllerError): Updated @queue.
(readableByteStreamControllerPull): Updated @queue.
(readableByteStreamControllerEnqueue): Added.
(readableByteStreamControllerEnqueueChunkToQueue): Added.
* Modules/streams/ReadableStreamInternals.js:
(readableStreamFulfillReadRequest): Added (used at different places).

LayoutTests:

Added 8 tests to cover newly added code as well as code that was previously unreachable.

* streams/readable-byte-stream-controller-expected.txt:
* streams/readable-byte-stream-controller.js:

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

LayoutTests/ChangeLog
LayoutTests/streams/readable-byte-stream-controller-expected.txt
LayoutTests/streams/readable-byte-stream-controller.js
Source/WebCore/ChangeLog
Source/WebCore/Modules/streams/ReadableByteStreamController.js
Source/WebCore/Modules/streams/ReadableByteStreamInternals.js
Source/WebCore/Modules/streams/ReadableStreamInternals.js

index 853051f..8c382e6 100644 (file)
@@ -1,3 +1,15 @@
+2017-02-07  Romain Bellessort  <romain.bellessort@crf.canon.fr>
+
+        [Readable Streams API] Implement ReadableByteStreamController enqueue()
+        https://bugs.webkit.org/show_bug.cgi?id=167786
+
+        Reviewed by Youenn Fablet.
+
+        Added 8 tests to cover newly added code as well as code that was previously unreachable.
+
+        * streams/readable-byte-stream-controller-expected.txt:
+        * streams/readable-byte-stream-controller.js:
+
 2017-02-07  Chris Dumez  <cdumez@apple.com>
 
         Object.preventExtensions() on a Location object should throw a TypeError
index f49d337..335a16e 100644 (file)
@@ -3,6 +3,11 @@ PASS Creating a ReadableStream with an underlyingSource with type property set t
 PASS ReadableByteStreamController instances should have the correct list of properties 
 PASS Calling error() with a this object different from ReadableByteStreamController should throw a TypeError 
 PASS Calling close() with a this object different from ReadableByteStreamController should throw a TypeError 
+PASS Calling enqueue() with a this object different from ReadableByteStreamController should throw a TypeError 
+PASS Calling enqueue() when close has been requested but not yet performed should throw a TypeError 
+PASS Calling enqueue() when stream is not readable should throw a TypeError 
+PASS Calling enqueue() with a chunk that is not an object should trhow a TypeError 
+PASS Calling enqueue() with a chunk that is not an ArrayBufferView should throw a TypeError 
 PASS Calling error() after calling close() should throw a TypeError 
 PASS Calling error() after calling error() should throw a TypeError 
 PASS Calling close() after calling close() should throw a TypeError 
@@ -11,7 +16,9 @@ PASS Calling read() on a reader associated to a controller that has been errored
 PASS Calling read() on a reader associated to a controller that has been closed should not be rejected 
 PASS Pending reading promise should be rejected if controller is errored (case where autoAllocateChunkSize is undefined) 
 FAIL Pending reading promise should be rejected if controller is errored (case where autoAllocateChunkSize is specified) Can't find private variable: @Uint8Array
-FAIL Calling read() after a chunk has been enqueued should result in obtaining said chunk ReadableByteStreamController enqueue() is not implemented
+PASS Enqueuing a chunk, getting a reader and calling read should result in a promise resolved with said chunk 
+PASS Getting a reader, calling read and enqueuing a chunk should result in the read promise being resolved with said chunk 
+PASS Getting a reader, enqueuing a chunk and finally calling read should result in a promise resolved with said chunk 
 PASS By default initial value of desiredSize should be 1 
 PASS Calling cancel() on a readable ReadableStream that is not locked to a reader should return a promise whose fulfillment handler returns undefined 
 PASS Test that pull is called once when a new ReadableStream is created with a ReadableByteStreamController 
@@ -21,6 +28,11 @@ PASS Creating a ReadableStream with an underlyingSource with type property set t
 PASS ReadableByteStreamController instances should have the correct list of properties 
 PASS Calling error() with a this object different from ReadableByteStreamController should throw a TypeError 
 PASS Calling close() with a this object different from ReadableByteStreamController should throw a TypeError 
+PASS Calling enqueue() with a this object different from ReadableByteStreamController should throw a TypeError 
+PASS Calling enqueue() when close has been requested but not yet performed should throw a TypeError 
+PASS Calling enqueue() when stream is not readable should throw a TypeError 
+PASS Calling enqueue() with a chunk that is not an object should trhow a TypeError 
+PASS Calling enqueue() with a chunk that is not an ArrayBufferView should throw a TypeError 
 PASS Calling error() after calling close() should throw a TypeError 
 PASS Calling error() after calling error() should throw a TypeError 
 PASS Calling close() after calling close() should throw a TypeError 
@@ -29,7 +41,9 @@ PASS Calling read() on a reader associated to a controller that has been errored
 PASS Calling read() on a reader associated to a controller that has been closed should not be rejected 
 PASS Pending reading promise should be rejected if controller is errored (case where autoAllocateChunkSize is undefined) 
 FAIL Pending reading promise should be rejected if controller is errored (case where autoAllocateChunkSize is specified) Can't find private variable: @Uint8Array
-FAIL Calling read() after a chunk has been enqueued should result in obtaining said chunk ReadableByteStreamController enqueue() is not implemented
+PASS Enqueuing a chunk, getting a reader and calling read should result in a promise resolved with said chunk 
+PASS Getting a reader, calling read and enqueuing a chunk should result in the read promise being resolved with said chunk 
+PASS Getting a reader, enqueuing a chunk and finally calling read should result in a promise resolved with said chunk 
 PASS By default initial value of desiredSize should be 1 
 PASS Calling cancel() on a readable ReadableStream that is not locked to a reader should return a promise whose fulfillment handler returns undefined 
 PASS Test that pull is called once when a new ReadableStream is created with a ReadableByteStreamController 
index 232fa05..d0c972e 100644 (file)
@@ -87,6 +87,86 @@ test(function() {
         type: "bytes"
     });
 
+    assert_throws(new TypeError("Can only call ReadableByteStreamController.enqueue on instances of ReadableByteStreamController"),
+        function() { controller.enqueue.apply(rs, new Int8Array(1)); });
+}, "Calling enqueue() with a this object different from ReadableByteStreamController should throw a TypeError");
+
+test(function() {
+    let controller;
+
+    const rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        },
+        type: "bytes"
+    });
+
+    controller.enqueue(new Int8Array(2));
+    controller.close();
+
+    assert_throws(new TypeError("ReadableByteStreamController is requested to close"),
+        function() { controller.enqueue(new Int8Array(1)); });
+}, "Calling enqueue() when close has been requested but not yet performed should throw a TypeError");
+
+test(function() {
+    let controller;
+
+    const rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        },
+        type: "bytes"
+    });
+    controller.close();
+
+    assert_throws(new TypeError("ReadableStream is not readable"),
+        function() {
+            controller.enqueue(new Int8Array(1));
+        });
+}, "Calling enqueue() when stream is not readable should throw a TypeError");
+
+test(function() {
+    let controller;
+
+    const rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        },
+        type: "bytes"
+    });
+
+    const invalidChunk = function() {};
+
+    assert_throws(new TypeError("Provided chunk is not an object"),
+        function() { controller.enqueue(invalidChunk); });
+}, "Calling enqueue() with a chunk that is not an object should trhow a TypeError");
+
+test(function() {
+    let controller;
+
+    const rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        },
+        type: "bytes"
+    });
+
+    const invalidChunk = {};
+
+    assert_throws(new TypeError("Provided chunk is not an object"),
+        function() { controller.enqueue(invalidChunk); });
+}, "Calling enqueue() with a chunk that is not an ArrayBufferView should throw a TypeError");
+
+test(function() {
+    let controller;
+
+    const rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        },
+        type: "bytes"
+    });
+
     assert_throws(new TypeError("ReadableStream is not readable"),
         function() {
             controller.close();
@@ -224,14 +304,55 @@ promise_test(function() {
         type: "bytes"
     });
 
-    controller.enqueue("test");
+    const buffer = new Uint8Array([3]);
+    controller.enqueue(buffer);
 
     return rs.getReader().read().then(
         function(res) {
-            assert_object_equals(res, {value: "test", done: false});
+            assert_object_equals(res, {value: buffer, done: false});
+        }
+    );
+}, "Enqueuing a chunk, getting a reader and calling read should result in a promise resolved with said chunk");
+
+promise_test(function() {
+    let controller;
+
+    const rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        },
+        type: "bytes"
+    });
+
+    let promise = rs.getReader().read();
+    const buffer = new Uint8Array([1]);
+    controller.enqueue(buffer);
+    return promise.then(
+        function(res) {
+            assert_object_equals(res, {value: buffer, done: false});
+        }
+    );
+}, "Getting a reader, calling read and enqueuing a chunk should result in the read promise being resolved with said chunk");
+
+promise_test(function() {
+    let controller;
+
+    const rs = new ReadableStream({
+        start: function(c) {
+            controller = c;
+        },
+        type: "bytes"
+    });
+
+    const reader = rs.getReader();
+    const buffer = new Uint8Array([1]);
+    controller.enqueue(buffer);
+    return reader.read().then(
+        function(res) {
+            assert_object_equals(res, {value: buffer, done: false});
         }
     );
-}, "Calling read() after a chunk has been enqueued should result in obtaining said chunk");
+}, "Getting a reader, enqueuing a chunk and finally calling read should result in a promise resolved with said chunk");
 
 test(function() {
     let controller;
index f7a237b..1b92c72 100644 (file)
@@ -1,3 +1,28 @@
+2017-02-07  Romain Bellessort  <romain.bellessort@crf.canon.fr>
+
+        [Readable Streams API] Implement ReadableByteStreamController enqueue()
+        https://bugs.webkit.org/show_bug.cgi?id=167786
+
+        Reviewed by Youenn Fablet.
+
+        Implemented ReadableByteStreamController enqueue() method. Also replaced
+        @queue by an Array instead of a dedicated queue object (said object is
+        useful for ReadableStreamDefaultController, but not for ReadableByteStreamController).
+
+        Added 8 tests to cover newly added code as well as code that was previously unreachable.
+
+        * Modules/streams/ReadableByteStreamController.js:
+        (enqueue): Implemented as defined by spec.
+        * Modules/streams/ReadableByteStreamInternals.js:
+        (privateInitializeReadableByteStreamController): Updated @queue.
+        (readableByteStreamControllerCancel): Updated @queue.
+        (readableByteStreamControllerError): Updated @queue.
+        (readableByteStreamControllerPull): Updated @queue.
+        (readableByteStreamControllerEnqueue): Added.
+        (readableByteStreamControllerEnqueueChunkToQueue): Added.
+        * Modules/streams/ReadableStreamInternals.js:
+        (readableStreamFulfillReadRequest): Added (used at different places).
+
 2017-02-07  Chris Dumez  <cdumez@apple.com>
 
         Object.preventExtensions() on a Location object should throw a TypeError
index f3d46c5..c2feb15 100644 (file)
@@ -29,8 +29,22 @@ function enqueue(chunk)
 {
     "use strict";
 
-    //FIXME: Implement appropriate behavior.
-    @throwTypeError("ReadableByteStreamController enqueue() is not implemented");
+    if (!@isReadableByteStreamController(this))
+        throw @makeThisTypeError("ReadableByteStreamController", "enqueue");
+
+    if (this.@closeRequested)
+        @throwTypeError("ReadableByteStreamController is requested to close");
+
+    if (this.@controlledReadableStream.@state !== @streamReadable)
+        @throwTypeError("ReadableStream is not readable");
+
+    if (!@isObject(chunk))
+        @throwTypeError("Provided chunk is not an object");
+
+    if (!@ArrayBuffer.@isView(chunk))
+        @throwTypeError("Provided chunk is not an ArrayBuffer view");
+
+    return @readableByteStreamControllerEnqueue(this, chunk);
 }
 
 function error(error)
index 203b493..3e73545 100644 (file)
@@ -42,7 +42,7 @@ function privateInitializeReadableByteStreamController(stream, underlyingByteSou
     this.@pullAgain = false;
     this.@pulling = false;
     @readableByteStreamControllerClearPendingPullIntos(this);
-    this.@queue = @newQueue();
+    this.@queue = [];
     this.@totalQueuedBytes = 0;
     this.@started = false;
     this.@closeRequested = false;
@@ -103,7 +103,7 @@ function readableByteStreamControllerCancel(controller, reason)
 
     if (controller.@pendingPullIntos.length > 0)
         controller.@pendingPullIntos[0].bytesFilled = 0;
-    controller.@queue = @newQueue();
+    controller.@queue = [];
     controller.@totalQueuedBytes = 0;
     return @promiseInvokeOrNoop(controller.@underlyingByteSource, "cancel", [reason]);
 }
@@ -114,7 +114,7 @@ function readableByteStreamControllerError(controller, e)
 
     @assert(controller.@controlledReadableStream.@state === @streamReadable);
     @readableByteStreamControllerClearPendingPullIntos(controller);
-    controller.@queue = @newQueue();
+    controller.@queue = [];
     @readableStreamError(controller.@controlledReadableStream, e);
 }
 
@@ -189,7 +189,7 @@ function readableByteStreamControllerPull(controller)
 
     if (controller.@totalQueuedBytes > 0) {
         @assert(stream.@reader.@readRequests.length === 0);
-        const entry = @dequeueValue(controller.@queue);
+        const entry = controller.@queue.@shift();
         controller.@totalQueuedBytes -= entry.byteLength;
         @readableByteStreamControllerHandleQueueDrain(controller);
         let view;
@@ -271,3 +271,60 @@ function readableByteStreamControllerCallPullIfNeeded(controller)
             @readableByteStreamControllerError(controller, error);
     });
 }
+
+function transferBufferToCurrentRealm(buffer)
+{
+    "use strict";
+
+    // FIXME: Determine what should be done here exactly (what is already existing in current
+    // codebase and what has to be added). According to spec, Transfer operation should be
+    // performed in order to transfer buffer to current realm. For the moment, simply return
+    // received buffer.
+    return buffer;
+}
+
+function readableByteStreamControllerEnqueue(controller, chunk)
+{
+    "use strict";
+
+    const stream = controller.@controlledReadableStream;
+    @assert(!controller.@closeRequested);
+    @assert(stream.@state === @streamReadable);
+    const buffer = chunk.buffer;
+    const byteOffset = chunk.byteOffset;
+    const byteLength = chunk.byteLength;
+    const transferredBuffer = @transferBufferToCurrentRealm(buffer);
+
+    if (@readableStreamHasDefaultReader(stream)) {
+        if (!stream.@reader.@readRequests.length)
+            @readableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
+        else {
+            @assert(!controller.@queue.length);
+            let transferredView = new @Uint8Array(transferredBuffer, byteOffset, byteLength);
+            @readableStreamFulfillReadRequest(stream, transferredView, false);
+        }
+        return;
+    }
+
+    if (@readableStreamHasBYOBReader(stream)) {
+        // FIXME: To be implemented once ReadableStreamBYOBReader has been implemented (for the moment,
+        // test cannot be true).
+        @throwTypeError("ReadableByteStreamController enqueue operation has no support for BYOB reader");
+        return;
+    }
+
+    @assert(!@isReadableStreamLocked(stream));
+    @readableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength);
+}
+
+function readableByteStreamControllerEnqueueChunkToQueue(controller, buffer, byteOffset, byteLength)
+{
+    "use strict";
+
+    controller.@queue.@push({
+        buffer: buffer,
+        byteOffset: byteOffset,
+        byteLength: byteLength
+    });
+    controller.@totalQueuedBytes += byteLength;
+}
index 4b3d61f..87094bf 100644 (file)
@@ -413,6 +413,13 @@ function readableStreamClose(stream)
     reader.@closedPromiseCapability.@resolve.@call();
 }
 
+function readableStreamFulfillReadRequest(stream, chunk, done)
+{
+    "use strict";
+
+    stream.@reader.@readRequests.@shift().@resolve.@call(@undefined, {value: chunk, done: done});
+}
+
 function readableStreamDefaultControllerEnqueue(controller, chunk)
 {
     "use strict";
@@ -422,7 +429,7 @@ function readableStreamDefaultControllerEnqueue(controller, chunk)
     @assert(stream.@state === @streamReadable);
 
     if (@isReadableStreamLocked(stream) && stream.@reader.@readRequests.length) {
-        stream.@reader.@readRequests.@shift().@resolve.@call(@undefined, {value: chunk, done: false});
+        @readableStreamFulfillReadRequest(stream, chunk, false);
         @readableStreamDefaultControllerCallPullIfNeeded(controller);
         return;
     }