mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-15 17:03:07 +09:00
430 lines
12 KiB
JavaScript
430 lines
12 KiB
JavaScript
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
// ==UserScript==
|
|
// @name TransformStream Polyfill (best-effort)
|
|
// @namespace internal-userscripts
|
|
// @description Minimal TransformStream implementation backed by ReadableStream and a lightweight WritableStream shim. Not a full Streams spec implementation.
|
|
// @match *://*/*
|
|
// @grant none
|
|
// @run-at document-start
|
|
// ==/UserScript==
|
|
(function () {
|
|
"use strict";
|
|
|
|
var global = typeof window !== "undefined" ? window : null;
|
|
if (!global || typeof global.TransformStream === "function") {
|
|
return;
|
|
}
|
|
|
|
var ReadableStreamCtor = global.ReadableStream;
|
|
var PromiseCtor = global.Promise;
|
|
if (typeof PromiseCtor !== "function") {
|
|
return;
|
|
}
|
|
|
|
function isObject(value) {
|
|
return value !== null && (typeof value === "object" || typeof value === "function");
|
|
}
|
|
|
|
function streamBrandCheckException(name) {
|
|
return new TypeError("TransformStream.prototype." + name +
|
|
" can only be used on a TransformStream");
|
|
}
|
|
|
|
function PolyfillWritableStream(underlyingSink) {
|
|
if (!(this instanceof PolyfillWritableStream)) {
|
|
throw new TypeError("WritableStream must be constructed with 'new'");
|
|
}
|
|
this._sink = underlyingSink || {};
|
|
this._state = "writable";
|
|
this._storedError = undefined;
|
|
this._writer = null;
|
|
this._readyPromise = PromiseCtor.resolve();
|
|
|
|
var resolveClosed;
|
|
var rejectClosed;
|
|
this._closedPromise = new PromiseCtor(function (resolve, reject) {
|
|
resolveClosed = resolve;
|
|
rejectClosed = reject;
|
|
});
|
|
this._closedResolve = resolveClosed;
|
|
this._closedReject = rejectClosed;
|
|
|
|
if (typeof this._sink.start === "function") {
|
|
try {
|
|
this._sink.start(this);
|
|
} catch (e) {
|
|
this._error(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
PolyfillWritableStream.prototype.getWriter = function () {
|
|
if (this._writer) {
|
|
throw new TypeError("WritableStream is locked");
|
|
}
|
|
this._writer = new PolyfillWritableStreamDefaultWriter(this);
|
|
return this._writer;
|
|
};
|
|
|
|
PolyfillWritableStream.prototype._releaseWriter = function (writer) {
|
|
if (this._writer === writer) {
|
|
this._writer = null;
|
|
}
|
|
};
|
|
|
|
PolyfillWritableStream.prototype._error = function (reason) {
|
|
if (this._state === "closed" || this._state === "errored") {
|
|
return;
|
|
}
|
|
this._state = "errored";
|
|
this._storedError = reason;
|
|
if (this._closedReject) {
|
|
this._closedReject(reason);
|
|
}
|
|
};
|
|
|
|
PolyfillWritableStream.prototype._write = function (chunk) {
|
|
if (this._state !== "writable") {
|
|
return PromiseCtor.reject(new TypeError("WritableStream is not writable"));
|
|
}
|
|
if (!this._sink || typeof this._sink.write !== "function") {
|
|
return PromiseCtor.resolve();
|
|
}
|
|
try {
|
|
return PromiseCtor.resolve(this._sink.write(chunk));
|
|
} catch (e) {
|
|
this._error(e);
|
|
return PromiseCtor.reject(e);
|
|
}
|
|
};
|
|
|
|
PolyfillWritableStream.prototype._close = function () {
|
|
if (this._state !== "writable") {
|
|
return PromiseCtor.reject(new TypeError("WritableStream is not writable"));
|
|
}
|
|
this._state = "closing";
|
|
|
|
var closeResult;
|
|
if (this._sink && typeof this._sink.close === "function") {
|
|
try {
|
|
closeResult = this._sink.close();
|
|
} catch (e) {
|
|
this._error(e);
|
|
return PromiseCtor.reject(e);
|
|
}
|
|
}
|
|
|
|
var self = this;
|
|
return PromiseCtor.resolve(closeResult).then(function () {
|
|
self._state = "closed";
|
|
if (self._closedResolve) {
|
|
self._closedResolve();
|
|
}
|
|
}, function (e) {
|
|
self._error(e);
|
|
throw e;
|
|
});
|
|
};
|
|
|
|
PolyfillWritableStream.prototype._abort = function (reason) {
|
|
if (this._state === "closed") {
|
|
return PromiseCtor.resolve();
|
|
}
|
|
this._error(reason);
|
|
|
|
if (this._sink && typeof this._sink.abort === "function") {
|
|
try {
|
|
return PromiseCtor.resolve(this._sink.abort(reason));
|
|
} catch (e) {
|
|
return PromiseCtor.reject(e);
|
|
}
|
|
}
|
|
return PromiseCtor.resolve();
|
|
};
|
|
|
|
function PolyfillWritableStreamDefaultWriter(stream) {
|
|
this._stream = stream;
|
|
}
|
|
|
|
PolyfillWritableStreamDefaultWriter.prototype._assertStream = function (name) {
|
|
if (!this._stream) {
|
|
throw new TypeError("Cannot " + name + " a stream using a released writer");
|
|
}
|
|
return this._stream;
|
|
};
|
|
|
|
Object.defineProperty(PolyfillWritableStreamDefaultWriter.prototype, "ready", {
|
|
get: function () {
|
|
return this._assertStream("get ready state for")._readyPromise;
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
|
|
Object.defineProperty(PolyfillWritableStreamDefaultWriter.prototype, "closed", {
|
|
get: function () {
|
|
return this._assertStream("get closed state for")._closedPromise;
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
|
|
PolyfillWritableStreamDefaultWriter.prototype.write = function (chunk) {
|
|
return this._assertStream("write to")._write(chunk);
|
|
};
|
|
|
|
PolyfillWritableStreamDefaultWriter.prototype.close = function () {
|
|
return this._assertStream("close")._close();
|
|
};
|
|
|
|
PolyfillWritableStreamDefaultWriter.prototype.abort = function (reason) {
|
|
return this._assertStream("abort")._abort(reason);
|
|
};
|
|
|
|
PolyfillWritableStreamDefaultWriter.prototype.releaseLock = function () {
|
|
var stream = this._stream;
|
|
if (!stream) {
|
|
return;
|
|
}
|
|
stream._releaseWriter(this);
|
|
this._stream = null;
|
|
};
|
|
|
|
var WritableStreamCtor = typeof global.WritableStream === "function"
|
|
? global.WritableStream
|
|
: PolyfillWritableStream;
|
|
|
|
function TransformStreamDefaultController(stream) {
|
|
this._stream = stream;
|
|
this._readableController = null;
|
|
}
|
|
|
|
Object.defineProperty(TransformStreamDefaultController.prototype, "desiredSize", {
|
|
get: function () {
|
|
if (!this._readableController) {
|
|
return null;
|
|
}
|
|
return this._readableController.desiredSize;
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
|
|
TransformStreamDefaultController.prototype.enqueue = function (chunk) {
|
|
var controller = this._readableController;
|
|
if (!controller) {
|
|
throw new TypeError("ReadableStream controller is not available");
|
|
}
|
|
controller.enqueue(chunk);
|
|
};
|
|
|
|
TransformStreamDefaultController.prototype.error = function (reason) {
|
|
this._stream._error(reason);
|
|
};
|
|
|
|
TransformStreamDefaultController.prototype.terminate = function () {
|
|
this._stream._terminate();
|
|
};
|
|
|
|
function isTransformStream(value) {
|
|
return isObject(value) && value._isTransformStream === true;
|
|
}
|
|
|
|
function PolyfillTransformStream(transformer, writableStrategy, readableStrategy) {
|
|
if (!(this instanceof PolyfillTransformStream)) {
|
|
throw new TypeError("TransformStream must be constructed with 'new'");
|
|
}
|
|
|
|
if (typeof ReadableStreamCtor !== "function") {
|
|
throw new TypeError("ReadableStream is not supported in this environment");
|
|
}
|
|
|
|
transformer = transformer || {};
|
|
writableStrategy = writableStrategy || {};
|
|
readableStrategy = readableStrategy || {};
|
|
|
|
if (transformer.writableType !== undefined) {
|
|
throw new RangeError("Invalid writable type specified");
|
|
}
|
|
if (transformer.readableType !== undefined) {
|
|
throw new RangeError("Invalid readable type specified");
|
|
}
|
|
|
|
this._transformer = transformer;
|
|
this._controller = new TransformStreamDefaultController(this);
|
|
this._readableClosed = false;
|
|
this._errored = false;
|
|
this._terminated = false;
|
|
this._pendingError = undefined;
|
|
this._hasPendingError = false;
|
|
this._isTransformStream = true;
|
|
|
|
var self = this;
|
|
|
|
this._readable = new ReadableStreamCtor({
|
|
start: function (controller) {
|
|
self._controller._readableController = controller;
|
|
if (self._hasPendingError) {
|
|
controller.error(self._pendingError);
|
|
return;
|
|
}
|
|
if (typeof transformer.start === "function") {
|
|
return transformer.start(self._controller);
|
|
}
|
|
},
|
|
pull: function () {},
|
|
cancel: function (reason) {
|
|
self._error(reason);
|
|
return PromiseCtor.resolve();
|
|
}
|
|
}, readableStrategy);
|
|
|
|
this._writable = new WritableStreamCtor({
|
|
write: function (chunk) {
|
|
return self._transform(chunk);
|
|
},
|
|
close: function () {
|
|
return self._flush();
|
|
},
|
|
abort: function (reason) {
|
|
self._error(reason);
|
|
return PromiseCtor.resolve();
|
|
}
|
|
}, writableStrategy);
|
|
}
|
|
|
|
Object.defineProperty(PolyfillTransformStream.prototype, "readable", {
|
|
get: function () {
|
|
if (!isTransformStream(this)) {
|
|
throw streamBrandCheckException("readable");
|
|
}
|
|
return this._readable;
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
|
|
Object.defineProperty(PolyfillTransformStream.prototype, "writable", {
|
|
get: function () {
|
|
if (!isTransformStream(this)) {
|
|
throw streamBrandCheckException("writable");
|
|
}
|
|
return this._writable;
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
|
|
PolyfillTransformStream.prototype._transform = function (chunk) {
|
|
if (this._errored || this._terminated) {
|
|
return PromiseCtor.reject(new TypeError("TransformStream is not writable"));
|
|
}
|
|
|
|
var transformer = this._transformer;
|
|
var controller = this._controller;
|
|
var result;
|
|
|
|
try {
|
|
if (typeof transformer.transform === "function") {
|
|
result = transformer.transform(chunk, controller);
|
|
} else {
|
|
controller.enqueue(chunk);
|
|
}
|
|
} catch (e) {
|
|
this._error(e);
|
|
return PromiseCtor.reject(e);
|
|
}
|
|
|
|
var self = this;
|
|
return PromiseCtor.resolve(result).then(function () {
|
|
return undefined;
|
|
}, function (e) {
|
|
self._error(e);
|
|
throw e;
|
|
});
|
|
};
|
|
|
|
PolyfillTransformStream.prototype._flush = function () {
|
|
if (this._errored || this._terminated) {
|
|
return PromiseCtor.reject(new TypeError("TransformStream is not writable"));
|
|
}
|
|
|
|
var transformer = this._transformer;
|
|
var controller = this._controller;
|
|
var result;
|
|
|
|
try {
|
|
if (typeof transformer.flush === "function") {
|
|
result = transformer.flush(controller);
|
|
}
|
|
} catch (e) {
|
|
this._error(e);
|
|
return PromiseCtor.reject(e);
|
|
}
|
|
|
|
var self = this;
|
|
return PromiseCtor.resolve(result).then(function () {
|
|
self._closeReadable();
|
|
}, function (e) {
|
|
self._error(e);
|
|
throw e;
|
|
});
|
|
};
|
|
|
|
PolyfillTransformStream.prototype._closeReadable = function () {
|
|
if (this._readableClosed) {
|
|
return;
|
|
}
|
|
this._readableClosed = true;
|
|
|
|
var controller = this._controller._readableController;
|
|
if (!controller) {
|
|
return;
|
|
}
|
|
try {
|
|
controller.close();
|
|
} catch (e) {}
|
|
};
|
|
|
|
PolyfillTransformStream.prototype._error = function (reason) {
|
|
if (this._errored) {
|
|
return;
|
|
}
|
|
this._errored = true;
|
|
|
|
var controller = this._controller._readableController;
|
|
if (controller) {
|
|
try {
|
|
controller.error(reason);
|
|
} catch (e) {}
|
|
} else {
|
|
this._pendingError = reason;
|
|
this._hasPendingError = true;
|
|
}
|
|
|
|
if (this._writable && typeof this._writable._error === "function") {
|
|
this._writable._error(reason);
|
|
}
|
|
};
|
|
|
|
PolyfillTransformStream.prototype._terminate = function () {
|
|
if (this._terminated) {
|
|
return;
|
|
}
|
|
this._terminated = true;
|
|
this._closeReadable();
|
|
if (this._writable && typeof this._writable._error === "function") {
|
|
this._writable._error(new TypeError("TransformStream terminated"));
|
|
}
|
|
};
|
|
|
|
global.TransformStream = PolyfillTransformStream;
|
|
if (typeof global.WritableStream !== "function") {
|
|
global.WritableStream = PolyfillWritableStream;
|
|
}
|
|
try {
|
|
global.__internalUserscriptsTransformStreamPolyfill = true;
|
|
} catch (e) {}
|
|
})();
|