1{
2    "comment": [
3        "## Tests for setters of https://url.spec.whatwg.org/#urlutils-members",
4        "",
5        "This file contains a JSON object.",
6        "Other than 'comment', each key is an attribute of the `URL` interface",
7        "defined in WHATWG’s URL Standard.",
8        "The values are arrays of test case objects for that attribute.",
9        "",
10        "To run a test case for the attribute `attr`:",
11        "",
12        "* Create a new `URL` object with the value for the 'href' key",
13        "  the constructor single parameter. (Without a base URL.)",
14        "  This must not throw.",
15        "* Set the attribute `attr` to (invoke its setter with)",
16        "  with the value of for 'new_value' key.",
17        "* The value for the 'expected' key is another object.",
18        "  For each `key` / `value` pair of that object,",
19        "  get the attribute `key` (invoke its getter).",
20        "  The returned string must be equal to `value`.",
21        "",
22        "Note: the 'href' setter is already covered by urltestdata.json."
23    ],
24    "protocol": [
25        {
26            "comment": "The empty string is not a valid scheme. Setter leaves the URL unchanged.",
27            "href": "a://example.net",
28            "new_value": "",
29            "expected": {
30                "href": "a://example.net/",
31                "protocol": "a:"
32            }
33        },
34        {
35            "href": "a://example.net",
36            "new_value": "b",
37            "expected": {
38                "href": "b://example.net/",
39                "protocol": "b:"
40            }
41        },
42        {
43            "comment": "Upper-case ASCII is lower-cased",
44            "href": "a://example.net",
45            "new_value": "B",
46            "expected": {
47                "href": "b://example.net/",
48                "protocol": "b:"
49            }
50        },
51        {
52            "comment": "Non-ASCII is rejected",
53            "href": "a://example.net",
54            "new_value": "é",
55            "expected": {
56                "href": "a://example.net/",
57                "protocol": "a:"
58            }
59        },
60        {
61            "comment": "No leading digit",
62            "href": "a://example.net",
63            "new_value": "0b",
64            "expected": {
65                "href": "a://example.net/",
66                "protocol": "a:"
67            }
68        },
69        {
70            "comment": "No leading punctuation",
71            "href": "a://example.net",
72            "new_value": "+b",
73            "expected": {
74                "href": "a://example.net/",
75                "protocol": "a:"
76            }
77        },
78        {
79            "href": "a://example.net",
80            "new_value": "bC0+-.",
81            "expected": {
82                "href": "bc0+-.://example.net/",
83                "protocol": "bc0+-.:"
84            }
85        },
86        {
87            "comment": "Only some punctuation is acceptable",
88            "href": "a://example.net",
89            "new_value": "b,c",
90            "expected": {
91                "href": "a://example.net/",
92                "protocol": "a:"
93            }
94        },
95        {
96            "comment": "Non-ASCII is rejected",
97            "href": "a://example.net",
98            "new_value": "bé",
99            "expected": {
100                "href": "a://example.net/",
101                "protocol": "a:"
102            }
103        },
104        {
105            "comment": "Can’t switch from file URL with no host",
106            "href": "file://localhost/",
107            "new_value": "http",
108            "expected": {
109                "href": "file:///",
110                "protocol": "file:"
111            }
112        },
113        {
114            "href": "file:///test",
115            "new_value": "gopher",
116            "expected": {
117                "href": "file:///test",
118                "protocol": "file:"
119            }
120        },
121        {
122            "href": "file:",
123            "new_value": "wss",
124            "expected": {
125                "href": "file:///",
126                "protocol": "file:"
127            }
128        },
129        {
130            "comment": "Spec deviation: from special scheme to not is not problematic. https://github.com/whatwg/url/issues/104",
131            "href": "http://example.net",
132            "new_value": "b",
133            "expected": {
134                "href": "b://example.net/",
135                "protocol": "b:"
136            }
137        },
138        {
139            "comment": "Cannot-be-a-base URL doesn’t have a host, but URL in a special scheme must.",
140            "href": "mailto:me@example.net",
141            "new_value": "http",
142            "expected": {
143                "href": "mailto:me@example.net",
144                "protocol": "mailto:"
145            }
146        },
147        {
148            "comment": "Spec deviation: from non-special scheme with a host to special is not problematic. https://github.com/whatwg/url/issues/104",
149            "href": "ssh://me@example.net",
150            "new_value": "http",
151            "expected": {
152                "href": "http://me@example.net/",
153                "protocol": "http:"
154            }
155        },
156        {
157            "comment": "Stuff after the first ':' is ignored",
158            "href": "http://example.net",
159            "new_value": "https:foo : bar",
160            "expected": {
161                "href": "https://example.net/",
162                "protocol": "https:"
163            }
164        },
165        {
166            "comment": "Stuff after the first ':' is ignored",
167            "href": "data:text/html,<p>Test",
168            "new_value": "view-source+data:foo : bar",
169            "expected": {
170                "href": "view-source+data:text/html,<p>Test",
171                "protocol": "view-source+data:"
172            }
173        }
174    ],
175    "username": [
176        {
177            "comment": "No host means no username",
178            "href": "file:///home/you/index.html",
179            "new_value": "me",
180            "expected": {
181                "href": "file:///home/you/index.html",
182                "username": ""
183            }
184        },
185        {
186            "comment": "No host means no username",
187            "href": "unix:/run/foo.socket",
188            "new_value": "me",
189            "expected": {
190                "href": "unix:/run/foo.socket",
191                "username": ""
192            }
193        },
194        {
195            "comment": "Cannot-be-a-base means no username",
196            "href": "mailto:you@example.net",
197            "new_value": "me",
198            "expected": {
199                "href": "mailto:you@example.net",
200                "username": ""
201            }
202        },
203        {
204            "href": "javascript:alert(1)",
205            "new_value": "wario",
206            "expected": {
207                "href": "javascript:alert(1)",
208                "username": ""
209            }
210        },
211        {
212            "href": "http://example.net",
213            "new_value": "me",
214            "expected": {
215                "href": "http://me@example.net/",
216                "username": "me"
217            }
218        },
219        {
220            "href": "http://:secret@example.net",
221            "new_value": "me",
222            "expected": {
223                "href": "http://me:secret@example.net/",
224                "username": "me"
225            }
226        },
227        {
228            "href": "http://me@example.net",
229            "new_value": "",
230            "expected": {
231                "href": "http://example.net/",
232                "username": ""
233            }
234        },
235        {
236            "href": "http://me:secret@example.net",
237            "new_value": "",
238            "expected": {
239                "href": "http://:secret@example.net/",
240                "username": ""
241            }
242        },
243        {
244            "comment": "UTF-8 percent encoding with the userinfo encode set.",
245            "href": "http://example.net",
246            "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
247            "expected": {
248                "href": "http://%00%01%09%0A%0D%1F%20!%22%23$%&'()*+,-.%2F09%3A%3B%3C%3D%3E%3F%40AZ%5B%5C%5D%5E_%60az%7B%7C%7D~%7F%C2%80%C2%81%C3%89%C3%A9@example.net/",
249                "username": "%00%01%09%0A%0D%1F%20!%22%23$%&'()*+,-.%2F09%3A%3B%3C%3D%3E%3F%40AZ%5B%5C%5D%5E_%60az%7B%7C%7D~%7F%C2%80%C2%81%C3%89%C3%A9"
250            }
251        },
252        {
253            "comment": "Bytes already percent-encoded are left as-is.",
254            "href": "http://example.net",
255            "new_value": "%c3%89té",
256            "expected": {
257                "href": "http://%c3%89t%C3%A9@example.net/",
258                "username": "%c3%89t%C3%A9"
259            }
260        },
261        {
262            "href": "sc:///",
263            "new_value": "x",
264            "expected": {
265                "href": "sc:///",
266                "username": ""
267            }
268        },
269        {
270            "href": "file://test/",
271            "new_value": "test",
272            "expected": {
273                "href": "file://test/",
274                "username": ""
275            }
276        },
277        {
278            "href": "javascript://x/",
279            "new_value": "wario",
280            "expected": {
281                "href": "javascript://wario@x/",
282                "username": "wario"
283            }
284        }
285    ],
286    "password": [
287        {
288            "comment": "No host means no password",
289            "href": "file:///home/me/index.html",
290            "new_value": "secret",
291            "expected": {
292                "href": "file:///home/me/index.html",
293                "password": ""
294            }
295        },
296        {
297            "comment": "No host means no password",
298            "href": "unix:/run/foo.socket",
299            "new_value": "secret",
300            "expected": {
301                "href": "unix:/run/foo.socket",
302                "password": ""
303            }
304        },
305        {
306            "comment": "Cannot-be-a-base means no password",
307            "href": "mailto:me@example.net",
308            "new_value": "secret",
309            "expected": {
310                "href": "mailto:me@example.net",
311                "password": ""
312            }
313        },
314        {
315            "href": "http://example.net",
316            "new_value": "secret",
317            "expected": {
318                "href": "http://:secret@example.net/",
319                "password": "secret"
320            }
321        },
322        {
323            "href": "http://me@example.net",
324            "new_value": "secret",
325            "expected": {
326                "href": "http://me:secret@example.net/",
327                "password": "secret"
328            }
329        },
330        {
331            "href": "http://:secret@example.net",
332            "new_value": "",
333            "expected": {
334                "href": "http://example.net/",
335                "password": ""
336            }
337        },
338        {
339            "href": "http://me:secret@example.net",
340            "new_value": "",
341            "expected": {
342                "href": "http://me@example.net/",
343                "password": ""
344            }
345        },
346        {
347            "comment": "UTF-8 percent encoding with the userinfo encode set.",
348            "href": "http://example.net",
349            "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
350            "expected": {
351                "href": "http://:%00%01%09%0A%0D%1F%20!%22%23$%&'()*+,-.%2F09%3A%3B%3C%3D%3E%3F%40AZ%5B%5C%5D%5E_%60az%7B%7C%7D~%7F%C2%80%C2%81%C3%89%C3%A9@example.net/",
352                "password": "%00%01%09%0A%0D%1F%20!%22%23$%&'()*+,-.%2F09%3A%3B%3C%3D%3E%3F%40AZ%5B%5C%5D%5E_%60az%7B%7C%7D~%7F%C2%80%C2%81%C3%89%C3%A9"
353            }
354        },
355        {
356            "comment": "Bytes already percent-encoded are left as-is.",
357            "href": "http://example.net",
358            "new_value": "%c3%89té",
359            "expected": {
360                "href": "http://:%c3%89t%C3%A9@example.net/",
361                "password": "%c3%89t%C3%A9"
362            }
363        },
364        {
365            "href": "sc:///",
366            "new_value": "x",
367            "expected": {
368                "href": "sc:///",
369                "password": ""
370            }
371        },
372        {
373            "href": "file://test/",
374            "new_value": "test",
375            "expected": {
376                "href": "file://test/",
377                "password": ""
378            }
379        },
380        {
381            "href": "javascript://x/",
382            "new_value": "bowser",
383            "expected": {
384                "href": "javascript://:bowser@x/",
385                "password": "bowser"
386            }
387        }
388    ],
389    "host": [
390        {
391            "href": "sc://x/",
392            "new_value": "\u0009",
393            "expected": {
394                "href": "sc:///",
395                "host": "",
396                "hostname": ""
397            }
398        },
399        {
400            "href": "sc://x/",
401            "new_value": "\u000A",
402            "expected": {
403                "href": "sc:///",
404                "host": "",
405                "hostname": ""
406            }
407        },
408        {
409            "href": "sc://x/",
410            "new_value": "\u000D",
411            "expected": {
412                "href": "sc:///",
413                "host": "",
414                "hostname": ""
415            }
416        },
417        {
418            "href": "sc://x/",
419            "new_value": "#",
420            "expected": {
421                "href": "sc:///",
422                "host": "",
423                "hostname": ""
424            }
425        },
426        {
427            "href": "sc://x/",
428            "new_value": "/",
429            "expected": {
430                "href": "sc:///",
431                "host": "",
432                "hostname": ""
433            }
434        },
435        {
436            "href": "sc://x/",
437            "new_value": "?",
438            "expected": {
439                "href": "sc:///",
440                "host": "",
441                "hostname": ""
442            }
443        },
444        {
445            "href": "sc://x/",
446            "new_value": "@",
447            "expected": {
448                "href": "sc://x/",
449                "host": "x",
450                "hostname": "x"
451            }
452        },
453        {
454            "href": "sc://x/",
455            "new_value": "ß",
456            "expected": {
457                "href": "sc://%C3%9F/",
458                "host": "%C3%9F",
459                "hostname": "%C3%9F"
460            }
461        },
462        {
463            "comment": "Cannot-be-a-base means no host",
464            "href": "mailto:me@example.net",
465            "new_value": "example.com",
466            "expected": {
467                "href": "mailto:me@example.net",
468                "host": ""
469            }
470        },
471        {
472            "comment": "Cannot-be-a-base means no password",
473            "href": "data:text/plain,Stuff",
474            "new_value": "example.net",
475            "expected": {
476                "href": "data:text/plain,Stuff",
477                "host": ""
478            }
479        },
480        {
481            "href": "http://example.net",
482            "new_value": "example.com:8080",
483            "expected": {
484                "href": "http://example.com:8080/",
485                "host": "example.com:8080",
486                "hostname": "example.com",
487                "port": "8080"
488            }
489        },
490        {
491            "comment": "Port number is unchanged if not specified in the new value",
492            "href": "http://example.net:8080",
493            "new_value": "example.com",
494            "expected": {
495                "href": "http://example.com:8080/",
496                "host": "example.com:8080",
497                "hostname": "example.com",
498                "port": "8080"
499            }
500        },
501        {
502            "comment": "Port number is removed if empty in the new value: https://github.com/whatwg/url/pull/113",
503            "href": "http://example.net:8080",
504            "new_value": "example.com:",
505            "expected": {
506                "href": "http://example.com/",
507                "host": "example.com",
508                "hostname": "example.com",
509                "port": ""
510            }
511        },
512        {
513            "comment": "The empty host is not valid for special schemes",
514            "href": "http://example.net",
515            "new_value": "",
516            "expected": {
517                "href": "http://example.net/",
518                "host": "example.net"
519            }
520        },
521        {
522            "comment": "The empty host is OK for non-special schemes",
523            "href": "view-source+http://example.net/foo",
524            "new_value": "",
525            "expected": {
526                "href": "view-source+http:///foo",
527                "host": ""
528            }
529        },
530        {
531            "comment": "Path-only URLs can gain a host",
532            "href": "a:/foo",
533            "new_value": "example.net",
534            "expected": {
535                "href": "a://example.net/foo",
536                "host": "example.net"
537            }
538        },
539        {
540            "comment": "IPv4 address syntax is normalized",
541            "href": "http://example.net",
542            "new_value": "0x7F000001:8080",
543            "expected": {
544                "href": "http://127.0.0.1:8080/",
545                "host": "127.0.0.1:8080",
546                "hostname": "127.0.0.1",
547                "port": "8080"
548            }
549        },
550        {
551            "comment": "IPv6 address syntax is normalized",
552            "href": "http://example.net",
553            "new_value": "[::0:01]:2",
554            "expected": {
555                "href": "http://[::1]:2/",
556                "host": "[::1]:2",
557                "hostname": "[::1]",
558                "port": "2"
559            }
560        },
561        {
562            "comment": "Default port number is removed",
563            "href": "http://example.net",
564            "new_value": "example.com:80",
565            "expected": {
566                "href": "http://example.com/",
567                "host": "example.com",
568                "hostname": "example.com",
569                "port": ""
570            }
571        },
572        {
573            "comment": "Default port number is removed",
574            "href": "https://example.net",
575            "new_value": "example.com:443",
576            "expected": {
577                "href": "https://example.com/",
578                "host": "example.com",
579                "hostname": "example.com",
580                "port": ""
581            }
582        },
583        {
584            "comment": "Default port number is only removed for the relevant scheme",
585            "href": "https://example.net",
586            "new_value": "example.com:80",
587            "expected": {
588                "href": "https://example.com:80/",
589                "host": "example.com:80",
590                "hostname": "example.com",
591                "port": "80"
592            }
593        },
594        {
595            "comment": "Stuff after a / delimiter is ignored",
596            "href": "http://example.net/path",
597            "new_value": "example.com/stuff",
598            "expected": {
599                "href": "http://example.com/path",
600                "host": "example.com",
601                "hostname": "example.com",
602                "port": ""
603            }
604        },
605        {
606            "comment": "Stuff after a / delimiter is ignored",
607            "href": "http://example.net/path",
608            "new_value": "example.com:8080/stuff",
609            "expected": {
610                "href": "http://example.com:8080/path",
611                "host": "example.com:8080",
612                "hostname": "example.com",
613                "port": "8080"
614            }
615        },
616        {
617            "comment": "Stuff after a ? delimiter is ignored",
618            "href": "http://example.net/path",
619            "new_value": "example.com?stuff",
620            "expected": {
621                "href": "http://example.com/path",
622                "host": "example.com",
623                "hostname": "example.com",
624                "port": ""
625            }
626        },
627        {
628            "comment": "Stuff after a ? delimiter is ignored",
629            "href": "http://example.net/path",
630            "new_value": "example.com:8080?stuff",
631            "expected": {
632                "href": "http://example.com:8080/path",
633                "host": "example.com:8080",
634                "hostname": "example.com",
635                "port": "8080"
636            }
637        },
638        {
639            "comment": "Stuff after a # delimiter is ignored",
640            "href": "http://example.net/path",
641            "new_value": "example.com#stuff",
642            "expected": {
643                "href": "http://example.com/path",
644                "host": "example.com",
645                "hostname": "example.com",
646                "port": ""
647            }
648        },
649        {
650            "comment": "Stuff after a # delimiter is ignored",
651            "href": "http://example.net/path",
652            "new_value": "example.com:8080#stuff",
653            "expected": {
654                "href": "http://example.com:8080/path",
655                "host": "example.com:8080",
656                "hostname": "example.com",
657                "port": "8080"
658            }
659        },
660        {
661            "comment": "Stuff after a \\ delimiter is ignored for special schemes",
662            "href": "http://example.net/path",
663            "new_value": "example.com\\stuff",
664            "expected": {
665                "href": "http://example.com/path",
666                "host": "example.com",
667                "hostname": "example.com",
668                "port": ""
669            }
670        },
671        {
672            "comment": "Stuff after a \\ delimiter is ignored for special schemes",
673            "href": "http://example.net/path",
674            "new_value": "example.com:8080\\stuff",
675            "expected": {
676                "href": "http://example.com:8080/path",
677                "host": "example.com:8080",
678                "hostname": "example.com",
679                "port": "8080"
680            }
681        },
682        {
683            "comment": "\\ is not a delimiter for non-special schemes, but still forbidden in hosts",
684            "href": "view-source+http://example.net/path",
685            "new_value": "example.com\\stuff",
686            "expected": {
687                "href": "view-source+http://example.net/path",
688                "host": "example.net",
689                "hostname": "example.net",
690                "port": ""
691            }
692        },
693        {
694            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
695            "href": "view-source+http://example.net/path",
696            "new_value": "example.com:8080stuff2",
697            "expected": {
698                "href": "view-source+http://example.com:8080/path",
699                "host": "example.com:8080",
700                "hostname": "example.com",
701                "port": "8080"
702            }
703        },
704        {
705            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
706            "href": "http://example.net/path",
707            "new_value": "example.com:8080stuff2",
708            "expected": {
709                "href": "http://example.com:8080/path",
710                "host": "example.com:8080",
711                "hostname": "example.com",
712                "port": "8080"
713            }
714        },
715        {
716            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
717            "href": "http://example.net/path",
718            "new_value": "example.com:8080+2",
719            "expected": {
720                "href": "http://example.com:8080/path",
721                "host": "example.com:8080",
722                "hostname": "example.com",
723                "port": "8080"
724            }
725        },
726        {
727            "comment": "Port numbers are 16 bit integers",
728            "href": "http://example.net/path",
729            "new_value": "example.com:65535",
730            "expected": {
731                "href": "http://example.com:65535/path",
732                "host": "example.com:65535",
733                "hostname": "example.com",
734                "port": "65535"
735            }
736        },
737        {
738            "comment": "Port numbers are 16 bit integers, overflowing is an error. Hostname is still set, though.",
739            "href": "http://example.net/path",
740            "new_value": "example.com:65536",
741            "expected": {
742                "href": "http://example.com/path",
743                "host": "example.com",
744                "hostname": "example.com",
745                "port": ""
746            }
747        },
748        {
749            "comment": "Broken IPv6",
750            "href": "http://example.net/",
751            "new_value": "[google.com]",
752            "expected": {
753                "href": "http://example.net/",
754                "host": "example.net",
755                "hostname": "example.net"
756            }
757        },
758        {
759            "href": "http://example.net/",
760            "new_value": "[::1.2.3.4x]",
761            "expected": {
762                "href": "http://example.net/",
763                "host": "example.net",
764                "hostname": "example.net"
765            }
766        },
767        {
768            "href": "http://example.net/",
769            "new_value": "[::1.2.3.]",
770            "expected": {
771                "href": "http://example.net/",
772                "host": "example.net",
773                "hostname": "example.net"
774            }
775        },
776        {
777            "href": "http://example.net/",
778            "new_value": "[::1.2.]",
779            "expected": {
780                "href": "http://example.net/",
781                "host": "example.net",
782                "hostname": "example.net"
783            }
784        },
785        {
786            "href": "http://example.net/",
787            "new_value": "[::1.]",
788            "expected": {
789                "href": "http://example.net/",
790                "host": "example.net",
791                "hostname": "example.net"
792            }
793        }
794    ],
795    "hostname": [
796        {
797            "href": "sc://x/",
798            "new_value": "\u0009",
799            "expected": {
800                "href": "sc:///",
801                "host": "",
802                "hostname": ""
803            }
804        },
805        {
806            "href": "sc://x/",
807            "new_value": "\u000A",
808            "expected": {
809                "href": "sc:///",
810                "host": "",
811                "hostname": ""
812            }
813        },
814        {
815            "href": "sc://x/",
816            "new_value": "\u000D",
817            "expected": {
818                "href": "sc:///",
819                "host": "",
820                "hostname": ""
821            }
822        },
823        {
824            "href": "sc://x/",
825            "new_value": "#",
826            "expected": {
827                "href": "sc:///",
828                "host": "",
829                "hostname": ""
830            }
831        },
832        {
833            "href": "sc://x/",
834            "new_value": "/",
835            "expected": {
836                "href": "sc:///",
837                "host": "",
838                "hostname": ""
839            }
840        },
841        {
842            "href": "sc://x/",
843            "new_value": "?",
844            "expected": {
845                "href": "sc:///",
846                "host": "",
847                "hostname": ""
848            }
849        },
850        {
851            "href": "sc://x/",
852            "new_value": "@",
853            "expected": {
854                "href": "sc://x/",
855                "host": "x",
856                "hostname": "x"
857            }
858        },
859        {
860            "comment": "Cannot-be-a-base means no host",
861            "href": "mailto:me@example.net",
862            "new_value": "example.com",
863            "expected": {
864                "href": "mailto:me@example.net",
865                "host": ""
866            }
867        },
868        {
869            "comment": "Cannot-be-a-base means no password",
870            "href": "data:text/plain,Stuff",
871            "new_value": "example.net",
872            "expected": {
873                "href": "data:text/plain,Stuff",
874                "host": ""
875            }
876        },
877        {
878            "href": "http://example.net:8080",
879            "new_value": "example.com",
880            "expected": {
881                "href": "http://example.com:8080/",
882                "host": "example.com:8080",
883                "hostname": "example.com",
884                "port": "8080"
885            }
886        },
887        {
888            "comment": "The empty host is not valid for special schemes",
889            "href": "http://example.net",
890            "new_value": "",
891            "expected": {
892                "href": "http://example.net/",
893                "host": "example.net"
894            }
895        },
896        {
897            "comment": "The empty host is OK for non-special schemes",
898            "href": "view-source+http://example.net/foo",
899            "new_value": "",
900            "expected": {
901                "href": "view-source+http:///foo",
902                "host": ""
903            }
904        },
905        {
906            "comment": "Path-only URLs can gain a host",
907            "href": "a:/foo",
908            "new_value": "example.net",
909            "expected": {
910                "href": "a://example.net/foo",
911                "host": "example.net"
912            }
913        },
914        {
915            "comment": "IPv4 address syntax is normalized",
916            "href": "http://example.net:8080",
917            "new_value": "0x7F000001",
918            "expected": {
919                "href": "http://127.0.0.1:8080/",
920                "host": "127.0.0.1:8080",
921                "hostname": "127.0.0.1",
922                "port": "8080"
923            }
924        },
925        {
926            "comment": "IPv6 address syntax is normalized",
927            "href": "http://example.net",
928            "new_value": "[::0:01]",
929            "expected": {
930                "href": "http://[::1]/",
931                "host": "[::1]",
932                "hostname": "[::1]",
933                "port": ""
934            }
935        },
936        {
937            "comment": "Stuff after a : delimiter is ignored",
938            "href": "http://example.net/path",
939            "new_value": "example.com:8080",
940            "expected": {
941                "href": "http://example.com/path",
942                "host": "example.com",
943                "hostname": "example.com",
944                "port": ""
945            }
946        },
947        {
948            "comment": "Stuff after a : delimiter is ignored",
949            "href": "http://example.net:8080/path",
950            "new_value": "example.com:",
951            "expected": {
952                "href": "http://example.com:8080/path",
953                "host": "example.com:8080",
954                "hostname": "example.com",
955                "port": "8080"
956            }
957        },
958        {
959            "comment": "Stuff after a / delimiter is ignored",
960            "href": "http://example.net/path",
961            "new_value": "example.com/stuff",
962            "expected": {
963                "href": "http://example.com/path",
964                "host": "example.com",
965                "hostname": "example.com",
966                "port": ""
967            }
968        },
969        {
970            "comment": "Stuff after a ? delimiter is ignored",
971            "href": "http://example.net/path",
972            "new_value": "example.com?stuff",
973            "expected": {
974                "href": "http://example.com/path",
975                "host": "example.com",
976                "hostname": "example.com",
977                "port": ""
978            }
979        },
980        {
981            "comment": "Stuff after a # delimiter is ignored",
982            "href": "http://example.net/path",
983            "new_value": "example.com#stuff",
984            "expected": {
985                "href": "http://example.com/path",
986                "host": "example.com",
987                "hostname": "example.com",
988                "port": ""
989            }
990        },
991        {
992            "comment": "Stuff after a \\ delimiter is ignored for special schemes",
993            "href": "http://example.net/path",
994            "new_value": "example.com\\stuff",
995            "expected": {
996                "href": "http://example.com/path",
997                "host": "example.com",
998                "hostname": "example.com",
999                "port": ""
1000            }
1001        },
1002        {
1003            "comment": "\\ is not a delimiter for non-special schemes, but still forbidden in hosts",
1004            "href": "view-source+http://example.net/path",
1005            "new_value": "example.com\\stuff",
1006            "expected": {
1007                "href": "view-source+http://example.net/path",
1008                "host": "example.net",
1009                "hostname": "example.net",
1010                "port": ""
1011            }
1012        },
1013        {
1014            "comment": "Broken IPv6",
1015            "href": "http://example.net/",
1016            "new_value": "[google.com]",
1017            "expected": {
1018                "href": "http://example.net/",
1019                "host": "example.net",
1020                "hostname": "example.net"
1021            }
1022        },
1023        {
1024            "href": "http://example.net/",
1025            "new_value": "[::1.2.3.4x]",
1026            "expected": {
1027                "href": "http://example.net/",
1028                "host": "example.net",
1029                "hostname": "example.net"
1030            }
1031        },
1032        {
1033            "href": "http://example.net/",
1034            "new_value": "[::1.2.3.]",
1035            "expected": {
1036                "href": "http://example.net/",
1037                "host": "example.net",
1038                "hostname": "example.net"
1039            }
1040        },
1041        {
1042            "href": "http://example.net/",
1043            "new_value": "[::1.2.]",
1044            "expected": {
1045                "href": "http://example.net/",
1046                "host": "example.net",
1047                "hostname": "example.net"
1048            }
1049        },
1050        {
1051            "href": "http://example.net/",
1052            "new_value": "[::1.]",
1053            "expected": {
1054                "href": "http://example.net/",
1055                "host": "example.net",
1056                "hostname": "example.net"
1057            }
1058        }
1059    ],
1060    "port": [
1061        {
1062            "href": "http://example.net",
1063            "new_value": "8080",
1064            "expected": {
1065                "href": "http://example.net:8080/",
1066                "host": "example.net:8080",
1067                "hostname": "example.net",
1068                "port": "8080"
1069            }
1070        },
1071        {
1072            "comment": "Port number is removed if empty is the new value",
1073            "href": "http://example.net:8080",
1074            "new_value": "",
1075            "expected": {
1076                "href": "http://example.net/",
1077                "host": "example.net",
1078                "hostname": "example.net",
1079                "port": ""
1080            }
1081        },
1082        {
1083            "comment": "Default port number is removed",
1084            "href": "http://example.net:8080",
1085            "new_value": "80",
1086            "expected": {
1087                "href": "http://example.net/",
1088                "host": "example.net",
1089                "hostname": "example.net",
1090                "port": ""
1091            }
1092        },
1093        {
1094            "comment": "Default port number is removed",
1095            "href": "https://example.net:4433",
1096            "new_value": "443",
1097            "expected": {
1098                "href": "https://example.net/",
1099                "host": "example.net",
1100                "hostname": "example.net",
1101                "port": ""
1102            }
1103        },
1104        {
1105            "comment": "Default port number is only removed for the relevant scheme",
1106            "href": "https://example.net",
1107            "new_value": "80",
1108            "expected": {
1109                "href": "https://example.net:80/",
1110                "host": "example.net:80",
1111                "hostname": "example.net",
1112                "port": "80"
1113            }
1114        },
1115        {
1116            "comment": "Stuff after a / delimiter is ignored",
1117            "href": "http://example.net/path",
1118            "new_value": "8080/stuff",
1119            "expected": {
1120                "href": "http://example.net:8080/path",
1121                "host": "example.net:8080",
1122                "hostname": "example.net",
1123                "port": "8080"
1124            }
1125        },
1126        {
1127            "comment": "Stuff after a ? delimiter is ignored",
1128            "href": "http://example.net/path",
1129            "new_value": "8080?stuff",
1130            "expected": {
1131                "href": "http://example.net:8080/path",
1132                "host": "example.net:8080",
1133                "hostname": "example.net",
1134                "port": "8080"
1135            }
1136        },
1137        {
1138            "comment": "Stuff after a # delimiter is ignored",
1139            "href": "http://example.net/path",
1140            "new_value": "8080#stuff",
1141            "expected": {
1142                "href": "http://example.net:8080/path",
1143                "host": "example.net:8080",
1144                "hostname": "example.net",
1145                "port": "8080"
1146            }
1147        },
1148        {
1149            "comment": "Stuff after a \\ delimiter is ignored for special schemes",
1150            "href": "http://example.net/path",
1151            "new_value": "8080\\stuff",
1152            "expected": {
1153                "href": "http://example.net:8080/path",
1154                "host": "example.net:8080",
1155                "hostname": "example.net",
1156                "port": "8080"
1157            }
1158        },
1159        {
1160            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
1161            "href": "view-source+http://example.net/path",
1162            "new_value": "8080stuff2",
1163            "expected": {
1164                "href": "view-source+http://example.net:8080/path",
1165                "host": "example.net:8080",
1166                "hostname": "example.net",
1167                "port": "8080"
1168            }
1169        },
1170        {
1171            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
1172            "href": "http://example.net/path",
1173            "new_value": "8080stuff2",
1174            "expected": {
1175                "href": "http://example.net:8080/path",
1176                "host": "example.net:8080",
1177                "hostname": "example.net",
1178                "port": "8080"
1179            }
1180        },
1181        {
1182            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
1183            "href": "http://example.net/path",
1184            "new_value": "8080+2",
1185            "expected": {
1186                "href": "http://example.net:8080/path",
1187                "host": "example.net:8080",
1188                "hostname": "example.net",
1189                "port": "8080"
1190            }
1191        },
1192        {
1193            "comment": "Port numbers are 16 bit integers",
1194            "href": "http://example.net/path",
1195            "new_value": "65535",
1196            "expected": {
1197                "href": "http://example.net:65535/path",
1198                "host": "example.net:65535",
1199                "hostname": "example.net",
1200                "port": "65535"
1201            }
1202        },
1203        {
1204            "comment": "Port numbers are 16 bit integers, overflowing is an error",
1205            "href": "http://example.net:8080/path",
1206            "new_value": "65536",
1207            "expected": {
1208                "href": "http://example.net:8080/path",
1209                "host": "example.net:8080",
1210                "hostname": "example.net",
1211                "port": "8080"
1212            }
1213        },
1214        {
1215            "comment": "Port numbers are 16 bit integers, overflowing is an error",
1216            "href": "non-special://example.net:8080/path",
1217            "new_value": "65536",
1218            "expected": {
1219                "href": "non-special://example.net:8080/path",
1220                "host": "example.net:8080",
1221                "hostname": "example.net",
1222                "port": "8080"
1223            }
1224        },
1225        {
1226            "href": "file://test/",
1227            "new_value": "12",
1228            "expected": {
1229                "href": "file://test/",
1230                "port": ""
1231            }
1232        },
1233        {
1234            "href": "file://localhost/",
1235            "new_value": "12",
1236            "expected": {
1237                "href": "file:///",
1238                "port": ""
1239            }
1240        },
1241        {
1242            "href": "non-base:value",
1243            "new_value": "12",
1244            "expected": {
1245                "href": "non-base:value",
1246                "port": ""
1247            }
1248        },
1249        {
1250            "href": "sc:///",
1251            "new_value": "12",
1252            "expected": {
1253                "href": "sc:///",
1254                "port": ""
1255            }
1256        },
1257        {
1258            "href": "sc://x/",
1259            "new_value": "12",
1260            "expected": {
1261                "href": "sc://x:12/",
1262                "port": "12"
1263            }
1264        },
1265        {
1266            "href": "javascript://x/",
1267            "new_value": "12",
1268            "expected": {
1269                "href": "javascript://x:12/",
1270                "port": "12"
1271            }
1272        }
1273    ],
1274    "pathname": [
1275        {
1276            "comment": "Cannot-be-a-base don’t have a path",
1277            "href": "mailto:me@example.net",
1278            "new_value": "/foo",
1279            "expected": {
1280                "href": "mailto:me@example.net",
1281                "pathname": "me@example.net"
1282            }
1283        },
1284        {
1285            "href": "unix:/run/foo.socket?timeout=10",
1286            "new_value": "/var/log/../run/bar.socket",
1287            "expected": {
1288                "href": "unix:/var/run/bar.socket?timeout=10",
1289                "pathname": "/var/run/bar.socket"
1290            }
1291        },
1292        {
1293            "href": "https://example.net#nav",
1294            "new_value": "home",
1295            "expected": {
1296                "href": "https://example.net/home#nav",
1297                "pathname": "/home"
1298            }
1299        },
1300        {
1301            "href": "https://example.net#nav",
1302            "new_value": "../home",
1303            "expected": {
1304                "href": "https://example.net/home#nav",
1305                "pathname": "/home"
1306            }
1307        },
1308        {
1309            "comment": "\\ is a segment delimiter for 'special' URLs",
1310            "href": "http://example.net/home?lang=fr#nav",
1311            "new_value": "\\a\\%2E\\b\\%2e.\\c",
1312            "expected": {
1313                "href": "http://example.net/a/c?lang=fr#nav",
1314                "pathname": "/a/c"
1315            }
1316        },
1317        {
1318            "comment": "\\ is *not* a segment delimiter for non-'special' URLs",
1319            "href": "view-source+http://example.net/home?lang=fr#nav",
1320            "new_value": "\\a\\%2E\\b\\%2e.\\c",
1321            "expected": {
1322                "href": "view-source+http://example.net/\\a\\%2E\\b\\%2e.\\c?lang=fr#nav",
1323                "pathname": "/\\a\\%2E\\b\\%2e.\\c"
1324            }
1325        },
1326        {
1327            "comment": "UTF-8 percent encoding with the default encode set. Tabs and newlines are removed. Leading or training C0 controls and space are removed.",
1328            "href": "a:/",
1329            "new_value": "\u0000\u0001\t\n\r\u001f !\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
1330            "expected": {
1331                "href": "a:/!%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E%3F@AZ[\\]^_%60az%7B|%7D~%7F%C2%80%C2%81%C3%89%C3%A9",
1332                "pathname": "/!%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E%3F@AZ[\\]^_%60az%7B|%7D~%7F%C2%80%C2%81%C3%89%C3%A9"
1333            }
1334        },
1335        {
1336            "comment": "Bytes already percent-encoded are left as-is, including %2E outside dotted segments.",
1337            "href": "http://example.net",
1338            "new_value": "%2e%2E%c3%89té",
1339            "expected": {
1340                "href": "http://example.net/%2e%2E%c3%89t%C3%A9",
1341                "pathname": "/%2e%2E%c3%89t%C3%A9"
1342            }
1343        },
1344        {
1345            "comment": "? needs to be encoded",
1346            "href": "http://example.net",
1347            "new_value": "?",
1348            "expected": {
1349                "href": "http://example.net/%3F",
1350                "pathname": "/%3F"
1351            }
1352        },
1353        {
1354            "comment": "# needs to be encoded",
1355            "href": "http://example.net",
1356            "new_value": "#",
1357            "expected": {
1358                "href": "http://example.net/%23",
1359                "pathname": "/%23"
1360            }
1361        },
1362        {
1363            "comment": "? needs to be encoded, non-special scheme",
1364            "href": "sc://example.net",
1365            "new_value": "?",
1366            "expected": {
1367                "href": "sc://example.net/%3F",
1368                "pathname": "/%3F"
1369            }
1370        },
1371        {
1372            "comment": "# needs to be encoded, non-special scheme",
1373            "href": "sc://example.net",
1374            "new_value": "#",
1375            "expected": {
1376                "href": "sc://example.net/%23",
1377                "pathname": "/%23"
1378            }
1379        }
1380    ],
1381    "search": [
1382        {
1383            "href": "https://example.net#nav",
1384            "new_value": "lang=fr",
1385            "expected": {
1386                "href": "https://example.net/?lang=fr#nav",
1387                "search": "?lang=fr"
1388            }
1389        },
1390        {
1391            "href": "https://example.net?lang=en-US#nav",
1392            "new_value": "lang=fr",
1393            "expected": {
1394                "href": "https://example.net/?lang=fr#nav",
1395                "search": "?lang=fr"
1396            }
1397        },
1398        {
1399            "href": "https://example.net?lang=en-US#nav",
1400            "new_value": "?lang=fr",
1401            "expected": {
1402                "href": "https://example.net/?lang=fr#nav",
1403                "search": "?lang=fr"
1404            }
1405        },
1406        {
1407            "href": "https://example.net?lang=en-US#nav",
1408            "new_value": "??lang=fr",
1409            "expected": {
1410                "href": "https://example.net/??lang=fr#nav",
1411                "search": "??lang=fr"
1412            }
1413        },
1414        {
1415            "href": "https://example.net?lang=en-US#nav",
1416            "new_value": "?",
1417            "expected": {
1418                "href": "https://example.net/?#nav",
1419                "search": ""
1420            }
1421        },
1422        {
1423            "href": "https://example.net?lang=en-US#nav",
1424            "new_value": "",
1425            "expected": {
1426                "href": "https://example.net/#nav",
1427                "search": ""
1428            }
1429        },
1430        {
1431            "href": "https://example.net?lang=en-US",
1432            "new_value": "",
1433            "expected": {
1434                "href": "https://example.net/",
1435                "search": ""
1436            }
1437        },
1438        {
1439            "href": "https://example.net",
1440            "new_value": "",
1441            "expected": {
1442                "href": "https://example.net/",
1443                "search": ""
1444            }
1445        },
1446        {
1447            "comment": "UTF-8 percent encoding with the query encode set. Tabs and newlines are removed. Leading or training C0 controls and space are removed.",
1448            "href": "a:/",
1449            "new_value": "\u0000\u0001\t\n\r\u001f !\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
1450            "expected": {
1451                "href": "a:/?!%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9",
1452                "search": "?!%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9"
1453            }
1454        },
1455        {
1456            "comment": "Bytes already percent-encoded are left as-is",
1457            "href": "http://example.net",
1458            "new_value": "%c3%89té",
1459            "expected": {
1460                "href": "http://example.net/?%c3%89t%C3%A9",
1461                "search": "?%c3%89t%C3%A9"
1462            }
1463        }
1464    ],
1465    "hash": [
1466        {
1467            "href": "https://example.net",
1468            "new_value": "main",
1469            "expected": {
1470                "href": "https://example.net/#main",
1471                "hash": "#main"
1472            }
1473        },
1474        {
1475            "href": "https://example.net#nav",
1476            "new_value": "main",
1477            "expected": {
1478                "href": "https://example.net/#main",
1479                "hash": "#main"
1480            }
1481        },
1482        {
1483            "href": "https://example.net?lang=en-US",
1484            "new_value": "##nav",
1485            "expected": {
1486                "href": "https://example.net/?lang=en-US##nav",
1487                "hash": "##nav"
1488            }
1489        },
1490        {
1491            "href": "https://example.net?lang=en-US#nav",
1492            "new_value": "#main",
1493            "expected": {
1494                "href": "https://example.net/?lang=en-US#main",
1495                "hash": "#main"
1496            }
1497        },
1498        {
1499            "href": "https://example.net?lang=en-US#nav",
1500            "new_value": "#",
1501            "expected": {
1502                "href": "https://example.net/?lang=en-US#",
1503                "hash": ""
1504            }
1505        },
1506        {
1507            "href": "https://example.net?lang=en-US#nav",
1508            "new_value": "",
1509            "expected": {
1510                "href": "https://example.net/?lang=en-US",
1511                "hash": ""
1512            }
1513        },
1514        {
1515            "comment": "Simple percent-encoding; nuls, tabs, and newlines are removed",
1516            "href": "a:/",
1517            "new_value": "\u0000\u0001\t\n\r\u001f !\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
1518            "expected": {
1519                "href": "a:/#!%01%1F !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9",
1520                "hash": "#!%01%1F !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9"
1521            }
1522        },
1523        {
1524            "comment": "Bytes already percent-encoded are left as-is",
1525            "href": "http://example.net",
1526            "new_value": "%c3%89té",
1527            "expected": {
1528                "href": "http://example.net/#%c3%89t%C3%A9",
1529                "hash": "#%c3%89t%C3%A9"
1530            }
1531        }
1532    ]
1533}
1534