mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-04 14:58:37 +09:00
Backport Basilisk internal userscripts
This commit is contained in:
parent
15bec18421
commit
5c168f64fc
23 changed files with 2648 additions and 0 deletions
28
browser/internaluserscripts/README.md
Normal file
28
browser/internaluserscripts/README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Internal Userscripts (Polyfills Only)
|
||||
|
||||
This directory contains a minimal, built-in userscript loader used to ship polyfill user scripts with Dactyloidae (forked from Basilisk's implementation of such). This originally started as a fork of GreaseMonkey, however at this point very little of GreaseMonkey remains.
|
||||
|
||||
## How it works
|
||||
- The XPCOM component `@internaluserscripts.mozdev.org/service;1` observes document creation and, when `browser.internal-userscripts.enabled` is true, injects bundled polyfill scripts into each page.
|
||||
- Bundled scripts live in `basilisk/internaluserscripts/bundled-scripts/` and are packaged into the app. They are loaded in the page principal and can override missing APIs.
|
||||
- The only default pref is `browser.internal-userscripts.enabled` (default: true). Toggle to disable all injection.
|
||||
|
||||
## Bundled polyfills
|
||||
- `elementfrompoint-finite-polyfill.user.js`: wraps `Document.elementFromPoint()` and `Document.elementsFromPoint()` to return `null`/`[]` when coordinates are not finite, avoiding page-breaking `TypeError`s from non-finite inputs. It exposes `window.__internalUserscriptsElementFromPointFinitePolyfill = true` for verification.
|
||||
- `getanimations-polyfill.user.js`: minimal `getAnimations()` shim for `Document`, `Element`, and `CSSPseudoElement` that returns an empty array when native support is unavailable. It exposes `window.__internalUserscriptsGetAnimationsPolyfill = true` for verification.
|
||||
- `imagedecode-polyfill.user.js`: best-effort `HTMLImageElement.decode()` shim that resolves on load and rejects on error. It exposes `window.__internalUserscriptsImageDecodePolyfill = true` for verification.
|
||||
- `intl-displaynames-polyfill.user.js`: minimal Intl.DisplayNames shim that validates options and returns the input code when display data is unavailable. It exposes `window.__internalUserscriptsIntlDisplayNamesPolyfill = true` for verification.
|
||||
- `intl-listformat-polyfill.user.js`: minimal Intl.ListFormat shim supporting `format()` and `formatToParts()` with simple fallback separators. It exposes `window.__internalUserscriptsIntlListFormatPolyfill = true` for verification.
|
||||
- `intl-relativetimeformat-formattoparts-polyfill.user.js`: adds `formatToParts()` to native Intl.RelativeTimeFormat implementations that lack it. It exposes `window.__internalUserscriptsIntlRelativeTimeFormatFormatToPartsPolyfill = true` for verification.
|
||||
- `intl-segmenter-polyfill.user.js`: minimal Intl.Segmenter shim supporting `segment()`, `resolvedOptions()`, and `supportedLocalesOf()` with best-effort grapheme/word/sentence segmentation. It exposes `window.__internalUserscriptsIntlSegmenterPolyfill = true` for verification.
|
||||
- `readablestream-pipethrough-polyfill.user.js`: best-effort ReadableStream `pipeThrough` implementation backed by `pipeTo` or reader/writer pumping. It exposes `window.__internalUserscriptsReadableStreamPipeThroughPolyfill = true` for verification.
|
||||
- `readablestream-pipeto-polyfill.user.js`: best-effort ReadableStream `pipeTo` implementation using reader/writer pumping. It exposes `window.__internalUserscriptsReadableStreamPipeToPolyfill = true` for verification.
|
||||
- `textencoderstream-polyfill.user.js`: best-effort TextEncoderStream implementation backed by TransformStream. It exposes `window.__internalUserscriptsTextEncoderStreamPolyfill = true` for verification.
|
||||
- `textdecoderstream-polyfill.user.js`: best-effort TextDecoderStream implementation backed by TransformStream. It exposes `window.__internalUserscriptsTextDecoderStreamPolyfill = true` for verification.
|
||||
- `transformstream-polyfill.user.js`: minimal TransformStream polyfill backed by ReadableStream with a lightweight WritableStream shim. It exposes `window.__internalUserscriptsTransformStreamPolyfill = true` for verification.
|
||||
- `webauthn-microsoft-shim.user.js`: Microsoft-domain WebAuthn capability shim that reports WebAuthn as unsupported by providing rejecting `navigator.credentials.create/get` stubs (when `navigator.credentials` is missing) and a `PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()` fallback that resolves `false`. It exposes `window.__internalUserscriptsWebAuthnMicrosoftShim = true` for verification.
|
||||
|
||||
## Adding new polyfills
|
||||
1. Drop a `*.user.js` file into `bundled-scripts/` with the appropriate header.
|
||||
2. List it in `moz.build` under `FINAL_TARGET_FILES['internal-userscripts']`.
|
||||
3. The loader will inject it automatically when enabled.
|
||||
|
|
@ -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) {}
|
||||
})();
|
||||
278
browser/internaluserscripts/components/internaluserscripts.js
Normal file
278
browser/internaluserscripts/components/internaluserscripts.js
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/* This source file is licensed under the MIT License.
|
||||
* A copy of the MIT License should have been distributed with this
|
||||
* file. If not, see https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
if (typeof Cc === "undefined") {
|
||||
var Cc = Components.classes;
|
||||
}
|
||||
if (typeof Ci === "undefined") {
|
||||
var Ci = Components.interfaces;
|
||||
}
|
||||
if (typeof Cu === "undefined") {
|
||||
var Cu = Components.utils;
|
||||
}
|
||||
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
|
||||
|
||||
const PREF_ENABLED = "browser.internal-userscripts.enabled";
|
||||
|
||||
function InternalUserscriptsService() {
|
||||
this.wrappedJSObject = this;
|
||||
}
|
||||
|
||||
InternalUserscriptsService.prototype = {
|
||||
classDescription: "Internal Userscripts Loader",
|
||||
classID: Components.ID("{d8b4bd27-b458-4417-8dfe-3b80bb6375bc}"),
|
||||
contractID: "@internaluserscripts.mozdev.org/service;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
if (topic === "profile-after-change") {
|
||||
this._startup();
|
||||
} else if (topic === "quit-application") {
|
||||
this._shutdown();
|
||||
} else if (topic === "nsPref:changed" && data === PREF_ENABLED) {
|
||||
this._updateObserverState();
|
||||
} else if (topic === "document-element-inserted") {
|
||||
let win = subject && subject.defaultView;
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
let self = this;
|
||||
Services.tm.mainThread.dispatch(function () {
|
||||
self._inject(win);
|
||||
}, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
},
|
||||
|
||||
_startup: function () {
|
||||
Services.obs.addObserver(this, "quit-application", false);
|
||||
Services.prefs.addObserver(PREF_ENABLED, this, false);
|
||||
this._updateObserverState();
|
||||
},
|
||||
|
||||
_shutdown: function () {
|
||||
try {
|
||||
Services.obs.removeObserver(this, "document-element-inserted");
|
||||
} catch (e) {}
|
||||
try {
|
||||
Services.obs.removeObserver(this, "quit-application");
|
||||
} catch (e) {}
|
||||
try {
|
||||
Services.prefs.removeObserver(PREF_ENABLED, this);
|
||||
} catch (e) {}
|
||||
this._observingDocuments = false;
|
||||
},
|
||||
|
||||
_updateObserverState: function () {
|
||||
let enabled = true;
|
||||
try {
|
||||
enabled = Services.prefs.getBoolPref(PREF_ENABLED, true);
|
||||
} catch (e) {}
|
||||
|
||||
if (enabled && !this._observingDocuments) {
|
||||
Services.obs.addObserver(this, "document-element-inserted", false);
|
||||
this._observingDocuments = true;
|
||||
} else if (!enabled && this._observingDocuments) {
|
||||
try {
|
||||
Services.obs.removeObserver(this, "document-element-inserted");
|
||||
} catch (e) {}
|
||||
this._observingDocuments = false;
|
||||
}
|
||||
},
|
||||
|
||||
_inject: function (win) {
|
||||
if (!Services.prefs.getBoolPref(PREF_ENABLED, true)) {
|
||||
return;
|
||||
}
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let doc = win.document;
|
||||
let uri = doc && doc.documentURIObject;
|
||||
if (uri && (uri.schemeIs("chrome") || uri.schemeIs("resource"))) {
|
||||
return;
|
||||
}
|
||||
} catch (e) {}
|
||||
let contentWin = win.wrappedJSObject || win;
|
||||
let logPolyfill = function (name, source) {
|
||||
try {
|
||||
if (
|
||||
contentWin.console &&
|
||||
typeof contentWin.console.info === "function"
|
||||
) {
|
||||
let suffix = source ? " (" + source + ")" : "";
|
||||
contentWin.console.info(
|
||||
"[internal userscripts] Loaded polyfill script: " + name + suffix,
|
||||
);
|
||||
}
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/intl-displaynames-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsIntlDisplayNamesPolyfill) {
|
||||
logPolyfill("Intl.DisplayNames", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/intl-listformat-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsIntlListFormatPolyfill) {
|
||||
logPolyfill("Intl.ListFormat", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/intl-relativetimeformat-formattoparts-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (
|
||||
contentWin.__internalUserscriptsIntlRelativeTimeFormatFormatToPartsPolyfill
|
||||
) {
|
||||
logPolyfill("Intl.RelativeTimeFormat.formatToParts", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/intl-segmenter-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsIntlSegmenterPolyfill) {
|
||||
logPolyfill("Intl.Segmenter", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/webauthn-microsoft-shim.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsWebAuthnMicrosoftShim) {
|
||||
logPolyfill("WebAuthn Microsoft unsupported shim", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/elementfrompoint-finite-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsElementFromPointFinitePolyfill) {
|
||||
logPolyfill("Document.elementFromPoint finite-args shim", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/getanimations-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsGetAnimationsPolyfill) {
|
||||
logPolyfill("getAnimations", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/imagedecode-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsImageDecodePolyfill) {
|
||||
logPolyfill("HTMLImageElement.decode", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/transformstream-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsTransformStreamPolyfill) {
|
||||
logPolyfill("TransformStream", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/textencoderstream-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsTextEncoderStreamPolyfill) {
|
||||
logPolyfill("TextEncoderStream", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/textdecoderstream-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsTextDecoderStreamPolyfill) {
|
||||
logPolyfill("TextDecoderStream", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/readablestream-pipeto-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsReadableStreamPipeToPolyfill) {
|
||||
logPolyfill("ReadableStream.pipeTo", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://internaluserscripts/content/bundled-scripts/readablestream-pipethrough-polyfill.user.js",
|
||||
contentWin,
|
||||
);
|
||||
if (contentWin.__internalUserscriptsReadableStreamPipeThroughPolyfill) {
|
||||
logPolyfill("ReadableStream.pipeThrough", "bundled");
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var NSGetFactory = XPCOMUtils.generateNSGetFactory([
|
||||
InternalUserscriptsService,
|
||||
]);
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# 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/.
|
||||
|
||||
component {d8b4bd27-b458-4417-8dfe-3b80bb6375bc} internaluserscripts.js
|
||||
contract @internaluserscripts.mozdev.org/service;1 {d8b4bd27-b458-4417-8dfe-3b80bb6375bc}
|
||||
category profile-after-change internaluserscripts @internaluserscripts.mozdev.org/service;1
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
// 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/.
|
||||
|
||||
pref("browser.internal-userscripts.enabled", true);
|
||||
6
browser/internaluserscripts/internaluserscripts.manifest
Normal file
6
browser/internaluserscripts/internaluserscripts.manifest
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# 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/.
|
||||
|
||||
content internaluserscripts internaluserscripts/content/
|
||||
resource internaluserscripts internaluserscripts/content/
|
||||
9
browser/internaluserscripts/jar.mn
Normal file
9
browser/internaluserscripts/jar.mn
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# 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/.
|
||||
|
||||
internaluserscripts.jar:
|
||||
% content internaluserscripts %content/
|
||||
% resource internaluserscripts %content/
|
||||
|
||||
content/bundled-scripts/ (bundled-scripts/*)
|
||||
33
browser/internaluserscripts/moz.build
Normal file
33
browser/internaluserscripts/moz.build
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# 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/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
FINAL_TARGET = 'dist/bin/browser'
|
||||
FINAL_TARGET_FILES.components += [
|
||||
'components/internaluserscripts.js',
|
||||
'components/internaluserscripts.manifest',
|
||||
]
|
||||
|
||||
FINAL_TARGET_FILES['defaults/preferences'] += [
|
||||
'defaults/preferences/internaluserscripts.js',
|
||||
]
|
||||
|
||||
# Copy any bundled internal user scripts into the app dir for automatic import.
|
||||
FINAL_TARGET_FILES['internal-userscripts'] += [
|
||||
'bundled-scripts/elementfrompoint-finite-polyfill.user.js',
|
||||
'bundled-scripts/getanimations-polyfill.user.js',
|
||||
'bundled-scripts/imagedecode-polyfill.user.js',
|
||||
'bundled-scripts/intl-displaynames-polyfill.user.js',
|
||||
'bundled-scripts/intl-listformat-polyfill.user.js',
|
||||
'bundled-scripts/intl-relativetimeformat-formattoparts-polyfill.user.js',
|
||||
'bundled-scripts/intl-segmenter-polyfill.user.js',
|
||||
'bundled-scripts/readablestream-pipethrough-polyfill.user.js',
|
||||
'bundled-scripts/readablestream-pipeto-polyfill.user.js',
|
||||
'bundled-scripts/textdecoderstream-polyfill.user.js',
|
||||
'bundled-scripts/textencoderstream-polyfill.user.js',
|
||||
'bundled-scripts/transformstream-polyfill.user.js',
|
||||
'bundled-scripts/webauthn-microsoft-shim.user.js',
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue