mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-21 03:43:16 +09:00
221 lines
6 KiB
JavaScript
221 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 pipeTo Polyfill (best-effort)
|
|
// @namespace internal-userscripts
|
|
// @description Adds ReadableStream.prototype.pipeTo using reader/writer pumping.
|
|
// @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 PromiseCtor = global.Promise;
|
|
if (typeof PromiseCtor !== "function") {
|
|
return;
|
|
}
|
|
|
|
function isObject(value) {
|
|
return value !== null && (typeof value === "object" || typeof value === "function");
|
|
}
|
|
|
|
var existingPipeTo = global.ReadableStream.prototype.pipeTo;
|
|
if (typeof existingPipeTo === "function") {
|
|
if (typeof global.TransformStream !== "function" &&
|
|
typeof global.WritableStream !== "function") {
|
|
return;
|
|
}
|
|
try {
|
|
var testWritable = null;
|
|
if (typeof global.TransformStream === "function") {
|
|
testWritable = new global.TransformStream().writable;
|
|
} else {
|
|
testWritable = new global.WritableStream();
|
|
}
|
|
var probeStream = new global.ReadableStream({
|
|
start: function (controller) {
|
|
controller.close();
|
|
}
|
|
});
|
|
probeStream.pipeTo(testWritable, { preventClose: true });
|
|
return;
|
|
} catch (e) {
|
|
if (String(e).indexOf("not yet implemented") === -1) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
function pipeTo(writable, options) {
|
|
if (!isObject(this)) {
|
|
throw new TypeError("ReadableStream.prototype.pipeTo called on incompatible receiver");
|
|
}
|
|
if (!isObject(writable) || typeof writable.getWriter !== "function") {
|
|
throw new TypeError("pipeTo requires a writable stream");
|
|
}
|
|
|
|
var source = this;
|
|
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;
|
|
var abortReject = null;
|
|
var aborted = false;
|
|
var abortReason = undefined;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
function abortWith(reason) {
|
|
if (aborted) {
|
|
return;
|
|
}
|
|
aborted = true;
|
|
abortReason = reason;
|
|
if (!preventCancel) {
|
|
try { reader.cancel(reason); } catch (e) {}
|
|
}
|
|
if (!preventAbort) {
|
|
try { writer.abort(reason); } catch (e) {}
|
|
}
|
|
if (abortReject) {
|
|
abortReject(reason);
|
|
}
|
|
}
|
|
|
|
var abortPromise = null;
|
|
if (signal) {
|
|
abortPromise = new PromiseCtor(function (_resolve, reject) {
|
|
abortReject = reject;
|
|
});
|
|
abortHandler = function () {
|
|
abortWith(signal.reason);
|
|
};
|
|
if (signal.aborted) {
|
|
abortWith(signal.reason);
|
|
} else if (typeof signal.addEventListener === "function") {
|
|
signal.addEventListener("abort", abortHandler);
|
|
}
|
|
}
|
|
|
|
function pump() {
|
|
if (aborted) {
|
|
return PromiseCtor.reject(abortReason);
|
|
}
|
|
var readPromise;
|
|
try {
|
|
readPromise = reader.read();
|
|
} catch (e) {
|
|
return PromiseCtor.reject(e);
|
|
}
|
|
return readPromise.then(function (result) {
|
|
if (aborted) {
|
|
throw abortReason;
|
|
}
|
|
if (result.done) {
|
|
if (!preventClose) {
|
|
return writer.close();
|
|
}
|
|
return undefined;
|
|
}
|
|
var writePromise;
|
|
try {
|
|
writePromise = writer.write(result.value);
|
|
} catch (e) {
|
|
return PromiseCtor.reject(e);
|
|
}
|
|
return PromiseCtor.resolve(writePromise).then(pump);
|
|
});
|
|
}
|
|
|
|
var pipePromise = pump().catch(function (err) {
|
|
if (!preventCancel) {
|
|
try { reader.cancel(err); } catch (e) {}
|
|
}
|
|
if (!preventAbort) {
|
|
try { writer.abort(err); } catch (e) {}
|
|
}
|
|
throw err;
|
|
});
|
|
|
|
var finalPromise = abortPromise
|
|
? PromiseCtor.race([pipePromise, abortPromise])
|
|
: pipePromise;
|
|
|
|
function settleAndRelease(err) {
|
|
return pipePromise.catch(function () {}).then(function () {
|
|
cleanupAbort();
|
|
releaseLocks();
|
|
if (err !== undefined) {
|
|
throw err;
|
|
}
|
|
return undefined;
|
|
});
|
|
}
|
|
|
|
return finalPromise.then(function () {
|
|
return settleAndRelease();
|
|
}, function (err) {
|
|
return settleAndRelease(err);
|
|
});
|
|
}
|
|
|
|
Object.defineProperty(global.ReadableStream.prototype, "pipeTo", {
|
|
value: pipeTo,
|
|
writable: true,
|
|
configurable: true
|
|
});
|
|
|
|
try {
|
|
global.__internalUserscriptsReadableStreamPipeToPolyfill = true;
|
|
} catch (e) {}
|
|
})();
|