mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
1e26508579
Closes #23069
907 lines
24 KiB
JavaScript
907 lines
24 KiB
JavaScript
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
|
|
// @ts-check
|
|
/// <reference path="../../core/internal.d.ts" />
|
|
/// <reference path="../../core/lib.deno_core.d.ts" />
|
|
/// <reference path="../webidl/internal.d.ts" />
|
|
|
|
import { primordials } from "ext:core/mod.js";
|
|
import {
|
|
op_url_get_serialization,
|
|
op_url_parse,
|
|
op_url_parse_search_params,
|
|
op_url_parse_with_base,
|
|
op_url_reparse,
|
|
op_url_stringify_search_params,
|
|
} from "ext:core/ops";
|
|
const {
|
|
ArrayIsArray,
|
|
ArrayPrototypeMap,
|
|
ArrayPrototypePush,
|
|
ArrayPrototypeSome,
|
|
ArrayPrototypeSort,
|
|
ArrayPrototypeSplice,
|
|
ObjectKeys,
|
|
ObjectPrototypeIsPrototypeOf,
|
|
SafeArrayIterator,
|
|
StringPrototypeSlice,
|
|
StringPrototypeStartsWith,
|
|
Symbol,
|
|
SymbolFor,
|
|
SymbolIterator,
|
|
TypeError,
|
|
Uint32Array,
|
|
} = primordials;
|
|
|
|
import * as webidl from "ext:deno_webidl/00_webidl.js";
|
|
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
|
|
|
|
const _list = Symbol("list");
|
|
const _urlObject = Symbol("url object");
|
|
|
|
// WARNING: must match rust code's UrlSetter::*
|
|
const SET_HASH = 0;
|
|
const SET_HOST = 1;
|
|
const SET_HOSTNAME = 2;
|
|
const SET_PASSWORD = 3;
|
|
const SET_PATHNAME = 4;
|
|
const SET_PORT = 5;
|
|
const SET_PROTOCOL = 6;
|
|
const SET_SEARCH = 7;
|
|
const SET_USERNAME = 8;
|
|
|
|
// Helper functions
|
|
/**
|
|
* @param {string} href
|
|
* @param {number} setter
|
|
* @param {string} value
|
|
* @returns {string}
|
|
*/
|
|
function opUrlReparse(href, setter, value) {
|
|
const status = op_url_reparse(
|
|
href,
|
|
setter,
|
|
value,
|
|
componentsBuf,
|
|
);
|
|
return getSerialization(status, href);
|
|
}
|
|
|
|
/**
|
|
* @param {string} href
|
|
* @param {string} [maybeBase]
|
|
* @returns {number}
|
|
*/
|
|
function opUrlParse(href, maybeBase) {
|
|
if (maybeBase === undefined) {
|
|
return op_url_parse(href, componentsBuf);
|
|
}
|
|
return op_url_parse_with_base(
|
|
href,
|
|
maybeBase,
|
|
componentsBuf,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {number} status
|
|
* @param {string} href
|
|
* @param {string} [maybeBase]
|
|
* @returns {string}
|
|
*/
|
|
function getSerialization(status, href, maybeBase) {
|
|
if (status === 0) {
|
|
return href;
|
|
} else if (status === 1) {
|
|
return op_url_get_serialization();
|
|
} else {
|
|
throw new TypeError(
|
|
`Invalid URL: '${href}'` +
|
|
(maybeBase ? ` with base '${maybeBase}'` : ""),
|
|
);
|
|
}
|
|
}
|
|
|
|
class URLSearchParams {
|
|
[_list];
|
|
[_urlObject] = null;
|
|
|
|
/**
|
|
* @param {string | [string][] | Record<string, string>} init
|
|
*/
|
|
constructor(init = "") {
|
|
const prefix = "Failed to construct 'URL'";
|
|
init = webidl.converters
|
|
["sequence<sequence<USVString>> or record<USVString, USVString> or USVString"](
|
|
init,
|
|
prefix,
|
|
"Argument 1",
|
|
);
|
|
this[webidl.brand] = webidl.brand;
|
|
if (!init) {
|
|
// if there is no query string, return early
|
|
this[_list] = [];
|
|
return;
|
|
}
|
|
|
|
if (typeof init === "string") {
|
|
// Overload: USVString
|
|
// If init is a string and starts with U+003F (?),
|
|
// remove the first code point from init.
|
|
if (init[0] == "?") {
|
|
init = StringPrototypeSlice(init, 1);
|
|
}
|
|
this[_list] = op_url_parse_search_params(init);
|
|
} else if (ArrayIsArray(init)) {
|
|
// Overload: sequence<sequence<USVString>>
|
|
this[_list] = ArrayPrototypeMap(init, (pair, i) => {
|
|
if (pair.length !== 2) {
|
|
throw new TypeError(
|
|
`${prefix}: Item ${
|
|
i + 0
|
|
} in the parameter list does have length 2 exactly.`,
|
|
);
|
|
}
|
|
return [pair[0], pair[1]];
|
|
});
|
|
} else {
|
|
// Overload: record<USVString, USVString>
|
|
this[_list] = ArrayPrototypeMap(
|
|
ObjectKeys(init),
|
|
(key) => [key, init[key]],
|
|
);
|
|
}
|
|
}
|
|
|
|
#updateUrlSearch() {
|
|
const url = this[_urlObject];
|
|
if (url === null) {
|
|
return;
|
|
}
|
|
// deno-lint-ignore prefer-primordials
|
|
url[_updateUrlSearch](this.toString());
|
|
}
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @param {string} value
|
|
*/
|
|
append(name, value) {
|
|
webidl.assertBranded(this, URLSearchParamsPrototype);
|
|
const prefix = "Failed to execute 'append' on 'URLSearchParams'";
|
|
webidl.requiredArguments(arguments.length, 2, prefix);
|
|
name = webidl.converters.USVString(name, prefix, "Argument 1");
|
|
value = webidl.converters.USVString(value, prefix, "Argument 2");
|
|
ArrayPrototypePush(this[_list], [name, value]);
|
|
this.#updateUrlSearch();
|
|
}
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @param {string} [value]
|
|
*/
|
|
delete(name, value = undefined) {
|
|
webidl.assertBranded(this, URLSearchParamsPrototype);
|
|
const prefix = "Failed to execute 'append' on 'URLSearchParams'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
name = webidl.converters.USVString(name, prefix, "Argument 1");
|
|
const list = this[_list];
|
|
let i = 0;
|
|
if (value === undefined) {
|
|
while (i < list.length) {
|
|
if (list[i][0] === name) {
|
|
ArrayPrototypeSplice(list, i, 1);
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
} else {
|
|
value = webidl.converters.USVString(value, prefix, "Argument 2");
|
|
while (i < list.length) {
|
|
if (list[i][0] === name && list[i][1] === value) {
|
|
ArrayPrototypeSplice(list, i, 1);
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
this.#updateUrlSearch();
|
|
}
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @returns {string[]}
|
|
*/
|
|
getAll(name) {
|
|
webidl.assertBranded(this, URLSearchParamsPrototype);
|
|
const prefix = "Failed to execute 'getAll' on 'URLSearchParams'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
name = webidl.converters.USVString(name, prefix, "Argument 1");
|
|
const values = [];
|
|
const entries = this[_list];
|
|
for (let i = 0; i < entries.length; ++i) {
|
|
const entry = entries[i];
|
|
if (entry[0] === name) {
|
|
ArrayPrototypePush(values, entry[1]);
|
|
}
|
|
}
|
|
return values;
|
|
}
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @return {string | null}
|
|
*/
|
|
get(name) {
|
|
webidl.assertBranded(this, URLSearchParamsPrototype);
|
|
const prefix = "Failed to execute 'get' on 'URLSearchParams'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
name = webidl.converters.USVString(name, prefix, "Argument 1");
|
|
const entries = this[_list];
|
|
for (let i = 0; i < entries.length; ++i) {
|
|
const entry = entries[i];
|
|
if (entry[0] === name) {
|
|
return entry[1];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @param {string} [value]
|
|
* @return {boolean}
|
|
*/
|
|
has(name, value = undefined) {
|
|
webidl.assertBranded(this, URLSearchParamsPrototype);
|
|
const prefix = "Failed to execute 'has' on 'URLSearchParams'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
name = webidl.converters.USVString(name, prefix, "Argument 1");
|
|
if (value !== undefined) {
|
|
value = webidl.converters.USVString(value, prefix, "Argument 2");
|
|
return ArrayPrototypeSome(
|
|
this[_list],
|
|
(entry) => entry[0] === name && entry[1] === value,
|
|
);
|
|
}
|
|
return ArrayPrototypeSome(this[_list], (entry) => entry[0] === name);
|
|
}
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @param {string} value
|
|
*/
|
|
set(name, value) {
|
|
webidl.assertBranded(this, URLSearchParamsPrototype);
|
|
const prefix = "Failed to execute 'set' on 'URLSearchParams'";
|
|
webidl.requiredArguments(arguments.length, 2, prefix);
|
|
name = webidl.converters.USVString(name, prefix, "Argument 1");
|
|
value = webidl.converters.USVString(value, prefix, "Argument 2");
|
|
|
|
const list = this[_list];
|
|
|
|
// If there are any name-value pairs whose name is name, in list,
|
|
// set the value of the first such name-value pair to value
|
|
// and remove the others.
|
|
let found = false;
|
|
let i = 0;
|
|
while (i < list.length) {
|
|
if (list[i][0] === name) {
|
|
if (!found) {
|
|
list[i][1] = value;
|
|
found = true;
|
|
i++;
|
|
} else {
|
|
ArrayPrototypeSplice(list, i, 1);
|
|
}
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// Otherwise, append a new name-value pair whose name is name
|
|
// and value is value, to list.
|
|
if (!found) {
|
|
ArrayPrototypePush(list, [name, value]);
|
|
}
|
|
|
|
this.#updateUrlSearch();
|
|
}
|
|
|
|
sort() {
|
|
webidl.assertBranded(this, URLSearchParamsPrototype);
|
|
ArrayPrototypeSort(
|
|
this[_list],
|
|
(a, b) => (a[0] === b[0] ? 0 : a[0] > b[0] ? 1 : -1),
|
|
);
|
|
this.#updateUrlSearch();
|
|
}
|
|
|
|
/**
|
|
* @return {string}
|
|
*/
|
|
toString() {
|
|
webidl.assertBranded(this, URLSearchParamsPrototype);
|
|
return op_url_stringify_search_params(this[_list]);
|
|
}
|
|
|
|
get size() {
|
|
webidl.assertBranded(this, URLSearchParamsPrototype);
|
|
return this[_list].length;
|
|
}
|
|
}
|
|
|
|
webidl.mixinPairIterable("URLSearchParams", URLSearchParams, _list, 0, 1);
|
|
|
|
webidl.configureInterface(URLSearchParams);
|
|
const URLSearchParamsPrototype = URLSearchParams.prototype;
|
|
|
|
webidl.converters["URLSearchParams"] = webidl.createInterfaceConverter(
|
|
"URLSearchParams",
|
|
URLSearchParamsPrototype,
|
|
);
|
|
|
|
const _updateUrlSearch = Symbol("updateUrlSearch");
|
|
|
|
function trim(s) {
|
|
if (s.length === 1) return "";
|
|
return s;
|
|
}
|
|
|
|
// Represents a "no port" value. A port in URL cannot be greater than 2^16 - 1
|
|
const NO_PORT = 65536;
|
|
|
|
const skipInit = Symbol();
|
|
const componentsBuf = new Uint32Array(8);
|
|
|
|
class URL {
|
|
/** @type {URLSearchParams|null} */
|
|
#queryObject = null;
|
|
/** @type {string} */
|
|
#serialization;
|
|
/** @type {number} */
|
|
#schemeEnd;
|
|
/** @type {number} */
|
|
#usernameEnd;
|
|
/** @type {number} */
|
|
#hostStart;
|
|
/** @type {number} */
|
|
#hostEnd;
|
|
/** @type {number} */
|
|
#port;
|
|
/** @type {number} */
|
|
#pathStart;
|
|
/** @type {number} */
|
|
#queryStart;
|
|
/** @type {number} */
|
|
#fragmentStart;
|
|
|
|
[_updateUrlSearch](value) {
|
|
this.#serialization = opUrlReparse(
|
|
this.#serialization,
|
|
SET_SEARCH,
|
|
value,
|
|
);
|
|
this.#updateComponents();
|
|
}
|
|
|
|
/**
|
|
* @param {string} url
|
|
* @param {string} [base]
|
|
*/
|
|
constructor(url, base = undefined) {
|
|
// skip initialization for URL.parse
|
|
if (url === skipInit) {
|
|
return;
|
|
}
|
|
const prefix = "Failed to construct 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
url = webidl.converters.DOMString(url, prefix, "Argument 1");
|
|
if (base !== undefined) {
|
|
base = webidl.converters.DOMString(base, prefix, "Argument 2");
|
|
}
|
|
const status = opUrlParse(url, base);
|
|
this[webidl.brand] = webidl.brand;
|
|
this.#serialization = getSerialization(status, url, base);
|
|
this.#updateComponents();
|
|
}
|
|
|
|
/**
|
|
* @param {string} url
|
|
* @param {string} [base]
|
|
*/
|
|
static parse(url, base = undefined) {
|
|
const prefix = "Failed to execute 'URL.parse'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
url = webidl.converters.DOMString(url, prefix, "Argument 1");
|
|
if (base !== undefined) {
|
|
base = webidl.converters.DOMString(base, prefix, "Argument 2");
|
|
}
|
|
const status = opUrlParse(url, base);
|
|
if (status !== 0 && status !== 1) {
|
|
return null;
|
|
}
|
|
// If initialized with webidl.createBranded, private properties are not be accessible,
|
|
// so it is passed through the constructor
|
|
const self = new this(skipInit);
|
|
self[webidl.brand] = webidl.brand;
|
|
self.#serialization = getSerialization(status, url, base);
|
|
self.#updateComponents();
|
|
return self;
|
|
}
|
|
|
|
/**
|
|
* @param {string} url
|
|
* @param {string} [base]
|
|
*/
|
|
static canParse(url, base = undefined) {
|
|
const prefix = "Failed to execute 'URL.canParse'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
url = webidl.converters.DOMString(url, prefix, "Argument 1");
|
|
if (base !== undefined) {
|
|
base = webidl.converters.DOMString(base, prefix, "Argument 2");
|
|
}
|
|
const status = opUrlParse(url, base);
|
|
return status === 0 || status === 1;
|
|
}
|
|
|
|
#updateComponents() {
|
|
({
|
|
0: this.#schemeEnd,
|
|
1: this.#usernameEnd,
|
|
2: this.#hostStart,
|
|
3: this.#hostEnd,
|
|
4: this.#port,
|
|
5: this.#pathStart,
|
|
6: this.#queryStart,
|
|
7: this.#fragmentStart,
|
|
} = componentsBuf);
|
|
}
|
|
|
|
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
|
|
return inspect(
|
|
createFilteredInspectProxy({
|
|
object: this,
|
|
evaluate: ObjectPrototypeIsPrototypeOf(URLPrototype, this),
|
|
keys: [
|
|
"href",
|
|
"origin",
|
|
"protocol",
|
|
"username",
|
|
"password",
|
|
"host",
|
|
"hostname",
|
|
"port",
|
|
"pathname",
|
|
"hash",
|
|
"search",
|
|
],
|
|
}),
|
|
inspectOptions,
|
|
);
|
|
}
|
|
|
|
#updateSearchParams() {
|
|
if (this.#queryObject !== null) {
|
|
const params = this.#queryObject[_list];
|
|
const newParams = op_url_parse_search_params(
|
|
StringPrototypeSlice(this.search, 1),
|
|
);
|
|
ArrayPrototypeSplice(
|
|
params,
|
|
0,
|
|
params.length,
|
|
...new SafeArrayIterator(newParams),
|
|
);
|
|
}
|
|
}
|
|
|
|
#hasAuthority() {
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/lib.rs#L824
|
|
return StringPrototypeStartsWith(
|
|
StringPrototypeSlice(this.#serialization, this.#schemeEnd),
|
|
"://",
|
|
);
|
|
}
|
|
|
|
/** @return {string} */
|
|
get hash() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/quirks.rs#L263
|
|
return this.#fragmentStart
|
|
? trim(StringPrototypeSlice(this.#serialization, this.#fragmentStart))
|
|
: "";
|
|
}
|
|
|
|
/** @param {string} value */
|
|
set hash(value) {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
const prefix = "Failed to set 'hash' on 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
value = webidl.converters.DOMString(value, prefix, "Argument 1");
|
|
try {
|
|
this.#serialization = opUrlReparse(
|
|
this.#serialization,
|
|
SET_HASH,
|
|
value,
|
|
);
|
|
this.#updateComponents();
|
|
} catch {
|
|
/* pass */
|
|
}
|
|
}
|
|
|
|
/** @return {string} */
|
|
get host() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/quirks.rs#L101
|
|
return StringPrototypeSlice(
|
|
this.#serialization,
|
|
this.#hostStart,
|
|
this.#pathStart,
|
|
);
|
|
}
|
|
|
|
/** @param {string} value */
|
|
set host(value) {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
const prefix = "Failed to set 'host' on 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
value = webidl.converters.DOMString(value, prefix, "Argument 1");
|
|
try {
|
|
this.#serialization = opUrlReparse(
|
|
this.#serialization,
|
|
SET_HOST,
|
|
value,
|
|
);
|
|
this.#updateComponents();
|
|
} catch {
|
|
/* pass */
|
|
}
|
|
}
|
|
|
|
/** @return {string} */
|
|
get hostname() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/lib.rs#L988
|
|
return StringPrototypeSlice(
|
|
this.#serialization,
|
|
this.#hostStart,
|
|
this.#hostEnd,
|
|
);
|
|
}
|
|
|
|
/** @param {string} value */
|
|
set hostname(value) {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
const prefix = "Failed to set 'hostname' on 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
value = webidl.converters.DOMString(value, prefix, "Argument 1");
|
|
try {
|
|
this.#serialization = opUrlReparse(
|
|
this.#serialization,
|
|
SET_HOSTNAME,
|
|
value,
|
|
);
|
|
this.#updateComponents();
|
|
} catch {
|
|
/* pass */
|
|
}
|
|
}
|
|
|
|
/** @return {string} */
|
|
get href() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
return this.#serialization;
|
|
}
|
|
|
|
/** @param {string} value */
|
|
set href(value) {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
const prefix = "Failed to set 'href' on 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
value = webidl.converters.DOMString(value, prefix, "Argument 1");
|
|
const status = opUrlParse(value);
|
|
this.#serialization = getSerialization(status, value);
|
|
this.#updateComponents();
|
|
this.#updateSearchParams();
|
|
}
|
|
|
|
/** @return {string} */
|
|
get origin() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/origin.rs#L14
|
|
const scheme = StringPrototypeSlice(
|
|
this.#serialization,
|
|
0,
|
|
this.#schemeEnd,
|
|
);
|
|
if (
|
|
scheme === "http" || scheme === "https" || scheme === "ftp" ||
|
|
scheme === "ws" || scheme === "wss"
|
|
) {
|
|
return `${scheme}://${this.host}`;
|
|
}
|
|
|
|
if (scheme === "blob") {
|
|
// TODO(@littledivy): Fast path.
|
|
try {
|
|
return new URL(this.pathname).origin;
|
|
} catch {
|
|
return "null";
|
|
}
|
|
}
|
|
|
|
return "null";
|
|
}
|
|
|
|
/** @return {string} */
|
|
get password() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/lib.rs#L914
|
|
if (
|
|
this.#hasAuthority() &&
|
|
this.#usernameEnd !== this.#serialization.length &&
|
|
this.#serialization[this.#usernameEnd] === ":"
|
|
) {
|
|
return StringPrototypeSlice(
|
|
this.#serialization,
|
|
this.#usernameEnd + 1,
|
|
this.#hostStart - 1,
|
|
);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/** @param {string} value */
|
|
set password(value) {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
const prefix = "Failed to set 'password' on 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
value = webidl.converters.DOMString(value, prefix, "Argument 1");
|
|
try {
|
|
this.#serialization = opUrlReparse(
|
|
this.#serialization,
|
|
SET_PASSWORD,
|
|
value,
|
|
);
|
|
this.#updateComponents();
|
|
} catch {
|
|
/* pass */
|
|
}
|
|
}
|
|
|
|
/** @return {string} */
|
|
get pathname() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/lib.rs#L1203
|
|
if (!this.#queryStart && !this.#fragmentStart) {
|
|
return StringPrototypeSlice(this.#serialization, this.#pathStart);
|
|
}
|
|
|
|
const nextComponentStart = this.#queryStart || this.#fragmentStart;
|
|
return StringPrototypeSlice(
|
|
this.#serialization,
|
|
this.#pathStart,
|
|
nextComponentStart,
|
|
);
|
|
}
|
|
|
|
/** @param {string} value */
|
|
set pathname(value) {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
const prefix = "Failed to set 'pathname' on 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
value = webidl.converters.DOMString(value, prefix, "Argument 1");
|
|
try {
|
|
this.#serialization = opUrlReparse(
|
|
this.#serialization,
|
|
SET_PATHNAME,
|
|
value,
|
|
);
|
|
this.#updateComponents();
|
|
} catch {
|
|
/* pass */
|
|
}
|
|
}
|
|
|
|
/** @return {string} */
|
|
get port() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/quirks.rs#L196
|
|
if (this.#port === NO_PORT) {
|
|
return StringPrototypeSlice(
|
|
this.#serialization,
|
|
this.#hostEnd,
|
|
this.#pathStart,
|
|
);
|
|
} else {
|
|
return StringPrototypeSlice(
|
|
this.#serialization,
|
|
this.#hostEnd + 1, /* : */
|
|
this.#pathStart,
|
|
);
|
|
}
|
|
}
|
|
|
|
/** @param {string} value */
|
|
set port(value) {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
const prefix = "Failed to set 'port' on 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
value = webidl.converters.DOMString(value, prefix, "Argument 1");
|
|
try {
|
|
this.#serialization = opUrlReparse(
|
|
this.#serialization,
|
|
SET_PORT,
|
|
value,
|
|
);
|
|
this.#updateComponents();
|
|
} catch {
|
|
/* pass */
|
|
}
|
|
}
|
|
|
|
/** @return {string} */
|
|
get protocol() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/quirks.rs#L56
|
|
return StringPrototypeSlice(
|
|
this.#serialization,
|
|
0,
|
|
this.#schemeEnd + 1, /* : */
|
|
);
|
|
}
|
|
|
|
/** @param {string} value */
|
|
set protocol(value) {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
const prefix = "Failed to set 'protocol' on 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
value = webidl.converters.DOMString(value, prefix, "Argument 1");
|
|
try {
|
|
this.#serialization = opUrlReparse(
|
|
this.#serialization,
|
|
SET_PROTOCOL,
|
|
value,
|
|
);
|
|
this.#updateComponents();
|
|
} catch {
|
|
/* pass */
|
|
}
|
|
}
|
|
|
|
/** @return {string} */
|
|
get search() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/quirks.rs#L249
|
|
const afterPath = this.#queryStart || this.#fragmentStart ||
|
|
this.#serialization.length;
|
|
const afterQuery = this.#fragmentStart || this.#serialization.length;
|
|
return trim(
|
|
StringPrototypeSlice(this.#serialization, afterPath, afterQuery),
|
|
);
|
|
}
|
|
|
|
/** @param {string} value */
|
|
set search(value) {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
const prefix = "Failed to set 'search' on 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
value = webidl.converters.DOMString(value, prefix, "Argument 1");
|
|
try {
|
|
this.#serialization = opUrlReparse(
|
|
this.#serialization,
|
|
SET_SEARCH,
|
|
value,
|
|
);
|
|
this.#updateComponents();
|
|
this.#updateSearchParams();
|
|
} catch {
|
|
/* pass */
|
|
}
|
|
}
|
|
|
|
/** @return {string} */
|
|
get username() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
// https://github.com/servo/rust-url/blob/1d307ae51a28fecc630ecec03380788bfb03a643/url/src/lib.rs#L881
|
|
const schemeSeparatorLen = 3; /* :// */
|
|
if (
|
|
this.#hasAuthority() &&
|
|
this.#usernameEnd > this.#schemeEnd + schemeSeparatorLen
|
|
) {
|
|
return StringPrototypeSlice(
|
|
this.#serialization,
|
|
this.#schemeEnd + schemeSeparatorLen,
|
|
this.#usernameEnd,
|
|
);
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/** @param {string} value */
|
|
set username(value) {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
const prefix = "Failed to set 'username' on 'URL'";
|
|
webidl.requiredArguments(arguments.length, 1, prefix);
|
|
value = webidl.converters.DOMString(value, prefix, "Argument 1");
|
|
try {
|
|
this.#serialization = opUrlReparse(
|
|
this.#serialization,
|
|
SET_USERNAME,
|
|
value,
|
|
);
|
|
this.#updateComponents();
|
|
} catch {
|
|
/* pass */
|
|
}
|
|
}
|
|
|
|
/** @return {URLSearchParams} */
|
|
get searchParams() {
|
|
if (this.#queryObject == null) {
|
|
this.#queryObject = new URLSearchParams(this.search);
|
|
this.#queryObject[_urlObject] = this;
|
|
}
|
|
return this.#queryObject;
|
|
}
|
|
|
|
/** @return {string} */
|
|
toString() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
return this.#serialization;
|
|
}
|
|
|
|
/** @return {string} */
|
|
toJSON() {
|
|
webidl.assertBranded(this, URLPrototype);
|
|
return this.#serialization;
|
|
}
|
|
}
|
|
|
|
webidl.configureInterface(URL);
|
|
const URLPrototype = URL.prototype;
|
|
|
|
/**
|
|
* This function implements application/x-www-form-urlencoded parsing.
|
|
* https://url.spec.whatwg.org/#concept-urlencoded-parser
|
|
* @param {Uint8Array} bytes
|
|
* @returns {[string, string][]}
|
|
*/
|
|
function parseUrlEncoded(bytes) {
|
|
return op_url_parse_search_params(null, bytes);
|
|
}
|
|
|
|
webidl
|
|
.converters[
|
|
"sequence<sequence<USVString>> or record<USVString, USVString> or USVString"
|
|
] = (V, prefix, context, opts) => {
|
|
// Union for (sequence<sequence<USVString>> or record<USVString, USVString> or USVString)
|
|
if (webidl.type(V) === "Object" && V !== null) {
|
|
if (V[SymbolIterator] !== undefined) {
|
|
return webidl.converters["sequence<sequence<USVString>>"](
|
|
V,
|
|
prefix,
|
|
context,
|
|
opts,
|
|
);
|
|
}
|
|
return webidl.converters["record<USVString, USVString>"](
|
|
V,
|
|
prefix,
|
|
context,
|
|
opts,
|
|
);
|
|
}
|
|
return webidl.converters.USVString(V, prefix, context, opts);
|
|
};
|
|
|
|
export {
|
|
parseUrlEncoded,
|
|
URL,
|
|
URLPrototype,
|
|
URLSearchParams,
|
|
URLSearchParamsPrototype,
|
|
};
|