diff options
Diffstat (limited to 'Source/WebCore/Modules/streams/ReadableStream.js')
-rw-r--r-- | Source/WebCore/Modules/streams/ReadableStream.js | 229 |
1 files changed, 229 insertions, 0 deletions
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); +} |