[Streams API] Align internal structure of ReadableStream with spec
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 1 Sep 2016 14:14:05 +0000 (14:14 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 1 Sep 2016 14:14:05 +0000 (14:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=160299

Patch by Romain Bellessort <romain.bellessort@crf.canon.fr> on 2016-09-01
Reviewed by Xabier Rodriguez-Calvar.

LayoutTests/imported/w3c:

Aligned internal structure of ReadableStream with spec. Fixed one expectation
that was set to FAIL while it is now PASSing.

* web-platform-tests/streams/readable-streams/general.https-expected.txt:

Source/WebCore:

Aligned internal structure of ReadableStream with spec. Various
internal properties have been moved to ReadableStreamDefaultController.
In addition, various behaviors had to be updated. Several other changes
will have to be performed in order to align with spec, e.g. changing
functions names. This patch does not change them in order to make the
structural changes easier to follow.

No change in functionality except support for 1 specific case where
an error was not thrown while it should have been. Changed corresponding
test expectation (now PASS instead of FAIL).
Modified test expectation: web-platform-tests/streams/readable-streams/general.https-expected.txt

* Modules/fetch/FetchResponse.js:
(initializeFetchResponse): Replaced reference to underlyingSource as no
more a property of readableStream (use readableStreamController instead,
as defined by spec).
* Modules/streams/ReadableStream.js:
(initializeReadableStream): Removed various properties now hanlded by
ReadableStreamDefaultController.
* Modules/streams/ReadableStreamDefaultController.js:
(enqueue): Updated based on new properties repartition between reader and controller.
(error): Updated based on new properties repartition between reader and controller.
(close): Updated based on new properties repartition between reader and controller.
(desiredSize): Updated based on new properties repartition between reader and controller.
* Modules/streams/ReadableStreamInternals.js:
(privateInitializeReadableStreamDefaultController): Added various properties now handled by
ReadableStreamDefaultController, as well as an internal pull function defined by spec.
(readableStreamDefaultControllerError): Added based on spec (error handling at controller level).
(teeReadableStream): Fixed typo and use readableStreamDefaultControllerError instead of errorReadableStream.
(doStructuredClone): Added "use strict";.
(teeReadableStreamPullFunction): Use readableStreamDefaultControllerClose instead of closeReadableStream.
(isReadableStream): Replaced check of underlyingSource by check that object is actually an instance of
ReadableStream (spec requires checking that readableStreamController slot is present, but this cannot
be checked).
(isReadableStreamDefaultReader): Replaced check of ownerReadableStream presence by check of readRequests,
in line with spec.
(isReadableStreamDefaultController): Replaced check of controlledReadableStream presence by check of unerlyingSource,
in line with spec.
(errorReadableStream): Updated based on new properties repartition between reader and controller.
(requestReadableStreamPull): Updated based on new properties repartition between reader and controller.
(readableStreamDefaultControllerGetDesiredSize): Replaces getReadableStreamDesiredSize (size now depends
on controller; new function name aligned with spec).
(cancelReadableStream): Updated based on new properties repartition between reader and controller.
(readableStreamDefaultControllerClose): Added based on spec (closing controller).
(closeReadableStream): Updated based on new properties repartition between reader and controller.
(enqueueInReadableStream): Updated based on new properties repartition between reader and controller.
(readFromReadableStreamDefaultReader): Updated based on new properties repartition between reader and controller.
* bindings/js/WebCoreBuiltinNames.h: Added pull (internal function of ReadableStreamDefaultController)
and readableStreamController (defined by spec)

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

LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/streams/readable-streams/general.https-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/Modules/fetch/FetchResponse.js
Source/WebCore/Modules/streams/ReadableStream.js
Source/WebCore/Modules/streams/ReadableStreamDefaultController.js
Source/WebCore/Modules/streams/ReadableStreamInternals.js
Source/WebCore/bindings/js/WebCoreBuiltinNames.h

index e3d01b1..7ce7f16 100644 (file)
@@ -1,3 +1,16 @@
+2016-09-01  Romain Bellessort  <romain.bellessort@crf.canon.fr>
+
+        [Streams API] Align internal structure of ReadableStream with spec
+        https://bugs.webkit.org/show_bug.cgi?id=160299
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Aligned internal structure of ReadableStream with spec. Fixed one expectation
+        that was set to FAIL while it is now PASSing.
+
+        * web-platform-tests/streams/readable-streams/general.https-expected.txt:
+
+
 2016-08-31  Youenn Fablet  <youenn@apple.com>
 
         [Fetch API] Fetch API should be able to load data URL in Same Origin mode
index d48e5fa..6892591 100644 (file)
@@ -1,7 +1,7 @@
 
 PASS ReadableStream can be constructed with no errors 
 PASS ReadableStream can't be constructed with garbage 
-FAIL ReadableStream can't be constructed with an invalid type assert_throws: constructor should throw when the type is null function "() => new ReadableStream({ type: null })" did not throw
+PASS ReadableStream can't be constructed with an invalid type 
 FAIL ReadableStream instances should have the correct list of properties assert_equals: getReader should have no parameters expected 0 but got 1
 PASS ReadableStream constructor should throw for non-function start arguments 
 PASS ReadableStream constructor can get initial garbage as cancel argument 
@@ -38,7 +38,7 @@ FAIL Load general.js with SharedWorker assert_unreached: SharedWorker is unavail
 FAIL Untitled undefined is not an object (evaluating 'navigator.serviceWorker.getRegistration')
 PASS ReadableStream can be constructed with no errors 
 PASS ReadableStream can't be constructed with garbage 
-FAIL ReadableStream can't be constructed with an invalid type assert_throws: constructor should throw when the type is null function "() => new ReadableStream({ type: null })" did not throw
+PASS ReadableStream can't be constructed with an invalid type 
 FAIL ReadableStream instances should have the correct list of properties assert_equals: getReader should have no parameters expected 0 but got 1
 PASS ReadableStream constructor should throw for non-function start arguments 
 PASS ReadableStream constructor can get initial garbage as cancel argument 
index c9fe856..e4aa23c 100644 (file)
@@ -1,3 +1,60 @@
+2016-09-01  Romain Bellessort  <romain.bellessort@crf.canon.fr>
+
+        [Streams API] Align internal structure of ReadableStream with spec
+        https://bugs.webkit.org/show_bug.cgi?id=160299
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Aligned internal structure of ReadableStream with spec. Various
+        internal properties have been moved to ReadableStreamDefaultController.
+        In addition, various behaviors had to be updated. Several other changes
+        will have to be performed in order to align with spec, e.g. changing
+        functions names. This patch does not change them in order to make the
+        structural changes easier to follow.
+
+        No change in functionality except support for 1 specific case where
+        an error was not thrown while it should have been. Changed corresponding
+        test expectation (now PASS instead of FAIL).
+        Modified test expectation: web-platform-tests/streams/readable-streams/general.https-expected.txt
+
+        * Modules/fetch/FetchResponse.js:
+        (initializeFetchResponse): Replaced reference to underlyingSource as no
+        more a property of readableStream (use readableStreamController instead,
+        as defined by spec).
+        * Modules/streams/ReadableStream.js:
+        (initializeReadableStream): Removed various properties now hanlded by
+        ReadableStreamDefaultController.
+        * Modules/streams/ReadableStreamDefaultController.js:
+        (enqueue): Updated based on new properties repartition between reader and controller. 
+        (error): Updated based on new properties repartition between reader and controller. 
+        (close): Updated based on new properties repartition between reader and controller. 
+        (desiredSize): Updated based on new properties repartition between reader and controller. 
+        * Modules/streams/ReadableStreamInternals.js:
+        (privateInitializeReadableStreamDefaultController): Added various properties now handled by
+        ReadableStreamDefaultController, as well as an internal pull function defined by spec.
+        (readableStreamDefaultControllerError): Added based on spec (error handling at controller level).
+        (teeReadableStream): Fixed typo and use readableStreamDefaultControllerError instead of errorReadableStream.
+        (doStructuredClone): Added "use strict";.
+        (teeReadableStreamPullFunction): Use readableStreamDefaultControllerClose instead of closeReadableStream.
+        (isReadableStream): Replaced check of underlyingSource by check that object is actually an instance of
+        ReadableStream (spec requires checking that readableStreamController slot is present, but this cannot
+        be checked).
+        (isReadableStreamDefaultReader): Replaced check of ownerReadableStream presence by check of readRequests,
+        in line with spec.
+        (isReadableStreamDefaultController): Replaced check of controlledReadableStream presence by check of unerlyingSource,
+        in line with spec.
+        (errorReadableStream): Updated based on new properties repartition between reader and controller.
+        (requestReadableStreamPull): Updated based on new properties repartition between reader and controller.
+        (readableStreamDefaultControllerGetDesiredSize): Replaces getReadableStreamDesiredSize (size now depends
+        on controller; new function name aligned with spec).
+        (cancelReadableStream): Updated based on new properties repartition between reader and controller. 
+        (readableStreamDefaultControllerClose): Added based on spec (closing controller).
+        (closeReadableStream): Updated based on new properties repartition between reader and controller. 
+        (enqueueInReadableStream): Updated based on new properties repartition between reader and controller. 
+        (readFromReadableStreamDefaultReader): Updated based on new properties repartition between reader and controller. 
+        * bindings/js/WebCoreBuiltinNames.h: Added pull (internal function of ReadableStreamDefaultController)
+        and readableStreamController (defined by spec)
+
 2016-09-01  Csaba Osztrogon√°c  <ossy@webkit.org>
 
         Unreviewed, fix the !ENABLE(SVG_FONTS) and !ENABLE(XSLT) build after r205269.
index 9b05ed3..56f3641 100644 (file)
@@ -50,7 +50,7 @@ function initializeFetchResponse(body, init)
             throw new @TypeError("Response cannot have a body with the given status");
 
         // FIXME: Use @isReadableStream once it is no longer guarded by STREAMS_API guard.
-        let isBodyReadableStream = (@isObject(body) && !!body.@underlyingSource);
+        let isBodyReadableStream = (@isObject(body) && !!body.@readableStreamController);
         if (isBodyReadableStream)
           this.@body = body;
 
index 9f16991..69d840e 100644 (file)
@@ -41,27 +41,25 @@ function initializeReadableStream(underlyingSource, strategy)
     if (strategy !== @undefined && !@isObject(strategy))
         throw new @TypeError("ReadableStream constructor takes an object as second argument, if any");
 
-    this.@underlyingSource = underlyingSource;
-
-    this.@queue = @newQueue();
     this.@state = @streamReadable;
-    this.@started = false;
-    this.@closeRequested = false;
-    this.@pullAgain = false;
-    this.@pulling = false;
     this.@reader = @undefined;
     this.@storedError = @undefined;
     this.@disturbed = false;
-    this.@controller = new @ReadableStreamDefaultController(this);
-    this.@strategy = @validateAndNormalizeQueuingStrategy(strategy.size, strategy.highWaterMark);
-
-    @promiseInvokeOrNoopNoCatch(underlyingSource, "start", [this.@controller]).@then(() => {
-        this.@started = true;
-        @requestReadableStreamPull(this);
-    }, (error) => {
-        if (this.@state === @streamReadable)
-            @errorReadableStream(this, error);
-    });
+    // Initialized with null value to enable distinction with undefined case.
+    this.@readableStreamController = null;
+
+    const type = underlyingSource.type;
+    const typeString = @String(type);
+
+    if (typeString === "bytes") {
+         // FIXME: Implement support of ReadableByteStreamController.
+        throw new @TypeError("ReadableByteStreamController is not implemented");
+    } else if (type === @undefined) {
+        if (strategy.highWaterMark === @undefined)
+            strategy.highWaterMark = 1;
+        this.@readableStreamController = new @ReadableStreamDefaultController(this, underlyingSource, strategy.size, strategy.highWaterMark);
+    } else
+        throw new @RangeError("Invalid type for underlying source");
 
     return this;
 }
index 2d49206..a453c7a 100644 (file)
@@ -32,15 +32,13 @@ function enqueue(chunk)
     if (!@isReadableStreamDefaultController(this))
         throw @makeThisTypeError("ReadableStreamDefaultController", "enqueue");
 
-    const stream = this.@controlledReadableStream;
-
-    if (stream.@closeRequested)
-        throw new @TypeError("ReadableStream is requested to close");
+    if (this.@closeRequested)
+        throw new @TypeError("ReadableStreamDefaultController is requested to close");
 
-    if (stream.@state !== @streamReadable)
+    if (this.@controlledReadableStream.@state !== @streamReadable)
         throw new @TypeError("ReadableStream is not readable");
 
-    return @enqueueInReadableStream(stream, chunk);
+    return @enqueueInReadableStream(this, chunk);
 }
 
 function error(error)
@@ -64,14 +62,13 @@ function close()
     if (!@isReadableStreamDefaultController(this))
         throw @makeThisTypeError("ReadableStreamDefaultController", "close");
 
-    const stream = this.@controlledReadableStream;
-    if (stream.@closeRequested)
-        throw new @TypeError("ReadableStream is already requested to close");
+    if (this.@closeRequested)
+        throw new @TypeError("ReadableStreamDefaultController is already requested to close");
 
-    if (stream.@state !== @streamReadable)
+    if (this.@controlledReadableStream.@state !== @streamReadable)
         throw new @TypeError("ReadableStream is not readable");
 
-    @closeReadableStream(stream);
+    @readableStreamDefaultControllerClose(this);
 }
 
 function desiredSize()
@@ -81,5 +78,6 @@ function desiredSize()
     if (!@isReadableStreamDefaultController(this))
         throw @makeGetterTypeError("ReadableStreamDefaultController", "desiredSize");
 
-    return @getReadableStreamDesiredSize(this.@controlledReadableStream);
+    return @readableStreamDefaultControllerGetDesiredSize(this);
 }
+
index 1308875..bd711c0 100644 (file)
@@ -53,19 +53,65 @@ function privateInitializeReadableStreamDefaultReader(stream)
     return this;
 }
 
-function privateInitializeReadableStreamDefaultController(stream)
+function privateInitializeReadableStreamDefaultController(stream, underlyingSource, size, highWaterMark)
 {
     "use strict";
 
     if (!@isReadableStream(stream))
         throw new @TypeError("ReadableStreamDefaultController needs a ReadableStream");
-    if (stream.@controller !== @undefined)
+
+    // readableStreamController is initialized with null value.
+    if (stream.@readableStreamController !== null)
         throw new @TypeError("ReadableStream already has a controller");
+
     this.@controlledReadableStream = stream;
+    this.@underlyingSource = underlyingSource;
+    this.@queue = @newQueue();
+    this.@started = false;
+    this.@closeRequested = false;
+    this.@pullAgain = false;
+    this.@pulling = false;
+    this.@strategy = @validateAndNormalizeQueuingStrategy(size, highWaterMark);
+
+    const controller = this;
+    const startResult = @promiseInvokeOrNoopNoCatch(underlyingSource, "start", [this]).@then(() => {
+        controller.@started = true;
+        @requestReadableStreamPull(controller);
+    }, (error) => {
+        if (stream.@state === @streamReadable)
+            @readableStreamDefaultControllerError(controller, error);
+    });
+
+    this.@pull = function() {
+        "use strict";
+
+        const stream = controller.@controlledReadableStream;
+        if (controller.@queue.content.length) {
+            const chunk = @dequeueValue(controller.@queue);
+            if (controller.@closeRequested && controller.@queue.content.length === 0)
+                @closeReadableStream(stream);
+            else
+                @requestReadableStreamPull(controller);
+            return @Promise.@resolve({value: chunk, done: false});
+        }
+        const pendingPromise = @readableStreamAddReadRequest(stream);
+        @requestReadableStreamPull(controller);
+        return pendingPromise;
+    }
 
     return this;
 }
 
+function readableStreamDefaultControllerError(controller, error)
+{
+    "use strict";
+
+    const stream = controller.@controlledReadableStream;
+    @assert(stream.@state === @streamReadable);
+    controller.@queue = @newQueue();
+    @errorReadableStream(stream, error);
+}
+
 function teeReadableStream(stream, shouldClone)
 {
     "use strict";
@@ -80,7 +126,7 @@ function teeReadableStream(stream, shouldClone)
         canceled1: false,
         canceled2: false,
         reason1: @undefined,
-        reason: @undefined,
+        reason2: @undefined,
     };
 
     teeState.cancelPromiseCapability = @newPromiseCapability(@InternalPromise);
@@ -99,8 +145,8 @@ function teeReadableStream(stream, shouldClone)
     reader.@closedPromiseCapability.@promise.@then(@undefined, function(e) {
         if (teeState.closedOrErrored)
             return;
-        @errorReadableStream(branch1, e);
-        @errorReadableStream(branch2, e);
+        @readableStreamDefaultControllerError(branch1.@readableStreamController, e);
+        @readableStreamDefaultControllerError(branch2.@readableStreamController, e);
         teeState.closedOrErrored = true;
     });
 
@@ -113,6 +159,8 @@ function teeReadableStream(stream, shouldClone)
 
 function doStructuredClone(object)
 {
+    "use strict";
+
     // FIXME: We should implement http://w3c.github.io/html/infrastructure.html#ref-for-structured-clone-4
     // Implementation is currently limited to ArrayBuffer/ArrayBufferView to meet Fetch API needs.
 
@@ -134,16 +182,18 @@ function teeReadableStreamPullFunction(teeState, reader, shouldClone)
             @assert(@isObject(result));
             @assert(typeof result.done === "boolean");
             if (result.done && !teeState.closedOrErrored) {
-                @closeReadableStream(teeState.branch1);
-                @closeReadableStream(teeState.branch2);
+                if (!teeState.canceled1)
+                    @readableStreamDefaultControllerClose(teeState.branch1.@readableStreamController);
+                if (!teeState.canceled2)
+                    @readableStreamDefaultControllerClose(teeState.branch2.@readableStreamController);
                 teeState.closedOrErrored = true;
             }
             if (teeState.closedOrErrored)
                 return;
             if (!teeState.canceled1)
-                @enqueueInReadableStream(teeState.branch1, shouldClone ? @doStructuredClone(result.value) : result.value);
+                @enqueueInReadableStream(teeState.branch1.@readableStreamController, shouldClone ? @doStructuredClone(result.value) : result.value);
             if (!teeState.canceled2)
-                @enqueueInReadableStream(teeState.branch2, shouldClone ? @doStructuredClone(result.value) : result.value);
+                @enqueueInReadableStream(teeState.branch2.@readableStreamController, shouldClone ? @doStructuredClone(result.value) : result.value);
         });
     }
 }
