|
|
@ -85,34 +85,39 @@ class TupleOrigin { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Reused by URL constructor and URL#href setter.
|
|
|
|
function parse(url, input, base) { |
|
|
|
input = String(input); |
|
|
|
const base_context = base ? base[context] : undefined; |
|
|
|
url[context] = new StorageObject(); |
|
|
|
binding.parse(input.trim(), -1, base_context, undefined, |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
throw new TypeError('Invalid URL'); |
|
|
|
url[context].flags = flags; |
|
|
|
url[context].scheme = protocol; |
|
|
|
url[context].username = username; |
|
|
|
url[context].password = password; |
|
|
|
url[context].port = port; |
|
|
|
url[context].path = path; |
|
|
|
url[context].query = query; |
|
|
|
url[context].fragment = fragment; |
|
|
|
url[context].host = host; |
|
|
|
if (url[searchParams]) { // invoked from href setter
|
|
|
|
initSearchParams(url[searchParams], query); |
|
|
|
} else { |
|
|
|
url[searchParams] = new URLSearchParams(query); |
|
|
|
} |
|
|
|
url[searchParams][context] = url; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
class URL { |
|
|
|
constructor(input, base) { |
|
|
|
if (base !== undefined && !(base instanceof URL)) |
|
|
|
base = new URL(String(base)); |
|
|
|
input = String(input); |
|
|
|
const base_context = base ? base[context] : undefined; |
|
|
|
this[context] = new StorageObject(); |
|
|
|
binding.parse(input.trim(), -1, base_context, undefined, |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
throw new TypeError('Invalid URL'); |
|
|
|
this[context].flags = flags; |
|
|
|
this[context].scheme = protocol; |
|
|
|
this[context].username = username; |
|
|
|
this[context].password = password; |
|
|
|
this[context].port = port; |
|
|
|
this[context].path = path; |
|
|
|
this[context].query = query; |
|
|
|
this[context].fragment = fragment; |
|
|
|
this[context].host = host; |
|
|
|
this[searchParams] = new URLSearchParams(query); |
|
|
|
this[searchParams][context] = this; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
get origin() { |
|
|
|
return URL.originFor(this).toString(true); |
|
|
|
parse(this, input, base); |
|
|
|
} |
|
|
|
|
|
|
|
get [special]() { |
|
|
@ -123,309 +128,6 @@ class URL { |
|
|
|
return (this[context].flags & binding.URL_FLAGS_CANNOT_BE_BASE) != 0; |
|
|
|
} |
|
|
|
|
|
|
|
get protocol() { |
|
|
|
return this[context].scheme; |
|
|
|
} |
|
|
|
|
|
|
|
get searchParams() { |
|
|
|
return this[searchParams]; |
|
|
|
} |
|
|
|
|
|
|
|
set protocol(scheme) { |
|
|
|
scheme = String(scheme); |
|
|
|
if (scheme.length === 0) |
|
|
|
return; |
|
|
|
binding.parse(scheme, |
|
|
|
binding.kSchemeStart, |
|
|
|
null, |
|
|
|
this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
const newIsSpecial = (flags & binding.URL_FLAGS_SPECIAL) != 0; |
|
|
|
if ((this[special] && !newIsSpecial) || |
|
|
|
(!this[special] && newIsSpecial) || |
|
|
|
(newIsSpecial && !this[special] && |
|
|
|
this[context].host === undefined)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
if (newIsSpecial) { |
|
|
|
this[context].flags |= binding.URL_FLAGS_SPECIAL; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_SPECIAL; |
|
|
|
} |
|
|
|
if (protocol) { |
|
|
|
this[context].scheme = protocol; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_SCHEME; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_SCHEME; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
get username() { |
|
|
|
return this[context].username || ''; |
|
|
|
} |
|
|
|
|
|
|
|
set username(username) { |
|
|
|
username = String(username); |
|
|
|
if (!this.hostname) |
|
|
|
return; |
|
|
|
if (!username) { |
|
|
|
this[context].username = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_USERNAME; |
|
|
|
return; |
|
|
|
} |
|
|
|
this[context].username = binding.encodeAuth(username); |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_USERNAME; |
|
|
|
} |
|
|
|
|
|
|
|
get password() { |
|
|
|
return this[context].password || ''; |
|
|
|
} |
|
|
|
|
|
|
|
set password(password) { |
|
|
|
password = String(password); |
|
|
|
if (!this.hostname) |
|
|
|
return; |
|
|
|
if (!password) { |
|
|
|
this[context].password = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_PASSWORD; |
|
|
|
return; |
|
|
|
} |
|
|
|
this[context].password = binding.encodeAuth(password); |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_PASSWORD; |
|
|
|
} |
|
|
|
|
|
|
|
get host() { |
|
|
|
var ret = this[context].host || ''; |
|
|
|
if (this[context].port !== undefined) |
|
|
|
ret += `:${this[context].port}`; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
set host(host) { |
|
|
|
host = String(host); |
|
|
|
if (this[cannotBeBase] || |
|
|
|
(this[special] && host.length === 0)) { |
|
|
|
// Cannot set the host if cannot-be-base is set or
|
|
|
|
// scheme is special and host length is zero
|
|
|
|
return; |
|
|
|
} |
|
|
|
if (!host) { |
|
|
|
this[context].host = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|
|
|
return; |
|
|
|
} |
|
|
|
binding.parse(host, binding.kHost, null, this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
if (host) { |
|
|
|
this[context].host = host; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_HOST; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|
|
|
} |
|
|
|
if (port !== undefined) |
|
|
|
this[context].port = port; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
get hostname() { |
|
|
|
return this[context].host || ''; |
|
|
|
} |
|
|
|
|
|
|
|
set hostname(host) { |
|
|
|
host = String(host); |
|
|
|
if (this[cannotBeBase] || |
|
|
|
(this[special] && host.length === 0)) { |
|
|
|
// Cannot set the host if cannot-be-base is set or
|
|
|
|
// scheme is special and host length is zero
|
|
|
|
return; |
|
|
|
} |
|
|
|
if (!host) { |
|
|
|
this[context].host = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|
|
|
return; |
|
|
|
} |
|
|
|
binding.parse(host, |
|
|
|
binding.kHostname, |
|
|
|
null, |
|
|
|
this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
if (host) { |
|
|
|
this[context].host = host; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_HOST; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
get port() { |
|
|
|
const port = this[context].port; |
|
|
|
return port === undefined ? '' : String(port); |
|
|
|
} |
|
|
|
|
|
|
|
set port(port) { |
|
|
|
if (!this[context].host || this[cannotBeBase] || this.protocol === 'file:') |
|
|
|
return; |
|
|
|
port = String(port); |
|
|
|
if (port === '') { |
|
|
|
// Currently, if port number is empty, left unchanged.
|
|
|
|
// TODO(jasnell): This might be changing in the spec
|
|
|
|
return; |
|
|
|
} |
|
|
|
binding.parse(port, binding.kPort, null, this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
this[context].port = port; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
get pathname() { |
|
|
|
if (this[cannotBeBase]) |
|
|
|
return this[context].path[0]; |
|
|
|
return this[context].path !== undefined ? |
|
|
|
`/${this[context].path.join('/')}` : ''; |
|
|
|
} |
|
|
|
|
|
|
|
set pathname(path) { |
|
|
|
if (this[cannotBeBase]) |
|
|
|
return; |
|
|
|
path = String(path); |
|
|
|
binding.parse(path, |
|
|
|
binding.kPathStart, |
|
|
|
null, |
|
|
|
this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
if (path) { |
|
|
|
this[context].path = path; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_PATH; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_PATH; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
get search() { |
|
|
|
return !this[context].query ? '' : `?${this[context].query}`; |
|
|
|
} |
|
|
|
|
|
|
|
set search(search) { |
|
|
|
search = String(search); |
|
|
|
if (search[0] === '?') search = search.slice(1); |
|
|
|
if (!search) { |
|
|
|
this[context].query = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_QUERY; |
|
|
|
this[searchParams][searchParams] = {}; |
|
|
|
return; |
|
|
|
} |
|
|
|
this[context].query = ''; |
|
|
|
binding.parse(search, |
|
|
|
binding.kQuery, |
|
|
|
null, |
|
|
|
this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
if (query) { |
|
|
|
this[context].query = query; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_QUERY; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_QUERY; |
|
|
|
} |
|
|
|
}); |
|
|
|
this[searchParams][searchParams] = querystring.parse(search); |
|
|
|
} |
|
|
|
|
|
|
|
get hash() { |
|
|
|
return !this[context].fragment ? '' : `#${this[context].fragment}`; |
|
|
|
} |
|
|
|
|
|
|
|
set hash(hash) { |
|
|
|
hash = String(hash); |
|
|
|
if (this.protocol === 'javascript:') |
|
|
|
return; |
|
|
|
if (!hash) { |
|
|
|
this[context].fragment = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_FRAGMENT; |
|
|
|
return; |
|
|
|
} |
|
|
|
if (hash[0] === '#') hash = hash.slice(1); |
|
|
|
this[context].fragment = ''; |
|
|
|
binding.parse(hash, |
|
|
|
binding.kFragment, |
|
|
|
null, |
|
|
|
this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
if (fragment) { |
|
|
|
this[context].fragment = fragment; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_FRAGMENT; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_FRAGMENT; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
get href() { |
|
|
|
return this.toString(); |
|
|
|
} |
|
|
|
|
|
|
|
toString(options) { |
|
|
|
options = options || {}; |
|
|
|
const fragment = |
|
|
|
options.fragment !== undefined ? |
|
|
|
!!options.fragment : true; |
|
|
|
const unicode = !!options.unicode; |
|
|
|
var ret; |
|
|
|
if (this.protocol) |
|
|
|
ret = this.protocol; |
|
|
|
if (this[context].host !== undefined) { |
|
|
|
ret += '//'; |
|
|
|
const has_username = typeof this[context].username === 'string'; |
|
|
|
const has_password = typeof this[context].password === 'string'; |
|
|
|
if (has_username || has_password) { |
|
|
|
if (has_username) |
|
|
|
ret += this[context].username; |
|
|
|
if (has_password) |
|
|
|
ret += `:${this[context].password}`; |
|
|
|
ret += '@'; |
|
|
|
} |
|
|
|
if (unicode) { |
|
|
|
ret += punycode.toUnicode(this.hostname); |
|
|
|
if (this.port !== undefined) |
|
|
|
ret += `:${this.port}`; |
|
|
|
} else { |
|
|
|
ret += this.host; |
|
|
|
} |
|
|
|
} else if (this[context].scheme === 'file:') { |
|
|
|
ret += '//'; |
|
|
|
} |
|
|
|
if (this.pathname) |
|
|
|
ret += this.pathname; |
|
|
|
if (typeof this[context].query === 'string') |
|
|
|
ret += `?${this[context].query}`; |
|
|
|
if (fragment & typeof this[context].fragment === 'string') |
|
|
|
ret += `#${this[context].fragment}`; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
inspect(depth, opts) { |
|
|
|
var ret = 'URL {\n'; |
|
|
|
ret += ` href: ${this.href}\n`; |
|
|
@ -456,6 +158,353 @@ class URL { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Object.defineProperties(URL.prototype, { |
|
|
|
toString: { |
|
|
|
// https://heycam.github.io/webidl/#es-stringifier
|
|
|
|
writable: true, |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
// eslint-disable-next-line func-name-matching
|
|
|
|
value: function toString(options) { |
|
|
|
options = options || {}; |
|
|
|
const fragment = |
|
|
|
options.fragment !== undefined ? |
|
|
|
!!options.fragment : true; |
|
|
|
const unicode = !!options.unicode; |
|
|
|
var ret; |
|
|
|
if (this.protocol) |
|
|
|
ret = this.protocol; |
|
|
|
if (this[context].host !== undefined) { |
|
|
|
ret += '//'; |
|
|
|
const has_username = typeof this[context].username === 'string'; |
|
|
|
const has_password = typeof this[context].password === 'string'; |
|
|
|
if (has_username || has_password) { |
|
|
|
if (has_username) |
|
|
|
ret += this[context].username; |
|
|
|
if (has_password) |
|
|
|
ret += `:${this[context].password}`; |
|
|
|
ret += '@'; |
|
|
|
} |
|
|
|
if (unicode) { |
|
|
|
ret += punycode.toUnicode(this.hostname); |
|
|
|
if (this.port !== undefined) |
|
|
|
ret += `:${this.port}`; |
|
|
|
} else { |
|
|
|
ret += this.host; |
|
|
|
} |
|
|
|
} else if (this[context].scheme === 'file:') { |
|
|
|
ret += '//'; |
|
|
|
} |
|
|
|
if (this.pathname) |
|
|
|
ret += this.pathname; |
|
|
|
if (typeof this[context].query === 'string') |
|
|
|
ret += `?${this[context].query}`; |
|
|
|
if (fragment & typeof this[context].fragment === 'string') |
|
|
|
ret += `#${this[context].fragment}`; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
}, |
|
|
|
href: { |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
return this.toString(); |
|
|
|
}, |
|
|
|
set(input) { |
|
|
|
parse(this, input); |
|
|
|
} |
|
|
|
}, |
|
|
|
origin: { // readonly
|
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
return originFor(this).toString(true); |
|
|
|
} |
|
|
|
}, |
|
|
|
protocol: { |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
return this[context].scheme; |
|
|
|
}, |
|
|
|
set(scheme) { |
|
|
|
scheme = String(scheme); |
|
|
|
if (scheme.length === 0) |
|
|
|
return; |
|
|
|
binding.parse(scheme, |
|
|
|
binding.kSchemeStart, |
|
|
|
null, |
|
|
|
this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
const newIsSpecial = (flags & binding.URL_FLAGS_SPECIAL) != 0; |
|
|
|
if ((this[special] && !newIsSpecial) || |
|
|
|
(!this[special] && newIsSpecial) || |
|
|
|
(newIsSpecial && !this[special] && |
|
|
|
this[context].host === undefined)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
if (newIsSpecial) { |
|
|
|
this[context].flags |= binding.URL_FLAGS_SPECIAL; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_SPECIAL; |
|
|
|
} |
|
|
|
if (protocol) { |
|
|
|
this[context].scheme = protocol; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_SCHEME; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_SCHEME; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
username: { |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
return this[context].username || ''; |
|
|
|
}, |
|
|
|
set(username) { |
|
|
|
username = String(username); |
|
|
|
if (!this.hostname) |
|
|
|
return; |
|
|
|
if (!username) { |
|
|
|
this[context].username = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_USERNAME; |
|
|
|
return; |
|
|
|
} |
|
|
|
this[context].username = binding.encodeAuth(username); |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_USERNAME; |
|
|
|
} |
|
|
|
}, |
|
|
|
password: { |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
return this[context].password || ''; |
|
|
|
}, |
|
|
|
set(password) { |
|
|
|
password = String(password); |
|
|
|
if (!this.hostname) |
|
|
|
return; |
|
|
|
if (!password) { |
|
|
|
this[context].password = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_PASSWORD; |
|
|
|
return; |
|
|
|
} |
|
|
|
this[context].password = binding.encodeAuth(password); |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_PASSWORD; |
|
|
|
} |
|
|
|
}, |
|
|
|
host: { |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
var ret = this[context].host || ''; |
|
|
|
if (this[context].port !== undefined) |
|
|
|
ret += `:${this[context].port}`; |
|
|
|
return ret; |
|
|
|
}, |
|
|
|
set(host) { |
|
|
|
host = String(host); |
|
|
|
if (this[cannotBeBase] || |
|
|
|
(this[special] && host.length === 0)) { |
|
|
|
// Cannot set the host if cannot-be-base is set or
|
|
|
|
// scheme is special and host length is zero
|
|
|
|
return; |
|
|
|
} |
|
|
|
if (!host) { |
|
|
|
this[context].host = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|
|
|
return; |
|
|
|
} |
|
|
|
binding.parse(host, binding.kHost, null, this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
if (host) { |
|
|
|
this[context].host = host; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_HOST; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|
|
|
} |
|
|
|
if (port !== undefined) |
|
|
|
this[context].port = port; |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
hostname: { |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
return this[context].host || ''; |
|
|
|
}, |
|
|
|
set(host) { |
|
|
|
host = String(host); |
|
|
|
if (this[cannotBeBase] || |
|
|
|
(this[special] && host.length === 0)) { |
|
|
|
// Cannot set the host if cannot-be-base is set or
|
|
|
|
// scheme is special and host length is zero
|
|
|
|
return; |
|
|
|
} |
|
|
|
if (!host) { |
|
|
|
this[context].host = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|
|
|
return; |
|
|
|
} |
|
|
|
binding.parse(host, |
|
|
|
binding.kHostname, |
|
|
|
null, |
|
|
|
this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
if (host) { |
|
|
|
this[context].host = host; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_HOST; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_HOST; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
port: { |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
const port = this[context].port; |
|
|
|
return port === undefined ? '' : String(port); |
|
|
|
}, |
|
|
|
set(port) { |
|
|
|
if (!this[context].host || this[cannotBeBase] || |
|
|
|
this.protocol === 'file:') |
|
|
|
return; |
|
|
|
port = String(port); |
|
|
|
if (port === '') { |
|
|
|
// Currently, if port number is empty, left unchanged.
|
|
|
|
// TODO(jasnell): This might be changing in the spec
|
|
|
|
return; |
|
|
|
} |
|
|
|
binding.parse(port, binding.kPort, null, this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
this[context].port = port; |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
pathname: { |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
if (this[cannotBeBase]) |
|
|
|
return this[context].path[0]; |
|
|
|
return this[context].path !== undefined ? |
|
|
|
`/${this[context].path.join('/')}` : ''; |
|
|
|
}, |
|
|
|
set(path) { |
|
|
|
if (this[cannotBeBase]) |
|
|
|
return; |
|
|
|
path = String(path); |
|
|
|
binding.parse(path, |
|
|
|
binding.kPathStart, |
|
|
|
null, |
|
|
|
this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
if (path) { |
|
|
|
this[context].path = path; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_PATH; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_PATH; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
search: { |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
return !this[context].query ? '' : `?${this[context].query}`; |
|
|
|
}, |
|
|
|
set(search) { |
|
|
|
search = String(search); |
|
|
|
if (search[0] === '?') search = search.slice(1); |
|
|
|
if (!search) { |
|
|
|
this[context].query = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_QUERY; |
|
|
|
this[searchParams][searchParams] = {}; |
|
|
|
return; |
|
|
|
} |
|
|
|
this[context].query = ''; |
|
|
|
binding.parse(search, |
|
|
|
binding.kQuery, |
|
|
|
null, |
|
|
|
this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
if (query) { |
|
|
|
this[context].query = query; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_QUERY; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_QUERY; |
|
|
|
} |
|
|
|
}); |
|
|
|
this[searchParams][searchParams] = querystring.parse(search); |
|
|
|
} |
|
|
|
}, |
|
|
|
searchParams: { // readonly
|
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
return this[searchParams]; |
|
|
|
} |
|
|
|
}, |
|
|
|
hash: { |
|
|
|
enumerable: true, |
|
|
|
configurable: true, |
|
|
|
get() { |
|
|
|
return !this[context].fragment ? '' : `#${this[context].fragment}`; |
|
|
|
}, |
|
|
|
set(hash) { |
|
|
|
hash = String(hash); |
|
|
|
if (this.protocol === 'javascript:') |
|
|
|
return; |
|
|
|
if (!hash) { |
|
|
|
this[context].fragment = null; |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_FRAGMENT; |
|
|
|
return; |
|
|
|
} |
|
|
|
if (hash[0] === '#') hash = hash.slice(1); |
|
|
|
this[context].fragment = ''; |
|
|
|
binding.parse(hash, |
|
|
|
binding.kFragment, |
|
|
|
null, |
|
|
|
this[context], |
|
|
|
(flags, protocol, username, password, |
|
|
|
host, port, path, query, fragment) => { |
|
|
|
if (flags & binding.URL_FLAGS_FAILED) |
|
|
|
return; |
|
|
|
if (fragment) { |
|
|
|
this[context].fragment = fragment; |
|
|
|
this[context].flags |= binding.URL_FLAGS_HAS_FRAGMENT; |
|
|
|
} else { |
|
|
|
this[context].flags &= ~binding.URL_FLAGS_HAS_FRAGMENT; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
var hexTable = new Array(256); |
|
|
|
for (var i = 0; i < 256; ++i) |
|
|
|
hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase(); |
|
|
@ -546,6 +595,12 @@ function getSearchParamPairs(target) { |
|
|
|
return values; |
|
|
|
} |
|
|
|
|
|
|
|
// Reused by the URL parse function invoked by
|
|
|
|
// the href setter, and the URLSearchParams constructor
|
|
|
|
function initSearchParams(url, init) { |
|
|
|
url[searchParams] = querystring.parse(init); |
|
|
|
} |
|
|
|
|
|
|
|
class URLSearchParams { |
|
|
|
constructor(init = '') { |
|
|
|
if (init instanceof URLSearchParams) { |
|
|
@ -554,7 +609,7 @@ class URLSearchParams { |
|
|
|
} else { |
|
|
|
init = String(init); |
|
|
|
if (init[0] === '?') init = init.slice(1); |
|
|
|
this[searchParams] = querystring.parse(init); |
|
|
|
initSearchParams(this, init); |
|
|
|
} |
|
|
|
|
|
|
|
// "associated url object"
|
|
|
@ -790,7 +845,7 @@ Object.defineProperty(URLSearchParamsIteratorPrototype, Symbol.toStringTag, { |
|
|
|
configurable: true |
|
|
|
}); |
|
|
|
|
|
|
|
URL.originFor = function(url, base) { |
|
|
|
function originFor(url, base) { |
|
|
|
if (!(url instanceof URL)) |
|
|
|
url = new URL(url, base); |
|
|
|
var origin; |
|
|
@ -822,8 +877,9 @@ URL.originFor = function(url, base) { |
|
|
|
origin = new OpaqueOrigin(); |
|
|
|
} |
|
|
|
return origin; |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
URL.originFor = originFor; |
|
|
|
URL.domainToASCII = function(domain) { |
|
|
|
return binding.domainToASCII(String(domain)); |
|
|
|
}; |
|
|
|