Initial commit
This commit is contained in:
commit
afed023152
8152 files changed
+2086461
No files matched your search
+42
@@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
|
||||
const conversions = require("webidl-conversions");
|
||||
const utils = require("./utils.js");
|
||||
|
||||
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
|
||||
if (typeof value !== "function") {
|
||||
throw new globalObject.TypeError(context + " is not a function");
|
||||
}
|
||||
|
||||
function invokeTheCallbackFunction(...args) {
|
||||
const thisArg = utils.tryWrapperForImpl(this);
|
||||
let callResult;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
args[i] = utils.tryWrapperForImpl(args[i]);
|
||||
}
|
||||
|
||||
callResult = Reflect.apply(value, thisArg, args);
|
||||
|
||||
callResult = conversions["any"](callResult, { context: context, globals: globalObject });
|
||||
|
||||
return callResult;
|
||||
}
|
||||
|
||||
invokeTheCallbackFunction.construct = (...args) => {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
args[i] = utils.tryWrapperForImpl(args[i]);
|
||||
}
|
||||
|
||||
let callResult = Reflect.construct(value, args);
|
||||
|
||||
callResult = conversions["any"](callResult, { context: context, globals: globalObject });
|
||||
|
||||
return callResult;
|
||||
};
|
||||
|
||||
invokeTheCallbackFunction[utils.wrapperSymbol] = value;
|
||||
invokeTheCallbackFunction.objectReference = value;
|
||||
|
||||
return invokeTheCallbackFunction;
|
||||
};
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
"use strict";
|
||||
const usm = require("./url-state-machine");
|
||||
const urlencoded = require("./urlencoded");
|
||||
const URLSearchParams = require("./URLSearchParams");
|
||||
const URL = require("./URL");
|
||||
|
||||
exports.implementation = class URLImpl {
|
||||
// Unlike the spec, we duplicate some code between the constructor and canParse, because we want to give useful error
|
||||
// messages in the constructor that distinguish between the different causes of failure.
|
||||
constructor(globalObject, [url, base]) {
|
||||
let parsedBase = null;
|
||||
if (base !== undefined) {
|
||||
parsedBase = usm.basicURLParse(base);
|
||||
if (parsedBase === null) {
|
||||
throw new TypeError(`Invalid base URL: ${base}`);
|
||||
}
|
||||
}
|
||||
|
||||
const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase });
|
||||
if (parsedURL === null) {
|
||||
throw new TypeError(`Invalid URL: ${url}`);
|
||||
}
|
||||
|
||||
const query = parsedURL.query !== null ? parsedURL.query : "";
|
||||
|
||||
this._url = parsedURL;
|
||||
|
||||
// We cannot invoke the "new URLSearchParams object" algorithm without going through the constructor, which strips
|
||||
// question mark by default. Therefore the doNotStripQMark hack is used.
|
||||
this._query = URLSearchParams.createImpl(globalObject, [query], { doNotStripQMark: true });
|
||||
this._query._url = this;
|
||||
}
|
||||
|
||||
static parse(globalObject, input, base) {
|
||||
try {
|
||||
return URL.createImpl(globalObject, [input, base]);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static canParse(url, base) {
|
||||
let parsedBase = null;
|
||||
if (base !== undefined) {
|
||||
parsedBase = usm.basicURLParse(base);
|
||||
if (parsedBase === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase });
|
||||
if (parsedURL === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
get href() {
|
||||
return usm.serializeURL(this._url);
|
||||
}
|
||||
|
||||
set href(v) {
|
||||
const parsedURL = usm.basicURLParse(v);
|
||||
if (parsedURL === null) {
|
||||
throw new TypeError(`Invalid URL: ${v}`);
|
||||
}
|
||||
|
||||
this._url = parsedURL;
|
||||
|
||||
this._query._list.splice(0);
|
||||
const { query } = parsedURL;
|
||||
if (query !== null) {
|
||||
this._query._list = urlencoded.parseUrlencodedString(query);
|
||||
}
|
||||
}
|
||||
|
||||
get origin() {
|
||||
return usm.serializeURLOrigin(this._url);
|
||||
}
|
||||
|
||||
get protocol() {
|
||||
return `${this._url.scheme}:`;
|
||||
}
|
||||
|
||||
set protocol(v) {
|
||||
usm.basicURLParse(`${v}:`, { url: this._url, stateOverride: "scheme start" });
|
||||
}
|
||||
|
||||
get username() {
|
||||
return this._url.username;
|
||||
}
|
||||
|
||||
set username(v) {
|
||||
if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
usm.setTheUsername(this._url, v);
|
||||
}
|
||||
|
||||
get password() {
|
||||
return this._url.password;
|
||||
}
|
||||
|
||||
set password(v) {
|
||||
if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
usm.setThePassword(this._url, v);
|
||||
}
|
||||
|
||||
get host() {
|
||||
const url = this._url;
|
||||
|
||||
if (url.host === null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (url.port === null) {
|
||||
return usm.serializeHost(url.host);
|
||||
}
|
||||
|
||||
return `${usm.serializeHost(url.host)}:${usm.serializeInteger(url.port)}`;
|
||||
}
|
||||
|
||||
set host(v) {
|
||||
if (usm.hasAnOpaquePath(this._url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
usm.basicURLParse(v, { url: this._url, stateOverride: "host" });
|
||||
}
|
||||
|
||||
get hostname() {
|
||||
if (this._url.host === null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return usm.serializeHost(this._url.host);
|
||||
}
|
||||
|
||||
set hostname(v) {
|
||||
if (usm.hasAnOpaquePath(this._url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
usm.basicURLParse(v, { url: this._url, stateOverride: "hostname" });
|
||||
}
|
||||
|
||||
get port() {
|
||||
if (this._url.port === null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return usm.serializeInteger(this._url.port);
|
||||
}
|
||||
|
||||
set port(v) {
|
||||
if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (v === "") {
|
||||
this._url.port = null;
|
||||
} else {
|
||||
usm.basicURLParse(v, { url: this._url, stateOverride: "port" });
|
||||
}
|
||||
}
|
||||
|
||||
get pathname() {
|
||||
return usm.serializePath(this._url);
|
||||
}
|
||||
|
||||
set pathname(v) {
|
||||
if (usm.hasAnOpaquePath(this._url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._url.path = [];
|
||||
usm.basicURLParse(v, { url: this._url, stateOverride: "path start" });
|
||||
}
|
||||
|
||||
get search() {
|
||||
if (this._url.query === null || this._url.query === "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return `?${this._url.query}`;
|
||||
}
|
||||
|
||||
set search(v) {
|
||||
const url = this._url;
|
||||
|
||||
if (v === "") {
|
||||
url.query = null;
|
||||
this._query._list = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const input = v[0] === "?" ? v.substring(1) : v;
|
||||
url.query = "";
|
||||
usm.basicURLParse(input, { url, stateOverride: "query" });
|
||||
this._query._list = urlencoded.parseUrlencodedString(input);
|
||||
}
|
||||
|
||||
get searchParams() {
|
||||
return this._query;
|
||||
}
|
||||
|
||||
get hash() {
|
||||
if (this._url.fragment === null || this._url.fragment === "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return `#${this._url.fragment}`;
|
||||
}
|
||||
|
||||
set hash(v) {
|
||||
if (v === "") {
|
||||
this._url.fragment = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const input = v[0] === "#" ? v.substring(1) : v;
|
||||
this._url.fragment = "";
|
||||
usm.basicURLParse(input, { url: this._url, stateOverride: "fragment" });
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return this.href;
|
||||
}
|
||||
};
|
||||
+476
@@ -0,0 +1,476 @@
|
||||
"use strict";
|
||||
|
||||
const conversions = require("webidl-conversions");
|
||||
const utils = require("./utils.js");
|
||||
|
||||
const ctorRegistrySymbol = utils.ctorRegistrySymbol;
|
||||
|
||||
const interfaceName = "URL";
|
||||
|
||||
const $interfaceDescriptor = utils.createInterfaceDescriptor();
|
||||
exports.interfaceDescriptor = $interfaceDescriptor;
|
||||
|
||||
exports.is = value => {
|
||||
return utils.implForWrapperWithInterface(value, $interfaceDescriptor) !== null;
|
||||
};
|
||||
exports.isImpl = value => {
|
||||
return utils.isObject(value) && value instanceof Impl.implementation;
|
||||
};
|
||||
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
|
||||
const impl = utils.implForWrapperWithInterface(value, $interfaceDescriptor);
|
||||
if (impl !== null) {
|
||||
return impl;
|
||||
}
|
||||
throw new globalObject.TypeError(`${context} is not of type 'URL'.`);
|
||||
};
|
||||
|
||||
function makeWrapper(globalObject, newTarget) {
|
||||
let proto;
|
||||
if (newTarget !== undefined) {
|
||||
proto = newTarget.prototype;
|
||||
}
|
||||
|
||||
if (!utils.isObject(proto)) {
|
||||
proto = globalObject[ctorRegistrySymbol]["URL"].prototype;
|
||||
}
|
||||
|
||||
return Object.create(proto);
|
||||
}
|
||||
|
||||
exports.create = (globalObject, constructorArgs, privateData) => {
|
||||
const wrapper = makeWrapper(globalObject);
|
||||
return exports.setup(wrapper, globalObject, constructorArgs, privateData);
|
||||
};
|
||||
|
||||
exports.createImpl = (globalObject, constructorArgs, privateData) => {
|
||||
const wrapper = exports.create(globalObject, constructorArgs, privateData);
|
||||
return utils.implForWrapper(wrapper);
|
||||
};
|
||||
|
||||
exports._internalSetup = (wrapper, globalObject) => {};
|
||||
|
||||
exports.setup = (wrapper, globalObject, constructorArgs = [], privateData = {}) => {
|
||||
privateData.wrapper = wrapper;
|
||||
|
||||
exports._internalSetup(wrapper, globalObject);
|
||||
const impl = new Impl.implementation(globalObject, constructorArgs, privateData);
|
||||
|
||||
utils.registerWrapper(wrapper, impl, $interfaceDescriptor);
|
||||
impl[utils.wrapperSymbol] = wrapper;
|
||||
if (Impl.init) {
|
||||
Impl.init(impl);
|
||||
}
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
exports.new = (globalObject, newTarget) => {
|
||||
const wrapper = makeWrapper(globalObject, newTarget);
|
||||
|
||||
exports._internalSetup(wrapper, globalObject);
|
||||
const impl = Object.create(Impl.implementation.prototype);
|
||||
|
||||
utils.registerWrapper(wrapper, impl, $interfaceDescriptor);
|
||||
impl[utils.wrapperSymbol] = wrapper;
|
||||
if (Impl.init) {
|
||||
Impl.init(impl);
|
||||
}
|
||||
return impl;
|
||||
};
|
||||
|
||||
const exposed = new Set(["Window", "Worker"]);
|
||||
|
||||
exports.install = (globalObject, globalNames) => {
|
||||
if (!globalNames.some(globalName => exposed.has(globalName))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ctorRegistry = utils.initCtorRegistry(globalObject);
|
||||
class URL {
|
||||
constructor(url) {
|
||||
if (arguments.length < 1) {
|
||||
throw new globalObject.TypeError(
|
||||
`Failed to construct 'URL': 1 argument required, but only ${arguments.length} present.`
|
||||
);
|
||||
}
|
||||
const args = [];
|
||||
{
|
||||
let curArg = arguments[0];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to construct 'URL': parameter 1",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
{
|
||||
let curArg = arguments[1];
|
||||
if (curArg !== undefined) {
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to construct 'URL': parameter 2",
|
||||
globals: globalObject
|
||||
});
|
||||
}
|
||||
args.push(curArg);
|
||||
}
|
||||
return exports.setup(Object.create(new.target.prototype), globalObject, args);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'toJSON' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl.toJSON();
|
||||
}
|
||||
|
||||
get href() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get href' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["href"];
|
||||
}
|
||||
|
||||
set href(V) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set href' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
V = conversions["USVString"](V, {
|
||||
context: "Failed to set the 'href' property on 'URL': The provided value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
$impl["href"] = V;
|
||||
}
|
||||
|
||||
toString() {
|
||||
const $impl = utils.implForWrapperWithInterface(this, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'toString' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["href"];
|
||||
}
|
||||
|
||||
get origin() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get origin' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["origin"];
|
||||
}
|
||||
|
||||
get protocol() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get protocol' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["protocol"];
|
||||
}
|
||||
|
||||
set protocol(V) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set protocol' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
V = conversions["USVString"](V, {
|
||||
context: "Failed to set the 'protocol' property on 'URL': The provided value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
$impl["protocol"] = V;
|
||||
}
|
||||
|
||||
get username() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get username' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["username"];
|
||||
}
|
||||
|
||||
set username(V) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set username' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
V = conversions["USVString"](V, {
|
||||
context: "Failed to set the 'username' property on 'URL': The provided value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
$impl["username"] = V;
|
||||
}
|
||||
|
||||
get password() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get password' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["password"];
|
||||
}
|
||||
|
||||
set password(V) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set password' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
V = conversions["USVString"](V, {
|
||||
context: "Failed to set the 'password' property on 'URL': The provided value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
$impl["password"] = V;
|
||||
}
|
||||
|
||||
get host() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get host' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["host"];
|
||||
}
|
||||
|
||||
set host(V) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set host' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
V = conversions["USVString"](V, {
|
||||
context: "Failed to set the 'host' property on 'URL': The provided value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
$impl["host"] = V;
|
||||
}
|
||||
|
||||
get hostname() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get hostname' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["hostname"];
|
||||
}
|
||||
|
||||
set hostname(V) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set hostname' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
V = conversions["USVString"](V, {
|
||||
context: "Failed to set the 'hostname' property on 'URL': The provided value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
$impl["hostname"] = V;
|
||||
}
|
||||
|
||||
get port() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get port' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["port"];
|
||||
}
|
||||
|
||||
set port(V) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set port' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
V = conversions["USVString"](V, {
|
||||
context: "Failed to set the 'port' property on 'URL': The provided value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
$impl["port"] = V;
|
||||
}
|
||||
|
||||
get pathname() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get pathname' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["pathname"];
|
||||
}
|
||||
|
||||
set pathname(V) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set pathname' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
V = conversions["USVString"](V, {
|
||||
context: "Failed to set the 'pathname' property on 'URL': The provided value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
$impl["pathname"] = V;
|
||||
}
|
||||
|
||||
get search() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get search' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["search"];
|
||||
}
|
||||
|
||||
set search(V) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set search' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
V = conversions["USVString"](V, {
|
||||
context: "Failed to set the 'search' property on 'URL': The provided value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
$impl["search"] = V;
|
||||
}
|
||||
|
||||
get searchParams() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get searchParams' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return utils.getSameObject(this, "searchParams", () => {
|
||||
return utils.tryWrapperForImpl($impl["searchParams"]);
|
||||
});
|
||||
}
|
||||
|
||||
get hash() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get hash' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
return $impl["hash"];
|
||||
}
|
||||
|
||||
set hash(V) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set hash' called on an object that is not a valid instance of URL.");
|
||||
}
|
||||
|
||||
V = conversions["USVString"](V, {
|
||||
context: "Failed to set the 'hash' property on 'URL': The provided value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
$impl["hash"] = V;
|
||||
}
|
||||
|
||||
static parse(url) {
|
||||
if (arguments.length < 1) {
|
||||
throw new globalObject.TypeError(
|
||||
`Failed to execute 'parse' on 'URL': 1 argument required, but only ${arguments.length} present.`
|
||||
);
|
||||
}
|
||||
const args = [];
|
||||
{
|
||||
let curArg = arguments[0];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'parse' on 'URL': parameter 1",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
{
|
||||
let curArg = arguments[1];
|
||||
if (curArg !== undefined) {
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'parse' on 'URL': parameter 2",
|
||||
globals: globalObject
|
||||
});
|
||||
}
|
||||
args.push(curArg);
|
||||
}
|
||||
return utils.tryWrapperForImpl(Impl.implementation.parse(globalObject, ...args));
|
||||
}
|
||||
|
||||
static canParse(url) {
|
||||
if (arguments.length < 1) {
|
||||
throw new globalObject.TypeError(
|
||||
`Failed to execute 'canParse' on 'URL': 1 argument required, but only ${arguments.length} present.`
|
||||
);
|
||||
}
|
||||
const args = [];
|
||||
{
|
||||
let curArg = arguments[0];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'canParse' on 'URL': parameter 1",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
{
|
||||
let curArg = arguments[1];
|
||||
if (curArg !== undefined) {
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'canParse' on 'URL': parameter 2",
|
||||
globals: globalObject
|
||||
});
|
||||
}
|
||||
args.push(curArg);
|
||||
}
|
||||
return Impl.implementation.canParse(...args);
|
||||
}
|
||||
}
|
||||
Object.defineProperties(URL.prototype, {
|
||||
toJSON: { enumerable: true },
|
||||
href: { enumerable: true },
|
||||
toString: { enumerable: true },
|
||||
origin: { enumerable: true },
|
||||
protocol: { enumerable: true },
|
||||
username: { enumerable: true },
|
||||
password: { enumerable: true },
|
||||
host: { enumerable: true },
|
||||
hostname: { enumerable: true },
|
||||
port: { enumerable: true },
|
||||
pathname: { enumerable: true },
|
||||
search: { enumerable: true },
|
||||
searchParams: { enumerable: true },
|
||||
hash: { enumerable: true },
|
||||
[Symbol.toStringTag]: { value: "URL", configurable: true }
|
||||
});
|
||||
Object.defineProperties(URL, { parse: { enumerable: true }, canParse: { enumerable: true } });
|
||||
ctorRegistry[interfaceName] = URL;
|
||||
|
||||
Object.defineProperty(globalObject, interfaceName, {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: URL
|
||||
});
|
||||
|
||||
if (globalNames.includes("Window")) {
|
||||
Object.defineProperty(globalObject, "webkitURL", {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: URL
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const Impl = require("./URL-impl.js");
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
"use strict";
|
||||
const urlencoded = require("./urlencoded");
|
||||
|
||||
exports.implementation = class URLSearchParamsImpl {
|
||||
constructor(globalObject, constructorArgs, { doNotStripQMark = false }) {
|
||||
let init = constructorArgs[0];
|
||||
this._list = [];
|
||||
this._url = null;
|
||||
|
||||
if (!doNotStripQMark && typeof init === "string" && init[0] === "?") {
|
||||
init = init.slice(1);
|
||||
}
|
||||
|
||||
if (Array.isArray(init)) {
|
||||
for (const pair of init) {
|
||||
if (pair.length !== 2) {
|
||||
throw new TypeError("Failed to construct 'URLSearchParams': parameter 1 sequence's element does not " +
|
||||
"contain exactly two elements.");
|
||||
}
|
||||
this._list.push([pair[0], pair[1]]);
|
||||
}
|
||||
} else if (typeof init === "object" && Object.getPrototypeOf(init) === null) {
|
||||
for (const name of Object.keys(init)) {
|
||||
const value = init[name];
|
||||
this._list.push([name, value]);
|
||||
}
|
||||
} else {
|
||||
this._list = urlencoded.parseUrlencodedString(init);
|
||||
}
|
||||
}
|
||||
|
||||
_updateSteps() {
|
||||
if (this._url !== null) {
|
||||
let serializedQuery = urlencoded.serializeUrlencoded(this._list);
|
||||
if (serializedQuery === "") {
|
||||
serializedQuery = null;
|
||||
}
|
||||
|
||||
this._url._url.query = serializedQuery;
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this._list.length;
|
||||
}
|
||||
|
||||
append(name, value) {
|
||||
this._list.push([name, value]);
|
||||
this._updateSteps();
|
||||
}
|
||||
|
||||
delete(name, value) {
|
||||
let i = 0;
|
||||
while (i < this._list.length) {
|
||||
if (this._list[i][0] === name && (value === undefined || this._list[i][1] === value)) {
|
||||
this._list.splice(i, 1);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
this._updateSteps();
|
||||
}
|
||||
|
||||
get(name) {
|
||||
for (const tuple of this._list) {
|
||||
if (tuple[0] === name) {
|
||||
return tuple[1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getAll(name) {
|
||||
const output = [];
|
||||
for (const tuple of this._list) {
|
||||
if (tuple[0] === name) {
|
||||
output.push(tuple[1]);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
has(name, value) {
|
||||
for (const tuple of this._list) {
|
||||
if (tuple[0] === name && (value === undefined || tuple[1] === value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
set(name, value) {
|
||||
let found = false;
|
||||
let i = 0;
|
||||
while (i < this._list.length) {
|
||||
if (this._list[i][0] === name) {
|
||||
if (found) {
|
||||
this._list.splice(i, 1);
|
||||
} else {
|
||||
found = true;
|
||||
this._list[i][1] = value;
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
this._list.push([name, value]);
|
||||
}
|
||||
this._updateSteps();
|
||||
}
|
||||
|
||||
sort() {
|
||||
this._list.sort((a, b) => {
|
||||
if (a[0] < b[0]) {
|
||||
return -1;
|
||||
}
|
||||
if (a[0] > b[0]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
this._updateSteps();
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this._list[Symbol.iterator]();
|
||||
}
|
||||
|
||||
toString() {
|
||||
return urlencoded.serializeUrlencoded(this._list);
|
||||
}
|
||||
};
|
||||
+514
@@ -0,0 +1,514 @@
|
||||
"use strict";
|
||||
|
||||
const conversions = require("webidl-conversions");
|
||||
const utils = require("./utils.js");
|
||||
|
||||
const Function = require("./Function.js");
|
||||
const newObjectInRealm = utils.newObjectInRealm;
|
||||
const ctorRegistrySymbol = utils.ctorRegistrySymbol;
|
||||
|
||||
const interfaceName = "URLSearchParams";
|
||||
|
||||
const $interfaceDescriptor = utils.createInterfaceDescriptor();
|
||||
exports.interfaceDescriptor = $interfaceDescriptor;
|
||||
|
||||
exports.is = value => {
|
||||
return utils.implForWrapperWithInterface(value, $interfaceDescriptor) !== null;
|
||||
};
|
||||
exports.isImpl = value => {
|
||||
return utils.isObject(value) && value instanceof Impl.implementation;
|
||||
};
|
||||
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
|
||||
const impl = utils.implForWrapperWithInterface(value, $interfaceDescriptor);
|
||||
if (impl !== null) {
|
||||
return impl;
|
||||
}
|
||||
throw new globalObject.TypeError(`${context} is not of type 'URLSearchParams'.`);
|
||||
};
|
||||
|
||||
exports.createDefaultIterator = (globalObject, target, kind) => {
|
||||
const ctorRegistry = globalObject[ctorRegistrySymbol];
|
||||
const iteratorPrototype = ctorRegistry["URLSearchParams Iterator"];
|
||||
const iterator = Object.create(iteratorPrototype);
|
||||
Object.defineProperty(iterator, utils.iterInternalSymbol, {
|
||||
value: { target, kind, index: 0 },
|
||||
configurable: true
|
||||
});
|
||||
return iterator;
|
||||
};
|
||||
|
||||
function makeWrapper(globalObject, newTarget) {
|
||||
let proto;
|
||||
if (newTarget !== undefined) {
|
||||
proto = newTarget.prototype;
|
||||
}
|
||||
|
||||
if (!utils.isObject(proto)) {
|
||||
proto = globalObject[ctorRegistrySymbol]["URLSearchParams"].prototype;
|
||||
}
|
||||
|
||||
return Object.create(proto);
|
||||
}
|
||||
|
||||
exports.create = (globalObject, constructorArgs, privateData) => {
|
||||
const wrapper = makeWrapper(globalObject);
|
||||
return exports.setup(wrapper, globalObject, constructorArgs, privateData);
|
||||
};
|
||||
|
||||
exports.createImpl = (globalObject, constructorArgs, privateData) => {
|
||||
const wrapper = exports.create(globalObject, constructorArgs, privateData);
|
||||
return utils.implForWrapper(wrapper);
|
||||
};
|
||||
|
||||
exports._internalSetup = (wrapper, globalObject) => {};
|
||||
|
||||
exports.setup = (wrapper, globalObject, constructorArgs = [], privateData = {}) => {
|
||||
privateData.wrapper = wrapper;
|
||||
|
||||
exports._internalSetup(wrapper, globalObject);
|
||||
const impl = new Impl.implementation(globalObject, constructorArgs, privateData);
|
||||
|
||||
utils.registerWrapper(wrapper, impl, $interfaceDescriptor);
|
||||
impl[utils.wrapperSymbol] = wrapper;
|
||||
if (Impl.init) {
|
||||
Impl.init(impl);
|
||||
}
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
exports.new = (globalObject, newTarget) => {
|
||||
const wrapper = makeWrapper(globalObject, newTarget);
|
||||
|
||||
exports._internalSetup(wrapper, globalObject);
|
||||
const impl = Object.create(Impl.implementation.prototype);
|
||||
|
||||
utils.registerWrapper(wrapper, impl, $interfaceDescriptor);
|
||||
impl[utils.wrapperSymbol] = wrapper;
|
||||
if (Impl.init) {
|
||||
Impl.init(impl);
|
||||
}
|
||||
return impl;
|
||||
};
|
||||
|
||||
const exposed = new Set(["Window", "Worker"]);
|
||||
|
||||
exports.install = (globalObject, globalNames) => {
|
||||
if (!globalNames.some(globalName => exposed.has(globalName))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ctorRegistry = utils.initCtorRegistry(globalObject);
|
||||
class URLSearchParams {
|
||||
constructor() {
|
||||
const args = [];
|
||||
{
|
||||
let curArg = arguments[0];
|
||||
if (curArg !== undefined) {
|
||||
if (utils.isObject(curArg)) {
|
||||
if (
|
||||
utils.getMethod(curArg, Symbol.iterator, "Failed to construct 'URLSearchParams': parameter 1") !==
|
||||
undefined
|
||||
) {
|
||||
if (!utils.isObject(curArg)) {
|
||||
throw new globalObject.TypeError(
|
||||
"Failed to construct 'URLSearchParams': parameter 1" + " sequence" + " is not an iterable object."
|
||||
);
|
||||
} else {
|
||||
const V = [];
|
||||
const tmp = curArg;
|
||||
for (let nextItem of tmp) {
|
||||
if (!utils.isObject(nextItem)) {
|
||||
throw new globalObject.TypeError(
|
||||
"Failed to construct 'URLSearchParams': parameter 1" +
|
||||
" sequence" +
|
||||
"'s element" +
|
||||
" is not an iterable object."
|
||||
);
|
||||
} else {
|
||||
const V = [];
|
||||
const tmp = nextItem;
|
||||
for (let nextItem of tmp) {
|
||||
nextItem = conversions["USVString"](nextItem, {
|
||||
context:
|
||||
"Failed to construct 'URLSearchParams': parameter 1" +
|
||||
" sequence" +
|
||||
"'s element" +
|
||||
"'s element",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
V.push(nextItem);
|
||||
}
|
||||
nextItem = V;
|
||||
}
|
||||
|
||||
V.push(nextItem);
|
||||
}
|
||||
curArg = V;
|
||||
}
|
||||
} else {
|
||||
if (!utils.isObject(curArg)) {
|
||||
throw new globalObject.TypeError(
|
||||
"Failed to construct 'URLSearchParams': parameter 1" + " record" + " is not an object."
|
||||
);
|
||||
} else {
|
||||
const result = Object.create(null);
|
||||
for (const key of Reflect.ownKeys(curArg)) {
|
||||
const desc = Object.getOwnPropertyDescriptor(curArg, key);
|
||||
if (desc && desc.enumerable) {
|
||||
let typedKey = key;
|
||||
|
||||
typedKey = conversions["USVString"](typedKey, {
|
||||
context: "Failed to construct 'URLSearchParams': parameter 1" + " record" + "'s key",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
let typedValue = curArg[key];
|
||||
|
||||
typedValue = conversions["USVString"](typedValue, {
|
||||
context: "Failed to construct 'URLSearchParams': parameter 1" + " record" + "'s value",
|
||||
globals: globalObject
|
||||
});
|
||||
|
||||
result[typedKey] = typedValue;
|
||||
}
|
||||
}
|
||||
curArg = result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to construct 'URLSearchParams': parameter 1",
|
||||
globals: globalObject
|
||||
});
|
||||
}
|
||||
} else {
|
||||
curArg = "";
|
||||
}
|
||||
args.push(curArg);
|
||||
}
|
||||
return exports.setup(Object.create(new.target.prototype), globalObject, args);
|
||||
}
|
||||
|
||||
append(name, value) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError(
|
||||
"'append' called on an object that is not a valid instance of URLSearchParams."
|
||||
);
|
||||
}
|
||||
|
||||
if (arguments.length < 2) {
|
||||
throw new globalObject.TypeError(
|
||||
`Failed to execute 'append' on 'URLSearchParams': 2 arguments required, but only ${arguments.length} present.`
|
||||
);
|
||||
}
|
||||
const args = [];
|
||||
{
|
||||
let curArg = arguments[0];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'append' on 'URLSearchParams': parameter 1",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
{
|
||||
let curArg = arguments[1];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'append' on 'URLSearchParams': parameter 2",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
return utils.tryWrapperForImpl($impl.append(...args));
|
||||
}
|
||||
|
||||
delete(name) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError(
|
||||
"'delete' called on an object that is not a valid instance of URLSearchParams."
|
||||
);
|
||||
}
|
||||
|
||||
if (arguments.length < 1) {
|
||||
throw new globalObject.TypeError(
|
||||
`Failed to execute 'delete' on 'URLSearchParams': 1 argument required, but only ${arguments.length} present.`
|
||||
);
|
||||
}
|
||||
const args = [];
|
||||
{
|
||||
let curArg = arguments[0];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'delete' on 'URLSearchParams': parameter 1",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
{
|
||||
let curArg = arguments[1];
|
||||
if (curArg !== undefined) {
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'delete' on 'URLSearchParams': parameter 2",
|
||||
globals: globalObject
|
||||
});
|
||||
}
|
||||
args.push(curArg);
|
||||
}
|
||||
return utils.tryWrapperForImpl($impl.delete(...args));
|
||||
}
|
||||
|
||||
get(name) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'get' called on an object that is not a valid instance of URLSearchParams.");
|
||||
}
|
||||
|
||||
if (arguments.length < 1) {
|
||||
throw new globalObject.TypeError(
|
||||
`Failed to execute 'get' on 'URLSearchParams': 1 argument required, but only ${arguments.length} present.`
|
||||
);
|
||||
}
|
||||
const args = [];
|
||||
{
|
||||
let curArg = arguments[0];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'get' on 'URLSearchParams': parameter 1",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
return $impl.get(...args);
|
||||
}
|
||||
|
||||
getAll(name) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError(
|
||||
"'getAll' called on an object that is not a valid instance of URLSearchParams."
|
||||
);
|
||||
}
|
||||
|
||||
if (arguments.length < 1) {
|
||||
throw new globalObject.TypeError(
|
||||
`Failed to execute 'getAll' on 'URLSearchParams': 1 argument required, but only ${arguments.length} present.`
|
||||
);
|
||||
}
|
||||
const args = [];
|
||||
{
|
||||
let curArg = arguments[0];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'getAll' on 'URLSearchParams': parameter 1",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
return utils.tryWrapperForImpl($impl.getAll(...args));
|
||||
}
|
||||
|
||||
has(name) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'has' called on an object that is not a valid instance of URLSearchParams.");
|
||||
}
|
||||
|
||||
if (arguments.length < 1) {
|
||||
throw new globalObject.TypeError(
|
||||
`Failed to execute 'has' on 'URLSearchParams': 1 argument required, but only ${arguments.length} present.`
|
||||
);
|
||||
}
|
||||
const args = [];
|
||||
{
|
||||
let curArg = arguments[0];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'has' on 'URLSearchParams': parameter 1",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
{
|
||||
let curArg = arguments[1];
|
||||
if (curArg !== undefined) {
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'has' on 'URLSearchParams': parameter 2",
|
||||
globals: globalObject
|
||||
});
|
||||
}
|
||||
args.push(curArg);
|
||||
}
|
||||
return $impl.has(...args);
|
||||
}
|
||||
|
||||
set(name, value) {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'set' called on an object that is not a valid instance of URLSearchParams.");
|
||||
}
|
||||
|
||||
if (arguments.length < 2) {
|
||||
throw new globalObject.TypeError(
|
||||
`Failed to execute 'set' on 'URLSearchParams': 2 arguments required, but only ${arguments.length} present.`
|
||||
);
|
||||
}
|
||||
const args = [];
|
||||
{
|
||||
let curArg = arguments[0];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'set' on 'URLSearchParams': parameter 1",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
{
|
||||
let curArg = arguments[1];
|
||||
curArg = conversions["USVString"](curArg, {
|
||||
context: "Failed to execute 'set' on 'URLSearchParams': parameter 2",
|
||||
globals: globalObject
|
||||
});
|
||||
args.push(curArg);
|
||||
}
|
||||
return utils.tryWrapperForImpl($impl.set(...args));
|
||||
}
|
||||
|
||||
sort() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'sort' called on an object that is not a valid instance of URLSearchParams.");
|
||||
}
|
||||
|
||||
return utils.tryWrapperForImpl($impl.sort());
|
||||
}
|
||||
|
||||
toString() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError(
|
||||
"'toString' called on an object that is not a valid instance of URLSearchParams."
|
||||
);
|
||||
}
|
||||
|
||||
return $impl.toString();
|
||||
}
|
||||
|
||||
keys() {
|
||||
const $impl = utils.implForWrapperWithInterface(this, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError("'keys' called on an object that is not a valid instance of URLSearchParams.");
|
||||
}
|
||||
|
||||
return exports.createDefaultIterator(globalObject, $impl, "key");
|
||||
}
|
||||
|
||||
values() {
|
||||
const $impl = utils.implForWrapperWithInterface(this, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError(
|
||||
"'values' called on an object that is not a valid instance of URLSearchParams."
|
||||
);
|
||||
}
|
||||
|
||||
return exports.createDefaultIterator(globalObject, $impl, "value");
|
||||
}
|
||||
|
||||
entries() {
|
||||
const $impl = utils.implForWrapperWithInterface(this, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError(
|
||||
"'entries' called on an object that is not a valid instance of URLSearchParams."
|
||||
);
|
||||
}
|
||||
|
||||
return exports.createDefaultIterator(globalObject, $impl, "key+value");
|
||||
}
|
||||
|
||||
forEach(callback) {
|
||||
const $impl = utils.implForWrapperWithInterface(this, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError(
|
||||
"'forEach' called on an object that is not a valid instance of URLSearchParams."
|
||||
);
|
||||
}
|
||||
|
||||
if (arguments.length < 1) {
|
||||
throw new globalObject.TypeError(
|
||||
"Failed to execute 'forEach' on 'iterable': 1 argument required, but only 0 present."
|
||||
);
|
||||
}
|
||||
callback = Function.convert(globalObject, callback, {
|
||||
context: "Failed to execute 'forEach' on 'iterable': The callback provided as parameter 1"
|
||||
});
|
||||
const thisArg = arguments[1];
|
||||
let pairs = Array.from($impl);
|
||||
let i = 0;
|
||||
while (i < pairs.length) {
|
||||
const [key, value] = pairs[i].map(utils.tryWrapperForImpl);
|
||||
callback.call(thisArg, value, key, this);
|
||||
pairs = Array.from($impl);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
const $impl = utils.implForWrapperWithInterface(this ?? globalObject, $interfaceDescriptor);
|
||||
if ($impl === null) {
|
||||
throw new globalObject.TypeError(
|
||||
"'get size' called on an object that is not a valid instance of URLSearchParams."
|
||||
);
|
||||
}
|
||||
|
||||
return $impl["size"];
|
||||
}
|
||||
}
|
||||
Object.defineProperties(URLSearchParams.prototype, {
|
||||
append: { enumerable: true },
|
||||
delete: { enumerable: true },
|
||||
get: { enumerable: true },
|
||||
getAll: { enumerable: true },
|
||||
has: { enumerable: true },
|
||||
set: { enumerable: true },
|
||||
sort: { enumerable: true },
|
||||
toString: { enumerable: true },
|
||||
keys: { enumerable: true },
|
||||
values: { enumerable: true },
|
||||
entries: { enumerable: true },
|
||||
forEach: { enumerable: true },
|
||||
size: { enumerable: true },
|
||||
[Symbol.toStringTag]: { value: "URLSearchParams", configurable: true },
|
||||
[Symbol.iterator]: { value: URLSearchParams.prototype.entries, configurable: true, writable: true }
|
||||
});
|
||||
ctorRegistry[interfaceName] = URLSearchParams;
|
||||
|
||||
ctorRegistry["URLSearchParams Iterator"] = Object.create(ctorRegistry["%IteratorPrototype%"], {
|
||||
[Symbol.toStringTag]: {
|
||||
configurable: true,
|
||||
value: "URLSearchParams Iterator"
|
||||
}
|
||||
});
|
||||
utils.define(ctorRegistry["URLSearchParams Iterator"], {
|
||||
next() {
|
||||
const internal = this && this[utils.iterInternalSymbol];
|
||||
if (!internal) {
|
||||
throw new globalObject.TypeError("next() called on a value that is not a URLSearchParams iterator object");
|
||||
}
|
||||
|
||||
const { target, kind, index } = internal;
|
||||
const values = Array.from(target);
|
||||
const len = values.length;
|
||||
if (index >= len) {
|
||||
return newObjectInRealm(globalObject, { value: undefined, done: true });
|
||||
}
|
||||
|
||||
const pair = values[index];
|
||||
internal.index = index + 1;
|
||||
return newObjectInRealm(globalObject, utils.iteratorResult(pair.map(utils.tryWrapperForImpl), kind));
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(globalObject, interfaceName, {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: URLSearchParams
|
||||
});
|
||||
};
|
||||
|
||||
const Impl = require("./URLSearchParams-impl.js");
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
const conversions = require("webidl-conversions");
|
||||
const utils = require("./utils.js");
|
||||
|
||||
exports.convert = (globalObject, value, { context = "The provided value" } = {}) => {
|
||||
if (typeof value !== "function") {
|
||||
throw new globalObject.TypeError(context + " is not a function");
|
||||
}
|
||||
|
||||
function invokeTheCallbackFunction() {
|
||||
const thisArg = utils.tryWrapperForImpl(this);
|
||||
let callResult;
|
||||
|
||||
callResult = Reflect.apply(value, thisArg, []);
|
||||
}
|
||||
|
||||
invokeTheCallbackFunction.construct = () => {
|
||||
let callResult = Reflect.construct(value, []);
|
||||
};
|
||||
|
||||
invokeTheCallbackFunction[utils.wrapperSymbol] = value;
|
||||
invokeTheCallbackFunction.objectReference = value;
|
||||
|
||||
return invokeTheCallbackFunction;
|
||||
};
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
const utf8Encoder = new TextEncoder();
|
||||
const utf8Decoder = new TextDecoder("utf-8", { ignoreBOM: true });
|
||||
|
||||
function utf8Encode(string) {
|
||||
return utf8Encoder.encode(string);
|
||||
}
|
||||
|
||||
function utf8DecodeWithoutBOM(bytes) {
|
||||
return utf8Decoder.decode(bytes);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
utf8Encode,
|
||||
utf8DecodeWithoutBOM
|
||||
};
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
"use strict";
|
||||
|
||||
// Note that we take code points as JS numbers, not JS strings.
|
||||
|
||||
function isASCIIDigit(c) {
|
||||
return c >= 0x30 && c <= 0x39;
|
||||
}
|
||||
|
||||
function isASCIIAlpha(c) {
|
||||
return (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A);
|
||||
}
|
||||
|
||||
function isASCIIAlphanumeric(c) {
|
||||
return isASCIIAlpha(c) || isASCIIDigit(c);
|
||||
}
|
||||
|
||||
function isASCIIHex(c) {
|
||||
return isASCIIDigit(c) || (c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66);
|
||||
}
|
||||
|
||||
function isASCIIString(string) {
|
||||
return !/[^\u0000-\u007F]/u.test(string);
|
||||
}
|
||||
|
||||
function isNoncharacter(c) {
|
||||
return (c >= 0xFDD0 && c <= 0xFDEF) || (c & 0xFFFE) === 0xFFFE;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isASCIIDigit,
|
||||
isASCIIAlpha,
|
||||
isASCIIAlphanumeric,
|
||||
isASCIIHex,
|
||||
isASCIIString,
|
||||
isNoncharacter
|
||||
};
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
"use strict";
|
||||
const { isASCIIHex } = require("./infra");
|
||||
const { utf8Encode } = require("./encoding");
|
||||
|
||||
function p(char) {
|
||||
return char.codePointAt(0);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#percent-encode
|
||||
function percentEncode(c) {
|
||||
let hex = c.toString(16).toUpperCase();
|
||||
if (hex.length === 1) {
|
||||
hex = `0${hex}`;
|
||||
}
|
||||
|
||||
return `%${hex}`;
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#percent-decode
|
||||
function percentDecodeBytes(input) {
|
||||
const output = new Uint8Array(input.byteLength);
|
||||
let outputIndex = 0;
|
||||
for (let i = 0; i < input.byteLength; ++i) {
|
||||
const byte = input[i];
|
||||
if (byte !== 0x25) {
|
||||
output[outputIndex++] = byte;
|
||||
} else if (byte === 0x25 && (!isASCIIHex(input[i + 1]) || !isASCIIHex(input[i + 2]))) {
|
||||
output[outputIndex++] = byte;
|
||||
} else {
|
||||
const bytePoint = parseInt(String.fromCodePoint(input[i + 1], input[i + 2]), 16);
|
||||
output[outputIndex++] = bytePoint;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
return output.slice(0, outputIndex);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#string-percent-decode
|
||||
function percentDecodeString(input) {
|
||||
const bytes = utf8Encode(input);
|
||||
return percentDecodeBytes(bytes);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#c0-control-percent-encode-set
|
||||
function isC0ControlPercentEncode(c) {
|
||||
return c <= 0x1F || c > 0x7E;
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#fragment-percent-encode-set
|
||||
const extraFragmentPercentEncodeSet = new Set([p(" "), p("\""), p("<"), p(">"), p("`")]);
|
||||
function isFragmentPercentEncode(c) {
|
||||
return isC0ControlPercentEncode(c) || extraFragmentPercentEncodeSet.has(c);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#query-percent-encode-set
|
||||
const extraQueryPercentEncodeSet = new Set([p(" "), p("\""), p("#"), p("<"), p(">")]);
|
||||
const extraQueryPercentEncodeChars = String.fromCodePoint(...[...extraQueryPercentEncodeSet].sort());
|
||||
function isQueryPercentEncode(c) {
|
||||
return isC0ControlPercentEncode(c) || extraQueryPercentEncodeSet.has(c);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#special-query-percent-encode-set
|
||||
const extraSpecialQueryPercentEncodeSet = new Set([...extraQueryPercentEncodeSet, p("'")]);
|
||||
const extraSpecialQueryPercentEncodeChars = String.fromCodePoint(...[...extraSpecialQueryPercentEncodeSet].sort());
|
||||
function isSpecialQueryPercentEncode(c) {
|
||||
return isC0ControlPercentEncode(c) || extraSpecialQueryPercentEncodeSet.has(c);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#path-percent-encode-set
|
||||
const extraPathPercentEncodeSet = new Set([p("?"), p("`"), p("{"), p("}"), p("^")]);
|
||||
function isPathPercentEncode(c) {
|
||||
return isQueryPercentEncode(c) || extraPathPercentEncodeSet.has(c);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#userinfo-percent-encode-set
|
||||
const extraUserinfoPercentEncodeSet =
|
||||
new Set([p("/"), p(":"), p(";"), p("="), p("@"), p("["), p("\\"), p("]"), p("|")]);
|
||||
function isUserinfoPercentEncode(c) {
|
||||
return isPathPercentEncode(c) || extraUserinfoPercentEncodeSet.has(c);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#component-percent-encode-set
|
||||
const extraComponentPercentEncodeSet = new Set([p("$"), p("%"), p("&"), p("+"), p(",")]);
|
||||
function isComponentPercentEncode(c) {
|
||||
return isUserinfoPercentEncode(c) || extraComponentPercentEncodeSet.has(c);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#application-x-www-form-urlencoded-percent-encode-set
|
||||
const extraURLEncodedPercentEncodeSet = new Set([p("!"), p("'"), p("("), p(")"), p("~")]);
|
||||
function isURLEncodedPercentEncode(c) {
|
||||
return isComponentPercentEncode(c) || extraURLEncodedPercentEncodeSet.has(c);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#code-point-percent-encode-after-encoding
|
||||
// https://url.spec.whatwg.org/#utf-8-percent-encode
|
||||
// Assuming encoding is always utf-8 allows us to trim one of the logic branches. TODO: support encoding.
|
||||
// The "-Internal" variant here has code points as JS strings. The external version used by other files has code points
|
||||
// as JS numbers, like the rest of the codebase.
|
||||
function utf8PercentEncodeCodePointInternal(codePoint, percentEncodePredicate) {
|
||||
const bytes = utf8Encode(codePoint);
|
||||
let output = "";
|
||||
for (const byte of bytes) {
|
||||
// Our percentEncodePredicate operates on bytes, not code points, so this is slightly different from the spec.
|
||||
if (!percentEncodePredicate(byte)) {
|
||||
output += String.fromCharCode(byte);
|
||||
} else {
|
||||
output += percentEncode(byte);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function utf8PercentEncodeCodePoint(codePoint, percentEncodePredicate) {
|
||||
return utf8PercentEncodeCodePointInternal(String.fromCodePoint(codePoint), percentEncodePredicate);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#string-percent-encode-after-encoding
|
||||
// https://url.spec.whatwg.org/#string-utf-8-percent-encode
|
||||
function utf8PercentEncodeString(input, percentEncodePredicate, spaceAsPlus = false) {
|
||||
let output = "";
|
||||
for (const codePoint of input) {
|
||||
if (spaceAsPlus && codePoint === " ") {
|
||||
output += "+";
|
||||
} else {
|
||||
output += utf8PercentEncodeCodePointInternal(codePoint, percentEncodePredicate);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isC0ControlPercentEncode,
|
||||
isFragmentPercentEncode,
|
||||
isQueryPercentEncode,
|
||||
isSpecialQueryPercentEncode,
|
||||
isPathPercentEncode,
|
||||
isUserinfoPercentEncode,
|
||||
isURLEncodedPercentEncode,
|
||||
extraQueryPercentEncodeChars,
|
||||
extraSpecialQueryPercentEncodeChars,
|
||||
percentDecodeString,
|
||||
percentDecodeBytes,
|
||||
utf8PercentEncodeString,
|
||||
utf8PercentEncodeCodePoint
|
||||
};
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
"use strict";
|
||||
|
||||
const tr46 = require("tr46");
|
||||
|
||||
const infra = require("./infra");
|
||||
|
||||
function p(char) {
|
||||
return char.codePointAt(0);
|
||||
}
|
||||
|
||||
const failure = Symbol("failure");
|
||||
|
||||
const specialSchemes = {
|
||||
ftp: 21,
|
||||
file: null,
|
||||
http: 80,
|
||||
https: 443,
|
||||
ws: 80,
|
||||
wss: 443
|
||||
};
|
||||
|
||||
const urlCodePoints = new Set([
|
||||
p("!"), p("$"), p("&"), p("'"), p("("), p(")"), p("*"), p("+"), p(","), p("-"), p("."), p("/"),
|
||||
p(":"), p(";"), p("="), p("?"), p("@"), p("_"), p("~")
|
||||
]);
|
||||
|
||||
const forbiddenHostCodePoints = new Set([
|
||||
0x00, 0x09, 0x0A, 0x0D, 0x20, p("#"), p("/"), p(":"), p("<"), p(">"), p("?"), p("@"), p("["),
|
||||
p("\\"), p("]"), p("^"), p("|")
|
||||
]);
|
||||
|
||||
function isURLCodePoint(c) {
|
||||
return infra.isASCIIAlphanumeric(c) ||
|
||||
urlCodePoints.has(c) ||
|
||||
(c >= 0xA0 && c <= 0x10FFFD && (c < 0xD800 || c > 0xDFFF) && !infra.isNoncharacter(c));
|
||||
}
|
||||
|
||||
function isInvalidURLCodePoint(c) {
|
||||
return !isURLCodePoint(c) && c !== p("%");
|
||||
}
|
||||
|
||||
function isPercentEncodedByteAt(input, index) {
|
||||
return index + 2 < input.length &&
|
||||
input[index] === "%" &&
|
||||
infra.isASCIIHex(input.charCodeAt(index + 1)) &&
|
||||
infra.isASCIIHex(input.charCodeAt(index + 2));
|
||||
}
|
||||
|
||||
function containsPercentEncodedByte(input) {
|
||||
return /%[0-9A-Fa-f]{2}/u.test(input);
|
||||
}
|
||||
|
||||
function containsForbiddenHostCodePoint(string) {
|
||||
return [...string].some(c => forbiddenHostCodePoints.has(c.codePointAt(0)));
|
||||
}
|
||||
|
||||
function containsForbiddenDomainCodePoint(string) {
|
||||
return [...string].some(c => {
|
||||
const cp = c.codePointAt(0);
|
||||
return forbiddenHostCodePoints.has(cp) || (cp >= 0x00 && cp <= 0x1F) || cp === p("%") || cp === 0x7F;
|
||||
});
|
||||
}
|
||||
|
||||
function domainParserToASCII(domain, beStrict) {
|
||||
return tr46.toASCII(domain, {
|
||||
checkHyphens: beStrict,
|
||||
checkBidi: true,
|
||||
checkJoiners: true,
|
||||
useSTD3ASCIIRules: beStrict,
|
||||
transitionalProcessing: false,
|
||||
verifyDNSLength: beStrict,
|
||||
ignoreInvalidPunycode: false
|
||||
});
|
||||
}
|
||||
|
||||
function parseIPv4Number(input, validationErrors = null) {
|
||||
if (input === "") {
|
||||
return failure;
|
||||
}
|
||||
|
||||
let validationErrorSeen = false;
|
||||
let R = 10;
|
||||
|
||||
if (input.length >= 2 && input.charAt(0) === "0" && input.charAt(1).toLowerCase() === "x") {
|
||||
validationErrorSeen = true;
|
||||
input = input.substring(2);
|
||||
R = 16;
|
||||
} else if (input.length >= 2 && input.charAt(0) === "0") {
|
||||
validationErrorSeen = true;
|
||||
input = input.substring(1);
|
||||
R = 8;
|
||||
}
|
||||
|
||||
if (input === "") {
|
||||
validationErrors?.push("IPv4-non-decimal-part");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let regex = /[^0-7]/u;
|
||||
if (R === 10) {
|
||||
regex = /[^0-9]/u;
|
||||
}
|
||||
if (R === 16) {
|
||||
regex = /[^0-9A-Fa-f]/u;
|
||||
}
|
||||
|
||||
if (regex.test(input)) {
|
||||
return failure;
|
||||
}
|
||||
|
||||
if (validationErrorSeen) {
|
||||
validationErrors?.push("IPv4-non-decimal-part");
|
||||
}
|
||||
|
||||
return parseInt(input, R);
|
||||
}
|
||||
|
||||
function endsInANumber(input) {
|
||||
const parts = input.split(".");
|
||||
if (parts[parts.length - 1] === "") {
|
||||
if (parts.length === 1) {
|
||||
return false;
|
||||
}
|
||||
parts.pop();
|
||||
}
|
||||
|
||||
const last = parts[parts.length - 1];
|
||||
if (/^[0-9]+$/u.test(last)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parseIPv4Number(last) !== failure) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function domainParser(domain, validationErrors = null, beStrict = false) {
|
||||
// A domain-to-ASCII validation error is reported whenever the strict Unicode ToASCII (CheckHyphens,
|
||||
// UseSTD3ASCIIRules, and VerifyDnsLength all true) fails, even when the relaxed parameters used
|
||||
// for non-strict parsing succeed. This step does not itself fail the algorithm.
|
||||
//
|
||||
// Spec divergence (performance): the URL Standard runs this strict pass unconditionally, but when
|
||||
// beStrict is false its only effect is that validation error, so we skip it when we are neither
|
||||
// being strict nor collecting validation errors.
|
||||
if (beStrict || validationErrors) {
|
||||
const strictResult = domainParserToASCII(domain, true);
|
||||
if (strictResult === null) {
|
||||
validationErrors?.push("domain-to-ASCII");
|
||||
}
|
||||
|
||||
if (beStrict) {
|
||||
return strictResult === null ? failure : strictResult;
|
||||
}
|
||||
}
|
||||
|
||||
let result;
|
||||
if (infra.isASCIIString(domain)) {
|
||||
// For web compatibility an ASCII domain is returned lowercased regardless of ToASCII's outcome.
|
||||
result = domain.toLowerCase();
|
||||
} else {
|
||||
result = domainParserToASCII(domain, false);
|
||||
if (result === null) {
|
||||
return failure;
|
||||
}
|
||||
}
|
||||
|
||||
if (result === "") {
|
||||
return failure;
|
||||
}
|
||||
if (containsForbiddenDomainCodePoint(result)) {
|
||||
return failure;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function isSpecialScheme(scheme) {
|
||||
return specialSchemes[scheme.toLowerCase()] !== undefined;
|
||||
}
|
||||
|
||||
function isSpecialSchemeExceptFile(scheme) {
|
||||
return isSpecialScheme(scheme) && scheme.toLowerCase() !== "file";
|
||||
}
|
||||
|
||||
function defaultPort(scheme) {
|
||||
return specialSchemes[scheme.toLowerCase()];
|
||||
}
|
||||
|
||||
function isWindowsDriveLetterCodePoints(cp1, cp2) {
|
||||
return infra.isASCIIAlpha(cp1) && (cp2 === p(":") || cp2 === p("|"));
|
||||
}
|
||||
|
||||
function isWindowsDriveLetterString(string) {
|
||||
return string.length === 2 && infra.isASCIIAlpha(string.codePointAt(0)) && (string[1] === ":" || string[1] === "|");
|
||||
}
|
||||
|
||||
function isNormalizedWindowsDriveLetterString(string) {
|
||||
return string.length === 2 && infra.isASCIIAlpha(string.codePointAt(0)) && string[1] === ":";
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
containsForbiddenHostCodePoint,
|
||||
containsPercentEncodedByte,
|
||||
defaultPort,
|
||||
domainParser,
|
||||
endsInANumber,
|
||||
failure,
|
||||
forbiddenHostCodePoints,
|
||||
isInvalidURLCodePoint,
|
||||
isNormalizedWindowsDriveLetterString,
|
||||
isPercentEncodedByteAt,
|
||||
parseIPv4Number,
|
||||
isSpecialScheme,
|
||||
isSpecialSchemeExceptFile,
|
||||
isURLCodePoint,
|
||||
isWindowsDriveLetterCodePoints,
|
||||
isWindowsDriveLetterString,
|
||||
p
|
||||
};
|
||||
+1230
File diff suppressed because it is too large.
Load diff
+438
@@ -0,0 +1,438 @@
|
||||
"use strict";
|
||||
|
||||
const infra = require("./infra");
|
||||
const {
|
||||
domainParser,
|
||||
endsInANumber,
|
||||
failure,
|
||||
forbiddenHostCodePoints,
|
||||
isPercentEncodedByteAt,
|
||||
isSpecialSchemeExceptFile,
|
||||
isURLCodePoint,
|
||||
p
|
||||
} = require("./url-miscellaneous");
|
||||
const { hasAnOpaquePath } = require("./url-state-machine");
|
||||
|
||||
const pathSegmentExcludedCodePoints = new Set([p("/"), p("?")]);
|
||||
const opaquePathExcludedCodePoints = new Set([p("?")]);
|
||||
|
||||
function splitOffFragment(input) {
|
||||
const fragmentStart = input.indexOf("#");
|
||||
|
||||
if (fragmentStart === -1) {
|
||||
return { beforeFragment: input, fragment: null };
|
||||
}
|
||||
|
||||
return {
|
||||
beforeFragment: input.substring(0, fragmentStart),
|
||||
fragment: input.substring(fragmentStart + 1)
|
||||
};
|
||||
}
|
||||
|
||||
function splitOffQuery(input) {
|
||||
const queryStart = input.indexOf("?");
|
||||
|
||||
if (queryStart === -1) {
|
||||
return { beforeQuery: input, query: null };
|
||||
}
|
||||
|
||||
return {
|
||||
beforeQuery: input.substring(0, queryStart),
|
||||
query: input.substring(queryStart + 1)
|
||||
};
|
||||
}
|
||||
|
||||
function isValidURLString(input, options = {}) {
|
||||
const baseURL = options.baseURL ?? null;
|
||||
|
||||
return isValidAbsoluteURLWithFragmentString(input) ||
|
||||
(baseURL !== null && isValidRelativeURLWithFragmentString(input, baseURL));
|
||||
}
|
||||
|
||||
function isValidAbsoluteURLWithFragmentString(input) {
|
||||
const { beforeFragment, fragment } = splitOffFragment(input);
|
||||
|
||||
return (fragment === null || isValidURLFragmentString(fragment)) && isValidAbsoluteURLString(beforeFragment);
|
||||
}
|
||||
|
||||
function isValidAbsoluteURLString(input) {
|
||||
const { beforeQuery, query } = splitOffQuery(input);
|
||||
|
||||
if (query !== null && !isValidURLQueryString(query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const schemeMatch = /^([A-Za-z][A-Za-z0-9+.-]*):/u.exec(beforeQuery);
|
||||
if (schemeMatch === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const scheme = schemeMatch[1].toLowerCase();
|
||||
const afterScheme = beforeQuery.substring(schemeMatch[0].length);
|
||||
|
||||
if (isSpecialSchemeExceptFile(scheme)) {
|
||||
return isValidSchemeRelativeSpecialURLString(afterScheme);
|
||||
}
|
||||
if (scheme === "file") {
|
||||
return isValidSchemeRelativeFileURLString(afterScheme);
|
||||
}
|
||||
|
||||
return isValidSchemeRelativeURLString(afterScheme) ||
|
||||
isValidPathAbsoluteNonAuthorityURLString(afterScheme) ||
|
||||
isValidOpaquePathURLString(afterScheme);
|
||||
}
|
||||
|
||||
function isValidRelativeURLWithFragmentString(input, baseURL) {
|
||||
if (hasAnOpaquePath(baseURL)) {
|
||||
return input.startsWith("#") && isValidURLFragmentString(input.substring(1));
|
||||
}
|
||||
|
||||
const { beforeFragment, fragment } = splitOffFragment(input);
|
||||
|
||||
return isValidRelativeURLString(beforeFragment, baseURL) &&
|
||||
(fragment === null || isValidURLFragmentString(fragment));
|
||||
}
|
||||
|
||||
function isValidRelativeURLString(input, baseURL) {
|
||||
const { beforeQuery, query } = splitOffQuery(input);
|
||||
const hasValidQuery = query === null || isValidURLQueryString(query);
|
||||
|
||||
if (isValidPathAbsoluteNonAuthorityURLString(beforeQuery)) {
|
||||
return hasValidQuery;
|
||||
}
|
||||
|
||||
if (isValidPathRelativeSchemeLessURLString(beforeQuery)) {
|
||||
return hasValidQuery;
|
||||
}
|
||||
|
||||
if (isSpecialSchemeExceptFile(baseURL.scheme)) {
|
||||
return isValidSchemeRelativeSpecialURLString(beforeQuery) && hasValidQuery;
|
||||
}
|
||||
|
||||
if (baseURL.scheme === "file") {
|
||||
return isValidSchemeRelativeFileURLString(beforeQuery) && hasValidQuery;
|
||||
}
|
||||
|
||||
return isValidSchemeRelativeURLString(beforeQuery) && hasValidQuery;
|
||||
}
|
||||
|
||||
function isValidSchemeRelativeSpecialURLString(input) {
|
||||
if (!input.startsWith("//")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { authority, path } = splitOffPath(input.substring(2));
|
||||
|
||||
return isValidHostAndPortString(authority, isValidHostString) &&
|
||||
(path === null || isValidPathAbsoluteURLString(path));
|
||||
}
|
||||
|
||||
function isValidSchemeRelativeURLString(input) {
|
||||
if (!input.startsWith("//")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { authority, path } = splitOffPath(input.substring(2));
|
||||
|
||||
return isValidOpaqueHostAndPortString(authority) &&
|
||||
(path === null || isValidPathAbsoluteURLString(path));
|
||||
}
|
||||
|
||||
function isValidSchemeRelativeFileURLString(input) {
|
||||
if (!input.startsWith("//")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const afterSlashes = input.substring(2);
|
||||
|
||||
return afterSlashes === "" ||
|
||||
isValidHostAndPathAbsoluteURLString(afterSlashes) ||
|
||||
isValidPathAbsoluteURLString(afterSlashes);
|
||||
}
|
||||
|
||||
function isValidHostAndPathAbsoluteURLString(input) {
|
||||
const { authority, path } = splitOffPath(input);
|
||||
|
||||
return isValidHostString(authority) &&
|
||||
(path === null || isValidPathAbsoluteURLString(path));
|
||||
}
|
||||
|
||||
function splitOffPath(input) {
|
||||
const pathStart = input.indexOf("/");
|
||||
|
||||
if (pathStart === -1) {
|
||||
return { authority: input, path: null };
|
||||
}
|
||||
|
||||
return {
|
||||
authority: input.substring(0, pathStart),
|
||||
path: input.substring(pathStart)
|
||||
};
|
||||
}
|
||||
|
||||
function isValidHostAndPortString(input, isValidHost) {
|
||||
const parsed = splitHostAndPort(input);
|
||||
|
||||
return parsed !== null &&
|
||||
parsed.host !== "" &&
|
||||
isValidHost(parsed.host) &&
|
||||
(parsed.port === null || isValidURLPortString(parsed.port));
|
||||
}
|
||||
|
||||
function isValidOpaqueHostAndPortString(input) {
|
||||
if (input === "") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const parsed = splitHostAndPort(input);
|
||||
|
||||
return parsed !== null &&
|
||||
parsed.host !== "" &&
|
||||
isValidOpaqueHostString(parsed.host) &&
|
||||
(parsed.port === null || isValidURLPortString(parsed.port));
|
||||
}
|
||||
|
||||
function splitHostAndPort(input) {
|
||||
if (input.startsWith("[")) {
|
||||
const hostEnd = input.indexOf("]");
|
||||
if (hostEnd === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const rest = input.substring(hostEnd + 1);
|
||||
if (rest !== "" && !rest.startsWith(":")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
host: input.substring(0, hostEnd + 1),
|
||||
port: rest === "" ? null : rest.substring(1)
|
||||
};
|
||||
}
|
||||
|
||||
const portStart = input.indexOf(":");
|
||||
if (portStart === -1) {
|
||||
return { host: input, port: null };
|
||||
}
|
||||
|
||||
return {
|
||||
host: input.substring(0, portStart),
|
||||
port: input.substring(portStart + 1)
|
||||
};
|
||||
}
|
||||
|
||||
function isValidHostString(input) {
|
||||
return isValidDomainString(input) ||
|
||||
isValidIPv4AddressString(input) ||
|
||||
isValidBracketedIPv6AddressString(input);
|
||||
}
|
||||
|
||||
function isValidDomainString(input) {
|
||||
const domain = domainParser(input, null, true);
|
||||
|
||||
return domain !== failure && !endsInANumber(domain);
|
||||
}
|
||||
|
||||
function isValidIPv4AddressString(input) {
|
||||
const pieces = input.split(".");
|
||||
|
||||
return pieces.length === 4 && pieces.every(isValidIPv4AddressPieceString);
|
||||
}
|
||||
|
||||
function isValidIPv4AddressPieceString(input) {
|
||||
if (input === "" || input.length > 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
if (!infra.isASCIIDigit(input.codePointAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (input.length > 1 && input[0] === "0") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Number(input) <= 255;
|
||||
}
|
||||
|
||||
function isValidBracketedIPv6AddressString(input) {
|
||||
return input.startsWith("[") &&
|
||||
input.endsWith("]") &&
|
||||
isValidIPv6AddressString(input.substring(1, input.length - 1));
|
||||
}
|
||||
|
||||
function isValidIPv6AddressString(input) {
|
||||
return getIPv6PiecesStringEffectiveLength(input) === 8 ||
|
||||
getIPv6PiecesAndIPv4StringEffectiveLength(input) === 8 ||
|
||||
isValidCompressedIPv6AddressString(input);
|
||||
}
|
||||
|
||||
function isValidCompressedIPv6AddressString(input) {
|
||||
const compressionIndex = input.indexOf("::");
|
||||
if (compressionIndex === -1 || compressionIndex !== input.lastIndexOf("::")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const preceding = input.substring(0, compressionIndex);
|
||||
const following = input.substring(compressionIndex + 2);
|
||||
|
||||
const precedingLength = getOptionalIPv6PiecesStringEffectiveLength(preceding);
|
||||
const followingLength = getOptionalIPv6PiecesOrPiecesAndIPv4StringEffectiveLength(following);
|
||||
if (precedingLength === failure || followingLength === failure) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return precedingLength + followingLength <= 7;
|
||||
}
|
||||
|
||||
function getOptionalIPv6PiecesStringEffectiveLength(input) {
|
||||
return input === "" ? 0 : getIPv6PiecesStringEffectiveLength(input);
|
||||
}
|
||||
|
||||
function getOptionalIPv6PiecesOrPiecesAndIPv4StringEffectiveLength(input) {
|
||||
return input === "" ? 0 : getIPv6PiecesOrPiecesAndIPv4StringEffectiveLength(input);
|
||||
}
|
||||
|
||||
function getIPv6PiecesOrPiecesAndIPv4StringEffectiveLength(input) {
|
||||
if (isValidIPv6PiecesString(input)) {
|
||||
return getIPv6PiecesStringEffectiveLength(input);
|
||||
}
|
||||
|
||||
if (isValidIPv6PiecesAndIPv4String(input)) {
|
||||
return getIPv6PiecesAndIPv4StringEffectiveLength(input);
|
||||
}
|
||||
|
||||
return failure;
|
||||
}
|
||||
|
||||
function isValidIPv6PiecesAndIPv4String(input) {
|
||||
if (isValidIPv4AddressString(input)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const ipv4SeparatorIndex = input.lastIndexOf(":");
|
||||
if (ipv4SeparatorIndex === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isValidIPv6PiecesString(input.substring(0, ipv4SeparatorIndex)) &&
|
||||
isValidIPv4AddressString(input.substring(ipv4SeparatorIndex + 1));
|
||||
}
|
||||
|
||||
function getIPv6PiecesAndIPv4StringEffectiveLength(input) {
|
||||
if (!isValidIPv6PiecesAndIPv4String(input)) {
|
||||
return failure;
|
||||
}
|
||||
|
||||
const ipv4SeparatorIndex = input.lastIndexOf(":");
|
||||
if (ipv4SeparatorIndex === -1) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
const piecesLength = getIPv6PiecesStringEffectiveLength(input.substring(0, ipv4SeparatorIndex));
|
||||
return piecesLength + 2;
|
||||
}
|
||||
|
||||
function isValidIPv6PiecesString(input) {
|
||||
if (input === "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pieces = input.split(":");
|
||||
return pieces.every(isValidIPv6PieceString);
|
||||
}
|
||||
|
||||
function getIPv6PiecesStringEffectiveLength(input) {
|
||||
return isValidIPv6PiecesString(input) ? input.split(":").length : failure;
|
||||
}
|
||||
|
||||
function isValidIPv6PieceString(input) {
|
||||
if (input === "" || input.length > 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
if (!infra.isASCIIHex(input.codePointAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const value = parseInt(input, 16);
|
||||
return value <= 0xFFFF && (input.length === 1 || input[0] !== "0");
|
||||
}
|
||||
|
||||
function isValidOpaqueHostString(input) {
|
||||
return isValidBracketedIPv6AddressString(input) ||
|
||||
(input !== "" && isValidURLUnits(input, forbiddenHostCodePoints));
|
||||
}
|
||||
|
||||
function isValidURLPortString(input) {
|
||||
if (input === "") {
|
||||
return true;
|
||||
}
|
||||
if (!/^[0-9]+$/u.test(input)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Number(input) <= 65535;
|
||||
}
|
||||
|
||||
function isValidPathAbsoluteURLString(input) {
|
||||
return input.startsWith("/") && input.substring(1).split("/").every(isValidURLPathSegmentString);
|
||||
}
|
||||
|
||||
function isValidPathAbsoluteNonAuthorityURLString(input) {
|
||||
return isValidPathAbsoluteURLString(input) && !input.startsWith("//");
|
||||
}
|
||||
|
||||
function isValidPathRelativeURLString(input) {
|
||||
return !input.startsWith("/") && input.split("/").every(isValidURLPathSegmentString);
|
||||
}
|
||||
|
||||
function isValidURLPathSegmentString(input) {
|
||||
return isValidURLUnits(input, pathSegmentExcludedCodePoints);
|
||||
}
|
||||
|
||||
function isValidPathRelativeSchemeLessURLString(input) {
|
||||
return isValidPathRelativeURLString(input) && !/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(input);
|
||||
}
|
||||
|
||||
function isValidOpaquePathURLString(input) {
|
||||
return (input === "" || input[0] !== "/") && isValidURLUnits(input, opaquePathExcludedCodePoints);
|
||||
}
|
||||
|
||||
function isValidURLQueryString(input) {
|
||||
return isValidURLUnits(input);
|
||||
}
|
||||
|
||||
function isValidURLFragmentString(input) {
|
||||
return isValidURLUnits(input);
|
||||
}
|
||||
|
||||
function isValidURLUnits(input, excludedCodePoints = new Set()) {
|
||||
for (let i = 0; i < input.length;) {
|
||||
if (input[i] === "%") {
|
||||
if (!isPercentEncodedByteAt(input, i)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
const codePoint = input.codePointAt(i);
|
||||
if (!isURLCodePoint(codePoint) || excludedCodePoints.has(codePoint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
i += codePoint > 0xFFFF ? 2 : 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isValidURLString
|
||||
};
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
const { utf8Encode, utf8DecodeWithoutBOM } = require("./encoding");
|
||||
const { percentDecodeBytes, utf8PercentEncodeString, isURLEncodedPercentEncode } = require("./percent-encoding");
|
||||
|
||||
function p(char) {
|
||||
return char.codePointAt(0);
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#concept-urlencoded-parser
|
||||
function parseUrlencoded(input) {
|
||||
const sequences = strictlySplitByteSequence(input, p("&"));
|
||||
const output = [];
|
||||
for (const bytes of sequences) {
|
||||
if (bytes.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let name, value;
|
||||
const indexOfEqual = bytes.indexOf(p("="));
|
||||
|
||||
if (indexOfEqual >= 0) {
|
||||
name = bytes.slice(0, indexOfEqual);
|
||||
value = bytes.slice(indexOfEqual + 1);
|
||||
} else {
|
||||
name = bytes;
|
||||
value = new Uint8Array(0);
|
||||
}
|
||||
|
||||
name = replaceByteInByteSequence(name, 0x2B, 0x20);
|
||||
value = replaceByteInByteSequence(value, 0x2B, 0x20);
|
||||
|
||||
const nameString = utf8DecodeWithoutBOM(percentDecodeBytes(name));
|
||||
const valueString = utf8DecodeWithoutBOM(percentDecodeBytes(value));
|
||||
|
||||
output.push([nameString, valueString]);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#concept-urlencoded-string-parser
|
||||
function parseUrlencodedString(input) {
|
||||
return parseUrlencoded(utf8Encode(input));
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#concept-urlencoded-serializer
|
||||
function serializeUrlencoded(tuples) {
|
||||
// TODO: accept and use encoding argument
|
||||
|
||||
let output = "";
|
||||
for (const [i, tuple] of tuples.entries()) {
|
||||
const name = utf8PercentEncodeString(tuple[0], isURLEncodedPercentEncode, true);
|
||||
const value = utf8PercentEncodeString(tuple[1], isURLEncodedPercentEncode, true);
|
||||
|
||||
if (i !== 0) {
|
||||
output += "&";
|
||||
}
|
||||
output += `${name}=${value}`;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function strictlySplitByteSequence(buf, cp) {
|
||||
const list = [];
|
||||
let last = 0;
|
||||
let i = buf.indexOf(cp);
|
||||
while (i >= 0) {
|
||||
list.push(buf.slice(last, i));
|
||||
last = i + 1;
|
||||
i = buf.indexOf(cp, last);
|
||||
}
|
||||
if (last !== buf.length) {
|
||||
list.push(buf.slice(last));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
function replaceByteInByteSequence(buf, from, to) {
|
||||
let i = buf.indexOf(from);
|
||||
while (i >= 0) {
|
||||
buf[i] = to;
|
||||
i = buf.indexOf(from, i + 1);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseUrlencodedString,
|
||||
serializeUrlencoded
|
||||
};
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
"use strict";
|
||||
|
||||
// Returns "Type(value) is Object" in ES terminology.
|
||||
function isObject(value) {
|
||||
return (typeof value === "object" && value !== null) || typeof value === "function";
|
||||
}
|
||||
|
||||
const call = Function.call.bind(Function.call);
|
||||
|
||||
// Like `Object.assign`, but using `[[GetOwnProperty]]` and `[[DefineOwnProperty]]`
|
||||
// instead of `[[Get]]` and `[[Set]]` and only allowing objects
|
||||
function define(target, source) {
|
||||
for (const key of Reflect.ownKeys(source)) {
|
||||
const descriptor = Reflect.getOwnPropertyDescriptor(source, key);
|
||||
if (descriptor && !Reflect.defineProperty(target, key, descriptor)) {
|
||||
throw new TypeError(`Cannot redefine property: ${String(key)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function newObjectInRealm(globalObject, object) {
|
||||
const ctorRegistry = initCtorRegistry(globalObject);
|
||||
return Object.defineProperties(
|
||||
Object.create(ctorRegistry["%Object.prototype%"]),
|
||||
Object.getOwnPropertyDescriptors(object)
|
||||
);
|
||||
}
|
||||
|
||||
const wrapperSymbol = Symbol("wrapper");
|
||||
const sameObjectCaches = Symbol("SameObject caches");
|
||||
const ctorRegistrySymbol = Symbol.for("[webidl2js] constructor registry");
|
||||
|
||||
const AsyncIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(async function* () {}).prototype);
|
||||
|
||||
function initCtorRegistry(globalObject) {
|
||||
if (Object.hasOwn(globalObject, ctorRegistrySymbol)) {
|
||||
return globalObject[ctorRegistrySymbol];
|
||||
}
|
||||
|
||||
const ctorRegistry = Object.create(null);
|
||||
|
||||
// In addition to registering all the WebIDL2JS-generated types in the constructor registry,
|
||||
// we also register a few intrinsics that we make use of in generated code, since they are not
|
||||
// easy to grab from the globalObject variable.
|
||||
ctorRegistry["%Object.prototype%"] = globalObject.Object.prototype;
|
||||
ctorRegistry["%IteratorPrototype%"] = Object.getPrototypeOf(
|
||||
Object.getPrototypeOf(new globalObject.Array()[Symbol.iterator]())
|
||||
);
|
||||
|
||||
try {
|
||||
ctorRegistry["%AsyncIteratorPrototype%"] = Object.getPrototypeOf(
|
||||
Object.getPrototypeOf(
|
||||
globalObject.eval("(async function* () {})").prototype
|
||||
)
|
||||
);
|
||||
} catch {
|
||||
ctorRegistry["%AsyncIteratorPrototype%"] = AsyncIteratorPrototype;
|
||||
}
|
||||
|
||||
globalObject[ctorRegistrySymbol] = ctorRegistry;
|
||||
return ctorRegistry;
|
||||
}
|
||||
|
||||
function getSameObject(wrapper, prop, creator) {
|
||||
if (!wrapper[sameObjectCaches]) {
|
||||
wrapper[sameObjectCaches] = Object.create(null);
|
||||
}
|
||||
|
||||
if (prop in wrapper[sameObjectCaches]) {
|
||||
return wrapper[sameObjectCaches][prop];
|
||||
}
|
||||
|
||||
wrapper[sameObjectCaches][prop] = creator();
|
||||
return wrapper[sameObjectCaches][prop];
|
||||
}
|
||||
|
||||
function createInterfaceDescriptor(getParent) {
|
||||
// A parent's module can import its child, so resolve ancestry only after the modules have loaded.
|
||||
const descriptor = {
|
||||
get inclusiveInheritedInterfaces() {
|
||||
const interfaces = getParent ? [...getParent().inclusiveInheritedInterfaces, descriptor] : [descriptor];
|
||||
Object.defineProperties(descriptor, {
|
||||
inclusiveInheritedInterfaces: { value: interfaces },
|
||||
inheritanceDepth: { value: interfaces.length - 1 }
|
||||
});
|
||||
return interfaces;
|
||||
},
|
||||
get inheritanceDepth() {
|
||||
return descriptor.inclusiveInheritedInterfaces.length - 1;
|
||||
}
|
||||
};
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
class ReturnValue {
|
||||
constructor(value) {
|
||||
// Returning `value` lets derived classes stamp private fields onto that object.
|
||||
// eslint-disable-next-line no-constructor-return
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
class WrapperData extends ReturnValue {
|
||||
#impl;
|
||||
#interfaceDescriptor;
|
||||
|
||||
constructor(wrapper, impl, interfaceDescriptor) {
|
||||
super(wrapper);
|
||||
this.#impl = impl;
|
||||
this.#interfaceDescriptor = interfaceDescriptor;
|
||||
}
|
||||
|
||||
static registerWrapper(wrapper, impl, interfaceDescriptor) {
|
||||
// Private fields make registration one-time: an existing object's brand cannot be replaced.
|
||||
return new WrapperData(wrapper, impl, interfaceDescriptor);
|
||||
}
|
||||
|
||||
static implForWrapper(wrapper) {
|
||||
if (!isObject(wrapper) || !(#impl in wrapper)) {
|
||||
return null;
|
||||
}
|
||||
return wrapper.#impl;
|
||||
}
|
||||
|
||||
static implForWrapperWithInterface(wrapper, interfaceDescriptor) {
|
||||
if (!isObject(wrapper) || !(#impl in wrapper)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (wrapper.#interfaceDescriptor?.inclusiveInheritedInterfaces[interfaceDescriptor.inheritanceDepth] !==
|
||||
interfaceDescriptor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return wrapper.#impl;
|
||||
}
|
||||
}
|
||||
|
||||
const { registerWrapper, implForWrapper, implForWrapperWithInterface } = WrapperData;
|
||||
|
||||
function wrapperForImpl(impl) {
|
||||
return impl?.[wrapperSymbol] ?? null;
|
||||
}
|
||||
|
||||
function tryWrapperForImpl(impl) {
|
||||
const wrapper = wrapperForImpl(impl);
|
||||
return wrapper !== null ? wrapper : impl;
|
||||
}
|
||||
|
||||
function tryImplForWrapper(wrapper) {
|
||||
const impl = implForWrapper(wrapper);
|
||||
return impl !== null ? impl : wrapper;
|
||||
}
|
||||
|
||||
const iterInternalSymbol = Symbol("internal");
|
||||
|
||||
function isArrayIndexPropName(P) {
|
||||
if (typeof P !== "string") {
|
||||
return false;
|
||||
}
|
||||
const i = P >>> 0;
|
||||
if (i === 2 ** 32 - 1) {
|
||||
return false;
|
||||
}
|
||||
const s = `${i}`;
|
||||
if (P !== s) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const arrayBufferByteLengthGetter =
|
||||
Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, "byteLength").get;
|
||||
function isArrayBuffer(value) {
|
||||
try {
|
||||
arrayBufferByteLengthGetter.call(value);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const sharedArrayBufferByteLengthGetter =
|
||||
Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, "byteLength").get;
|
||||
function isSharedArrayBuffer(value) {
|
||||
try {
|
||||
sharedArrayBufferByteLengthGetter.call(value);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function iteratorResult([key, value], kind) {
|
||||
let result;
|
||||
switch (kind) {
|
||||
case "key":
|
||||
result = key;
|
||||
break;
|
||||
case "value":
|
||||
result = value;
|
||||
break;
|
||||
case "key+value":
|
||||
result = [key, value];
|
||||
break;
|
||||
}
|
||||
return { value: result, done: false };
|
||||
}
|
||||
|
||||
function ordinarySetWithOwnDescriptor(target, property, value, receiver, ownDesc) {
|
||||
if (ownDesc === undefined) {
|
||||
const parent = Reflect.getPrototypeOf(target);
|
||||
if (parent !== null) {
|
||||
return Reflect.set(parent, property, value, receiver);
|
||||
}
|
||||
ownDesc = { writable: true, enumerable: true, configurable: true, value: undefined };
|
||||
}
|
||||
if (isDataDescriptor(ownDesc)) {
|
||||
if (!ownDesc.writable) {
|
||||
return false;
|
||||
}
|
||||
if (!isObject(receiver)) {
|
||||
return false;
|
||||
}
|
||||
const existingDesc = Reflect.getOwnPropertyDescriptor(receiver, property);
|
||||
if (existingDesc !== undefined) {
|
||||
if (isAccessorDescriptor(existingDesc)) {
|
||||
return false;
|
||||
}
|
||||
if (existingDesc.writable === false) {
|
||||
return false;
|
||||
}
|
||||
const valueDesc = { value };
|
||||
return Reflect.defineProperty(receiver, property, valueDesc);
|
||||
}
|
||||
|
||||
return Reflect.defineProperty(
|
||||
receiver,
|
||||
property,
|
||||
{ value, writable: true, enumerable: true, configurable: true }
|
||||
);
|
||||
}
|
||||
|
||||
const setter = ownDesc.set;
|
||||
if (setter === undefined) {
|
||||
return false;
|
||||
}
|
||||
call(setter, receiver, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function isDataDescriptor(desc) {
|
||||
return Object.hasOwn(desc, "value") || Object.hasOwn(desc, "writable");
|
||||
}
|
||||
|
||||
function isAccessorDescriptor(desc) {
|
||||
return Object.hasOwn(desc, "get") || Object.hasOwn(desc, "set");
|
||||
}
|
||||
|
||||
function getMethod(value, property, errPrefix = "The provided value") {
|
||||
const func = value[property];
|
||||
if (func === undefined || func === null) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof func !== "function") {
|
||||
throw new TypeError(`${errPrefix}'s ${property} property is not a function.`);
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
function createAsyncFromSyncIterator(syncIterator) {
|
||||
// Instead of re-implementing CreateAsyncFromSyncIterator and %AsyncFromSyncIteratorPrototype%,
|
||||
// we use yield* inside an async generator function to achieve the same result.
|
||||
|
||||
// Wrap the sync iterator inside a sync iterable, so we can use it with yield*.
|
||||
const syncIterable = {
|
||||
[Symbol.iterator]: () => syncIterator
|
||||
};
|
||||
// Create an async generator function and immediately invoke it.
|
||||
const asyncIterator = (async function* () {
|
||||
return yield* syncIterable;
|
||||
})();
|
||||
// Return as an async iterator record.
|
||||
return asyncIterator;
|
||||
}
|
||||
|
||||
function convertAsyncSequence(object, itemConverter, errPrefix = "The provided value") {
|
||||
if (!isObject(object)) {
|
||||
throw new TypeError(`${errPrefix} is not an object.`);
|
||||
}
|
||||
let method = getMethod(object, Symbol.asyncIterator, errPrefix);
|
||||
let type = "async";
|
||||
if (method === undefined) {
|
||||
method = getMethod(object, Symbol.iterator, errPrefix);
|
||||
if (method === undefined) {
|
||||
throw new TypeError(`${errPrefix} is not an async iterable object.`);
|
||||
}
|
||||
type = "sync";
|
||||
}
|
||||
|
||||
return {
|
||||
object,
|
||||
method,
|
||||
type,
|
||||
// The wrapperSymbol ensures that if the async sequence is used as a return value,
|
||||
// that it exposes the original JavaScript value.
|
||||
// https://webidl.spec.whatwg.org/#js-async-iterable
|
||||
[wrapperSymbol]: object,
|
||||
// Implement the async iterator protocol, so users can iterate
|
||||
// the async sequence directly (e.g. with for await...of)
|
||||
// instead of needing to call a separate helper function to open the async sequence.
|
||||
// https://webidl.spec.whatwg.org/#async-sequence-open
|
||||
[Symbol.asyncIterator]() {
|
||||
return openAsyncSequence(object, method, type, itemConverter, `${errPrefix}'s iterator`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function openAsyncSequence(object, method, type, itemConverter, errPrefix = "The provided value") {
|
||||
let iterator = call(method, object);
|
||||
if (!isObject(iterator)) {
|
||||
throw new TypeError(`${errPrefix}'s method must return an object`);
|
||||
}
|
||||
if (type === "sync") {
|
||||
iterator = createAsyncFromSyncIterator(iterator);
|
||||
}
|
||||
const nextMethod = iterator.next;
|
||||
return {
|
||||
async next() {
|
||||
const nextResult = await call(nextMethod, iterator);
|
||||
if (!isObject(nextResult)) {
|
||||
throw new TypeError(`${errPrefix}'s next method must return an object`);
|
||||
}
|
||||
const { done, value } = nextResult;
|
||||
if (done) {
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
return { done: false, value: itemConverter(value) };
|
||||
},
|
||||
async return(reason) {
|
||||
const returnMethod = getMethod(iterator, "return", errPrefix);
|
||||
if (returnMethod === undefined) {
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
const returnResult = await call(returnMethod, iterator, reason);
|
||||
if (!isObject(returnResult)) {
|
||||
throw new TypeError(`${errPrefix}'s return method must return an object`);
|
||||
}
|
||||
return { done: true, value: undefined };
|
||||
},
|
||||
[Symbol.asyncIterator]() {
|
||||
return this;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const supportsPropertyIndex = Symbol("supports property index");
|
||||
const supportedPropertyIndices = Symbol("supported property indices");
|
||||
const supportsPropertyName = Symbol("supports property name");
|
||||
const supportedPropertyNames = Symbol("supported property names");
|
||||
const indexedGet = Symbol("indexed property get");
|
||||
const indexedSetNew = Symbol("indexed property set new");
|
||||
const indexedSetExisting = Symbol("indexed property set existing");
|
||||
const namedGet = Symbol("named property get");
|
||||
const namedSetNew = Symbol("named property set new");
|
||||
const namedSetExisting = Symbol("named property set existing");
|
||||
const namedDelete = Symbol("named property delete");
|
||||
|
||||
const asyncIteratorNext = Symbol("async iterator get the next iteration result");
|
||||
const asyncIteratorReturn = Symbol("async iterator return steps");
|
||||
const asyncIteratorInit = Symbol("async iterator initialization steps");
|
||||
const asyncIteratorEOI = Symbol("async iterator end of iteration");
|
||||
|
||||
module.exports = exports = {
|
||||
isObject,
|
||||
define,
|
||||
newObjectInRealm,
|
||||
wrapperSymbol,
|
||||
createInterfaceDescriptor,
|
||||
registerWrapper,
|
||||
getSameObject,
|
||||
ctorRegistrySymbol,
|
||||
initCtorRegistry,
|
||||
wrapperForImpl,
|
||||
implForWrapper,
|
||||
implForWrapperWithInterface,
|
||||
tryWrapperForImpl,
|
||||
tryImplForWrapper,
|
||||
iterInternalSymbol,
|
||||
isArrayBuffer,
|
||||
isSharedArrayBuffer,
|
||||
isArrayIndexPropName,
|
||||
getMethod,
|
||||
convertAsyncSequence,
|
||||
supportsPropertyIndex,
|
||||
supportedPropertyIndices,
|
||||
supportsPropertyName,
|
||||
supportedPropertyNames,
|
||||
indexedGet,
|
||||
indexedSetNew,
|
||||
indexedSetExisting,
|
||||
namedGet,
|
||||
namedSetNew,
|
||||
namedSetExisting,
|
||||
namedDelete,
|
||||
asyncIteratorNext,
|
||||
asyncIteratorReturn,
|
||||
asyncIteratorInit,
|
||||
asyncIteratorEOI,
|
||||
iteratorResult,
|
||||
ordinarySetWithOwnDescriptor
|
||||
};
|
||||
Reference in new issue
Block a user