summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/streams
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/Modules/streams
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/Modules/streams')
-rw-r--r--Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.idl37
-rw-r--r--Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.js46
-rw-r--r--Source/WebCore/Modules/streams/CountQueuingStrategy.idl37
-rw-r--r--Source/WebCore/Modules/streams/CountQueuingStrategy.js45
-rw-r--r--Source/WebCore/Modules/streams/ReadableByteStreamController.idl43
-rw-r--r--Source/WebCore/Modules/streams/ReadableByteStreamController.js95
-rw-r--r--Source/WebCore/Modules/streams/ReadableByteStreamInternals.js330
-rw-r--r--Source/WebCore/Modules/streams/ReadableStream.idl45
-rw-r--r--Source/WebCore/Modules/streams/ReadableStream.js229
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamDefaultController.idl42
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamDefaultController.js82
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamDefaultReader.idl42
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamDefaultReader.js77
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamInternals.js503
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamSource.h121
-rw-r--r--Source/WebCore/Modules/streams/ReadableStreamSource.idl40
-rw-r--r--Source/WebCore/Modules/streams/StreamInternals.js129
-rw-r--r--Source/WebCore/Modules/streams/WritableStream.idl42
-rw-r--r--Source/WebCore/Modules/streams/WritableStream.js189
-rw-r--r--Source/WebCore/Modules/streams/WritableStreamInternals.js135
20 files changed, 2309 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.idl b/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.idl
new file mode 100644
index 000000000..ac6ba9495
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.idl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Canon Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+ JSBuiltin,
+ Constructor,
+ Conditional=READABLE_STREAM_API|WRITABLE_STREAM_API,
+ Exposed=(Window,Worker),
+] interface ByteLengthQueuingStrategy {
+ double size();
+};
diff --git a/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.js b/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.js
new file mode 100644
index 000000000..3dd793700
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ByteLengthQueuingStrategy.js
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @conditional=ENABLE(READABLE_STREAM_API) || ENABLE(WRITABLE_STREAM_API)
+
+function size(chunk)
+{
+ "use strict";
+
+ return chunk.byteLength;
+}
+
+function initializeByteLengthQueuingStrategy(parameters)
+{
+ "use strict";
+
+ @Object.@defineProperty(this, "highWaterMark", {
+ value: parameters.highWaterMark,
+ configurable: true,
+ enumerable: true,
+ writable: true
+ });
+}
diff --git a/Source/WebCore/Modules/streams/CountQueuingStrategy.idl b/Source/WebCore/Modules/streams/CountQueuingStrategy.idl
new file mode 100644
index 000000000..3eee7a284
--- /dev/null
+++ b/Source/WebCore/Modules/streams/CountQueuingStrategy.idl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Canon Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+ JSBuiltin,
+ Constructor,
+ Conditional=READABLE_STREAM_API|WRITABLE_STREAM_API,
+ Exposed=(Window,Worker),
+] interface CountQueuingStrategy {
+ double size();
+};
diff --git a/Source/WebCore/Modules/streams/CountQueuingStrategy.js b/Source/WebCore/Modules/streams/CountQueuingStrategy.js
new file mode 100644
index 000000000..ab6697836
--- /dev/null
+++ b/Source/WebCore/Modules/streams/CountQueuingStrategy.js
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @conditional=ENABLE(READABLE_STREAM_API) || ENABLE(WRITABLE_STREAM_API)
+
+function size()
+{
+ "use strict";
+
+ return 1;
+}
+
+function initializeCountQueuingStrategy(parameters)
+{
+ "use strict";
+
+ @Object.@defineProperty(this, "highWaterMark", {
+ value: parameters.highWaterMark,
+ configurable: true,
+ enumerable: true,
+ writable: true
+ });
+}
diff --git a/Source/WebCore/Modules/streams/ReadableByteStreamController.idl b/Source/WebCore/Modules/streams/ReadableByteStreamController.idl
new file mode 100644
index 000000000..d0dc23f01
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableByteStreamController.idl
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Canon Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+ Conditional=READABLE_STREAM_API&READABLE_BYTE_STREAM_API,
+ CustomConstructor(ReadableStream stream, any underlyingByteSource, unsigned long highWaterMark),
+ Exposed=(Window,Worker),
+ JSBuiltin,
+ NoInterfaceObject
+] interface ReadableByteStreamController {
+ [NotEnumerable] void enqueue(optional any chunk);
+ [NotEnumerable] void close();
+ [NotEnumerable] void error(optional any error);
+
+ //FIXME: Add byobRequest when implemented.
+ [NotEnumerable] readonly attribute double desiredSize;
+};
diff --git a/Source/WebCore/Modules/streams/ReadableByteStreamController.js b/Source/WebCore/Modules/streams/ReadableByteStreamController.js
new file mode 100644
index 000000000..c2feb1579
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableByteStreamController.js
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 Canon Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @conditional=ENABLE(READABLE_STREAM_API) && ENABLE(READABLE_BYTE_STREAM_API)
+
+function enqueue(chunk)
+{
+ "use strict";
+
+ 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)
+{
+ "use strict";
+
+ if (!@isReadableByteStreamController(this))
+ throw @makeThisTypeError("ReadableByteStreamController", "error");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ @readableByteStreamControllerError(this, error);
+}
+
+function close()
+{
+ "use strict";
+
+ if (!@isReadableByteStreamController(this))
+ throw @makeThisTypeError("ReadableByteStreamController", "close");
+
+ if (this.@closeRequested)
+ @throwTypeError("Close has already been requested");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ @readableByteStreamControllerClose(this);
+}
+
+function byobRequest()
+{
+ "use strict";
+
+ //FIXME: Implement appropriate behavior.
+ @throwTypeError("ReadableByteStreamController byobRequest is not implemented");
+}
+
+function desiredSize()
+{
+ "use strict";
+
+ if (!@isReadableByteStreamController(this))
+ throw @makeGetterTypeError("ReadableByteStreamController", "desiredSize");
+
+ return @readableByteStreamControllerGetDesiredSize(this);
+}
diff --git a/Source/WebCore/Modules/streams/ReadableByteStreamInternals.js b/Source/WebCore/Modules/streams/ReadableByteStreamInternals.js
new file mode 100644
index 000000000..3e7354560
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableByteStreamInternals.js
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2016 Canon Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @conditional=ENABLE(READABLE_STREAM_API) && ENABLE(READABLE_BYTE_STREAM_API)
+// @internal
+
+function privateInitializeReadableByteStreamController(stream, underlyingByteSource, highWaterMark)
+{
+ "use strict";
+
+ if (!@isReadableStream(stream))
+ @throwTypeError("ReadableByteStreamController needs a ReadableStream");
+
+ // readableStreamController is initialized with null value.
+ if (stream.@readableStreamController !== null)
+ @throwTypeError("ReadableStream already has a controller");
+
+ this.@controlledReadableStream = stream;
+ this.@underlyingByteSource = underlyingByteSource;
+ this.@pullAgain = false;
+ this.@pulling = false;
+ @readableByteStreamControllerClearPendingPullIntos(this);
+ this.@queue = [];
+ this.@totalQueuedBytes = 0;
+ this.@started = false;
+ this.@closeRequested = false;
+
+ let hwm = @Number(highWaterMark);
+ if (@isNaN(hwm) || hwm < 0)
+ @throwRangeError("highWaterMark value is negative or not a number");
+ this.@strategyHWM = hwm;
+
+ let autoAllocateChunkSize = underlyingByteSource.autoAllocateChunkSize;
+ if (autoAllocateChunkSize !== @undefined) {
+ autoAllocateChunkSize = @Number(autoAllocateChunkSize);
+ if (autoAllocateChunkSize <= 0 || autoAllocateChunkSize === @Number.POSITIVE_INFINITY || autoAllocateChunkSize === @Number.NEGATIVE_INFINITY)
+ @throwRangeError("autoAllocateChunkSize value is negative or equal to positive or negative infinity");
+ }
+ this.@autoAllocateChunkSize = autoAllocateChunkSize;
+ this.@pendingPullIntos = [];
+
+ const controller = this;
+ const startResult = @promiseInvokeOrNoopNoCatch(underlyingByteSource, "start", [this]).@then(() => {
+ controller.@started = true;
+ @assert(!controller.@pulling);
+ @assert(!controller.@pullAgain);
+ @readableByteStreamControllerCallPullIfNeeded(controller);
+ }, (error) => {
+ if (stream.@state === @streamReadable)
+ @readableByteStreamControllerError(controller, error);
+ });
+
+ this.@cancel = @readableByteStreamControllerCancel;
+ this.@pull = @readableByteStreamControllerPull;
+
+ return this;
+}
+
+function isReadableByteStreamController(controller)
+{
+ "use strict";
+
+ // Same test mechanism as in isReadableStreamDefaultController (ReadableStreamInternals.js).
+ // See corresponding function for explanations.
+ return @isObject(controller) && !!controller.@underlyingByteSource;
+}
+
+function isReadableStreamBYOBReader(reader)
+{
+ "use strict";
+
+ // FIXME: Since BYOBReader is not yet implemented, always return false.
+ // To be implemented at the same time as BYOBReader (see isReadableStreamDefaultReader
+ // to apply same model).
+ return false;
+}
+
+function readableByteStreamControllerCancel(controller, reason)
+{
+ "use strict";
+
+ if (controller.@pendingPullIntos.length > 0)
+ controller.@pendingPullIntos[0].bytesFilled = 0;
+ controller.@queue = [];
+ controller.@totalQueuedBytes = 0;
+ return @promiseInvokeOrNoop(controller.@underlyingByteSource, "cancel", [reason]);
+}
+
+function readableByteStreamControllerError(controller, e)
+{
+ "use strict";
+
+ @assert(controller.@controlledReadableStream.@state === @streamReadable);
+ @readableByteStreamControllerClearPendingPullIntos(controller);
+ controller.@queue = [];
+ @readableStreamError(controller.@controlledReadableStream, e);
+}
+
+function readableByteStreamControllerClose(controller)
+{
+ "use strict";
+
+ @assert(!controller.@closeRequested);
+ @assert(controller.@controlledReadableStream.@state === @streamReadable);
+
+ if (controller.@totalQueuedBytes > 0) {
+ controller.@closeRequested = true;
+ return;
+ }
+
+ if (controller.@pendingPullIntos.length > 0) {
+ if (controller.@pendingPullIntos[0].bytesFilled > 0) {
+ const e = new @TypeError("Close requested while there remain pending bytes");
+ @readableByteStreamControllerError(controller, e);
+ throw e;
+ }
+ }
+
+ @readableStreamClose(controller.@controlledReadableStream);
+}
+
+function readableByteStreamControllerClearPendingPullIntos(controller)
+{
+ "use strict";
+
+ // FIXME: To be implemented in conjunction with ReadableStreamBYOBRequest.
+}
+
+function readableByteStreamControllerGetDesiredSize(controller)
+{
+ "use strict";
+
+ return controller.@strategyHWM - controller.@totalQueuedBytes;
+}
+
+function readableStreamHasBYOBReader(stream)
+{
+ "use strict";
+
+ return stream.@reader !== @undefined && @isReadableStreamBYOBReader(stream.@reader);
+}
+
+function readableStreamHasDefaultReader(stream)
+{
+ "use strict";
+
+ return stream.@reader !== @undefined && @isReadableStreamDefaultReader(stream.@reader);
+}
+
+function readableByteStreamControllerHandleQueueDrain(controller) {
+
+ "use strict";
+
+ @assert(controller.@controlledReadableStream.@state === @streamReadable);
+ if (!controller.@totalQueuedBytes && controller.@closeRequested)
+ @readableStreamClose(controller.@controlledReadableStream);
+ else
+ @readableByteStreamControllerCallPullIfNeeded(controller);
+}
+
+function readableByteStreamControllerPull(controller)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ @assert(@readableStreamHasDefaultReader(stream));
+
+ if (controller.@totalQueuedBytes > 0) {
+ @assert(stream.@reader.@readRequests.length === 0);
+ const entry = controller.@queue.@shift();
+ controller.@totalQueuedBytes -= entry.byteLength;
+ @readableByteStreamControllerHandleQueueDrain(controller);
+ let view;
+ try {
+ view = new @Uint8Array(entry.buffer, entry.byteOffset, entry.byteLength);
+ } catch (error) {
+ return @Promise.@reject(error);
+ }
+ return @Promise.@resolve({value: view, done: false});
+ }
+
+ if (controller.@autoAllocateChunkSize !== @undefined) {
+ let buffer;
+ try {
+ buffer = new @ArrayBuffer(controller.@autoAllocateChunkSize);
+ } catch (error) {
+ return @Promise.@reject(error);
+ }
+ const pullIntoDescriptor = {
+ buffer,
+ byteOffset: 0,
+ byteLength: controller.@autoAllocateChunkSize,
+ bytesFilled: 0,
+ elementSize: 1,
+ ctor: @Uint8Array,
+ readerType: 'default'
+ };
+ controller.@pendingPullIntos.@push(pullIntoDescriptor);
+ }
+
+ const promise = @readableStreamAddReadRequest(stream);
+ @readableByteStreamControllerCallPullIfNeeded(controller);
+ return promise;
+}
+
+function readableByteStreamControllerShouldCallPull(controller)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+
+ if (stream.@state !== @streamReadable)
+ return false;
+ if (controller.@closeRequested)
+ return false;
+ if (!controller.@started)
+ return false;
+ if (@readableStreamHasDefaultReader(stream) && stream.@reader.@readRequests.length > 0)
+ return true;
+ if (@readableStreamHasBYOBReader(stream) && stream.@reader.@readIntoRequests.length > 0)
+ return true;
+ if (@readableByteStreamControllerGetDesiredSize(controller) > 0)
+ return true;
+ return false;
+}
+
+function readableByteStreamControllerCallPullIfNeeded(controller)
+{
+ "use strict";
+
+ if (!@readableByteStreamControllerShouldCallPull(controller))
+ return;
+
+ if (controller.@pulling) {
+ controller.@pullAgain = true;
+ return;
+ }
+
+ @assert(!controller.@pullAgain);
+ controller.@pulling = true;
+ @promiseInvokeOrNoop(controller.@underlyingByteSource, "pull", [controller]).@then(() => {
+ controller.@pulling = false;
+ if (controller.@pullAgain) {
+ controller.@pullAgain = false;
+ @readableByteStreamControllerCallPullIfNeeded(controller);
+ }
+ }, (error) => {
+ if (controller.@controlledReadableStream.@state === @streamReadable)
+ @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;
+}
diff --git a/Source/WebCore/Modules/streams/ReadableStream.idl b/Source/WebCore/Modules/streams/ReadableStream.idl
new file mode 100644
index 000000000..3d841481b
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStream.idl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Canon Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+ Conditional=READABLE_STREAM_API,
+ Constructor(optional any underlyingSource, optional any options),
+ Exposed=(Window,Worker),
+ JSBuiltin,
+ PrivateIdentifier,
+ PublicIdentifier
+] interface ReadableStream {
+ [NotEnumerable] Promise<any> cancel(optional any reason);
+ [NotEnumerable] Object getReader(optional any options);
+ [NotEnumerable] Promise<any> pipeTo(any streams, optional any options);
+ [NotEnumerable] Object pipeThrough(any dest, any options);
+ [NotEnumerable] Object tee();
+
+ [NotEnumerable] readonly attribute boolean locked;
+};
diff --git a/Source/WebCore/Modules/streams/ReadableStream.js b/Source/WebCore/Modules/streams/ReadableStream.js
new file mode 100644
index 000000000..faf6c1611
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStream.js
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @conditional=ENABLE(READABLE_STREAM_API)
+
+function initializeReadableStream(underlyingSource, strategy)
+{
+ "use strict";
+
+ if (underlyingSource === @undefined)
+ underlyingSource = { };
+ if (strategy === @undefined)
+ strategy = { highWaterMark: 1, size: function() { return 1; } };
+
+ if (!@isObject(underlyingSource))
+ @throwTypeError("ReadableStream constructor takes an object as first argument");
+
+ if (strategy !== @undefined && !@isObject(strategy))
+ @throwTypeError("ReadableStream constructor takes an object as second argument, if any");
+
+ this.@state = @streamReadable;
+ this.@reader = @undefined;
+ this.@storedError = @undefined;
+ this.@disturbed = false;
+ // Initialized with null value to enable distinction with undefined case.
+ this.@readableStreamController = null;
+
+ const type = underlyingSource.type;
+ const typeString = @String(type);
+
+ if (typeString === "bytes") {
+ if (strategy.highWaterMark === @undefined)
+ strategy.highWaterMark = 0;
+ // FIXME: When ReadableByteStreamController is no more dependent on a compile flag, specific error handling can be removed.
+ // Constructor is not necessarily available if the byteStream part of Readeable Stream API is not activated. Therefore, a
+ // specific handling of error is done.
+ try {
+ let readableByteStreamControllerConstructor = @ReadableByteStreamController;
+ } catch (e) {
+ @throwTypeError("ReadableByteStreamController is not implemented");
+ }
+ this.@readableStreamController = new @ReadableByteStreamController(this, underlyingSource, strategy.highWaterMark);
+ } else if (type === @undefined) {
+ if (strategy.highWaterMark === @undefined)
+ strategy.highWaterMark = 1;
+ this.@readableStreamController = new @ReadableStreamDefaultController(this, underlyingSource, strategy.size, strategy.highWaterMark);
+ } else
+ @throwRangeError("Invalid type for underlying source");
+
+ return this;
+}
+
+function cancel(reason)
+{
+ "use strict";
+
+ if (!@isReadableStream(this))
+ return @Promise.@reject(@makeThisTypeError("ReadableStream", "cancel"));
+
+ if (@isReadableStreamLocked(this))
+ return @Promise.@reject(new @TypeError("ReadableStream is locked"));
+
+ return @readableStreamCancel(this, reason);
+}
+
+function getReader(options)
+{
+ "use strict";
+
+ if (!@isReadableStream(this))
+ throw @makeThisTypeError("ReadableStream", "getReader");
+
+ if (options === @undefined)
+ options = { };
+
+ if (options.mode === 'byob') {
+ // FIXME: Update once ReadableByteStreamContoller and ReadableStreamBYOBReader are implemented.
+ @throwTypeError("ReadableStreamBYOBReader is not implemented");
+ }
+
+ if (options.mode === @undefined)
+ return new @ReadableStreamDefaultReader(this);
+
+ @throwRangeError("Invalid mode is specified");
+}
+
+function pipeThrough(streams, options)
+{
+ "use strict";
+
+ const writable = streams.writable;
+ const readable = streams.readable;
+ this.pipeTo(writable, options);
+ return readable;
+}
+
+function pipeTo(destination)
+{
+ "use strict";
+
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=159869.
+ // Built-in generator should be able to parse function signature to compute the function length correctly.
+ const options = arguments[1];
+
+ // FIXME: rewrite pipeTo so as to require to have 'this' as a ReadableStream and destination be a WritableStream.
+ // See https://github.com/whatwg/streams/issues/407.
+ // We should shield the pipeTo implementation at the same time.
+
+ const preventClose = @isObject(options) && !!options.preventClose;
+ const preventAbort = @isObject(options) && !!options.preventAbort;
+ const preventCancel = @isObject(options) && !!options.preventCancel;
+
+ const source = this;
+
+ let reader;
+ let lastRead;
+ let lastWrite;
+ let closedPurposefully = false;
+ let promiseCapability;
+
+ function doPipe() {
+ lastRead = reader.read();
+ @Promise.prototype.@then.@call(@Promise.all([lastRead, destination.ready]), function([{ value, done }]) {
+ if (done)
+ closeDestination();
+ else if (destination.state === "writable") {
+ lastWrite = destination.write(value);
+ doPipe();
+ }
+ }, function(e) {
+ throw e;
+ });
+ }
+
+ function cancelSource(reason) {
+ if (!preventCancel) {
+ reader.cancel(reason);
+ reader.releaseLock();
+ promiseCapability.@reject.@call(@undefined, reason);
+ } else {
+ @Promise.prototype.@then.@call(lastRead, function() {
+ reader.releaseLock();
+ promiseCapability.@reject.@call(@undefined, reason);
+ });
+ }
+ }
+
+ function closeDestination() {
+ reader.releaseLock();
+
+ const destinationState = destination.state;
+ if (!preventClose && (destinationState === "waiting" || destinationState === "writable")) {
+ closedPurposefully = true;
+ @Promise.prototype.@then.@call(destination.close(), promiseCapability.@resolve, promiseCapability.@reject);
+ } else if (lastWrite !== @undefined)
+ @Promise.prototype.@then.@call(lastWrite, promiseCapability.@resolve, promiseCapability.@reject);
+ else
+ promiseCapability.@resolve.@call();
+
+ }
+
+ function abortDestination(reason) {
+ reader.releaseLock();
+
+ if (!preventAbort)
+ destination.abort(reason);
+ promiseCapability.@reject.@call(@undefined, reason);
+ }
+
+ promiseCapability = @newPromiseCapability(@Promise);
+
+ reader = source.getReader();
+
+ @Promise.prototype.@then.@call(reader.closed, @undefined, abortDestination);
+ @Promise.prototype.@then.@call(destination.closed,
+ function() {
+ if (!closedPurposefully)
+ cancelSource(new @TypeError('destination is closing or closed and cannot be piped to anymore'));
+ },
+ cancelSource
+ );
+
+ doPipe();
+
+ return promiseCapability.@promise;
+}
+
+function tee()
+{
+ "use strict";
+
+ if (!@isReadableStream(this))
+ throw @makeThisTypeError("ReadableStream", "tee");
+
+ return @readableStreamTee(this, false);
+}
+
+function locked()
+{
+ "use strict";
+
+ if (!@isReadableStream(this))
+ throw @makeGetterTypeError("ReadableStream", "locked");
+
+ return @isReadableStreamLocked(this);
+}
diff --git a/Source/WebCore/Modules/streams/ReadableStreamDefaultController.idl b/Source/WebCore/Modules/streams/ReadableStreamDefaultController.idl
new file mode 100644
index 000000000..9d05c952a
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamDefaultController.idl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Canon Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+ Conditional=READABLE_STREAM_API,
+ CustomConstructor(ReadableStream stream, any underlyingSource, unsigned long size, unsigned long highWaterMark),
+ Exposed=(Window,Worker),
+ JSBuiltin,
+ NoInterfaceObject
+] interface ReadableStreamDefaultController {
+ [NotEnumerable] void enqueue(optional any chunk);
+ [NotEnumerable] void close();
+ [NotEnumerable] void error(optional any error);
+
+ [NotEnumerable] readonly attribute double desiredSize;
+};
diff --git a/Source/WebCore/Modules/streams/ReadableStreamDefaultController.js b/Source/WebCore/Modules/streams/ReadableStreamDefaultController.js
new file mode 100644
index 000000000..e9c94998a
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamDefaultController.js
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @conditional=ENABLE(READABLE_STREAM_API)
+
+function enqueue(chunk)
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultController(this))
+ throw @makeThisTypeError("ReadableStreamDefaultController", "enqueue");
+
+ if (this.@closeRequested)
+ @throwTypeError("ReadableStreamDefaultController is requested to close");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ return @readableStreamDefaultControllerEnqueue(this, chunk);
+}
+
+function error(error)
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultController(this))
+ throw @makeThisTypeError("ReadableStreamDefaultController", "error");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ @readableStreamDefaultControllerError(this, error);
+}
+
+function close()
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultController(this))
+ throw @makeThisTypeError("ReadableStreamDefaultController", "close");
+
+ if (this.@closeRequested)
+ @throwTypeError("ReadableStreamDefaultController is already requested to close");
+
+ if (this.@controlledReadableStream.@state !== @streamReadable)
+ @throwTypeError("ReadableStream is not readable");
+
+ @readableStreamDefaultControllerClose(this);
+}
+
+function desiredSize()
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultController(this))
+ throw @makeGetterTypeError("ReadableStreamDefaultController", "desiredSize");
+
+ return @readableStreamDefaultControllerGetDesiredSize(this);
+}
+
diff --git a/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.idl b/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.idl
new file mode 100644
index 000000000..fd2926fc4
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.idl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Canon Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+ Conditional=READABLE_STREAM_API,
+ CustomConstructor(ReadableStream stream),
+ Exposed=(Window,Worker),
+ JSBuiltin,
+ NoInterfaceObject
+] interface ReadableStreamDefaultReader {
+ [NotEnumerable] Promise<any> read();
+ [NotEnumerable] Promise<any> cancel(optional any reason);
+ [NotEnumerable] void releaseLock();
+
+ [NotEnumerable] readonly attribute Promise<boolean> closed;
+};
diff --git a/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.js b/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.js
new file mode 100644
index 000000000..d5a51c3a0
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamDefaultReader.js
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @conditional=ENABLE(READABLE_STREAM_API)
+
+function cancel(reason)
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultReader(this))
+ return @Promise.@reject(@makeThisTypeError("ReadableStreamDefaultReader", "cancel"));
+
+ if (!this.@ownerReadableStream)
+ return @Promise.@reject(new @TypeError("cancel() called on a reader owned by no readable stream"));
+
+ return @readableStreamReaderGenericCancel(this, reason);
+}
+
+function read()
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultReader(this))
+ return @Promise.@reject(@makeThisTypeError("ReadableStreamDefaultReader", "read"));
+ if (!this.@ownerReadableStream)
+ return @Promise.@reject(new @TypeError("read() called on a reader owned by no readable stream"));
+
+ return @readableStreamDefaultReaderRead(this);
+}
+
+function releaseLock()
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultReader(this))
+ throw @makeThisTypeError("ReadableStreamDefaultReader", "releaseLock");
+
+ if (!this.@ownerReadableStream)
+ return;
+
+ if (this.@readRequests.length)
+ @throwTypeError("There are still pending read requests, cannot release the lock");
+
+ @readableStreamReaderGenericRelease(this);
+}
+
+function closed()
+{
+ "use strict";
+
+ if (!@isReadableStreamDefaultReader(this))
+ return @Promise.@reject(@makeGetterTypeError("ReadableStreamDefaultReader", "closed"));
+
+ return this.@closedPromiseCapability.@promise;
+}
diff --git a/Source/WebCore/Modules/streams/ReadableStreamInternals.js b/Source/WebCore/Modules/streams/ReadableStreamInternals.js
new file mode 100644
index 000000000..87094bf6a
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamInternals.js
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2015 Canon Inc. All rights reserved.
+ * Copyright (C) 2015 Igalia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @conditional=ENABLE(READABLE_STREAM_API)
+// @internal
+
+function privateInitializeReadableStreamDefaultReader(stream)
+{
+ "use strict";
+
+ if (!@isReadableStream(stream))
+ @throwTypeError("ReadableStreamDefaultReader needs a ReadableStream");
+ if (@isReadableStreamLocked(stream))
+ @throwTypeError("ReadableStream is locked");
+
+ @readableStreamReaderGenericInitialize(this, stream);
+ this.@readRequests = [];
+
+ return this;
+}
+
+function readableStreamReaderGenericInitialize(reader, stream)
+{
+ "use strict";
+
+ reader.@ownerReadableStream = stream;
+ stream.@reader = reader;
+ if (stream.@state === @streamReadable)
+ reader.@closedPromiseCapability = @newPromiseCapability(@Promise);
+ else if (stream.@state === @streamClosed)
+ reader.@closedPromiseCapability = { @promise: @Promise.@resolve() };
+ else {
+ @assert(stream.@state === @streamErrored);
+ reader.@closedPromiseCapability = { @promise: @Promise.@reject(stream.@storedError) };
+ }
+}
+
+function privateInitializeReadableStreamDefaultController(stream, underlyingSource, size, highWaterMark)
+{
+ "use strict";
+
+ if (!@isReadableStream(stream))
+ @throwTypeError("ReadableStreamDefaultController needs a ReadableStream");
+
+ // readableStreamController is initialized with null value.
+ if (stream.@readableStreamController !== null)
+ @throwTypeError("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;
+ @promiseInvokeOrNoopNoCatch(underlyingSource, "start", [this]).@then(() => {
+ controller.@started = true;
+ @assert(!controller.@pulling);
+ @assert(!controller.@pullAgain);
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+ }, (error) => {
+ if (stream.@state === @streamReadable)
+ @readableStreamDefaultControllerError(controller, error);
+ });
+
+ this.@cancel = @readableStreamDefaultControllerCancel;
+
+ this.@pull = @readableStreamDefaultControllerPull;
+
+ return this;
+}
+
+function readableStreamDefaultControllerError(controller, error)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ @assert(stream.@state === @streamReadable);
+ controller.@queue = @newQueue();
+ @readableStreamError(stream, error);
+}
+
+function readableStreamTee(stream, shouldClone)
+{
+ "use strict";
+
+ @assert(@isReadableStream(stream));
+ @assert(typeof(shouldClone) === "boolean");
+
+ const reader = new @ReadableStreamDefaultReader(stream);
+
+ const teeState = {
+ closedOrErrored: false,
+ canceled1: false,
+ canceled2: false,
+ reason1: @undefined,
+ reason2: @undefined,
+ };
+
+ teeState.cancelPromiseCapability = @newPromiseCapability(@InternalPromise);
+
+ const pullFunction = @readableStreamTeePullFunction(teeState, reader, shouldClone);
+
+ const branch1 = new @ReadableStream({
+ "pull": pullFunction,
+ "cancel": @readableStreamTeeBranch1CancelFunction(teeState, stream)
+ });
+ const branch2 = new @ReadableStream({
+ "pull": pullFunction,
+ "cancel": @readableStreamTeeBranch2CancelFunction(teeState, stream)
+ });
+
+ reader.@closedPromiseCapability.@promise.@then(@undefined, function(e) {
+ if (teeState.closedOrErrored)
+ return;
+ @readableStreamDefaultControllerError(branch1.@readableStreamController, e);
+ @readableStreamDefaultControllerError(branch2.@readableStreamController, e);
+ teeState.closedOrErrored = true;
+ });
+
+ // Additional fields compared to the spec, as they are needed within pull/cancel functions.
+ teeState.branch1 = branch1;
+ teeState.branch2 = branch2;
+
+ return [branch1, branch2];
+}
+
+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.
+
+ if (object instanceof @ArrayBuffer)
+ return @structuredCloneArrayBuffer(object);
+
+ if (@ArrayBuffer.@isView(object))
+ return @structuredCloneArrayBufferView(object);
+
+ @throwTypeError("structuredClone not implemented for: " + object);
+}
+
+function readableStreamTeePullFunction(teeState, reader, shouldClone)
+{
+ "use strict";
+
+ return function() {
+ @Promise.prototype.@then.@call(@readableStreamDefaultReaderRead(reader), function(result) {
+ @assert(@isObject(result));
+ @assert(typeof result.done === "boolean");
+ if (result.done && !teeState.closedOrErrored) {
+ if (!teeState.canceled1)
+ @readableStreamDefaultControllerClose(teeState.branch1.@readableStreamController);
+ if (!teeState.canceled2)
+ @readableStreamDefaultControllerClose(teeState.branch2.@readableStreamController);
+ teeState.closedOrErrored = true;
+ }
+ if (teeState.closedOrErrored)
+ return;
+ if (!teeState.canceled1)
+ @readableStreamDefaultControllerEnqueue(teeState.branch1.@readableStreamController, result.value);
+ if (!teeState.canceled2)
+ @readableStreamDefaultControllerEnqueue(teeState.branch2.@readableStreamController, shouldClone ? @doStructuredClone(result.value) : result.value);
+ });
+ }
+}
+
+function readableStreamTeeBranch1CancelFunction(teeState, stream)
+{
+ "use strict";
+
+ return function(r) {
+ teeState.canceled1 = true;
+ teeState.reason1 = r;
+ if (teeState.canceled2) {
+ @readableStreamCancel(stream, [teeState.reason1, teeState.reason2]).@then(
+ teeState.cancelPromiseCapability.@resolve,
+ teeState.cancelPromiseCapability.@reject);
+ }
+ return teeState.cancelPromiseCapability.@promise;
+ }
+}
+
+function readableStreamTeeBranch2CancelFunction(teeState, stream)
+{
+ "use strict";
+
+ return function(r) {
+ teeState.canceled2 = true;
+ teeState.reason2 = r;
+ if (teeState.canceled1) {
+ @readableStreamCancel(stream, [teeState.reason1, teeState.reason2]).@then(
+ teeState.cancelPromiseCapability.@resolve,
+ teeState.cancelPromiseCapability.@reject);
+ }
+ return teeState.cancelPromiseCapability.@promise;
+ }
+}
+
+function isReadableStream(stream)
+{
+ "use strict";
+
+ // 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";
+
+ // 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";
+
+ // 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 readableStreamError(stream, error)
+{
+ "use strict";
+
+ @assert(@isReadableStream(stream));
+ @assert(stream.@state === @streamReadable);
+ stream.@state = @streamErrored;
+ stream.@storedError = error;
+
+ if (!stream.@reader)
+ return;
+
+ const reader = stream.@reader;
+
+ 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.
+ @throwTypeError("Only ReadableStreamDefaultReader is currently supported");
+
+ reader.@closedPromiseCapability.@reject.@call(@undefined, error);
+}
+
+function readableStreamDefaultControllerCallPullIfNeeded(controller)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+
+ if (stream.@state === @streamClosed || stream.@state === @streamErrored)
+ return;
+ if (controller.@closeRequested)
+ return;
+ if (!controller.@started)
+ return;
+ if ((!@isReadableStreamLocked(stream) || !stream.@reader.@readRequests.length) && @readableStreamDefaultControllerGetDesiredSize(controller) <= 0)
+ return;
+
+ if (controller.@pulling) {
+ controller.@pullAgain = true;
+ return;
+ }
+
+ @assert(!controller.@pullAgain);
+ controller.@pulling = true;
+
+ @promiseInvokeOrNoop(controller.@underlyingSource, "pull", [controller]).@then(function() {
+ controller.@pulling = false;
+ if (controller.@pullAgain) {
+ controller.@pullAgain = false;
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+ }
+ }, function(error) {
+ if (stream.@state === @streamReadable)
+ @readableStreamDefaultControllerError(controller, error);
+ });
+}
+
+function isReadableStreamLocked(stream)
+{
+ "use strict";
+
+ @assert(@isReadableStream(stream));
+ return !!stream.@reader;
+}
+
+function readableStreamDefaultControllerGetDesiredSize(controller)
+{
+ "use strict";
+
+ return controller.@strategy.highWaterMark - controller.@queue.size;
+}
+
+
+function readableStreamReaderGenericCancel(reader, reason)
+{
+ "use strict";
+
+ const stream = reader.@ownerReadableStream;
+ @assert(!!stream);
+ return @readableStreamCancel(stream, reason);
+}
+
+function readableStreamCancel(stream, reason)
+{
+ "use strict";
+
+ stream.@disturbed = true;
+ if (stream.@state === @streamClosed)
+ return @Promise.@resolve();
+ if (stream.@state === @streamErrored)
+ return @Promise.@reject(stream.@storedError);
+ @readableStreamClose(stream);
+ return stream.@readableStreamController.@cancel(stream.@readableStreamController, reason).@then(function() { });
+}
+
+function readableStreamDefaultControllerCancel(controller, reason)
+{
+ "use strict";
+
+ controller.@queue = @newQueue();
+ return @promiseInvokeOrNoop(controller.@underlyingSource, "cancel", [reason]);
+}
+
+function readableStreamDefaultControllerPull(controller)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ if (controller.@queue.content.length) {
+ const chunk = @dequeueValue(controller.@queue);
+ if (controller.@closeRequested && controller.@queue.content.length === 0)
+ @readableStreamClose(stream);
+ else
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+ return @Promise.@resolve({value: chunk, done: false});
+ }
+ const pendingPromise = @readableStreamAddReadRequest(stream);
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+ return pendingPromise;
+}
+
+function readableStreamDefaultControllerClose(controller)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ @assert(!controller.@closeRequested);
+ @assert(stream.@state === @streamReadable);
+ controller.@closeRequested = true;
+ if (controller.@queue.content.length === 0)
+ @readableStreamClose(stream);
+}
+
+function readableStreamClose(stream)
+{
+ "use strict";
+
+ @assert(stream.@state === @streamReadable);
+ stream.@state = @streamClosed;
+ const reader = stream.@reader;
+
+ if (!reader)
+ return;
+
+ 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 readableStreamFulfillReadRequest(stream, chunk, done)
+{
+ "use strict";
+
+ stream.@reader.@readRequests.@shift().@resolve.@call(@undefined, {value: chunk, done: done});
+}
+
+function readableStreamDefaultControllerEnqueue(controller, chunk)
+{
+ "use strict";
+
+ const stream = controller.@controlledReadableStream;
+ @assert(!controller.@closeRequested);
+ @assert(stream.@state === @streamReadable);
+
+ if (@isReadableStreamLocked(stream) && stream.@reader.@readRequests.length) {
+ @readableStreamFulfillReadRequest(stream, chunk, false);
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+ return;
+ }
+
+ try {
+ let chunkSize = 1;
+ if (controller.@strategy.size !== @undefined)
+ chunkSize = controller.@strategy.size(chunk);
+ @enqueueValueWithSize(controller.@queue, chunk, chunkSize);
+ }
+ catch(error) {
+ if (stream.@state === @streamReadable)
+ @readableStreamDefaultControllerError(controller, error);
+ throw error;
+ }
+ @readableStreamDefaultControllerCallPullIfNeeded(controller);
+}
+
+function readableStreamDefaultReaderRead(reader)
+{
+ "use strict";
+
+ const stream = reader.@ownerReadableStream;
+ @assert(!!stream);
+
+ stream.@disturbed = true;
+ if (stream.@state === @streamClosed)
+ return @Promise.@resolve({value: @undefined, done: true});
+ if (stream.@state === @streamErrored)
+ return @Promise.@reject(stream.@storedError);
+ @assert(stream.@state === @streamReadable);
+
+ return stream.@readableStreamController.@pull(stream.@readableStreamController);
+}
+
+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)
+{
+ "use strict";
+
+ @assert(@isReadableStream(stream));
+ return stream.@disturbed;
+}
+
+function readableStreamReaderGenericRelease(reader)
+{
+ "use strict";
+
+ @assert(!!reader.@ownerReadableStream);
+ @assert(reader.@ownerReadableStream.@reader === reader);
+
+ if (reader.@ownerReadableStream.@state === @streamReadable)
+ reader.@closedPromiseCapability.@reject.@call(@undefined, new @TypeError("releasing lock of reader whose stream is still in readable state"));
+ else
+ reader.@closedPromiseCapability = { @promise: @Promise.@reject(new @TypeError("reader released lock")) };
+
+ reader.@ownerReadableStream.@reader = @undefined;
+ reader.@ownerReadableStream = null;
+}
diff --git a/Source/WebCore/Modules/streams/ReadableStreamSource.h b/Source/WebCore/Modules/streams/ReadableStreamSource.h
new file mode 100644
index 000000000..65919f294
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamSource.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 Canon Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Canon Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(READABLE_STREAM_API)
+
+#include "JSDOMPromise.h"
+#include "ReadableStreamDefaultController.h"
+#include <wtf/Optional.h>
+
+namespace WebCore {
+
+class ReadableStreamSource : public RefCounted<ReadableStreamSource> {
+public:
+ virtual ~ReadableStreamSource() { }
+
+ void start(ReadableStreamDefaultController&&, DOMPromise<void>&&);
+ void pull(DOMPromise<void>&&);
+ void cancel(JSC::JSValue);
+
+ bool isPulling() const { return !!m_promise; }
+
+protected:
+ ReadableStreamDefaultController& controller() { return m_controller.value(); }
+ const ReadableStreamDefaultController& controller() const { return m_controller.value(); }
+
+ void startFinished();
+ void pullFinished();
+ void cancelFinished();
+ void clean();
+
+ virtual void setActive() = 0;
+ virtual void setInactive() = 0;
+
+ virtual void doStart() = 0;
+ virtual void doPull() = 0;
+ virtual void doCancel() = 0;
+
+private:
+ std::optional<DOMPromise<void>> m_promise;
+ std::optional<ReadableStreamDefaultController> m_controller;
+};
+
+inline void ReadableStreamSource::start(ReadableStreamDefaultController&& controller, DOMPromise<void>&& promise)
+{
+ ASSERT(!m_promise);
+ m_promise = WTFMove(promise);
+ m_controller = WTFMove(controller);
+
+ setActive();
+ doStart();
+}
+
+inline void ReadableStreamSource::pull(DOMPromise<void>&& promise)
+{
+ ASSERT(!m_promise);
+ ASSERT(m_controller);
+
+ m_promise = WTFMove(promise);
+
+ setActive();
+ doPull();
+}
+
+inline void ReadableStreamSource::startFinished()
+{
+ ASSERT(m_promise);
+ std::exchange(m_promise, std::nullopt).value().resolve();
+ setInactive();
+}
+
+inline void ReadableStreamSource::pullFinished()
+{
+ ASSERT(m_promise);
+ std::exchange(m_promise, std::nullopt).value().resolve();
+ setInactive();
+}
+
+inline void ReadableStreamSource::cancel(JSC::JSValue)
+{
+ clean();
+ doCancel();
+}
+
+inline void ReadableStreamSource::clean()
+{
+ if (m_promise) {
+ m_promise = std::nullopt;
+ setInactive();
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(READABLE_STREAM_API)
diff --git a/Source/WebCore/Modules/streams/ReadableStreamSource.idl b/Source/WebCore/Modules/streams/ReadableStreamSource.idl
new file mode 100644
index 000000000..ad3afcc99
--- /dev/null
+++ b/Source/WebCore/Modules/streams/ReadableStreamSource.idl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 Canon Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Canon Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+ NoInterfaceObject,
+ Conditional=READABLE_STREAM_API,
+ SkipVTableValidation
+] interface ReadableStreamSource {
+ [Custom] Promise<void> start(ReadableStreamDefaultController controller);
+ [Custom] Promise<void> pull(ReadableStreamDefaultController controller);
+ void cancel(any reason);
+
+ // Place holder to keep the controller linked to the source.
+ [CachedAttribute, CustomGetter] readonly attribute any controller;
+};
diff --git a/Source/WebCore/Modules/streams/StreamInternals.js b/Source/WebCore/Modules/streams/StreamInternals.js
new file mode 100644
index 000000000..94a96e396
--- /dev/null
+++ b/Source/WebCore/Modules/streams/StreamInternals.js
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// @conditional=ENABLE(READABLE_STREAM_API) || ENABLE(WRITABLE_STREAM_API)
+// @internal
+
+function shieldingPromiseResolve(result)
+{
+ const promise = @Promise.@resolve(result);
+ if (promise.@then === @undefined)
+ promise.@then = @Promise.prototype.@then;
+ return promise;
+}
+
+function promiseInvokeOrNoopNoCatch(object, key, args)
+{
+ "use strict";
+
+ const method = object[key];
+ if (method === @undefined)
+ return @Promise.@resolve();
+ return @shieldingPromiseResolve(method.@apply(object, args));
+}
+
+function promiseInvokeOrNoop(object, key, args)
+{
+ "use strict";
+
+ try {
+ return @promiseInvokeOrNoopNoCatch(object, key, args);
+ }
+ catch(error) {
+ return @Promise.@reject(error);
+ }
+
+}
+
+function promiseInvokeOrFallbackOrNoop(object, key1, args1, key2, args2)
+{
+ "use strict";
+
+ try {
+ const method = object[key1];
+ if (method === @undefined)
+ return @promiseInvokeOrNoopNoCatch(object, key2, args2);
+ return @shieldingPromiseResolve(method.@apply(object, args1));
+ }
+ catch(error) {
+ return @Promise.@reject(error);
+ }
+}
+
+function validateAndNormalizeQueuingStrategy(size, highWaterMark)
+{
+ "use strict";
+
+ if (size !== @undefined && typeof size !== "function")
+ @throwTypeError("size parameter must be a function");
+
+ const normalizedStrategy = { };
+
+ normalizedStrategy.size = size;
+ normalizedStrategy.highWaterMark = @Number(highWaterMark);
+
+ if (@isNaN(normalizedStrategy.highWaterMark) || normalizedStrategy.highWaterMark < 0)
+ @throwRangeError("highWaterMark value is negative or not a number");
+
+ return normalizedStrategy;
+}
+
+function newQueue()
+{
+ "use strict";
+
+ return { content: [], size: 0 };
+}
+
+function dequeueValue(queue)
+{
+ "use strict";
+
+ const record = queue.content.@shift();
+ queue.size -= record.size;
+ return record.value;
+}
+
+function enqueueValueWithSize(queue, value, size)
+{
+ "use strict";
+
+ size = @Number(size);
+ if (!@isFinite(size) || size < 0)
+ @throwRangeError("size has an incorrect value");
+ queue.content.@push({ value: value, size: size });
+ queue.size += size;
+}
+
+function peekQueueValue(queue)
+{
+ "use strict";
+
+ @assert(queue.content.length > 0);
+
+ return queue.content[0].value;
+}
diff --git a/Source/WebCore/Modules/streams/WritableStream.idl b/Source/WebCore/Modules/streams/WritableStream.idl
new file mode 100644
index 000000000..62e92234c
--- /dev/null
+++ b/Source/WebCore/Modules/streams/WritableStream.idl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted, provided that the following conditions
+ * are required to be met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Canon Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+ Conditional=WRITABLE_STREAM_API,
+ Constructor,
+ JSBuiltin
+] interface WritableStream {
+ Promise<any> abort(optional any reason);
+ Promise<any> close();
+ Promise<any> write(any chunk);
+
+ readonly attribute Promise<boolean> closed;
+ readonly attribute Promise<boolean> ready;
+ readonly attribute DOMString state;
+};
diff --git a/Source/WebCore/Modules/streams/WritableStream.js b/Source/WebCore/Modules/streams/WritableStream.js
new file mode 100644
index 000000000..ac6ca2649
--- /dev/null
+++ b/Source/WebCore/Modules/streams/WritableStream.js
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @conditional=ENABLE(WRITABLE_STREAM_API)
+
+function initializeWritableStream(underlyingSink, strategy)
+{
+ "use strict";
+
+ if (underlyingSink === @undefined)
+ underlyingSink = { };
+ if (strategy === @undefined)
+ strategy = { highWaterMark: 0, size: function() { return 1; } };
+
+ if (!@isObject(underlyingSink))
+ @throwTypeError("WritableStream constructor takes an object as first argument");
+
+ if (!@isObject(strategy))
+ @throwTypeError("WritableStream constructor takes an object as second argument, if any");
+
+ this.@underlyingSink = underlyingSink;
+ this.@closedPromiseCapability = @newPromiseCapability(@Promise);
+ this.@readyPromiseCapability = { @promise: @Promise.@resolve() };
+ this.@queue = @newQueue();
+ this.@state = @streamWritable;
+ this.@started = false;
+ this.@writing = false;
+
+ this.@strategy = @validateAndNormalizeQueuingStrategy(strategy.size, strategy.highWaterMark);
+
+ @syncWritableStreamStateWithQueue(this);
+
+ const errorFunction = (e) => {
+ @errorWritableStream(this, e);
+ };
+ this.@startedPromise = @promiseInvokeOrNoopNoCatch(underlyingSink, "start", [errorFunction]);
+ this.@startedPromise.@then(() => {
+ this.@started = true;
+ this.@startedPromise = @undefined;
+ }, errorFunction);
+
+ return this;
+}
+
+function abort(reason)
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ return @Promise.@reject(new @TypeError("The WritableStream.abort method can only be used on instances of WritableStream"));
+
+ if (this.@state === @streamClosed)
+ return @Promise.@resolve();
+
+ if (this.@state === @streamErrored)
+ return @Promise.@reject(this.@storedError);
+
+ @errorWritableStream(this, reason);
+
+ return @promiseInvokeOrFallbackOrNoop(this.@underlyingSink, "abort", [reason], "close", []).@then(function() { });
+}
+
+function close()
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ return @Promise.@reject(new @TypeError("The WritableStream.close method can only be used on instances of WritableStream"));
+
+ if (this.@state === @streamClosed || this.@state === @streamClosing)
+ return @Promise.@reject(new @TypeError("Cannot close a WritableString that is closed or closing"));
+
+ if (this.@state === @streamErrored)
+ return @Promise.@reject(this.@storedError);
+
+ if (this.@state === @streamWaiting)
+ this.@readyPromiseCapability.@resolve.@call();
+
+ this.@state = @streamClosing;
+ @enqueueValueWithSize(this.@queue, "close", 0);
+ @callOrScheduleWritableStreamAdvanceQueue(this);
+
+ return this.@closedPromiseCapability.@promise;
+}
+
+function write(chunk)
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ return @Promise.@reject(new @TypeError("The WritableStream.write method can only be used on instances of WritableStream"));
+
+ if (this.@state === @streamClosed || this.@state === @streamClosing)
+ return @Promise.@reject(new @TypeError("Cannot write on a WritableString that is closed or closing"));
+
+ if (this.@state === @streamErrored)
+ return @Promise.@reject(this.@storedError);
+
+ @assert(this.@state === @streamWritable || this.@state === @streamWaiting);
+
+ let chunkSize = 1;
+ if (this.@strategy.size !== @undefined) {
+ try {
+ chunkSize = this.@strategy.size.@call(@undefined, chunk);
+ } catch(e) {
+ @errorWritableStream(this, e);
+ return @Promise.@reject(e);
+ }
+ }
+
+ const promiseCapability = @newPromiseCapability(@Promise);
+ try {
+ @enqueueValueWithSize(this.@queue, { promiseCapability: promiseCapability, chunk: chunk }, chunkSize);
+ } catch (e) {
+ @errorWritableStream(this, e);
+ return @Promise.@reject(e);
+ }
+
+ @syncWritableStreamStateWithQueue(this);
+ @callOrScheduleWritableStreamAdvanceQueue(this);
+
+ return promiseCapability.@promise;
+}
+
+function closed()
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ return @Promise.@reject(new @TypeError("The WritableStream.closed getter can only be used on instances of WritableStream"));
+
+ return this.@closedPromiseCapability.@promise;
+}
+
+function ready()
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ return @Promise.@reject(new @TypeError("The WritableStream.ready getter can only be used on instances of WritableStream"));
+
+ return this.@readyPromiseCapability.@promise;
+}
+
+function state()
+{
+ "use strict";
+
+ if (!@isWritableStream(this))
+ @throwTypeError("The WritableStream.state getter can only be used on instances of WritableStream");
+
+ switch(this.@state) {
+ case @streamClosed:
+ return "closed";
+ case @streamClosing:
+ return "closing";
+ case @streamErrored:
+ return "errored";
+ case @streamWaiting:
+ return "waiting";
+ case @streamWritable:
+ return "writable";
+ }
+
+ @assert(false);
+}
diff --git a/Source/WebCore/Modules/streams/WritableStreamInternals.js b/Source/WebCore/Modules/streams/WritableStreamInternals.js
new file mode 100644
index 000000000..cffea5a3d
--- /dev/null
+++ b/Source/WebCore/Modules/streams/WritableStreamInternals.js
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2015 Canon Inc.
+ * Copyright (C) 2015 Igalia
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// @conditional=ENABLE(WRITABLE_STREAM_API)
+// @internal
+
+function isWritableStream(stream)
+{
+ "use strict";
+
+ return @isObject(stream) && !!stream.@underlyingSink;
+}
+
+function syncWritableStreamStateWithQueue(stream)
+{
+ "use strict";
+
+ if (stream.@state === @streamClosing)
+ return;
+
+ @assert(stream.@state === @streamWritable || stream.@state === @streamWaiting);
+
+ const shouldApplyBackpressure = stream.@queue.size > stream.@strategy.highWaterMark;
+ if (shouldApplyBackpressure && stream.@state === @streamWritable) {
+ stream.@state = @streamWaiting;
+ stream.@readyPromiseCapability = @newPromiseCapability(@Promise);
+ }
+ if (!shouldApplyBackpressure && stream.@state === @streamWaiting) {
+ stream.@state = @streamWritable;
+ stream.@readyPromiseCapability.@resolve.@call();
+ }
+}
+
+function errorWritableStream(stream, e)
+{
+ "use strict";
+
+ if (stream.@state === @streamClosed || stream.@state === @streamErrored)
+ return;
+ while (stream.@queue.content.length > 0) {
+ const writeRecord = @dequeueValue(stream.@queue);
+ if (writeRecord !== "close")
+ writeRecord.promiseCapability.@reject.@call(@undefined, e);
+ }
+ stream.@storedError = e;
+ if (stream.@state === @streamWaiting)
+ stream.@readyPromiseCapability.@resolve.@call();
+ stream.@closedPromiseCapability.@reject.@call(@undefined, e);
+ stream.@state = @streamErrored;
+}
+
+function callOrScheduleWritableStreamAdvanceQueue(stream)
+{
+ "use strict";
+
+ if (!stream.@started)
+ stream.@startedPromise.@then(function() { @writableStreamAdvanceQueue(stream); });
+ else
+ @writableStreamAdvanceQueue(stream);
+}
+
+function writableStreamAdvanceQueue(stream)
+{
+ "use strict";
+
+ if (stream.@queue.content.length === 0 || stream.@writing)
+ return;
+
+ const writeRecord = @peekQueueValue(stream.@queue);
+ if (writeRecord === "close") {
+ @assert(stream.@state === @streamClosing);
+ @dequeueValue(stream.@queue);
+ @assert(stream.@queue.content.length === 0);
+ @closeWritableStream(stream);
+ return;
+ }
+
+ stream.@writing = true;
+ @promiseInvokeOrNoop(stream.@underlyingSink, "write", [writeRecord.chunk]).@then(
+ function() {
+ if (stream.@state === @streamErrored)
+ return;
+ stream.@writing = false;
+ writeRecord.promiseCapability.@resolve.@call();
+ @dequeueValue(stream.@queue);
+ @syncWritableStreamStateWithQueue(stream);
+ @writableStreamAdvanceQueue(stream);
+ },
+ function(r) {
+ @errorWritableStream(stream, r);
+ }
+ );
+}
+
+function closeWritableStream(stream)
+{
+ "use strict";
+
+ @assert(stream.@state === @streamClosing);
+ @promiseInvokeOrNoop(stream.@underlyingSink, "close").@then(
+ function() {
+ if (stream.@state === @streamErrored)
+ return;
+ @assert(stream.@state === @streamClosing);
+ stream.@closedPromiseCapability.@resolve.@call();
+ stream.@state = @streamClosed;
+ },
+ function(r) {
+ @errorWritableStream(stream, r);
+ }
+ );
+}