mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-03 14:28:39 +09:00
Backport Basilisk internal userscripts
This commit is contained in:
parent
15bec18421
commit
5c168f64fc
23 changed files with 2648 additions and 0 deletions
|
|
@ -0,0 +1,93 @@
|
|||
// 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 elementFromPoint finite-args shim
|
||||
// @namespace internal-userscripts
|
||||
// @description Prevents throws when Document.elementFromPoint / elementsFromPoint receives non-finite coordinates.
|
||||
// @match *://*/*
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var global = typeof window !== "undefined" ? window : null;
|
||||
if (!global || !global.Document || !global.Document.prototype) {
|
||||
return;
|
||||
}
|
||||
|
||||
var patched = false;
|
||||
|
||||
function toFiniteNumber(value) {
|
||||
var number = Number(value);
|
||||
if (number !== number || number === Infinity || number === -Infinity) {
|
||||
return null;
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
function fallbackValue(methodName) {
|
||||
return methodName === "elementsFromPoint" ? [] : null;
|
||||
}
|
||||
|
||||
function wrapMethod(proto, methodName) {
|
||||
var nativeMethod = proto[methodName];
|
||||
if (typeof nativeMethod !== "function") {
|
||||
return;
|
||||
}
|
||||
if (nativeMethod.__internalUserscriptsFinitePointShim) {
|
||||
return;
|
||||
}
|
||||
|
||||
function wrappedMethod(x, y) {
|
||||
var finiteX = toFiniteNumber(x);
|
||||
var finiteY = toFiniteNumber(y);
|
||||
if (finiteX === null || finiteY === null) {
|
||||
return fallbackValue(methodName);
|
||||
}
|
||||
|
||||
try {
|
||||
return nativeMethod.call(this, finiteX, finiteY);
|
||||
} catch (e) {
|
||||
var message = e && e.message ? String(e.message) : "";
|
||||
if (message.indexOf("not a finite floating-point value") !== -1 ||
|
||||
message.indexOf("is not finite") !== -1) {
|
||||
return fallbackValue(methodName);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Object.defineProperty(wrappedMethod, "__internalUserscriptsFinitePointShim", {
|
||||
value: true
|
||||
});
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
Object.defineProperty(proto, methodName, {
|
||||
value: wrappedMethod,
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
} catch (e) {
|
||||
try {
|
||||
proto[methodName] = wrappedMethod;
|
||||
} catch (ignore) {}
|
||||
}
|
||||
|
||||
if (proto[methodName] === wrappedMethod) {
|
||||
patched = true;
|
||||
}
|
||||
}
|
||||
|
||||
wrapMethod(global.Document.prototype, "elementFromPoint");
|
||||
wrapMethod(global.Document.prototype, "elementsFromPoint");
|
||||
|
||||
if (patched) {
|
||||
try {
|
||||
global.__internalUserscriptsElementFromPointFinitePolyfill = true;
|
||||
} catch (e) {}
|
||||
}
|
||||
})();
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// 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 getAnimations Polyfill (minimal)
|
||||
// @namespace internal-userscripts
|
||||
// @description Adds minimal Document/Element getAnimations() methods that return an empty array when unsupported.
|
||||
// @match *://*/*
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var global = typeof window !== "undefined" ? window : null;
|
||||
if (!global) {
|
||||
return;
|
||||
}
|
||||
|
||||
var patched = false;
|
||||
|
||||
function getAnimations() {
|
||||
return [];
|
||||
}
|
||||
|
||||
function defineGetAnimations(proto) {
|
||||
if (!proto || typeof proto.getAnimations === "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.defineProperty(proto, "getAnimations", {
|
||||
value: getAnimations,
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
} catch (e) {
|
||||
proto.getAnimations = getAnimations;
|
||||
}
|
||||
patched = true;
|
||||
}
|
||||
|
||||
defineGetAnimations(global.Document && global.Document.prototype);
|
||||
defineGetAnimations(global.Element && global.Element.prototype);
|
||||
defineGetAnimations(global.CSSPseudoElement && global.CSSPseudoElement.prototype);
|
||||
|
||||
if (patched) {
|
||||
try {
|
||||
global.__internalUserscriptsGetAnimationsPolyfill = true;
|
||||
} catch (e) {}
|
||||
}
|
||||
})();
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
// 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 HTMLImageElement.decode Polyfill (best-effort)
|
||||
// @namespace internal-userscripts
|
||||
// @description Adds a Promise-based decode() that resolves on load and rejects on error.
|
||||
// @match *://*/*
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var global = typeof window !== "undefined" ? window : null;
|
||||
if (!global || !global.HTMLImageElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
var proto = global.HTMLImageElement.prototype;
|
||||
if (typeof proto.decode === "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
var PromiseCtor = global.Promise;
|
||||
if (typeof PromiseCtor !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
function rejectPromise(error) {
|
||||
return new PromiseCtor(function (resolve, reject) {
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
function hasLoadedImage(img) {
|
||||
return img.complete && (img.naturalWidth > 0 || img.naturalHeight > 0);
|
||||
}
|
||||
|
||||
function isBrokenImage(img) {
|
||||
return img.complete && img.naturalWidth === 0 && img.naturalHeight === 0;
|
||||
}
|
||||
|
||||
function decode() {
|
||||
var img = this;
|
||||
if (!(img instanceof global.HTMLImageElement)) {
|
||||
return rejectPromise(new TypeError("decode can only be used on an HTMLImageElement"));
|
||||
}
|
||||
|
||||
if (hasLoadedImage(img)) {
|
||||
return PromiseCtor.resolve();
|
||||
}
|
||||
if (isBrokenImage(img)) {
|
||||
return rejectPromise(new Error("Image decode failed"));
|
||||
}
|
||||
|
||||
var src = "";
|
||||
try {
|
||||
src = img.currentSrc || img.src || "";
|
||||
} catch (e) {}
|
||||
if (!src) {
|
||||
return rejectPromise(new Error("Image has no src"));
|
||||
}
|
||||
|
||||
return new PromiseCtor(function (resolve, reject) {
|
||||
var settled = false;
|
||||
|
||||
function cleanup() {
|
||||
if (settled) {
|
||||
return;
|
||||
}
|
||||
settled = true;
|
||||
img.removeEventListener("load", onLoad, false);
|
||||
img.removeEventListener("error", onError, false);
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
cleanup();
|
||||
resolve();
|
||||
}
|
||||
|
||||
function onError() {
|
||||
cleanup();
|
||||
reject(new Error("Image decode failed"));
|
||||
}
|
||||
|
||||
img.addEventListener("load", onLoad, false);
|
||||
img.addEventListener("error", onError, false);
|
||||
|
||||
if (hasLoadedImage(img)) {
|
||||
cleanup();
|
||||
resolve();
|
||||
} else if (isBrokenImage(img)) {
|
||||
cleanup();
|
||||
reject(new Error("Image decode failed"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
Object.defineProperty(proto, "decode", {
|
||||
value: decode,
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
} catch (e) {
|
||||
proto.decode = decode;
|
||||
}
|
||||
|
||||
try { global.__internalUserscriptsImageDecodePolyfill = true; } catch (e) {}
|
||||
})();
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
// 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 Intl.DisplayNames Polyfill (minimal)
|
||||
// @namespace internal-userscripts
|
||||
// @description Provides a minimal Intl.DisplayNames implementation for environments without native support.
|
||||
// @match *://*/*
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
if (typeof Intl !== "object" || typeof Intl.DisplayNames === "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
var STYLE_VALUES = { long: true, short: true, narrow: true };
|
||||
var TYPE_VALUES = { language: true, region: true, script: true, currency: true };
|
||||
var FALLBACK_VALUES = { code: true, none: true };
|
||||
|
||||
function canonicalizeLocales(locales) {
|
||||
if (typeof Intl.getCanonicalLocales === "function") {
|
||||
return Intl.getCanonicalLocales(locales);
|
||||
}
|
||||
if (locales == null) {
|
||||
return [];
|
||||
}
|
||||
if (Array.isArray(locales)) {
|
||||
return locales.map(function (loc) { return String(loc); });
|
||||
}
|
||||
return [String(locales)];
|
||||
}
|
||||
|
||||
function defaultLocale() {
|
||||
try {
|
||||
if (typeof Intl.DateTimeFormat === "function") {
|
||||
return Intl.DateTimeFormat().resolvedOptions().locale;
|
||||
}
|
||||
} catch (e) {}
|
||||
if (typeof navigator !== "undefined" && navigator.language) {
|
||||
return String(navigator.language);
|
||||
}
|
||||
return "en-US";
|
||||
}
|
||||
|
||||
function DisplayNames(locales, options) {
|
||||
if (!(this instanceof DisplayNames)) {
|
||||
throw new TypeError("Intl.DisplayNames must be called with new");
|
||||
}
|
||||
options = options || {};
|
||||
|
||||
var type = options.type;
|
||||
if (type == null) {
|
||||
throw new TypeError("Intl.DisplayNames requires a type option");
|
||||
}
|
||||
type = String(type);
|
||||
if (!TYPE_VALUES[type]) {
|
||||
throw new RangeError("Invalid type");
|
||||
}
|
||||
|
||||
var style = options.style == null ? "long" : String(options.style);
|
||||
if (!STYLE_VALUES[style]) {
|
||||
throw new RangeError("Invalid style");
|
||||
}
|
||||
|
||||
var fallback = options.fallback == null ? "code" : String(options.fallback);
|
||||
if (!FALLBACK_VALUES[fallback]) {
|
||||
throw new RangeError("Invalid fallback");
|
||||
}
|
||||
|
||||
var localeList = canonicalizeLocales(locales);
|
||||
this._locale = localeList.length ? localeList[0] : defaultLocale();
|
||||
this._style = style;
|
||||
this._type = type;
|
||||
this._fallback = fallback;
|
||||
}
|
||||
|
||||
DisplayNames.supportedLocalesOf = function (locales) {
|
||||
return canonicalizeLocales(locales);
|
||||
};
|
||||
|
||||
DisplayNames.prototype.resolvedOptions = function () {
|
||||
return {
|
||||
locale: this._locale,
|
||||
style: this._style,
|
||||
type: this._type,
|
||||
fallback: this._fallback,
|
||||
};
|
||||
};
|
||||
|
||||
DisplayNames.prototype.of = function (code) {
|
||||
if (this._fallback === "none") {
|
||||
return undefined;
|
||||
}
|
||||
return String(code);
|
||||
};
|
||||
|
||||
Intl.DisplayNames = DisplayNames;
|
||||
try {
|
||||
window.__internalUserscriptsIntlDisplayNamesPolyfill = true;
|
||||
} catch (e) {}
|
||||
})();
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
// 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 Intl.ListFormat Polyfill (minimal)
|
||||
// @namespace internal-userscripts
|
||||
// @description Provides a minimal Intl.ListFormat implementation for environments without native support.
|
||||
// @match *://*/*
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
if (typeof Intl !== "object" || typeof Intl.ListFormat === "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
var TYPE_VALUES = { conjunction: true, disjunction: true, unit: true };
|
||||
var STYLE_VALUES = { long: true, short: true, narrow: true };
|
||||
var LOCALE_MATCHER_VALUES = { lookup: true, "best fit": true };
|
||||
var PATTERNS = {
|
||||
conjunction: {
|
||||
long: { pair: " and ", middle: ", ", end: ", and " },
|
||||
short: { pair: " & ", middle: ", ", end: ", & " },
|
||||
narrow: { pair: ", ", middle: ", ", end: ", " },
|
||||
},
|
||||
disjunction: {
|
||||
long: { pair: " or ", middle: ", ", end: ", or " },
|
||||
short: { pair: " or ", middle: ", ", end: ", or " },
|
||||
narrow: { pair: " or ", middle: ", ", end: ", or " },
|
||||
},
|
||||
unit: {
|
||||
long: { pair: ", ", middle: ", ", end: ", " },
|
||||
short: { pair: ", ", middle: ", ", end: ", " },
|
||||
narrow: { pair: " ", middle: " ", end: " " },
|
||||
},
|
||||
};
|
||||
|
||||
function canonicalizeLocales(locales) {
|
||||
if (typeof Intl.getCanonicalLocales === "function") {
|
||||
return Intl.getCanonicalLocales(locales);
|
||||
}
|
||||
if (locales == null) {
|
||||
return [];
|
||||
}
|
||||
if (Array.isArray(locales)) {
|
||||
return locales.map(function (locale) {
|
||||
return String(locale);
|
||||
});
|
||||
}
|
||||
return [String(locales)];
|
||||
}
|
||||
|
||||
function defaultLocale() {
|
||||
try {
|
||||
if (typeof Intl.DateTimeFormat === "function") {
|
||||
return Intl.DateTimeFormat().resolvedOptions().locale;
|
||||
}
|
||||
} catch (e) {}
|
||||
if (typeof navigator !== "undefined" && navigator.language) {
|
||||
return String(navigator.language);
|
||||
}
|
||||
return "en-US";
|
||||
}
|
||||
|
||||
function toStringValue(value) {
|
||||
if (typeof value === "symbol") {
|
||||
throw new TypeError("Cannot convert a Symbol value to a string");
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function toStringList(list) {
|
||||
if (list == null) {
|
||||
throw new TypeError("List argument is required");
|
||||
}
|
||||
|
||||
var result = [];
|
||||
if (typeof Symbol !== "undefined" && list[Symbol.iterator] != null) {
|
||||
var iterator = list[Symbol.iterator]();
|
||||
if (!iterator || typeof iterator.next !== "function") {
|
||||
throw new TypeError("List argument must be iterable");
|
||||
}
|
||||
while (true) {
|
||||
var step = iterator.next();
|
||||
if (step.done) {
|
||||
break;
|
||||
}
|
||||
result.push(toStringValue(step.value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Fallback for very old environments without Symbol.iterator.
|
||||
if (Array.isArray(list)) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
result.push(toStringValue(list[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
throw new TypeError("List argument must be iterable");
|
||||
}
|
||||
|
||||
function requireListFormatInstance(value) {
|
||||
if (
|
||||
value == null ||
|
||||
(typeof value !== "object" && typeof value !== "function") ||
|
||||
!TYPE_VALUES[value._type] ||
|
||||
!STYLE_VALUES[value._style]
|
||||
) {
|
||||
throw new TypeError(
|
||||
"Method Intl.ListFormat.prototype called on incompatible receiver",
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function buildParts(items, pattern) {
|
||||
var parts = [];
|
||||
var i = 0;
|
||||
var lastIndex = items.length - 1;
|
||||
|
||||
if (items.length === 0) {
|
||||
return parts;
|
||||
}
|
||||
if (items.length === 1) {
|
||||
parts.push({ type: "element", value: items[0] });
|
||||
return parts;
|
||||
}
|
||||
if (items.length === 2) {
|
||||
parts.push({ type: "element", value: items[0] });
|
||||
parts.push({ type: "literal", value: pattern.pair });
|
||||
parts.push({ type: "element", value: items[1] });
|
||||
return parts;
|
||||
}
|
||||
|
||||
parts.push({ type: "element", value: items[0] });
|
||||
for (i = 1; i < lastIndex; i++) {
|
||||
parts.push({ type: "literal", value: pattern.middle });
|
||||
parts.push({ type: "element", value: items[i] });
|
||||
}
|
||||
parts.push({ type: "literal", value: pattern.end });
|
||||
parts.push({ type: "element", value: items[lastIndex] });
|
||||
return parts;
|
||||
}
|
||||
|
||||
function ListFormat(locales, options) {
|
||||
if (!(this instanceof ListFormat)) {
|
||||
throw new TypeError("Intl.ListFormat must be called with new");
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
var type = options.type == null ? "conjunction" : String(options.type);
|
||||
var style = options.style == null ? "long" : String(options.style);
|
||||
var localeMatcher =
|
||||
options.localeMatcher == null
|
||||
? "best fit"
|
||||
: String(options.localeMatcher);
|
||||
|
||||
if (!TYPE_VALUES[type]) {
|
||||
throw new RangeError("Invalid type");
|
||||
}
|
||||
if (!STYLE_VALUES[style]) {
|
||||
throw new RangeError("Invalid style");
|
||||
}
|
||||
if (!LOCALE_MATCHER_VALUES[localeMatcher]) {
|
||||
throw new RangeError("Invalid localeMatcher");
|
||||
}
|
||||
|
||||
var localeList = canonicalizeLocales(locales);
|
||||
this._locale = localeList.length ? localeList[0] : defaultLocale();
|
||||
this._type = type;
|
||||
this._style = style;
|
||||
}
|
||||
|
||||
ListFormat.supportedLocalesOf = function (locales, options) {
|
||||
if (options != null && typeof options === "object") {
|
||||
var localeMatcher = options.localeMatcher;
|
||||
if (
|
||||
localeMatcher != null &&
|
||||
!LOCALE_MATCHER_VALUES[String(localeMatcher)]
|
||||
) {
|
||||
throw new RangeError("Invalid localeMatcher");
|
||||
}
|
||||
}
|
||||
return canonicalizeLocales(locales);
|
||||
};
|
||||
|
||||
ListFormat.prototype.resolvedOptions = function () {
|
||||
var self = requireListFormatInstance(this);
|
||||
return {
|
||||
locale: self._locale,
|
||||
type: self._type,
|
||||
style: self._style,
|
||||
};
|
||||
};
|
||||
|
||||
ListFormat.prototype.formatToParts = function (list) {
|
||||
var self = requireListFormatInstance(this);
|
||||
var items = toStringList(list);
|
||||
var pattern = PATTERNS[self._type][self._style];
|
||||
return buildParts(items, pattern);
|
||||
};
|
||||
|
||||
ListFormat.prototype._format = function (list) {
|
||||
var parts = this.formatToParts(list);
|
||||
var result = "";
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
result += parts[i].value;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
Object.defineProperty(ListFormat.prototype, "format", {
|
||||
configurable: true,
|
||||
get: function () {
|
||||
var self = requireListFormatInstance(this);
|
||||
if (!self._boundFormat) {
|
||||
self._boundFormat = function (list) {
|
||||
return self._format(list);
|
||||
};
|
||||
}
|
||||
return self._boundFormat;
|
||||
},
|
||||
});
|
||||
|
||||
Intl.ListFormat = ListFormat;
|
||||
try {
|
||||
window.__internalUserscriptsIntlListFormatPolyfill = true;
|
||||
} catch (e) {}
|
||||
})();
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// 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 Intl.RelativeTimeFormat formatToParts Polyfill
|
||||
// @namespace internal-userscripts
|
||||
// @description Adds formatToParts() to native Intl.RelativeTimeFormat implementations that lack it.
|
||||
// @match *://*/*
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
if (
|
||||
typeof Intl !== "object" ||
|
||||
typeof Intl.RelativeTimeFormat !== "function" ||
|
||||
typeof Intl.RelativeTimeFormat.prototype.formatToParts === "function"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
function formatToParts(value, unit) {
|
||||
var number = Number(value);
|
||||
var formatted = this.format(number, unit);
|
||||
var canonicalUnit = String(unit).replace(/s$/, "");
|
||||
var resolved = this.resolvedOptions();
|
||||
var numberOptions = {
|
||||
maximumFractionDigits: 3,
|
||||
};
|
||||
|
||||
if (resolved.numberingSystem) {
|
||||
numberOptions.numberingSystem = resolved.numberingSystem;
|
||||
}
|
||||
|
||||
var numberFormat = new Intl.NumberFormat(resolved.locale, numberOptions);
|
||||
var numberParts;
|
||||
if (typeof numberFormat.formatToParts === "function") {
|
||||
numberParts = numberFormat.formatToParts(Math.abs(number));
|
||||
} else {
|
||||
numberParts = [
|
||||
{ type: "integer", value: numberFormat.format(Math.abs(number)) },
|
||||
];
|
||||
}
|
||||
|
||||
var numberText = numberParts
|
||||
.map(function (part) {
|
||||
return part.value;
|
||||
})
|
||||
.join("");
|
||||
var numberIndex = formatted.indexOf(numberText);
|
||||
|
||||
// numeric: "auto" may produce a word such as "yesterday" with no number.
|
||||
if (numberIndex === -1) {
|
||||
return [{ type: "literal", value: formatted }];
|
||||
}
|
||||
|
||||
var parts = [];
|
||||
if (numberIndex > 0) {
|
||||
parts.push({ type: "literal", value: formatted.slice(0, numberIndex) });
|
||||
}
|
||||
numberParts.forEach(function (part) {
|
||||
parts.push({ type: part.type, value: part.value, unit: canonicalUnit });
|
||||
});
|
||||
if (numberIndex + numberText.length < formatted.length) {
|
||||
parts.push({
|
||||
type: "literal",
|
||||
value: formatted.slice(numberIndex + numberText.length),
|
||||
});
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
Object.defineProperty(
|
||||
Intl.RelativeTimeFormat.prototype,
|
||||
"formatToParts",
|
||||
{
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: formatToParts,
|
||||
},
|
||||
);
|
||||
|
||||
window.__internalUserscriptsIntlRelativeTimeFormatFormatToPartsPolyfill = true;
|
||||
})();
|
||||
|
|
@ -0,0 +1,365 @@
|
|||
// 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 Intl.Segmenter Polyfill (minimal)
|
||||
// @namespace internal-userscripts
|
||||
// @description Provides a minimal Intl.Segmenter implementation for environments without native support.
|
||||
// @match *://*/*
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var global =
|
||||
(typeof globalThis !== "undefined" && globalThis) ||
|
||||
(typeof window !== "undefined" && window) ||
|
||||
(typeof self !== "undefined" && self) ||
|
||||
this;
|
||||
if (typeof global.Intl !== "object" || global.Intl == null) {
|
||||
global.Intl = {};
|
||||
}
|
||||
if (typeof global.Intl.Segmenter === "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
var GRANULARITY_VALUES = { grapheme: true, word: true, sentence: true };
|
||||
var LOCALE_MATCHER_VALUES = { lookup: true, "best fit": true };
|
||||
var FALLBACK_WORD_LIKE = /^[A-Za-z0-9_]+$/;
|
||||
var FALLBACK_WORD_SPLIT = /[A-Za-z0-9_]+|[\s]+|[^\s]/g;
|
||||
var COMBINING_MARK_RANGES = [
|
||||
[0x0300, 0x036f],
|
||||
[0x1ab0, 0x1aff],
|
||||
[0x1dc0, 0x1dff],
|
||||
[0x20d0, 0x20ff],
|
||||
[0xfe20, 0xfe2f],
|
||||
];
|
||||
|
||||
var unicodeGraphemeRegex = null;
|
||||
var unicodeWordSplit = null;
|
||||
var unicodeWordLike = null;
|
||||
try {
|
||||
unicodeGraphemeRegex = new RegExp(
|
||||
"(?:\\P{Mark}\\p{Mark}*|\\p{Mark}+)",
|
||||
"gu",
|
||||
);
|
||||
unicodeWordSplit = new RegExp("[\\p{L}\\p{N}\\p{M}_]+|[\\s]+|[^\\s]", "gu");
|
||||
unicodeWordLike = new RegExp("^[\\p{L}\\p{N}\\p{M}_]+$", "u");
|
||||
} catch (e) {}
|
||||
|
||||
function canonicalizeLocales(locales) {
|
||||
if (typeof global.Intl.getCanonicalLocales === "function") {
|
||||
return global.Intl.getCanonicalLocales(locales);
|
||||
}
|
||||
if (locales == null) {
|
||||
return [];
|
||||
}
|
||||
if (Array.isArray(locales)) {
|
||||
return locales.map(function (locale) {
|
||||
return String(locale);
|
||||
});
|
||||
}
|
||||
return [String(locales)];
|
||||
}
|
||||
|
||||
function defaultLocale() {
|
||||
try {
|
||||
if (typeof global.Intl.DateTimeFormat === "function") {
|
||||
return global.Intl.DateTimeFormat().resolvedOptions().locale;
|
||||
}
|
||||
} catch (e) {}
|
||||
if (typeof navigator !== "undefined" && navigator.language) {
|
||||
return String(navigator.language);
|
||||
}
|
||||
return "en-US";
|
||||
}
|
||||
|
||||
function toStringValue(value) {
|
||||
if (typeof value === "symbol") {
|
||||
throw new TypeError("Cannot convert a Symbol value to a string");
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function readCodePoint(str, index) {
|
||||
var first = str.charCodeAt(index);
|
||||
if (first >= 0xd800 && first <= 0xdbff && index + 1 < str.length) {
|
||||
var second = str.charCodeAt(index + 1);
|
||||
if (second >= 0xdc00 && second <= 0xdfff) {
|
||||
return {
|
||||
codePoint: ((first - 0xd800) << 10) + (second - 0xdc00) + 0x10000,
|
||||
length: 2,
|
||||
};
|
||||
}
|
||||
}
|
||||
return { codePoint: first, length: 1 };
|
||||
}
|
||||
|
||||
function isCombiningMark(codePoint) {
|
||||
for (var i = 0; i < COMBINING_MARK_RANGES.length; i++) {
|
||||
var range = COMBINING_MARK_RANGES[i];
|
||||
if (codePoint >= range[0] && codePoint <= range[1]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function segmentGrapheme(string) {
|
||||
var segments = [];
|
||||
var i = 0;
|
||||
|
||||
if (unicodeGraphemeRegex) {
|
||||
var match;
|
||||
unicodeGraphemeRegex.lastIndex = 0;
|
||||
while ((match = unicodeGraphemeRegex.exec(string))) {
|
||||
segments.push({
|
||||
segment: match[0],
|
||||
index: match.index,
|
||||
});
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
while (i < string.length) {
|
||||
var start = i;
|
||||
var current = readCodePoint(string, i);
|
||||
i += current.length;
|
||||
while (i < string.length) {
|
||||
var next = readCodePoint(string, i);
|
||||
if (!isCombiningMark(next.codePoint)) {
|
||||
break;
|
||||
}
|
||||
i += next.length;
|
||||
}
|
||||
segments.push({
|
||||
segment: string.slice(start, i),
|
||||
index: start,
|
||||
});
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
function segmentWord(string) {
|
||||
var segments = [];
|
||||
var regex = unicodeWordSplit || FALLBACK_WORD_SPLIT;
|
||||
var match;
|
||||
|
||||
regex.lastIndex = 0;
|
||||
while ((match = regex.exec(string))) {
|
||||
segments.push({
|
||||
segment: match[0],
|
||||
index: match.index,
|
||||
});
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
|
||||
function isSentenceTerminal(ch) {
|
||||
return ch === "." || ch === "!" || ch === "?";
|
||||
}
|
||||
|
||||
function isWhitespaceChar(ch) {
|
||||
return (
|
||||
ch === " " || ch === "\t" || ch === "\r" || ch === "\n" || ch === "\f"
|
||||
);
|
||||
}
|
||||
|
||||
function segmentSentence(string) {
|
||||
var segments = [];
|
||||
var start = 0;
|
||||
var i = 0;
|
||||
|
||||
if (!string) {
|
||||
return segments;
|
||||
}
|
||||
|
||||
while (i < string.length) {
|
||||
if (isSentenceTerminal(string.charAt(i))) {
|
||||
i++;
|
||||
while (i < string.length && isSentenceTerminal(string.charAt(i))) {
|
||||
i++;
|
||||
}
|
||||
while (i < string.length && isWhitespaceChar(string.charAt(i))) {
|
||||
i++;
|
||||
}
|
||||
segments.push({
|
||||
segment: string.slice(start, i),
|
||||
index: start,
|
||||
});
|
||||
start = i;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (start < string.length) {
|
||||
segments.push({
|
||||
segment: string.slice(start),
|
||||
index: start,
|
||||
});
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
function segmentString(string, granularity) {
|
||||
if (granularity === "word") {
|
||||
return segmentWord(string);
|
||||
}
|
||||
if (granularity === "sentence") {
|
||||
return segmentSentence(string);
|
||||
}
|
||||
return segmentGrapheme(string);
|
||||
}
|
||||
|
||||
function isWordLikeSegment(segment) {
|
||||
if (unicodeWordLike) {
|
||||
return unicodeWordLike.test(segment);
|
||||
}
|
||||
return FALLBACK_WORD_LIKE.test(segment);
|
||||
}
|
||||
|
||||
function requireSegmenter(value) {
|
||||
if (
|
||||
value == null ||
|
||||
(typeof value !== "object" && typeof value !== "function") ||
|
||||
!GRANULARITY_VALUES[value._granularity]
|
||||
) {
|
||||
throw new TypeError(
|
||||
"Method Intl.Segmenter.prototype called on incompatible receiver",
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function SegmentIterator(segments) {
|
||||
this._segments = segments;
|
||||
this._index = 0;
|
||||
}
|
||||
|
||||
SegmentIterator.prototype.next = function () {
|
||||
if (this._index >= this._segments.length) {
|
||||
return { value: undefined, done: true };
|
||||
}
|
||||
var value = this._segments[this._index];
|
||||
this._index += 1;
|
||||
return { value: value, done: false };
|
||||
};
|
||||
|
||||
if (typeof Symbol !== "undefined" && Symbol.iterator) {
|
||||
SegmentIterator.prototype[Symbol.iterator] = function () {
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
function Segments(input, segments) {
|
||||
this._input = input;
|
||||
this._segments = segments;
|
||||
}
|
||||
|
||||
Segments.prototype.containing = function (index) {
|
||||
if (index == null) {
|
||||
return undefined;
|
||||
}
|
||||
var position = Number(index);
|
||||
if (!isFinite(position)) {
|
||||
return undefined;
|
||||
}
|
||||
position = Math.floor(position);
|
||||
for (var i = 0; i < this._segments.length; i++) {
|
||||
var segment = this._segments[i];
|
||||
if (
|
||||
position >= segment.index &&
|
||||
position < segment.index + segment.segment.length
|
||||
) {
|
||||
return segment;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
if (typeof Symbol !== "undefined" && Symbol.iterator) {
|
||||
Segments.prototype[Symbol.iterator] = function () {
|
||||
return new SegmentIterator(this._segments);
|
||||
};
|
||||
}
|
||||
|
||||
function Segmenter(locales, options) {
|
||||
if (!(this instanceof Segmenter)) {
|
||||
throw new TypeError("Intl.Segmenter must be called with new");
|
||||
}
|
||||
|
||||
if (options == null) {
|
||||
options = {};
|
||||
} else if (typeof options !== "object") {
|
||||
options = Object(options);
|
||||
}
|
||||
|
||||
var granularity =
|
||||
options.granularity == null ? "grapheme" : String(options.granularity);
|
||||
if (!GRANULARITY_VALUES[granularity]) {
|
||||
throw new RangeError("Invalid granularity");
|
||||
}
|
||||
if (
|
||||
options.localeMatcher != null &&
|
||||
!LOCALE_MATCHER_VALUES[String(options.localeMatcher)]
|
||||
) {
|
||||
throw new RangeError("Invalid localeMatcher");
|
||||
}
|
||||
|
||||
var localeList = canonicalizeLocales(locales);
|
||||
this._locale = localeList.length ? localeList[0] : defaultLocale();
|
||||
this._granularity = granularity;
|
||||
}
|
||||
|
||||
Segmenter.supportedLocalesOf = function (locales, options) {
|
||||
if (options != null && typeof options === "object") {
|
||||
var localeMatcher = options.localeMatcher;
|
||||
if (
|
||||
localeMatcher != null &&
|
||||
!LOCALE_MATCHER_VALUES[String(localeMatcher)]
|
||||
) {
|
||||
throw new RangeError("Invalid localeMatcher");
|
||||
}
|
||||
}
|
||||
return canonicalizeLocales(locales);
|
||||
};
|
||||
|
||||
Segmenter.prototype.resolvedOptions = function () {
|
||||
var self = requireSegmenter(this);
|
||||
return {
|
||||
locale: self._locale,
|
||||
granularity: self._granularity,
|
||||
};
|
||||
};
|
||||
|
||||
Segmenter.prototype.segment = function (input) {
|
||||
var self = requireSegmenter(this);
|
||||
var string = toStringValue(input);
|
||||
var entries = segmentString(string, self._granularity);
|
||||
var segments = [];
|
||||
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
var part = entries[i];
|
||||
segments.push({
|
||||
segment: part.segment,
|
||||
index: part.index,
|
||||
input: string,
|
||||
isWordLike:
|
||||
self._granularity === "word"
|
||||
? isWordLikeSegment(part.segment)
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
return new Segments(string, segments);
|
||||
};
|
||||
|
||||
global.Intl.Segmenter = Segmenter;
|
||||
try {
|
||||
global.__internalUserscriptsIntlSegmenterPolyfill = true;
|
||||
} catch (e) {}
|
||||
})();
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
// 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) {}
|
||||
})();
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
// 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) {}
|
||||
})();
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
// 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 TextDecoderStream Polyfill (best-effort)
|
||||
// @namespace internal-userscripts
|
||||
// @description Minimal TextDecoderStream implementation backed by TransformStream.
|
||||
// @match *://*/*
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var global = typeof window !== "undefined" ? window : null;
|
||||
if (!global) {
|
||||
return;
|
||||
}
|
||||
|
||||
var existingCtor = global.TextDecoderStream;
|
||||
if (typeof existingCtor === "function") {
|
||||
try {
|
||||
var probe = new existingCtor();
|
||||
if (probe && probe.readable && probe.writable) {
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
if (String(e).indexOf("not yet implemented") === -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof global.TransformStream !== "function") {
|
||||
return;
|
||||
}
|
||||
if (typeof global.TextDecoder !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
function isObject(value) {
|
||||
return value !== null && (typeof value === "object" || typeof value === "function");
|
||||
}
|
||||
|
||||
function streamBrandCheckException(name) {
|
||||
return new TypeError("TextDecoderStream.prototype." + name +
|
||||
" can only be used on a TextDecoderStream");
|
||||
}
|
||||
|
||||
function isTextDecoderStream(value) {
|
||||
return isObject(value) && value._isTextDecoderStream === true;
|
||||
}
|
||||
|
||||
function normalizeOptions(options) {
|
||||
var normalized = {
|
||||
fatal: false,
|
||||
ignoreBOM: false
|
||||
};
|
||||
|
||||
if (!isObject(options)) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
normalized.fatal = !!options.fatal;
|
||||
normalized.ignoreBOM = !!options.ignoreBOM;
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function PolyfillTextDecoderStream(label, options) {
|
||||
if (!(this instanceof PolyfillTextDecoderStream)) {
|
||||
throw new TypeError("TextDecoderStream must be constructed with 'new'");
|
||||
}
|
||||
|
||||
var normalizedOptions = normalizeOptions(options);
|
||||
var effectiveLabel = label === undefined ? "utf-8" : label;
|
||||
|
||||
var decoder;
|
||||
try {
|
||||
decoder = new global.TextDecoder(effectiveLabel, {
|
||||
fatal: normalizedOptions.fatal,
|
||||
ignoreBOM: normalizedOptions.ignoreBOM
|
||||
});
|
||||
} catch (e) {
|
||||
decoder = new global.TextDecoder(effectiveLabel, {
|
||||
fatal: normalizedOptions.fatal
|
||||
});
|
||||
}
|
||||
|
||||
this._decoder = decoder;
|
||||
this._encoding = typeof decoder.encoding === "string"
|
||||
? decoder.encoding
|
||||
: String(effectiveLabel).toLowerCase();
|
||||
this._fatal = typeof decoder.fatal === "boolean"
|
||||
? decoder.fatal
|
||||
: normalizedOptions.fatal;
|
||||
this._ignoreBOM = typeof decoder.ignoreBOM === "boolean"
|
||||
? decoder.ignoreBOM
|
||||
: normalizedOptions.ignoreBOM;
|
||||
|
||||
this._transform = new global.TransformStream({
|
||||
transform: function (chunk, controller) {
|
||||
var output = decoder.decode(chunk, { stream: true });
|
||||
if (output.length > 0) {
|
||||
controller.enqueue(output);
|
||||
}
|
||||
},
|
||||
flush: function (controller) {
|
||||
var output = decoder.decode();
|
||||
if (output.length > 0) {
|
||||
controller.enqueue(output);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._readable = this._transform.readable;
|
||||
this._writable = this._transform.writable;
|
||||
this._isTextDecoderStream = true;
|
||||
}
|
||||
|
||||
Object.defineProperty(PolyfillTextDecoderStream.prototype, "encoding", {
|
||||
get: function () {
|
||||
if (!isTextDecoderStream(this)) {
|
||||
throw streamBrandCheckException("encoding");
|
||||
}
|
||||
return this._encoding;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(PolyfillTextDecoderStream.prototype, "fatal", {
|
||||
get: function () {
|
||||
if (!isTextDecoderStream(this)) {
|
||||
throw streamBrandCheckException("fatal");
|
||||
}
|
||||
return this._fatal;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(PolyfillTextDecoderStream.prototype, "ignoreBOM", {
|
||||
get: function () {
|
||||
if (!isTextDecoderStream(this)) {
|
||||
throw streamBrandCheckException("ignoreBOM");
|
||||
}
|
||||
return this._ignoreBOM;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(PolyfillTextDecoderStream.prototype, "readable", {
|
||||
get: function () {
|
||||
if (!isTextDecoderStream(this)) {
|
||||
throw streamBrandCheckException("readable");
|
||||
}
|
||||
return this._readable;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(PolyfillTextDecoderStream.prototype, "writable", {
|
||||
get: function () {
|
||||
if (!isTextDecoderStream(this)) {
|
||||
throw streamBrandCheckException("writable");
|
||||
}
|
||||
return this._writable;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
global.TextDecoderStream = PolyfillTextDecoderStream;
|
||||
try {
|
||||
global.__internalUserscriptsTextDecoderStreamPolyfill = true;
|
||||
} catch (e) {}
|
||||
})();
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
// 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 TextEncoderStream Polyfill (best-effort)
|
||||
// @namespace internal-userscripts
|
||||
// @description Minimal TextEncoderStream implementation backed by TransformStream.
|
||||
// @match *://*/*
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var global = typeof window !== "undefined" ? window : null;
|
||||
if (!global) {
|
||||
return;
|
||||
}
|
||||
|
||||
var existingCtor = global.TextEncoderStream;
|
||||
if (typeof existingCtor === "function") {
|
||||
try {
|
||||
var probe = new existingCtor();
|
||||
if (probe && probe.readable && probe.writable) {
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
if (String(e).indexOf("not yet implemented") === -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof global.TransformStream !== "function") {
|
||||
return;
|
||||
}
|
||||
if (typeof global.TextEncoder !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
function isObject(value) {
|
||||
return value !== null && (typeof value === "object" || typeof value === "function");
|
||||
}
|
||||
|
||||
function streamBrandCheckException(name) {
|
||||
return new TypeError("TextEncoderStream.prototype." + name +
|
||||
" can only be used on a TextEncoderStream");
|
||||
}
|
||||
|
||||
function isTextEncoderStream(value) {
|
||||
return isObject(value) && value._isTextEncoderStream === true;
|
||||
}
|
||||
|
||||
function PolyfillTextEncoderStream() {
|
||||
if (!(this instanceof PolyfillTextEncoderStream)) {
|
||||
throw new TypeError("TextEncoderStream must be constructed with 'new'");
|
||||
}
|
||||
|
||||
var encoder = new global.TextEncoder();
|
||||
this._transform = new global.TransformStream({
|
||||
transform: function (chunk, controller) {
|
||||
controller.enqueue(encoder.encode(String(chunk)));
|
||||
}
|
||||
});
|
||||
this._readable = this._transform.readable;
|
||||
this._writable = this._transform.writable;
|
||||
this._isTextEncoderStream = true;
|
||||
}
|
||||
|
||||
Object.defineProperty(PolyfillTextEncoderStream.prototype, "readable", {
|
||||
get: function () {
|
||||
if (!isTextEncoderStream(this)) {
|
||||
throw streamBrandCheckException("readable");
|
||||
}
|
||||
return this._readable;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
Object.defineProperty(PolyfillTextEncoderStream.prototype, "writable", {
|
||||
get: function () {
|
||||
if (!isTextEncoderStream(this)) {
|
||||
throw streamBrandCheckException("writable");
|
||||
}
|
||||
return this._writable;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
global.TextEncoderStream = PolyfillTextEncoderStream;
|
||||
try {
|
||||
global.__internalUserscriptsTextEncoderStreamPolyfill = true;
|
||||
} catch (e) {}
|
||||
})();
|
||||
|
|
@ -0,0 +1,430 @@
|
|||
// 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) {}
|
||||
})();
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// 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 WebAuthn Shim for Microsoft
|
||||
// @namespace internal-userscripts
|
||||
// @description Forces WebAuthn capability checks to report unsupported on Microsoft auth domains.
|
||||
// @match https://*.microsoft.com/*
|
||||
// @match https://*.live.com/*
|
||||
// @match https://*.msauth.net/*
|
||||
// @match https://*.visualstudio.com/*
|
||||
// @grant none
|
||||
// @run-at document-start
|
||||
// ==/UserScript==
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var global = typeof window !== "undefined" ? window : null;
|
||||
if (!global || !global.location) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (global.location.protocol !== "https:") {
|
||||
return;
|
||||
}
|
||||
|
||||
var host = String(global.location.hostname || "").toLowerCase();
|
||||
function matchesDomain(domain) {
|
||||
return host === domain || host.slice(-(domain.length + 1)) === "." + domain;
|
||||
}
|
||||
|
||||
if (
|
||||
!matchesDomain("microsoft.com") &&
|
||||
!matchesDomain("live.com") &&
|
||||
!matchesDomain("msauth.net") &&
|
||||
!matchesDomain("visualstudio.com")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
function installShim() {
|
||||
if (typeof navigator.credentials === "undefined") {
|
||||
var fake = {
|
||||
create: function () {
|
||||
return Promise.reject(
|
||||
new DOMException("WebAuthn not supported", "NotSupportedError"),
|
||||
);
|
||||
},
|
||||
get: function () {
|
||||
return Promise.reject(
|
||||
new DOMException("WebAuthn not supported", "NotSupportedError"),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
navigator.credentials = fake;
|
||||
} catch (e) {
|
||||
try {
|
||||
Object.defineProperty(navigator, "credentials", {
|
||||
value: fake,
|
||||
});
|
||||
} catch (e2) {}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof global.PublicKeyCredential === "undefined") {
|
||||
global.PublicKeyCredential = function () {};
|
||||
global.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable =
|
||||
function () {
|
||||
return Promise.resolve(false);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
installShim();
|
||||
try {
|
||||
global.__internalUserscriptsWebAuthnMicrosoftShim = true;
|
||||
} catch (e) {}
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue