summaryrefslogtreecommitdiff
path: root/Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp
blob: 043e9c21dcfd8144ec1848108f13756948313dcd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
/*
 * Copyright (C) 2016 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "WTFStringUtilities.h"
#include <WebCore/URLParser.h>
#include <wtf/MainThread.h>
#include <wtf/text/StringBuilder.h>

using namespace WebCore;

namespace TestWebKitAPI {

class URLParserTest : public testing::Test {
public:
    void SetUp() final {
        WTF::initializeMainThread();
    }
};

struct ExpectedParts {
    String protocol;
    String user;
    String password;
    String host;
    unsigned short port;
    String path;
    String query;
    String fragment;
    String string;

    bool isInvalid() const
    {
        return protocol.isEmpty()
            && user.isEmpty()
            && password.isEmpty()
            && host.isEmpty()
            && !port
            && path.isEmpty()
            && query.isEmpty()
            && fragment.isEmpty();
    }
};

static bool eq(const String& s1, const String& s2)
{
    EXPECT_STREQ(s1.utf8().data(), s2.utf8().data());
    return s1.utf8() == s2.utf8();
}

static String insertTabAtLocation(const String& string, size_t location)
{
    ASSERT(location <= string.length());
    return makeString(string.substring(0, location), "\t", string.substring(location));
}

static ExpectedParts invalidParts(const String& urlStringWithTab)
{
    return {"", "", "", "", 0, "" , "", "", urlStringWithTab};
}

enum class TestTabs { No, Yes };

// Inserting tabs between surrogate pairs changes the encoded value instead of being skipped by the URLParser.
const TestTabs testTabsValueForSurrogatePairs = TestTabs::No;

static void checkURL(const String& urlString, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes)
{
    auto url = URL(URL(), urlString);
    
    EXPECT_TRUE(eq(parts.protocol, url.protocol().toString()));
    EXPECT_TRUE(eq(parts.user, url.user()));
    EXPECT_TRUE(eq(parts.password, url.pass()));
    EXPECT_TRUE(eq(parts.host, url.host()));
    EXPECT_EQ(parts.port, url.port().value_or(0));
    EXPECT_TRUE(eq(parts.path, url.path()));
    EXPECT_TRUE(eq(parts.query, url.query()));
    EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
    EXPECT_TRUE(eq(parts.string, url.string()));

    EXPECT_TRUE(URLParser::internalValuesConsistent(url));

    if (testTabs == TestTabs::No)
        return;

    for (size_t i = 0; i < urlString.length(); ++i) {
        String urlStringWithTab = insertTabAtLocation(urlString, i);
        checkURL(urlStringWithTab,
            parts.isInvalid() ? invalidParts(urlStringWithTab) : parts,
            TestTabs::No);
    }
}

static void checkRelativeURL(const String& urlString, const String& baseURLString, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes)
{
    auto url = URL(URL(URL(), baseURLString), urlString);
    
    EXPECT_TRUE(eq(parts.protocol, url.protocol().toString()));
    EXPECT_TRUE(eq(parts.user, url.user()));
    EXPECT_TRUE(eq(parts.password, url.pass()));
    EXPECT_TRUE(eq(parts.host, url.host()));
    EXPECT_EQ(parts.port, url.port().value_or(0));
    EXPECT_TRUE(eq(parts.path, url.path()));
    EXPECT_TRUE(eq(parts.query, url.query()));
    EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
    EXPECT_TRUE(eq(parts.string, url.string()));
    
    EXPECT_TRUE(URLParser::internalValuesConsistent(url));
    
    if (testTabs == TestTabs::No)
        return;
    
    for (size_t i = 0; i < urlString.length(); ++i) {
        String urlStringWithTab = insertTabAtLocation(urlString, i);
        checkRelativeURL(urlStringWithTab,
            baseURLString,
            parts.isInvalid() ? invalidParts(urlStringWithTab) : parts,
            TestTabs::No);
    }
}

static void checkURLDifferences(const String& urlString, const ExpectedParts& partsNew, const ExpectedParts& partsOld, TestTabs testTabs = TestTabs::Yes)
{
    UNUSED_PARAM(partsOld); // FIXME: Remove all the old expected parts.
    auto url = URL(URL(), urlString);
    
    EXPECT_TRUE(eq(partsNew.protocol, url.protocol().toString()));
    EXPECT_TRUE(eq(partsNew.user, url.user()));
    EXPECT_TRUE(eq(partsNew.password, url.pass()));
    EXPECT_TRUE(eq(partsNew.host, url.host()));
    EXPECT_EQ(partsNew.port, url.port().value_or(0));
    EXPECT_TRUE(eq(partsNew.path, url.path()));
    EXPECT_TRUE(eq(partsNew.query, url.query()));
    EXPECT_TRUE(eq(partsNew.fragment, url.fragmentIdentifier()));
    EXPECT_TRUE(eq(partsNew.string, url.string()));
    
    EXPECT_TRUE(URLParser::internalValuesConsistent(url));
    
    if (testTabs == TestTabs::No)
        return;
    
    for (size_t i = 0; i < urlString.length(); ++i) {
        String urlStringWithTab = insertTabAtLocation(urlString, i);
        checkURLDifferences(urlStringWithTab,
            partsNew.isInvalid() ? invalidParts(urlStringWithTab) : partsNew,
            partsOld.isInvalid() ? invalidParts(urlStringWithTab) : partsOld,
            TestTabs::No);
    }
}

static void checkRelativeURLDifferences(const String& urlString, const String& baseURLString, const ExpectedParts& partsNew, const ExpectedParts& partsOld, TestTabs testTabs = TestTabs::Yes)
{
    UNUSED_PARAM(partsOld); // FIXME: Remove all the old expected parts.
    auto url = URL(URL(URL(), baseURLString), urlString);
    
    EXPECT_TRUE(eq(partsNew.protocol, url.protocol().toString()));
    EXPECT_TRUE(eq(partsNew.user, url.user()));
    EXPECT_TRUE(eq(partsNew.password, url.pass()));
    EXPECT_TRUE(eq(partsNew.host, url.host()));
    EXPECT_EQ(partsNew.port, url.port().value_or(0));
    EXPECT_TRUE(eq(partsNew.path, url.path()));
    EXPECT_TRUE(eq(partsNew.query, url.query()));
    EXPECT_TRUE(eq(partsNew.fragment, url.fragmentIdentifier()));
    EXPECT_TRUE(eq(partsNew.string, url.string()));
    
    EXPECT_TRUE(URLParser::internalValuesConsistent(url));
    
    if (testTabs == TestTabs::No)
        return;
    
    for (size_t i = 0; i < urlString.length(); ++i) {
        String urlStringWithTab = insertTabAtLocation(urlString, i);
        checkRelativeURLDifferences(urlStringWithTab, baseURLString,
            partsNew.isInvalid() ? invalidParts(urlStringWithTab) : partsNew,
            partsOld.isInvalid() ? invalidParts(urlStringWithTab) : partsOld,
            TestTabs::No);
    }
}

static void shouldFail(const String& urlString)
{
    checkURL(urlString, {"", "", "", "", 0, "", "", "", urlString});
}

static void shouldFail(const String& urlString, const String& baseString)
{
    checkRelativeURL(urlString, baseString, {"", "", "", "", 0, "", "", "", urlString});
}

static void checkURL(const String& urlString, const TextEncoding& encoding, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes)
{
    URLParser parser(urlString, { }, encoding);
    auto url = parser.result();
    EXPECT_TRUE(eq(parts.protocol, url.protocol().toString()));
    EXPECT_TRUE(eq(parts.user, url.user()));
    EXPECT_TRUE(eq(parts.password, url.pass()));
    EXPECT_TRUE(eq(parts.host, url.host()));
    EXPECT_EQ(parts.port, url.port().value_or(0));
    EXPECT_TRUE(eq(parts.path, url.path()));
    EXPECT_TRUE(eq(parts.query, url.query()));
    EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
    EXPECT_TRUE(eq(parts.string, url.string()));
    
    if (testTabs == TestTabs::No)
        return;
    
    for (size_t i = 0; i < urlString.length(); ++i) {
        String urlStringWithTab = insertTabAtLocation(urlString, i);
        checkURL(urlStringWithTab, encoding,
            parts.isInvalid() ? invalidParts(urlStringWithTab) : parts,
            TestTabs::No);
    }
}

static void checkURL(const String& urlString, const String& baseURLString, const TextEncoding& encoding, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes)
{
    URLParser baseParser(baseURLString, { }, encoding);
    URLParser parser(urlString, baseParser.result(), encoding);
    auto url = parser.result();
    EXPECT_TRUE(eq(parts.protocol, url.protocol().toString()));
    EXPECT_TRUE(eq(parts.user, url.user()));
    EXPECT_TRUE(eq(parts.password, url.pass()));
    EXPECT_TRUE(eq(parts.host, url.host()));
    EXPECT_EQ(parts.port, url.port().value_or(0));
    EXPECT_TRUE(eq(parts.path, url.path()));
    EXPECT_TRUE(eq(parts.query, url.query()));
    EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
    EXPECT_TRUE(eq(parts.string, url.string()));
    
    if (testTabs == TestTabs::No)
        return;
    
    for (size_t i = 0; i < urlString.length(); ++i) {
        String urlStringWithTab = insertTabAtLocation(urlString, i);
        checkURL(urlStringWithTab, baseURLString, encoding,
            parts.isInvalid() ? invalidParts(urlStringWithTab) : parts,
            TestTabs::No);
    }
}

TEST_F(URLParserTest, Basic)
{
    checkURL("http://user:pass@webkit.org:123/path?query#fragment", {"http", "user", "pass", "webkit.org", 123, "/path", "query", "fragment", "http://user:pass@webkit.org:123/path?query#fragment"});
    checkURL("http://user:pass@webkit.org:123/path?query", {"http", "user", "pass", "webkit.org", 123, "/path", "query", "", "http://user:pass@webkit.org:123/path?query"});
    checkURL("http://user:pass@webkit.org:123/path", {"http", "user", "pass", "webkit.org", 123, "/path", "", "", "http://user:pass@webkit.org:123/path"});
    checkURL("http://user:pass@webkit.org:123/", {"http", "user", "pass", "webkit.org", 123, "/", "", "", "http://user:pass@webkit.org:123/"});
    checkURL("http://user:pass@webkit.org:123", {"http", "user", "pass", "webkit.org", 123, "/", "", "", "http://user:pass@webkit.org:123/"});
    checkURL("http://user:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"});
    checkURL("http://user:\t\t\tpass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"});
    checkURL("http://us\ter:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"});
    checkURL("http://user:pa\tss@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"});
    checkURL("http://user:pass\t@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"});
    checkURL("http://\tuser:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"});
    checkURL("http://user\t:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"});
    checkURL("http://webkit.org", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"});
    checkURL("http://127.0.0.1", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"});
    checkURL("http://webkit.org/", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"});
    checkURL("http://webkit.org/path1/path2/index.html", {"http", "", "", "webkit.org", 0, "/path1/path2/index.html", "", "", "http://webkit.org/path1/path2/index.html"});
    checkURL("about:blank", {"about", "", "", "", 0, "blank", "", "", "about:blank"});
    checkURL("about:blank?query", {"about", "", "", "", 0, "blank", "query", "", "about:blank?query"});
    checkURL("about:blank#fragment", {"about", "", "", "", 0, "blank", "", "fragment", "about:blank#fragment"});
    checkURL("http://[0:f::f:f:0:0]", {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"});
    checkURL("http://[0:f:0:0:f::]", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"});
    checkURL("http://[::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"});
    checkURL("http://[0:f:0:0:f::]:", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"});
    checkURL("http://[0:f:0:0:f::]:\t", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"});
    checkURL("http://[0:f:0:0:f::]\t:", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"});
    checkURL("http://\t[::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"});
    checkURL("http://[\t::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"});
    checkURL("http://[:\t:f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"});
    checkURL("http://[::\tf:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"});
    checkURL("http://[::f\t:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"});
    checkURL("http://[::f:\t0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"});
    checkURL("http://example.com/path1/path2/.", {"http", "", "", "example.com", 0, "/path1/path2/", "", "", "http://example.com/path1/path2/"});
    checkURL("http://example.com/path1/path2/..", {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"});
    checkURL("http://example.com/path1/path2/./path3", {"http", "", "", "example.com", 0, "/path1/path2/path3", "", "", "http://example.com/path1/path2/path3"});
    checkURL("http://example.com/path1/path2/.\\path3", {"http", "", "", "example.com", 0, "/path1/path2/path3", "", "", "http://example.com/path1/path2/path3"});
    checkURL("http://example.com/path1/path2/../path3", {"http", "", "", "example.com", 0, "/path1/path3", "", "", "http://example.com/path1/path3"});
    checkURL("http://example.com/path1/path2/..\\path3", {"http", "", "", "example.com", 0, "/path1/path3", "", "", "http://example.com/path1/path3"});
    checkURL("http://example.com/.", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
    checkURL("http://example.com/..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
    checkURL("http://example.com/./path1", {"http", "", "", "example.com", 0, "/path1", "", "", "http://example.com/path1"});
    checkURL("http://example.com/../path1", {"http", "", "", "example.com", 0, "/path1", "", "", "http://example.com/path1"});
    checkURL("http://example.com/../path1/../../path2/path3/../path4", {"http", "", "", "example.com", 0, "/path2/path4", "", "", "http://example.com/path2/path4"});
    checkURL("http://example.com/path1/.%2", {"http", "", "", "example.com", 0, "/path1/.%2", "", "", "http://example.com/path1/.%2"});
    checkURL("http://example.com/path1/%2", {"http", "", "", "example.com", 0, "/path1/%2", "", "", "http://example.com/path1/%2"});
    checkURL("http://example.com/path1/%", {"http", "", "", "example.com", 0, "/path1/%", "", "", "http://example.com/path1/%"});
    checkURL("http://example.com/path1/.%", {"http", "", "", "example.com", 0, "/path1/.%", "", "", "http://example.com/path1/.%"});
    checkURL("http://example.com//.", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
    checkURL("http://example.com//./", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
    checkURL("http://example.com//.//", {"http", "", "", "example.com", 0, "///", "", "", "http://example.com///"});
    checkURL("http://example.com//..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
    checkURL("http://example.com//../", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
    checkURL("http://example.com//..//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
    checkURL("http://example.com//..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
    checkURL("http://example.com/.//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
    checkURL("http://example.com/..//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
    checkURL("http://example.com/./", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
    checkURL("http://example.com/../", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
    checkURL("http://example.com/path1/.../path3", {"http", "", "", "example.com", 0, "/path1/.../path3", "", "", "http://example.com/path1/.../path3"});
    checkURL("http://example.com/path1/...", {"http", "", "", "example.com", 0, "/path1/...", "", "", "http://example.com/path1/..."});
    checkURL("http://example.com/path1/.../", {"http", "", "", "example.com", 0, "/path1/.../", "", "", "http://example.com/path1/.../"});
    checkURL("http://example.com/.path1/", {"http", "", "", "example.com", 0, "/.path1/", "", "", "http://example.com/.path1/"});
    checkURL("http://example.com/..path1/", {"http", "", "", "example.com", 0, "/..path1/", "", "", "http://example.com/..path1/"});
    checkURL("http://example.com/path1/.path2", {"http", "", "", "example.com", 0, "/path1/.path2", "", "", "http://example.com/path1/.path2"});
    checkURL("http://example.com/path1/..path2", {"http", "", "", "example.com", 0, "/path1/..path2", "", "", "http://example.com/path1/..path2"});
    checkURL("http://example.com/path1/path2/.?query", {"http", "", "", "example.com", 0, "/path1/path2/", "query", "", "http://example.com/path1/path2/?query"});
    checkURL("http://example.com/path1/path2/..?query", {"http", "", "", "example.com", 0, "/path1/", "query", "", "http://example.com/path1/?query"});
    checkURL("http://example.com/path1/path2/.#fragment", {"http", "", "", "example.com", 0, "/path1/path2/", "", "fragment", "http://example.com/path1/path2/#fragment"});
    checkURL("http://example.com/path1/path2/..#fragment", {"http", "", "", "example.com", 0, "/path1/", "", "fragment", "http://example.com/path1/#fragment"});

    checkURL("file:", {"file", "", "", "", 0, "/", "", "", "file:///"});
    checkURL("file:/", {"file", "", "", "", 0, "/", "", "", "file:///"});
    checkURL("file://", {"file", "", "", "", 0, "/", "", "", "file:///"});
    checkURL("file:///", {"file", "", "", "", 0, "/", "", "", "file:///"});
    checkURL("file:////", {"file", "", "", "", 0, "//", "", "", "file:////"}); // This matches Firefox and URL::parse which I believe are correct, but not Chrome.
    checkURL("file:/path", {"file", "", "", "", 0, "/path", "", "", "file:///path"});
    checkURL("file://host/path", {"file", "", "", "host", 0, "/path", "", "", "file://host/path"});
    checkURL("file://host", {"file", "", "", "host", 0, "/", "", "", "file://host/"});
    checkURL("file://host/", {"file", "", "", "host", 0, "/", "", "", "file://host/"});
    checkURL("file:///path", {"file", "", "", "", 0, "/path", "", "", "file:///path"});
    checkURL("file:////path", {"file", "", "", "", 0, "//path", "", "", "file:////path"});
    checkURL("file://localhost/path", {"file", "", "", "", 0, "/path", "", "", "file:///path"});
    checkURL("file://localhost/", {"file", "", "", "", 0, "/", "", "", "file:///"});
    checkURL("file://localhost", {"file", "", "", "", 0, "/", "", "", "file:///"});
    checkURL("file://lOcAlHoSt", {"file", "", "", "", 0, "/", "", "", "file:///"});
    checkURL("file://lOcAlHoSt/", {"file", "", "", "", 0, "/", "", "", "file:///"});
    checkURL("file:/pAtH/", {"file", "", "", "", 0, "/pAtH/", "", "", "file:///pAtH/"});
    checkURL("file:/pAtH", {"file", "", "", "", 0, "/pAtH", "", "", "file:///pAtH"});
    checkURL("file:?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"});
    checkURL("file:#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"});
    checkURL("file:?query#fragment", {"file", "", "", "", 0, "/", "query", "fragment", "file:///?query#fragment"});
    checkURL("file:#fragment?notquery", {"file", "", "", "", 0, "/", "", "fragment?notquery", "file:///#fragment?notquery"});
    checkURL("file:/?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"});
    checkURL("file:/#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"});
    checkURL("file://?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"});
    checkURL("file://#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"});
    checkURL("file:///?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"});
    checkURL("file:///#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"});
    checkURL("file:////?query", {"file", "", "", "", 0, "//", "query", "", "file:////?query"});
    checkURL("file:////#fragment", {"file", "", "", "", 0, "//", "", "fragment", "file:////#fragment"});
    checkURL("http://host/A b", {"http", "", "", "host", 0, "/A%20b", "", "", "http://host/A%20b"});
    checkURL("http://host/a%20B", {"http", "", "", "host", 0, "/a%20B", "", "", "http://host/a%20B"});
    checkURL("http://host?q=@ <>!#fragment", {"http", "", "", "host", 0, "/", "q=@%20%3C%3E!", "fragment", "http://host/?q=@%20%3C%3E!#fragment"});
    checkURL("http://user:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"});
    checkURL("http://user:@\thost", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"});
    checkURL("http://user:\t@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"});
    checkURL("http://user\t:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"});
    checkURL("http://use\tr:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"});
    checkURL("http://127.0.0.1:10100/path", {"http", "", "", "127.0.0.1", 10100, "/path", "", "", "http://127.0.0.1:10100/path"});
    checkURL("http://127.0.0.1:/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"});
    checkURL("http://127.0.0.1\t:/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"});
    checkURL("http://127.0.0.1:\t/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"});
    checkURL("http://127.0.0.1:/\tpath", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"});
    checkURL("http://127.0.0.1:123", {"http", "", "", "127.0.0.1", 123, "/", "", "", "http://127.0.0.1:123/"});
    checkURL("http://127.0.0.1:", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"});
    checkURL("http://[0:f::f:f:0:0]:123/path", {"http", "", "", "[0:f::f:f:0:0]", 123, "/path", "", "", "http://[0:f::f:f:0:0]:123/path"});
    checkURL("http://[0:f::f:f:0:0]:123", {"http", "", "", "[0:f::f:f:0:0]", 123, "/", "", "", "http://[0:f::f:f:0:0]:123/"});
    checkURL("http://[0:f:0:0:f:\t:]:123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"});
    checkURL("http://[0:f:0:0:f::\t]:123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"});
    checkURL("http://[0:f:0:0:f::]\t:123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"});
    checkURL("http://[0:f:0:0:f::]:\t123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"});
    checkURL("http://[0:f:0:0:f::]:1\t23", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"});
    checkURL("http://[0:f::f:f:0:0]:/path", {"http", "", "", "[0:f::f:f:0:0]", 0, "/path", "", "", "http://[0:f::f:f:0:0]/path"});
    checkURL("http://[0:f::f:f:0:0]:", {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"});
    checkURL("http://host:10100/path", {"http", "", "", "host", 10100, "/path", "", "", "http://host:10100/path"});
    checkURL("http://host:/path", {"http", "", "", "host", 0, "/path", "", "", "http://host/path"});
    checkURL("http://host:123", {"http", "", "", "host", 123, "/", "", "", "http://host:123/"});
    checkURL("http://host:", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkURL("http://hos\tt\n:\t1\n2\t3\t/\npath", {"http", "", "", "host", 123, "/path", "", "", "http://host:123/path"});
    checkURL("http://user@example.org/path3", {"http", "user", "", "example.org", 0, "/path3", "", "", "http://user@example.org/path3"});
    checkURL("sc:/pa/pa", {"sc", "", "", "", 0, "/pa/pa", "", "", "sc:/pa/pa"});
    checkURL("sc:/pa", {"sc", "", "", "", 0, "/pa", "", "", "sc:/pa"});
    checkURL("sc:/pa/", {"sc", "", "", "", 0, "/pa/", "", "", "sc:/pa/"});
    checkURL("notspecial:/notuser:notpassword@nothost", {"notspecial", "", "", "", 0, "/notuser:notpassword@nothost", "", "", "notspecial:/notuser:notpassword@nothost"});
    checkURL("sc://pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"});
    checkURL("sc://\tpa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"});
    checkURL("sc:/\t/pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"});
    checkURL("sc:\t//pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"});
    checkURL("http://host   \a   ", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkURL("notspecial:/a", {"notspecial", "", "", "", 0, "/a", "", "", "notspecial:/a"});
    checkURL("notspecial:", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
    checkURL("http:/a", {"http", "", "", "a", 0, "/", "", "", "http://a/"});
    checkURL("http://256../", {"http", "", "", "256..", 0, "/", "", "", "http://256../"});
    checkURL("http://256..", {"http", "", "", "256..", 0, "/", "", "", "http://256../"});
    checkURL("http://127..1/", {"http", "", "", "127..1", 0, "/", "", "", "http://127..1/"});
    checkURL("http://127.a.0.1/", {"http", "", "", "127.a.0.1", 0, "/", "", "", "http://127.a.0.1/"});
    checkURL("http://127.0.0.1/", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"});
    checkURL("http://12\t7.0.0.1/", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"});
    checkURL("http://127.\t0.0.1/", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"});
    checkURL("http://./", {"http", "", "", ".", 0, "/", "", "", "http://./"});
    checkURL("http://.", {"http", "", "", ".", 0, "/", "", "", "http://./"});
    checkURL("notspecial:/a", {"notspecial", "", "", "", 0, "/a", "", "", "notspecial:/a"});
    checkURL("notspecial:", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
    checkURL("notspecial:/", {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"});
    checkURL("data:image/png;base64,encoded-data-follows-here", {"data", "", "", "", 0, "image/png;base64,encoded-data-follows-here", "", "", "data:image/png;base64,encoded-data-follows-here"});
    checkURL("data:image/png;base64,encoded/data-with-slash", {"data", "", "", "", 0, "image/png;base64,encoded/data-with-slash", "", "", "data:image/png;base64,encoded/data-with-slash"});
    checkURL("about:~", {"about", "", "", "", 0, "~", "", "", "about:~"});
    checkURL("https://@test@test@example:800\\path@end", {"", "", "", "", 0, "", "", "", "https://@test@test@example:800\\path@end"});
    checkURL("http://www.example.com/#a\nb\rc\td", {"http", "", "", "www.example.com", 0, "/", "", "abcd", "http://www.example.com/#abcd"});
    checkURL("http://[A:b:c:DE:fF:0:1:aC]/", {"http", "", "", "[a:b:c:de:ff:0:1:ac]", 0, "/", "", "", "http://[a:b:c:de:ff:0:1:ac]/"});
    checkURL("http:////////user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"});
    checkURL("http:////////user:@webkit.org:99#foo", {"http", "user", "", "webkit.org", 99, "/", "", "foo", "http://user@webkit.org:99/#foo"});
    checkURL("http:////\t////user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"});
    checkURL("http://\t//\\///user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"});
    checkURL("http:/\\user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"});
    checkURL("http://127.0.0.1", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"});
    checkURLDifferences("http://127.0.0.1.",
        {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"},
        {"http", "", "", "127.0.0.1.", 0, "/", "", "", "http://127.0.0.1./"});
    checkURLDifferences("http://127.0.0.1./",
        {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"},
        {"http", "", "", "127.0.0.1.", 0, "/", "", "", "http://127.0.0.1./"});
    checkURL("http://127.0.0.1../", {"http", "", "", "127.0.0.1..", 0, "/", "", "", "http://127.0.0.1../"});
    checkURLDifferences("http://0x100.0/",
        {"", "", "", "", 0, "", "", "", "http://0x100.0/"},
        {"http", "", "", "0x100.0", 0, "/", "", "", "http://0x100.0/"});
    checkURLDifferences("http://0.0.0x100.0/",
        {"", "", "", "", 0, "", "", "", "http://0.0.0x100.0/"},
        {"http", "", "", "0.0.0x100.0", 0, "/", "", "", "http://0.0.0x100.0/"});
    checkURLDifferences("http://0.0.0.0x100/",
        {"", "", "", "", 0, "", "", "", "http://0.0.0.0x100/"},
        {"http", "", "", "0.0.0.0x100", 0, "/", "", "", "http://0.0.0.0x100/"});
    checkURL("http://host:123?", {"http", "", "", "host", 123, "/", "", "", "http://host:123/?"});
    checkURL("http://host:123?query", {"http", "", "", "host", 123, "/", "query", "", "http://host:123/?query"});
    checkURL("http://host:123#", {"http", "", "", "host", 123, "/", "", "", "http://host:123/#"});
    checkURL("http://host:123#fragment", {"http", "", "", "host", 123, "/", "", "fragment", "http://host:123/#fragment"});
    checkURLDifferences("foo:////",
        {"foo", "", "", "", 0, "//", "", "", "foo:////"},
        {"foo", "", "", "", 0, "////", "", "", "foo:////"});
    checkURLDifferences("foo:///?",
        {"foo", "", "", "", 0, "/", "", "", "foo:///?"},
        {"foo", "", "", "", 0, "///", "", "", "foo:///?"});
    checkURLDifferences("foo:///#",
        {"foo", "", "", "", 0, "/", "", "", "foo:///#"},
        {"foo", "", "", "", 0, "///", "", "", "foo:///#"});
    checkURLDifferences("foo:///",
        {"foo", "", "", "", 0, "/", "", "", "foo:///"},
        {"foo", "", "", "", 0, "///", "", "", "foo:///"});
    checkURLDifferences("foo://?",
        {"foo", "", "", "", 0, "", "", "", "foo://?"},
        {"foo", "", "", "", 0, "//", "", "", "foo://?"});
    checkURLDifferences("foo://#",
        {"foo", "", "", "", 0, "", "", "", "foo://#"},
        {"foo", "", "", "", 0, "//", "", "", "foo://#"});
    checkURLDifferences("foo://",
        {"foo", "", "", "", 0, "", "", "", "foo://"},
        {"foo", "", "", "", 0, "//", "", "", "foo://"});
    checkURL("foo:/?", {"foo", "", "", "", 0, "/", "", "", "foo:/?"});
    checkURL("foo:/#", {"foo", "", "", "", 0, "/", "", "", "foo:/#"});
    checkURL("foo:/", {"foo", "", "", "", 0, "/", "", "", "foo:/"});
    checkURL("foo:?", {"foo", "", "", "", 0, "", "", "", "foo:?"});
    checkURL("foo:#", {"foo", "", "", "", 0, "", "", "", "foo:#"});
    checkURLDifferences("A://",
        {"a", "", "", "", 0, "", "", "", "a://"},
        {"a", "", "", "", 0, "//", "", "", "a://"});
    checkURLDifferences("aA://",
        {"aa", "", "", "", 0, "", "", "", "aa://"},
        {"aa", "", "", "", 0, "//", "", "", "aa://"});
    checkURL(utf16String(u"foo://host/#ПП\u0007 a</"), {"foo", "", "", "host", 0, "/", "", "%D0%9F%D0%9F%07 a</", "foo://host/#%D0%9F%D0%9F%07 a</"});
    checkURL(utf16String(u"foo://host/#\u0007 a</"), {"foo", "", "", "host", 0, "/", "", "%07 a</", "foo://host/#%07 a</"});
    checkURL(utf16String(u"http://host?ß😍#ß😍"), {"http", "", "", "host", 0, "/", "%C3%9F%F0%9F%98%8D", "%C3%9F%F0%9F%98%8D", "http://host/?%C3%9F%F0%9F%98%8D#%C3%9F%F0%9F%98%8D"}, testTabsValueForSurrogatePairs);
    checkURL(utf16String(u"http://host/path#💩\t💩"), {"http", "", "", "host", 0, "/path", "", "%F0%9F%92%A9%F0%9F%92%A9", "http://host/path#%F0%9F%92%A9%F0%9F%92%A9"}, testTabsValueForSurrogatePairs);
    checkURL(utf16String(u"http://host/#ПП\u0007 a</"), {"http", "", "", "host", 0, "/", "", "%D0%9F%D0%9F%07 a</", "http://host/#%D0%9F%D0%9F%07 a</"});
    checkURL(utf16String(u"http://host/#\u0007 a</"), {"http", "", "", "host", 0, "/", "", "%07 a</", "http://host/#%07 a</"});

    // This disagrees with the web platform test for http://:@www.example.com but agrees with Chrome and URL::parse,
    // and Firefox fails the web platform test differently. Maybe the web platform test ought to be changed.
    checkURL("http://:@host", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
}

TEST_F(URLParserTest, ParseRelative)
{
    checkRelativeURL("/index.html", "http://webkit.org/path1/path2/", {"http", "", "", "webkit.org", 0, "/index.html", "", "", "http://webkit.org/index.html"});
    checkRelativeURL("http://whatwg.org/index.html", "http://webkit.org/path1/path2/", {"http", "", "", "whatwg.org", 0, "/index.html", "", "", "http://whatwg.org/index.html"});
    checkRelativeURL("index.html", "http://webkit.org/path1/path2/page.html?query#fragment", {"http", "", "", "webkit.org", 0, "/path1/path2/index.html", "", "", "http://webkit.org/path1/path2/index.html"});
    checkRelativeURL("//whatwg.org/index.html", "https://www.webkit.org/path", {"https", "", "", "whatwg.org", 0, "/index.html", "", "", "https://whatwg.org/index.html"});
    checkRelativeURL("http://example\t.\norg", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/", "", "", "http://example.org/"});
    checkRelativeURL("test", "file:///path1/path2", {"file", "", "", "", 0, "/path1/test", "", "", "file:///path1/test"});
    checkRelativeURL(utf16String(u"http://www.foo。bar.com"), "http://other.com/", {"http", "", "", "www.foo.bar.com", 0, "/", "", "", "http://www.foo.bar.com/"});
    checkRelativeURLDifferences(utf16String(u"sc://ñ.test/"), "about:blank",
        {"sc", "", "", "%C3%B1.test", 0, "/", "", "", "sc://%C3%B1.test/"},
        {"sc", "", "", "xn--ida.test", 0, "/", "", "", "sc://xn--ida.test/"});
    checkRelativeURL("#fragment", "http://host/path", {"http", "", "", "host", 0, "/path", "", "fragment", "http://host/path#fragment"});
    checkRelativeURL("#fragment", "file:///path", {"file", "", "", "", 0, "/path", "", "fragment", "file:///path#fragment"});
    checkRelativeURL("#fragment", "file:///path#old", {"file", "", "", "", 0, "/path", "", "fragment", "file:///path#fragment"});
    checkRelativeURL("#", "file:///path#old", {"file", "", "", "", 0, "/path", "", "", "file:///path#"});
    checkRelativeURL("  ", "file:///path#old", {"file", "", "", "", 0, "/path", "", "", "file:///path"});
    checkRelativeURL("#", "file:///path", {"file", "", "", "", 0, "/path", "", "", "file:///path#"});
    checkRelativeURL("#", "file:///path?query", {"file", "", "", "", 0, "/path", "query", "", "file:///path?query#"});
    checkRelativeURL("#", "file:///path?query#old", {"file", "", "", "", 0, "/path", "query", "", "file:///path?query#"});
    checkRelativeURL("?query", "http://host/path", {"http", "", "", "host", 0, "/path", "query", "", "http://host/path?query"});
    checkRelativeURL("?query#fragment", "http://host/path", {"http", "", "", "host", 0, "/path", "query", "fragment", "http://host/path?query#fragment"});
    checkRelativeURL("?new", "file:///path?old#fragment", {"file", "", "", "", 0, "/path", "new", "", "file:///path?new"});
    checkRelativeURL("?", "file:///path?old#fragment", {"file", "", "", "", 0, "/path", "", "", "file:///path?"});
    checkRelativeURL("?", "file:///path", {"file", "", "", "", 0, "/path", "", "", "file:///path?"});
    checkRelativeURL("?query", "file:///path", {"file", "", "", "", 0, "/path", "query", "", "file:///path?query"});
    checkRelativeURL(utf16String(u"?β"), "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "%CE%B2", "", "http://example.org/foo/bar?%CE%B2"});
    checkRelativeURL("?", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar?"});
    checkRelativeURL("#", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar#"});
    checkRelativeURL("?#", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar?#"});
    checkRelativeURL("#?", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "?", "http://example.org/foo/bar#?"});
    checkRelativeURL("/", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/", "", "", "http://example.org/"});
    checkRelativeURL("http://@host", "about:blank", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkRelativeURL("http://:@host", "about:blank", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkRelativeURL("http://foo.com/\\@", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "//@", "", "", "http://foo.com//@"});
    checkRelativeURL("\\@", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/@", "", "", "http://example.org/@"});
    checkRelativeURL("/path3", "http://user@example.org/path1/path2", {"http", "user", "", "example.org", 0, "/path3", "", "", "http://user@example.org/path3"});
    checkRelativeURL("", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
    checkRelativeURL("\t", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
    checkRelativeURL(" ", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
    checkRelativeURL("  \a  \t\n", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
    checkRelativeURL(":foo.com\\", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/:foo.com/", "", "", "http://example.org/foo/:foo.com/"});
    checkRelativeURL("http:/example.com/", "about:blank", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
    checkRelativeURL("http:example.com/", "about:blank", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
    checkRelativeURL("http:\\\\foo.com\\", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"});
    checkRelativeURL("http:\\\\foo.com/", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"});
    checkRelativeURL("http:\\\\foo.com", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"});
    checkRelativeURL("http://ExAmPlE.CoM", "http://other.com", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
    checkRelativeURL("http:", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
    checkRelativeURL("#x", "data:,", {"data", "", "", "", 0, ",", "", "x", "data:,#x"});
    checkRelativeURL("#x", "about:blank", {"about", "", "", "", 0, "blank", "", "x", "about:blank#x"});
    checkRelativeURL("  foo.com  ", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/foo.com", "", "", "http://example.org/foo/foo.com"});
    checkRelativeURL(" \a baz", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/baz", "", "", "http://example.org/foo/baz"});
    checkRelativeURL("~", "http://example.org", {"http", "", "", "example.org", 0, "/~", "", "", "http://example.org/~"});
    checkRelativeURL("notspecial:", "about:blank", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
    checkRelativeURL("notspecial:", "http://host", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
    checkRelativeURL("http:", "http://host", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkRelativeURL("i", "sc:/pa/po", {"sc", "", "", "", 0, "/pa/i", "", "", "sc:/pa/i"});
    checkRelativeURL("i    ", "sc:/pa/po", {"sc", "", "", "", 0, "/pa/i", "", "", "sc:/pa/i"});
    checkRelativeURL("i\t\n  ", "sc:/pa/po", {"sc", "", "", "", 0, "/pa/i", "", "", "sc:/pa/i"});
    checkRelativeURL("i", "sc://ho/pa", {"sc", "", "", "ho", 0, "/i", "", "", "sc://ho/i"});
    checkRelativeURL("!", "sc://ho/pa", {"sc", "", "", "ho", 0, "/!", "", "", "sc://ho/!"});
    checkRelativeURL("!", "sc:/ho/pa", {"sc", "", "", "", 0, "/ho/!", "", "", "sc:/ho/!"});
    checkRelativeURL("notspecial:/", "about:blank", {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"});
    checkRelativeURL("notspecial:/", "http://host", {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"});
    checkRelativeURL("foo:/", "http://example.org/foo/bar", {"foo", "", "", "", 0, "/", "", "", "foo:/"});
    checkRelativeURL("://:0/", "http://webkit.org/", {"http", "", "", "webkit.org", 0, "/://:0/", "", "", "http://webkit.org/://:0/"});
    checkRelativeURL(String(), "http://webkit.org/", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"});
    checkRelativeURL("https://@test@test@example:800\\path@end", "http://doesnotmatter/", {"", "", "", "", 0, "", "", "", "https://@test@test@example:800\\path@end"});
    checkRelativeURL("http://f:0/c", "http://example.org/foo/bar", {"http", "", "", "f", 0, "/c", "", "", "http://f:0/c"});
    checkRelativeURL(String(), "http://host/#fragment", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkRelativeURL("", "http://host/#fragment", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkRelativeURL("  ", "http://host/#fragment", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkRelativeURL("  ", "http://host/path?query#fra#gment", {"http", "", "", "host", 0, "/path", "query", "", "http://host/path?query"});
    checkRelativeURL(" \a ", "http://host/#fragment", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkRelativeURLDifferences("foo://", "http://example.org/foo/bar",
        {"foo", "", "", "", 0, "", "", "", "foo://"},
        {"foo", "", "", "", 0, "//", "", "", "foo://"});
    checkRelativeURL(utf16String(u"#β"), "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "%CE%B2", "http://example.org/foo/bar#%CE%B2"});
    checkRelativeURL("index.html", "applewebdata://Host/", {"applewebdata", "", "", "Host", 0, "/index.html", "", "", "applewebdata://Host/index.html"});
    checkRelativeURL("index.html", "applewebdata://Host", {"applewebdata", "", "", "Host", 0, "/index.html", "", "", "applewebdata://Host/index.html"});
    checkRelativeURL("", "applewebdata://Host", {"applewebdata", "", "", "Host", 0, "", "", "", "applewebdata://Host"});
    checkRelativeURL("?query", "applewebdata://Host", {"applewebdata", "", "", "Host", 0, "", "query", "", "applewebdata://Host?query"});
    checkRelativeURL("#fragment", "applewebdata://Host", {"applewebdata", "", "", "Host", 0, "", "", "fragment", "applewebdata://Host#fragment"});
    checkRelativeURL("notspecial://something?", "file:////var//containers//stuff/", {"notspecial", "", "", "something", 0, "", "", "", "notspecial://something?"}, TestTabs::No);
    checkRelativeURL("notspecial://something#", "file:////var//containers//stuff/", {"notspecial", "", "", "something", 0, "", "", "", "notspecial://something#"}, TestTabs::No);
    checkRelativeURL("http://something?", "file:////var//containers//stuff/", {"http", "", "", "something", 0, "/", "", "", "http://something/?"}, TestTabs::No);
    checkRelativeURL("http://something#", "file:////var//containers//stuff/", {"http", "", "", "something", 0, "/", "", "", "http://something/#"}, TestTabs::No);
    checkRelativeURL("file:", "file:///path?query#fragment", {"file", "", "", "", 0, "/path", "query", "", "file:///path?query"});
    checkRelativeURL("/", "file:///C:/a/b", {"file", "", "", "", 0, "/C:/", "", "", "file:///C:/"});
    checkRelativeURL("/abc", "file:///C:/a/b", {"file", "", "", "", 0, "/C:/abc", "", "", "file:///C:/abc"});
    checkRelativeURL("/abc", "file:///C:", {"file", "", "", "", 0, "/C:/abc", "", "", "file:///C:/abc"});
    checkRelativeURL("/abc", "file:///", {"file", "", "", "", 0, "/abc", "", "", "file:///abc"});
    checkRelativeURL("//d:", "file:///C:/a/b", {"file", "", "", "", 0, "/d:", "", "", "file:///d:"}, TestTabs::No);
    checkRelativeURL("//d|", "file:///C:/a/b", {"file", "", "", "", 0, "/d:", "", "", "file:///d:"}, TestTabs::No);
    checkRelativeURL("//A|", "file:///C:/a/b", {"file", "", "", "", 0, "/A:", "", "", "file:///A:"}, TestTabs::No);

    // The checking of slashes in SpecialAuthoritySlashes needed to get this to pass contradicts what is in the spec,
    // but it is included in the web platform tests.
    checkRelativeURL("http:\\\\host\\foo", "about:blank", {"http", "", "", "host", 0, "/foo", "", "", "http://host/foo"});
}

// These are differences between the new URLParser and the old URL::parse which make URLParser more standards compliant.
TEST_F(URLParserTest, ParserDifferences)
{
    checkURLDifferences("http://127.0.1",
        {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"},
        {"http", "", "", "127.0.1", 0, "/", "", "", "http://127.0.1/"});
    checkURLDifferences("http://011.11.0X11.0x011",
        {"http", "", "", "9.11.17.17", 0, "/", "", "", "http://9.11.17.17/"},
        {"http", "", "", "011.11.0x11.0x011", 0, "/", "", "", "http://011.11.0x11.0x011/"});
    checkURLDifferences("http://[1234:0078:90AB:CdEf:0123:0007:89AB:0000]",
        {"http", "", "", "[1234:78:90ab:cdef:123:7:89ab:0]", 0, "/", "", "", "http://[1234:78:90ab:cdef:123:7:89ab:0]/"},
        {"http", "", "", "[1234:0078:90ab:cdef:0123:0007:89ab:0000]", 0, "/", "", "", "http://[1234:0078:90ab:cdef:0123:0007:89ab:0000]/"});
    checkURLDifferences("http://[0:f:0:0:f:f:0:0]",
        {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"},
        {"http", "", "", "[0:f:0:0:f:f:0:0]", 0, "/", "", "", "http://[0:f:0:0:f:f:0:0]/"});
    checkURLDifferences("http://[0:f:0:0:f:0:0:0]",
        {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"},
        {"http", "", "", "[0:f:0:0:f:0:0:0]", 0, "/", "", "", "http://[0:f:0:0:f:0:0:0]/"});
    checkURLDifferences("http://[0:0:f:0:0:f:0:0]",
        {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"},
        {"http", "", "", "[0:0:f:0:0:f:0:0]", 0, "/", "", "", "http://[0:0:f:0:0:f:0:0]/"});
    checkURLDifferences("http://[a:0:0:0:b:c::d]",
        {"http", "", "", "[a::b:c:0:d]", 0, "/", "", "", "http://[a::b:c:0:d]/"},
        {"http", "", "", "[a:0:0:0:b:c::d]", 0, "/", "", "", "http://[a:0:0:0:b:c::d]/"});
    checkURLDifferences("http://[::7f00:0001]/",
        {"http", "", "", "[::7f00:1]", 0, "/", "", "", "http://[::7f00:1]/"},
        {"http", "", "", "[::7f00:0001]", 0, "/", "", "", "http://[::7f00:0001]/"});
    checkURLDifferences("http://[::7f00:00]/",
        {"http", "", "", "[::7f00:0]", 0, "/", "", "", "http://[::7f00:0]/"},
        {"http", "", "", "[::7f00:00]", 0, "/", "", "", "http://[::7f00:00]/"});
    checkURLDifferences("http://[::0:7f00:0001]/",
        {"http", "", "", "[::7f00:1]", 0, "/", "", "", "http://[::7f00:1]/"},
        {"http", "", "", "[::0:7f00:0001]", 0, "/", "", "", "http://[::0:7f00:0001]/"});
    checkURLDifferences("http://127.00.0.1/",
        {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"},
        {"http", "", "", "127.00.0.1", 0, "/", "", "", "http://127.00.0.1/"});
    checkURLDifferences("http://127.0.0.01/",
        {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"},
        {"http", "", "", "127.0.0.01", 0, "/", "", "", "http://127.0.0.01/"});
    checkURLDifferences("http://example.com/path1/.%2e",
        {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
        {"http", "", "", "example.com", 0, "/path1/.%2e", "", "", "http://example.com/path1/.%2e"});
    checkURLDifferences("http://example.com/path1/.%2E",
        {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
        {"http", "", "", "example.com", 0, "/path1/.%2E", "", "", "http://example.com/path1/.%2E"});
    checkURLDifferences("http://example.com/path1/.%2E/",
        {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
        {"http", "", "", "example.com", 0, "/path1/.%2E/", "", "", "http://example.com/path1/.%2E/"});
    checkURLDifferences("http://example.com/path1/%2e.",
        {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
        {"http", "", "", "example.com", 0, "/path1/%2e.", "", "", "http://example.com/path1/%2e."});
    checkURLDifferences("http://example.com/path1/%2E%2e",
        {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
        {"http", "", "", "example.com", 0, "/path1/%2E%2e", "", "", "http://example.com/path1/%2E%2e"});
    checkURLDifferences("http://example.com/path1/%2e",
        {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"},
        {"http", "", "", "example.com", 0, "/path1/%2e", "", "", "http://example.com/path1/%2e"});
    checkURLDifferences("http://example.com/path1/%2E",
        {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"},
        {"http", "", "", "example.com", 0, "/path1/%2E", "", "", "http://example.com/path1/%2E"});
    checkURLDifferences("http://example.com/path1/%2E/",
        {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"},
        {"http", "", "", "example.com", 0, "/path1/%2E/", "", "", "http://example.com/path1/%2E/"});
    checkURLDifferences("http://example.com/path1/path2/%2e?query",
        {"http", "", "", "example.com", 0, "/path1/path2/", "query", "", "http://example.com/path1/path2/?query"},
        {"http", "", "", "example.com", 0, "/path1/path2/%2e", "query", "", "http://example.com/path1/path2/%2e?query"});
    checkURLDifferences("http://example.com/path1/path2/%2e%2e?query",
        {"http", "", "", "example.com", 0, "/path1/", "query", "", "http://example.com/path1/?query"},
        {"http", "", "", "example.com", 0, "/path1/path2/%2e%2e", "query", "", "http://example.com/path1/path2/%2e%2e?query"});
    checkURLDifferences("http://example.com/path1/path2/%2e#fragment",
        {"http", "", "", "example.com", 0, "/path1/path2/", "", "fragment", "http://example.com/path1/path2/#fragment"},
        {"http", "", "", "example.com", 0, "/path1/path2/%2e", "", "fragment", "http://example.com/path1/path2/%2e#fragment"});
    checkURLDifferences("http://example.com/path1/path2/%2e%2e#fragment",
        {"http", "", "", "example.com", 0, "/path1/", "", "fragment", "http://example.com/path1/#fragment"},
        {"http", "", "", "example.com", 0, "/path1/path2/%2e%2e", "", "fragment", "http://example.com/path1/path2/%2e%2e#fragment"});
    checkURL("http://example.com/path1/path2/A%2e%2e#fragment", {"http", "", "", "example.com", 0, "/path1/path2/A%2e%2e", "", "fragment", "http://example.com/path1/path2/A%2e%2e#fragment"});
    checkURLDifferences("file://[0:a:0:0:b:c:0:0]/path",
        {"file", "", "", "[0:a::b:c:0:0]", 0, "/path", "", "", "file://[0:a::b:c:0:0]/path"},
        {"file", "", "", "[0:a:0:0:b:c:0:0]", 0, "/path", "", "", "file://[0:a:0:0:b:c:0:0]/path"});
    checkURLDifferences("http://",
        {"", "", "", "", 0, "", "", "", "http://"},
        {"http", "", "", "", 0, "/", "", "", "http:/"});
    checkRelativeURLDifferences("//", "https://www.webkit.org/path",
        {"", "", "", "", 0, "", "", "", "//"},
        {"https", "", "", "", 0, "/", "", "", "https:/"});
    checkURLDifferences("http://127.0.0.1:65536/path",
        {"", "", "", "", 0, "", "", "", "http://127.0.0.1:65536/path"},
        {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1:65536/path"});
    checkURLDifferences("http://host:65536",
        {"", "", "", "", 0, "", "", "", "http://host:65536"},
        {"http", "", "", "host", 0, "/", "", "", "http://host:65536/"});
    checkURLDifferences("http://127.0.0.1:65536",
        {"", "", "", "", 0, "", "", "", "http://127.0.0.1:65536"},
        {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1:65536/"});
    checkURLDifferences("http://[0:f::f:f:0:0]:65536",
        {"", "", "", "", 0, "", "", "", "http://[0:f::f:f:0:0]:65536"},
        {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]:65536/"});
    checkRelativeURLDifferences(":foo.com\\", "notspecial://example.org/foo/bar",
        {"notspecial", "", "", "example.org", 0, "/foo/:foo.com\\", "", "", "notspecial://example.org/foo/:foo.com\\"},
        {"notspecial", "", "", "example.org", 0, "/foo/:foo.com/", "", "", "notspecial://example.org/foo/:foo.com/"});
    checkURL("sc://pa", {"sc", "", "", "pa", 0, "", "", "", "sc://pa"});
    checkRelativeURLDifferences("notspecial:\\\\foo.com\\", "http://example.org/foo/bar",
        {"notspecial", "", "", "", 0, "\\\\foo.com\\", "", "", "notspecial:\\\\foo.com\\"},
        {"notspecial", "", "", "foo.com", 0, "/", "", "", "notspecial://foo.com/"});
    checkRelativeURLDifferences("notspecial:\\\\foo.com/", "http://example.org/foo/bar",
        {"notspecial", "", "", "", 0, "\\\\foo.com/", "", "", "notspecial:\\\\foo.com/"},
        {"notspecial", "", "", "foo.com", 0, "/", "", "", "notspecial://foo.com/"});
    checkRelativeURLDifferences("notspecial:\\\\foo.com", "http://example.org/foo/bar",
        {"notspecial", "", "", "", 0, "\\\\foo.com", "", "", "notspecial:\\\\foo.com"},
        {"notspecial", "", "", "foo.com", 0, "", "", "", "notspecial://foo.com"});
    checkURLDifferences("file://notuser:notpassword@test",
        {"", "", "", "", 0, "", "", "", "file://notuser:notpassword@test"},
        {"file", "notuser", "notpassword", "test", 0, "/", "", "", "file://notuser:notpassword@test/"});
    checkURLDifferences("file://notuser:notpassword@test/",
        {"", "", "", "", 0, "", "", "", "file://notuser:notpassword@test/"},
        {"file", "notuser", "notpassword", "test", 0, "/", "", "", "file://notuser:notpassword@test/"});
    checkRelativeURLDifferences("http:/", "about:blank",
        {"", "", "", "", 0, "", "", "", "http:/"},
        {"http", "", "", "", 0, "/", "", "", "http:/"});
    checkRelativeURLDifferences("http:", "about:blank",
        {"http", "", "", "", 0, "", "", "", "http:"},
        {"http", "", "", "", 0, "/", "", "", "http:/"});
    checkRelativeURLDifferences("http:/", "http://host",
        {"", "", "", "", 0, "", "", "", "http:/"},
        {"http", "", "", "", 0, "/", "", "", "http:/"});
    checkURLDifferences("http:/",
        {"", "", "", "", 0, "", "", "", "http:/"},
        {"http", "", "", "", 0, "/", "", "", "http:/"});
    checkURLDifferences("http:",
        {"http", "", "", "", 0, "", "", "", "http:"},
        {"http", "", "", "", 0, "/", "", "", "http:/"});
    checkRelativeURLDifferences("http:/example.com/", "http://example.org/foo/bar",
        {"http", "", "", "example.org", 0, "/example.com/", "", "", "http://example.org/example.com/"},
        {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});

    // This behavior matches Chrome and Firefox, but not WebKit using URL::parse.
    // The behavior of URL::parse is clearly wrong because reparsing file://path would make path the host.
    // The spec is unclear.
    checkURLDifferences("file:path",
        {"file", "", "", "", 0, "/path", "", "", "file:///path"},
        {"file", "", "", "", 0, "path", "", "", "file://path"});
    checkURLDifferences("file:pAtH",
        {"file", "", "", "", 0, "/pAtH", "", "", "file:///pAtH"},
        {"file", "", "", "", 0, "pAtH", "", "", "file://pAtH"});
    checkURLDifferences("file:pAtH/",
        {"file", "", "", "", 0, "/pAtH/", "", "", "file:///pAtH/"},
        {"file", "", "", "", 0, "pAtH/", "", "", "file://pAtH/"});
    checkURLDifferences("http://example.com%A0",
        {"", "", "", "", 0, "", "", "", "http://example.com%A0"},
        {"http", "", "", "example.com%a0", 0, "/", "", "", "http://example.com%a0/"});
    checkURLDifferences("http://%E2%98%83",
        {"http", "", "", "xn--n3h", 0, "/", "", "", "http://xn--n3h/"},
        {"http", "", "", "%e2%98%83", 0, "/", "", "", "http://%e2%98%83/"});
    checkURLDifferences("http://host%73",
        {"http", "", "", "hosts", 0, "/", "", "", "http://hosts/"},
        {"http", "", "", "host%73", 0, "/", "", "", "http://host%73/"});
    checkURLDifferences("http://host%53",
        {"http", "", "", "hosts", 0, "/", "", "", "http://hosts/"},
        {"http", "", "", "host%53", 0, "/", "", "", "http://host%53/"});
    checkURLDifferences("http://%",
        {"", "", "", "", 0, "", "", "", "http://%"},
        {"http", "", "", "%", 0, "/", "", "", "http://%/"});
    checkURLDifferences("http://%7",
        {"", "", "", "", 0, "", "", "", "http://%7"},
        {"http", "", "", "%7", 0, "/", "", "", "http://%7/"});
    checkURLDifferences("http://%7s",
        {"", "", "", "", 0, "", "", "", "http://%7s"},
        {"http", "", "", "%7s", 0, "/", "", "", "http://%7s/"});
    checkURLDifferences("http://%73",
        {"http", "", "", "s", 0, "/", "", "", "http://s/"},
        {"http", "", "", "%73", 0, "/", "", "", "http://%73/"});
    checkURLDifferences("http://abcdefg%",
        {"", "", "", "", 0, "", "", "", "http://abcdefg%"},
        {"http", "", "", "abcdefg%", 0, "/", "", "", "http://abcdefg%/"});
    checkURLDifferences("http://abcd%7Xefg",
        {"", "", "", "", 0, "", "", "", "http://abcd%7Xefg"},
        {"http", "", "", "abcd%7xefg", 0, "/", "", "", "http://abcd%7xefg/"});

    
    // URLParser matches Chrome and the spec, but not URL::parse or Firefox.
    checkURLDifferences(utf16String(u"http://0Xc0.0250.01"),
        {"http", "", "", "192.168.0.1", 0, "/", "", "", "http://192.168.0.1/"},
        {"http", "", "", "0xc0.0250.01", 0, "/", "", "", "http://0xc0.0250.01/"});

    checkURL("http://host/path%2e.%2E", {"http", "", "", "host", 0, "/path%2e.%2E", "", "", "http://host/path%2e.%2E"});

    checkRelativeURLDifferences(utf16String(u"http://foo:💩@example.com/bar"), "http://other.com/",
        {"http", "foo", utf16String(u"💩"), "example.com", 0, "/bar", "", "", "http://foo:%F0%9F%92%A9@example.com/bar"},
        {"", "", "", "", 0, "", "", "", utf16String(u"http://foo:💩@example.com/bar")}, testTabsValueForSurrogatePairs);
    checkRelativeURLDifferences("http://&a:foo(b]c@d:2/", "http://example.org/foo/bar",
        {"http", "&a", "foo(b]c", "d", 2, "/", "", "", "http://&a:foo(b%5Dc@d:2/"},
        {"", "", "", "", 0, "", "", "", "http://&a:foo(b]c@d:2/"});
    checkRelativeURLDifferences("http://`{}:`{}@h/`{}?`{}", "http://doesnotmatter/",
        {"http", "`{}", "`{}", "h", 0, "/%60%7B%7D", "`{}", "", "http://%60%7B%7D:%60%7B%7D@h/%60%7B%7D?`{}"},
        {"", "", "", "", 0, "", "", "", "http://`{}:`{}@h/`{}?`{}"});
    checkURLDifferences("http://[0:f::f::f]",
        {"", "", "", "", 0, "" , "", "", "http://[0:f::f::f]"},
        {"http", "", "", "[0:f::f::f]", 0, "/" , "", "", "http://[0:f::f::f]/"});
    checkURLDifferences("http://123",
        {"http", "", "", "0.0.0.123", 0, "/", "", "", "http://0.0.0.123/"},
        {"http", "", "", "123", 0, "/", "", "", "http://123/"});
    checkURLDifferences("http://123.234/",
        {"http", "", "", "123.0.0.234", 0, "/", "", "", "http://123.0.0.234/"},
        {"http", "", "", "123.234", 0, "/", "", "", "http://123.234/"});
    checkURLDifferences("http://123.234.012",
        {"http", "", "", "123.234.0.10", 0, "/", "", "", "http://123.234.0.10/"},
        {"http", "", "", "123.234.012", 0, "/", "", "", "http://123.234.012/"});
    checkURLDifferences("http://123.234.12",
        {"http", "", "", "123.234.0.12", 0, "/", "", "", "http://123.234.0.12/"},
        {"http", "", "", "123.234.12", 0, "/", "", "", "http://123.234.12/"});
    checkRelativeURLDifferences("file:c:\\foo\\bar.html", "file:///tmp/mock/path",
        {"file", "", "", "", 0, "/c:/foo/bar.html", "", "", "file:///c:/foo/bar.html"},
        {"file", "", "", "", 0, "/tmp/mock/c:/foo/bar.html", "", "", "file:///tmp/mock/c:/foo/bar.html"});
    checkRelativeURLDifferences("  File:c|////foo\\bar.html", "file:///tmp/mock/path",
        {"file", "", "", "", 0, "/c:////foo/bar.html", "", "", "file:///c:////foo/bar.html"},
        {"file", "", "", "", 0, "/tmp/mock/c|////foo/bar.html", "", "", "file:///tmp/mock/c|////foo/bar.html"});
    checkRelativeURLDifferences("  Fil\t\n\te\n\t\n:\t\n\tc\t\n\t|\n\t\n/\t\n\t/\n\t\n//foo\\bar.html", "file:///tmp/mock/path",
        {"file", "", "", "", 0, "/c:////foo/bar.html", "", "", "file:///c:////foo/bar.html"},
        {"file", "", "", "", 0, "/tmp/mock/c|////foo/bar.html", "", "", "file:///tmp/mock/c|////foo/bar.html"});
    checkRelativeURLDifferences("C|/foo/bar", "file:///tmp/mock/path",
        {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"},
        {"file", "", "", "", 0, "/tmp/mock/C|/foo/bar", "", "", "file:///tmp/mock/C|/foo/bar"});
    checkRelativeURLDifferences("/C|/foo/bar", "file:///tmp/mock/path",
        {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"},
        {"file", "", "", "", 0, "/C|/foo/bar", "", "", "file:///C|/foo/bar"});
    checkRelativeURLDifferences("https://@test@test@example:800/", "http://doesnotmatter/",
        {"https", "@test@test", "", "example", 800, "/", "", "", "https://%40test%40test@example:800/"},
        {"", "", "", "", 0, "", "", "", "https://@test@test@example:800/"});
    checkRelativeURLDifferences("https://@test@test@example:800/path@end", "http://doesnotmatter/",
        {"https", "@test@test", "", "example", 800, "/path@end", "", "", "https://%40test%40test@example:800/path@end"},
        {"", "", "", "", 0, "", "", "", "https://@test@test@example:800/path@end"});
    checkURLDifferences("notspecial://@test@test@example:800/path@end",
        {"notspecial", "@test@test", "", "example", 800, "/path@end", "", "", "notspecial://%40test%40test@example:800/path@end"},
        {"", "", "", "", 0, "", "", "", "notspecial://@test@test@example:800/path@end"});
    checkURLDifferences("notspecial://@test@test@example:800\\path@end",
        {"notspecial", "@test@test@example", "800\\path", "end", 0, "", "", "", "notspecial://%40test%40test%40example:800%5Cpath@end"},
        {"", "", "", "", 0, "", "", "", "notspecial://@test@test@example:800\\path@end"});
    checkURLDifferences("http://%48OsT",
        {"http", "", "", "host", 0, "/", "", "", "http://host/"},
        {"http", "", "", "%48ost", 0, "/", "", "", "http://%48ost/"});
    checkURLDifferences("http://h%4FsT",
        {"http", "", "", "host", 0, "/", "", "", "http://host/"},
        {"http", "", "", "h%4fst", 0, "/", "", "", "http://h%4fst/"});
    checkURLDifferences("http://h%4fsT",
        {"http", "", "", "host", 0, "/", "", "", "http://host/"},
        {"http", "", "", "h%4fst", 0, "/", "", "", "http://h%4fst/"});
    checkURLDifferences("http://h%6fsT",
        {"http", "", "", "host", 0, "/", "", "", "http://host/"},
        {"http", "", "", "h%6fst", 0, "/", "", "", "http://h%6fst/"});
    checkURLDifferences("http://host/`",
        {"http", "", "", "host", 0, "/%60", "", "", "http://host/%60"},
        {"http", "", "", "host", 0, "/`", "", "", "http://host/`"});
    checkURLDifferences("http://://",
        {"", "", "", "", 0, "", "", "", "http://://"},
        {"http", "", "", "", 0, "//", "", "", "http://://"});
    checkURLDifferences("http://:123?",
        {"", "", "", "", 0, "", "", "", "http://:123?"},
        {"http", "", "", "", 123, "/", "", "", "http://:123/?"});
    checkURLDifferences("http:/:",
        {"", "", "", "", 0, "", "", "", "http:/:"},
        {"http", "", "", "", 0, "/", "", "", "http://:/"});
    checkURLDifferences("asdf://:",
        {"", "", "", "", 0, "", "", "", "asdf://:"},
        {"asdf", "", "", "", 0, "", "", "", "asdf://:"});
    checkURLDifferences("http://:",
        {"", "", "", "", 0, "", "", "", "http://:"},
        {"http", "", "", "", 0, "/", "", "", "http://:/"});
    checkURLDifferences("http:##foo",
        {"", "", "", "", 0, "", "", "", "http:##foo"},
        {"http", "", "", "", 0, "/", "", "#foo", "http:/##foo"});
    checkURLDifferences("http:??bar",
        {"", "", "", "", 0, "", "", "", "http:??bar"},
        {"http", "", "", "", 0, "/", "?bar", "", "http:/??bar"});
    checkURL("asdf:##foo", {"asdf", "", "", "", 0, "", "", "#foo", "asdf:##foo"});
    checkURL("asdf:??bar", {"asdf", "", "", "", 0, "", "?bar", "", "asdf:??bar"});
    checkRelativeURLDifferences("//C|/foo/bar", "file:///tmp/mock/path",
        {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"},
        {"", "", "", "", 0, "", "", "", "//C|/foo/bar"});
    checkRelativeURLDifferences("//C:/foo/bar", "file:///tmp/mock/path",
        {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"},
        {"file", "", "", "c", 0, "/foo/bar", "", "", "file://c/foo/bar"});
    checkRelativeURLDifferences("//C|?foo/bar", "file:///tmp/mock/path",
        {"file", "", "", "", 0, "/C:/", "foo/bar", "", "file:///C:/?foo/bar"},
        {"", "", "", "", 0, "", "", "", "//C|?foo/bar"});
    checkRelativeURLDifferences("//C|#foo/bar", "file:///tmp/mock/path",
        {"file", "", "", "", 0, "/C:/", "", "foo/bar", "file:///C:/#foo/bar"},
        {"", "", "", "", 0, "", "", "", "//C|#foo/bar"});
    checkURLDifferences("http://0xFFFFFfFF/",
        {"http", "", "", "255.255.255.255", 0, "/", "", "", "http://255.255.255.255/"},
        {"http", "", "", "0xffffffff", 0, "/", "", "", "http://0xffffffff/"});
    checkURLDifferences("http://0000000000000000037777777777/",
        {"http", "", "", "255.255.255.255", 0, "/", "", "", "http://255.255.255.255/"},
        {"http", "", "", "0000000000000000037777777777", 0, "/", "", "", "http://0000000000000000037777777777/"});
    checkURLDifferences("http://4294967295/",
        {"http", "", "", "255.255.255.255", 0, "/", "", "", "http://255.255.255.255/"},
        {"http", "", "", "4294967295", 0, "/", "", "", "http://4294967295/"});
    checkURLDifferences("http://256/",
        {"http", "", "", "0.0.1.0", 0, "/", "", "", "http://0.0.1.0/"},
        {"http", "", "", "256", 0, "/", "", "", "http://256/"});
    checkURLDifferences("http://256./",
        {"http", "", "", "0.0.1.0", 0, "/", "", "", "http://0.0.1.0/"},
        {"http", "", "", "256.", 0, "/", "", "", "http://256./"});
    checkURLDifferences("http://123.256/",
        {"http", "", "", "123.0.1.0", 0, "/", "", "", "http://123.0.1.0/"},
        {"http", "", "", "123.256", 0, "/", "", "", "http://123.256/"});
    checkURLDifferences("http://127.%.0.1/",
        {"", "", "", "", 0, "", "", "", "http://127.%.0.1/"},
        {"http", "", "", "127.%.0.1", 0, "/", "", "", "http://127.%.0.1/"});
    checkURLDifferences("http://[1:2:3:4:5:6:7:8:]/",
        {"", "", "", "", 0, "", "", "", "http://[1:2:3:4:5:6:7:8:]/"},
        {"http", "", "", "[1:2:3:4:5:6:7:8:]", 0, "/", "", "", "http://[1:2:3:4:5:6:7:8:]/"});
    checkURLDifferences("http://[:2:3:4:5:6:7:8:]/",
        {"", "", "", "", 0, "", "", "", "http://[:2:3:4:5:6:7:8:]/"},
        {"http", "", "", "[:2:3:4:5:6:7:8:]", 0, "/", "", "", "http://[:2:3:4:5:6:7:8:]/"});
    checkURLDifferences("http://[1:2:3:4:5:6:7::]/",
        {"http", "", "", "[1:2:3:4:5:6:7:0]", 0, "/", "", "", "http://[1:2:3:4:5:6:7:0]/"},
        {"http", "", "", "[1:2:3:4:5:6:7::]", 0, "/", "", "", "http://[1:2:3:4:5:6:7::]/"});
    checkURLDifferences("http://[1:2:3:4:5:6:7:::]/",
        {"", "", "", "", 0, "", "", "", "http://[1:2:3:4:5:6:7:::]/"},
        {"http", "", "", "[1:2:3:4:5:6:7:::]", 0, "/", "", "", "http://[1:2:3:4:5:6:7:::]/"});
    checkURLDifferences("http://127.0.0.1~/",
        {"http", "", "", "127.0.0.1~", 0, "/", "", "", "http://127.0.0.1~/"},
        {"", "", "", "", 0, "", "", "", "http://127.0.0.1~/"});
    checkURLDifferences("http://127.0.1~/",
        {"http", "", "", "127.0.1~", 0, "/", "", "", "http://127.0.1~/"},
        {"", "", "", "", 0, "", "", "", "http://127.0.1~/"});
    checkURLDifferences("http://127.0.1./",
        {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"},
        {"http", "", "", "127.0.1.", 0, "/", "", "", "http://127.0.1./"});
    checkURLDifferences("http://127.0.1.~/",
        {"http", "", "", "127.0.1.~", 0, "/", "", "", "http://127.0.1.~/"},
        {"", "", "", "", 0, "", "", "", "http://127.0.1.~/"});
    checkURLDifferences("http://127.0.1.~",
        {"http", "", "", "127.0.1.~", 0, "/", "", "", "http://127.0.1.~/"},
        {"", "", "", "", 0, "", "", "", "http://127.0.1.~"});
    checkRelativeURLDifferences("http://f:000/c", "http://example.org/foo/bar",
        {"http", "", "", "f", 0, "/c", "", "", "http://f:0/c"},
        {"http", "", "", "f", 0, "/c", "", "", "http://f:000/c"});
    checkRelativeURLDifferences("http://f:010/c", "http://example.org/foo/bar",
        {"http", "", "", "f", 10, "/c", "", "", "http://f:10/c"},
        {"http", "", "", "f", 10, "/c", "", "", "http://f:010/c"});
    checkURL("notspecial://HoSt", {"notspecial", "", "", "HoSt", 0, "", "", "", "notspecial://HoSt"});
    checkURL("notspecial://H%6FSt", {"notspecial", "", "", "H%6FSt", 0, "", "", "", "notspecial://H%6FSt"});
    checkURL("notspecial://H%4fSt", {"notspecial", "", "", "H%4fSt", 0, "", "", "", "notspecial://H%4fSt"});
    checkURLDifferences(utf16String(u"notspecial://H😍ßt"),
        {"notspecial", "", "", "H%F0%9F%98%8D%C3%9Ft", 0, "", "", "", "notspecial://H%F0%9F%98%8D%C3%9Ft"},
        {"notspecial", "", "", "xn--hsst-qc83c", 0, "", "", "", "notspecial://xn--hsst-qc83c"}, testTabsValueForSurrogatePairs);
    checkURLDifferences("http://[ffff:aaaa:cccc:eeee:bbbb:dddd:255.255.255.255]/",
        {"http", "", "", "[ffff:aaaa:cccc:eeee:bbbb:dddd:ffff:ffff]", 0, "/", "", "", "http://[ffff:aaaa:cccc:eeee:bbbb:dddd:ffff:ffff]/"},
        {"http", "", "", "[ffff:aaaa:cccc:eeee:bbbb:dddd:255.255.255.255]", 0, "/", "", "", "http://[ffff:aaaa:cccc:eeee:bbbb:dddd:255.255.255.255]/"}, TestTabs::No);
    checkURLDifferences("http://[::123.234.12.210]/",
        {"http", "", "", "[::7bea:cd2]", 0, "/", "", "", "http://[::7bea:cd2]/"},
        {"http", "", "", "[::123.234.12.210]", 0, "/", "", "", "http://[::123.234.12.210]/"});
    checkURLDifferences("http://[::a:255.255.255.255]/",
        {"http", "", "", "[::a:ffff:ffff]", 0, "/", "", "", "http://[::a:ffff:ffff]/"},
        {"http", "", "", "[::a:255.255.255.255]", 0, "/", "", "", "http://[::a:255.255.255.255]/"});
    checkURLDifferences("http://[::0.00.255.255]/",
        {"", "", "", "", 0, "", "", "", "http://[::0.00.255.255]/"},
        {"http", "", "", "[::0.00.255.255]", 0, "/", "", "", "http://[::0.00.255.255]/"});
    checkURLDifferences("http://[::0.0.255.255]/",
        {"http", "", "", "[::ffff]", 0, "/", "", "", "http://[::ffff]/"},
        {"http", "", "", "[::0.0.255.255]", 0, "/", "", "", "http://[::0.0.255.255]/"});
    checkURLDifferences("http://[::0:1.0.255.255]/",
        {"http", "", "", "[::100:ffff]", 0, "/", "", "", "http://[::100:ffff]/"},
        {"http", "", "", "[::0:1.0.255.255]", 0, "/", "", "", "http://[::0:1.0.255.255]/"});
    checkURLDifferences("http://[::A:1.0.255.255]/",
        {"http", "", "", "[::a:100:ffff]", 0, "/", "", "", "http://[::a:100:ffff]/"},
        {"http", "", "", "[::a:1.0.255.255]", 0, "/", "", "", "http://[::a:1.0.255.255]/"});
    checkURLDifferences("http://[:127.0.0.1]",
        {"", "", "", "", 0, "", "", "", "http://[:127.0.0.1]"},
        {"http", "", "", "[:127.0.0.1]", 0, "/", "", "", "http://[:127.0.0.1]/"});
    checkURLDifferences("http://[127.0.0.1]",
        {"", "", "", "", 0, "", "", "", "http://[127.0.0.1]"},
        {"http", "", "", "[127.0.0.1]", 0, "/", "", "", "http://[127.0.0.1]/"});
    checkURLDifferences("http://[a:b:c:d:e:f:127.0.0.1]",
        {"http", "", "", "[a:b:c:d:e:f:7f00:1]", 0, "/", "", "", "http://[a:b:c:d:e:f:7f00:1]/"},
        {"http", "", "", "[a:b:c:d:e:f:127.0.0.1]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.0.1]/"});
    checkURLDifferences("http://[a:b:c:d:e:f:127.0.0.101]",
        {"http", "", "", "[a:b:c:d:e:f:7f00:65]", 0, "/", "", "", "http://[a:b:c:d:e:f:7f00:65]/"},
        {"http", "", "", "[a:b:c:d:e:f:127.0.0.101]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.0.101]/"});
    checkURLDifferences("http://[::a:b:c:d:e:f:127.0.0.1]",
        {"", "", "", "", 0, "", "", "", "http://[::a:b:c:d:e:f:127.0.0.1]"},
        {"http", "", "", "[::a:b:c:d:e:f:127.0.0.1]", 0, "/", "", "", "http://[::a:b:c:d:e:f:127.0.0.1]/"});
    checkURLDifferences("http://[a:b::c:d:e:f:127.0.0.1]",
        {"", "", "", "", 0, "", "", "", "http://[a:b::c:d:e:f:127.0.0.1]"},
        {"http", "", "", "[a:b::c:d:e:f:127.0.0.1]", 0, "/", "", "", "http://[a:b::c:d:e:f:127.0.0.1]/"});
    checkURLDifferences("http://[a:b:c:d:e:127.0.0.1]",
        {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:127.0.0.1]"},
        {"http", "", "", "[a:b:c:d:e:127.0.0.1]", 0, "/", "", "", "http://[a:b:c:d:e:127.0.0.1]/"});
    checkURLDifferences("http://[a:b:c:d:e:f:127.0.0.0.1]",
        {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0.0.0.1]"},
        {"http", "", "", "[a:b:c:d:e:f:127.0.0.0.1]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.0.0.1]/"});
    checkURLDifferences("http://[a:b:c:d:e:f:127.0.1]",
        {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0.1]"},
        {"http", "", "", "[a:b:c:d:e:f:127.0.1]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.1]/"});
    checkURLDifferences("http://[a:b:c:d:e:f:127.0.0.011]", // Chrome treats this as octal, Firefox and the spec fail
        {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0.0.011]"},
        {"http", "", "", "[a:b:c:d:e:f:127.0.0.011]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.0.011]/"});
    checkURLDifferences("http://[a:b:c:d:e:f:127.0.00.1]",
        {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0.00.1]"},
        {"http", "", "", "[a:b:c:d:e:f:127.0.00.1]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.00.1]/"});
    checkURLDifferences("http://[a:b:c:d:e:f:127.0.0.1.]",
        {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0.0.1.]"},
        {"http", "", "", "[a:b:c:d:e:f:127.0.0.1.]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.0.1.]/"});
    checkURLDifferences("http://[a:b:c:d:e:f:127.0..0.1]",
        {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0..0.1]"},
        {"http", "", "", "[a:b:c:d:e:f:127.0..0.1]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0..0.1]/"});
    checkURLDifferences("http://[a:b:c:d:e:f::127.0.0.1]",
        {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f::127.0.0.1]"},
        {"http", "", "", "[a:b:c:d:e:f::127.0.0.1]", 0, "/", "", "", "http://[a:b:c:d:e:f::127.0.0.1]/"});
    checkURLDifferences("http://[a:b:c:d:e::127.0.0.1]",
        {"http", "", "", "[a:b:c:d:e:0:7f00:1]", 0, "/", "", "", "http://[a:b:c:d:e:0:7f00:1]/"},
        {"http", "", "", "[a:b:c:d:e::127.0.0.1]", 0, "/", "", "", "http://[a:b:c:d:e::127.0.0.1]/"});
    checkURLDifferences("http://[a:b:c:d::e:127.0.0.1]",
        {"http", "", "", "[a:b:c:d:0:e:7f00:1]", 0, "/", "", "", "http://[a:b:c:d:0:e:7f00:1]/"},
        {"http", "", "", "[a:b:c:d::e:127.0.0.1]", 0, "/", "", "", "http://[a:b:c:d::e:127.0.0.1]/"});
    checkURLDifferences("http://[a:b:c:d:e:f::127.0.0.]",
        {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f::127.0.0.]"},
        {"http", "", "", "[a:b:c:d:e:f::127.0.0.]", 0, "/", "", "", "http://[a:b:c:d:e:f::127.0.0.]/"});
    checkURLDifferences("http://[a:b:c:d:e:f::127.0.0.256]",
        {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f::127.0.0.256]"},
        {"http", "", "", "[a:b:c:d:e:f::127.0.0.256]", 0, "/", "", "", "http://[a:b:c:d:e:f::127.0.0.256]/"});
    checkURLDifferences("http://123456", {"http", "", "", "0.1.226.64", 0, "/", "", "", "http://0.1.226.64/"}, {"http", "", "", "123456", 0, "/", "", "", "http://123456/"});
    checkURL("asdf://123456", {"asdf", "", "", "123456", 0, "", "", "", "asdf://123456"});
    checkURLDifferences("http://[0:0:0:0:a:b:c:d]",
        {"http", "", "", "[::a:b:c:d]", 0, "/", "", "", "http://[::a:b:c:d]/"},
        {"http", "", "", "[0:0:0:0:a:b:c:d]", 0, "/", "", "", "http://[0:0:0:0:a:b:c:d]/"});
    checkURLDifferences("asdf://[0:0:0:0:a:b:c:d]",
        {"asdf", "", "", "[::a:b:c:d]", 0, "", "", "", "asdf://[::a:b:c:d]"},
        {"asdf", "", "", "[0:0:0:0:a:b:c:d]", 0, "", "", "", "asdf://[0:0:0:0:a:b:c:d]"}, TestTabs::No);
    shouldFail("a://%:a");
    checkURL("a://%:/", {"a", "", "", "%", 0, "/", "", "", "a://%/"});
    checkURL("a://%:", {"a", "", "", "%", 0, "", "", "", "a://%"});
    checkURL("a://%:1/", {"a", "", "", "%", 1, "/", "", "", "a://%:1/"});
    checkURLDifferences("http://%:",
        {"", "", "", "", 0, "", "", "", "http://%:"},
        {"http", "", "", "%", 0, "/", "", "", "http://%/"});
    checkURL("a://123456", {"a", "", "", "123456", 0, "", "", "", "a://123456"});
    checkURL("a://123456:7", {"a", "", "", "123456", 7, "", "", "", "a://123456:7"});
    checkURL("a://123456:7/", {"a", "", "", "123456", 7, "/", "", "", "a://123456:7/"});
    checkURL("a://A", {"a", "", "", "A", 0, "", "", "", "a://A"});
    checkURL("a://A:2", {"a", "", "", "A", 2, "", "", "", "a://A:2"});
    checkURL("a://A:2/", {"a", "", "", "A", 2, "/", "", "", "a://A:2/"});
    checkURLDifferences(u8"asd://ß",
        {"asd", "", "", "%C3%83%C2%9F", 0, "", "", "", "asd://%C3%83%C2%9F"},
        {"", "", "", "", 0, "", "", "", "about:blank"}, TestTabs::No);
    checkURLDifferences(u8"asd://ß:4",
        {"asd", "", "", "%C3%83%C2%9F", 4, "", "", "", "asd://%C3%83%C2%9F:4"},
        {"", "", "", "", 0, "", "", "", "about:blank"}, TestTabs::No);
    checkURLDifferences(u8"asd://ß:4/",
        {"asd", "", "", "%C3%83%C2%9F", 4, "/", "", "", "asd://%C3%83%C2%9F:4/"},
        {"", "", "", "", 0, "", "", "", "about:blank"}, TestTabs::No);
    checkURLDifferences("a://[A::b]:4",
        {"a", "", "", "[a::b]", 4, "", "", "", "a://[a::b]:4"},
        {"a", "", "", "[A::b]", 4, "", "", "", "a://[A::b]:4"});
    shouldFail("http://[~]");
    shouldFail("a://[~]");
    checkRelativeURLDifferences("a://b", "//[aBc]",
        {"a", "", "", "b", 0, "", "", "", "a://b"},
        {"", "", "", "", 0, "", "", "", "a://b"});
    checkURL(utf16String(u"http://öbb.at"), {"http", "", "", "xn--bb-eka.at", 0, "/", "", "", "http://xn--bb-eka.at/"});
    checkURL(utf16String(u"http://ÖBB.at"), {"http", "", "", "xn--bb-eka.at", 0, "/", "", "", "http://xn--bb-eka.at/"});
    checkURL(utf16String(u"http://√.com"), {"http", "", "", "xn--19g.com", 0, "/", "", "", "http://xn--19g.com/"});
    checkURLDifferences(utf16String(u"http://faß.de"),
        {"http", "", "", "xn--fa-hia.de", 0, "/", "", "", "http://xn--fa-hia.de/"},
        {"http", "", "", "fass.de", 0, "/", "", "", "http://fass.de/"});
    checkURL(utf16String(u"http://ԛәлп.com"), {"http", "", "", "xn--k1ai47bhi.com", 0, "/", "", "", "http://xn--k1ai47bhi.com/"});
    checkURLDifferences(utf16String(u"http://Ⱥbby.com"),
        {"http", "", "", "xn--bby-iy0b.com", 0, "/", "", "", "http://xn--bby-iy0b.com/"},
        {"http", "", "", "xn--bby-spb.com", 0, "/", "", "", "http://xn--bby-spb.com/"});
    checkURLDifferences(utf16String(u"http://\u2132"),
        {"", "", "", "", 0, "", "", "", utf16String(u"http://Ⅎ")},
        {"http", "", "", "xn--f3g", 0, "/", "", "", "http://xn--f3g/"});
    checkURLDifferences(utf16String(u"http://\u05D9\u05B4\u05D5\u05D0\u05B8/"),
        {"http", "", "", "xn--cdbi5etas", 0, "/", "", "", "http://xn--cdbi5etas/"},
        {"", "", "", "", 0, "", "", "", "about:blank"}, TestTabs::No);
    checkURLDifferences(utf16String(u"http://bidirectional\u0786\u07AE\u0782\u07B0\u0795\u07A9\u0793\u07A6\u0783\u07AA/"),
        {"", "", "", "", 0, "", "", "", utf16String(u"http://bidirectionalކޮންޕީޓަރު/")},
        {"", "", "", "", 0, "", "", "", "about:blank"}, TestTabs::No);
    checkURLDifferences(utf16String(u"http://contextj\u200D"),
        {"", "", "", "", 0, "", "", "", utf16String(u"http://contextj\u200D")},
        {"http", "", "", "contextj", 0, "/", "", "", "http://contextj/"});
    checkURL(utf16String(u"http://contexto\u30FB"), {"http", "", "", "xn--contexto-wg5g", 0, "/", "", "", "http://xn--contexto-wg5g/"});
    checkURLDifferences(utf16String(u"http://\u321D\u321E/"),
        {"http", "", "", "xn--()()-bs0sc174agx4b", 0, "/", "", "", "http://xn--()()-bs0sc174agx4b/"},
        {"http", "", "", "xn--5mkc", 0, "/", "", "", "http://xn--5mkc/"});
}

TEST_F(URLParserTest, DefaultPort)
{
    checkURL("FtP://host:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("ftp://host:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("f\ttp://host:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("f\ttp://host\t:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("f\ttp://host:\t21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("f\ttp://host:2\t1/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("f\ttp://host:21\t/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("ftp://host\t:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("ftp://host:\t21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("ftp://host:2\t1/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("ftp://host:21\t/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
    checkURL("ftp://host:22/", {"ftp", "", "", "host", 22, "/", "", "", "ftp://host:22/"});
    checkURLDifferences("ftp://host:21",
        {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"},
        {"ftp", "", "", "host", 0, "", "", "", "ftp://host"});
    checkURLDifferences("ftp://host:22",
        {"ftp", "", "", "host", 22, "/", "", "", "ftp://host:22/"},
        {"ftp", "", "", "host", 22, "", "", "", "ftp://host:22"});
    
    checkURL("gOpHeR://host:70/", {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"});
    checkURL("gopher://host:70/", {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"});
    checkURL("gopher://host:71/", {"gopher", "", "", "host", 71, "/", "", "", "gopher://host:71/"});
    // Spec, Chrome, Firefox, and URLParser have "/", URL::parse does not.
    // Spec, Chrome, URLParser, URL::parse recognize gopher default port, Firefox does not.
    checkURLDifferences("gopher://host:70",
        {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"},
        {"gopher", "", "", "host", 0, "", "", "", "gopher://host"});
    checkURLDifferences("gopher://host:71",
        {"gopher", "", "", "host", 71, "/", "", "", "gopher://host:71/"},
        {"gopher", "", "", "host", 71, "", "", "", "gopher://host:71"});
    
    checkURL("hTtP://host:80", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkURL("http://host:80", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkURL("http://host:80/", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    checkURL("http://host:81", {"http", "", "", "host", 81, "/", "", "", "http://host:81/"});
    checkURL("http://host:81/", {"http", "", "", "host", 81, "/", "", "", "http://host:81/"});
    
    checkURL("hTtPs://host:443", {"https", "", "", "host", 0, "/", "", "", "https://host/"});
    checkURL("https://host:443", {"https", "", "", "host", 0, "/", "", "", "https://host/"});
    checkURL("https://host:443/", {"https", "", "", "host", 0, "/", "", "", "https://host/"});
    checkURL("https://host:444", {"https", "", "", "host", 444, "/", "", "", "https://host:444/"});
    checkURL("https://host:444/", {"https", "", "", "host", 444, "/", "", "", "https://host:444/"});
    
    checkURL("wS://host:80/", {"ws", "", "", "host", 0, "/", "", "", "ws://host/"});
    checkURL("ws://host:80/", {"ws", "", "", "host", 0, "/", "", "", "ws://host/"});
    checkURL("ws://host:81/", {"ws", "", "", "host", 81, "/", "", "", "ws://host:81/"});
    // URLParser matches Chrome and Firefox, but not URL::parse
    checkURLDifferences("ws://host:80",
        {"ws", "", "", "host", 0, "/", "", "", "ws://host/"},
        {"ws", "", "", "host", 0, "", "", "", "ws://host"});
    checkURLDifferences("ws://host:81",
        {"ws", "", "", "host", 81, "/", "", "", "ws://host:81/"},
        {"ws", "", "", "host", 81, "", "", "", "ws://host:81"});
    
    checkURL("WsS://host:443/", {"wss", "", "", "host", 0, "/", "", "", "wss://host/"});
    checkURL("wss://host:443/", {"wss", "", "", "host", 0, "/", "", "", "wss://host/"});
    checkURL("wss://host:444/", {"wss", "", "", "host", 444, "/", "", "", "wss://host:444/"});
    // URLParser matches Chrome and Firefox, but not URL::parse
    checkURLDifferences("wss://host:443",
        {"wss", "", "", "host", 0, "/", "", "", "wss://host/"},
        {"wss", "", "", "host", 0, "", "", "", "wss://host"});
    checkURLDifferences("wss://host:444",
        {"wss", "", "", "host", 444, "/", "", "", "wss://host:444/"},
        {"wss", "", "", "host", 444, "", "", "", "wss://host:444"});

    checkURL("fTpS://host:990/", {"ftps", "", "", "host", 990, "/", "", "", "ftps://host:990/"});
    checkURL("ftps://host:990/", {"ftps", "", "", "host", 990, "/", "", "", "ftps://host:990/"});
    checkURL("ftps://host:991/", {"ftps", "", "", "host", 991, "/", "", "", "ftps://host:991/"});
    checkURL("ftps://host:990", {"ftps", "", "", "host", 990, "", "", "", "ftps://host:990"});
    checkURL("ftps://host:991", {"ftps", "", "", "host", 991, "", "", "", "ftps://host:991"});

    checkURL("uNkNoWn://host:80/", {"unknown", "", "", "host", 80, "/", "", "", "unknown://host:80/"});
    checkURL("unknown://host:80/", {"unknown", "", "", "host", 80, "/", "", "", "unknown://host:80/"});
    checkURL("unknown://host:81/", {"unknown", "", "", "host", 81, "/", "", "", "unknown://host:81/"});
    checkURL("unknown://host:80", {"unknown", "", "", "host", 80, "", "", "", "unknown://host:80"});
    checkURL("unknown://host:81", {"unknown", "", "", "host", 81, "", "", "", "unknown://host:81"});

    checkURL("file://host:0", {"file", "", "", "host", 0, "/", "", "", "file://host:0/"});
    checkURL("file://host:80", {"file", "", "", "host", 80, "/", "", "", "file://host:80/"});
    checkURL("file://host:80/path", {"file", "", "", "host", 80, "/path", "", "", "file://host:80/path"});
    checkURLDifferences("file://:80/path",
        {"", "", "", "", 0, "", "", "", "file://:80/path"},
        {"file", "", "", "", 80, "/path", "", "", "file://:80/path"});
    checkURLDifferences("file://:0/path",
        {"", "", "", "", 0, "", "", "", "file://:0/path"},
        {"file", "", "", "", 0, "/path", "", "", "file://:0/path"});
}

TEST_F(URLParserTest, ParserFailures)
{
    shouldFail("    ");
    shouldFail("  \a  ");
    shouldFail("");
    shouldFail(String());
    shouldFail("", "about:blank");
    shouldFail(String(), "about:blank");
    shouldFail("http://127.0.0.1:abc");
    shouldFail("http://host:abc");
    shouldFail("http://:abc");
    shouldFail("http://a:@", "about:blank");
    shouldFail("http://:b@", "about:blank");
    shouldFail("http://:@", "about:blank");
    shouldFail("http://a:@");
    shouldFail("http://:b@");
    shouldFail("http://@");
    shouldFail("http://[0:f::f:f:0:0]:abc");
    shouldFail("../i", "sc:sd");
    shouldFail("../i", "sc:sd/sd");
    shouldFail("/i", "sc:sd");
    shouldFail("/i", "sc:sd/sd");
    shouldFail("?i", "sc:sd");
    shouldFail("?i", "sc:sd/sd");
    shouldFail("http://example example.com", "http://other.com/");
    shouldFail("http://[www.example.com]/", "about:blank");
    shouldFail("http://192.168.0.1 hello", "http://other.com/");
    shouldFail("http://[example.com]", "http://other.com/");
    shouldFail("i", "sc:sd");
    shouldFail("i", "sc:sd/sd");
    shouldFail("i");
    shouldFail("asdf");
    shouldFail("~");
    shouldFail("%");
    shouldFail("//%");
    shouldFail("~", "about:blank");
    shouldFail("~~~");
    shouldFail("://:0/");
    shouldFail("://:0/", "");
    shouldFail("://:0/", "about:blank");
    shouldFail("about~");
    shouldFail("//C:asdf/foo/bar", "file:///tmp/mock/path");
    shouldFail("http://[1234::ab#]");
    shouldFail("http://[1234::ab/]");
    shouldFail("http://[1234::ab?]");
    shouldFail("http://[1234::ab@]");
    shouldFail("http://[1234::ab~]");
    shouldFail("http://[2001::1");
    shouldFail("http://4:b\xE1");
    shouldFail("http://[1:2:3:4:5:6:7:8~]/");
    shouldFail("http://[a:b:c:d:e:f:g:127.0.0.1]");
    shouldFail("http://[a:b:c:d:e:f:g:h:127.0.0.1]");
    shouldFail("http://[a:b:c:d:e:f:127.0.0.0x11]"); // Chrome treats this as hex, Firefox and the spec fail
    shouldFail("http://[a:b:c:d:e:f:127.0.-0.1]");
    shouldFail("asdf://space In\aHost");
    shouldFail("asdf://[0:0:0:0:a:b:c:d");
}

// These are in the spec but not in the web platform tests.
TEST_F(URLParserTest, AdditionalTests)
{
    checkURL("about:\a\aabc", {"about", "", "", "", 0, "%07%07abc", "", "", "about:%07%07abc"});
    checkURL("notspecial:\t\t\n\t", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
    checkURL("notspecial\t\t\n\t:\t\t\n\t/\t\t\n\t/\t\t\n\thost", {"notspecial", "", "", "host", 0, "", "", "", "notspecial://host"});
    checkRelativeURL("http:", "http://example.org/foo/bar?query#fragment", {"http", "", "", "example.org", 0, "/foo/bar", "query", "", "http://example.org/foo/bar?query"});
    checkRelativeURLDifferences("ws:", "http://example.org/foo/bar",
        {"ws", "", "", "", 0, "", "", "", "ws:"},
        {"ws", "", "", "", 0, "s:", "", "", "ws:s:"});
    checkRelativeURL("notspecial:", "http://example.org/foo/bar", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});

    const wchar_t surrogateBegin = 0xD800;
    const wchar_t validSurrogateEnd = 0xDD55;
    const wchar_t invalidSurrogateEnd = 'A';
    checkURL(utf16String<12>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, validSurrogateEnd, '\0'}),
        {"http", "", "", "w", 0, "/%F0%90%85%95", "", "", "http://w/%F0%90%85%95"}, testTabsValueForSurrogatePairs);

    // URLParser matches Chrome and Firefox but not URL::parse.
    checkURLDifferences(utf16String<12>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, invalidSurrogateEnd}),
        {"http", "", "", "w", 0, "/%EF%BF%BDA", "", "", "http://w/%EF%BF%BDA"},
        {"http", "", "", "w", 0, "/%ED%A0%80A", "", "", "http://w/%ED%A0%80A"});
    checkURLDifferences(utf16String<13>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, invalidSurrogateEnd, '\0'}),
        {"http", "", "", "w", 0, "/", "%EF%BF%BDA", "", "http://w/?%EF%BF%BDA"},
        {"http", "", "", "w", 0, "/", "%ED%A0%80A", "", "http://w/?%ED%A0%80A"});
    checkURLDifferences(utf16String<11>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, '\0'}),
        {"http", "", "", "w", 0, "/%EF%BF%BD", "", "", "http://w/%EF%BF%BD"},
        {"http", "", "", "w", 0, "/%ED%A0%80", "", "", "http://w/%ED%A0%80"});
    checkURLDifferences(utf16String<12>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, '\0'}),
        {"http", "", "", "w", 0, "/", "%EF%BF%BD", "", "http://w/?%EF%BF%BD"},
        {"http", "", "", "w", 0, "/", "%ED%A0%80", "", "http://w/?%ED%A0%80"});
    checkURLDifferences(utf16String<13>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, ' ', '\0'}),
        {"http", "", "", "w", 0, "/", "%EF%BF%BD", "", "http://w/?%EF%BF%BD"},
        {"http", "", "", "w", 0, "/", "%ED%A0%80", "", "http://w/?%ED%A0%80"});
    
    // FIXME: Write more invalid surrogate pair tests based on feedback from https://bugs.webkit.org/show_bug.cgi?id=162105
}

TEST_F(URLParserTest, QueryEncoding)
{
    checkURL(utf16String(u"http://host?ß😍#ß😍"), UTF8Encoding(), {"http", "", "", "host", 0, "/", "%C3%9F%F0%9F%98%8D", "%C3%9F%F0%9F%98%8D", utf16String(u"http://host/?%C3%9F%F0%9F%98%8D#%C3%9F%F0%9F%98%8D")}, testTabsValueForSurrogatePairs);

    TextEncoding latin1(String("latin1"));
    checkURL("http://host/?query with%20spaces", latin1, {"http", "", "", "host", 0, "/", "query%20with%20spaces", "", "http://host/?query%20with%20spaces"});
    checkURL("http://host/?query", latin1, {"http", "", "", "host", 0, "/", "query", "", "http://host/?query"});
    checkURL("http://host/?\tquery", latin1, {"http", "", "", "host", 0, "/", "query", "", "http://host/?query"});
    checkURL("http://host/?q\tuery", latin1, {"http", "", "", "host", 0, "/", "query", "", "http://host/?query"});
    checkURL("http://host/?query with SpAcEs#fragment", latin1, {"http", "", "", "host", 0, "/", "query%20with%20SpAcEs", "fragment", "http://host/?query%20with%20SpAcEs#fragment"});
    checkURL("http://host/?que\rry\t\r\n#fragment", latin1, {"http", "", "", "host", 0, "/", "query", "fragment", "http://host/?query#fragment"});

    TextEncoding unrecognized(String("unrecognized invalid encoding name"));
    checkURL("http://host/?query", unrecognized, {"http", "", "", "host", 0, "/", "", "", "http://host/?"});
    checkURL("http://host/?", unrecognized, {"http", "", "", "host", 0, "/", "", "", "http://host/?"});

    TextEncoding iso88591(String("ISO-8859-1"));
    String withUmlauts = utf16String<4>({0xDC, 0x430, 0x451, '\0'});
    checkURL(makeString("ws://host/path?", withUmlauts), iso88591, {"ws", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "", "ws://host/path?%C3%9C%D0%B0%D1%91"});
    checkURL(makeString("wss://host/path?", withUmlauts), iso88591, {"wss", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "", "wss://host/path?%C3%9C%D0%B0%D1%91"});
    checkURL(makeString("asdf://host/path?", withUmlauts), iso88591, {"asdf", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "", "asdf://host/path?%C3%9C%D0%B0%D1%91"});
    checkURL(makeString("https://host/path?", withUmlauts), iso88591, {"https", "", "", "host", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "", "https://host/path?%DC%26%231072%3B%26%231105%3B"});
    checkURL(makeString("gopher://host/path?", withUmlauts), iso88591, {"gopher", "", "", "host", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "", "gopher://host/path?%DC%26%231072%3B%26%231105%3B"});
    checkURL(makeString("/path?", withUmlauts, "#fragment"), "ws://example.com/", iso88591, {"ws", "", "", "example.com", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "ws://example.com/path?%C3%9C%D0%B0%D1%91#fragment"});
    checkURL(makeString("/path?", withUmlauts, "#fragment"), "wss://example.com/", iso88591, {"wss", "", "", "example.com", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "wss://example.com/path?%C3%9C%D0%B0%D1%91#fragment"});
    checkURL(makeString("/path?", withUmlauts, "#fragment"), "asdf://example.com/", iso88591, {"asdf", "", "", "example.com", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "asdf://example.com/path?%C3%9C%D0%B0%D1%91#fragment"});
    checkURL(makeString("/path?", withUmlauts, "#fragment"), "https://example.com/", iso88591, {"https", "", "", "example.com", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "fragment", "https://example.com/path?%DC%26%231072%3B%26%231105%3B#fragment"});
    checkURL(makeString("/path?", withUmlauts, "#fragment"), "gopher://example.com/", iso88591, {"gopher", "", "", "example.com", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "fragment", "gopher://example.com/path?%DC%26%231072%3B%26%231105%3B#fragment"});
    checkURL(makeString("gopher://host/path?", withUmlauts, "#fragment"), "asdf://example.com/?doesntmatter", iso88591, {"gopher", "", "", "host", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "fragment", "gopher://host/path?%DC%26%231072%3B%26%231105%3B#fragment"});
    checkURL(makeString("asdf://host/path?", withUmlauts, "#fragment"), "http://example.com/?doesntmatter", iso88591, {"asdf", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "asdf://host/path?%C3%9C%D0%B0%D1%91#fragment"});

    checkURL("http://host/?query=foo'bar", UTF8Encoding(), {"http", "", "", "host", 0, "/", "query=foo%27bar", "", "http://host/?query=foo%27bar"});
    // FIXME: Add more tests with other encodings and things like non-ascii characters, emoji and unmatched surrogate pairs.
}

} // namespace TestWebKitAPI