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": "gopher://example.net:1234",
123            "new_value": "file",
124            "expected": {
125                "href": "gopher://example.net:1234",
126                "protocol": "gopher:"
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": "gopher",
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    "hostname": [
996        {
997            "comment": "Non-special scheme",
998            "href": "sc://x/",
999            "new_value": "\u0000",
1000            "expected": {
1001                "href": "sc://x/",
1002                "host": "x",
1003                "hostname": "x"
1004            }
1005        },
1006        {
1007            "href": "sc://x/",
1008            "new_value": "\u0009",
1009            "expected": {
1010                "href": "sc:///",
1011                "host": "",
1012                "hostname": ""
1013            }
1014        },
1015        {
1016            "href": "sc://x/",
1017            "new_value": "\u000A",
1018            "expected": {
1019                "href": "sc:///",
1020                "host": "",
1021                "hostname": ""
1022            }
1023        },
1024        {
1025            "href": "sc://x/",
1026            "new_value": "\u000D",
1027            "expected": {
1028                "href": "sc:///",
1029                "host": "",
1030                "hostname": ""
1031            }
1032        },
1033        {
1034            "href": "sc://x/",
1035            "new_value": " ",
1036            "expected": {
1037                "href": "sc://x/",
1038                "host": "x",
1039                "hostname": "x"
1040            }
1041        },
1042        {
1043            "href": "sc://x/",
1044            "new_value": "#",
1045            "expected": {
1046                "href": "sc:///",
1047                "host": "",
1048                "hostname": ""
1049            }
1050        },
1051        {
1052            "href": "sc://x/",
1053            "new_value": "/",
1054            "expected": {
1055                "href": "sc:///",
1056                "host": "",
1057                "hostname": ""
1058            }
1059        },
1060        {
1061            "href": "sc://x/",
1062            "new_value": "?",
1063            "expected": {
1064                "href": "sc:///",
1065                "host": "",
1066                "hostname": ""
1067            }
1068        },
1069        {
1070            "href": "sc://x/",
1071            "new_value": "@",
1072            "expected": {
1073                "href": "sc://x/",
1074                "host": "x",
1075                "hostname": "x"
1076            }
1077        },
1078        {
1079            "comment": "Cannot-be-a-base means no host",
1080            "href": "mailto:me@example.net",
1081            "new_value": "example.com",
1082            "expected": {
1083                "href": "mailto:me@example.net",
1084                "host": ""
1085            }
1086        },
1087        {
1088            "comment": "Cannot-be-a-base means no host",
1089            "href": "data:text/plain,Stuff",
1090            "new_value": "example.net",
1091            "expected": {
1092                "href": "data:text/plain,Stuff",
1093                "host": ""
1094            }
1095        },
1096        {
1097            "href": "http://example.net:8080",
1098            "new_value": "example.com",
1099            "expected": {
1100                "href": "http://example.com:8080/",
1101                "host": "example.com:8080",
1102                "hostname": "example.com",
1103                "port": "8080"
1104            }
1105        },
1106        {
1107            "comment": "The empty host is not valid for special schemes",
1108            "href": "http://example.net",
1109            "new_value": "",
1110            "expected": {
1111                "href": "http://example.net/",
1112                "host": "example.net"
1113            }
1114        },
1115        {
1116            "comment": "The empty host is OK for non-special schemes",
1117            "href": "view-source+http://example.net/foo",
1118            "new_value": "",
1119            "expected": {
1120                "href": "view-source+http:///foo",
1121                "host": ""
1122            }
1123        },
1124        {
1125            "comment": "Path-only URLs can gain a host",
1126            "href": "a:/foo",
1127            "new_value": "example.net",
1128            "expected": {
1129                "href": "a://example.net/foo",
1130                "host": "example.net"
1131            }
1132        },
1133        {
1134            "comment": "IPv4 address syntax is normalized",
1135            "href": "http://example.net:8080",
1136            "new_value": "0x7F000001",
1137            "expected": {
1138                "href": "http://127.0.0.1:8080/",
1139                "host": "127.0.0.1:8080",
1140                "hostname": "127.0.0.1",
1141                "port": "8080"
1142            }
1143        },
1144        {
1145            "comment": "IPv6 address syntax is normalized",
1146            "href": "http://example.net",
1147            "new_value": "[::0:01]",
1148            "expected": {
1149                "href": "http://[::1]/",
1150                "host": "[::1]",
1151                "hostname": "[::1]",
1152                "port": ""
1153            }
1154        },
1155        {
1156            "comment": "Stuff after a : delimiter is ignored",
1157            "href": "http://example.net/path",
1158            "new_value": "example.com:8080",
1159            "expected": {
1160                "href": "http://example.com/path",
1161                "host": "example.com",
1162                "hostname": "example.com",
1163                "port": ""
1164            }
1165        },
1166        {
1167            "comment": "Stuff after a : delimiter is ignored",
1168            "href": "http://example.net:8080/path",
1169            "new_value": "example.com:",
1170            "expected": {
1171                "href": "http://example.com:8080/path",
1172                "host": "example.com:8080",
1173                "hostname": "example.com",
1174                "port": "8080"
1175            }
1176        },
1177        {
1178            "comment": "Stuff after a / delimiter is ignored",
1179            "href": "http://example.net/path",
1180            "new_value": "example.com/stuff",
1181            "expected": {
1182                "href": "http://example.com/path",
1183                "host": "example.com",
1184                "hostname": "example.com",
1185                "port": ""
1186            }
1187        },
1188        {
1189            "comment": "Stuff after a ? delimiter is ignored",
1190            "href": "http://example.net/path",
1191            "new_value": "example.com?stuff",
1192            "expected": {
1193                "href": "http://example.com/path",
1194                "host": "example.com",
1195                "hostname": "example.com",
1196                "port": ""
1197            }
1198        },
1199        {
1200            "comment": "Stuff after a # delimiter is ignored",
1201            "href": "http://example.net/path",
1202            "new_value": "example.com#stuff",
1203            "expected": {
1204                "href": "http://example.com/path",
1205                "host": "example.com",
1206                "hostname": "example.com",
1207                "port": ""
1208            }
1209        },
1210        {
1211            "comment": "Stuff after a \\ delimiter is ignored for special schemes",
1212            "href": "http://example.net/path",
1213            "new_value": "example.com\\stuff",
1214            "expected": {
1215                "href": "http://example.com/path",
1216                "host": "example.com",
1217                "hostname": "example.com",
1218                "port": ""
1219            }
1220        },
1221        {
1222            "comment": "\\ is not a delimiter for non-special schemes, but still forbidden in hosts",
1223            "href": "view-source+http://example.net/path",
1224            "new_value": "example.com\\stuff",
1225            "expected": {
1226                "href": "view-source+http://example.net/path",
1227                "host": "example.net",
1228                "hostname": "example.net",
1229                "port": ""
1230            }
1231        },
1232        {
1233            "comment": "Broken IPv6",
1234            "href": "http://example.net/",
1235            "new_value": "[google.com]",
1236            "expected": {
1237                "href": "http://example.net/",
1238                "host": "example.net",
1239                "hostname": "example.net"
1240            }
1241        },
1242        {
1243            "href": "http://example.net/",
1244            "new_value": "[::1.2.3.4x]",
1245            "expected": {
1246                "href": "http://example.net/",
1247                "host": "example.net",
1248                "hostname": "example.net"
1249            }
1250        },
1251        {
1252            "href": "http://example.net/",
1253            "new_value": "[::1.2.3.]",
1254            "expected": {
1255                "href": "http://example.net/",
1256                "host": "example.net",
1257                "hostname": "example.net"
1258            }
1259        },
1260        {
1261            "href": "http://example.net/",
1262            "new_value": "[::1.2.]",
1263            "expected": {
1264                "href": "http://example.net/",
1265                "host": "example.net",
1266                "hostname": "example.net"
1267            }
1268        },
1269        {
1270            "href": "http://example.net/",
1271            "new_value": "[::1.]",
1272            "expected": {
1273                "href": "http://example.net/",
1274                "host": "example.net",
1275                "hostname": "example.net"
1276            }
1277        },
1278        {
1279            "href": "file://y/",
1280            "new_value": "x:123",
1281            "expected": {
1282                "href": "file://y/",
1283                "host": "y",
1284                "hostname": "y",
1285                "port": ""
1286            }
1287        },
1288        {
1289            "href": "file://y/",
1290            "new_value": "loc%41lhost",
1291            "expected": {
1292                "href": "file:///",
1293                "host": "",
1294                "hostname": "",
1295                "port": ""
1296            }
1297        },
1298        {
1299            "href": "file://hi/x",
1300            "new_value": "",
1301            "expected": {
1302                "href": "file:///x",
1303                "host": "",
1304                "hostname": "",
1305                "port": ""
1306            }
1307        },
1308        {
1309            "href": "sc://test@test/",
1310            "new_value": "",
1311            "expected": {
1312                "href": "sc://test@test/",
1313                "host": "test",
1314                "hostname": "test",
1315                "username": "test"
1316            }
1317        },
1318        {
1319            "href": "sc://test:12/",
1320            "new_value": "",
1321            "expected": {
1322                "href": "sc://test:12/",
1323                "host": "test:12",
1324                "hostname": "test",
1325                "port": "12"
1326            }
1327        }
1328    ],
1329    "port": [
1330        {
1331            "href": "http://example.net",
1332            "new_value": "8080",
1333            "expected": {
1334                "href": "http://example.net:8080/",
1335                "host": "example.net:8080",
1336                "hostname": "example.net",
1337                "port": "8080"
1338            }
1339        },
1340        {
1341            "comment": "Port number is removed if empty is the new value",
1342            "href": "http://example.net:8080",
1343            "new_value": "",
1344            "expected": {
1345                "href": "http://example.net/",
1346                "host": "example.net",
1347                "hostname": "example.net",
1348                "port": ""
1349            }
1350        },
1351        {
1352            "comment": "Default port number is removed",
1353            "href": "http://example.net:8080",
1354            "new_value": "80",
1355            "expected": {
1356                "href": "http://example.net/",
1357                "host": "example.net",
1358                "hostname": "example.net",
1359                "port": ""
1360            }
1361        },
1362        {
1363            "comment": "Default port number is removed",
1364            "href": "https://example.net:4433",
1365            "new_value": "443",
1366            "expected": {
1367                "href": "https://example.net/",
1368                "host": "example.net",
1369                "hostname": "example.net",
1370                "port": ""
1371            }
1372        },
1373        {
1374            "comment": "Default port number is only removed for the relevant scheme",
1375            "href": "https://example.net",
1376            "new_value": "80",
1377            "expected": {
1378                "href": "https://example.net:80/",
1379                "host": "example.net:80",
1380                "hostname": "example.net",
1381                "port": "80"
1382            }
1383        },
1384        {
1385            "comment": "Stuff after a / delimiter is ignored",
1386            "href": "http://example.net/path",
1387            "new_value": "8080/stuff",
1388            "expected": {
1389                "href": "http://example.net:8080/path",
1390                "host": "example.net:8080",
1391                "hostname": "example.net",
1392                "port": "8080"
1393            }
1394        },
1395        {
1396            "comment": "Stuff after a ? delimiter is ignored",
1397            "href": "http://example.net/path",
1398            "new_value": "8080?stuff",
1399            "expected": {
1400                "href": "http://example.net:8080/path",
1401                "host": "example.net:8080",
1402                "hostname": "example.net",
1403                "port": "8080"
1404            }
1405        },
1406        {
1407            "comment": "Stuff after a # delimiter is ignored",
1408            "href": "http://example.net/path",
1409            "new_value": "8080#stuff",
1410            "expected": {
1411                "href": "http://example.net:8080/path",
1412                "host": "example.net:8080",
1413                "hostname": "example.net",
1414                "port": "8080"
1415            }
1416        },
1417        {
1418            "comment": "Stuff after a \\ delimiter is ignored for special schemes",
1419            "href": "http://example.net/path",
1420            "new_value": "8080\\stuff",
1421            "expected": {
1422                "href": "http://example.net:8080/path",
1423                "host": "example.net:8080",
1424                "hostname": "example.net",
1425                "port": "8080"
1426            }
1427        },
1428        {
1429            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
1430            "href": "view-source+http://example.net/path",
1431            "new_value": "8080stuff2",
1432            "expected": {
1433                "href": "view-source+http://example.net:8080/path",
1434                "host": "example.net:8080",
1435                "hostname": "example.net",
1436                "port": "8080"
1437            }
1438        },
1439        {
1440            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
1441            "href": "http://example.net/path",
1442            "new_value": "8080stuff2",
1443            "expected": {
1444                "href": "http://example.net:8080/path",
1445                "host": "example.net:8080",
1446                "hostname": "example.net",
1447                "port": "8080"
1448            }
1449        },
1450        {
1451            "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error",
1452            "href": "http://example.net/path",
1453            "new_value": "8080+2",
1454            "expected": {
1455                "href": "http://example.net:8080/path",
1456                "host": "example.net:8080",
1457                "hostname": "example.net",
1458                "port": "8080"
1459            }
1460        },
1461        {
1462            "comment": "Port numbers are 16 bit integers",
1463            "href": "http://example.net/path",
1464            "new_value": "65535",
1465            "expected": {
1466                "href": "http://example.net:65535/path",
1467                "host": "example.net:65535",
1468                "hostname": "example.net",
1469                "port": "65535"
1470            }
1471        },
1472        {
1473            "comment": "Port numbers are 16 bit integers, overflowing is an error",
1474            "href": "http://example.net:8080/path",
1475            "new_value": "65536",
1476            "expected": {
1477                "href": "http://example.net:8080/path",
1478                "host": "example.net:8080",
1479                "hostname": "example.net",
1480                "port": "8080"
1481            }
1482        },
1483        {
1484            "comment": "Port numbers are 16 bit integers, overflowing is an error",
1485            "href": "non-special://example.net:8080/path",
1486            "new_value": "65536",
1487            "expected": {
1488                "href": "non-special://example.net:8080/path",
1489                "host": "example.net:8080",
1490                "hostname": "example.net",
1491                "port": "8080"
1492            }
1493        },
1494        {
1495            "href": "file://test/",
1496            "new_value": "12",
1497            "expected": {
1498                "href": "file://test/",
1499                "port": ""
1500            }
1501        },
1502        {
1503            "href": "file://localhost/",
1504            "new_value": "12",
1505            "expected": {
1506                "href": "file:///",
1507                "port": ""
1508            }
1509        },
1510        {
1511            "href": "non-base:value",
1512            "new_value": "12",
1513            "expected": {
1514                "href": "non-base:value",
1515                "port": ""
1516            }
1517        },
1518        {
1519            "href": "sc:///",
1520            "new_value": "12",
1521            "expected": {
1522                "href": "sc:///",
1523                "port": ""
1524            }
1525        },
1526        {
1527            "href": "sc://x/",
1528            "new_value": "12",
1529            "expected": {
1530                "href": "sc://x:12/",
1531                "port": "12"
1532            }
1533        },
1534        {
1535            "href": "javascript://x/",
1536            "new_value": "12",
1537            "expected": {
1538                "href": "javascript://x:12/",
1539                "port": "12"
1540            }
1541        }
1542    ],
1543    "pathname": [
1544        {
1545            "comment": "Cannot-be-a-base don’t have a path",
1546            "href": "mailto:me@example.net",
1547            "new_value": "/foo",
1548            "expected": {
1549                "href": "mailto:me@example.net",
1550                "pathname": "me@example.net"
1551            }
1552        },
1553        {
1554            "href": "unix:/run/foo.socket?timeout=10",
1555            "new_value": "/var/log/../run/bar.socket",
1556            "expected": {
1557                "href": "unix:/var/run/bar.socket?timeout=10",
1558                "pathname": "/var/run/bar.socket"
1559            }
1560        },
1561        {
1562            "href": "https://example.net#nav",
1563            "new_value": "home",
1564            "expected": {
1565                "href": "https://example.net/home#nav",
1566                "pathname": "/home"
1567            }
1568        },
1569        {
1570            "href": "https://example.net#nav",
1571            "new_value": "../home",
1572            "expected": {
1573                "href": "https://example.net/home#nav",
1574                "pathname": "/home"
1575            }
1576        },
1577        {
1578            "comment": "\\ is a segment delimiter for 'special' URLs",
1579            "href": "http://example.net/home?lang=fr#nav",
1580            "new_value": "\\a\\%2E\\b\\%2e.\\c",
1581            "expected": {
1582                "href": "http://example.net/a/c?lang=fr#nav",
1583                "pathname": "/a/c"
1584            }
1585        },
1586        {
1587            "comment": "\\ is *not* a segment delimiter for non-'special' URLs",
1588            "href": "view-source+http://example.net/home?lang=fr#nav",
1589            "new_value": "\\a\\%2E\\b\\%2e.\\c",
1590            "expected": {
1591                "href": "view-source+http://example.net/\\a\\%2E\\b\\%2e.\\c?lang=fr#nav",
1592                "pathname": "/\\a\\%2E\\b\\%2e.\\c"
1593            }
1594        },
1595        {
1596            "comment": "UTF-8 percent encoding with the default encode set. Tabs and newlines are removed.",
1597            "href": "a:/",
1598            "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
1599            "expected": {
1600                "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",
1601                "pathname": "/%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E%3F@AZ[\\]^_%60az%7B|%7D~%7F%C2%80%C2%81%C3%89%C3%A9"
1602            }
1603        },
1604        {
1605            "comment": "Bytes already percent-encoded are left as-is, including %2E outside dotted segments.",
1606            "href": "http://example.net",
1607            "new_value": "%2e%2E%c3%89té",
1608            "expected": {
1609                "href": "http://example.net/%2e%2E%c3%89t%C3%A9",
1610                "pathname": "/%2e%2E%c3%89t%C3%A9"
1611            }
1612        },
1613        {
1614            "comment": "? needs to be encoded",
1615            "href": "http://example.net",
1616            "new_value": "?",
1617            "expected": {
1618                "href": "http://example.net/%3F",
1619                "pathname": "/%3F"
1620            }
1621        },
1622        {
1623            "comment": "# needs to be encoded",
1624            "href": "http://example.net",
1625            "new_value": "#",
1626            "expected": {
1627                "href": "http://example.net/%23",
1628                "pathname": "/%23"
1629            }
1630        },
1631        {
1632            "comment": "? needs to be encoded, non-special scheme",
1633            "href": "sc://example.net",
1634            "new_value": "?",
1635            "expected": {
1636                "href": "sc://example.net/%3F",
1637                "pathname": "/%3F"
1638            }
1639        },
1640        {
1641            "comment": "# needs to be encoded, non-special scheme",
1642            "href": "sc://example.net",
1643            "new_value": "#",
1644            "expected": {
1645                "href": "sc://example.net/%23",
1646                "pathname": "/%23"
1647            }
1648        },
1649        {
1650            "comment": "File URLs and (back)slashes",
1651            "href": "file://monkey/",
1652            "new_value": "\\\\",
1653            "expected": {
1654                "href": "file://monkey/",
1655                "pathname": "/"
1656            }
1657        },
1658        {
1659            "comment": "File URLs and (back)slashes",
1660            "href": "file:///unicorn",
1661            "new_value": "//\\/",
1662            "expected": {
1663                "href": "file:///",
1664                "pathname": "/"
1665            }
1666        },
1667        {
1668            "comment": "File URLs and (back)slashes",
1669            "href": "file:///unicorn",
1670            "new_value": "//monkey/..//",
1671            "expected": {
1672                "href": "file:///",
1673                "pathname": "/"
1674            }
1675        }
1676    ],
1677    "search": [
1678        {
1679            "href": "https://example.net#nav",
1680            "new_value": "lang=fr",
1681            "expected": {
1682                "href": "https://example.net/?lang=fr#nav",
1683                "search": "?lang=fr"
1684            }
1685        },
1686        {
1687            "href": "https://example.net?lang=en-US#nav",
1688            "new_value": "lang=fr",
1689            "expected": {
1690                "href": "https://example.net/?lang=fr#nav",
1691                "search": "?lang=fr"
1692            }
1693        },
1694        {
1695            "href": "https://example.net?lang=en-US#nav",
1696            "new_value": "?lang=fr",
1697            "expected": {
1698                "href": "https://example.net/?lang=fr#nav",
1699                "search": "?lang=fr"
1700            }
1701        },
1702        {
1703            "href": "https://example.net?lang=en-US#nav",
1704            "new_value": "??lang=fr",
1705            "expected": {
1706                "href": "https://example.net/??lang=fr#nav",
1707                "search": "??lang=fr"
1708            }
1709        },
1710        {
1711            "href": "https://example.net?lang=en-US#nav",
1712            "new_value": "?",
1713            "expected": {
1714                "href": "https://example.net/?#nav",
1715                "search": ""
1716            }
1717        },
1718        {
1719            "href": "https://example.net?lang=en-US#nav",
1720            "new_value": "",
1721            "expected": {
1722                "href": "https://example.net/#nav",
1723                "search": ""
1724            }
1725        },
1726        {
1727            "href": "https://example.net?lang=en-US",
1728            "new_value": "",
1729            "expected": {
1730                "href": "https://example.net/",
1731                "search": ""
1732            }
1733        },
1734        {
1735            "href": "https://example.net",
1736            "new_value": "",
1737            "expected": {
1738                "href": "https://example.net/",
1739                "search": ""
1740            }
1741        },
1742        {
1743            "comment": "UTF-8 percent encoding with the query encode set. Tabs and newlines are removed.",
1744            "href": "a:/",
1745            "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
1746            "expected": {
1747                "href": "a:/?%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9",
1748                "search": "?%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9"
1749            }
1750        },
1751        {
1752            "comment": "Bytes already percent-encoded are left as-is",
1753            "href": "http://example.net",
1754            "new_value": "%c3%89té",
1755            "expected": {
1756                "href": "http://example.net/?%c3%89t%C3%A9",
1757                "search": "?%c3%89t%C3%A9"
1758            }
1759        }
1760    ],
1761    "hash": [
1762        {
1763            "href": "https://example.net",
1764            "new_value": "main",
1765            "expected": {
1766                "href": "https://example.net/#main",
1767                "hash": "#main"
1768            }
1769        },
1770        {
1771            "href": "https://example.net#nav",
1772            "new_value": "main",
1773            "expected": {
1774                "href": "https://example.net/#main",
1775                "hash": "#main"
1776            }
1777        },
1778        {
1779            "href": "https://example.net?lang=en-US",
1780            "new_value": "##nav",
1781            "expected": {
1782                "href": "https://example.net/?lang=en-US##nav",
1783                "hash": "##nav"
1784            }
1785        },
1786        {
1787            "href": "https://example.net?lang=en-US#nav",
1788            "new_value": "#main",
1789            "expected": {
1790                "href": "https://example.net/?lang=en-US#main",
1791                "hash": "#main"
1792            }
1793        },
1794        {
1795            "href": "https://example.net?lang=en-US#nav",
1796            "new_value": "#",
1797            "expected": {
1798                "href": "https://example.net/?lang=en-US#",
1799                "hash": ""
1800            }
1801        },
1802        {
1803            "href": "https://example.net?lang=en-US#nav",
1804            "new_value": "",
1805            "expected": {
1806                "href": "https://example.net/?lang=en-US",
1807                "hash": ""
1808            }
1809        },
1810        {
1811            "href": "http://example.net",
1812            "new_value": "#foo bar",
1813            "expected": {
1814                "href": "http://example.net/#foo%20bar",
1815                "hash": "#foo%20bar"
1816            }
1817        },
1818        {
1819            "href": "http://example.net",
1820            "new_value": "#foo\"bar",
1821            "expected": {
1822                "href": "http://example.net/#foo%22bar",
1823                "hash": "#foo%22bar"
1824            }
1825        },
1826        {
1827            "href": "http://example.net",
1828            "new_value": "#foo<bar",
1829            "expected": {
1830                "href": "http://example.net/#foo%3Cbar",
1831                "hash": "#foo%3Cbar"
1832            }
1833        },
1834        {
1835            "href": "http://example.net",
1836            "new_value": "#foo>bar",
1837            "expected": {
1838                "href": "http://example.net/#foo%3Ebar",
1839                "hash": "#foo%3Ebar"
1840            }
1841        },
1842        {
1843            "href": "http://example.net",
1844            "new_value": "#foo`bar",
1845            "expected": {
1846                "href": "http://example.net/#foo%60bar",
1847                "hash": "#foo%60bar"
1848            }
1849        },
1850        {
1851            "comment": "Simple percent-encoding; tabs and newlines are removed",
1852            "href": "a:/",
1853            "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
1854            "expected": {
1855                "href": "a:/#%00%01%1F%20!%22#$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_%60az{|}~%7F%C2%80%C2%81%C3%89%C3%A9",
1856                "hash": "#%00%01%1F%20!%22#$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_%60az{|}~%7F%C2%80%C2%81%C3%89%C3%A9"
1857            }
1858        },
1859        {
1860            "comment": "Percent-encode NULLs in fragment",
1861            "href": "http://example.net",
1862            "new_value": "a\u0000b",
1863            "expected": {
1864                "href": "http://example.net/#a%00b",
1865                "hash": "#a%00b"
1866            }
1867        },
1868        {
1869            "comment": "Percent-encode NULLs in fragment",
1870            "href": "non-spec:/",
1871            "new_value": "a\u0000b",
1872            "expected": {
1873                "href": "non-spec:/#a%00b",
1874                "hash": "#a%00b"
1875            }
1876        },
1877        {
1878            "comment": "Bytes already percent-encoded are left as-is",
1879            "href": "http://example.net",
1880            "new_value": "%c3%89té",
1881            "expected": {
1882                "href": "http://example.net/#%c3%89t%C3%A9",
1883                "hash": "#%c3%89t%C3%A9"
1884            }
1885        },
1886        {
1887            "href": "javascript:alert(1)",
1888            "new_value": "castle",
1889            "expected": {
1890                "href": "javascript:alert(1)#castle",
1891                "hash": "#castle"
1892            }
1893        }
1894    ]
1895}
1896