@@ -184,75 +234,90 @@ function isReadableStream(stream)
 {
     "use strict";
 
-    return @isObject(stream) && !!stream.@underlyingSource;
+    // Spec tells to return true only if stream has a readableStreamController internal slot.
+    // However, since it is a private slot, it cannot be checked using hasOwnProperty().
+    // Therefore, readableStreamController is initialized with null value.
+    return @isObject(stream) && stream.@readableStreamController !== @undefined;
 }
 
 function isReadableStreamDefaultReader(reader)
 {
     "use strict";
 
-    // To reset @ownerReadableStream it must be set to null instead of undefined because there is no way to distinguish
-    // between a non-existent slot and an slot set to undefined.
-    return @isObject(reader) && reader.@ownerReadableStream !== @undefined;
+    // Spec tells to return true only if reader has a readRequests internal slot.
+    // However, since it is a private slot, it cannot be checked using hasOwnProperty().
+    // Since readRequests is initialized with an empty array, the following test is ok.
+    return @isObject(reader) && !!reader.@readRequests;
 }
 
 function isReadableStreamDefaultController(controller)
 {
     "use strict";
 
-    return @isObject(controller) && !!controller.@controlledReadableStream;
+    // Spec tells to return true only if controller has an underlyingSource internal slot.
+    // However, since it is a private slot, it cannot be checked using hasOwnProperty().
+    // underlyingSource is obtained in ReadableStream constructor: if undefined, it is set
+    // to an empty object. Therefore, following test is ok.
+    return @isObject(controller) && !!controller.@underlyingSource;
 }
 
 function errorReadableStream(stream, error)
 {
     "use strict";
 
+    @assert(@isReadableStream(stream));
     @assert(stream.@state === @streamReadable);
-    stream.@queue = @newQueue();
-    stream.@storedError = error;
     stream.@state = @streamErrored;
+    stream.@storedError = error;
 
     if (!stream.@reader)
         return;
+
     const reader = stream.@reader;
 
-    const requests = reader.@readRequests;
-    for (let index = 0, length = requests.length; index < length; ++index)
-        requests[index].@reject.@call(@undefined, error);
-    reader.@readRequests = [];
+    if (@isReadableStreamDefaultReader(reader)) {
+        const requests = reader.@readRequests;
+        for (let index = 0, length = requests.length; index < length; ++index)
+            requests[index].@reject.@call(@undefined, error);
+        reader.@readRequests = [];
+    } else
+        // FIXME: Implement ReadableStreamBYOBReader.
+        throw new @TypeError("Only ReadableStreamDefaultReader is currently supported");
 
     reader.@closedPromiseCapability.@reject.@call(@undefined, error);
 }
 
