authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-02-03 20:51:29 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-02-03 20:51:29 -08:00
logd4423c228d4eb0a749fec2db387df8b5bf522d3e
tree8d2fd2480a5230c84f1ec73310ce46a395ae6422
parent7895771e940a0d38141203bb110c40349c2bd9d0

pass parsePass with base


3 files changed, 428 insertions(+), 62 deletions(-)

generate.ts-2
...@@ -130,8 +130,6 @@ for (const c of cases) {...@@ -130,8 +130,6 @@ for (const c of cases) {
130}130}
131131
132w.write(`\n`);132w.write(`\n`);
133// prettier-ignore
134if (false)
135for (const c of cases) {133for (const c of cases) {
136 if (c.base === null) continue;134 if (c.base === null) continue;
137 if (c.failure) continue;135 if (c.failure) continue;
url.zig+160-60
...@@ -16,6 +16,7 @@ pub const URL = struct {...@@ -16,6 +16,7 @@ pub const URL = struct {
16 pathname: []const u8,16 pathname: []const u8,
17 search: []const u8,17 search: []const u8,
18 hash: []const u8,18 hash: []const u8,
19 has_opaque_path: bool,
1920
20 pub const HostKind = enum {21 pub const HostKind = enum {
21 unset,22 unset,
...@@ -36,13 +37,13 @@ pub const URL = struct {...@@ -36,13 +37,13 @@ pub const URL = struct {
36 if (base) |b| {37 if (base) |b| {
37 const b_url = try parseBasic(alloc, b, null, null);38 const b_url = try parseBasic(alloc, b, null, null);
38 defer alloc.free(b_url.href);39 defer alloc.free(b_url.href);
39 return parseBasic(alloc, input, b_url, null);40 return parseBasic(alloc, input, &b_url, null);
40 }41 }
41 return parseBasic(alloc, input, if (base) |b| try parseBasic(alloc, b, null, null) else null, null);42 return parseBasic(alloc, input, null, null);
42 }43 }
4344
44 /// https://url.spec.whatwg.org/#concept-basic-url-parser45 /// https://url.spec.whatwg.org/#concept-basic-url-parser
45 fn parseBasic(alloc: std.mem.Allocator, input: []const u8, base: ?URL, state_override: ?BasicParserState) error{ InvalidURL, OutOfMemory }!URL {46 fn parseBasic(alloc: std.mem.Allocator, input: []const u8, base: ?*const URL, state_override: ?BasicParserState) error{ InvalidURL, OutOfMemory }!URL {
46 // input is a scalar value string47 // input is a scalar value string
47 if (!std.unicode.utf8ValidateSlice(input)) return error.InvalidURL;48 if (!std.unicode.utf8ValidateSlice(input)) return error.InvalidURL;
4849
...@@ -93,6 +94,7 @@ pub const URL = struct {...@@ -93,6 +94,7 @@ pub const URL = struct {
93 var href = ManyArrayList(8 + 7, u8).init(alloc);94 var href = ManyArrayList(8 + 7, u8).init(alloc);
94 defer href.deinit();95 defer href.deinit();
95 var hostname_kind: HostKind = .unset;96 var hostname_kind: HostKind = .unset;
97 var has_opaque_path = false;
96 // scheme98 // scheme
97 // :99 // :
98 // //100 // //
...@@ -164,9 +166,11 @@ pub const URL = struct {...@@ -164,9 +166,11 @@ pub const URL = struct {
164 }166 }
165 // 6. Otherwise, if url is special, base is non-null, and base’s scheme is url’s scheme:167 // 6. Otherwise, if url is special, base is non-null, and base’s scheme is url’s scheme:
166 else if (isSchemeSpecial(href.items(0)) and base != null and std.mem.eql(u8, base.?.scheme(), href.items(0))) {168 else if (isSchemeSpecial(href.items(0)) and base != null and std.mem.eql(u8, base.?.scheme(), href.items(0))) {
167 @panic("TODO");
168 // 1. Assert: base is special (and therefore does not have an opaque path).169 // 1. Assert: base is special (and therefore does not have an opaque path).
170 std.debug.assert(base.?.isSpecial());
171 std.debug.assert(!base.?.has_opaque_path);
169 // 2. Set state to special relative or authority state.172 // 2. Set state to special relative or authority state.
173 state = .special_relative_or_authority;
170 }174 }
171 // 7. Otherwise, if url is special, set state to special authority slashes state.175 // 7. Otherwise, if url is special, set state to special authority slashes state.
172 else if (isSchemeSpecial(href.items(0))) {176 else if (isSchemeSpecial(href.items(0))) {
...@@ -182,6 +186,7 @@ pub const URL = struct {...@@ -182,6 +186,7 @@ pub const URL = struct {
182 // 9. Otherwise, set url’s path to the empty string and set state to opaque path state.186 // 9. Otherwise, set url’s path to the empty string and set state to opaque path state.
183 else {187 else {
184 href.clear(10);188 href.clear(10);
189 has_opaque_path = true;
185 state = .opaque_path;190 state = .opaque_path;
186 }191 }
187 }192 }
...@@ -192,6 +197,7 @@ pub const URL = struct {...@@ -192,6 +197,7 @@ pub const URL = struct {
192 pointer = 0;197 pointer = 0;
193 i = 0;198 i = 0;
194 c = inputl.items[i];199 c = inputl.items[i];
200 continue;
195 }201 }
196 // 4. Otherwise, return failure.202 // 4. Otherwise, return failure.
197 else {203 else {
...@@ -200,11 +206,38 @@ pub const URL = struct {...@@ -200,11 +206,38 @@ pub const URL = struct {
200 },206 },
201 .no_scheme => {207 .no_scheme => {
202 // 1. If base is null, or base has an opaque path and c is not U+0023 (#), missing-scheme-non-relative-URL validation error, return failure.208 // 1. If base is null, or base has an opaque path and c is not U+0023 (#), missing-scheme-non-relative-URL validation error, return failure.
203 if (base == null) return error.InvalidURL;209 if (base == null or (base != null and base.?.has_opaque_path and c != '#')) {
210 return error.InvalidURL;
211 }
204 // 2. Otherwise, if base has an opaque path and c is U+0023 (#), set url’s scheme to base’s scheme, url’s path to base’s path, url’s query to base’s query, url’s fragment to the empty string, and set state to fragment state.212 // 2. Otherwise, if base has an opaque path and c is U+0023 (#), set url’s scheme to base’s scheme, url’s path to base’s path, url’s query to base’s query, url’s fragment to the empty string, and set state to fragment state.
213 else if (base.?.has_opaque_path and c == '#') {
214 try href.set(0, base.?.scheme());
215 try href.set(1, ":");
216 try href.set(7, base.?.hostname);
217 hostname_kind = base.?.hostname_kind;
218 try href.set(10, base.?.pathname);
219 has_opaque_path = base.?.has_opaque_path;
220 try href.set(12, base.?.query());
221 try href.set(13, "#");
222 try href.set(14, "");
223 state = .fragment;
224 }
205 // 3. Otherwise, if base’s scheme is not "file", set state to relative state and decrease pointer by 1.225 // 3. Otherwise, if base’s scheme is not "file", set state to relative state and decrease pointer by 1.
226 else if (!std.mem.eql(u8, base.?.scheme(), "file")) {
227 state = .relative;
228 if (pointer == 0) continue; // pointer goes to -1 then +1'd
229 pointer -= 1;
230 i = lastcpi(inputl.items[0..i]);
231 c = inputl.items[i];
232 }
206 // 4. Otherwise, set state to file state and decrease pointer by 1.233 // 4. Otherwise, set state to file state and decrease pointer by 1.
207 return error.InvalidURL;234 else {
235 state = .file;
236 if (pointer == 0) continue; // pointer goes to -1 then +1'd
237 pointer -= 1;
238 i = lastcpi(inputl.items[0..i]);
239 c = inputl.items[i];
240 }
208 },241 },
209 .special_relative_or_authority => {242 .special_relative_or_authority => {
210 // 1. If c is U+002F (/) and remaining starts with U+002F (/), then set state to special authority ignore slashes state and increase pointer by 1.243 // 1. If c is U+002F (/) and remaining starts with U+002F (/), then set state to special authority ignore slashes state and increase pointer by 1.
...@@ -217,7 +250,9 @@ pub const URL = struct {...@@ -217,7 +250,9 @@ pub const URL = struct {
217 // 2. Otherwise, special-scheme-missing-following-solidus validation error, set state to relative state and decrease pointer by 1.250 // 2. Otherwise, special-scheme-missing-following-solidus validation error, set state to relative state and decrease pointer by 1.
218 else {251 else {
219 state = .relative;252 state = .relative;
220 @panic("TODO");253 pointer -= 1;
254 i = lastcpi(inputl.items[0..i]);
255 c = inputl.items[i];
221 }256 }
222 },257 },
223 .path_or_authority => {258 .path_or_authority => {
...@@ -231,7 +266,6 @@ pub const URL = struct {...@@ -231,7 +266,6 @@ pub const URL = struct {
231 pointer -= 1;266 pointer -= 1;
232 i = lastcpi(inputl.items[0..i]);267 i = lastcpi(inputl.items[0..i]);
233 c = inputl.items[i];268 c = inputl.items[i];
234 try href.appendSlice(10, "/");
235 }269 }
236 },270 },
237 .relative => {271 .relative => {
...@@ -251,12 +285,14 @@ pub const URL = struct {...@@ -251,12 +285,14 @@ pub const URL = struct {
251 // 5. Otherwise:285 // 5. Otherwise:
252 else {286 else {
253 // 1. Set url’s username to base’s username, url’s password to base’s password, url’s host to base’s host, url’s port to base’s port, url’s path to a clone of base’s path, and url’s query to base’s query.287 // 1. Set url’s username to base’s username, url’s password to base’s password, url’s host to base’s host, url’s port to base’s port, url’s path to a clone of base’s path, and url’s query to base’s query.
254 // try username.appendSlice(base.?.username);288 try href.set(3, base.?.username);
255 // try password.appendSlice(base.?.password);289 try href.set(5, base.?.password);
256 // host = base.?.host;290 try href.set(7, base.?.hostname);
257 // try port.writer().print("{d}", .{base.?.port.?});291 hostname_kind = base.?.hostname_kind;
258 // try path.appendSlice(base.?.path);292 try href.set(9, base.?.port);
259 // try query.appendSlice(base.?.query.?);293 try href.set(10, base.?.pathname);
294 has_opaque_path = base.?.has_opaque_path;
295 try href.set(12, base.?.query());
260 // 2. If c is U+003F (?), then set url’s query to the empty string, and state to query state.296 // 2. If c is U+003F (?), then set url’s query to the empty string, and state to query state.
261 if (c == '?') {297 if (c == '?') {
262 href.clear(12);298 href.clear(12);
...@@ -274,12 +310,13 @@ pub const URL = struct {...@@ -274,12 +310,13 @@ pub const URL = struct {
274 // 1. Set url’s query to null.310 // 1. Set url’s query to null.
275 href.clear(12);311 href.clear(12);
276 // 2. Shorten url’s path.312 // 2. Shorten url’s path.
277 @panic("TODO");313 shortenUrlPath(&href, has_opaque_path);
278 // 3. Set state to path state and decrease pointer by 1.314 // 3. Set state to path state and decrease pointer by 1.
279 // state = .path;315 state = .path;
280 // pointer -= 1;316 if (pointer == 0) continue; // pointer goes to -1 then +1'd
281 // i = lastcpi(inputl.items[0..i]);317 pointer -= 1;
282 // c = inputl.items[i];318 i = lastcpi(inputl.items[0..i]);
319 c = inputl.items[i];
283 }320 }
284 }321 }
285 },322 },
...@@ -296,15 +333,16 @@ pub const URL = struct {...@@ -296,15 +333,16 @@ pub const URL = struct {
296 }333 }
297 // 3. Otherwise, set url’s username to base’s username, url’s password to base’s password, url’s host to base’s host, url’s port to base’s port, state to path state, and then, decrease pointer by 1.334 // 3. Otherwise, set url’s username to base’s username, url’s password to base’s password, url’s host to base’s host, url’s port to base’s port, state to path state, and then, decrease pointer by 1.
298 else {335 else {
299 // try username.appendSlice(base.?.username);336 try href.set(3, base.?.username);
300 // try password.appendSlice(base.?.password);337 try href.set(5, base.?.password);
301 // host = base.?.host;338 try href.set(7, base.?.hostname);
302 // try port.writer().print("{d}", .{base.?.port.?});339 hostname_kind = base.?.hostname_kind;
340 try href.set(9, base.?.port);
303 state = .path;341 state = .path;
342 if (pointer == 0) continue; // pointer goes to -1 then +1'd
304 pointer -= 1;343 pointer -= 1;
305 i = lastcpi(inputl.items[0..i]);344 i = lastcpi(inputl.items[0..i]);
306 c = inputl.items[i];345 c = inputl.items[i];
307 @panic("TODO");
308 }346 }
309 },347 },
310 .special_authority_slashes => {348 .special_authority_slashes => {
...@@ -340,6 +378,7 @@ pub const URL = struct {...@@ -340,6 +378,7 @@ pub const URL = struct {
340 // 1. If c is U+0040 (@), then:378 // 1. If c is U+0040 (@), then:
341 if (c == '@') {379 if (c == '@') {
342 // 1. Invalid-credentials validation error.380 // 1. Invalid-credentials validation error.
381 {}
343 // 2. If atSignSeen is true, then prepend "%40" to buffer.382 // 2. If atSignSeen is true, then prepend "%40" to buffer.
344 if (atSignSeen) try buffer.insertSlice(0, "%40");383 if (atSignSeen) try buffer.insertSlice(0, "%40");
345 // 3. Set atSignSeen to true.384 // 3. Set atSignSeen to true.
...@@ -496,9 +535,11 @@ pub const URL = struct {...@@ -496,9 +535,11 @@ pub const URL = struct {
496 // 4. Otherwise, if base is non-null and base’s scheme is "file":535 // 4. Otherwise, if base is non-null and base’s scheme is "file":
497 else if (base != null and std.mem.eql(u8, base.?.scheme(), "file")) {536 else if (base != null and std.mem.eql(u8, base.?.scheme(), "file")) {
498 // 1. Set url’s host to base’s host, url’s path to a clone of base’s path, and url’s query to base’s query.537 // 1. Set url’s host to base’s host, url’s path to a clone of base’s path, and url’s query to base’s query.
499 // host = base.?.host;538 try href.set(7, base.?.hostname);
500 // try path.appendSlice(base.?.path.?);539 hostname_kind = base.?.hostname_kind;
501 // try query.appendSlice(base.?.query.?);540 try href.set(10, base.?.pathname);
541 has_opaque_path = base.?.has_opaque_path;
542 try href.set(12, base.?.query());
502 // 2. If c is U+003F (?), then set url’s query to the empty string and state to query state.543 // 2. If c is U+003F (?), then set url’s query to the empty string and state to query state.
503 if (c == '?') {544 if (c == '?') {
504 href.clear(12);545 href.clear(12);
...@@ -516,15 +557,20 @@ pub const URL = struct {...@@ -516,15 +557,20 @@ pub const URL = struct {
516 // 1. Set url’s query to null.557 // 1. Set url’s query to null.
517 href.clear(12);558 href.clear(12);
518 // 2. If the code point substring from pointer to the end of input does not start with a Windows drive letter, then shorten url’s path.559 // 2. If the code point substring from pointer to the end of input does not start with a Windows drive letter, then shorten url’s path.
560 if (!startsWithWindowsDriveLetter(inputl.items[i..])) {
561 shortenUrlPath(&href, has_opaque_path);
562 }
519 // 3. Otherwise:563 // 3. Otherwise:
520 // This is a (platform-independent) Windows drive letter quirk.564 // This is a (platform-independent) Windows drive letter quirk.
521 if (builtin.target.os.tag == .windows) @compileError("TODO");565 else {
522 {
523 // 1. File-invalid-Windows-drive-letter validation error.566 // 1. File-invalid-Windows-drive-letter validation error.
567 {}
524 // 2. Set url’s path to « ».568 // 2. Set url’s path to « ».
569 href.clear(10);
525 }570 }
526 // 4. Set state to path state and decrease pointer by 1.571 // 4. Set state to path state and decrease pointer by 1.
527 state = .path;572 state = .path;
573 if (pointer == 0) continue; // pointer goes to -1 then +1'd
528 pointer -= 1;574 pointer -= 1;
529 i = lastcpi(inputl.items[0..i]);575 i = lastcpi(inputl.items[0..i]);
530 c = inputl.items[i];576 c = inputl.items[i];
...@@ -536,7 +582,6 @@ pub const URL = struct {...@@ -536,7 +582,6 @@ pub const URL = struct {
536 pointer -= 1;582 pointer -= 1;
537 i = lastcpi(inputl.items[0..i]);583 i = lastcpi(inputl.items[0..i]);
538 c = inputl.items[i];584 c = inputl.items[i];
539 try href.appendSlice(10, "/");
540 }585 }
541 },586 },
542 .file_slash => {587 .file_slash => {
...@@ -552,17 +597,24 @@ pub const URL = struct {...@@ -552,17 +597,24 @@ pub const URL = struct {
552 // 1. If base is non-null and base’s scheme is "file", then:597 // 1. If base is non-null and base’s scheme is "file", then:
553 if (base != null and std.mem.eql(u8, base.?.scheme(), "file")) {598 if (base != null and std.mem.eql(u8, base.?.scheme(), "file")) {
554 // 1. Set url’s host to base’s host.599 // 1. Set url’s host to base’s host.
555 // host = base.?.host;600 try href.set(7, base.?.hostname);
601 hostname_kind = base.?.hostname_kind;
556 // 2. If the code point substring from pointer to the end of input does not start with a Windows drive letter and base’s path[0] is a normalized Windows drive letter, then append base’s path[0] to url’s path.602 // 2. If the code point substring from pointer to the end of input does not start with a Windows drive letter and base’s path[0] is a normalized Windows drive letter, then append base’s path[0] to url’s path.
557 // > This is a (platform-independent) Windows drive letter quirk.603 // > This is a (platform-independent) Windows drive letter quirk.
558 if (builtin.target.os.tag == .windows) @compileError("TODO");604 if (!startsWithWindowsDriveLetter(inputl.items[i..])) {
605 const base_path0 = nthScalarItem(u8, base.?.pathname, '/', 1);
606 if (isNormalizedWindowsDriveLetter(base_path0)) {
607 try href.appendSlice(10, "/");
608 try href.appendSlice(10, base_path0);
609 }
610 }
559 }611 }
560 // 2. Set state to path state, and decrease pointer by 1.612 // 2. Set state to path state, and decrease pointer by 1.
561 state = .path;613 state = .path;
614 if (pointer == 0) continue; // pointer goes to -1 then +1'd
562 pointer -= 1;615 pointer -= 1;
563 i = lastcpi(inputl.items[0..i]);616 i = lastcpi(inputl.items[0..i]);
564 c = inputl.items[i];617 c = inputl.items[i];
565 try href.appendSlice(10, "/");
566 }618 }
567 },619 },
568 .file_host => {620 .file_host => {
...@@ -575,7 +627,6 @@ pub const URL = struct {...@@ -575,7 +627,6 @@ pub const URL = struct {
575 // > This is a (platform-independent) Windows drive letter quirk. buffer is not reset here and instead used in the path state.627 // > This is a (platform-independent) Windows drive letter quirk. buffer is not reset here and instead used in the path state.
576 if (state_override == null and isWindowsDriveLetter(buffer.items)) {628 if (state_override == null and isWindowsDriveLetter(buffer.items)) {
577 state = .path;629 state = .path;
578 try href.appendSlice(10, "/");
579 }630 }
580 // 2. Otherwise, if buffer is the empty string, then:631 // 2. Otherwise, if buffer is the empty string, then:
581 else if (buffer.items.len == 0) {632 else if (buffer.items.len == 0) {
...@@ -626,7 +677,6 @@ pub const URL = struct {...@@ -626,7 +677,6 @@ pub const URL = struct {
626 i = lastcpi(inputl.items[0..i]);677 i = lastcpi(inputl.items[0..i]);
627 c = inputl.items[i];678 c = inputl.items[i];
628 }679 }
629 try href.appendSlice(10, "/");
630 }680 }
631 // 2. Otherwise, if state override is not given and c is U+003F (?), set url’s query to the empty string and state to query state.681 // 2. Otherwise, if state override is not given and c is U+003F (?), set url’s query to the empty string and state to query state.
632 else if (state_override == null and c == '?') {682 else if (state_override == null and c == '?') {
...@@ -649,8 +699,6 @@ pub const URL = struct {...@@ -649,8 +699,6 @@ pub const URL = struct {
649 pointer -= 1;699 pointer -= 1;
650 i = lastcpi(inputl.items[0..i]);700 i = lastcpi(inputl.items[0..i]);
651 c = inputl.items[i];701 c = inputl.items[i];
652 } else {
653 try href.appendSlice(10, &.{c});
654 }702 }
655 }703 }
656 // 5. Otherwise, if state override is given and url’s host is null, append the empty string to url’s path.704 // 5. Otherwise, if state override is given and url’s host is null, append the empty string to url’s path.
...@@ -671,29 +719,28 @@ pub const URL = struct {...@@ -671,29 +719,28 @@ pub const URL = struct {
671 {}719 {}
672 // 2. If buffer is a double-dot URL path segment, then:720 // 2. If buffer is a double-dot URL path segment, then:
673 if (isDoubleDotPathSeg(buffer.items)) {721 if (isDoubleDotPathSeg(buffer.items)) {
674 const olen = href.lengths[10];
675 const idx = std.mem.lastIndexOfScalar(u8, href.items(10)[0 .. olen - 1], '/') orelse 0;
676 try href.replace(10, idx + 1, olen - idx - 1, "");
677 // 1. Shorten url’s path.722 // 1. Shorten url’s path.
723 shortenUrlPath(&href, has_opaque_path);
678 // 2. If neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path.724 // 2. If neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path.
679 // > This means that for input /usr/.. the result is / and not a lack of a path.725 // > This means that for input /usr/.. the result is / and not a lack of a path.
726 if (!is_lsep and !is_rsep) {
727 try href.appendSlice(10, "/");
728 }
680 }729 }
681 // 3. Otherwise, if buffer is a single-dot URL path segment and if neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path.730 // 3. Otherwise, if buffer is a single-dot URL path segment and if neither c is U+002F (/), nor url is special and c is U+005C (\), append the empty string to url’s path.
682 else if (isSingleDotPathSeg(buffer.items) and (c != '/' or !(is_rsep))) {731 else if (isSingleDotPathSeg(buffer.items) and !is_lsep and !is_rsep) {
683 // =no action needed732 try href.appendSlice(10, "/");
684 }733 }
685 // 4. Otherwise, if buffer is not a single-dot URL path segment, then:734 // 4. Otherwise, if buffer is not a single-dot URL path segment, then:
686 else if (!isSingleDotPathSeg(buffer.items)) {735 else if (!isSingleDotPathSeg(buffer.items)) {
687 // 1. If url’s scheme is "file", url’s path is empty, and buffer is a Windows drive letter, then replace the second code point in buffer with U+003A (:).736 // 1. If url’s scheme is "file", url’s path is empty, and buffer is a Windows drive letter, then replace the second code point in buffer with U+003A (:).
688 // > This is a (platform-independent) Windows drive letter quirk.737 // > This is a (platform-independent) Windows drive letter quirk.
689 if (std.mem.eql(u8, href.items(0), "file") and href.lengths[10] <= 1 and isWindowsDriveLetter(buffer.items)) {738 if (std.mem.eql(u8, href.items(0), "file") and href.lengths[10] == 0 and isWindowsDriveLetter(buffer.items)) {
690 buffer.items[1] = ':';739 buffer.items[1] = ':';
691 }740 }
692 // 2. Append buffer to url’s path.741 // 2. Append buffer to url’s path.
693 // if (!std.mem.endsWith(u8, href.items(10), "/")) try href.appendSlice(10, "/");742 try href.appendSlice(10, "/");
694 try href.appendSlice(10, buffer.items);743 try href.appendSlice(10, buffer.items);
695 if (is_lsep) try href.appendSlice(10, "/");
696 if (is_rsep) try href.appendSlice(10, "/");
697 }744 }
698 // 5. Set buffer to the empty string.745 // 5. Set buffer to the empty string.
699 buffer.clearRetainingCapacity();746 buffer.clearRetainingCapacity();
...@@ -766,6 +813,7 @@ pub const URL = struct {...@@ -766,6 +813,7 @@ pub const URL = struct {
766 // - url is not special813 // - url is not special
767 // - url’s scheme is "ws" or "wss"814 // - url’s scheme is "ws" or "wss"
768 // then set encoding to UTF-8.815 // then set encoding to UTF-8.
816 {}
769 // 2. If one of the following is true:817 // 2. If one of the following is true:
770 // - state override is not given and c is U+0023 (#)818 // - state override is not given and c is U+0023 (#)
771 // - c is the EOF code point819 // - c is the EOF code point
...@@ -812,14 +860,10 @@ pub const URL = struct {...@@ -812,14 +860,10 @@ pub const URL = struct {
812 }860 }
813861
814 // If after a run pointer points to the EOF code point, go to the next step. Otherwise, increase pointer by 1 and continue with the state machine.862 // If after a run pointer points to the EOF code point, go to the next step. Otherwise, increase pointer by 1 and continue with the state machine.
815 if (i == length) {863 if (i == length) break;
816 if (state == .fragment) break;864 i += l(c);
817 state = @enumFromInt(@intFromEnum(state) + 1);865 c = if (i < length) inputl.items[i] else 0;
818 } else {866 pointer += 1;
819 i += l(c);
820 c = if (i < length) inputl.items[i] else 0;
821 pointer += 1;
822 }
823 }867 }
824868
825 if (hostname_kind != .unset) {869 if (hostname_kind != .unset) {
...@@ -834,6 +878,12 @@ pub const URL = struct {...@@ -834,6 +878,12 @@ pub const URL = struct {
834 if (href.lengths[9] > 0) {878 if (href.lengths[9] > 0) {
835 try href.set(8, ":");879 try href.set(8, ":");
836 }880 }
881 if (href.lengths[12] > 0) {
882 try href.set(11, "?");
883 }
884 if (href.lengths[14] > 0) {
885 try href.set(13, "#");
886 }
837887
838 var path_offset: usize = 0;888 var path_offset: usize = 0;
839 if (hostname_kind == .unset and std.mem.startsWith(u8, href.items(10), "//")) {889 if (hostname_kind == .unset and std.mem.startsWith(u8, href.items(10), "//")) {
...@@ -855,6 +905,7 @@ pub const URL = struct {...@@ -855,6 +905,7 @@ pub const URL = struct {
855 .pathname = _href[extras.sum(usize, href.lengths[0..10])..][0..href.lengths[10]][path_offset..],905 .pathname = _href[extras.sum(usize, href.lengths[0..10])..][0..href.lengths[10]][path_offset..],
856 .search = if (href.lengths[12] == 0) "" else _href[extras.sum(usize, href.lengths[0..11])..][0..extras.sum(usize, href.lengths[11..][0..2])],906 .search = if (href.lengths[12] == 0) "" else _href[extras.sum(usize, href.lengths[0..11])..][0..extras.sum(usize, href.lengths[11..][0..2])],
857 .hash = if (href.lengths[14] == 0) "" else _href[extras.sum(usize, href.lengths[0..13])..][0..extras.sum(usize, href.lengths[13..][0..2])],907 .hash = if (href.lengths[14] == 0) "" else _href[extras.sum(usize, href.lengths[0..13])..][0..extras.sum(usize, href.lengths[13..][0..2])],
908 .has_opaque_path = has_opaque_path,
858 };909 };
859 return url;910 return url;
860 }911 }
...@@ -890,6 +941,16 @@ pub const URL = struct {...@@ -890,6 +941,16 @@ pub const URL = struct {
890 pub fn scheme(u: *const URL) []const u8 {941 pub fn scheme(u: *const URL) []const u8 {
891 return u.protocol[0 .. u.protocol.len - 1];942 return u.protocol[0 .. u.protocol.len - 1];
892 }943 }
944
945 pub fn query(u: *const URL) []const u8 {
946 if (u.search.len == 0) return "";
947 return u.search[1..]; // search includes '?'
948 }
949
950 pub fn fragment(u: *const URL) []const u8 {
951 if (u.hash.len == 0) return "";
952 return u.hash[1..]; // hash includes '#'
953 }
893};954};
894955
895/// https://url.spec.whatwg.org/#special-scheme956/// https://url.spec.whatwg.org/#special-scheme
...@@ -938,6 +999,24 @@ fn isWindowsDriveLetter(buffer: []const u8) bool {...@@ -938,6 +999,24 @@ fn isWindowsDriveLetter(buffer: []const u8) bool {
938 return true;999 return true;
939}1000}
9401001
1002/// https://url.spec.whatwg.org/#normalized-windows-drive-letter
1003fn isNormalizedWindowsDriveLetter(buffer: []const u8) bool {
1004 if (buffer.len != 2) return false;
1005 if (!std.ascii.isAlphabetic(buffer[0])) return false;
1006 if (!(buffer[1] == ':')) return false;
1007 return true;
1008}
1009
1010/// https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
1011fn startsWithWindowsDriveLetter(buffer: []const u8) bool {
1012 if (buffer.len < 2) return false;
1013 if (!std.ascii.isAlphabetic(buffer[0])) return false;
1014 if (!(buffer[1] == ':' or buffer[1] == '|')) return false;
1015 if (buffer.len == 2) return true;
1016 if (!(buffer[2] == '/' or buffer[2] == '\\' or buffer[2] == '?' or buffer[2] == '#')) return false;
1017 return true;
1018}
1019
941/// https://url.spec.whatwg.org/#concept-host-parser1020/// https://url.spec.whatwg.org/#concept-host-parser
942fn parseHost(allocator: std.mem.Allocator, input: []u8, isOpaque: bool) !URL.Host {1021fn parseHost(allocator: std.mem.Allocator, input: []u8, isOpaque: bool) !URL.Host {
943 // 1. If input starts with U+005B ([), then:1022 // 1. If input starts with U+005B ([), then:
...@@ -963,7 +1042,7 @@ fn parseHost(allocator: std.mem.Allocator, input: []u8, isOpaque: bool) !URL.Hos...@@ -963,7 +1042,7 @@ fn parseHost(allocator: std.mem.Allocator, input: []u8, isOpaque: bool) !URL.Hos
963 // 7. If asciiDomain ends in a number, then return the result of IPv4 parsing asciiDomain.1042 // 7. If asciiDomain ends in a number, then return the result of IPv4 parsing asciiDomain.
964 if (endsInANumber(asciidomain)) {1043 if (endsInANumber(asciidomain)) {
965 defer allocator.free(asciidomain);1044 defer allocator.free(asciidomain);
966 const adr = try parseIPv4(input);1045 const adr = try parseIPv4(asciidomain);
967 return .{ .ipv4 = adr };1046 return .{ .ipv4 = adr };
968 }1047 }
969 // 8. Return asciiDomain.1048 // 8. Return asciiDomain.
...@@ -1031,7 +1110,7 @@ fn parseIPv6(input: []const u8) !u128 {...@@ -1031,7 +1110,7 @@ fn parseIPv6(input: []const u8) !u128 {
1031 // 4. Let numbersSeen be 0.1110 // 4. Let numbersSeen be 0.
1032 var numbersSeen: usize = 0;1111 var numbersSeen: usize = 0;
1033 // 5. While c is not the EOF code point:1112 // 5. While c is not the EOF code point:
1034 if (pointer < input.len) {1113 while (pointer < input.len) {
1035 // 1. Let ipv4Piece be null.1114 // 1. Let ipv4Piece be null.
1036 var ipv4Piece: ?u16 = null;1115 var ipv4Piece: ?u16 = null;
1037 // 2. If numbersSeen is greater than 0, then:1116 // 2. If numbersSeen is greater than 0, then:
...@@ -1046,9 +1125,9 @@ fn parseIPv6(input: []const u8) !u128 {...@@ -1046,9 +1125,9 @@ fn parseIPv6(input: []const u8) !u128 {
1046 }1125 }
1047 }1126 }
1048 // 3. If c is not an ASCII digit, IPv4-in-IPv6-invalid-code-point validation error, return failure.1127 // 3. If c is not an ASCII digit, IPv4-in-IPv6-invalid-code-point validation error, return failure.
1049 if (!std.ascii.isDigit(input[pointer])) return error.InvalidURL;1128 if (pointer == input.len or !std.ascii.isDigit(input[pointer])) return error.InvalidURL;
1050 // 4. While c is an ASCII digit:1129 // 4. While c is an ASCII digit:
1051 while (std.ascii.isDigit(input[pointer])) {1130 while (pointer < input.len and std.ascii.isDigit(input[pointer])) {
1052 // 1. Let number be c interpreted as decimal number.1131 // 1. Let number be c interpreted as decimal number.
1053 const dec_alpha = "0123456789";1132 const dec_alpha = "0123456789";
1054 const number: u8 = @intCast(std.mem.indexOfScalar(u8, dec_alpha, input[pointer]).?);1133 const number: u8 = @intCast(std.mem.indexOfScalar(u8, dec_alpha, input[pointer]).?);
...@@ -1196,7 +1275,7 @@ fn endsInANumber(input: []const u8) bool {...@@ -1196,7 +1275,7 @@ fn endsInANumber(input: []const u8) bool {
1196 var end = input.len;1275 var end = input.len;
1197 var start: usize = if (std.mem.lastIndexOfScalar(u8, input[0..end], '.')) |i| i + 1 else 0;1276 var start: usize = if (std.mem.lastIndexOfScalar(u8, input[0..end], '.')) |i| i + 1 else 0;
1198 if (end - start == 0) {1277 if (end - start == 0) {
1199 if (std.mem.count(u8, input, ".") == 0) return false;1278 if (extras.countScalar(u8, input, '.') == 0) return false;
1200 end = start - 1;1279 end = start - 1;
1201 start = if (std.mem.lastIndexOfScalar(u8, input[0..end], '.')) |i| i + 1 else 0;1280 start = if (std.mem.lastIndexOfScalar(u8, input[0..end], '.')) |i| i + 1 else 0;
1202 }1281 }
...@@ -1210,7 +1289,7 @@ fn endsInANumber(input: []const u8) bool {...@@ -1210,7 +1289,7 @@ fn endsInANumber(input: []const u8) bool {
1210fn parseIPv4(input: []const u8) !u32 {1289fn parseIPv4(input: []const u8) !u32 {
1211 // 1. Let parts be the result of strictly splitting input on U+002E (.).1290 // 1. Let parts be the result of strictly splitting input on U+002E (.).
1212 var end = input.len;1291 var end = input.len;
1213 var parts_size = std.mem.count(u8, input[0..end], ".") + 1;1292 var parts_size = extras.countScalar(u8, input[0..end], '.') + 1;
1214 // 2. If the last item in parts is the empty string, then:1293 // 2. If the last item in parts is the empty string, then:
1215 if (std.mem.endsWith(u8, input, ".")) {1294 if (std.mem.endsWith(u8, input, ".")) {
1216 // 1. IPv4-empty-part validation error.1295 // 1. IPv4-empty-part validation error.
...@@ -1305,6 +1384,21 @@ fn parseIPv4Number(input_: []const u8, T: type) !T {...@@ -1305,6 +1384,21 @@ fn parseIPv4Number(input_: []const u8, T: type) !T {
1305 return output;1384 return output;
1306}1385}
13071386
1387/// https://url.spec.whatwg.org/#shorten-a-urls-path
1388fn shortenUrlPath(url: *ManyArrayList(15, u8), has_opaque_path: bool) void {
1389 // 1. Assert: url does not have an opaque path.
1390 std.debug.assert(!has_opaque_path);
1391 // 2. Let path be url’s path.
1392 const path = url.items(10);
1393 // 3. If url’s scheme is "file", path’s size is 1, and path[0] is a normalized Windows drive letter, then return.
1394 if (std.mem.eql(u8, url.items(0), "file") and extras.countScalar(u8, path, '/') == 1 and isNormalizedWindowsDriveLetter(nthScalarItem(u8, path, '/', 1))) {
1395 return;
1396 }
1397 // 4. Remove path’s last item, if any.
1398 const new_len = std.mem.lastIndexOfScalar(u8, path, '/') orelse return;
1399 url.replace(10, new_len, path.len - new_len, "") catch unreachable;
1400}
1401
1308//1402//
1309//1403//
1310//1404//
...@@ -1562,6 +1656,12 @@ pub fn replaceInPlace(comptime T: type, input: []T, needle: []const T, replaceme...@@ -1562,6 +1656,12 @@ pub fn replaceInPlace(comptime T: type, input: []T, needle: []const T, replaceme
1562 }1656 }
1563 return replacements;1657 return replacements;
1564}1658}
1659fn nthScalarItem(T: type, haystack: []const u8, needle: T, index: usize) []const T {
1660 var it = std.mem.splitScalar(T, haystack, needle);
1661 var idx: usize = 0;
1662 while (idx < index) : (idx += 1) _ = it.next().?;
1663 return it.next().?;
1664}
15651665
1566pub fn ManyArrayList(N: usize, T: type) type {1666pub fn ManyArrayList(N: usize, T: type) type {
1567 return struct {1667 return struct {
zig-out/test.zig+268
...@@ -658,6 +658,274 @@ test { try parsePass("non-special:\\/opaque", null, "non-special:\\/opaque", "nu...@@ -658,6 +658,274 @@ test { try parsePass("non-special:\\/opaque", null, "non-special:\\/opaque", "nu
658test { try parsePass("non-special:/\\path", null, "non-special:/\\path", "null", "non-special:", "", "", "", "", "", "/\\path", "", ""); }658test { try parsePass("non-special:/\\path", null, "non-special:/\\path", "null", "non-special:", "", "", "", "", "", "/\\path", "", ""); }
659test { try parsePass("non-special://host/a\\b", null, "non-special://host/a\\b", "null", "non-special:", "", "", "host", "host", "", "/a\\b", "", ""); }659test { try parsePass("non-special://host/a\\b", null, "non-special://host/a\\b", "null", "non-special:", "", "", "host", "host", "", "/a\\b", "", ""); }
660660
661test { try parsePass("http://example\t.\norg", "http://example.org/foo/bar", "http://example.org/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/", "", ""); }
662test { try parsePass("http://user:pass@foo:21/bar;par?b#c", "http://example.org/foo/bar", "http://user:pass@foo:21/bar;par?b#c", "http://foo:21", "http:", "user", "pass", "foo:21", "foo", "21", "/bar;par", "?b", "#c"); }
663test { try parsePass("http:foo.com", "http://example.org/foo/bar", "http://example.org/foo/foo.com", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/foo.com", "", ""); }
664test { try parsePass("\t :foo.com \n", "http://example.org/foo/bar", "http://example.org/foo/:foo.com", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:foo.com", "", ""); }
665test { try parsePass(" foo.com ", "http://example.org/foo/bar", "http://example.org/foo/foo.com", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/foo.com", "", ""); }
666test { try parsePass("a:\t foo.com", "http://example.org/foo/bar", "a: foo.com", "null", "a:", "", "", "", "", "", " foo.com", "", ""); }
667test { try parsePass("http://f:21/ b ? d # e ", "http://example.org/foo/bar", "http://f:21/%20b%20?%20d%20#%20e", "http://f:21", "http:", "", "", "f:21", "f", "21", "/%20b%20", "?%20d%20", "#%20e"); }
668test { try parsePass("http://f:/c", "http://example.org/foo/bar", "http://f/c", "http://f", "http:", "", "", "f", "f", "", "/c", "", ""); }
669test { try parsePass("http://f:0/c", "http://example.org/foo/bar", "http://f:0/c", "http://f:0", "http:", "", "", "f:0", "f", "0", "/c", "", ""); }
670test { try parsePass("http://f:00000000000000/c", "http://example.org/foo/bar", "http://f:0/c", "http://f:0", "http:", "", "", "f:0", "f", "0", "/c", "", ""); }
671test { try parsePass("http://f:00000000000000000000080/c", "http://example.org/foo/bar", "http://f/c", "http://f", "http:", "", "", "f", "f", "", "/c", "", ""); }
672test { try parsePass("http://f:\n/c", "http://example.org/foo/bar", "http://f/c", "http://f", "http:", "", "", "f", "f", "", "/c", "", ""); }
673test { try parsePass("", "http://example.org/foo/bar", "http://example.org/foo/bar", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", ""); }
674test { try parsePass(" \t", "http://example.org/foo/bar", "http://example.org/foo/bar", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", ""); }
675test { try parsePass(":foo.com/", "http://example.org/foo/bar", "http://example.org/foo/:foo.com/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:foo.com/", "", ""); }
676test { try parsePass(":foo.com\\", "http://example.org/foo/bar", "http://example.org/foo/:foo.com/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:foo.com/", "", ""); }
677test { try parsePass(":", "http://example.org/foo/bar", "http://example.org/foo/:", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:", "", ""); }
678test { try parsePass(":a", "http://example.org/foo/bar", "http://example.org/foo/:a", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:a", "", ""); }
679test { try parsePass(":/", "http://example.org/foo/bar", "http://example.org/foo/:/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:/", "", ""); }
680test { try parsePass(":\\", "http://example.org/foo/bar", "http://example.org/foo/:/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:/", "", ""); }
681test { try parsePass(":#", "http://example.org/foo/bar", "http://example.org/foo/:#", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:", "", ""); }
682test { try parsePass("#", "http://example.org/foo/bar", "http://example.org/foo/bar#", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", ""); }
683test { try parsePass("#/", "http://example.org/foo/bar", "http://example.org/foo/bar#/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", "#/"); }
684test { try parsePass("#\\", "http://example.org/foo/bar", "http://example.org/foo/bar#\\", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", "#\\"); }
685test { try parsePass("#;?", "http://example.org/foo/bar", "http://example.org/foo/bar#;?", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", "#;?"); }
686test { try parsePass("?", "http://example.org/foo/bar", "http://example.org/foo/bar?", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", ""); }
687test { try parsePass("/", "http://example.org/foo/bar", "http://example.org/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/", "", ""); }
688test { try parsePass(":23", "http://example.org/foo/bar", "http://example.org/foo/:23", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:23", "", ""); }
689test { try parsePass("/:23", "http://example.org/foo/bar", "http://example.org/:23", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/:23", "", ""); }
690test { try parsePass("\\x", "http://example.org/foo/bar", "http://example.org/x", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/x", "", ""); }
691test { try parsePass("\\\\x\\hello", "http://example.org/foo/bar", "http://x/hello", "http://x", "http:", "", "", "x", "x", "", "/hello", "", ""); }
692test { try parsePass("::", "http://example.org/foo/bar", "http://example.org/foo/::", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/::", "", ""); }
693test { try parsePass("::23", "http://example.org/foo/bar", "http://example.org/foo/::23", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/::23", "", ""); }
694test { try parsePass("foo://", "http://example.org/foo/bar", "foo://", "null", "foo:", "", "", "", "", "", "", "", ""); }
695test { try parsePass("http://a:b@c:29/d", "http://example.org/foo/bar", "http://a:b@c:29/d", "http://c:29", "http:", "a", "b", "c:29", "c", "29", "/d", "", ""); }
696test { try parsePass("http::@c:29", "http://example.org/foo/bar", "http://example.org/foo/:@c:29", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/:@c:29", "", ""); }
697test { try parsePass("http://&a:foo(b]c@d:2/", "http://example.org/foo/bar", "http://&a:foo(b%5Dc@d:2/", "http://d:2", "http:", "&a", "foo(b%5Dc", "d:2", "d", "2", "/", "", ""); }
698test { try parsePass("http://::@c@d:2", "http://example.org/foo/bar", "http://:%3A%40c@d:2/", "http://d:2", "http:", "", "%3A%40c", "d:2", "d", "2", "/", "", ""); }
699test { try parsePass("http://foo.com:b@d/", "http://example.org/foo/bar", "http://foo.com:b@d/", "http://d", "http:", "foo.com", "b", "d", "d", "", "/", "", ""); }
700test { try parsePass("http://foo.com/\\@", "http://example.org/foo/bar", "http://foo.com//@", "http://foo.com", "http:", "", "", "foo.com", "foo.com", "", "//@", "", ""); }
701test { try parsePass("http:\\\\foo.com\\", "http://example.org/foo/bar", "http://foo.com/", "http://foo.com", "http:", "", "", "foo.com", "foo.com", "", "/", "", ""); }
702test { try parsePass("http:\\\\a\\b:c\\d@foo.com\\", "http://example.org/foo/bar", "http://a/b:c/d@foo.com/", "http://a", "http:", "", "", "a", "a", "", "/b:c/d@foo.com/", "", ""); }
703test { try parsePass("foo:/", "http://example.org/foo/bar", "foo:/", "null", "foo:", "", "", "", "", "", "/", "", ""); }
704test { try parsePass("foo:/bar.com/", "http://example.org/foo/bar", "foo:/bar.com/", "null", "foo:", "", "", "", "", "", "/bar.com/", "", ""); }
705test { try parsePass("foo://///////", "http://example.org/foo/bar", "foo://///////", "null", "foo:", "", "", "", "", "", "///////", "", ""); }
706test { try parsePass("foo://///////bar.com/", "http://example.org/foo/bar", "foo://///////bar.com/", "null", "foo:", "", "", "", "", "", "///////bar.com/", "", ""); }
707test { try parsePass("foo:////://///", "http://example.org/foo/bar", "foo:////://///", "null", "foo:", "", "", "", "", "", "//://///", "", ""); }
708test { try parsePass("c:/foo", "http://example.org/foo/bar", "c:/foo", "null", "c:", "", "", "", "", "", "/foo", "", ""); }
709test { try parsePass("//foo/bar", "http://example.org/foo/bar", "http://foo/bar", "http://foo", "http:", "", "", "foo", "foo", "", "/bar", "", ""); }
710test { try parsePass("http://foo/path;a??e#f#g", "http://example.org/foo/bar", "http://foo/path;a??e#f#g", "http://foo", "http:", "", "", "foo", "foo", "", "/path;a", "??e", "#f#g"); }
711test { try parsePass("http://foo/abcd?efgh?ijkl", "http://example.org/foo/bar", "http://foo/abcd?efgh?ijkl", "http://foo", "http:", "", "", "foo", "foo", "", "/abcd", "?efgh?ijkl", ""); }
712test { try parsePass("http://foo/abcd#foo?bar", "http://example.org/foo/bar", "http://foo/abcd#foo?bar", "http://foo", "http:", "", "", "foo", "foo", "", "/abcd", "", "#foo?bar"); }
713test { try parsePass("[61:24:74]:98", "http://example.org/foo/bar", "http://example.org/foo/[61:24:74]:98", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/[61:24:74]:98", "", ""); }
714test { try parsePass("http:[61:27]/:foo", "http://example.org/foo/bar", "http://example.org/foo/[61:27]/:foo", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/[61:27]/:foo", "", ""); }
715test { try parsePass("http://[2001::1]", "http://example.org/foo/bar", "http://[2001::1]/", "http://[2001::1]", "http:", "", "", "[2001::1]", "[2001::1]", "", "/", "", ""); }
716test { try parsePass("http://[::127.0.0.1]", "http://example.org/foo/bar", "http://[::7f00:1]/", "http://[::7f00:1]", "http:", "", "", "[::7f00:1]", "[::7f00:1]", "", "/", "", ""); }
717test { try parsePass("http://[0:0:0:0:0:0:13.1.68.3]", "http://example.org/foo/bar", "http://[::d01:4403]/", "http://[::d01:4403]", "http:", "", "", "[::d01:4403]", "[::d01:4403]", "", "/", "", ""); }
718test { try parsePass("http://[2001::1]:80", "http://example.org/foo/bar", "http://[2001::1]/", "http://[2001::1]", "http:", "", "", "[2001::1]", "[2001::1]", "", "/", "", ""); }
719test { try parsePass("http:/example.com/", "http://example.org/foo/bar", "http://example.org/example.com/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/example.com/", "", ""); }
720test { try parsePass("ftp:/example.com/", "http://example.org/foo/bar", "ftp://example.com/", "ftp://example.com", "ftp:", "", "", "example.com", "example.com", "", "/", "", ""); }
721test { try parsePass("https:/example.com/", "http://example.org/foo/bar", "https://example.com/", "https://example.com", "https:", "", "", "example.com", "example.com", "", "/", "", ""); }
722test { try parsePass("madeupscheme:/example.com/", "http://example.org/foo/bar", "madeupscheme:/example.com/", "null", "madeupscheme:", "", "", "", "", "", "/example.com/", "", ""); }
723test { try parsePass("file:/example.com/", "http://example.org/foo/bar", "file:///example.com/", "", "file:", "", "", "", "", "", "/example.com/", "", ""); }
724test { try parsePass("ftps:/example.com/", "http://example.org/foo/bar", "ftps:/example.com/", "null", "ftps:", "", "", "", "", "", "/example.com/", "", ""); }
725test { try parsePass("gopher:/example.com/", "http://example.org/foo/bar", "gopher:/example.com/", "null", "gopher:", "", "", "", "", "", "/example.com/", "", ""); }
726test { try parsePass("ws:/example.com/", "http://example.org/foo/bar", "ws://example.com/", "ws://example.com", "ws:", "", "", "example.com", "example.com", "", "/", "", ""); }
727test { try parsePass("wss:/example.com/", "http://example.org/foo/bar", "wss://example.com/", "wss://example.com", "wss:", "", "", "example.com", "example.com", "", "/", "", ""); }
728test { try parsePass("data:/example.com/", "http://example.org/foo/bar", "data:/example.com/", "null", "data:", "", "", "", "", "", "/example.com/", "", ""); }
729test { try parsePass("javascript:/example.com/", "http://example.org/foo/bar", "javascript:/example.com/", "null", "javascript:", "", "", "", "", "", "/example.com/", "", ""); }
730test { try parsePass("mailto:/example.com/", "http://example.org/foo/bar", "mailto:/example.com/", "null", "mailto:", "", "", "", "", "", "/example.com/", "", ""); }
731test { try parsePass("http:example.com/", "http://example.org/foo/bar", "http://example.org/foo/example.com/", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/example.com/", "", ""); }
732test { try parsePass("ftp:example.com/", "http://example.org/foo/bar", "ftp://example.com/", "ftp://example.com", "ftp:", "", "", "example.com", "example.com", "", "/", "", ""); }
733test { try parsePass("https:example.com/", "http://example.org/foo/bar", "https://example.com/", "https://example.com", "https:", "", "", "example.com", "example.com", "", "/", "", ""); }
734test { try parsePass("madeupscheme:example.com/", "http://example.org/foo/bar", "madeupscheme:example.com/", "null", "madeupscheme:", "", "", "", "", "", "example.com/", "", ""); }
735test { try parsePass("ftps:example.com/", "http://example.org/foo/bar", "ftps:example.com/", "null", "ftps:", "", "", "", "", "", "example.com/", "", ""); }
736test { try parsePass("gopher:example.com/", "http://example.org/foo/bar", "gopher:example.com/", "null", "gopher:", "", "", "", "", "", "example.com/", "", ""); }
737test { try parsePass("ws:example.com/", "http://example.org/foo/bar", "ws://example.com/", "ws://example.com", "ws:", "", "", "example.com", "example.com", "", "/", "", ""); }
738test { try parsePass("wss:example.com/", "http://example.org/foo/bar", "wss://example.com/", "wss://example.com", "wss:", "", "", "example.com", "example.com", "", "/", "", ""); }
739test { try parsePass("data:example.com/", "http://example.org/foo/bar", "data:example.com/", "null", "data:", "", "", "", "", "", "example.com/", "", ""); }
740test { try parsePass("javascript:example.com/", "http://example.org/foo/bar", "javascript:example.com/", "null", "javascript:", "", "", "", "", "", "example.com/", "", ""); }
741test { try parsePass("mailto:example.com/", "http://example.org/foo/bar", "mailto:example.com/", "null", "mailto:", "", "", "", "", "", "example.com/", "", ""); }
742test { try parsePass("/a/b/c", "http://example.org/foo/bar", "http://example.org/a/b/c", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/a/b/c", "", ""); }
743test { try parsePass("/a/ /c", "http://example.org/foo/bar", "http://example.org/a/%20/c", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/a/%20/c", "", ""); }
744test { try parsePass("/a%2fc", "http://example.org/foo/bar", "http://example.org/a%2fc", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/a%2fc", "", ""); }
745test { try parsePass("/a/%2f/c", "http://example.org/foo/bar", "http://example.org/a/%2f/c", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/a/%2f/c", "", ""); }
746test { try parsePass("#\xce\xb2", "http://example.org/foo/bar", "http://example.org/foo/bar#%CE%B2", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", "#%CE%B2"); }
747test { try parsePass("data:text/html,test#test", "http://example.org/foo/bar", "data:text/html,test#test", "null", "data:", "", "", "", "", "", "text/html,test", "", "#test"); }
748test { try parsePass("tel:1234567890", "http://example.org/foo/bar", "tel:1234567890", "null", "tel:", "", "", "", "", "", "1234567890", "", ""); }
749test { try parsePass("ssh://example.com/foo/bar.git", "http://example.org/", "ssh://example.com/foo/bar.git", "null", "ssh:", "", "", "example.com", "example.com", "", "/foo/bar.git", "", ""); }
750test { try parsePass("file:c:\\foo\\bar.html", "file:///tmp/mock/path", "file:///c:/foo/bar.html", "", "file:", "", "", "", "", "", "/c:/foo/bar.html", "", ""); }
751test { try parsePass(" File:c|////foo\\bar.html", "file:///tmp/mock/path", "file:///c:////foo/bar.html", "", "file:", "", "", "", "", "", "/c:////foo/bar.html", "", ""); }
752test { try parsePass("C|/foo/bar", "file:///tmp/mock/path", "file:///C:/foo/bar", "", "file:", "", "", "", "", "", "/C:/foo/bar", "", ""); }
753test { try parsePass("/C|\\foo\\bar", "file:///tmp/mock/path", "file:///C:/foo/bar", "", "file:", "", "", "", "", "", "/C:/foo/bar", "", ""); }
754test { try parsePass("//C|/foo/bar", "file:///tmp/mock/path", "file:///C:/foo/bar", "", "file:", "", "", "", "", "", "/C:/foo/bar", "", ""); }
755test { try parsePass("//server/file", "file:///tmp/mock/path", "file://server/file", "", "file:", "", "", "server", "server", "", "/file", "", ""); }
756test { try parsePass("\\\\server\\file", "file:///tmp/mock/path", "file://server/file", "", "file:", "", "", "server", "server", "", "/file", "", ""); }
757test { try parsePass("/\\server/file", "file:///tmp/mock/path", "file://server/file", "", "file:", "", "", "server", "server", "", "/file", "", ""); }
758test { try parsePass("file:///foo/bar.txt", "file:///tmp/mock/path", "file:///foo/bar.txt", "", "file:", "", "", "", "", "", "/foo/bar.txt", "", ""); }
759test { try parsePass("file:///home/me", "file:///tmp/mock/path", "file:///home/me", "", "file:", "", "", "", "", "", "/home/me", "", ""); }
760test { try parsePass("//", "file:///tmp/mock/path", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
761test { try parsePass("///", "file:///tmp/mock/path", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
762test { try parsePass("///test", "file:///tmp/mock/path", "file:///test", "", "file:", "", "", "", "", "", "/test", "", ""); }
763test { try parsePass("file://test", "file:///tmp/mock/path", "file://test/", "", "file:", "", "", "test", "test", "", "/", "", ""); }
764test { try parsePass("file://localhost", "file:///tmp/mock/path", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
765test { try parsePass("file://localhost/", "file:///tmp/mock/path", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
766test { try parsePass("file://localhost/test", "file:///tmp/mock/path", "file:///test", "", "file:", "", "", "", "", "", "/test", "", ""); }
767test { try parsePass("test", "file:///tmp/mock/path", "file:///tmp/mock/test", "", "file:", "", "", "", "", "", "/tmp/mock/test", "", ""); }
768test { try parsePass("file:test", "file:///tmp/mock/path", "file:///tmp/mock/test", "", "file:", "", "", "", "", "", "/tmp/mock/test", "", ""); }
769test { try parsePass("/", "http://www.example.com/test", "http://www.example.com/", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/", "", ""); }
770test { try parsePass("/test.txt", "http://www.example.com/test", "http://www.example.com/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/test.txt", "", ""); }
771test { try parsePass(".", "http://www.example.com/test", "http://www.example.com/", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/", "", ""); }
772test { try parsePass("..", "http://www.example.com/test", "http://www.example.com/", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/", "", ""); }
773test { try parsePass("test.txt", "http://www.example.com/test", "http://www.example.com/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/test.txt", "", ""); }
774test { try parsePass("./test.txt", "http://www.example.com/test", "http://www.example.com/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/test.txt", "", ""); }
775test { try parsePass("../test.txt", "http://www.example.com/test", "http://www.example.com/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/test.txt", "", ""); }
776test { try parsePass("../aaa/test.txt", "http://www.example.com/test", "http://www.example.com/aaa/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/aaa/test.txt", "", ""); }
777test { try parsePass("../../test.txt", "http://www.example.com/test", "http://www.example.com/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/test.txt", "", ""); }
778test { try parsePass("\xe4\xb8\xad/test.txt", "http://www.example.com/test", "http://www.example.com/%E4%B8%AD/test.txt", "http://www.example.com", "http:", "", "", "www.example.com", "www.example.com", "", "/%E4%B8%AD/test.txt", "", ""); }
779test { try parsePass("http://www.example2.com", "http://www.example.com/test", "http://www.example2.com/", "http://www.example2.com", "http:", "", "", "www.example2.com", "www.example2.com", "", "/", "", ""); }
780test { try parsePass("//www.example2.com", "http://www.example.com/test", "http://www.example2.com/", "http://www.example2.com", "http:", "", "", "www.example2.com", "www.example2.com", "", "/", "", ""); }
781test { try parsePass("file:...", "http://www.example.com/test", "file:///...", "", "file:", "", "", "", "", "", "/...", "", ""); }
782test { try parsePass("file:..", "http://www.example.com/test", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
783test { try parsePass("file:a", "http://www.example.com/test", "file:///a", "", "file:", "", "", "", "", "", "/a", "", ""); }
784test { try parsePass("file:.", "http://www.example.com/test", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
785test { try parsePass("http://ExAmPlE.CoM", "http://other.com/", "http://example.com/", "http://example.com", "http:", "", "", "example.com", "example.com", "", "/", "", ""); }
786test { try parsePass("http://GOO\xe2\x80\x8b\xe2\x81\xa0\xef\xbb\xbfgoo.com", "http://other.com/", "http://googoo.com/", "http://googoo.com", "http:", "", "", "googoo.com", "googoo.com", "", "/", "", ""); }
787test { try parsePass("http://www.foo\xe3\x80\x82bar.com", "http://other.com/", "http://www.foo.bar.com/", "http://www.foo.bar.com", "http:", "", "", "www.foo.bar.com", "www.foo.bar.com", "", "/", "", ""); }
788test { try parsePass("http://\xef\xbc\xa7\xef\xbd\x8f.com", "http://other.com/", "http://go.com/", "http://go.com", "http:", "", "", "go.com", "go.com", "", "/", "", ""); }
789test { try parsePass("http://\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", "http://other.com/", "http://xn--6qqa088eba/", "http://xn--6qqa088eba", "http:", "", "", "xn--6qqa088eba", "xn--6qqa088eba", "", "/", "", ""); }
790test { try parsePass("http://%30%78%63%30%2e%30%32%35%30.01", "http://other.com/", "http://192.168.0.1/", "http://192.168.0.1", "http:", "", "", "192.168.0.1", "192.168.0.1", "", "/", "", ""); }
791test { try parsePass("http://%30%78%63%30%2e%30%32%35%30.01%2e", "http://other.com/", "http://192.168.0.1/", "http://192.168.0.1", "http:", "", "", "192.168.0.1", "192.168.0.1", "", "/", "", ""); }
792test { try parsePass("http://\xef\xbc\x90\xef\xbc\xb8\xef\xbd\x83\xef\xbc\x90\xef\xbc\x8e\xef\xbc\x90\xef\xbc\x92\xef\xbc\x95\xef\xbc\x90\xef\xbc\x8e\xef\xbc\x90\xef\xbc\x91", "http://other.com/", "http://192.168.0.1/", "http://192.168.0.1", "http:", "", "", "192.168.0.1", "192.168.0.1", "", "/", "", ""); }
793test { try parsePass("http://foo:\xf0\x9f\x92\xa9@example.com/bar", "http://other.com/", "http://foo:%F0%9F%92%A9@example.com/bar", "http://example.com", "http:", "foo", "%F0%9F%92%A9", "example.com", "example.com", "", "/bar", "", ""); }
794test { try parsePass("#", "test:test", "test:test#", "null", "test:", "", "", "", "", "", "test", "", ""); }
795test { try parsePass("#x", "mailto:x@x.com", "mailto:x@x.com#x", "null", "mailto:", "", "", "", "", "", "x@x.com", "", "#x"); }
796test { try parsePass("#x", "data:,", "data:,#x", "null", "data:", "", "", "", "", "", ",", "", "#x"); }
797test { try parsePass("#x", "about:blank", "about:blank#x", "null", "about:", "", "", "", "", "", "blank", "", "#x"); }
798test { try parsePass("#x:y", "about:blank", "about:blank#x:y", "null", "about:", "", "", "", "", "", "blank", "", "#x:y"); }
799test { try parsePass("#", "test:test?test", "test:test?test#", "null", "test:", "", "", "", "", "", "test", "?test", ""); }
800test { try parsePass("https://@test@test@example:800/", "http://doesnotmatter/", "https://%40test%40test@example:800/", "https://example:800", "https:", "%40test%40test", "", "example:800", "example", "800", "/", "", ""); }
801test { try parsePass("https://@@@example", "http://doesnotmatter/", "https://%40%40@example/", "https://example", "https:", "%40%40", "", "example", "example", "", "/", "", ""); }
802test { try parsePass("http://`{}:`{}@h/`{}?`{}", "http://doesnotmatter/", "http://%60%7B%7D:%60%7B%7D@h/%60%7B%7D?`{}", "http://h", "http:", "%60%7B%7D", "%60%7B%7D", "h", "h", "", "/%60%7B%7D", "?`{}", ""); }
803test { try parsePass("/some/path", "http://user@example.org/smth", "http://user@example.org/some/path", "http://example.org", "http:", "user", "", "example.org", "example.org", "", "/some/path", "", ""); }
804test { try parsePass("", "http://user:pass@example.org:21/smth", "http://user:pass@example.org:21/smth", "http://example.org:21", "http:", "user", "pass", "example.org:21", "example.org", "21", "/smth", "", ""); }
805test { try parsePass("/some/path", "http://user:pass@example.org:21/smth", "http://user:pass@example.org:21/some/path", "http://example.org:21", "http:", "user", "pass", "example.org:21", "example.org", "21", "/some/path", "", ""); }
806test { try parsePass("i", "sc:/pa/pa", "sc:/pa/i", "null", "sc:", "", "", "", "", "", "/pa/i", "", ""); }
807test { try parsePass("i", "sc://ho/pa", "sc://ho/i", "null", "sc:", "", "", "ho", "ho", "", "/i", "", ""); }
808test { try parsePass("i", "sc:///pa/pa", "sc:///pa/i", "null", "sc:", "", "", "", "", "", "/pa/i", "", ""); }
809test { try parsePass("../i", "sc:/pa/pa", "sc:/i", "null", "sc:", "", "", "", "", "", "/i", "", ""); }
810test { try parsePass("../i", "sc://ho/pa", "sc://ho/i", "null", "sc:", "", "", "ho", "ho", "", "/i", "", ""); }
811test { try parsePass("../i", "sc:///pa/pa", "sc:///i", "null", "sc:", "", "", "", "", "", "/i", "", ""); }
812test { try parsePass("/i", "sc:/pa/pa", "sc:/i", "null", "sc:", "", "", "", "", "", "/i", "", ""); }
813test { try parsePass("/i", "sc://ho/pa", "sc://ho/i", "null", "sc:", "", "", "ho", "ho", "", "/i", "", ""); }
814test { try parsePass("/i", "sc:///pa/pa", "sc:///i", "null", "sc:", "", "", "", "", "", "/i", "", ""); }
815test { try parsePass("?i", "sc:/pa/pa", "sc:/pa/pa?i", "null", "sc:", "", "", "", "", "", "/pa/pa", "?i", ""); }
816test { try parsePass("?i", "sc://ho/pa", "sc://ho/pa?i", "null", "sc:", "", "", "ho", "ho", "", "/pa", "?i", ""); }
817test { try parsePass("?i", "sc:///pa/pa", "sc:///pa/pa?i", "null", "sc:", "", "", "", "", "", "/pa/pa", "?i", ""); }
818test { try parsePass("#i", "sc:sd", "sc:sd#i", "null", "sc:", "", "", "", "", "", "sd", "", "#i"); }
819test { try parsePass("#i", "sc:sd/sd", "sc:sd/sd#i", "null", "sc:", "", "", "", "", "", "sd/sd", "", "#i"); }
820test { try parsePass("#i", "sc:/pa/pa", "sc:/pa/pa#i", "null", "sc:", "", "", "", "", "", "/pa/pa", "", "#i"); }
821test { try parsePass("#i", "sc://ho/pa", "sc://ho/pa#i", "null", "sc:", "", "", "ho", "ho", "", "/pa", "", "#i"); }
822test { try parsePass("#i", "sc:///pa/pa", "sc:///pa/pa#i", "null", "sc:", "", "", "", "", "", "/pa/pa", "", "#i"); }
823test { try parsePass("x", "sc://\xc3\xb1", "sc://%C3%B1/x", "null", "sc:", "", "", "%C3%B1", "%C3%B1", "", "/x", "", ""); }
824test { try parsePass("?a=b&c=d", "http://example.org/foo/bar", "http://example.org/foo/bar?a=b&c=d", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "?a=b&c=d", ""); }
825test { try parsePass("??a=b&c=d", "http://example.org/foo/bar", "http://example.org/foo/bar??a=b&c=d", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "??a=b&c=d", ""); }
826test { try parsePass("http:", "http://example.org/foo/bar", "http://example.org/foo/bar", "http://example.org", "http:", "", "", "example.org", "example.org", "", "/foo/bar", "", ""); }
827test { try parsePass("sc:", "https://example.org/foo/bar", "sc:", "null", "sc:", "", "", "", "", "", "", "", ""); }
828test { try parsePass("http://1.2.3.4/", "http://other.com/", "http://1.2.3.4/", "http://1.2.3.4", "http:", "", "", "1.2.3.4", "1.2.3.4", "", "/", "", ""); }
829test { try parsePass("http://1.2.3.4./", "http://other.com/", "http://1.2.3.4/", "http://1.2.3.4", "http:", "", "", "1.2.3.4", "1.2.3.4", "", "/", "", ""); }
830test { try parsePass("http://192.168.257", "http://other.com/", "http://192.168.1.1/", "http://192.168.1.1", "http:", "", "", "192.168.1.1", "192.168.1.1", "", "/", "", ""); }
831test { try parsePass("http://192.168.257.", "http://other.com/", "http://192.168.1.1/", "http://192.168.1.1", "http:", "", "", "192.168.1.1", "192.168.1.1", "", "/", "", ""); }
832test { try parsePass("http://192.168.257.com", "http://other.com/", "http://192.168.257.com/", "http://192.168.257.com", "http:", "", "", "192.168.257.com", "192.168.257.com", "", "/", "", ""); }
833test { try parsePass("http://256", "http://other.com/", "http://0.0.1.0/", "http://0.0.1.0", "http:", "", "", "0.0.1.0", "0.0.1.0", "", "/", "", ""); }
834test { try parsePass("http://256.com", "http://other.com/", "http://256.com/", "http://256.com", "http:", "", "", "256.com", "256.com", "", "/", "", ""); }
835test { try parsePass("http://999999999", "http://other.com/", "http://59.154.201.255/", "http://59.154.201.255", "http:", "", "", "59.154.201.255", "59.154.201.255", "", "/", "", ""); }
836test { try parsePass("http://999999999.", "http://other.com/", "http://59.154.201.255/", "http://59.154.201.255", "http:", "", "", "59.154.201.255", "59.154.201.255", "", "/", "", ""); }
837test { try parsePass("http://999999999.com", "http://other.com/", "http://999999999.com/", "http://999999999.com", "http:", "", "", "999999999.com", "999999999.com", "", "/", "", ""); }
838test { try parsePass("http://10000000000.com", "http://other.com/", "http://10000000000.com/", "http://10000000000.com", "http:", "", "", "10000000000.com", "10000000000.com", "", "/", "", ""); }
839test { try parsePass("http://4294967295", "http://other.com/", "http://255.255.255.255/", "http://255.255.255.255", "http:", "", "", "255.255.255.255", "255.255.255.255", "", "/", "", ""); }
840test { try parsePass("http://0xffffffff", "http://other.com/", "http://255.255.255.255/", "http://255.255.255.255", "http:", "", "", "255.255.255.255", "255.255.255.255", "", "/", "", ""); }
841test { try parsePass("pix/submit.gif", "file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/anchor.html", "file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif", "", "file:", "", "", "", "", "", "/C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif", "", ""); }
842test { try parsePass("..", "file:///C:/", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); }
843test { try parsePass("..", "file:///", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
844test { try parsePass("/", "file:///C:/a/b", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); }
845test { try parsePass("/", "file://h/C:/a/b", "file://h/C:/", "", "file:", "", "", "h", "h", "", "/C:/", "", ""); }
846test { try parsePass("/", "file://h/a/b", "file://h/", "", "file:", "", "", "h", "h", "", "/", "", ""); }
847test { try parsePass("//d:", "file:///C:/a/b", "file:///d:", "", "file:", "", "", "", "", "", "/d:", "", ""); }
848test { try parsePass("//d:/..", "file:///C:/a/b", "file:///d:/", "", "file:", "", "", "", "", "", "/d:/", "", ""); }
849test { try parsePass("..", "file:///ab:/", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
850test { try parsePass("..", "file:///1:/", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
851test { try parsePass("", "file:///test?test#test", "file:///test?test", "", "file:", "", "", "", "", "", "/test", "?test", ""); }
852test { try parsePass("file:", "file:///test?test#test", "file:///test?test", "", "file:", "", "", "", "", "", "/test", "?test", ""); }
853test { try parsePass("?x", "file:///test?test#test", "file:///test?x", "", "file:", "", "", "", "", "", "/test", "?x", ""); }
854test { try parsePass("file:?x", "file:///test?test#test", "file:///test?x", "", "file:", "", "", "", "", "", "/test", "?x", ""); }
855test { try parsePass("#x", "file:///test?test#test", "file:///test?test#x", "", "file:", "", "", "", "", "", "/test", "?test", "#x"); }
856test { try parsePass("file:#x", "file:///test?test#test", "file:///test?test#x", "", "file:", "", "", "", "", "", "/test", "?test", "#x"); }
857test { try parsePass("/////mouse", "file:///elephant", "file://///mouse", "", "file:", "", "", "", "", "", "///mouse", "", ""); }
858test { try parsePass("\\//pig", "file://lion/", "file:///pig", "", "file:", "", "", "", "", "", "/pig", "", ""); }
859test { try parsePass("\\/localhost//pig", "file://lion/", "file:////pig", "", "file:", "", "", "", "", "", "//pig", "", ""); }
860test { try parsePass("//localhost//pig", "file://lion/", "file:////pig", "", "file:", "", "", "", "", "", "//pig", "", ""); }
861test { try parsePass("/..//localhost//pig", "file://lion/", "file://lion//localhost//pig", "", "file:", "", "", "lion", "lion", "", "//localhost//pig", "", ""); }
862test { try parsePass("file://", "file://ape/", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
863test { try parsePass("/rooibos", "file://tea/", "file://tea/rooibos", "", "file:", "", "", "tea", "tea", "", "/rooibos", "", ""); }
864test { try parsePass("/?chai", "file://tea/", "file://tea/?chai", "", "file:", "", "", "tea", "tea", "", "/", "?chai", ""); }
865test { try parsePass("C|", "file://host/dir/file", "file://host/C:", "", "file:", "", "", "host", "host", "", "/C:", "", ""); }
866test { try parsePass("C|", "file://host/D:/dir1/dir2/file", "file://host/C:", "", "file:", "", "", "host", "host", "", "/C:", "", ""); }
867test { try parsePass("C|#", "file://host/dir/file", "file://host/C:#", "", "file:", "", "", "host", "host", "", "/C:", "", ""); }
868test { try parsePass("C|?", "file://host/dir/file", "file://host/C:?", "", "file:", "", "", "host", "host", "", "/C:", "", ""); }
869test { try parsePass("C|/", "file://host/dir/file", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); }
870test { try parsePass("C|\n/", "file://host/dir/file", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); }
871test { try parsePass("C|\\", "file://host/dir/file", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); }
872test { try parsePass("C", "file://host/dir/file", "file://host/dir/C", "", "file:", "", "", "host", "host", "", "/dir/C", "", ""); }
873test { try parsePass("C|a", "file://host/dir/file", "file://host/dir/C|a", "", "file:", "", "", "host", "host", "", "/dir/C|a", "", ""); }
874test { try parsePass("/c:/foo/bar", "file:///c:/baz/qux", "file:///c:/foo/bar", "", "file:", "", "", "", "", "", "/c:/foo/bar", "", ""); }
875test { try parsePass("/c|/foo/bar", "file:///c:/baz/qux", "file:///c:/foo/bar", "", "file:", "", "", "", "", "", "/c:/foo/bar", "", ""); }
876test { try parsePass("file:\\c:\\foo\\bar", "file:///c:/baz/qux", "file:///c:/foo/bar", "", "file:", "", "", "", "", "", "/c:/foo/bar", "", ""); }
877test { try parsePass("/c:/foo/bar", "file://host/path", "file://host/c:/foo/bar", "", "file:", "", "", "host", "host", "", "/c:/foo/bar", "", ""); }
878test { try parsePass("C|/", "file://host/", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); }
879test { try parsePass("/C:/", "file://host/", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); }
880test { try parsePass("file:C:/", "file://host/", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); }
881test { try parsePass("file:/C:/", "file://host/", "file://host/C:/", "", "file:", "", "", "host", "host", "", "/C:/", "", ""); }
882test { try parsePass("//C:/", "file://host/", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); }
883test { try parsePass("file://C:/", "file://host/", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); }
884test { try parsePass("///C:/", "file://host/", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); }
885test { try parsePass("file:///C:/", "file://host/", "file:///C:/", "", "file:", "", "", "", "", "", "/C:/", "", ""); }
886test { try parsePass("file:///one/two", "file:///", "file:///one/two", "", "file:", "", "", "", "", "", "/one/two", "", ""); }
887test { try parsePass("file:////one/two", "file:///", "file:////one/two", "", "file:", "", "", "", "", "", "//one/two", "", ""); }
888test { try parsePass("//one/two", "file:///", "file://one/two", "", "file:", "", "", "one", "one", "", "/two", "", ""); }
889test { try parsePass("///one/two", "file:///", "file:///one/two", "", "file:", "", "", "", "", "", "/one/two", "", ""); }
890test { try parsePass("////one/two", "file:///", "file:////one/two", "", "file:", "", "", "", "", "", "//one/two", "", ""); }
891test { try parsePass("file:///.//", "file:////", "file:////", "", "file:", "", "", "", "", "", "//", "", ""); }
892test { try parsePass("http://[1:0::]", "http://example.net/", "http://[1::]/", "http://[1::]", "http:", "", "", "[1::]", "[1::]", "", "/", "", ""); }
893test { try parsePass("#x", "sc://\xc3\xb1", "sc://%C3%B1#x", "null", "sc:", "", "", "%C3%B1", "%C3%B1", "", "", "", "#x"); }
894test { try parsePass("?x", "sc://\xc3\xb1", "sc://%C3%B1?x", "null", "sc:", "", "", "%C3%B1", "%C3%B1", "", "", "?x", ""); }
895test { try parsePass("///", "sc://x/", "sc:///", "", "sc:", "", "", "", "", "", "/", "", ""); }
896test { try parsePass("////", "sc://x/", "sc:////", "", "sc:", "", "", "", "", "", "//", "", ""); }
897test { try parsePass("////x/", "sc://x/", "sc:////x/", "", "sc:", "", "", "", "", "", "//x/", "", ""); }
898test { try parsePass("/.//path", "non-spec:/p", "non-spec:/.//path", "", "non-spec:", "", "", "", "", "", "//path", "", ""); }
899test { try parsePass("/..//path", "non-spec:/p", "non-spec:/.//path", "", "non-spec:", "", "", "", "", "", "//path", "", ""); }
900test { try parsePass("..//path", "non-spec:/p", "non-spec:/.//path", "", "non-spec:", "", "", "", "", "", "//path", "", ""); }
901test { try parsePass("a/..//path", "non-spec:/p", "non-spec:/.//path", "", "non-spec:", "", "", "", "", "", "//path", "", ""); }
902test { try parsePass("", "non-spec:/..//p", "non-spec:/.//p", "", "non-spec:", "", "", "", "", "", "//p", "", ""); }
903test { try parsePass("path", "non-spec:/..//p", "non-spec:/.//path", "", "non-spec:", "", "", "", "", "", "//path", "", ""); }
904test { try parsePass("../path", "non-spec:/.//p", "non-spec:/path", "", "non-spec:", "", "", "", "", "", "/path", "", ""); }
905test { try parsePass("test-a-colon-slash.html", "a:/", "a:/test-a-colon-slash.html", "", "a:", "", "", "", "", "", "/test-a-colon-slash.html", "", ""); }
906test { try parsePass("test-a-colon-slash-slash.html", "a://", "a:///test-a-colon-slash-slash.html", "", "a:", "", "", "", "", "", "/test-a-colon-slash-slash.html", "", ""); }
907test { try parsePass("test-a-colon-slash-b.html", "a:/b", "a:/test-a-colon-slash-b.html", "", "a:", "", "", "", "", "", "/test-a-colon-slash-b.html", "", ""); }
908test { try parsePass("test-a-colon-slash-slash-b.html", "a://b", "a://b/test-a-colon-slash-slash-b.html", "", "a:", "", "", "b", "b", "", "/test-a-colon-slash-slash-b.html", "", ""); }
909test { try parsePass("10.0.0.7:8080/foo.html", "file:///some/dir/bar.html", "file:///some/dir/10.0.0.7:8080/foo.html", "", "file:", "", "", "", "", "", "/some/dir/10.0.0.7:8080/foo.html", "", ""); }
910test { try parsePass("a!@$*=/foo.html", "file:///some/dir/bar.html", "file:///some/dir/a!@$*=/foo.html", "", "file:", "", "", "", "", "", "/some/dir/a!@$*=/foo.html", "", ""); }
911test { try parsePass("a1234567890-+.:foo/bar", "http://example.com/dir/file", "a1234567890-+.:foo/bar", "", "a1234567890-+.:", "", "", "", "", "", "foo/bar", "", ""); }
912test { try parsePass("#link", "https://example.org/##link", "https://example.org/#link", "", "https:", "", "", "example.org", "example.org", "", "/", "", "#link"); }
913test { try parsePass("https://user:pass[\x7f@foo/bar", "http://example.org", "https://user:pass%5B%7F@foo/bar", "https://foo", "https:", "user", "pass%5B%7F", "foo", "foo", "", "/bar", "", ""); }
914test { try parsePass("abc:rootless", "abc://host/path", "abc:rootless", "", "abc:", "", "", "", "", "", "rootless", "", ""); }
915test { try parsePass("abc:rootless", "abc:/path", "abc:rootless", "", "abc:", "", "", "", "", "", "rootless", "", ""); }
916test { try parsePass("abc:rootless", "abc:path", "abc:rootless", "", "abc:", "", "", "", "", "", "rootless", "", ""); }
917test { try parsePass("abc:/rooted", "abc://host/path", "abc:/rooted", "", "abc:", "", "", "", "", "", "/rooted", "", ""); }
918test { try parsePass("///test", "http://example.org/", "http://test/", "", "http:", "", "", "test", "test", "", "/", "", ""); }
919test { try parsePass("///\\//\\//test", "http://example.org/", "http://test/", "", "http:", "", "", "test", "test", "", "/", "", ""); }
920test { try parsePass("///example.org/path", "http://example.org/", "http://example.org/path", "", "http:", "", "", "example.org", "example.org", "", "/path", "", ""); }
921test { try parsePass("///example.org/../path", "http://example.org/", "http://example.org/path", "", "http:", "", "", "example.org", "example.org", "", "/path", "", ""); }
922test { try parsePass("///example.org/../../", "http://example.org/", "http://example.org/", "", "http:", "", "", "example.org", "example.org", "", "/", "", ""); }
923test { try parsePass("///example.org/../path/../../", "http://example.org/", "http://example.org/", "", "http:", "", "", "example.org", "example.org", "", "/", "", ""); }
924test { try parsePass("///example.org/../path/../../path", "http://example.org/", "http://example.org/path", "", "http:", "", "", "example.org", "example.org", "", "/path", "", ""); }
925test { try parsePass("/\\/\\//example.org/../path", "http://example.org/", "http://example.org/path", "", "http:", "", "", "example.org", "example.org", "", "/path", "", ""); }
926test { try parsePass("///abcdef/../", "file:///", "file:///", "", "file:", "", "", "", "", "", "/", "", ""); }
927test { try parsePass("/\\//\\/a/../", "file:///", "file://////", "", "file:", "", "", "", "", "", "////", "", ""); }
928test { try parsePass("//a/../", "file:///", "file://a/", "", "file:", "", "", "a", "a", "", "/", "", ""); }
661929
662test { try parseIDNAFail("a\xe2\x80\x8cb"); }930test { try parseIDNAFail("a\xe2\x80\x8cb"); }
663test { try parseIDNAFail("A\xe2\x80\x8cB"); }931test { try parseIDNAFail("A\xe2\x80\x8cB"); }