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            "href": "javascript:alert(1)",
44            "new_value": "defuse",
45            "expected": {
46                "href": "defuse:alert(1)",
47                "protocol": "defuse:"
48            }
49        },
50        {
51            "comment": "Upper-case ASCII is lower-cased",
52            "href": "a://example.net",
53            "new_value": "B",
54            "expected": {
55                "href": "b://example.net",
56                "protocol": "b:"
57            }
58        },
59        {
60            "comment": "Non-ASCII is rejected",
61            "href": "a://example.net",
62            "new_value": "é",
63            "expected": {
64                "href": "a://example.net",
65                "protocol": "a:"
66            }
67        },
68        {
69            "comment": "No leading digit",
70            "href": "a://example.net",
71            "new_value": "0b",
72            "expected": {
73                "href": "a://example.net",
74                "protocol": "a:"
75            }
76        },
77        {
78            "comment": "No leading punctuation",
79            "href": "a://example.net",
80            "new_value": "+b",
81            "expected": {
82                "href": "a://example.net",
83                "protocol": "a:"
84            }
85        },
86        {
87            "href": "a://example.net",
88            "new_value": "bC0+-.",
89            "expected": {
90                "href": "bc0+-.://example.net",
91                "protocol": "bc0+-.:"
92            }
93        },
94        {
95            "comment": "Only some punctuation is acceptable",
96            "href": "a://example.net",
97            "new_value": "b,c",
98            "expected": {
99                "href": "a://example.net",
100                "protocol": "a:"
101            }
102        },
103        {
104            "comment": "Non-ASCII is rejected",
105            "href": "a://example.net",
106            "new_value": "bé",
107            "expected": {
108                "href": "a://example.net",
109                "protocol": "a:"
110            }
111        },
112        {
113            "comment": "Can’t switch from URL containing username/password/port to file",
114            "href": "http://test@example.net",
115            "new_value": "file",
116            "expected": {
117                "href": "http://test@example.net/",
118                "protocol": "http:"
119            }
120        },
121        {
122            "href": "https://example.net:1234",
123            "new_value": "file",
124            "expected": {
125                "href": "https://example.net:1234/",
126                "protocol": "https:"
127            }
128        },
129        {
130            "href": "wss://x:x@example.net:1234",
131            "new_value": "file",
132            "expected": {
133                "href": "wss://x:x@example.net:1234/",
134                "protocol": "wss:"
135            }
136        },
137        {
138            "comment": "Can’t switch from file URL with no host",
139            "href": "file://localhost/",
140            "new_value": "http",
141            "expected": {
142                "href": "file:///",
143                "protocol": "file:"
144            }
145        },
146        {
147            "href": "file:///test",
148            "new_value": "https",
149            "expected": {
150                "href": "file:///test",
151                "protocol": "file:"
152            }
153        },
154        {
155            "href": "file:",
156            "new_value": "wss",
157            "expected": {
158                "href": "file:///",
159                "protocol": "file:"
160            }
161        },
162        {
163            "comment": "Can’t switch from special scheme to non-special",
164            "href": "http://example.net",
165            "new_value": "b",
166            "expected": {
167                "href": "http://example.net/",
168                "protocol": "http:"
169            }
170        },
171        {
172            "href": "file://hi/path",
173            "new_value": "s",
174            "expected": {
175                "href": "file://hi/path",
176                "protocol": "file:"
177            }
178        },
179        {
180            "href": "https://example.net",
181            "new_value": "s",
182            "expected": {
183                "href": "https://example.net/",
184                "protocol": "https:"
185            }
186        },
187        {
188            "href": "ftp://example.net",
189            "new_value": "test",
190            "expected": {
191                "href": "ftp://example.net/",
192                "protocol": "ftp:"
193            }
194        },
195        {
196            "comment": "Cannot-be-a-base URL doesn’t have a host, but URL in a special scheme must.",
197            "href": "mailto:me@example.net",
198            "new_value": "http",
199            "expected": {
200                "href": "mailto:me@example.net",
201                "protocol": "mailto:"
202            }
203        },
204        {
205            "comment": "Can’t switch from non-special scheme to special",
206            "href": "ssh://me@example.net",
207            "new_value": "http",
208            "expected": {
209                "href": "ssh://me@example.net",
210                "protocol": "ssh:"
211            }
212        },
213        {
214            "href": "ssh://me@example.net",
215            "new_value": "https",
216            "expected": {
217                "href": "ssh://me@example.net",
218                "protocol": "ssh:"
219            }
220        },
221        {
222            "href": "ssh://me@example.net",
223            "new_value": "file",
224            "expected": {
225                "href": "ssh://me@example.net",
226                "protocol": "ssh:"
227            }
228        },
229        {
230            "href": "ssh://example.net",
231            "new_value": "file",
232            "expected": {
233                "href": "ssh://example.net",
234                "protocol": "ssh:"
235            }
236        },
237        {
238            "href": "nonsense:///test",
239            "new_value": "https",
240            "expected": {
241                "href": "nonsense:///test",
242                "protocol": "nonsense:"
243            }
244        },
245        {
246            "comment": "Stuff after the first ':' is ignored",
247            "href": "http://example.net",
248            "new_value": "https:foo : bar",
249            "expected": {
250                "href": "https://example.net/",
251                "protocol": "https:"
252            }
253        },
254        {
255            "comment": "Stuff after the first ':' is ignored",
256            "href": "data:text/html,<p>Test",
257            "new_value": "view-source+data:foo : bar",
258            "expected": {
259                "href": "view-source+data:text/html,<p>Test",
260                "protocol": "view-source+data:"
261            }
262        },
263        {
264            "comment": "Port is set to null if it is the default for new scheme.",
265            "href": "http://foo.com:443/",
266            "new_value": "https",
267            "expected": {
268                "href": "https://foo.com/",
269                "protocol": "https:",
270                "port": ""
271            }
272        }
273    ],
274    "username": [
275        {
276            "comment": "No host means no username",
277            "href": "file:///home/you/index.html",
278            "new_value": "me",
279            "expected": {
280                "href": "file:///home/you/index.html",
281                "username": ""
282            }
283        },
284        {
285            "comment": "No host means no username",
286            "href": "unix:/run/foo.socket",
287            "new_value": "me",
288            "expected": {
289                "href": "unix:/run/foo.socket",
290                "username": ""
291            }
292        },
293        {
294            "comment": "Cannot-be-a-base means no username",
295            "href": "mailto:you@example.net",
296            "new_value": "me",
297            "expected": {
298                "href": "mailto:you@example.net",
299                "username": ""
300            }
301        },
302        {
303            "href": "javascript:alert(1)",
304            "new_value": "wario",
305            "expected": {
306                "href": "javascript:alert(1)",
307                "username": ""
308            }
309        },
310        {
311            "href": "http://example.net",
312            "new_value": "me",
313            "expected": {
314                "href": "http://me@example.net/",
315                "username": "me"
316            }
317        },
318        {
319            "href": "http://:secret@example.net",
320            "new_value": "me",
321            "expected": {
322                "href": "http://me:secret@example.net/",
323                "username": "me"
324            }
325        },
326        {
327            "href": "http://me@example.net",
328            "new_value": "",
329            "expected": {
330                "href": "http://example.net/",
331                "username": ""
332            }
333        },
334        {
335            "href": "http://me:secret@example.net",
336            "new_value": "",
337            "expected": {
338                "href": "http://:secret@example.net/",
339                "username": ""
340            }
341        },
342        {
343            "comment": "UTF-8 percent encoding with the userinfo encode set.",
344            "href": "http://example.net",
345            "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
346            "expected": {
347                "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/",
348                "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"
349            }
350        },
351        {
352            "comment": "Bytes already percent-encoded are left as-is.",
353            "href": "http://example.net",
354            "new_value": "%c3%89té",
355            "expected": {
356                "href": "http://%c3%89t%C3%A9@example.net/",
357                "username": "%c3%89t%C3%A9"
358            }
359        },
360        {
361            "href": "sc:///",
362            "new_value": "x",
363            "expected": {
364                "href": "sc:///",
365                "username": ""
366            }
367        },
368        {
369            "href": "javascript://x/",
370            "new_value": "wario",
371            "expected": {
372                "href": "javascript://wario@x/",
373                "username": "wario"
374            }
375        },
376        {
377            "href": "file://test/",
378            "new_value": "test",
379            "expected": {
380                "href": "file://test/",
381                "username": ""
382            }
383        }
384    ],
385    "password": [
386        {
387            "comment": "No host means no password",
388            "href": "file:///home/me/index.html",
389            "new_value": "secret",
390            "expected": {
391                "href": "file:///home/me/index.html",
392                "password": ""
393            }
394        },
395        {
396            "comment": "No host means no password",
397            "href": "unix:/run/foo.socket",
398            "new_value": "secret",
399            "expected": {
400                "href": "unix:/run/foo.socket",
401                "password": ""
402            }
403        },
404        {
405            "comment": "Cannot-be-a-base means no password",
406            "href": "mailto:me@example.net",
407            "new_value": "secret",
408            "expected": {
409                "href": "mailto:me@example.net",
410                "password": ""
411            }
412        },
413        {
414            "href": "http://example.net",
415            "new_value": "secret",
416            "expected": {
417                "href": "http://:secret@example.net/",
418                "password": "secret"
419            }
420        },
421        {
422            "href": "http://me@example.net",
423            "new_value": "secret",
424            "expected": {
425                "href": "http://me:secret@example.net/",
426                "password": "secret"
427            }
428        },
429        {
430            "href": "http://:secret@example.net",
431            "new_value": "",
432            "expected": {
433                "href": "http://example.net/",
434                "password": ""
435            }
436        },
437        {
438            "href": "http://me:secret@example.net",
439            "new_value": "",
440            "expected": {
441                "href": "http://me@example.net/",
442                "password": ""
443            }
444        },
445        {
446            "comment": "UTF-8 percent encoding with the userinfo encode set.",
447            "href": "http://example.net",
448            "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
449            "expected": {
450                "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/",
451                "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"
452            }
453        },
454        {
455            "comment": "Bytes already percent-encoded are left as-is.",
456            "href": "http://example.net",
457            "new_value": "%c3%89té",
458            "expected": {
459                "href": "http://:%c3%89t%C3%A9@example.net/",
460                "password": "%c3%89t%C3%A9"
461            }
462        },
463        {
464            "href": "sc:///",
465            "new_value": "x",
466            "expected": {
467                "href": "sc:///",
468                "password": ""
469            }
470        },
471        {
472            "href": "javascript://x/",
473            "new_value": "bowser",
474            "expected": {
475                "href": "javascript://:bowser@x/",
476                "password": "bowser"
477            }
478        },
479        {
480            "href": "file://test/",
481            "new_value": "test",
482            "expected": {
483                "href": "file://test/",
484                "password": ""
485            }
486        }
487    ],
488    "host": [
489        {
490            "comment": "Non-special scheme",
491            "href": "sc://x/",
492            "new_value": "\u0000",
493            "expected": {
494                "href": "sc://x/",
495                "host": "x",
496                "hostname": "x"
497            }
498        },
499        {
500            "href": "sc://x/",
501            "new_value": "\u0009",
502            "expected": {
503                "href": "sc:///",
504                "host": "",
505                "hostname": ""
506            }
507        },
508        {
509            "href": "sc://x/",
510            "new_value": "\u000A",
511            "expected": {
512                "href": "sc:///",
513                "host": "",
514                "hostname": ""
515            }
516        },
517        {
518            "href": "sc://x/",
519            "new_value": "\u000D",
520            "expected": {
521                "href": "sc:///",
522                "host": "",
523                "hostname": ""
524            }
525        },
526        {
527            "href": "sc://x/",
528            "new_value": " ",
529            "expected": {
530                "href": "sc://x/",
531                "host": "x",
532                "hostname": "x"
533            }
534        },
535        {
536            "href": "sc://x/",
537            "new_value": "#",
538            "expected": {
539                "href": "sc:///",
540                "host": "",
541                "hostname": ""
542            }
543        },
544        {
545            "href": "sc://x/",
546            "new_value": "/",
547            "expected": {
548                "href": "sc:///",
549                "host": "",
550                "hostname": ""
551            }
552        },
553        {
554            "href": "sc://x/",
555            "new_value": "?",
556            "expected": {
557                "href": "sc:///",
558                "host": "",
559                "hostname": ""
560            }
561        },
562        {
563            "href": "sc://x/",
564            "new_value": "@",
565            "expected": {
566                "href": "sc://x/",
567                "host": "x",
568                "hostname": "x"
569            }
570        },
571        {
572            "href": "sc://x/",
573            "new_value": "ß",
574            "expected": {
575                "href": "sc://%C3%9F/",
576                "host": "%C3%9F",
577                "hostname": "%C3%9F"
578            }
579        },
580        {
581            "comment": "IDNA Nontransitional_Processing",
582            "href": "https://x/",
583            "new_value": "ß",
584            "expected": {
585                "href": "https://xn--zca/",
586                "host": "xn--zca",
587                "hostname": "xn--zca"
588            }
589        },
590        {
591            "comment": "Cannot-be-a-base means no host",
592            "href": "mailto:me@example.net",
593            "new_value": "example.com",
594            "expected": {
595                "href": "mailto:me@example.net",
596                "host": ""
597            }
598        },
599        {
600            "comment": "Cannot-be-a-base means no host",
601            "href": "data:text/plain,Stuff",
602            "new_value": "example.net",
603            "expected": {
604                "href": "data:text/plain,Stuff",
605                "host": ""
606            }
607        },
608        {
609            "href": "http://example.net",
610            "new_value": "example.com:8080",
611            "expected": {
612                "href": "http://example.com:8080/",
613                "host": "example.com:8080",
614                "hostname": "example.com",
615                "port": "8080"
616            }
617        },
618        {
619            "comment": "Port number is unchanged if not specified in the new value",
620            "href": "http://example.net:8080",
621            "new_value": "example.com",
622            "expected": {
623                "href": "http://example.com:8080/",
624                "host": "example.com:8080",
625                "hostname": "example.com",
626                "port": "8080"
627            }
628        },
629        {
630            "comment": "Port number is unchanged if not specified",
631            "href": "http://example.net:8080",
632            "new_value": "example.com:",
633            "expected": {
634                "href": "http://example.com:8080/",
635                "host": "example.com:8080",
636                "hostname": "example.com",
637                "port": "8080"
638            }
639        },
640        {
641            "comment": "The empty host is not valid for special schemes",
642            "href": "http://example.net",
643            "new_value": "",
644            "expected": {
645                "href": "http://example.net/",
646                "host": "example.net"
647            }
648        },
649        {
650            "comment": "The empty host is OK for non-special schemes",
651            "href": "view-source+http://example.net/foo",
652            "new_value": "",
653            "expected": {
654                "href": "view-source+http:///foo",
655                "host": ""
656            }
657        },
658        {
659            "comment": "Path-only URLs can gain a host",
660            "href": "a:/foo",
661            "new_value": "example.net",
662            "expected": {
663                "href": "a://example.net/foo",
664                "host": "example.net"
665            }
666        },
667        {
668            "comment": "IPv4 address syntax is normalized",
669            "href": "http://example.net",
670            "new_value": "0x7F000001:8080",
671            "expected": {
672                "href": "http://127.0.0.1:8080/",
673                "host": "127.0.0.1:8080",
674                "hostname": "127.0.0.1",
675                "port": "8080"
676            }
677        },
678        {
679            "comment": "IPv6 address syntax is normalized",
680            "href": "http://example.net",
681            "new_value": "[::0:01]:2",
682            "expected": {
683                "href": "http://[::1]:2/",
684                "host": "[::1]:2",
685                "hostname": "[::1]",
686                "port": "2"
687            }
688        },
689        {
690            "comment": "IPv6 literal address with port, crbug.com/1012416",
691            "href": "http://example.net",
692            "new_value": "[2001:db8::2]:4002",
693            "expected": {
694                "href": "http://[2001:db8::2]:4002/",
695                "host": "[2001:db8::2]:4002",
696                "hostname": "[2001:db8::2]",
697                "port": "4002"
698             }
699        },
700        {
701            "comment": "Default port number is removed",
702            "href": "http://example.net",
703            "new_value": "example.com:80",
704            "expected": {
705                "href": "http://example.com/",
706                "host": "example.com",
707                "hostname": "example.com",
708                "port": ""
709            }
710        },
711        {
712            "comment": "Default port number is removed",
713            "href": "https://example.net",
714            "new_value": "example.com:443",
715            "expected": {
716                "href": "https://example.com/",
717                "host": "example.com",
718                "hostname": "example.com",
719                "port": ""
720            }
721        },
722        {
723            "comment": "Default port number is only removed for the relevant scheme",
724            "href": "https://example.net",
725            "new_value": "example.com:80",
726            "expected": {
727                "href": "https://example.com:80/",
728                "host": "example.com:80",
729                "hostname": "example.com",
730                "port": "80"
731            }
732        },
733        {
734            "comment": "Port number is removed if new port is scheme default and existing URL has a non-default port",
735            "href": "http://example.net:8080",
736            "new_value": "example.com:80",
737            "expected": {
738                "href": "http://example.com/",
739                "host": "example.com",
740                "hostname": "example.com",
741                "port": ""
742            }
743        },
744        {
745            "comment": "Stuff after a / delimiter is ignored",
746            "href": "http://example.net/path",
747            "new_value": "example.com/stuff",
748            "expected": {
749                "href": "http://example.com/path",
750                "host": "example.com",
751                "hostname": "example.com",
752                "port": ""
753            }
754        },
755        {
756            "comment": "Stuff after a / delimiter is ignored",
757            "href": "http://example.net/path",
758            "new_value": "example.com:8080/stuff",
759            "expected": {
760                "href": "http://example.com:8080/path",
761                "host": "example.com:8080",
762                "hostname": "example.com",
763                "port": "8080"
764            }
765        },
766        {
767            "comment": "Stuff after a ? delimiter is ignored",
768            "href": "http://example.net/path",
769            "new_value": "example.com?stuff",
770            "expected": {
771                "href": "http://example.com/path",
772                "host": "example.com",
773                "hostname": "example.com",
774                "port": ""
775            }
776        },
777        {
778            "comment": "Stuff after a ? delimiter is ignored",
779            "href": "http://example.net/path",
780            "new_value": "example.com:8080?stuff",
781            "expected": {
782                "href": "http://example.com:8080/path",
783                "host": "example.com:8080",
784                "hostname": "example.com",
785                "port": "8080"
786            }
787        },
788        {
789            "comment": "Stuff after a # delimiter is ignored",
790            "href": "http://example.net/path",
791            "new_value": "example.com#stuff",
792            "expected": {
793                "href": "http://example.com/path",
794                "host": "example.com",
795                "hostname": "example.com",
796                "port": ""
797            }
798        },
799        {
800            "comment": "Stuff after a # delimiter is ignored",
801            "href": "http://example.net/path",
802            "new_value": "example.com:8080#stuff",
803            "expected": {
804                "href": "http://example.com:8080/path",
805                "host": "example.com:8080",
806                "hostname": "example.com",
807                "port": "8080"
808            }
809        },
810        {
811            "comment": "Stuff after a \\ delimiter is ignored for special schemes",
812            "href": "http://example.net/path",
813            "new_value": "example.com\\stuff",
814            "expected": {
815                "href": "http://example.com/path",
816                "host": "example.com",
817                "hostname": "example.com",
818                "port": ""
819            }
820        },
821        {
822            "comment": "Stuff after a \\ delimiter is ignored for special schemes",
823            "href": "http://example.net/path",
824            "new_value": "example.com:8080\\stuff",
825            "expected": {
826                "href": "http://example.com:8080/path",
827                "host": "example.com:8080",
828                "hostname": "example.com",
829                "port": "8080"
830            }
831        },
832        {
833            "comment": "\\ is not a delimiter for non-special schemes, but still forbidden in hosts",
834            "href": "view-source+http://example.net/path",
835            "new_value": "example.com\\stuff",
836            "expected": {
837                "href": "view-source+http://example.net/path",
838                "host": "example.net",
839                "hostname": "example.net",
840                "port": ""
841            }
842        },
843        {
844            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
845            "href": "view-source+http://example.net/path",
846            "new_value": "example.com:8080stuff2",
847            "expected": {
848                "href": "view-source+http://example.com:8080/path",
849                "host": "example.com:8080",
850                "hostname": "example.com",
851                "port": "8080"
852            }
853        },
854        {
855            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
856            "href": "http://example.net/path",
857            "new_value": "example.com:8080stuff2",
858            "expected": {
859                "href": "http://example.com:8080/path",
860                "host": "example.com:8080",
861                "hostname": "example.com",
862                "port": "8080"
863            }
864        },
865        {
866            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
867            "href": "http://example.net/path",
868            "new_value": "example.com:8080+2",
869            "expected": {
870                "href": "http://example.com:8080/path",
871                "host": "example.com:8080",
872                "hostname": "example.com",
873                "port": "8080"
874            }
875        },
876        {
877            "comment": "Port numbers are 16 bit integers",
878            "href": "http://example.net/path",
879            "new_value": "example.com:65535",
880            "expected": {
881                "href": "http://example.com:65535/path",
882                "host": "example.com:65535",
883                "hostname": "example.com",
884                "port": "65535"
885            }
886        },
887        {
888            "comment": "Port numbers are 16 bit integers, overflowing is an error. Hostname is still set, though.",
889            "href": "http://example.net/path",
890            "new_value": "example.com:65536",
891            "expected": {
892                "href": "http://example.com/path",
893                "host": "example.com",
894                "hostname": "example.com",
895                "port": ""
896            }
897        },
898        {
899            "comment": "Broken IPv6",
900            "href": "http://example.net/",
901            "new_value": "[google.com]",
902            "expected": {
903                "href": "http://example.net/",
904                "host": "example.net",
905                "hostname": "example.net"
906            }
907        },
908        {
909            "href": "http://example.net/",
910            "new_value": "[::1.2.3.4x]",
911            "expected": {
912                "href": "http://example.net/",
913                "host": "example.net",
914                "hostname": "example.net"
915            }
916        },
917        {
918            "href": "http://example.net/",
919            "new_value": "[::1.2.3.]",
920            "expected": {
921                "href": "http://example.net/",
922                "host": "example.net",
923                "hostname": "example.net"
924            }
925        },
926        {
927            "href": "http://example.net/",
928            "new_value": "[::1.2.]",
929            "expected": {
930                "href": "http://example.net/",
931                "host": "example.net",
932                "hostname": "example.net"
933            }
934        },
935        {
936            "href": "http://example.net/",
937            "new_value": "[::1.]",
938            "expected": {
939                "href": "http://example.net/",
940                "host": "example.net",
941                "hostname": "example.net"
942            }
943        },
944        {
945            "href": "file://y/",
946            "new_value": "x:123",
947            "expected": {
948                "href": "file://y/",
949                "host": "y",
950                "hostname": "y",
951                "port": ""
952            }
953        },
954        {
955            "href": "file://y/",
956            "new_value": "loc%41lhost",
957            "expected": {
958                "href": "file:///",
959                "host": "",
960                "hostname": "",
961                "port": ""
962            }
963        },
964        {
965            "href": "file://hi/x",
966            "new_value": "",
967            "expected": {
968                "href": "file:///x",
969                "host": "",
970                "hostname": "",
971                "port": ""
972            }
973        },
974        {
975            "href": "sc://test@test/",
976            "new_value": "",
977            "expected": {
978                "href": "sc://test@test/",
979                "host": "test",
980                "hostname": "test",
981                "username": "test"
982            }
983        },
984        {
985            "href": "sc://test:12/",
986            "new_value": "",
987            "expected": {
988                "href": "sc://test:12/",
989                "host": "test:12",
990                "hostname": "test",
991                "port": "12"
992            }
993        },
994        {
995            "comment": "Leading / is not stripped",
996            "href": "http://example.com/",
997            "new_value": "///bad.com",
998            "expected": {
999                "href": "http://example.com/",
1000                "host": "example.com",
1001                "hostname": "example.com"
1002            }
1003        },
1004        {
1005            "comment": "Leading / is not stripped",
1006            "href": "sc://example.com/",
1007            "new_value": "///bad.com",
1008            "expected": {
1009                "href": "sc:///",
1010                "host": "",
1011                "hostname": ""
1012            }
1013        }
1014    ],
1015    "hostname": [
1016        {
1017            "comment": "Non-special scheme",
1018            "href": "sc://x/",
1019            "new_value": "\u0000",
1020            "expected": {
1021                "href": "sc://x/",
1022                "host": "x",
1023                "hostname": "x"
1024            }
1025        },
1026        {
1027            "href": "sc://x/",
1028            "new_value": "\u0009",
1029            "expected": {
1030                "href": "sc:///",
1031                "host": "",
1032                "hostname": ""
1033            }
1034        },
1035        {
1036            "href": "sc://x/",
1037            "new_value": "\u000A",
1038            "expected": {
1039                "href": "sc:///",
1040                "host": "",
1041                "hostname": ""
1042            }
1043        },
1044        {
1045            "href": "sc://x/",
1046            "new_value": "\u000D",
1047            "expected": {
1048                "href": "sc:///",
1049                "host": "",
1050                "hostname": ""
1051            }
1052        },
1053        {
1054            "href": "sc://x/",
1055            "new_value": " ",
1056            "expected": {
1057                "href": "sc://x/",
1058                "host": "x",
1059                "hostname": "x"
1060            }
1061        },
1062        {
1063            "href": "sc://x/",
1064            "new_value": "#",
1065            "expected": {
1066                "href": "sc:///",
1067                "host": "",
1068                "hostname": ""
1069            }
1070        },
1071        {
1072            "href": "sc://x/",
1073            "new_value": "/",
1074            "expected": {
1075                "href": "sc:///",
1076                "host": "",
1077                "hostname": ""
1078            }
1079        },
1080        {
1081            "href": "sc://x/",
1082            "new_value": "?",
1083            "expected": {
1084                "href": "sc:///",
1085                "host": "",
1086                "hostname": ""
1087            }
1088        },
1089        {
1090            "href": "sc://x/",
1091            "new_value": "@",
1092            "expected": {
1093                "href": "sc://x/",
1094                "host": "x",
1095                "hostname": "x"
1096            }
1097        },
1098        {
1099            "comment": "Cannot-be-a-base means no host",
1100            "href": "mailto:me@example.net",
1101            "new_value": "example.com",
1102            "expected": {
1103                "href": "mailto:me@example.net",
1104                "host": ""
1105            }
1106        },
1107        {
1108            "comment": "Cannot-be-a-base means no host",
1109            "href": "data:text/plain,Stuff",
1110            "new_value": "example.net",
1111            "expected": {
1112                "href": "data:text/plain,Stuff",
1113                "host": ""
1114            }
1115        },
1116        {
1117            "href": "http://example.net:8080",
1118            "new_value": "example.com",
1119            "expected": {
1120                "href": "http://example.com:8080/",
1121                "host": "example.com:8080",
1122                "hostname": "example.com",
1123                "port": "8080"
1124            }
1125        },
1126        {
1127            "comment": "The empty host is not valid for special schemes",
1128            "href": "http://example.net",
1129            "new_value": "",
1130            "expected": {
1131                "href": "http://example.net/",
1132                "host": "example.net"
1133            }
1134        },
1135        {
1136            "comment": "The empty host is OK for non-special schemes",
1137            "href": "view-source+http://example.net/foo",
1138            "new_value": "",
1139            "expected": {
1140                "href": "view-source+http:///foo",
1141                "host": ""
1142            }
1143        },
1144        {
1145            "comment": "Path-only URLs can gain a host",
1146            "href": "a:/foo",
1147            "new_value": "example.net",
1148            "expected": {
1149                "href": "a://example.net/foo",
1150                "host": "example.net"
1151            }
1152        },
1153        {
1154            "comment": "IPv4 address syntax is normalized",
1155            "href": "http://example.net:8080",
1156            "new_value": "0x7F000001",
1157            "expected": {
1158                "href": "http://127.0.0.1:8080/",
1159                "host": "127.0.0.1:8080",
1160                "hostname": "127.0.0.1",
1161                "port": "8080"
1162            }
1163        },
1164        {
1165            "comment": "IPv6 address syntax is normalized",
1166            "href": "http://example.net",
1167            "new_value": "[::0:01]",
1168            "expected": {
1169                "href": "http://[::1]/",
1170                "host": "[::1]",
1171                "hostname": "[::1]",
1172                "port": ""
1173            }
1174        },
1175        {
1176            "comment": ": delimiter invalidates entire value",
1177            "href": "http://example.net/path",
1178            "new_value": "example.com:8080",
1179            "expected": {
1180                "href": "http://example.net/path",
1181                "host": "example.net",
1182                "hostname": "example.net",
1183                "port": ""
1184            }
1185        },
1186        {
1187            "comment": ": delimiter invalidates entire value",
1188            "href": "http://example.net:8080/path",
1189            "new_value": "example.com:",
1190            "expected": {
1191                "href": "http://example.net:8080/path",
1192                "host": "example.net:8080",
1193                "hostname": "example.net",
1194                "port": "8080"
1195            }
1196        },
1197        {
1198            "comment": "Stuff after a / delimiter is ignored",
1199            "href": "http://example.net/path",
1200            "new_value": "example.com/stuff",
1201            "expected": {
1202                "href": "http://example.com/path",
1203                "host": "example.com",
1204                "hostname": "example.com",
1205                "port": ""
1206            }
1207        },
1208        {
1209            "comment": "Stuff after a ? delimiter is ignored",
1210            "href": "http://example.net/path",
1211            "new_value": "example.com?stuff",
1212            "expected": {
1213                "href": "http://example.com/path",
1214                "host": "example.com",
1215                "hostname": "example.com",
1216                "port": ""
1217            }
1218        },
1219        {
1220            "comment": "Stuff after a # delimiter is ignored",
1221            "href": "http://example.net/path",
1222            "new_value": "example.com#stuff",
1223            "expected": {
1224                "href": "http://example.com/path",
1225                "host": "example.com",
1226                "hostname": "example.com",
1227                "port": ""
1228            }
1229        },
1230        {
1231            "comment": "Stuff after a \\ delimiter is ignored for special schemes",
1232            "href": "http://example.net/path",
1233            "new_value": "example.com\\stuff",
1234            "expected": {
1235                "href": "http://example.com/path",
1236                "host": "example.com",
1237                "hostname": "example.com",
1238                "port": ""
1239            }
1240        },
1241        {
1242            "comment": "\\ is not a delimiter for non-special schemes, but still forbidden in hosts",
1243            "href": "view-source+http://example.net/path",
1244            "new_value": "example.com\\stuff",
1245            "expected": {
1246                "href": "view-source+http://example.net/path",
1247                "host": "example.net",
1248                "hostname": "example.net",
1249                "port": ""
1250            }
1251        },
1252        {
1253            "comment": "Broken IPv6",
1254            "href": "http://example.net/",
1255            "new_value": "[google.com]",
1256            "expected": {
1257                "href": "http://example.net/",
1258                "host": "example.net",
1259                "hostname": "example.net"
1260            }
1261        },
1262        {
1263            "href": "http://example.net/",
1264            "new_value": "[::1.2.3.4x]",
1265            "expected": {
1266                "href": "http://example.net/",
1267                "host": "example.net",
1268                "hostname": "example.net"
1269            }
1270        },
1271        {
1272            "href": "http://example.net/",
1273            "new_value": "[::1.2.3.]",
1274            "expected": {
1275                "href": "http://example.net/",
1276                "host": "example.net",
1277                "hostname": "example.net"
1278            }
1279        },
1280        {
1281            "href": "http://example.net/",
1282            "new_value": "[::1.2.]",
1283            "expected": {
1284                "href": "http://example.net/",
1285                "host": "example.net",
1286                "hostname": "example.net"
1287            }
1288        },
1289        {
1290            "href": "http://example.net/",
1291            "new_value": "[::1.]",
1292            "expected": {
1293                "href": "http://example.net/",
1294                "host": "example.net",
1295                "hostname": "example.net"
1296            }
1297        },
1298        {
1299            "href": "file://y/",
1300            "new_value": "x:123",
1301            "expected": {
1302                "href": "file://y/",
1303                "host": "y",
1304                "hostname": "y",
1305                "port": ""
1306            }
1307        },
1308        {
1309            "href": "file://y/",
1310            "new_value": "loc%41lhost",
1311            "expected": {
1312                "href": "file:///",
1313                "host": "",
1314                "hostname": "",
1315                "port": ""
1316            }
1317        },
1318        {
1319            "href": "file://hi/x",
1320            "new_value": "",
1321            "expected": {
1322                "href": "file:///x",
1323                "host": "",
1324                "hostname": "",
1325                "port": ""
1326            }
1327        },
1328        {
1329            "href": "sc://test@test/",
1330            "new_value": "",
1331            "expected": {
1332                "href": "sc://test@test/",
1333                "host": "test",
1334                "hostname": "test",
1335                "username": "test"
1336            }
1337        },
1338        {
1339            "href": "sc://test:12/",
1340            "new_value": "",
1341            "expected": {
1342                "href": "sc://test:12/",
1343                "host": "test:12",
1344                "hostname": "test",
1345                "port": "12"
1346            }
1347        },
1348        {
1349            "comment": "Drop /. from path",
1350            "href": "non-spec:/.//p",
1351            "new_value": "h",
1352            "expected": {
1353                "href": "non-spec://h//p",
1354                "host": "h",
1355                "hostname": "h",
1356                "pathname": "//p"
1357            }
1358        },
1359        {
1360            "href": "non-spec:/.//p",
1361            "new_value": "",
1362            "expected": {
1363                "href": "non-spec:////p",
1364                "host": "",
1365                "hostname": "",
1366                "pathname": "//p"
1367            }
1368        },
1369        {
1370            "comment": "Leading / is not stripped",
1371            "href": "http://example.com/",
1372            "new_value": "///bad.com",
1373            "expected": {
1374                "href": "http://example.com/",
1375                "host": "example.com",
1376                "hostname": "example.com"
1377            }
1378        },
1379        {
1380            "comment": "Leading / is not stripped",
1381            "href": "sc://example.com/",
1382            "new_value": "///bad.com",
1383            "expected": {
1384                "href": "sc:///",
1385                "host": "",
1386                "hostname": ""
1387            }
1388        }
1389    ],
1390    "port": [
1391        {
1392            "href": "http://example.net",
1393            "new_value": "8080",
1394            "expected": {
1395                "href": "http://example.net:8080/",
1396                "host": "example.net:8080",
1397                "hostname": "example.net",
1398                "port": "8080"
1399            }
1400        },
1401        {
1402            "comment": "Port number is removed if empty is the new value",
1403            "href": "http://example.net:8080",
1404            "new_value": "",
1405            "expected": {
1406                "href": "http://example.net/",
1407                "host": "example.net",
1408                "hostname": "example.net",
1409                "port": ""
1410            }
1411        },
1412        {
1413            "comment": "Default port number is removed",
1414            "href": "http://example.net:8080",
1415            "new_value": "80",
1416            "expected": {
1417                "href": "http://example.net/",
1418                "host": "example.net",
1419                "hostname": "example.net",
1420                "port": ""
1421            }
1422        },
1423        {
1424            "comment": "Default port number is removed",
1425            "href": "https://example.net:4433",
1426            "new_value": "443",
1427            "expected": {
1428                "href": "https://example.net/",
1429                "host": "example.net",
1430                "hostname": "example.net",
1431                "port": ""
1432            }
1433        },
1434        {
1435            "comment": "Default port number is only removed for the relevant scheme",
1436            "href": "https://example.net",
1437            "new_value": "80",
1438            "expected": {
1439                "href": "https://example.net:80/",
1440                "host": "example.net:80",
1441                "hostname": "example.net",
1442                "port": "80"
1443            }
1444        },
1445        {
1446            "comment": "Stuff after a / delimiter is ignored",
1447            "href": "http://example.net/path",
1448            "new_value": "8080/stuff",
1449            "expected": {
1450                "href": "http://example.net:8080/path",
1451                "host": "example.net:8080",
1452                "hostname": "example.net",
1453                "port": "8080"
1454            }
1455        },
1456        {
1457            "comment": "Stuff after a ? delimiter is ignored",
1458            "href": "http://example.net/path",
1459            "new_value": "8080?stuff",
1460            "expected": {
1461                "href": "http://example.net:8080/path",
1462                "host": "example.net:8080",
1463                "hostname": "example.net",
1464                "port": "8080"
1465            }
1466        },
1467        {
1468            "comment": "Stuff after a # delimiter is ignored",
1469            "href": "http://example.net/path",
1470            "new_value": "8080#stuff",
1471            "expected": {
1472                "href": "http://example.net:8080/path",
1473                "host": "example.net:8080",
1474                "hostname": "example.net",
1475                "port": "8080"
1476            }
1477        },
1478        {
1479            "comment": "Stuff after a \\ delimiter is ignored for special schemes",
1480            "href": "http://example.net/path",
1481            "new_value": "8080\\stuff",
1482            "expected": {
1483                "href": "http://example.net:8080/path",
1484                "host": "example.net:8080",
1485                "hostname": "example.net",
1486                "port": "8080"
1487            }
1488        },
1489        {
1490            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
1491            "href": "view-source+http://example.net/path",
1492            "new_value": "8080stuff2",
1493            "expected": {
1494                "href": "view-source+http://example.net:8080/path",
1495                "host": "example.net:8080",
1496                "hostname": "example.net",
1497                "port": "8080"
1498            }
1499        },
1500        {
1501            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
1502            "href": "http://example.net/path",
1503            "new_value": "8080stuff2",
1504            "expected": {
1505                "href": "http://example.net:8080/path",
1506                "host": "example.net:8080",
1507                "hostname": "example.net",
1508                "port": "8080"
1509            }
1510        },
1511        {
1512            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
1513            "href": "http://example.net/path",
1514            "new_value": "8080+2",
1515            "expected": {
1516                "href": "http://example.net:8080/path",
1517                "host": "example.net:8080",
1518                "hostname": "example.net",
1519                "port": "8080"
1520            }
1521        },
1522        {
1523            "comment": "Port numbers are 16 bit integers",
1524            "href": "http://example.net/path",
1525            "new_value": "65535",
1526            "expected": {
1527                "href": "http://example.net:65535/path",
1528                "host": "example.net:65535",
1529                "hostname": "example.net",
1530                "port": "65535"
1531            }
1532        },
1533        {
1534            "comment": "Port numbers are 16 bit integers, overflowing is an error",
1535            "href": "http://example.net:8080/path",
1536            "new_value": "65536",
1537            "expected": {
1538                "href": "http://example.net:8080/path",
1539                "host": "example.net:8080",
1540                "hostname": "example.net",
1541                "port": "8080"
1542            }
1543        },
1544        {
1545            "comment": "Port numbers are 16 bit integers, overflowing is an error",
1546            "href": "non-special://example.net:8080/path",
1547            "new_value": "65536",
1548            "expected": {
1549                "href": "non-special://example.net:8080/path",
1550                "host": "example.net:8080",
1551                "hostname": "example.net",
1552                "port": "8080"
1553            }
1554        },
1555        {
1556            "href": "file://test/",
1557            "new_value": "12",
1558            "expected": {
1559                "href": "file://test/",
1560                "port": ""
1561            }
1562        },
1563        {
1564            "href": "file://localhost/",
1565            "new_value": "12",
1566            "expected": {
1567                "href": "file:///",
1568                "port": ""
1569            }
1570        },
1571        {
1572            "href": "non-base:value",
1573            "new_value": "12",
1574            "expected": {
1575                "href": "non-base:value",
1576                "port": ""
1577            }
1578        },
1579        {
1580            "href": "sc:///",
1581            "new_value": "12",
1582            "expected": {
1583                "href": "sc:///",
1584                "port": ""
1585            }
1586        },
1587        {
1588            "href": "sc://x/",
1589            "new_value": "12",
1590            "expected": {
1591                "href": "sc://x:12/",
1592                "port": "12"
1593            }
1594        },
1595        {
1596            "href": "javascript://x/",
1597            "new_value": "12",
1598            "expected": {
1599                "href": "javascript://x:12/",
1600                "port": "12"
1601            }
1602        }
1603    ],
1604    "pathname": [
1605        {
1606            "comment": "Cannot-be-a-base don’t have a path",
1607            "href": "mailto:me@example.net",
1608            "new_value": "/foo",
1609            "expected": {
1610                "href": "mailto:me@example.net",
1611                "pathname": "me@example.net"
1612            }
1613        },
1614        {
1615            "comment": "Special URLs cannot have their paths erased",
1616            "href": "file:///some/path",
1617            "new_value": "",
1618            "expected": {
1619                "href": "file:///",
1620                "pathname": "/"
1621            }
1622        },
1623        {
1624            "comment": "Non-special URLs can have their paths erased",
1625            "href": "foo://somehost/some/path",
1626            "new_value": "",
1627            "expected": {
1628                "href": "foo://somehost",
1629                "pathname": ""
1630            }
1631        },
1632        {
1633            "comment": "Non-special URLs with an empty host can have their paths erased",
1634            "href": "foo:///some/path",
1635            "new_value": "",
1636            "expected": {
1637                "href": "foo://",
1638                "pathname": ""
1639            }
1640        },
1641        {
1642            "comment": "Path-only URLs cannot have their paths erased",
1643            "href": "foo:/some/path",
1644            "new_value": "",
1645            "expected": {
1646                "href": "foo:/",
1647                "pathname": "/"
1648            }
1649        },
1650        {
1651            "comment": "Path-only URLs always have an initial slash",
1652            "href": "foo:/some/path",
1653            "new_value": "test",
1654            "expected": {
1655                "href": "foo:/test",
1656                "pathname": "/test"
1657            }
1658        },
1659        {
1660            "href": "unix:/run/foo.socket?timeout=10",
1661            "new_value": "/var/log/../run/bar.socket",
1662            "expected": {
1663                "href": "unix:/var/run/bar.socket?timeout=10",
1664                "pathname": "/var/run/bar.socket"
1665            }
1666        },
1667        {
1668            "href": "https://example.net#nav",
1669            "new_value": "home",
1670            "expected": {
1671                "href": "https://example.net/home#nav",
1672                "pathname": "/home"
1673            }
1674        },
1675        {
1676            "href": "https://example.net#nav",
1677            "new_value": "../home",
1678            "expected": {
1679                "href": "https://example.net/home#nav",
1680                "pathname": "/home"
1681            }
1682        },
1683        {
1684            "comment": "\\ is a segment delimiter for 'special' URLs",
1685            "href": "http://example.net/home?lang=fr#nav",
1686            "new_value": "\\a\\%2E\\b\\%2e.\\c",
1687            "expected": {
1688                "href": "http://example.net/a/c?lang=fr#nav",
1689                "pathname": "/a/c"
1690            }
1691        },
1692        {
1693            "comment": "\\ is *not* a segment delimiter for non-'special' URLs",
1694            "href": "view-source+http://example.net/home?lang=fr#nav",
1695            "new_value": "\\a\\%2E\\b\\%2e.\\c",
1696            "expected": {
1697                "href": "view-source+http://example.net/\\a\\%2E\\b\\%2e.\\c?lang=fr#nav",
1698                "pathname": "/\\a\\%2E\\b\\%2e.\\c"
1699            }
1700        },
1701        {
1702            "comment": "UTF-8 percent encoding with the default encode set. Tabs and newlines are removed.",
1703            "href": "a:/",
1704            "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
1705            "expected": {
1706                "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",
1707                "pathname": "/%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E%3F@AZ[\\]^_%60az%7B|%7D~%7F%C2%80%C2%81%C3%89%C3%A9"
1708            }
1709        },
1710        {
1711            "comment": "Bytes already percent-encoded are left as-is, including %2E outside dotted segments.",
1712            "href": "http://example.net",
1713            "new_value": "%2e%2E%c3%89té",
1714            "expected": {
1715                "href": "http://example.net/%2e%2E%c3%89t%C3%A9",
1716                "pathname": "/%2e%2E%c3%89t%C3%A9"
1717            }
1718        },
1719        {
1720            "comment": "? needs to be encoded",
1721            "href": "http://example.net",
1722            "new_value": "?",
1723            "expected": {
1724                "href": "http://example.net/%3F",
1725                "pathname": "/%3F"
1726            }
1727        },
1728        {
1729            "comment": "# needs to be encoded",
1730            "href": "http://example.net",
1731            "new_value": "#",
1732            "expected": {
1733                "href": "http://example.net/%23",
1734                "pathname": "/%23"
1735            }
1736        },
1737        {
1738            "comment": "? needs to be encoded, non-special scheme",
1739            "href": "sc://example.net",
1740            "new_value": "?",
1741            "expected": {
1742                "href": "sc://example.net/%3F",
1743                "pathname": "/%3F"
1744            }
1745        },
1746        {
1747            "comment": "# needs to be encoded, non-special scheme",
1748            "href": "sc://example.net",
1749            "new_value": "#",
1750            "expected": {
1751                "href": "sc://example.net/%23",
1752                "pathname": "/%23"
1753            }
1754        },
1755        {
1756            "comment": "? doesn't mess up encoding",
1757            "href": "http://example.net",
1758            "new_value": "/?é",
1759            "expected": {
1760                "href": "http://example.net/%3F%C3%A9",
1761                "pathname": "/%3F%C3%A9"
1762            }
1763        },
1764        {
1765            "comment": "# doesn't mess up encoding",
1766            "href": "http://example.net",
1767            "new_value": "/#é",
1768            "expected": {
1769                "href": "http://example.net/%23%C3%A9",
1770                "pathname": "/%23%C3%A9"
1771            }
1772        },
1773        {
1774            "comment": "File URLs and (back)slashes",
1775            "href": "file://monkey/",
1776            "new_value": "\\\\",
1777            "expected": {
1778                "href": "file://monkey//",
1779                "pathname": "//"
1780            }
1781        },
1782        {
1783            "comment": "File URLs and (back)slashes",
1784            "href": "file:///unicorn",
1785            "new_value": "//\\/",
1786            "expected": {
1787                "href": "file://////",
1788                "pathname": "////"
1789            }
1790        },
1791        {
1792            "comment": "File URLs and (back)slashes",
1793            "href": "file:///unicorn",
1794            "new_value": "//monkey/..//",
1795            "expected": {
1796                "href": "file://///",
1797                "pathname": "///"
1798            }
1799        },
1800        {
1801            "comment": "Serialize /. in path",
1802            "href": "non-spec:/",
1803            "new_value": "/.//p",
1804            "expected": {
1805                "href": "non-spec:/.//p",
1806                "pathname": "//p"
1807            }
1808        },
1809        {
1810            "href": "non-spec:/",
1811            "new_value": "/..//p",
1812            "expected": {
1813                "href": "non-spec:/.//p",
1814                "pathname": "//p"
1815            }
1816        },
1817        {
1818            "href": "non-spec:/",
1819            "new_value": "//p",
1820            "expected": {
1821                "href": "non-spec:/.//p",
1822                "pathname": "//p"
1823            }
1824        },
1825        {
1826            "comment": "Drop /. from path",
1827            "href": "non-spec:/.//",
1828            "new_value": "p",
1829            "expected": {
1830                "href": "non-spec:/p",
1831                "pathname": "/p"
1832            }
1833        }
1834    ],
1835    "search": [
1836        {
1837            "href": "https://example.net#nav",
1838            "new_value": "lang=fr",
1839            "expected": {
1840                "href": "https://example.net/?lang=fr#nav",
1841                "search": "?lang=fr"
1842            }
1843        },
1844        {
1845            "href": "https://example.net?lang=en-US#nav",
1846            "new_value": "lang=fr",
1847            "expected": {
1848                "href": "https://example.net/?lang=fr#nav",
1849                "search": "?lang=fr"
1850            }
1851        },
1852        {
1853            "href": "https://example.net?lang=en-US#nav",
1854            "new_value": "?lang=fr",
1855            "expected": {
1856                "href": "https://example.net/?lang=fr#nav",
1857                "search": "?lang=fr"
1858            }
1859        },
1860        {
1861            "href": "https://example.net?lang=en-US#nav",
1862            "new_value": "??lang=fr",
1863            "expected": {
1864                "href": "https://example.net/??lang=fr#nav",
1865                "search": "??lang=fr"
1866            }
1867        },
1868        {
1869            "href": "https://example.net?lang=en-US#nav",
1870            "new_value": "?",
1871            "expected": {
1872                "href": "https://example.net/?#nav",
1873                "search": ""
1874            }
1875        },
1876        {
1877            "href": "https://example.net?lang=en-US#nav",
1878            "new_value": "",
1879            "expected": {
1880                "href": "https://example.net/#nav",
1881                "search": ""
1882            }
1883        },
1884        {
1885            "href": "https://example.net?lang=en-US",
1886            "new_value": "",
1887            "expected": {
1888                "href": "https://example.net/",
1889                "search": ""
1890            }
1891        },
1892        {
1893            "href": "https://example.net",
1894            "new_value": "",
1895            "expected": {
1896                "href": "https://example.net/",
1897                "search": ""
1898            }
1899        },
1900        {
1901            "comment": "UTF-8 percent encoding with the query encode set. Tabs and newlines are removed.",
1902            "href": "a:/",
1903            "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
1904            "expected": {
1905                "href": "a:/?%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9",
1906                "search": "?%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9"
1907            }
1908        },
1909        {
1910            "comment": "Bytes already percent-encoded are left as-is",
1911            "href": "http://example.net",
1912            "new_value": "%c3%89té",
1913            "expected": {
1914                "href": "http://example.net/?%c3%89t%C3%A9",
1915                "search": "?%c3%89t%C3%A9"
1916            }
1917        }
1918    ],
1919    "hash": [
1920        {
1921            "href": "https://example.net",
1922            "new_value": "main",
1923            "expected": {
1924                "href": "https://example.net/#main",
1925                "hash": "#main"
1926            }
1927        },
1928        {
1929            "href": "https://example.net#nav",
1930            "new_value": "main",
1931            "expected": {
1932                "href": "https://example.net/#main",
1933                "hash": "#main"
1934            }
1935        },
1936        {
1937            "href": "https://example.net?lang=en-US",
1938            "new_value": "##nav",
1939            "expected": {
1940                "href": "https://example.net/?lang=en-US##nav",
1941                "hash": "##nav"
1942            }
1943        },
1944        {
1945            "href": "https://example.net?lang=en-US#nav",
1946            "new_value": "#main",
1947            "expected": {
1948                "href": "https://example.net/?lang=en-US#main",
1949                "hash": "#main"
1950            }
1951        },
1952        {
1953            "href": "https://example.net?lang=en-US#nav",
1954            "new_value": "#",
1955            "expected": {
1956                "href": "https://example.net/?lang=en-US#",
1957                "hash": ""
1958            }
1959        },
1960        {
1961            "href": "https://example.net?lang=en-US#nav",
1962            "new_value": "",
1963            "expected": {
1964                "href": "https://example.net/?lang=en-US",
1965                "hash": ""
1966            }
1967        },
1968        {
1969            "href": "http://example.net",
1970            "new_value": "#foo bar",
1971            "expected": {
1972                "href": "http://example.net/#foo%20bar",
1973                "hash": "#foo%20bar"
1974            }
1975        },
1976        {
1977            "href": "http://example.net",
1978            "new_value": "#foo\"bar",
1979            "expected": {
1980                "href": "http://example.net/#foo%22bar",
1981                "hash": "#foo%22bar"
1982            }
1983        },
1984        {
1985            "href": "http://example.net",
1986            "new_value": "#foo<bar",
1987            "expected": {
1988                "href": "http://example.net/#foo%3Cbar",
1989                "hash": "#foo%3Cbar"
1990            }
1991        },
1992        {
1993            "href": "http://example.net",
1994            "new_value": "#foo>bar",
1995            "expected": {
1996                "href": "http://example.net/#foo%3Ebar",
1997                "hash": "#foo%3Ebar"
1998            }
1999        },
2000        {
2001            "href": "http://example.net",
2002            "new_value": "#foo`bar",
2003            "expected": {
2004                "href": "http://example.net/#foo%60bar",
2005                "hash": "#foo%60bar"
2006            }
2007        },
2008        {
2009            "comment": "Simple percent-encoding; tabs and newlines are removed",
2010            "href": "a:/",
2011            "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
2012            "expected": {
2013                "href": "a:/#%00%01%1F%20!%22#$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_%60az{|}~%7F%C2%80%C2%81%C3%89%C3%A9",
2014                "hash": "#%00%01%1F%20!%22#$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_%60az{|}~%7F%C2%80%C2%81%C3%89%C3%A9"
2015            }
2016        },
2017        {
2018            "comment": "Percent-encode NULLs in fragment",
2019            "href": "http://example.net",
2020            "new_value": "a\u0000b",
2021            "expected": {
2022                "href": "http://example.net/#a%00b",
2023                "hash": "#a%00b"
2024            }
2025        },
2026        {
2027            "comment": "Percent-encode NULLs in fragment",
2028            "href": "non-spec:/",
2029            "new_value": "a\u0000b",
2030            "expected": {
2031                "href": "non-spec:/#a%00b",
2032                "hash": "#a%00b"
2033            }
2034        },
2035        {
2036            "comment": "Bytes already percent-encoded are left as-is",
2037            "href": "http://example.net",
2038            "new_value": "%c3%89té",
2039            "expected": {
2040                "href": "http://example.net/#%c3%89t%C3%A9",
2041                "hash": "#%c3%89t%C3%A9"
2042            }
2043        },
2044        {
2045            "href": "javascript:alert(1)",
2046            "new_value": "castle",
2047            "expected": {
2048                "href": "javascript:alert(1)#castle",
2049                "hash": "#castle"
2050            }
2051        }
2052    ]
2053}
2054