1049 lines
35 KiB
JavaScript
1049 lines
35 KiB
JavaScript
'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/url-constructor-detection');
|
|
var global = require('../internals/global');
|
|
var bind = require('../internals/function-bind-context');
|
|
var uncurryThis = require('../internals/function-uncurry-this');
|
|
var defineBuiltIn = require('../internals/define-built-in');
|
|
var defineBuiltInAccessor = require('../internals/define-built-in-accessor');
|
|
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.constructor');
|
|
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_C0_CONTROL_OR_SPACE = /^[\u0000-\u0020]+/;
|
|
var TRAILING_C0_CONTROL_OR_SPACE = /(^|[^\u0000-\u0020])[\u0000-\u0020]+$/;
|
|
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_C0_CONTROL_OR_SPACE, '');
|
|
input = replace(input, TRAILING_C0_CONTROL_OR_SPACE, '$1');
|
|
}
|
|
|
|
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') {
|
|
switch (chr) {
|
|
case EOF:
|
|
url.host = base.host;
|
|
url.path = arraySlice(base.path);
|
|
url.query = base.query;
|
|
break;
|
|
case '?':
|
|
url.host = base.host;
|
|
url.path = arraySlice(base.path);
|
|
url.query = '';
|
|
state = QUERY;
|
|
break;
|
|
case '#':
|
|
url.host = base.host;
|
|
url.path = arraySlice(base.path);
|
|
url.query = base.query;
|
|
url.fragment = '';
|
|
state = FRAGMENT;
|
|
break;
|
|
default:
|
|
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) {
|
|
// `URL.prototype.href` accessors pair
|
|
// https://url.spec.whatwg.org/#dom-url-href
|
|
defineBuiltInAccessor(URLPrototype, 'href', accessorDescriptor('serialize', 'setHref'));
|
|
// `URL.prototype.origin` getter
|
|
// https://url.spec.whatwg.org/#dom-url-origin
|
|
defineBuiltInAccessor(URLPrototype, 'origin', accessorDescriptor('getOrigin'));
|
|
// `URL.prototype.protocol` accessors pair
|
|
// https://url.spec.whatwg.org/#dom-url-protocol
|
|
defineBuiltInAccessor(URLPrototype, 'protocol', accessorDescriptor('getProtocol', 'setProtocol'));
|
|
// `URL.prototype.username` accessors pair
|
|
// https://url.spec.whatwg.org/#dom-url-username
|
|
defineBuiltInAccessor(URLPrototype, 'username', accessorDescriptor('getUsername', 'setUsername'));
|
|
// `URL.prototype.password` accessors pair
|
|
// https://url.spec.whatwg.org/#dom-url-password
|
|
defineBuiltInAccessor(URLPrototype, 'password', accessorDescriptor('getPassword', 'setPassword'));
|
|
// `URL.prototype.host` accessors pair
|
|
// https://url.spec.whatwg.org/#dom-url-host
|
|
defineBuiltInAccessor(URLPrototype, 'host', accessorDescriptor('getHost', 'setHost'));
|
|
// `URL.prototype.hostname` accessors pair
|
|
// https://url.spec.whatwg.org/#dom-url-hostname
|
|
defineBuiltInAccessor(URLPrototype, 'hostname', accessorDescriptor('getHostname', 'setHostname'));
|
|
// `URL.prototype.port` accessors pair
|
|
// https://url.spec.whatwg.org/#dom-url-port
|
|
defineBuiltInAccessor(URLPrototype, 'port', accessorDescriptor('getPort', 'setPort'));
|
|
// `URL.prototype.pathname` accessors pair
|
|
// https://url.spec.whatwg.org/#dom-url-pathname
|
|
defineBuiltInAccessor(URLPrototype, 'pathname', accessorDescriptor('getPathname', 'setPathname'));
|
|
// `URL.prototype.search` accessors pair
|
|
// https://url.spec.whatwg.org/#dom-url-search
|
|
defineBuiltInAccessor(URLPrototype, 'search', accessorDescriptor('getSearch', 'setSearch'));
|
|
// `URL.prototype.searchParams` getter
|
|
// https://url.spec.whatwg.org/#dom-url-searchparams
|
|
defineBuiltInAccessor(URLPrototype, 'searchParams', accessorDescriptor('getSearchParams'));
|
|
// `URL.prototype.hash` accessors pair
|
|
// https://url.spec.whatwg.org/#dom-url-hash
|
|
defineBuiltInAccessor(URLPrototype, 'hash', accessorDescriptor('getHash', 'setHash'));
|
|
}
|
|
|
|
// `URL.prototype.toJSON` method
|
|
// https://url.spec.whatwg.org/#dom-url-tojson
|
|
defineBuiltIn(URLPrototype, 'toJSON', function toJSON() {
|
|
return getInternalURLState(this).serialize();
|
|
}, { enumerable: true });
|
|
|
|
// `URL.prototype.toString` method
|
|
// https://url.spec.whatwg.org/#URL-stringification-behavior
|
|
defineBuiltIn(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) defineBuiltIn(URLConstructor, 'createObjectURL', bind(nativeCreateObjectURL, NativeURL));
|
|
// `URL.revokeObjectURL` method
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL
|
|
if (nativeRevokeObjectURL) defineBuiltIn(URLConstructor, 'revokeObjectURL', bind(nativeRevokeObjectURL, NativeURL));
|
|
}
|
|
|
|
setToStringTag(URLConstructor, 'URL');
|
|
|
|
$({ global: true, constructor: true, forced: !USE_NATIVE_URL, sham: !DESCRIPTORS }, {
|
|
URL: URLConstructor
|
|
});
|