You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1044 lines
34 KiB
1044 lines
34 KiB
'use strict'; |
|
// TODO: in core-js@4, move /modules/ dependencies to public entries for better optimization by tools like `preset-env` |
|
require('../modules/es.string.iterator'); |
|
var $ = require('../internals/export'); |
|
var DESCRIPTORS = require('../internals/descriptors'); |
|
var USE_NATIVE_URL = require('../internals/native-url'); |
|
var global = require('../internals/global'); |
|
var bind = require('../internals/function-bind-context'); |
|
var uncurryThis = require('../internals/function-uncurry-this'); |
|
var defineProperties = require('../internals/object-define-properties').f; |
|
var redefine = require('../internals/redefine'); |
|
var anInstance = require('../internals/an-instance'); |
|
var hasOwn = require('../internals/has-own-property'); |
|
var assign = require('../internals/object-assign'); |
|
var arrayFrom = require('../internals/array-from'); |
|
var arraySlice = require('../internals/array-slice-simple'); |
|
var codeAt = require('../internals/string-multibyte').codeAt; |
|
var toASCII = require('../internals/string-punycode-to-ascii'); |
|
var $toString = require('../internals/to-string'); |
|
var setToStringTag = require('../internals/set-to-string-tag'); |
|
var validateArgumentsLength = require('../internals/validate-arguments-length'); |
|
var URLSearchParamsModule = require('../modules/web.url-search-params'); |
|
var InternalStateModule = require('../internals/internal-state'); |
|
|
|
var setInternalState = InternalStateModule.set; |
|
var getInternalURLState = InternalStateModule.getterFor('URL'); |
|
var URLSearchParams = URLSearchParamsModule.URLSearchParams; |
|
var getInternalSearchParamsState = URLSearchParamsModule.getState; |
|
|
|
var NativeURL = global.URL; |
|
var TypeError = global.TypeError; |
|
var parseInt = global.parseInt; |
|
var floor = Math.floor; |
|
var pow = Math.pow; |
|
var charAt = uncurryThis(''.charAt); |
|
var exec = uncurryThis(/./.exec); |
|
var join = uncurryThis([].join); |
|
var numberToString = uncurryThis(1.0.toString); |
|
var pop = uncurryThis([].pop); |
|
var push = uncurryThis([].push); |
|
var replace = uncurryThis(''.replace); |
|
var shift = uncurryThis([].shift); |
|
var split = uncurryThis(''.split); |
|
var stringSlice = uncurryThis(''.slice); |
|
var toLowerCase = uncurryThis(''.toLowerCase); |
|
var unshift = uncurryThis([].unshift); |
|
|
|
var INVALID_AUTHORITY = 'Invalid authority'; |
|
var INVALID_SCHEME = 'Invalid scheme'; |
|
var INVALID_HOST = 'Invalid host'; |
|
var INVALID_PORT = 'Invalid port'; |
|
|
|
var ALPHA = /[a-z]/i; |
|
// eslint-disable-next-line regexp/no-obscure-range -- safe |
|
var ALPHANUMERIC = /[\d+-.a-z]/i; |
|
var DIGIT = /\d/; |
|
var HEX_START = /^0x/i; |
|
var OCT = /^[0-7]+$/; |
|
var DEC = /^\d+$/; |
|
var HEX = /^[\da-f]+$/i; |
|
/* eslint-disable regexp/no-control-character -- safe */ |
|
var FORBIDDEN_HOST_CODE_POINT = /[\0\t\n\r #%/:<>?@[\\\]^|]/; |
|
var FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT = /[\0\t\n\r #/:<>?@[\\\]^|]/; |
|
var LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE = /^[\u0000-\u0020]+|[\u0000-\u0020]+$/g; |
|
var TAB_AND_NEW_LINE = /[\t\n\r]/g; |
|
/* eslint-enable regexp/no-control-character -- safe */ |
|
var EOF; |
|
|
|
// https://url.spec.whatwg.org/#ipv4-number-parser |
|
var parseIPv4 = function (input) { |
|
var parts = split(input, '.'); |
|
var partsLength, numbers, index, part, radix, number, ipv4; |
|
if (parts.length && parts[parts.length - 1] == '') { |
|
parts.length--; |
|
} |
|
partsLength = parts.length; |
|
if (partsLength > 4) return input; |
|
numbers = []; |
|
for (index = 0; index < partsLength; index++) { |
|
part = parts[index]; |
|
if (part == '') return input; |
|
radix = 10; |
|
if (part.length > 1 && charAt(part, 0) == '0') { |
|
radix = exec(HEX_START, part) ? 16 : 8; |
|
part = stringSlice(part, radix == 8 ? 1 : 2); |
|
} |
|
if (part === '') { |
|
number = 0; |
|
} else { |
|
if (!exec(radix == 10 ? DEC : radix == 8 ? OCT : HEX, part)) return input; |
|
number = parseInt(part, radix); |
|
} |
|
push(numbers, number); |
|
} |
|
for (index = 0; index < partsLength; index++) { |
|
number = numbers[index]; |
|
if (index == partsLength - 1) { |
|
if (number >= pow(256, 5 - partsLength)) return null; |
|
} else if (number > 255) return null; |
|
} |
|
ipv4 = pop(numbers); |
|
for (index = 0; index < numbers.length; index++) { |
|
ipv4 += numbers[index] * pow(256, 3 - index); |
|
} |
|
return ipv4; |
|
}; |
|
|
|
// https://url.spec.whatwg.org/#concept-ipv6-parser |
|
// eslint-disable-next-line max-statements -- TODO |
|
var parseIPv6 = function (input) { |
|
var address = [0, 0, 0, 0, 0, 0, 0, 0]; |
|
var pieceIndex = 0; |
|
var compress = null; |
|
var pointer = 0; |
|
var value, length, numbersSeen, ipv4Piece, number, swaps, swap; |
|
|
|
var chr = function () { |
|
return charAt(input, pointer); |
|
}; |
|
|
|
if (chr() == ':') { |
|
if (charAt(input, 1) != ':') return; |
|
pointer += 2; |
|
pieceIndex++; |
|
compress = pieceIndex; |
|
} |
|
while (chr()) { |
|
if (pieceIndex == 8) return; |
|
if (chr() == ':') { |
|
if (compress !== null) return; |
|
pointer++; |
|
pieceIndex++; |
|
compress = pieceIndex; |
|
continue; |
|
} |
|
value = length = 0; |
|
while (length < 4 && exec(HEX, chr())) { |
|
value = value * 16 + parseInt(chr(), 16); |
|
pointer++; |
|
length++; |
|
} |
|
if (chr() == '.') { |
|
if (length == 0) return; |
|
pointer -= length; |
|
if (pieceIndex > 6) return; |
|
numbersSeen = 0; |
|
while (chr()) { |
|
ipv4Piece = null; |
|
if (numbersSeen > 0) { |
|
if (chr() == '.' && numbersSeen < 4) pointer++; |
|
else return; |
|
} |
|
if (!exec(DIGIT, chr())) return; |
|
while (exec(DIGIT, chr())) { |
|
number = parseInt(chr(), 10); |
|
if (ipv4Piece === null) ipv4Piece = number; |
|
else if (ipv4Piece == 0) return; |
|
else ipv4Piece = ipv4Piece * 10 + number; |
|
if (ipv4Piece > 255) return; |
|
pointer++; |
|
} |
|
address[pieceIndex] = address[pieceIndex] * 256 + ipv4Piece; |
|
numbersSeen++; |
|
if (numbersSeen == 2 || numbersSeen == 4) pieceIndex++; |
|
} |
|
if (numbersSeen != 4) return; |
|
break; |
|
} else if (chr() == ':') { |
|
pointer++; |
|
if (!chr()) return; |
|
} else if (chr()) return; |
|
address[pieceIndex++] = value; |
|
} |
|
if (compress !== null) { |
|
swaps = pieceIndex - compress; |
|
pieceIndex = 7; |
|
while (pieceIndex != 0 && swaps > 0) { |
|
swap = address[pieceIndex]; |
|
address[pieceIndex--] = address[compress + swaps - 1]; |
|
address[compress + --swaps] = swap; |
|
} |
|
} else if (pieceIndex != 8) return; |
|
return address; |
|
}; |
|
|
|
var findLongestZeroSequence = function (ipv6) { |
|
var maxIndex = null; |
|
var maxLength = 1; |
|
var currStart = null; |
|
var currLength = 0; |
|
var index = 0; |
|
for (; index < 8; index++) { |
|
if (ipv6[index] !== 0) { |
|
if (currLength > maxLength) { |
|
maxIndex = currStart; |
|
maxLength = currLength; |
|
} |
|
currStart = null; |
|
currLength = 0; |
|
} else { |
|
if (currStart === null) currStart = index; |
|
++currLength; |
|
} |
|
} |
|
if (currLength > maxLength) { |
|
maxIndex = currStart; |
|
maxLength = currLength; |
|
} |
|
return maxIndex; |
|
}; |
|
|
|
// https://url.spec.whatwg.org/#host-serializing |
|
var serializeHost = function (host) { |
|
var result, index, compress, ignore0; |
|
// ipv4 |
|
if (typeof host == 'number') { |
|
result = []; |
|
for (index = 0; index < 4; index++) { |
|
unshift(result, host % 256); |
|
host = floor(host / 256); |
|
} return join(result, '.'); |
|
// ipv6 |
|
} else if (typeof host == 'object') { |
|
result = ''; |
|
compress = findLongestZeroSequence(host); |
|
for (index = 0; index < 8; index++) { |
|
if (ignore0 && host[index] === 0) continue; |
|
if (ignore0) ignore0 = false; |
|
if (compress === index) { |
|
result += index ? ':' : '::'; |
|
ignore0 = true; |
|
} else { |
|
result += numberToString(host[index], 16); |
|
if (index < 7) result += ':'; |
|
} |
|
} |
|
return '[' + result + ']'; |
|
} return host; |
|
}; |
|
|
|
var C0ControlPercentEncodeSet = {}; |
|
var fragmentPercentEncodeSet = assign({}, C0ControlPercentEncodeSet, { |
|
' ': 1, '"': 1, '<': 1, '>': 1, '`': 1 |
|
}); |
|
var pathPercentEncodeSet = assign({}, fragmentPercentEncodeSet, { |
|
'#': 1, '?': 1, '{': 1, '}': 1 |
|
}); |
|
var userinfoPercentEncodeSet = assign({}, pathPercentEncodeSet, { |
|
'/': 1, ':': 1, ';': 1, '=': 1, '@': 1, '[': 1, '\\': 1, ']': 1, '^': 1, '|': 1 |
|
}); |
|
|
|
var percentEncode = function (chr, set) { |
|
var code = codeAt(chr, 0); |
|
return code > 0x20 && code < 0x7F && !hasOwn(set, chr) ? chr : encodeURIComponent(chr); |
|
}; |
|
|
|
// https://url.spec.whatwg.org/#special-scheme |
|
var specialSchemes = { |
|
ftp: 21, |
|
file: null, |
|
http: 80, |
|
https: 443, |
|
ws: 80, |
|
wss: 443 |
|
}; |
|
|
|
// https://url.spec.whatwg.org/#windows-drive-letter |
|
var isWindowsDriveLetter = function (string, normalized) { |
|
var second; |
|
return string.length == 2 && exec(ALPHA, charAt(string, 0)) |
|
&& ((second = charAt(string, 1)) == ':' || (!normalized && second == '|')); |
|
}; |
|
|
|
// https://url.spec.whatwg.org/#start-with-a-windows-drive-letter |
|
var startsWithWindowsDriveLetter = function (string) { |
|
var third; |
|
return string.length > 1 && isWindowsDriveLetter(stringSlice(string, 0, 2)) && ( |
|
string.length == 2 || |
|
((third = charAt(string, 2)) === '/' || third === '\\' || third === '?' || third === '#') |
|
); |
|
}; |
|
|
|
// https://url.spec.whatwg.org/#single-dot-path-segment |
|
var isSingleDot = function (segment) { |
|
return segment === '.' || toLowerCase(segment) === '%2e'; |
|
}; |
|
|
|
// https://url.spec.whatwg.org/#double-dot-path-segment |
|
var isDoubleDot = function (segment) { |
|
segment = toLowerCase(segment); |
|
return segment === '..' || segment === '%2e.' || segment === '.%2e' || segment === '%2e%2e'; |
|
}; |
|
|
|
// States: |
|
var SCHEME_START = {}; |
|
var SCHEME = {}; |
|
var NO_SCHEME = {}; |
|
var SPECIAL_RELATIVE_OR_AUTHORITY = {}; |
|
var PATH_OR_AUTHORITY = {}; |
|
var RELATIVE = {}; |
|
var RELATIVE_SLASH = {}; |
|
var SPECIAL_AUTHORITY_SLASHES = {}; |
|
var SPECIAL_AUTHORITY_IGNORE_SLASHES = {}; |
|
var AUTHORITY = {}; |
|
var HOST = {}; |
|
var HOSTNAME = {}; |
|
var PORT = {}; |
|
var FILE = {}; |
|
var FILE_SLASH = {}; |
|
var FILE_HOST = {}; |
|
var PATH_START = {}; |
|
var PATH = {}; |
|
var CANNOT_BE_A_BASE_URL_PATH = {}; |
|
var QUERY = {}; |
|
var FRAGMENT = {}; |
|
|
|
var URLState = function (url, isBase, base) { |
|
var urlString = $toString(url); |
|
var baseState, failure, searchParams; |
|
if (isBase) { |
|
failure = this.parse(urlString); |
|
if (failure) throw TypeError(failure); |
|
this.searchParams = null; |
|
} else { |
|
if (base !== undefined) baseState = new URLState(base, true); |
|
failure = this.parse(urlString, null, baseState); |
|
if (failure) throw TypeError(failure); |
|
searchParams = getInternalSearchParamsState(new URLSearchParams()); |
|
searchParams.bindURL(this); |
|
this.searchParams = searchParams; |
|
} |
|
}; |
|
|
|
URLState.prototype = { |
|
type: 'URL', |
|
// https://url.spec.whatwg.org/#url-parsing |
|
// eslint-disable-next-line max-statements -- TODO |
|
parse: function (input, stateOverride, base) { |
|
var url = this; |
|
var state = stateOverride || SCHEME_START; |
|
var pointer = 0; |
|
var buffer = ''; |
|
var seenAt = false; |
|
var seenBracket = false; |
|
var seenPasswordToken = false; |
|
var codePoints, chr, bufferCodePoints, failure; |
|
|
|
input = $toString(input); |
|
|
|
if (!stateOverride) { |
|
url.scheme = ''; |
|
url.username = ''; |
|
url.password = ''; |
|
url.host = null; |
|
url.port = null; |
|
url.path = []; |
|
url.query = null; |
|
url.fragment = null; |
|
url.cannotBeABaseURL = false; |
|
input = replace(input, LEADING_AND_TRAILING_C0_CONTROL_OR_SPACE, ''); |
|
} |
|
|
|
input = replace(input, TAB_AND_NEW_LINE, ''); |
|
|
|
codePoints = arrayFrom(input); |
|
|
|
while (pointer <= codePoints.length) { |
|
chr = codePoints[pointer]; |
|
switch (state) { |
|
case SCHEME_START: |
|
if (chr && exec(ALPHA, chr)) { |
|
buffer += toLowerCase(chr); |
|
state = SCHEME; |
|
} else if (!stateOverride) { |
|
state = NO_SCHEME; |
|
continue; |
|
} else return INVALID_SCHEME; |
|
break; |
|
|
|
case SCHEME: |
|
if (chr && (exec(ALPHANUMERIC, chr) || chr == '+' || chr == '-' || chr == '.')) { |
|
buffer += toLowerCase(chr); |
|
} else if (chr == ':') { |
|
if (stateOverride && ( |
|
(url.isSpecial() != hasOwn(specialSchemes, buffer)) || |
|
(buffer == 'file' && (url.includesCredentials() || url.port !== null)) || |
|
(url.scheme == 'file' && !url.host) |
|
)) return; |
|
url.scheme = buffer; |
|
if (stateOverride) { |
|
if (url.isSpecial() && specialSchemes[url.scheme] == url.port) url.port = null; |
|
return; |
|
} |
|
buffer = ''; |
|
if (url.scheme == 'file') { |
|
state = FILE; |
|
} else if (url.isSpecial() && base && base.scheme == url.scheme) { |
|
state = SPECIAL_RELATIVE_OR_AUTHORITY; |
|
} else if (url.isSpecial()) { |
|
state = SPECIAL_AUTHORITY_SLASHES; |
|
} else if (codePoints[pointer + 1] == '/') { |
|
state = PATH_OR_AUTHORITY; |
|
pointer++; |
|
} else { |
|
url.cannotBeABaseURL = true; |
|
push(url.path, ''); |
|
state = CANNOT_BE_A_BASE_URL_PATH; |
|
} |
|
} else if (!stateOverride) { |
|
buffer = ''; |
|
state = NO_SCHEME; |
|
pointer = 0; |
|
continue; |
|
} else return INVALID_SCHEME; |
|
break; |
|
|
|
case NO_SCHEME: |
|
if (!base || (base.cannotBeABaseURL && chr != '#')) return INVALID_SCHEME; |
|
if (base.cannotBeABaseURL && chr == '#') { |
|
url.scheme = base.scheme; |
|
url.path = arraySlice(base.path); |
|
url.query = base.query; |
|
url.fragment = ''; |
|
url.cannotBeABaseURL = true; |
|
state = FRAGMENT; |
|
break; |
|
} |
|
state = base.scheme == 'file' ? FILE : RELATIVE; |
|
continue; |
|
|
|
case SPECIAL_RELATIVE_OR_AUTHORITY: |
|
if (chr == '/' && codePoints[pointer + 1] == '/') { |
|
state = SPECIAL_AUTHORITY_IGNORE_SLASHES; |
|
pointer++; |
|
} else { |
|
state = RELATIVE; |
|
continue; |
|
} break; |
|
|
|
case PATH_OR_AUTHORITY: |
|
if (chr == '/') { |
|
state = AUTHORITY; |
|
break; |
|
} else { |
|
state = PATH; |
|
continue; |
|
} |
|
|
|
case RELATIVE: |
|
url.scheme = base.scheme; |
|
if (chr == EOF) { |
|
url.username = base.username; |
|
url.password = base.password; |
|
url.host = base.host; |
|
url.port = base.port; |
|
url.path = arraySlice(base.path); |
|
url.query = base.query; |
|
} else if (chr == '/' || (chr == '\\' && url.isSpecial())) { |
|
state = RELATIVE_SLASH; |
|
} else if (chr == '?') { |
|
url.username = base.username; |
|
url.password = base.password; |
|
url.host = base.host; |
|
url.port = base.port; |
|
url.path = arraySlice(base.path); |
|
url.query = ''; |
|
state = QUERY; |
|
} else if (chr == '#') { |
|
url.username = base.username; |
|
url.password = base.password; |
|
url.host = base.host; |
|
url.port = base.port; |
|
url.path = arraySlice(base.path); |
|
url.query = base.query; |
|
url.fragment = ''; |
|
state = FRAGMENT; |
|
} else { |
|
url.username = base.username; |
|
url.password = base.password; |
|
url.host = base.host; |
|
url.port = base.port; |
|
url.path = arraySlice(base.path); |
|
url.path.length--; |
|
state = PATH; |
|
continue; |
|
} break; |
|
|
|
case RELATIVE_SLASH: |
|
if (url.isSpecial() && (chr == '/' || chr == '\\')) { |
|
state = SPECIAL_AUTHORITY_IGNORE_SLASHES; |
|
} else if (chr == '/') { |
|
state = AUTHORITY; |
|
} else { |
|
url.username = base.username; |
|
url.password = base.password; |
|
url.host = base.host; |
|
url.port = base.port; |
|
state = PATH; |
|
continue; |
|
} break; |
|
|
|
case SPECIAL_AUTHORITY_SLASHES: |
|
state = SPECIAL_AUTHORITY_IGNORE_SLASHES; |
|
if (chr != '/' || charAt(buffer, pointer + 1) != '/') continue; |
|
pointer++; |
|
break; |
|
|
|
case SPECIAL_AUTHORITY_IGNORE_SLASHES: |
|
if (chr != '/' && chr != '\\') { |
|
state = AUTHORITY; |
|
continue; |
|
} break; |
|
|
|
case AUTHORITY: |
|
if (chr == '@') { |
|
if (seenAt) buffer = '%40' + buffer; |
|
seenAt = true; |
|
bufferCodePoints = arrayFrom(buffer); |
|
for (var i = 0; i < bufferCodePoints.length; i++) { |
|
var codePoint = bufferCodePoints[i]; |
|
if (codePoint == ':' && !seenPasswordToken) { |
|
seenPasswordToken = true; |
|
continue; |
|
} |
|
var encodedCodePoints = percentEncode(codePoint, userinfoPercentEncodeSet); |
|
if (seenPasswordToken) url.password += encodedCodePoints; |
|
else url.username += encodedCodePoints; |
|
} |
|
buffer = ''; |
|
} else if ( |
|
chr == EOF || chr == '/' || chr == '?' || chr == '#' || |
|
(chr == '\\' && url.isSpecial()) |
|
) { |
|
if (seenAt && buffer == '') return INVALID_AUTHORITY; |
|
pointer -= arrayFrom(buffer).length + 1; |
|
buffer = ''; |
|
state = HOST; |
|
} else buffer += chr; |
|
break; |
|
|
|
case HOST: |
|
case HOSTNAME: |
|
if (stateOverride && url.scheme == 'file') { |
|
state = FILE_HOST; |
|
continue; |
|
} else if (chr == ':' && !seenBracket) { |
|
if (buffer == '') return INVALID_HOST; |
|
failure = url.parseHost(buffer); |
|
if (failure) return failure; |
|
buffer = ''; |
|
state = PORT; |
|
if (stateOverride == HOSTNAME) return; |
|
} else if ( |
|
chr == EOF || chr == '/' || chr == '?' || chr == '#' || |
|
(chr == '\\' && url.isSpecial()) |
|
) { |
|
if (url.isSpecial() && buffer == '') return INVALID_HOST; |
|
if (stateOverride && buffer == '' && (url.includesCredentials() || url.port !== null)) return; |
|
failure = url.parseHost(buffer); |
|
if (failure) return failure; |
|
buffer = ''; |
|
state = PATH_START; |
|
if (stateOverride) return; |
|
continue; |
|
} else { |
|
if (chr == '[') seenBracket = true; |
|
else if (chr == ']') seenBracket = false; |
|
buffer += chr; |
|
} break; |
|
|
|
case PORT: |
|
if (exec(DIGIT, chr)) { |
|
buffer += chr; |
|
} else if ( |
|
chr == EOF || chr == '/' || chr == '?' || chr == '#' || |
|
(chr == '\\' && url.isSpecial()) || |
|
stateOverride |
|
) { |
|
if (buffer != '') { |
|
var port = parseInt(buffer, 10); |
|
if (port > 0xFFFF) return INVALID_PORT; |
|
url.port = (url.isSpecial() && port === specialSchemes[url.scheme]) ? null : port; |
|
buffer = ''; |
|
} |
|
if (stateOverride) return; |
|
state = PATH_START; |
|
continue; |
|
} else return INVALID_PORT; |
|
break; |
|
|
|
case FILE: |
|
url.scheme = 'file'; |
|
if (chr == '/' || chr == '\\') state = FILE_SLASH; |
|
else if (base && base.scheme == 'file') { |
|
if (chr == EOF) { |
|
url.host = base.host; |
|
url.path = arraySlice(base.path); |
|
url.query = base.query; |
|
} else if (chr == '?') { |
|
url.host = base.host; |
|
url.path = arraySlice(base.path); |
|
url.query = ''; |
|
state = QUERY; |
|
} else if (chr == '#') { |
|
url.host = base.host; |
|
url.path = arraySlice(base.path); |
|
url.query = base.query; |
|
url.fragment = ''; |
|
state = FRAGMENT; |
|
} else { |
|
if (!startsWithWindowsDriveLetter(join(arraySlice(codePoints, pointer), ''))) { |
|
url.host = base.host; |
|
url.path = arraySlice(base.path); |
|
url.shortenPath(); |
|
} |
|
state = PATH; |
|
continue; |
|
} |
|
} else { |
|
state = PATH; |
|
continue; |
|
} break; |
|
|
|
case FILE_SLASH: |
|
if (chr == '/' || chr == '\\') { |
|
state = FILE_HOST; |
|
break; |
|
} |
|
if (base && base.scheme == 'file' && !startsWithWindowsDriveLetter(join(arraySlice(codePoints, pointer), ''))) { |
|
if (isWindowsDriveLetter(base.path[0], true)) push(url.path, base.path[0]); |
|
else url.host = base.host; |
|
} |
|
state = PATH; |
|
continue; |
|
|
|
case FILE_HOST: |
|
if (chr == EOF || chr == '/' || chr == '\\' || chr == '?' || chr == '#') { |
|
if (!stateOverride && isWindowsDriveLetter(buffer)) { |
|
state = PATH; |
|
} else if (buffer == '') { |
|
url.host = ''; |
|
if (stateOverride) return; |
|
state = PATH_START; |
|
} else { |
|
failure = url.parseHost(buffer); |
|
if (failure) return failure; |
|
if (url.host == 'localhost') url.host = ''; |
|
if (stateOverride) return; |
|
buffer = ''; |
|
state = PATH_START; |
|
} continue; |
|
} else buffer += chr; |
|
break; |
|
|
|
case PATH_START: |
|
if (url.isSpecial()) { |
|
state = PATH; |
|
if (chr != '/' && chr != '\\') continue; |
|
} else if (!stateOverride && chr == '?') { |
|
url.query = ''; |
|
state = QUERY; |
|
} else if (!stateOverride && chr == '#') { |
|
url.fragment = ''; |
|
state = FRAGMENT; |
|
} else if (chr != EOF) { |
|
state = PATH; |
|
if (chr != '/') continue; |
|
} break; |
|
|
|
case PATH: |
|
if ( |
|
chr == EOF || chr == '/' || |
|
(chr == '\\' && url.isSpecial()) || |
|
(!stateOverride && (chr == '?' || chr == '#')) |
|
) { |
|
if (isDoubleDot(buffer)) { |
|
url.shortenPath(); |
|
if (chr != '/' && !(chr == '\\' && url.isSpecial())) { |
|
push(url.path, ''); |
|
} |
|
} else if (isSingleDot(buffer)) { |
|
if (chr != '/' && !(chr == '\\' && url.isSpecial())) { |
|
push(url.path, ''); |
|
} |
|
} else { |
|
if (url.scheme == 'file' && !url.path.length && isWindowsDriveLetter(buffer)) { |
|
if (url.host) url.host = ''; |
|
buffer = charAt(buffer, 0) + ':'; // normalize windows drive letter |
|
} |
|
push(url.path, buffer); |
|
} |
|
buffer = ''; |
|
if (url.scheme == 'file' && (chr == EOF || chr == '?' || chr == '#')) { |
|
while (url.path.length > 1 && url.path[0] === '') { |
|
shift(url.path); |
|
} |
|
} |
|
if (chr == '?') { |
|
url.query = ''; |
|
state = QUERY; |
|
} else if (chr == '#') { |
|
url.fragment = ''; |
|
state = FRAGMENT; |
|
} |
|
} else { |
|
buffer += percentEncode(chr, pathPercentEncodeSet); |
|
} break; |
|
|
|
case CANNOT_BE_A_BASE_URL_PATH: |
|
if (chr == '?') { |
|
url.query = ''; |
|
state = QUERY; |
|
} else if (chr == '#') { |
|
url.fragment = ''; |
|
state = FRAGMENT; |
|
} else if (chr != EOF) { |
|
url.path[0] += percentEncode(chr, C0ControlPercentEncodeSet); |
|
} break; |
|
|
|
case QUERY: |
|
if (!stateOverride && chr == '#') { |
|
url.fragment = ''; |
|
state = FRAGMENT; |
|
} else if (chr != EOF) { |
|
if (chr == "'" && url.isSpecial()) url.query += '%27'; |
|
else if (chr == '#') url.query += '%23'; |
|
else url.query += percentEncode(chr, C0ControlPercentEncodeSet); |
|
} break; |
|
|
|
case FRAGMENT: |
|
if (chr != EOF) url.fragment += percentEncode(chr, fragmentPercentEncodeSet); |
|
break; |
|
} |
|
|
|
pointer++; |
|
} |
|
}, |
|
// https://url.spec.whatwg.org/#host-parsing |
|
parseHost: function (input) { |
|
var result, codePoints, index; |
|
if (charAt(input, 0) == '[') { |
|
if (charAt(input, input.length - 1) != ']') return INVALID_HOST; |
|
result = parseIPv6(stringSlice(input, 1, -1)); |
|
if (!result) return INVALID_HOST; |
|
this.host = result; |
|
// opaque host |
|
} else if (!this.isSpecial()) { |
|
if (exec(FORBIDDEN_HOST_CODE_POINT_EXCLUDING_PERCENT, input)) return INVALID_HOST; |
|
result = ''; |
|
codePoints = arrayFrom(input); |
|
for (index = 0; index < codePoints.length; index++) { |
|
result += percentEncode(codePoints[index], C0ControlPercentEncodeSet); |
|
} |
|
this.host = result; |
|
} else { |
|
input = toASCII(input); |
|
if (exec(FORBIDDEN_HOST_CODE_POINT, input)) return INVALID_HOST; |
|
result = parseIPv4(input); |
|
if (result === null) return INVALID_HOST; |
|
this.host = result; |
|
} |
|
}, |
|
// https://url.spec.whatwg.org/#cannot-have-a-username-password-port |
|
cannotHaveUsernamePasswordPort: function () { |
|
return !this.host || this.cannotBeABaseURL || this.scheme == 'file'; |
|
}, |
|
// https://url.spec.whatwg.org/#include-credentials |
|
includesCredentials: function () { |
|
return this.username != '' || this.password != ''; |
|
}, |
|
// https://url.spec.whatwg.org/#is-special |
|
isSpecial: function () { |
|
return hasOwn(specialSchemes, this.scheme); |
|
}, |
|
// https://url.spec.whatwg.org/#shorten-a-urls-path |
|
shortenPath: function () { |
|
var path = this.path; |
|
var pathSize = path.length; |
|
if (pathSize && (this.scheme != 'file' || pathSize != 1 || !isWindowsDriveLetter(path[0], true))) { |
|
path.length--; |
|
} |
|
}, |
|
// https://url.spec.whatwg.org/#concept-url-serializer |
|
serialize: function () { |
|
var url = this; |
|
var scheme = url.scheme; |
|
var username = url.username; |
|
var password = url.password; |
|
var host = url.host; |
|
var port = url.port; |
|
var path = url.path; |
|
var query = url.query; |
|
var fragment = url.fragment; |
|
var output = scheme + ':'; |
|
if (host !== null) { |
|
output += '//'; |
|
if (url.includesCredentials()) { |
|
output += username + (password ? ':' + password : '') + '@'; |
|
} |
|
output += serializeHost(host); |
|
if (port !== null) output += ':' + port; |
|
} else if (scheme == 'file') output += '//'; |
|
output += url.cannotBeABaseURL ? path[0] : path.length ? '/' + join(path, '/') : ''; |
|
if (query !== null) output += '?' + query; |
|
if (fragment !== null) output += '#' + fragment; |
|
return output; |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-href |
|
setHref: function (href) { |
|
var failure = this.parse(href); |
|
if (failure) throw TypeError(failure); |
|
this.searchParams.update(); |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-origin |
|
getOrigin: function () { |
|
var scheme = this.scheme; |
|
var port = this.port; |
|
if (scheme == 'blob') try { |
|
return new URLConstructor(scheme.path[0]).origin; |
|
} catch (error) { |
|
return 'null'; |
|
} |
|
if (scheme == 'file' || !this.isSpecial()) return 'null'; |
|
return scheme + '://' + serializeHost(this.host) + (port !== null ? ':' + port : ''); |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-protocol |
|
getProtocol: function () { |
|
return this.scheme + ':'; |
|
}, |
|
setProtocol: function (protocol) { |
|
this.parse($toString(protocol) + ':', SCHEME_START); |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-username |
|
getUsername: function () { |
|
return this.username; |
|
}, |
|
setUsername: function (username) { |
|
var codePoints = arrayFrom($toString(username)); |
|
if (this.cannotHaveUsernamePasswordPort()) return; |
|
this.username = ''; |
|
for (var i = 0; i < codePoints.length; i++) { |
|
this.username += percentEncode(codePoints[i], userinfoPercentEncodeSet); |
|
} |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-password |
|
getPassword: function () { |
|
return this.password; |
|
}, |
|
setPassword: function (password) { |
|
var codePoints = arrayFrom($toString(password)); |
|
if (this.cannotHaveUsernamePasswordPort()) return; |
|
this.password = ''; |
|
for (var i = 0; i < codePoints.length; i++) { |
|
this.password += percentEncode(codePoints[i], userinfoPercentEncodeSet); |
|
} |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-host |
|
getHost: function () { |
|
var host = this.host; |
|
var port = this.port; |
|
return host === null ? '' |
|
: port === null ? serializeHost(host) |
|
: serializeHost(host) + ':' + port; |
|
}, |
|
setHost: function (host) { |
|
if (this.cannotBeABaseURL) return; |
|
this.parse(host, HOST); |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-hostname |
|
getHostname: function () { |
|
var host = this.host; |
|
return host === null ? '' : serializeHost(host); |
|
}, |
|
setHostname: function (hostname) { |
|
if (this.cannotBeABaseURL) return; |
|
this.parse(hostname, HOSTNAME); |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-port |
|
getPort: function () { |
|
var port = this.port; |
|
return port === null ? '' : $toString(port); |
|
}, |
|
setPort: function (port) { |
|
if (this.cannotHaveUsernamePasswordPort()) return; |
|
port = $toString(port); |
|
if (port == '') this.port = null; |
|
else this.parse(port, PORT); |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-pathname |
|
getPathname: function () { |
|
var path = this.path; |
|
return this.cannotBeABaseURL ? path[0] : path.length ? '/' + join(path, '/') : ''; |
|
}, |
|
setPathname: function (pathname) { |
|
if (this.cannotBeABaseURL) return; |
|
this.path = []; |
|
this.parse(pathname, PATH_START); |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-search |
|
getSearch: function () { |
|
var query = this.query; |
|
return query ? '?' + query : ''; |
|
}, |
|
setSearch: function (search) { |
|
search = $toString(search); |
|
if (search == '') { |
|
this.query = null; |
|
} else { |
|
if ('?' == charAt(search, 0)) search = stringSlice(search, 1); |
|
this.query = ''; |
|
this.parse(search, QUERY); |
|
} |
|
this.searchParams.update(); |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-searchparams |
|
getSearchParams: function () { |
|
return this.searchParams.facade; |
|
}, |
|
// https://url.spec.whatwg.org/#dom-url-hash |
|
getHash: function () { |
|
var fragment = this.fragment; |
|
return fragment ? '#' + fragment : ''; |
|
}, |
|
setHash: function (hash) { |
|
hash = $toString(hash); |
|
if (hash == '') { |
|
this.fragment = null; |
|
return; |
|
} |
|
if ('#' == charAt(hash, 0)) hash = stringSlice(hash, 1); |
|
this.fragment = ''; |
|
this.parse(hash, FRAGMENT); |
|
}, |
|
update: function () { |
|
this.query = this.searchParams.serialize() || null; |
|
} |
|
}; |
|
|
|
// `URL` constructor |
|
// https://url.spec.whatwg.org/#url-class |
|
var URLConstructor = function URL(url /* , base */) { |
|
var that = anInstance(this, URLPrototype); |
|
var base = validateArgumentsLength(arguments.length, 1) > 1 ? arguments[1] : undefined; |
|
var state = setInternalState(that, new URLState(url, false, base)); |
|
if (!DESCRIPTORS) { |
|
that.href = state.serialize(); |
|
that.origin = state.getOrigin(); |
|
that.protocol = state.getProtocol(); |
|
that.username = state.getUsername(); |
|
that.password = state.getPassword(); |
|
that.host = state.getHost(); |
|
that.hostname = state.getHostname(); |
|
that.port = state.getPort(); |
|
that.pathname = state.getPathname(); |
|
that.search = state.getSearch(); |
|
that.searchParams = state.getSearchParams(); |
|
that.hash = state.getHash(); |
|
} |
|
}; |
|
|
|
var URLPrototype = URLConstructor.prototype; |
|
|
|
var accessorDescriptor = function (getter, setter) { |
|
return { |
|
get: function () { |
|
return getInternalURLState(this)[getter](); |
|
}, |
|
set: setter && function (value) { |
|
return getInternalURLState(this)[setter](value); |
|
}, |
|
configurable: true, |
|
enumerable: true |
|
}; |
|
}; |
|
|
|
if (DESCRIPTORS) { |
|
defineProperties(URLPrototype, { |
|
// `URL.prototype.href` accessors pair |
|
// https://url.spec.whatwg.org/#dom-url-href |
|
href: accessorDescriptor('serialize', 'setHref'), |
|
// `URL.prototype.origin` getter |
|
// https://url.spec.whatwg.org/#dom-url-origin |
|
origin: accessorDescriptor('getOrigin'), |
|
// `URL.prototype.protocol` accessors pair |
|
// https://url.spec.whatwg.org/#dom-url-protocol |
|
protocol: accessorDescriptor('getProtocol', 'setProtocol'), |
|
// `URL.prototype.username` accessors pair |
|
// https://url.spec.whatwg.org/#dom-url-username |
|
username: accessorDescriptor('getUsername', 'setUsername'), |
|
// `URL.prototype.password` accessors pair |
|
// https://url.spec.whatwg.org/#dom-url-password |
|
password: accessorDescriptor('getPassword', 'setPassword'), |
|
// `URL.prototype.host` accessors pair |
|
// https://url.spec.whatwg.org/#dom-url-host |
|
host: accessorDescriptor('getHost', 'setHost'), |
|
// `URL.prototype.hostname` accessors pair |
|
// https://url.spec.whatwg.org/#dom-url-hostname |
|
hostname: accessorDescriptor('getHostname', 'setHostname'), |
|
// `URL.prototype.port` accessors pair |
|
// https://url.spec.whatwg.org/#dom-url-port |
|
port: accessorDescriptor('getPort', 'setPort'), |
|
// `URL.prototype.pathname` accessors pair |
|
// https://url.spec.whatwg.org/#dom-url-pathname |
|
pathname: accessorDescriptor('getPathname', 'setPathname'), |
|
// `URL.prototype.search` accessors pair |
|
// https://url.spec.whatwg.org/#dom-url-search |
|
search: accessorDescriptor('getSearch', 'setSearch'), |
|
// `URL.prototype.searchParams` getter |
|
// https://url.spec.whatwg.org/#dom-url-searchparams |
|
searchParams: accessorDescriptor('getSearchParams'), |
|
// `URL.prototype.hash` accessors pair |
|
// https://url.spec.whatwg.org/#dom-url-hash |
|
hash: accessorDescriptor('getHash', 'setHash') |
|
}); |
|
} |
|
|
|
// `URL.prototype.toJSON` method |
|
// https://url.spec.whatwg.org/#dom-url-tojson |
|
redefine(URLPrototype, 'toJSON', function toJSON() { |
|
return getInternalURLState(this).serialize(); |
|
}, { enumerable: true }); |
|
|
|
// `URL.prototype.toString` method |
|
// https://url.spec.whatwg.org/#URL-stringification-behavior |
|
redefine(URLPrototype, 'toString', function toString() { |
|
return getInternalURLState(this).serialize(); |
|
}, { enumerable: true }); |
|
|
|
if (NativeURL) { |
|
var nativeCreateObjectURL = NativeURL.createObjectURL; |
|
var nativeRevokeObjectURL = NativeURL.revokeObjectURL; |
|
// `URL.createObjectURL` method |
|
// https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL |
|
if (nativeCreateObjectURL) redefine(URLConstructor, 'createObjectURL', bind(nativeCreateObjectURL, NativeURL)); |
|
// `URL.revokeObjectURL` method |
|
// https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL |
|
if (nativeRevokeObjectURL) redefine(URLConstructor, 'revokeObjectURL', bind(nativeRevokeObjectURL, NativeURL)); |
|
} |
|
|
|
setToStringTag(URLConstructor, 'URL'); |
|
|
|
$({ global: true, forced: !USE_NATIVE_URL, sham: !DESCRIPTORS }, { |
|
URL: URLConstructor |
|
});
|
|
|