UNPKG

openpgp

Version:

OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.

1,154 lines (1,139 loc) 1.53 MB
/*! OpenPGP.js v5.0.0-1 - 2021-03-03 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */ var openpgp = (function (exports) { 'use strict'; const globalThis = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; /** * web-streams-polyfill v2.1.1 */ /// <reference lib="es2015.symbol" /> const SymbolPolyfill = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? Symbol : description => `Symbol(${description})`; /// <reference lib="dom" /> function noop() { // do nothing } /// <reference lib="es2015.core" /> // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN#Polyfill const NumberIsNaN = Number.isNaN || function (x) { // eslint-disable-next-line no-self-compare return x !== x; }; const rethrowAssertionErrorRejection = noop; function typeIsObject(x) { return (typeof x === 'object' && x !== null) || typeof x === 'function'; } function createArrayFromList(elements) { // We use arrays to represent lists, so this is basically a no-op. // Do a slice though just in case we happen to depend on the unique-ness. return elements.slice(); } function ArrayBufferCopy(dest, destOffset, src, srcOffset, n) { new Uint8Array(dest).set(new Uint8Array(src, srcOffset, n), destOffset); } function IsFiniteNonNegativeNumber(v) { if (IsNonNegativeNumber(v) === false) { return false; } if (v === Infinity) { return false; } return true; } function IsNonNegativeNumber(v) { if (typeof v !== 'number') { return false; } if (NumberIsNaN(v)) { return false; } if (v < 0) { return false; } return true; } function Call(F, V, args) { if (typeof F !== 'function') { throw new TypeError('Argument is not a function'); } return Function.prototype.apply.call(F, V, args); } function CreateAlgorithmFromUnderlyingMethod(underlyingObject, methodName, algoArgCount, extraArgs) { const method = underlyingObject[methodName]; if (method !== undefined) { if (typeof method !== 'function') { throw new TypeError(`${method} is not a method`); } switch (algoArgCount) { case 0: { return () => { return PromiseCall(method, underlyingObject, extraArgs); }; } case 1: { return arg => { const fullArgs = [arg].concat(extraArgs); return PromiseCall(method, underlyingObject, fullArgs); }; } } } return () => promiseResolvedWith(undefined); } function InvokeOrNoop(O, P, args) { const method = O[P]; if (method === undefined) { return undefined; } return Call(method, O, args); } function PromiseCall(F, V, args) { try { return promiseResolvedWith(Call(F, V, args)); } catch (value) { return promiseRejectedWith(value); } } // Not implemented correctly function TransferArrayBuffer(O) { return O; } // Not implemented correctly function IsDetachedBuffer(O) { return false; } function ValidateAndNormalizeHighWaterMark(highWaterMark) { highWaterMark = Number(highWaterMark); if (NumberIsNaN(highWaterMark) || highWaterMark < 0) { throw new RangeError('highWaterMark property of a queuing strategy must be non-negative and non-NaN'); } return highWaterMark; } function MakeSizeAlgorithmFromSizeFunction(size) { if (size === undefined) { return () => 1; } if (typeof size !== 'function') { throw new TypeError('size property of a queuing strategy must be a function'); } return chunk => size(chunk); } const originalPromise = Promise; const originalPromiseThen = Promise.prototype.then; const originalPromiseResolve = Promise.resolve.bind(originalPromise); const originalPromiseReject = Promise.reject.bind(originalPromise); function newPromise(executor) { return new originalPromise(executor); } function promiseResolvedWith(value) { return originalPromiseResolve(value); } function promiseRejectedWith(reason) { return originalPromiseReject(reason); } function PerformPromiseThen(promise, onFulfilled, onRejected) { // There doesn't appear to be any way to correctly emulate the behaviour from JavaScript, so this is just an // approximation. return originalPromiseThen.call(promise, onFulfilled, onRejected); } function uponPromise(promise, onFulfilled, onRejected) { PerformPromiseThen(PerformPromiseThen(promise, onFulfilled, onRejected), undefined, rethrowAssertionErrorRejection); } function uponFulfillment(promise, onFulfilled) { uponPromise(promise, onFulfilled); } function uponRejection(promise, onRejected) { uponPromise(promise, undefined, onRejected); } function transformPromiseWith(promise, fulfillmentHandler, rejectionHandler) { return PerformPromiseThen(promise, fulfillmentHandler, rejectionHandler); } function setPromiseIsHandledToTrue(promise) { PerformPromiseThen(promise, undefined, rethrowAssertionErrorRejection); } // Original from Chromium // https://chromium.googlesource.com/chromium/src/+/0aee4434a4dba42a42abaea9bfbc0cd196a63bc1/third_party/blink/renderer/core/streams/SimpleQueue.js const QUEUE_MAX_ARRAY_SIZE = 16384; /** * Simple queue structure. * * Avoids scalability issues with using a packed array directly by using * multiple arrays in a linked list and keeping the array size bounded. */ class SimpleQueue { constructor() { this._cursor = 0; this._size = 0; // _front and _back are always defined. this._front = { _elements: [], _next: undefined }; this._back = this._front; // The cursor is used to avoid calling Array.shift(). // It contains the index of the front element of the array inside the // front-most node. It is always in the range [0, QUEUE_MAX_ARRAY_SIZE). this._cursor = 0; // When there is only one node, size === elements.length - cursor. this._size = 0; } get length() { return this._size; } // For exception safety, this method is structured in order: // 1. Read state // 2. Calculate required state mutations // 3. Perform state mutations push(element) { const oldBack = this._back; let newBack = oldBack; if (oldBack._elements.length === QUEUE_MAX_ARRAY_SIZE - 1) { newBack = { _elements: [], _next: undefined }; } // push() is the mutation most likely to throw an exception, so it // goes first. oldBack._elements.push(element); if (newBack !== oldBack) { this._back = newBack; oldBack._next = newBack; } ++this._size; } // Like push(), shift() follows the read -> calculate -> mutate pattern for // exception safety. shift() { // must not be called on an empty queue const oldFront = this._front; let newFront = oldFront; const oldCursor = this._cursor; let newCursor = oldCursor + 1; const elements = oldFront._elements; const element = elements[oldCursor]; if (newCursor === QUEUE_MAX_ARRAY_SIZE) { newFront = oldFront._next; newCursor = 0; } // No mutations before this point. --this._size; this._cursor = newCursor; if (oldFront !== newFront) { this._front = newFront; } // Permit shifted element to be garbage collected. elements[oldCursor] = undefined; return element; } // The tricky thing about forEach() is that it can be called // re-entrantly. The queue may be mutated inside the callback. It is easy to // see that push() within the callback has no negative effects since the end // of the queue is checked for on every iteration. If shift() is called // repeatedly within the callback then the next iteration may return an // element that has been removed. In this case the callback will be called // with undefined values until we either "catch up" with elements that still // exist or reach the back of the queue. forEach(callback) { let i = this._cursor; let node = this._front; let elements = node._elements; while (i !== elements.length || node._next !== undefined) { if (i === elements.length) { node = node._next; elements = node._elements; i = 0; if (elements.length === 0) { break; } } callback(elements[i]); ++i; } } // Return the element that would be returned if shift() was called now, // without modifying the queue. peek() { // must not be called on an empty queue const front = this._front; const cursor = this._cursor; return front._elements[cursor]; } } function ReadableStreamCreateReadResult(value, done, forAuthorCode) { let prototype = null; if (forAuthorCode === true) { prototype = Object.prototype; } const obj = Object.create(prototype); obj.value = value; obj.done = done; return obj; } function ReadableStreamReaderGenericInitialize(reader, stream) { reader._forAuthorCode = true; reader._ownerReadableStream = stream; stream._reader = reader; if (stream._state === 'readable') { defaultReaderClosedPromiseInitialize(reader); } else if (stream._state === 'closed') { defaultReaderClosedPromiseInitializeAsResolved(reader); } else { defaultReaderClosedPromiseInitializeAsRejected(reader, stream._storedError); } } // A client of ReadableStreamDefaultReader and ReadableStreamBYOBReader may use these functions directly to bypass state // check. function ReadableStreamReaderGenericCancel(reader, reason) { const stream = reader._ownerReadableStream; return ReadableStreamCancel(stream, reason); } function ReadableStreamReaderGenericRelease(reader) { if (reader._ownerReadableStream._state === 'readable') { defaultReaderClosedPromiseReject(reader, new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness')); } else { defaultReaderClosedPromiseResetToRejected(reader, new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness')); } reader._ownerReadableStream._reader = undefined; reader._ownerReadableStream = undefined; } // Helper functions for the readers. function readerLockException(name) { return new TypeError('Cannot ' + name + ' a stream using a released reader'); } // Helper functions for the ReadableStreamDefaultReader. function defaultReaderClosedPromiseInitialize(reader) { reader._closedPromise = newPromise((resolve, reject) => { reader._closedPromise_resolve = resolve; reader._closedPromise_reject = reject; }); } function defaultReaderClosedPromiseInitializeAsRejected(reader, reason) { defaultReaderClosedPromiseInitialize(reader); defaultReaderClosedPromiseReject(reader, reason); } function defaultReaderClosedPromiseInitializeAsResolved(reader) { defaultReaderClosedPromiseInitialize(reader); defaultReaderClosedPromiseResolve(reader); } function defaultReaderClosedPromiseReject(reader, reason) { setPromiseIsHandledToTrue(reader._closedPromise); reader._closedPromise_reject(reason); reader._closedPromise_resolve = undefined; reader._closedPromise_reject = undefined; } function defaultReaderClosedPromiseResetToRejected(reader, reason) { defaultReaderClosedPromiseInitializeAsRejected(reader, reason); } function defaultReaderClosedPromiseResolve(reader) { reader._closedPromise_resolve(undefined); reader._closedPromise_resolve = undefined; reader._closedPromise_reject = undefined; } const CancelSteps = SymbolPolyfill('[[CancelSteps]]'); const PullSteps = SymbolPolyfill('[[PullSteps]]'); // Abstract operations for the ReadableStream. function AcquireReadableStreamDefaultReader(stream, forAuthorCode = false) { const reader = new ReadableStreamDefaultReader(stream); reader._forAuthorCode = forAuthorCode; return reader; } // ReadableStream API exposed for controllers. function ReadableStreamAddReadRequest(stream) { const promise = newPromise((resolve, reject) => { const readRequest = { _resolve: resolve, _reject: reject }; stream._reader._readRequests.push(readRequest); }); return promise; } function ReadableStreamFulfillReadRequest(stream, chunk, done) { const reader = stream._reader; const readRequest = reader._readRequests.shift(); readRequest._resolve(ReadableStreamCreateReadResult(chunk, done, reader._forAuthorCode)); } function ReadableStreamGetNumReadRequests(stream) { return stream._reader._readRequests.length; } function ReadableStreamHasDefaultReader(stream) { const reader = stream._reader; if (reader === undefined) { return false; } if (!IsReadableStreamDefaultReader(reader)) { return false; } return true; } class ReadableStreamDefaultReader { constructor(stream) { if (IsReadableStream(stream) === false) { throw new TypeError('ReadableStreamDefaultReader can only be constructed with a ReadableStream instance'); } if (IsReadableStreamLocked(stream) === true) { throw new TypeError('This stream has already been locked for exclusive reading by another reader'); } ReadableStreamReaderGenericInitialize(this, stream); this._readRequests = new SimpleQueue(); } get closed() { if (!IsReadableStreamDefaultReader(this)) { return promiseRejectedWith(defaultReaderBrandCheckException('closed')); } return this._closedPromise; } cancel(reason) { if (!IsReadableStreamDefaultReader(this)) { return promiseRejectedWith(defaultReaderBrandCheckException('cancel')); } if (this._ownerReadableStream === undefined) { return promiseRejectedWith(readerLockException('cancel')); } return ReadableStreamReaderGenericCancel(this, reason); } read() { if (!IsReadableStreamDefaultReader(this)) { return promiseRejectedWith(defaultReaderBrandCheckException('read')); } if (this._ownerReadableStream === undefined) { return promiseRejectedWith(readerLockException('read from')); } return ReadableStreamDefaultReaderRead(this); } releaseLock() { if (!IsReadableStreamDefaultReader(this)) { throw defaultReaderBrandCheckException('releaseLock'); } if (this._ownerReadableStream === undefined) { return; } if (this._readRequests.length > 0) { throw new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled'); } ReadableStreamReaderGenericRelease(this); } } // Abstract operations for the readers. function IsReadableStreamDefaultReader(x) { if (!typeIsObject(x)) { return false; } if (!Object.prototype.hasOwnProperty.call(x, '_readRequests')) { return false; } return true; } function ReadableStreamDefaultReaderRead(reader) { const stream = reader._ownerReadableStream; stream._disturbed = true; if (stream._state === 'closed') { return promiseResolvedWith(ReadableStreamCreateReadResult(undefined, true, reader._forAuthorCode)); } if (stream._state === 'errored') { return promiseRejectedWith(stream._storedError); } return stream._readableStreamController[PullSteps](); } // Helper functions for the ReadableStreamDefaultReader. function defaultReaderBrandCheckException(name) { return new TypeError(`ReadableStreamDefaultReader.prototype.${name} can only be used on a ReadableStreamDefaultReader`); } /// <reference lib="es2018.asynciterable" /> let AsyncIteratorPrototype; if (typeof SymbolPolyfill.asyncIterator === 'symbol') { // We're running inside a ES2018+ environment, but we're compiling to an older syntax. // We cannot access %AsyncIteratorPrototype% without non-ES2018 syntax, but we can re-create it. AsyncIteratorPrototype = { // 25.1.3.1 %AsyncIteratorPrototype% [ @@asyncIterator ] ( ) // https://tc39.github.io/ecma262/#sec-asynciteratorprototype-asynciterator [SymbolPolyfill.asyncIterator]() { return this; } }; Object.defineProperty(AsyncIteratorPrototype, SymbolPolyfill.asyncIterator, { enumerable: false }); } /// <reference lib="es2018.asynciterable" /> const ReadableStreamAsyncIteratorPrototype = { next() { if (IsReadableStreamAsyncIterator(this) === false) { return promiseRejectedWith(streamAsyncIteratorBrandCheckException('next')); } const reader = this._asyncIteratorReader; if (reader._ownerReadableStream === undefined) { return promiseRejectedWith(readerLockException('iterate')); } return transformPromiseWith(ReadableStreamDefaultReaderRead(reader), result => { const done = result.done; if (done) { ReadableStreamReaderGenericRelease(reader); } const value = result.value; return ReadableStreamCreateReadResult(value, done, true); }); }, return(value) { if (IsReadableStreamAsyncIterator(this) === false) { return promiseRejectedWith(streamAsyncIteratorBrandCheckException('next')); } const reader = this._asyncIteratorReader; if (reader._ownerReadableStream === undefined) { return promiseRejectedWith(readerLockException('finish iterating')); } if (reader._readRequests.length > 0) { return promiseRejectedWith(new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled')); } if (this._preventCancel === false) { const result = ReadableStreamReaderGenericCancel(reader, value); ReadableStreamReaderGenericRelease(reader); return transformPromiseWith(result, () => ReadableStreamCreateReadResult(value, true, true)); } ReadableStreamReaderGenericRelease(reader); return promiseResolvedWith(ReadableStreamCreateReadResult(value, true, true)); } }; if (AsyncIteratorPrototype !== undefined) { Object.setPrototypeOf(ReadableStreamAsyncIteratorPrototype, AsyncIteratorPrototype); } Object.defineProperty(ReadableStreamAsyncIteratorPrototype, 'next', { enumerable: false }); Object.defineProperty(ReadableStreamAsyncIteratorPrototype, 'return', { enumerable: false }); // Abstract operations for the ReadableStream. function AcquireReadableStreamAsyncIterator(stream, preventCancel = false) { const reader = AcquireReadableStreamDefaultReader(stream); const iterator = Object.create(ReadableStreamAsyncIteratorPrototype); iterator._asyncIteratorReader = reader; iterator._preventCancel = Boolean(preventCancel); return iterator; } function IsReadableStreamAsyncIterator(x) { if (!typeIsObject(x)) { return false; } if (!Object.prototype.hasOwnProperty.call(x, '_asyncIteratorReader')) { return false; } return true; } // Helper functions for the ReadableStream. function streamAsyncIteratorBrandCheckException(name) { return new TypeError(`ReadableStreamAsyncIterator.${name} can only be used on a ReadableSteamAsyncIterator`); } function DequeueValue(container) { const pair = container._queue.shift(); container._queueTotalSize -= pair.size; if (container._queueTotalSize < 0) { container._queueTotalSize = 0; } return pair.value; } function EnqueueValueWithSize(container, value, size) { size = Number(size); if (!IsFiniteNonNegativeNumber(size)) { throw new RangeError('Size must be a finite, non-NaN, non-negative number.'); } container._queue.push({ value, size }); container._queueTotalSize += size; } function PeekQueueValue(container) { const pair = container._queue.peek(); return pair.value; } function ResetQueue(container) { container._queue = new SimpleQueue(); container._queueTotalSize = 0; } const AbortSteps = SymbolPolyfill('[[AbortSteps]]'); const ErrorSteps = SymbolPolyfill('[[ErrorSteps]]'); class WritableStream { constructor(underlyingSink = {}, strategy = {}) { InitializeWritableStream(this); const size = strategy.size; let highWaterMark = strategy.highWaterMark; const type = underlyingSink.type; if (type !== undefined) { throw new RangeError('Invalid type is specified'); } const sizeAlgorithm = MakeSizeAlgorithmFromSizeFunction(size); if (highWaterMark === undefined) { highWaterMark = 1; } highWaterMark = ValidateAndNormalizeHighWaterMark(highWaterMark); SetUpWritableStreamDefaultControllerFromUnderlyingSink(this, underlyingSink, highWaterMark, sizeAlgorithm); } get locked() { if (IsWritableStream(this) === false) { throw streamBrandCheckException('locked'); } return IsWritableStreamLocked(this); } abort(reason) { if (IsWritableStream(this) === false) { return promiseRejectedWith(streamBrandCheckException('abort')); } if (IsWritableStreamLocked(this) === true) { return promiseRejectedWith(new TypeError('Cannot abort a stream that already has a writer')); } return WritableStreamAbort(this, reason); } close() { if (IsWritableStream(this) === false) { return promiseRejectedWith(streamBrandCheckException('close')); } if (IsWritableStreamLocked(this) === true) { return promiseRejectedWith(new TypeError('Cannot close a stream that already has a writer')); } if (WritableStreamCloseQueuedOrInFlight(this) === true) { return promiseRejectedWith(new TypeError('Cannot close an already-closing stream')); } return WritableStreamClose(this); } getWriter() { if (IsWritableStream(this) === false) { throw streamBrandCheckException('getWriter'); } return AcquireWritableStreamDefaultWriter(this); } } // Abstract operations for the WritableStream. function AcquireWritableStreamDefaultWriter(stream) { return new WritableStreamDefaultWriter(stream); } // Throws if and only if startAlgorithm throws. function CreateWritableStream(startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark = 1, sizeAlgorithm = () => 1) { const stream = Object.create(WritableStream.prototype); InitializeWritableStream(stream); const controller = Object.create(WritableStreamDefaultController.prototype); SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm); return stream; } function InitializeWritableStream(stream) { stream._state = 'writable'; // The error that will be reported by new method calls once the state becomes errored. Only set when [[state]] is // 'erroring' or 'errored'. May be set to an undefined value. stream._storedError = undefined; stream._writer = undefined; // Initialize to undefined first because the constructor of the controller checks this // variable to validate the caller. stream._writableStreamController = undefined; // This queue is placed here instead of the writer class in order to allow for passing a writer to the next data // producer without waiting for the queued writes to finish. stream._writeRequests = new SimpleQueue(); // Write requests are removed from _writeRequests when write() is called on the underlying sink. This prevents // them from being erroneously rejected on error. If a write() call is in-flight, the request is stored here. stream._inFlightWriteRequest = undefined; // The promise that was returned from writer.close(). Stored here because it may be fulfilled after the writer // has been detached. stream._closeRequest = undefined; // Close request is removed from _closeRequest when close() is called on the underlying sink. This prevents it // from being erroneously rejected on error. If a close() call is in-flight, the request is stored here. stream._inFlightCloseRequest = undefined; // The promise that was returned from writer.abort(). This may also be fulfilled after the writer has detached. stream._pendingAbortRequest = undefined; // The backpressure signal set by the controller. stream._backpressure = false; } function IsWritableStream(x) { if (!typeIsObject(x)) { return false; } if (!Object.prototype.hasOwnProperty.call(x, '_writableStreamController')) { return false; } return true; } function IsWritableStreamLocked(stream) { if (stream._writer === undefined) { return false; } return true; } function WritableStreamAbort(stream, reason) { const state = stream._state; if (state === 'closed' || state === 'errored') { return promiseResolvedWith(undefined); } if (stream._pendingAbortRequest !== undefined) { return stream._pendingAbortRequest._promise; } let wasAlreadyErroring = false; if (state === 'erroring') { wasAlreadyErroring = true; // reason will not be used, so don't keep a reference to it. reason = undefined; } const promise = newPromise((resolve, reject) => { stream._pendingAbortRequest = { _promise: undefined, _resolve: resolve, _reject: reject, _reason: reason, _wasAlreadyErroring: wasAlreadyErroring }; }); stream._pendingAbortRequest._promise = promise; if (wasAlreadyErroring === false) { WritableStreamStartErroring(stream, reason); } return promise; } function WritableStreamClose(stream) { const state = stream._state; if (state === 'closed' || state === 'errored') { return promiseRejectedWith(new TypeError(`The stream (in ${state} state) is not in the writable state and cannot be closed`)); } const promise = newPromise((resolve, reject) => { const closeRequest = { _resolve: resolve, _reject: reject }; stream._closeRequest = closeRequest; }); const writer = stream._writer; if (writer !== undefined && stream._backpressure === true && state === 'writable') { defaultWriterReadyPromiseResolve(writer); } WritableStreamDefaultControllerClose(stream._writableStreamController); return promise; } // WritableStream API exposed for controllers. function WritableStreamAddWriteRequest(stream) { const promise = newPromise((resolve, reject) => { const writeRequest = { _resolve: resolve, _reject: reject }; stream._writeRequests.push(writeRequest); }); return promise; } function WritableStreamDealWithRejection(stream, error) { const state = stream._state; if (state === 'writable') { WritableStreamStartErroring(stream, error); return; } WritableStreamFinishErroring(stream); } function WritableStreamStartErroring(stream, reason) { const controller = stream._writableStreamController; stream._state = 'erroring'; stream._storedError = reason; const writer = stream._writer; if (writer !== undefined) { WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason); } if (WritableStreamHasOperationMarkedInFlight(stream) === false && controller._started === true) { WritableStreamFinishErroring(stream); } } function WritableStreamFinishErroring(stream) { stream._state = 'errored'; stream._writableStreamController[ErrorSteps](); const storedError = stream._storedError; stream._writeRequests.forEach(writeRequest => { writeRequest._reject(storedError); }); stream._writeRequests = new SimpleQueue(); if (stream._pendingAbortRequest === undefined) { WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); return; } const abortRequest = stream._pendingAbortRequest; stream._pendingAbortRequest = undefined; if (abortRequest._wasAlreadyErroring === true) { abortRequest._reject(storedError); WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); return; } const promise = stream._writableStreamController[AbortSteps](abortRequest._reason); uponPromise(promise, () => { abortRequest._resolve(); WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); }, (reason) => { abortRequest._reject(reason); WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); }); } function WritableStreamFinishInFlightWrite(stream) { stream._inFlightWriteRequest._resolve(undefined); stream._inFlightWriteRequest = undefined; } function WritableStreamFinishInFlightWriteWithError(stream, error) { stream._inFlightWriteRequest._reject(error); stream._inFlightWriteRequest = undefined; WritableStreamDealWithRejection(stream, error); } function WritableStreamFinishInFlightClose(stream) { stream._inFlightCloseRequest._resolve(undefined); stream._inFlightCloseRequest = undefined; const state = stream._state; if (state === 'erroring') { // The error was too late to do anything, so it is ignored. stream._storedError = undefined; if (stream._pendingAbortRequest !== undefined) { stream._pendingAbortRequest._resolve(); stream._pendingAbortRequest = undefined; } } stream._state = 'closed'; const writer = stream._writer; if (writer !== undefined) { defaultWriterClosedPromiseResolve(writer); } } function WritableStreamFinishInFlightCloseWithError(stream, error) { stream._inFlightCloseRequest._reject(error); stream._inFlightCloseRequest = undefined; // Never execute sink abort() after sink close(). if (stream._pendingAbortRequest !== undefined) { stream._pendingAbortRequest._reject(error); stream._pendingAbortRequest = undefined; } WritableStreamDealWithRejection(stream, error); } // TODO(ricea): Fix alphabetical order. function WritableStreamCloseQueuedOrInFlight(stream) { if (stream._closeRequest === undefined && stream._inFlightCloseRequest === undefined) { return false; } return true; } function WritableStreamHasOperationMarkedInFlight(stream) { if (stream._inFlightWriteRequest === undefined && stream._inFlightCloseRequest === undefined) { return false; } return true; } function WritableStreamMarkCloseRequestInFlight(stream) { stream._inFlightCloseRequest = stream._closeRequest; stream._closeRequest = undefined; } function WritableStreamMarkFirstWriteRequestInFlight(stream) { stream._inFlightWriteRequest = stream._writeRequests.shift(); } function WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream) { if (stream._closeRequest !== undefined) { stream._closeRequest._reject(stream._storedError); stream._closeRequest = undefined; } const writer = stream._writer; if (writer !== undefined) { defaultWriterClosedPromiseReject(writer, stream._storedError); } } function WritableStreamUpdateBackpressure(stream, backpressure) { const writer = stream._writer; if (writer !== undefined && backpressure !== stream._backpressure) { if (backpressure === true) { defaultWriterReadyPromiseReset(writer); } else { defaultWriterReadyPromiseResolve(writer); } } stream._backpressure = backpressure; } class WritableStreamDefaultWriter { constructor(stream) { if (IsWritableStream(stream) === false) { throw new TypeError('WritableStreamDefaultWriter can only be constructed with a WritableStream instance'); } if (IsWritableStreamLocked(stream) === true) { throw new TypeError('This stream has already been locked for exclusive writing by another writer'); } this._ownerWritableStream = stream; stream._writer = this; const state = stream._state; if (state === 'writable') { if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._backpressure === true) { defaultWriterReadyPromiseInitialize(this); } else { defaultWriterReadyPromiseInitializeAsResolved(this); } defaultWriterClosedPromiseInitialize(this); } else if (state === 'erroring') { defaultWriterReadyPromiseInitializeAsRejected(this, stream._storedError); defaultWriterClosedPromiseInitialize(this); } else if (state === 'closed') { defaultWriterReadyPromiseInitializeAsResolved(this); defaultWriterClosedPromiseInitializeAsResolved(this); } else { const storedError = stream._storedError; defaultWriterReadyPromiseInitializeAsRejected(this, storedError); defaultWriterClosedPromiseInitializeAsRejected(this, storedError); } } get closed() { if (IsWritableStreamDefaultWriter(this) === false) { return promiseRejectedWith(defaultWriterBrandCheckException('closed')); } return this._closedPromise; } get desiredSize() { if (IsWritableStreamDefaultWriter(this) === false) { throw defaultWriterBrandCheckException('desiredSize'); } if (this._ownerWritableStream === undefined) { throw defaultWriterLockException('desiredSize'); } return WritableStreamDefaultWriterGetDesiredSize(this); } get ready() { if (IsWritableStreamDefaultWriter(this) === false) { return promiseRejectedWith(defaultWriterBrandCheckException('ready')); } return this._readyPromise; } abort(reason) { if (IsWritableStreamDefaultWriter(this) === false) { return promiseRejectedWith(defaultWriterBrandCheckException('abort')); } if (this._ownerWritableStream === undefined) { return promiseRejectedWith(defaultWriterLockException('abort')); } return WritableStreamDefaultWriterAbort(this, reason); } close() { if (IsWritableStreamDefaultWriter(this) === false) { return promiseRejectedWith(defaultWriterBrandCheckException('close')); } const stream = this._ownerWritableStream; if (stream === undefined) { return promiseRejectedWith(defaultWriterLockException('close')); } if (WritableStreamCloseQueuedOrInFlight(stream) === true) { return promiseRejectedWith(new TypeError('Cannot close an already-closing stream')); } return WritableStreamDefaultWriterClose(this); } releaseLock() { if (IsWritableStreamDefaultWriter(this) === false) { throw defaultWriterBrandCheckException('releaseLock'); } const stream = this._ownerWritableStream; if (stream === undefined) { return; } WritableStreamDefaultWriterRelease(this); } write(chunk) { if (IsWritableStreamDefaultWriter(this) === false) { return promiseRejectedWith(defaultWriterBrandCheckException('write')); } if (this._ownerWritableStream === undefined) { return promiseRejectedWith(defaultWriterLockException('write to')); } return WritableStreamDefaultWriterWrite(this, chunk); } } // Abstract operations for the WritableStreamDefaultWriter. function IsWritableStreamDefaultWriter(x) { if (!typeIsObject(x)) { return false; } if (!Object.prototype.hasOwnProperty.call(x, '_ownerWritableStream')) { return false; } return true; } // A client of WritableStreamDefaultWriter may use these functions directly to bypass state check. function WritableStreamDefaultWriterAbort(writer, reason) { const stream = writer._ownerWritableStream; return WritableStreamAbort(stream, reason); } function WritableStreamDefaultWriterClose(writer) { const stream = writer._ownerWritableStream; return WritableStreamClose(stream); } function WritableStreamDefaultWriterCloseWithErrorPropagation(writer) { const stream = writer._ownerWritableStream; const state = stream._state; if (WritableStreamCloseQueuedOrInFlight(stream) === true || state === 'closed') { return promiseResolvedWith(undefined); } if (state === 'errored') { return promiseRejectedWith(stream._storedError); } return WritableStreamDefaultWriterClose(writer); } function WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, error) { if (writer._closedPromiseState === 'pending') { defaultWriterClosedPromiseReject(writer, error); } else { defaultWriterClosedPromiseResetToRejected(writer, error); } } function WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error) { if (writer._readyPromiseState === 'pending') { defaultWriterReadyPromiseReject(writer, error); } else { defaultWriterReadyPromiseResetToRejected(writer, error); } } function WritableStreamDefaultWriterGetDesiredSize(writer) { const stream = writer._ownerWritableStream; const state = stream._state; if (state === 'errored' || state === 'erroring') { return null; } if (state === 'closed') { return 0; } return WritableStreamDefaultControllerGetDesiredSize(stream._writableStreamController); } function WritableStreamDefaultWriterRelease(writer) { const stream = writer._ownerWritableStream; const releasedError = new TypeError('Writer was released and can no longer be used to monitor the stream\'s closedness'); WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError); // The state transitions to "errored" before the sink abort() method runs, but the writer.closed promise is not // rejected until afterwards. This means that simply testing state will not work. WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError); stream._writer = undefined; writer._ownerWritableStream = undefined; } function WritableStreamDefaultWriterWrite(writer, chunk) { const stream = writer._ownerWritableStream; const controller = stream._writableStreamController; const chunkSize = WritableStreamDefaultControllerGetChunkSize(controller, chunk); if (stream !== writer._ownerWritableStream) { return promiseRejectedWith(defaultWriterLockException('write to')); } const state = stream._state; if (state === 'errored') { return promiseRejectedWith(stream._storedError); } if (WritableStreamCloseQueuedOrInFlight(stream) === true || state === 'closed') { return promiseRejectedWith(new TypeError('The stream is closing or closed and cannot be written to')); } if (state === 'erroring') { return promiseRejectedWith(stream._storedError); } const promise = WritableStreamAddWriteRequest(stream); WritableStreamDefaultControllerWrite(controller, chunk, chunkSize); return promise; } class WritableStreamDefaultController { /** @internal */ constructor() { throw new TypeError('WritableStreamDefaultController cannot be constructed explicitly'); } error(e) { if (IsWritableStreamDefaultController(this) === false) { throw new TypeError('WritableStreamDefaultController.prototype.error can only be used on a WritableStreamDefaultController'); } const state = this._controlledWritableStream._state; if (state !== 'writable') { // The stream is closed, errored or will be soon. The sink can't do anything useful if it gets an error here, so // just treat it as a no-op. return; } WritableStreamDefaultControllerError(this, e); } /** @internal */ [AbortSteps](reason) { const result = this._abortAlgorithm(reason); WritableStreamDefaultControllerClearAlgorithms(this); return result; } /** @internal */ [ErrorSteps]() { ResetQueue(this); } } // Abstract operations implementing interface required by the WritableStream. function IsWritableStreamDefaultController(x) { if (!typeIsObject(x)) { return false; } if (!Object.prototype.hasOwnProperty.call(x, '_controlledWritableStream')) { return false; } return true; } function SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm) { controller._controlledWritableStream = stream; stream._writableStreamController = controller; // Need to set the slots so that the assert doesn't fire. In the spec the slots already exist implicitly. controller._queue = undefined; controller._queueTotalSize = undefined; ResetQueue(controller); controller._started = false; controller._strategySizeAlgorithm = sizeAlgorithm; controller._strategyHWM = highWaterMark; controller._writeAlgorithm = writeAlgorithm; controller._closeAlgorithm = closeAlgorithm; controller._abortAlgorithm = abortAlgorithm; const backpressure = WritableStreamDefaultControllerGetBackpressure(controller); WritableStreamUpdateBackpressure(stream, backpressure); const startResult = startAlgorithm(); const startPromise = promiseResolvedWith(startResult); uponPromise(startPromise, () => { controller._started = true; WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); }, r => { controller._started = true; WritableStreamDealWithRejection(stream, r); }); } function SetUpWritableStreamDefaultControllerFromUnderlyingSink(stream, underlyingSink, highWaterMark, sizeAlgorithm) { const controller = Object.create(WritableStreamDefaultController.prototype); function startAlgorithm() { return InvokeOrNoop(underlyingSink, 'start', [controller]); } const writeAlgorithm = CreateAlgorithmFromUnderlyingMethod(underlyingSink, 'write', 1, [controller]); const closeAlgorithm = CreateAlgorithmFromUnderlyingMethod(underlyingSink, 'close', 0, []); const abortAlgorithm = CreateAlgorithmFromUnderlyingMethod(underlyingSink, 'abort', 1, []); SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm); } // ClearAlgorithms may be called twice. Erroring the same stream in multiple ways will often result in redundant calls. function WritableStreamDefaultControllerClearAlgorithms(controller) { controller._writeAlgorithm = undefined; controller._closeAlgorithm = undefined; controller._abortAlgorithm = undefined; controller._strategySizeAlgorithm = undefined; } function WritableStreamDefaultControllerClose(controller) { EnqueueValueWithSize(controller, 'close', 0); WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); } function WritableStreamDefaultControllerGetChunkSize(controller, chunk) { try { return controller._strategySizeAlgorithm(chunk); } catch (chunkSizeE) { WritableStreamDefaultControllerErrorIfNeeded(controller, chunkSizeE); return 1; } } function WritableStreamDefaultControllerGetDesiredSize(controller) { return controller._strategyHWM - controller._queueTotalSize; } function WritableStreamDefaultControllerWrite(controller, chunk, chunkSize) { const writeRecord = { chunk }; try { EnqueueValueWithSize(controller, writeRecord, chunkSize); } catch (enqueueE) { WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueE); return; } const stream = controller._controlledWritableStream; if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._state === 'writable') { const backpressure = WritableStreamDefaultControllerGetBackpressure(controller); WritableStreamUpdateBackpressure(stream, backpressure); } WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); } // Abstract operations for the WritableStreamDefaultController. function WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller) { const stream = controller._controlledWritableStream; if (controller._started === false) {