-function requestReadableStreamPull(stream)
+function requestReadableStreamPull(controller)
 {
     "use strict";
 
+    const stream = controller.@controlledReadableStream;
+
     if (stream.@state === @streamClosed || stream.@state === @streamErrored)
         return;
-    if (stream.@closeRequested)
+    if (controller.@closeRequested)
         return;
-    if (!stream.@started)
+    if (!controller.@started)
         return;
-    if ((!@isReadableStreamLocked(stream) || !stream.@reader.@readRequests.length) && @getReadableStreamDesiredSize(stream) <= 0)
+    if ((!@isReadableStreamLocked(stream) || !stream.@reader.@readRequests.length) && @readableStreamDefaultControllerGetDesiredSize(controller) <= 0)
         return;
 
-    if (stream.@pulling) {
-        stream.@pullAgain = true;
+    if (controller.@pulling) {
+        controller.@pullAgain = true;
         return;
     }
 
-    stream.@pulling = true;
+    controller.@pulling = true;
 
-    @promiseInvokeOrNoop(stream.@underlyingSource, "pull", [stream.@controller]).@then(function() {
-        stream.@pulling = false;
-        if (stream.@pullAgain) {
-            stream.@pullAgain = false;
-            @requestReadableStreamPull(stream);
+    @promiseInvokeOrNoop(controller.@underlyingSource, "pull", [controller]).@then(function() {
+        controller.@pulling = false;
+        if (controller.@pullAgain) {
+            controller.@pullAgain = false;
+            @requestReadableStreamPull(controller);
         }
     }, function(error) {
         if (stream.@state === @streamReadable)
-            @errorReadableStream(stream, error);
+            @readableStreamDefaultControllerError(controller, error);
     });
 }
 
@@ -264,11 +329,11 @@ function isReadableStreamLocked(stream)
     return !!stream.@reader;
 }
 
-function getReadableStreamDesiredSize(stream)
+function readableStreamDefaultControllerGetDesiredSize(controller)
 {
    "use strict";
 
-   return stream.@strategy.highWaterMark - stream.@queue.size;
+   return controller.@strategy.highWaterMark - controller.@queue.size;
 }
 
 function cancelReadableStream(stream, reason)
@@ -280,69 +345,78 @@ function cancelReadableStream(stream, reason)
         return @Promise.@resolve();
     if (stream.@state === @streamErrored)
         return @Promise.@reject(stream.@storedError);
-    stream.@queue = @newQueue();
-    @finishClosingReadableStream(stream);
-    return @promiseInvokeOrNoop(stream.@underlyingSource, "cancel", [reason]).@then(function() { });
+    @closeReadableStream(stream);
+    // FIXME: Fix below, which is a temporary solution to the case where controller is undefined.
+    // This issue is due to the fact that in previous version of the spec, cancel was associated
+    // to underlyingSource, whereas in new version it is associated to controller. As this patch
+    // does not yet fully implement the new version, this solution is used.
+    const controller = stream.@readableStreamController;
+    var underlyingSource = @undefined;
+    if (controller !== @undefined)
+        underlyingSource = controller.@underlyingSource;
+    return @promiseInvokeOrNoop(underlyingSource, "cancel", [reason]).@then(function() { });
 }
 
-function finishClosingReadableStream(stream)
+
+function readableStreamDefaultControllerClose(controller)
 {
     "use strict";
 
+    const stream = controller.@controlledReadableStream;
+    @assert(!controller.@closeRequested);
     @assert(stream.@state === @streamReadable);
-    stream.@state = @streamClosed;
-    const reader = stream.@reader;
-    if (!reader)
-        return;
-
-    const requests = reader.@readRequests;
-    for (let index = 0, length = requests.length; index < length; ++index)
-        requests[index].@resolve.@call(@undefined, {value:@undefined, done: true});
-    reader.@readRequests = [];
-    reader.@closedPromiseCapability.@resolve.@call();
+    controller.@closeRequested = true;
+    if (controller.@queue.content.length === 0)
+        @closeReadableStream(stream);
 }
 
 function closeReadableStream(stream)
 {
     "use strict";
 
-    @assert(!stream.@closeRequested);
-    @assert(stream.@state !== @streamErrored);
-    if (stream.@state === @streamClosed)
+    @assert(stream.@state === @streamReadable);
+    stream.@state = @streamClosed;
+    const reader = stream.@reader;
+
+    if (!reader)
         return;
-    stream.@closeRequested = true;
-    if (!stream.@queue.content.length)
-        @finishClosingReadableStream(stream);
+
+    if (@isReadableStreamDefaultReader(reader)) {
+        const requests = reader.@readRequests;
+        for (let index = 0, length = requests.length; index < length; ++index)
+            requests[index].@resolve.@call(@undefined, {value:@undefined, done: true});
+        reader.@readRequests = [];
+    }
+
+    reader.@closedPromiseCapability.@resolve.@call();
 }
 
-function enqueueInReadableStream(stream, chunk)
+function enqueueInReadableStream(controller, chunk)
 {
     "use strict";
 
-    @assert(!stream.@closeRequested);
-    @assert(stream.@state !== @streamErrored);
-    if (stream.@state === @streamClosed)
-        return;
+    const stream = controller.@controlledReadableStream;
+    @assert(!controller.@closeRequested);
+    @assert(stream.@state === @streamReadable);
+
     if (@isReadableStreamLocked(stream) && stream.@reader.@readRequests.length) {
         stream.@reader.@readRequests.@shift().@resolve.@call(@undefined, {value: chunk, done: false});
-        @requestReadableStreamPull(stream);
+        @requestReadableStreamPull(controller);
         return;
     }
+
     try {
-        let size = 1;
-        if (stream.@strategy.size) {
-            size = @Number(stream.@strategy.size(chunk));
-            if (!@isFinite(size) || size < 0)
-                throw new @RangeError("Chunk size is not valid");
-        }
-        @enqueueValueWithSize(stream.@queue, chunk, size);
+        let chunkSize = 1;
+        if (controller.@strategy.size !== @undefined)
+            chunkSize = controller.@strategy.size(chunk);
+        @enqueueValueWithSize(controller.@queue, chunk, chunkSize);
     }
     catch(error) {
         if (stream.@state === @streamReadable)
-            @errorReadableStream(stream, error);
+            @readableStreamDefaultControllerError(controller, error);
         throw error;
     }
-    @requestReadableStreamPull(stream);
+    @requestReadableStreamPull(controller);
 }
 
 function readFromReadableStreamDefaultReader(reader)
@@ -353,8 +427,8 @@ function readFromReadableStreamDefaultReader(reader)
     @assert(!!stream);
 
     // Native sources may want to start enqueueing at the time of the first read request.
-    if (!stream.@disturbed && stream.@state === @streamReadable && stream.@underlyingSource.@firstReadCallback)
-        stream.@underlyingSource.@firstReadCallback();
+    if (!stream.@disturbed && stream.@state === @streamReadable && stream.@readableStreamController.@underlyingSource.@firstReadCallback)
+        stream.@readableStreamController.@underlyingSource.@firstReadCallback();
 
     stream.@disturbed = true;
     if (stream.@state === @streamClosed)
@@ -362,18 +436,21 @@ function readFromReadableStreamDefaultReader(reader)
     if (stream.@state === @streamErrored)
         return @Promise.@reject(stream.@storedError);
     @assert(stream.@state === @streamReadable);
-    if (stream.@queue.content.length) {
-        const chunk = @dequeueValue(stream.@queue);
-        if (stream.@closeRequested && stream.@queue.content.length === 0)
-            @finishClosingReadableStream(stream);
-        else
-            @requestReadableStreamPull(stream);
-        return @Promise.@resolve({value: chunk, done: false});
-    }
-    const readPromiseCapability = @newPromiseCapability(@Promise);
-    reader.@readRequests.@push(readPromiseCapability);
-    @requestReadableStreamPull(stream);
-    return readPromiseCapability.@promise;
+
+    return stream.@readableStreamController.@pull();
+}
+
+function readableStreamAddReadRequest(stream)
+{
+    "use strict";
+
+    @assert(@isReadableStreamDefaultReader(stream.@reader));
+    @assert(stream.@state == @streamReadable);
+
+    const readRequest = @newPromiseCapability(@Promise);
+    stream.@reader.@readRequests.@push(readRequest);
+
+    return readRequest.@promise;
 }
 
 function isReadableStreamDisturbed(stream)
index 6271e57..8197d42 100644 (file)
@@ -61,6 +61,7 @@ namespace WebCore {
     macro(operations) \
     macro(ownerReadableStream) \
     macro(privateGetStats) \
+    macro(pull) \
     macro(pulling) \
     macro(pullAgain) \
     macro(queue) \
@@ -71,6 +72,7 @@ namespace WebCore {
     macro(queuedSetRemoteDescription) \
     macro(reader) \
     macro(readRequests) \
+    macro(readableStreamController) \
     macro(readyPromiseCapability) \
     macro(removeTrack) \
     macro(responseCacheIsValid) \