mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-19 02:43:07 +09:00
208 lines
6 KiB
JavaScript
208 lines
6 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 ReadableStream pipeThrough Polyfill (best-effort)
|
|
// @namespace internal-userscripts
|
|
// @description Adds ReadableStream.prototype.pipeThrough using pipeTo or reader/writer pumping when missing.
|
|
// @match *://*/*
|
|
// @grant none
|
|
// @run-at document-start
|
|
// ==/UserScript==
|
|
(function () {
|
|
"use strict";
|
|
|
|
var global = typeof window !== "undefined" ? window : null;
|
|
if (!global || !global.ReadableStream || !global.ReadableStream.prototype) {
|
|
return;
|
|
}
|
|
var existingPipeThrough = global.ReadableStream.prototype.pipeThrough;
|
|
if (typeof existingPipeThrough === "function") {
|
|
if (typeof global.TransformStream !== "function") {
|
|
return;
|
|
}
|
|
try {
|
|
var probeStream = new global.ReadableStream({
|
|
start: function (controller) {
|
|
controller.close();
|
|
}
|
|
});
|
|
probeStream.pipeThrough(new global.TransformStream(), { preventClose: true });
|
|
return;
|
|
} catch (e) {
|
|
if (String(e).indexOf("not yet implemented") === -1) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
var PromiseCtor = global.Promise;
|
|
if (typeof PromiseCtor !== "function") {
|
|
return;
|
|
}
|
|
|
|
function isObject(value) {
|
|
return value !== null && (typeof value === "object" || typeof value === "function");
|
|
}
|
|
|
|
function getTransformStream(transform, name) {
|
|
var stream = transform[name];
|
|
if (!isObject(stream)) {
|
|
throw new TypeError("pipeThrough requires a transform stream with " + name);
|
|
}
|
|
return stream;
|
|
}
|
|
|
|
function startPipeWithReader(source, writable, options) {
|
|
var reader = source.getReader();
|
|
var writer = writable.getWriter();
|
|
var preventClose = options && options.preventClose;
|
|
var preventAbort = options && options.preventAbort;
|
|
var preventCancel = options && options.preventCancel;
|
|
var signal = options && options.signal;
|
|
var abortHandler = null;
|
|
|
|
function safeReleaseLock(stream, maybeReader, lockedProp) {
|
|
if (!maybeReader) {
|
|
return;
|
|
}
|
|
var isLocked = true;
|
|
if (typeof lockedProp === "function") {
|
|
try {
|
|
var lockedValue = lockedProp();
|
|
if (typeof lockedValue === "boolean") {
|
|
isLocked = lockedValue;
|
|
}
|
|
} catch (e) {
|
|
isLocked = true;
|
|
}
|
|
}
|
|
if (!isLocked) {
|
|
return;
|
|
}
|
|
var releaseFn = null;
|
|
try {
|
|
releaseFn = maybeReader.releaseLock;
|
|
} catch (e) {
|
|
releaseFn = null;
|
|
}
|
|
if (typeof releaseFn === "function") {
|
|
try { releaseFn.call(maybeReader); } catch (e) {}
|
|
}
|
|
}
|
|
|
|
function releaseLocks() {
|
|
safeReleaseLock(source, reader, function () { return source.locked; });
|
|
safeReleaseLock(writable, writer, function () { return writable.locked; });
|
|
reader = null;
|
|
writer = null;
|
|
}
|
|
|
|
function cleanupAbort() {
|
|
if (abortHandler && signal && typeof signal.removeEventListener === "function") {
|
|
signal.removeEventListener("abort", abortHandler);
|
|
}
|
|
}
|
|
|
|
if (signal) {
|
|
abortHandler = function () {
|
|
var reason = signal.reason;
|
|
if (!preventCancel) {
|
|
try { reader.cancel(reason); } catch (e) {}
|
|
}
|
|
if (!preventAbort) {
|
|
try { writer.abort(reason); } catch (e) {}
|
|
}
|
|
};
|
|
if (signal.aborted) {
|
|
abortHandler();
|
|
cleanupAbort();
|
|
releaseLocks();
|
|
return;
|
|
}
|
|
if (typeof signal.addEventListener === "function") {
|
|
signal.addEventListener("abort", abortHandler);
|
|
}
|
|
}
|
|
|
|
function pump() {
|
|
var readPromise;
|
|
try {
|
|
readPromise = reader.read();
|
|
} catch (e) {
|
|
return PromiseCtor.reject(e);
|
|
}
|
|
return readPromise.then(function (result) {
|
|
if (result.done) {
|
|
if (!preventClose) {
|
|
var closePromise;
|
|
try {
|
|
closePromise = writer.close();
|
|
} catch (e) {
|
|
return PromiseCtor.reject(e);
|
|
}
|
|
return PromiseCtor.resolve(closePromise);
|
|
}
|
|
return undefined;
|
|
}
|
|
var writePromise;
|
|
try {
|
|
writePromise = writer.write(result.value);
|
|
} catch (e) {
|
|
return PromiseCtor.reject(e);
|
|
}
|
|
return PromiseCtor.resolve(writePromise).then(pump);
|
|
});
|
|
}
|
|
|
|
pump().catch(function (err) {
|
|
if (!preventCancel) {
|
|
try { reader.cancel(err); } catch (e) {}
|
|
}
|
|
if (!preventAbort) {
|
|
try { writer.abort(err); } catch (e) {}
|
|
}
|
|
}).then(function () {
|
|
cleanupAbort();
|
|
releaseLocks();
|
|
}, function () {
|
|
cleanupAbort();
|
|
releaseLocks();
|
|
});
|
|
}
|
|
|
|
function pipeThrough(transform, options) {
|
|
if (!isObject(this)) {
|
|
throw new TypeError("ReadableStream.prototype.pipeThrough called on incompatible receiver");
|
|
}
|
|
if (!isObject(transform)) {
|
|
throw new TypeError("pipeThrough requires a transform stream");
|
|
}
|
|
var readable = getTransformStream(transform, "readable");
|
|
var writable = getTransformStream(transform, "writable");
|
|
var source = this;
|
|
|
|
if (typeof source.pipeTo === "function") {
|
|
var pipePromise = source.pipeTo(writable, options);
|
|
if (pipePromise && typeof pipePromise.catch === "function") {
|
|
pipePromise.catch(function () {});
|
|
}
|
|
} else if (typeof source.getReader === "function" && typeof writable.getWriter === "function") {
|
|
startPipeWithReader(source, writable, options);
|
|
} else {
|
|
throw new TypeError("ReadableStream.prototype.pipeThrough is not supported");
|
|
}
|
|
|
|
return readable;
|
|
}
|
|
|
|
Object.defineProperty(global.ReadableStream.prototype, "pipeThrough", {
|
|
value: pipeThrough,
|
|
writable: true,
|
|
configurable: true
|
|
});
|
|
|
|
try {
|
|
global.__internalUserscriptsReadableStreamPipeThroughPolyfill = true;
|
|
} catch (e) {}
|
|
})();
|