Reduce allocations in URLParser
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebCore / URLParser.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include <WebCore/URLParser.h>
28 #include <wtf/MainThread.h>
29 #include <wtf/text/StringBuilder.h>
30
31 using namespace WebCore;
32
33 namespace TestWebKitAPI {
34
35 class URLParserTest : public testing::Test {
36 public:
37     void SetUp() final {
38         WTF::initializeMainThread();
39     }
40 };
41
42 struct ExpectedParts {
43     String protocol;
44     String user;
45     String password;
46     String host;
47     unsigned short port;
48     String path;
49     String query;
50     String fragment;
51     String string;
52 };
53
54 static bool eq(const String& s1, const String& s2)
55 {
56     EXPECT_STREQ(s1.utf8().data(), s2.utf8().data());
57     return s1.utf8() == s2.utf8();
58 }
59
60 static void checkURL(const String& urlString, const ExpectedParts& parts)
61 {
62     URLParser parser;
63     auto url = parser.parse(urlString);
64     EXPECT_TRUE(eq(parts.protocol, url.protocol()));
65     EXPECT_TRUE(eq(parts.user, url.user()));
66     EXPECT_TRUE(eq(parts.password, url.pass()));
67     EXPECT_TRUE(eq(parts.host, url.host()));
68     EXPECT_EQ(parts.port, url.port());
69     EXPECT_TRUE(eq(parts.path, url.path()));
70     EXPECT_TRUE(eq(parts.query, url.query()));
71     EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
72     EXPECT_TRUE(eq(parts.string, url.string()));
73     
74     auto oldURL = URL(URL(), urlString);
75     EXPECT_TRUE(eq(parts.protocol, oldURL.protocol()));
76     EXPECT_TRUE(eq(parts.user, oldURL.user()));
77     EXPECT_TRUE(eq(parts.password, oldURL.pass()));
78     EXPECT_TRUE(eq(parts.host, oldURL.host()));
79     EXPECT_EQ(parts.port, oldURL.port());
80     EXPECT_TRUE(eq(parts.path, oldURL.path()));
81     EXPECT_TRUE(eq(parts.query, oldURL.query()));
82     EXPECT_TRUE(eq(parts.fragment, oldURL.fragmentIdentifier()));
83     EXPECT_TRUE(eq(parts.string, oldURL.string()));
84     
85     EXPECT_TRUE(URLParser::allValuesEqual(url, oldURL));
86     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
87     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
88 }
89
90 template<size_t length>
91 static String wideString(const wchar_t (&url)[length])
92 {
93     StringBuilder builder;
94     builder.reserveCapacity(length - 1);
95     for (size_t i = 0; i < length - 1; ++i)
96         builder.append(url[i]);
97     return builder.toString();
98 }
99
100 TEST_F(URLParserTest, Basic)
101 {
102     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"});
103     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"});
104     checkURL("http://user:pass@webkit.org:123/path", {"http", "user", "pass", "webkit.org", 123, "/path", "", "", "http://user:pass@webkit.org:123/path"});
105     checkURL("http://user:pass@webkit.org:123/", {"http", "user", "pass", "webkit.org", 123, "/", "", "", "http://user:pass@webkit.org:123/"});
106     checkURL("http://user:pass@webkit.org:123", {"http", "user", "pass", "webkit.org", 123, "/", "", "", "http://user:pass@webkit.org:123/"});
107     checkURL("http://user:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"});
108     checkURL("http://webkit.org", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"});
109     checkURL("http://127.0.0.1", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"});
110     checkURL("http://webkit.org/", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"});
111     checkURL("http://webkit.org/path1/path2/index.html", {"http", "", "", "webkit.org", 0, "/path1/path2/index.html", "", "", "http://webkit.org/path1/path2/index.html"});
112     checkURL("about:blank", {"about", "", "", "", 0, "blank", "", "", "about:blank"});
113     checkURL("about:blank?query", {"about", "", "", "", 0, "blank", "query", "", "about:blank?query"});
114     checkURL("about:blank#fragment", {"about", "", "", "", 0, "blank", "", "fragment", "about:blank#fragment"});
115     checkURL("http://[0:f::f:f:0:0]", {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"});
116     checkURL("http://[0:f:0:0:f::]", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"});
117     checkURL("http://[::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"});
118     checkURL("http://example.com/path1/path2/.", {"http", "", "", "example.com", 0, "/path1/path2/", "", "", "http://example.com/path1/path2/"});
119     checkURL("http://example.com/path1/path2/..", {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"});
120     checkURL("http://example.com/path1/path2/./path3", {"http", "", "", "example.com", 0, "/path1/path2/path3", "", "", "http://example.com/path1/path2/path3"});
121     checkURL("http://example.com/path1/path2/.\\path3", {"http", "", "", "example.com", 0, "/path1/path2/path3", "", "", "http://example.com/path1/path2/path3"});
122     checkURL("http://example.com/path1/path2/../path3", {"http", "", "", "example.com", 0, "/path1/path3", "", "", "http://example.com/path1/path3"});
123     checkURL("http://example.com/path1/path2/..\\path3", {"http", "", "", "example.com", 0, "/path1/path3", "", "", "http://example.com/path1/path3"});
124     checkURL("http://example.com/.", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
125     checkURL("http://example.com/..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
126     checkURL("http://example.com/./path1", {"http", "", "", "example.com", 0, "/path1", "", "", "http://example.com/path1"});
127     checkURL("http://example.com/../path1", {"http", "", "", "example.com", 0, "/path1", "", "", "http://example.com/path1"});
128     checkURL("http://example.com/../path1/../../path2/path3/../path4", {"http", "", "", "example.com", 0, "/path2/path4", "", "", "http://example.com/path2/path4"});
129     checkURL("http://example.com/path1/.%2", {"http", "", "", "example.com", 0, "/path1/.%2", "", "", "http://example.com/path1/.%2"});
130     checkURL("http://example.com/path1/%2", {"http", "", "", "example.com", 0, "/path1/%2", "", "", "http://example.com/path1/%2"});
131     checkURL("http://example.com/path1/%", {"http", "", "", "example.com", 0, "/path1/%", "", "", "http://example.com/path1/%"});
132     checkURL("http://example.com/path1/.%", {"http", "", "", "example.com", 0, "/path1/.%", "", "", "http://example.com/path1/.%"});
133     checkURL("http://example.com//.", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
134     checkURL("http://example.com//./", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
135     checkURL("http://example.com//.//", {"http", "", "", "example.com", 0, "///", "", "", "http://example.com///"});
136     checkURL("http://example.com//..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
137     checkURL("http://example.com//../", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
138     checkURL("http://example.com//..//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
139     checkURL("http://example.com//..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
140     checkURL("http://example.com/.//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
141     checkURL("http://example.com/..//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
142     checkURL("http://example.com/./", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
143     checkURL("http://example.com/../", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
144     checkURL("http://example.com/path1/.../path3", {"http", "", "", "example.com", 0, "/path1/.../path3", "", "", "http://example.com/path1/.../path3"});
145     checkURL("http://example.com/path1/...", {"http", "", "", "example.com", 0, "/path1/...", "", "", "http://example.com/path1/..."});
146     checkURL("http://example.com/path1/.../", {"http", "", "", "example.com", 0, "/path1/.../", "", "", "http://example.com/path1/.../"});
147     checkURL("http://example.com/.path1/", {"http", "", "", "example.com", 0, "/.path1/", "", "", "http://example.com/.path1/"});
148     checkURL("http://example.com/..path1/", {"http", "", "", "example.com", 0, "/..path1/", "", "", "http://example.com/..path1/"});
149     checkURL("http://example.com/path1/.path2", {"http", "", "", "example.com", 0, "/path1/.path2", "", "", "http://example.com/path1/.path2"});
150     checkURL("http://example.com/path1/..path2", {"http", "", "", "example.com", 0, "/path1/..path2", "", "", "http://example.com/path1/..path2"});
151     checkURL("http://example.com/path1/path2/.?query", {"http", "", "", "example.com", 0, "/path1/path2/", "query", "", "http://example.com/path1/path2/?query"});
152     checkURL("http://example.com/path1/path2/..?query", {"http", "", "", "example.com", 0, "/path1/", "query", "", "http://example.com/path1/?query"});
153     checkURL("http://example.com/path1/path2/.#fragment", {"http", "", "", "example.com", 0, "/path1/path2/", "", "fragment", "http://example.com/path1/path2/#fragment"});
154     checkURL("http://example.com/path1/path2/..#fragment", {"http", "", "", "example.com", 0, "/path1/", "", "fragment", "http://example.com/path1/#fragment"});
155
156     checkURL("file:", {"file", "", "", "", 0, "/", "", "", "file:///"});
157     checkURL("file:/", {"file", "", "", "", 0, "/", "", "", "file:///"});
158     checkURL("file://", {"file", "", "", "", 0, "/", "", "", "file:///"});
159     checkURL("file:///", {"file", "", "", "", 0, "/", "", "", "file:///"});
160     checkURL("file:////", {"file", "", "", "", 0, "//", "", "", "file:////"}); // This matches Firefox and URL::parse which I believe are correct, but not Chrome.
161     checkURL("file:/path", {"file", "", "", "", 0, "/path", "", "", "file:///path"});
162     checkURL("file://host/path", {"file", "", "", "host", 0, "/path", "", "", "file://host/path"});
163     checkURL("file://host", {"file", "", "", "host", 0, "/", "", "", "file://host/"});
164     checkURL("file://host/", {"file", "", "", "host", 0, "/", "", "", "file://host/"});
165     checkURL("file:///path", {"file", "", "", "", 0, "/path", "", "", "file:///path"});
166     checkURL("file:////path", {"file", "", "", "", 0, "//path", "", "", "file:////path"});
167     checkURL("file://localhost/path", {"file", "", "", "", 0, "/path", "", "", "file:///path"});
168     checkURL("file://localhost/", {"file", "", "", "", 0, "/", "", "", "file:///"});
169     checkURL("file://localhost", {"file", "", "", "", 0, "/", "", "", "file:///"});
170     checkURL("file://lOcAlHoSt", {"file", "", "", "", 0, "/", "", "", "file:///"});
171     checkURL("file://lOcAlHoSt/", {"file", "", "", "", 0, "/", "", "", "file:///"});
172     checkURL("file:/pAtH/", {"file", "", "", "", 0, "/pAtH/", "", "", "file:///pAtH/"});
173     checkURL("file:/pAtH", {"file", "", "", "", 0, "/pAtH", "", "", "file:///pAtH"});
174     checkURL("file:?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"});
175     checkURL("file:#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"});
176     checkURL("file:?query#fragment", {"file", "", "", "", 0, "/", "query", "fragment", "file:///?query#fragment"});
177     checkURL("file:#fragment?notquery", {"file", "", "", "", 0, "/", "", "fragment?notquery", "file:///#fragment?notquery"});
178     checkURL("file:/?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"});
179     checkURL("file:/#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"});
180     checkURL("file://?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"});
181     checkURL("file://#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"});
182     checkURL("file:///?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"});
183     checkURL("file:///#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"});
184     checkURL("file:////?query", {"file", "", "", "", 0, "//", "query", "", "file:////?query"});
185     checkURL("file:////#fragment", {"file", "", "", "", 0, "//", "", "fragment", "file:////#fragment"});
186     checkURL("http://host/A b", {"http", "", "", "host", 0, "/A%20b", "", "", "http://host/A%20b"});
187     checkURL("http://host/a%20B", {"http", "", "", "host", 0, "/a%20B", "", "", "http://host/a%20B"});
188     checkURL("http://host?q=@ <>!#fragment", {"http", "", "", "host", 0, "/", "q=@%20%3C%3E!", "fragment", "http://host/?q=@%20%3C%3E!#fragment"});
189     checkURL("http://user:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"});
190     checkURL("http://127.0.0.1:10100/path", {"http", "", "", "127.0.0.1", 10100, "/path", "", "", "http://127.0.0.1:10100/path"});
191     checkURL("http://127.0.0.1:/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"});
192     checkURL("http://127.0.0.1:123", {"http", "", "", "127.0.0.1", 123, "/", "", "", "http://127.0.0.1:123/"});
193     checkURL("http://127.0.0.1:", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"});
194     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"});
195     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/"});
196     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"});
197     checkURL("http://[0:f::f:f:0:0]:", {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"});
198     checkURL("http://host:10100/path", {"http", "", "", "host", 10100, "/path", "", "", "http://host:10100/path"});
199     checkURL("http://host:/path", {"http", "", "", "host", 0, "/path", "", "", "http://host/path"});
200     checkURL("http://host:123", {"http", "", "", "host", 123, "/", "", "", "http://host:123/"});
201     checkURL("http://host:", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
202     checkURL("http://hos\tt\n:\t1\n2\t3\t/\npath", {"http", "", "", "host", 123, "/path", "", "", "http://host:123/path"});
203     checkURL("http://user@example.org/path3", {"http", "user", "", "example.org", 0, "/path3", "", "", "http://user@example.org/path3"});
204     checkURL("sc:/pa/pa", {"sc", "", "", "", 0, "/pa/pa", "", "", "sc:/pa/pa"});
205     checkURL("sc:/pa", {"sc", "", "", "", 0, "/pa", "", "", "sc:/pa"});
206     checkURL("sc:/pa/", {"sc", "", "", "", 0, "/pa/", "", "", "sc:/pa/"});
207     checkURL("notspecial:/notuser:notpassword@nothost", {"notspecial", "", "", "", 0, "/notuser:notpassword@nothost", "", "", "notspecial:/notuser:notpassword@nothost"});
208     checkURL("sc://pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"});
209     checkURL("http://host   \a   ", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
210     checkURL("notspecial:/a", {"notspecial", "", "", "", 0, "/a", "", "", "notspecial:/a"});
211     checkURL("notspecial:", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
212     checkURL("http:/a", {"http", "", "", "a", 0, "/", "", "", "http://a/"});
213     checkURL("http://256/", {"http", "", "", "256", 0, "/", "", "", "http://256/"});
214     checkURL("http://256./", {"http", "", "", "256.", 0, "/", "", "", "http://256./"});
215     checkURL("http://123.256/", {"http", "", "", "123.256", 0, "/", "", "", "http://123.256/"});
216     checkURL("notspecial:/a", {"notspecial", "", "", "", 0, "/a", "", "", "notspecial:/a"});
217     checkURL("notspecial:", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
218     // FIXME: Fix and add a test with an invalid surrogate pair at the end with a space as the second code unit.
219
220     // This disagrees with the web platform test for http://:@www.example.com but agrees with Chrome and URL::parse,
221     // and Firefox fails the web platform test differently. Maybe the web platform test ought to be changed.
222     checkURL("http://:@host", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
223 }
224
225 static void checkRelativeURL(const String& urlString, const String& baseURLString, const ExpectedParts& parts)
226 {
227     URLParser baseParser;
228     auto base = baseParser.parse(baseURLString);
229
230     URLParser parser;
231     auto url = parser.parse(urlString, base);
232     EXPECT_TRUE(eq(parts.protocol, url.protocol()));
233     EXPECT_TRUE(eq(parts.user, url.user()));
234     EXPECT_TRUE(eq(parts.password, url.pass()));
235     EXPECT_TRUE(eq(parts.host, url.host()));
236     EXPECT_EQ(parts.port, url.port());
237     EXPECT_TRUE(eq(parts.path, url.path()));
238     EXPECT_TRUE(eq(parts.query, url.query()));
239     EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
240     EXPECT_TRUE(eq(parts.string, url.string()));
241
242     auto oldURL = URL(URL(URL(), baseURLString), urlString);
243     EXPECT_TRUE(eq(parts.protocol, oldURL.protocol()));
244     EXPECT_TRUE(eq(parts.user, oldURL.user()));
245     EXPECT_TRUE(eq(parts.password, oldURL.pass()));
246     EXPECT_TRUE(eq(parts.host, oldURL.host()));
247     EXPECT_EQ(parts.port, oldURL.port());
248     EXPECT_TRUE(eq(parts.path, oldURL.path()));
249     EXPECT_TRUE(eq(parts.query, oldURL.query()));
250     EXPECT_TRUE(eq(parts.fragment, oldURL.fragmentIdentifier()));
251     EXPECT_TRUE(eq(parts.string, oldURL.string()));
252
253     EXPECT_TRUE(URLParser::allValuesEqual(url, oldURL));
254     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
255     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
256 }
257
258 TEST_F(URLParserTest, ParseRelative)
259 {
260     checkRelativeURL("/index.html", "http://webkit.org/path1/path2/", {"http", "", "", "webkit.org", 0, "/index.html", "", "", "http://webkit.org/index.html"});
261     checkRelativeURL("http://whatwg.org/index.html", "http://webkit.org/path1/path2/", {"http", "", "", "whatwg.org", 0, "/index.html", "", "", "http://whatwg.org/index.html"});
262     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"});
263     checkRelativeURL("//whatwg.org/index.html", "https://www.webkit.org/path", {"https", "", "", "whatwg.org", 0, "/index.html", "", "", "https://whatwg.org/index.html"});
264     checkRelativeURL("http://example\t.\norg", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/", "", "", "http://example.org/"});
265     checkRelativeURL("test", "file:///path1/path2", {"file", "", "", "", 0, "/path1/test", "", "", "file:///path1/test"});
266     checkRelativeURL(wideString(L"http://www.foo。bar.com"), "http://other.com/", {"http", "", "", "www.foo.bar.com", 0, "/", "", "", "http://www.foo.bar.com/"});
267     checkRelativeURL(wideString(L"sc://ñ.test/"), "about:blank", {"sc", "", "", "xn--ida.test", 0, "/", "", "", "sc://xn--ida.test/"});
268     checkRelativeURL("#fragment", "http://host/path", {"http", "", "", "host", 0, "/path", "", "fragment", "http://host/path#fragment"});
269     checkRelativeURL("?query", "http://host/path", {"http", "", "", "host", 0, "/path", "query", "", "http://host/path?query"});
270     checkRelativeURL("?query#fragment", "http://host/path", {"http", "", "", "host", 0, "/path", "query", "fragment", "http://host/path?query#fragment"});
271     checkRelativeURL(wideString(L"?β"), "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "%CE%B2", "", "http://example.org/foo/bar?%CE%B2"});
272     checkRelativeURL("?", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar?"});
273     checkRelativeURL("#", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar#"});
274     checkRelativeURL("?#", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar?#"});
275     checkRelativeURL("#?", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "?", "http://example.org/foo/bar#?"});
276     checkRelativeURL("/", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/", "", "", "http://example.org/"});
277     checkRelativeURL("http://@host", "about:blank", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
278     checkRelativeURL("http://:@host", "about:blank", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
279     checkRelativeURL("http://foo.com/\\@", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "//@", "", "", "http://foo.com//@"});
280     checkRelativeURL("\\@", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/@", "", "", "http://example.org/@"});
281     checkRelativeURL("/path3", "http://user@example.org/path1/path2", {"http", "user", "", "example.org", 0, "/path3", "", "", "http://user@example.org/path3"});
282     checkRelativeURL("", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
283     checkRelativeURL("  \a  \t\n", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
284     checkRelativeURL(":foo.com\\", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/:foo.com/", "", "", "http://example.org/foo/:foo.com/"});
285     checkRelativeURL("http:/example.com/", "about:blank", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
286     checkRelativeURL("http:example.com/", "about:blank", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
287     checkRelativeURL("http:\\\\foo.com\\", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"});
288     checkRelativeURL("http:\\\\foo.com/", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"});
289     checkRelativeURL("http:\\\\foo.com", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"});
290     checkRelativeURL("http://ExAmPlE.CoM", "http://other.com", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
291     checkRelativeURL("http:", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
292     checkRelativeURL("#x", "data:,", {"data", "", "", "", 0, ",", "", "x", "data:,#x"});
293     checkRelativeURL("#x", "about:blank", {"about", "", "", "", 0, "blank", "", "x", "about:blank#x"});
294     checkRelativeURL("  foo.com  ", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/foo.com", "", "", "http://example.org/foo/foo.com"});
295     checkRelativeURL(" \a baz", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/baz", "", "", "http://example.org/foo/baz"});
296     checkRelativeURL("~", "http://example.org", {"http", "", "", "example.org", 0, "/~", "", "", "http://example.org/~"});
297     checkRelativeURL("notspecial:", "about:blank", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
298     checkRelativeURL("notspecial:", "http://host", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
299     checkRelativeURL("http:", "http://host", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
300     
301     // The checking of slashes in SpecialAuthoritySlashes needed to get this to pass contradicts what is in the spec,
302     // but it is included in the web platform tests.
303     checkRelativeURL("http:\\\\host\\foo", "about:blank", {"http", "", "", "host", 0, "/foo", "", "", "http://host/foo"});
304 }
305
306 static void checkURLDifferences(const String& urlString, const ExpectedParts& partsNew, const ExpectedParts& partsOld)
307 {
308     URLParser parser;
309     auto url = parser.parse(urlString);
310     EXPECT_TRUE(eq(partsNew.protocol, url.protocol()));
311     EXPECT_TRUE(eq(partsNew.user, url.user()));
312     EXPECT_TRUE(eq(partsNew.password, url.pass()));
313     EXPECT_TRUE(eq(partsNew.host, url.host()));
314     EXPECT_EQ(partsNew.port, url.port());
315     EXPECT_TRUE(eq(partsNew.path, url.path()));
316     EXPECT_TRUE(eq(partsNew.query, url.query()));
317     EXPECT_TRUE(eq(partsNew.fragment, url.fragmentIdentifier()));
318     EXPECT_TRUE(eq(partsNew.string, url.string()));
319     
320     auto oldURL = URL(URL(), urlString);
321     EXPECT_TRUE(eq(partsOld.protocol, oldURL.protocol()));
322     EXPECT_TRUE(eq(partsOld.user, oldURL.user()));
323     EXPECT_TRUE(eq(partsOld.password, oldURL.pass()));
324     EXPECT_TRUE(eq(partsOld.host, oldURL.host()));
325     EXPECT_EQ(partsOld.port, oldURL.port());
326     EXPECT_TRUE(eq(partsOld.path, oldURL.path()));
327     EXPECT_TRUE(eq(partsOld.query, oldURL.query()));
328     EXPECT_TRUE(eq(partsOld.fragment, oldURL.fragmentIdentifier()));
329     EXPECT_TRUE(eq(partsOld.string, oldURL.string()));
330     
331     EXPECT_FALSE(URLParser::allValuesEqual(url, oldURL));
332     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
333     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
334 }
335
336 static void checkRelativeURLDifferences(const String& urlString, const String& baseURLString, const ExpectedParts& partsNew, const ExpectedParts& partsOld)
337 {
338     URLParser baseParser;
339     auto base = baseParser.parse(baseURLString);
340     
341     URLParser parser;
342     auto url = parser.parse(urlString, base);
343     EXPECT_TRUE(eq(partsNew.protocol, url.protocol()));
344     EXPECT_TRUE(eq(partsNew.user, url.user()));
345     EXPECT_TRUE(eq(partsNew.password, url.pass()));
346     EXPECT_TRUE(eq(partsNew.host, url.host()));
347     EXPECT_EQ(partsNew.port, url.port());
348     EXPECT_TRUE(eq(partsNew.path, url.path()));
349     EXPECT_TRUE(eq(partsNew.query, url.query()));
350     EXPECT_TRUE(eq(partsNew.fragment, url.fragmentIdentifier()));
351     EXPECT_TRUE(eq(partsNew.string, url.string()));
352     
353     auto oldURL = URL(URL(URL(), baseURLString), urlString);
354     EXPECT_TRUE(eq(partsOld.protocol, oldURL.protocol()));
355     EXPECT_TRUE(eq(partsOld.user, oldURL.user()));
356     EXPECT_TRUE(eq(partsOld.password, oldURL.pass()));
357     EXPECT_TRUE(eq(partsOld.host, oldURL.host()));
358     EXPECT_EQ(partsOld.port, oldURL.port());
359     EXPECT_TRUE(eq(partsOld.path, oldURL.path()));
360     EXPECT_TRUE(eq(partsOld.query, oldURL.query()));
361     EXPECT_TRUE(eq(partsOld.fragment, oldURL.fragmentIdentifier()));
362     EXPECT_TRUE(eq(partsOld.string, oldURL.string()));
363     
364     EXPECT_FALSE(URLParser::allValuesEqual(url, oldURL));
365     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
366     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
367 }
368
369 // These are differences between the new URLParser and the old URL::parse which make URLParser more standards compliant.
370 TEST_F(URLParserTest, ParserDifferences)
371 {
372     checkURLDifferences("http://127.0.1",
373         {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"},
374         {"http", "", "", "127.0.1", 0, "/", "", "", "http://127.0.1/"});
375     checkURLDifferences("http://011.11.0X11.0x011",
376         {"http", "", "", "9.11.17.17", 0, "/", "", "", "http://9.11.17.17/"},
377         {"http", "", "", "011.11.0x11.0x011", 0, "/", "", "", "http://011.11.0x11.0x011/"});
378     checkURLDifferences("http://[1234:0078:90AB:CdEf:0123:0007:89AB:0000]",
379         {"http", "", "", "[1234:78:90ab:cdef:123:7:89ab:0]", 0, "/", "", "", "http://[1234:78:90ab:cdef:123:7:89ab:0]/"},
380         {"http", "", "", "[1234:0078:90ab:cdef:0123:0007:89ab:0000]", 0, "/", "", "", "http://[1234:0078:90ab:cdef:0123:0007:89ab:0000]/"});
381     checkURLDifferences("http://[0:f:0:0:f:f:0:0]",
382         {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"},
383         {"http", "", "", "[0:f:0:0:f:f:0:0]", 0, "/", "", "", "http://[0:f:0:0:f:f:0:0]/"});
384     checkURLDifferences("http://[0:f:0:0:f:0:0:0]",
385         {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"},
386         {"http", "", "", "[0:f:0:0:f:0:0:0]", 0, "/", "", "", "http://[0:f:0:0:f:0:0:0]/"});
387     checkURLDifferences("http://[0:0:f:0:0:f:0:0]",
388         {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"},
389         {"http", "", "", "[0:0:f:0:0:f:0:0]", 0, "/", "", "", "http://[0:0:f:0:0:f:0:0]/"});
390     checkURLDifferences("http://example.com/path1/.%2e",
391         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
392         {"http", "", "", "example.com", 0, "/path1/.%2e", "", "", "http://example.com/path1/.%2e"});
393     checkURLDifferences("http://example.com/path1/.%2E",
394         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
395         {"http", "", "", "example.com", 0, "/path1/.%2E", "", "", "http://example.com/path1/.%2E"});
396     checkURLDifferences("http://example.com/path1/.%2E/",
397         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
398         {"http", "", "", "example.com", 0, "/path1/.%2E/", "", "", "http://example.com/path1/.%2E/"});
399     checkURLDifferences("http://example.com/path1/%2e.",
400         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
401         {"http", "", "", "example.com", 0, "/path1/%2e.", "", "", "http://example.com/path1/%2e."});
402     checkURLDifferences("http://example.com/path1/%2E%2e",
403         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
404         {"http", "", "", "example.com", 0, "/path1/%2E%2e", "", "", "http://example.com/path1/%2E%2e"});
405     checkURLDifferences("http://example.com/path1/%2e",
406         {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"},
407         {"http", "", "", "example.com", 0, "/path1/%2e", "", "", "http://example.com/path1/%2e"});
408     checkURLDifferences("http://example.com/path1/%2E",
409         {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"},
410         {"http", "", "", "example.com", 0, "/path1/%2E", "", "", "http://example.com/path1/%2E"});
411     checkURLDifferences("http://example.com/path1/%2E/",
412         {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"},
413         {"http", "", "", "example.com", 0, "/path1/%2E/", "", "", "http://example.com/path1/%2E/"});
414     checkURLDifferences("http://example.com/path1/path2/%2e?query",
415         {"http", "", "", "example.com", 0, "/path1/path2/", "query", "", "http://example.com/path1/path2/?query"},
416         {"http", "", "", "example.com", 0, "/path1/path2/%2e", "query", "", "http://example.com/path1/path2/%2e?query"});
417     checkURLDifferences("http://example.com/path1/path2/%2e%2e?query",
418         {"http", "", "", "example.com", 0, "/path1/", "query", "", "http://example.com/path1/?query"},
419         {"http", "", "", "example.com", 0, "/path1/path2/%2e%2e", "query", "", "http://example.com/path1/path2/%2e%2e?query"});
420     checkURLDifferences("http://example.com/path1/path2/%2e#fragment",
421         {"http", "", "", "example.com", 0, "/path1/path2/", "", "fragment", "http://example.com/path1/path2/#fragment"},
422         {"http", "", "", "example.com", 0, "/path1/path2/%2e", "", "fragment", "http://example.com/path1/path2/%2e#fragment"});
423     checkURLDifferences("http://example.com/path1/path2/%2e%2e#fragment",
424         {"http", "", "", "example.com", 0, "/path1/", "", "fragment", "http://example.com/path1/#fragment"},
425         {"http", "", "", "example.com", 0, "/path1/path2/%2e%2e", "", "fragment", "http://example.com/path1/path2/%2e%2e#fragment"});
426     checkURLDifferences("file://[0:a:0:0:b:c:0:0]/path",
427         {"file", "", "", "[0:a::b:c:0:0]", 0, "/path", "", "", "file://[0:a::b:c:0:0]/path"},
428         {"file", "", "", "[0:a:0:0:b:c:0:0]", 0, "/path", "", "", "file://[0:a:0:0:b:c:0:0]/path"});
429     checkRelativeURLDifferences(wideString(L"#β"), "http://example.org/foo/bar",
430         {"http", "", "", "example.org", 0, "/foo/bar", "", wideString(L"β"), wideString(L"http://example.org/foo/bar#β")},
431         {"http", "", "", "example.org", 0, "/foo/bar", "", "%CE%B2", "http://example.org/foo/bar#%CE%B2"});
432     checkURLDifferences("http://",
433         {"", "", "", "", 0, "", "", "", "http://"},
434         {"http", "", "", "", 0, "/", "", "", "http:/"});
435     checkRelativeURLDifferences("//", "https://www.webkit.org/path",
436         {"", "", "", "", 0, "", "", "", "//"},
437         {"https", "", "", "", 0, "/", "", "", "https:/"});
438     checkURLDifferences("http://127.0.0.1:65536/path",
439         {"", "", "", "", 0, "", "", "", "http://127.0.0.1:65536/path"},
440         {"http", "", "", "127.0.0.1", 65535, "/path", "", "", "http://127.0.0.1:65536/path"});
441     checkURLDifferences("http://host:65536",
442         {"", "", "", "", 0, "", "", "", "http://host:65536"},
443         {"http", "", "", "host", 65535, "/", "", "", "http://host:65536/"});
444     checkURLDifferences("http://127.0.0.1:65536",
445         {"", "", "", "", 0, "", "", "", "http://127.0.0.1:65536"},
446         {"http", "", "", "127.0.0.1", 65535, "/", "", "", "http://127.0.0.1:65536/"});
447     checkURLDifferences("http://[0:f::f:f:0:0]:65536",
448         {"", "", "", "", 0, "", "", "", "http://[0:f::f:f:0:0]:65536"},
449         {"http", "", "", "[0:f::f:f:0:0]", 65535, "/", "", "", "http://[0:f::f:f:0:0]:65536/"});
450     checkRelativeURLDifferences(":foo.com\\", "notspecial://example.org/foo/bar",
451         {"notspecial", "", "", "example.org", 0, "/foo/:foo.com\\", "", "", "notspecial://example.org/foo/:foo.com\\"},
452         {"notspecial", "", "", "example.org", 0, "/foo/:foo.com/", "", "", "notspecial://example.org/foo/:foo.com/"});
453     checkURLDifferences("sc://pa",
454         {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"},
455         {"sc", "", "", "pa", 0, "", "", "", "sc://pa"});
456     checkRelativeURLDifferences("notspecial:\\\\foo.com\\", "http://example.org/foo/bar",
457         {"notspecial", "", "", "", 0, "\\\\foo.com\\", "", "", "notspecial:\\\\foo.com\\"},
458         {"notspecial", "", "", "foo.com", 0, "/", "", "", "notspecial://foo.com/"});
459     checkRelativeURLDifferences("notspecial:\\\\foo.com/", "http://example.org/foo/bar",
460         {"notspecial", "", "", "", 0, "\\\\foo.com/", "", "", "notspecial:\\\\foo.com/"},
461         {"notspecial", "", "", "foo.com", 0, "/", "", "", "notspecial://foo.com/"});
462     checkRelativeURLDifferences("notspecial:\\\\foo.com", "http://example.org/foo/bar",
463         {"notspecial", "", "", "", 0, "\\\\foo.com", "", "", "notspecial:\\\\foo.com"},
464         {"notspecial", "", "", "foo.com", 0, "", "", "", "notspecial://foo.com"});
465     checkURLDifferences("file://notuser:notpassword@test",
466         {"", "", "", "", 0, "", "", "", "file://notuser:notpassword@test"},
467         {"file", "notuser", "notpassword", "test", 0, "/", "", "", "file://notuser:notpassword@test/"});
468     checkURLDifferences("file://notuser:notpassword@test/",
469         {"", "", "", "", 0, "", "", "", "file://notuser:notpassword@test/"},
470         {"file", "notuser", "notpassword", "test", 0, "/", "", "", "file://notuser:notpassword@test/"});
471     checkRelativeURLDifferences("http:/", "about:blank",
472         {"", "", "", "", 0, "", "", "", "http:/"},
473         {"http", "", "", "", 0, "/", "", "", "http:/"});
474     checkRelativeURLDifferences("http:", "about:blank",
475         {"http", "", "", "", 0, "", "", "", "http:"},
476         {"http", "", "", "", 0, "/", "", "", "http:/"});
477     checkRelativeURLDifferences("http:/", "http://host",
478         {"", "", "", "", 0, "", "", "", "http:/"},
479         {"http", "", "", "", 0, "/", "", "", "http:/"});
480     checkURLDifferences("http:/",
481         {"", "", "", "", 0, "", "", "", "http:/"},
482         {"http", "", "", "", 0, "/", "", "", "http:/"});
483     checkURLDifferences("http:",
484         {"http", "", "", "", 0, "", "", "", "http:"},
485         {"http", "", "", "", 0, "/", "", "", "http:/"});
486     checkRelativeURLDifferences("http:/example.com/", "http://example.org/foo/bar",
487         {"http", "", "", "example.org", 0, "/example.com/", "", "", "http://example.org/example.com/"},
488         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
489     
490     // This behavior matches Chrome and Firefox, but not WebKit using URL::parse.
491     // The behavior of URL::parse is clearly wrong because reparsing file://path would make path the host.
492     // The spec is unclear.
493     checkURLDifferences("file:path",
494         {"file", "", "", "", 0, "/path", "", "", "file:///path"},
495         {"file", "", "", "", 0, "path", "", "", "file://path"});
496     checkURLDifferences("file:pAtH",
497         {"file", "", "", "", 0, "/pAtH", "", "", "file:///pAtH"},
498         {"file", "", "", "", 0, "pAtH", "", "", "file://pAtH"});
499     checkURLDifferences("file:pAtH/",
500         {"file", "", "", "", 0, "/pAtH/", "", "", "file:///pAtH/"},
501         {"file", "", "", "", 0, "pAtH/", "", "", "file://pAtH/"});
502     
503     // FIXME: Fix and test incomplete percent encoded characters in the middle and end of the input string.
504     // FIXME: Fix and test percent encoded upper case characters in the host.
505     checkURLDifferences("http://host%73",
506         {"http", "", "", "hosts", 0, "/", "", "", "http://hosts/"},
507         {"http", "", "", "host%73", 0, "/", "", "", "http://host%73/"});
508     
509     // URLParser matches Chrome and the spec, but not URL::parse or Firefox.
510     checkURLDifferences(wideString(L"http://0Xc0.0250.01"),
511         {"http", "", "", "192.168.0.1", 0, "/", "", "", "http://192.168.0.1/"},
512         {"http", "", "", "0xc0.0250.01", 0, "/", "", "", "http://0xc0.0250.01/"});
513     checkURLDifferences("http://host/path%2e.%2E",
514         {"http", "", "", "host", 0, "/path...", "", "", "http://host/path..."},
515         {"http", "", "", "host", 0, "/path%2e.%2E", "", "", "http://host/path%2e.%2E"});
516
517     checkRelativeURLDifferences(wideString(L"http://foo:💩@example.com/bar"), "http://other.com/",
518         {"http", "foo", wideString(L"💩"), "example.com", 0, "/bar", "", "", "http://foo:%F0%9F%92%A9@example.com/bar"},
519         {"", "", "", "", 0, "", "", "", wideString(L"http://foo:💩@example.com/bar")});
520     checkRelativeURLDifferences("http://&a:foo(b]c@d:2/", "http://example.org/foo/bar",
521         {"http", "&a", "foo(b]c", "d", 2, "/", "", "", "http://&a:foo(b%5Dc@d:2/"},
522         {"", "", "", "", 0, "", "", "", "http://&a:foo(b]c@d:2/"});
523     checkRelativeURLDifferences("http://`{}:`{}@h/`{}?`{}", "http://doesnotmatter/",
524         {"http", "`{}", "`{}", "h", 0, "/%60%7B%7D", "`{}", "", "http://%60%7B%7D:%60%7B%7D@h/%60%7B%7D?`{}"},
525         {"", "", "", "", 0, "", "", "", "http://`{}:`{}@h/`{}?`{}"});
526     checkURLDifferences("http://[0:f::f::f]",
527         {"", "", "", "", 0, "" , "", "", "http://[0:f::f::f]"},
528         {"http", "", "", "[0:f::f::f]", 0, "/" , "", "", "http://[0:f::f::f]/"});
529     checkURLDifferences("http://123",
530         {"http", "", "", "0.0.0.123", 0, "/", "", "", "http://0.0.0.123/"},
531         {"http", "", "", "123", 0, "/", "", "", "http://123/"});
532     checkURLDifferences("http://123.234/",
533         {"http", "", "", "123.0.0.234", 0, "/", "", "", "http://123.0.0.234/"},
534         {"http", "", "", "123.234", 0, "/", "", "", "http://123.234/"});
535     checkURLDifferences("http://123.234.012",
536         {"http", "", "", "123.234.0.10", 0, "/", "", "", "http://123.234.0.10/"},
537         {"http", "", "", "123.234.012", 0, "/", "", "", "http://123.234.012/"});
538     checkURLDifferences("http://123.234.12",
539         {"http", "", "", "123.234.0.12", 0, "/", "", "", "http://123.234.0.12/"},
540         {"http", "", "", "123.234.12", 0, "/", "", "", "http://123.234.12/"});
541     checkRelativeURLDifferences("file:c:\\foo\\bar.html", "file:///tmp/mock/path",
542         {"file", "", "", "", 0, "/c:/foo/bar.html", "", "", "file:///c:/foo/bar.html"},
543         {"file", "", "", "", 0, "/tmp/mock/c:/foo/bar.html", "", "", "file:///tmp/mock/c:/foo/bar.html"});
544     checkRelativeURLDifferences("  File:c|////foo\\bar.html", "file:///tmp/mock/path",
545         {"file", "", "", "", 0, "/c:////foo/bar.html", "", "", "file:///c:////foo/bar.html"},
546         {"file", "", "", "", 0, "/tmp/mock/c|////foo/bar.html", "", "", "file:///tmp/mock/c|////foo/bar.html"});
547     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",
548         {"file", "", "", "", 0, "/c:////foo/bar.html", "", "", "file:///c:////foo/bar.html"},
549         {"file", "", "", "", 0, "/tmp/mock/c|////foo/bar.html", "", "", "file:///tmp/mock/c|////foo/bar.html"});
550     checkRelativeURLDifferences("C|/foo/bar", "file:///tmp/mock/path",
551         {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"},
552         {"file", "", "", "", 0, "/tmp/mock/C|/foo/bar", "", "", "file:///tmp/mock/C|/foo/bar"});
553     checkRelativeURLDifferences("/C|/foo/bar", "file:///tmp/mock/path",
554         {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"},
555         {"file", "", "", "", 0, "/C|/foo/bar", "", "", "file:///C|/foo/bar"});
556     checkRelativeURLDifferences("https://@test@test@example:800/", "http://doesnotmatter/",
557         {"https", "@test@test", "", "example", 800, "/", "", "", "https://%40test%40test@example:800/"},
558         {"", "", "", "", 0, "", "", "", "https://@test@test@example:800/"});
559     checkRelativeURLDifferences("foo://", "http://example.org/foo/bar",
560         {"foo", "", "", "", 0, "/", "", "", "foo:///"},
561         {"foo", "", "", "", 0, "//", "", "", "foo://"});
562     checkURLDifferences(wideString(L"http://host?ß😍#ß😍"),
563         {"http", "", "", "host", 0, "/", "%C3%9F%F0%9F%98%8D", wideString(L"ß😍"), wideString(L"http://host/?%C3%9F%F0%9F%98%8D#ß😍")},
564         {"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"});
565
566     // This matches the spec and web platform tests, but not Chrome, Firefox, or URL::parse.
567     checkRelativeURLDifferences("notspecial:/", "about:blank",
568         {"notspecial", "", "", "", 0, "", "", "", "notspecial:/"},
569         {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"});
570     checkRelativeURLDifferences("notspecial:/", "http://host",
571         {"notspecial", "", "", "", 0, "", "", "", "notspecial:/"},
572         {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"});
573     checkURLDifferences("notspecial:/",
574         {"notspecial", "", "", "", 0, "", "", "", "notspecial:/"},
575         {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"});
576 }
577
578 TEST_F(URLParserTest, DefaultPort)
579 {
580     checkURL("FtP://host:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
581     checkURL("ftp://host:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
582     checkURL("ftp://host:22/", {"ftp", "", "", "host", 22, "/", "", "", "ftp://host:22/"});
583     checkURLDifferences("ftp://host:21",
584         {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"},
585         {"ftp", "", "", "host", 0, "", "", "", "ftp://host"});
586     checkURLDifferences("ftp://host:22",
587         {"ftp", "", "", "host", 22, "/", "", "", "ftp://host:22/"},
588         {"ftp", "", "", "host", 22, "", "", "", "ftp://host:22"});
589     
590     checkURL("gOpHeR://host:70/", {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"});
591     checkURL("gopher://host:70/", {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"});
592     checkURL("gopher://host:71/", {"gopher", "", "", "host", 71, "/", "", "", "gopher://host:71/"});
593     // Spec, Chrome, Firefox, and URLParser have "/", URL::parse does not.
594     // Spec, Chrome, URLParser, URL::parse recognize gopher default port, Firefox does not.
595     checkURLDifferences("gopher://host:70",
596         {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"},
597         {"gopher", "", "", "host", 0, "", "", "", "gopher://host"});
598     checkURLDifferences("gopher://host:71",
599         {"gopher", "", "", "host", 71, "/", "", "", "gopher://host:71/"},
600         {"gopher", "", "", "host", 71, "", "", "", "gopher://host:71"});
601     
602     checkURL("hTtP://host:80", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
603     checkURL("http://host:80", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
604     checkURL("http://host:80/", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
605     checkURL("http://host:81", {"http", "", "", "host", 81, "/", "", "", "http://host:81/"});
606     checkURL("http://host:81/", {"http", "", "", "host", 81, "/", "", "", "http://host:81/"});
607     
608     checkURL("hTtPs://host:443", {"https", "", "", "host", 0, "/", "", "", "https://host/"});
609     checkURL("https://host:443", {"https", "", "", "host", 0, "/", "", "", "https://host/"});
610     checkURL("https://host:443/", {"https", "", "", "host", 0, "/", "", "", "https://host/"});
611     checkURL("https://host:444", {"https", "", "", "host", 444, "/", "", "", "https://host:444/"});
612     checkURL("https://host:444/", {"https", "", "", "host", 444, "/", "", "", "https://host:444/"});
613     
614     checkURL("wS://host:80/", {"ws", "", "", "host", 0, "/", "", "", "ws://host/"});
615     checkURL("ws://host:80/", {"ws", "", "", "host", 0, "/", "", "", "ws://host/"});
616     checkURL("ws://host:81/", {"ws", "", "", "host", 81, "/", "", "", "ws://host:81/"});
617     // URLParser matches Chrome and Firefox, but not URL::parse
618     checkURLDifferences("ws://host:80",
619         {"ws", "", "", "host", 0, "/", "", "", "ws://host/"},
620         {"ws", "", "", "host", 0, "", "", "", "ws://host"});
621     checkURLDifferences("ws://host:81",
622         {"ws", "", "", "host", 81, "/", "", "", "ws://host:81/"},
623         {"ws", "", "", "host", 81, "", "", "", "ws://host:81"});
624     
625     checkURL("WsS://host:443/", {"wss", "", "", "host", 0, "/", "", "", "wss://host/"});
626     checkURL("wss://host:443/", {"wss", "", "", "host", 0, "/", "", "", "wss://host/"});
627     checkURL("wss://host:444/", {"wss", "", "", "host", 444, "/", "", "", "wss://host:444/"});
628     // URLParser matches Chrome and Firefox, but not URL::parse
629     checkURLDifferences("wss://host:443",
630         {"wss", "", "", "host", 0, "/", "", "", "wss://host/"},
631         {"wss", "", "", "host", 0, "", "", "", "wss://host"});
632     checkURLDifferences("wss://host:444",
633         {"wss", "", "", "host", 444, "/", "", "", "wss://host:444/"},
634         {"wss", "", "", "host", 444, "", "", "", "wss://host:444"});
635
636     // 990 is the default ftps port in URL::parse, but it's not in the URL spec. Maybe it should be.
637     checkURL("fTpS://host:990/", {"ftps", "", "", "host", 990, "/", "", "", "ftps://host:990/"});
638     checkURL("ftps://host:990/", {"ftps", "", "", "host", 990, "/", "", "", "ftps://host:990/"});
639     checkURL("ftps://host:991/", {"ftps", "", "", "host", 991, "/", "", "", "ftps://host:991/"});
640     checkURLDifferences("ftps://host:990",
641         {"ftps", "", "", "host", 990, "/", "", "", "ftps://host:990/"},
642         {"ftps", "", "", "host", 990, "", "", "", "ftps://host:990"});
643     checkURLDifferences("ftps://host:991",
644         {"ftps", "", "", "host", 991, "/", "", "", "ftps://host:991/"},
645         {"ftps", "", "", "host", 991, "", "", "", "ftps://host:991"});
646
647     checkURL("uNkNoWn://host:80/", {"unknown", "", "", "host", 80, "/", "", "", "unknown://host:80/"});
648     checkURL("unknown://host:80/", {"unknown", "", "", "host", 80, "/", "", "", "unknown://host:80/"});
649     checkURL("unknown://host:81/", {"unknown", "", "", "host", 81, "/", "", "", "unknown://host:81/"});
650     checkURLDifferences("unknown://host:80",
651         {"unknown", "", "", "host", 80, "/", "", "", "unknown://host:80/"},
652         {"unknown", "", "", "host", 80, "", "", "", "unknown://host:80"});
653     checkURLDifferences("unknown://host:81",
654         {"unknown", "", "", "host", 81, "/", "", "", "unknown://host:81/"},
655         {"unknown", "", "", "host", 81, "", "", "", "unknown://host:81"});
656     checkURLDifferences("http://%48OsT",
657         {"http", "", "", "host", 0, "/", "", "", "http://host/"},
658         {"http", "", "", "%48ost", 0, "/", "", "", "http://%48ost/"});
659
660 }
661     
662 static void shouldFail(const String& urlString)
663 {
664     URLParser parser;
665     auto invalidURL = parser.parse(urlString);
666     checkURL(urlString, {"", "", "", "", 0, "", "", "", urlString});
667 }
668
669 static void shouldFail(const String& urlString, const String& baseString)
670 {
671     URLParser parser;
672     auto invalidURL = parser.parse(urlString);
673     checkRelativeURL(urlString, baseString, {"", "", "", "", 0, "", "", "", urlString});
674 }
675     
676 TEST_F(URLParserTest, ParserFailures)
677 {
678     shouldFail("    ");
679     shouldFail("  \a  ");
680     shouldFail("");
681     shouldFail("http://127.0.0.1:abc");
682     shouldFail("http://host:abc");
683     shouldFail("http://a:@", "about:blank");
684     shouldFail("http://:b@", "about:blank");
685     shouldFail("http://:@", "about:blank");
686     shouldFail("http://a:@");
687     shouldFail("http://:b@");
688     shouldFail("http://@");
689     shouldFail("http://[0:f::f:f:0:0]:abc");
690     shouldFail("../i", "sc:sd");
691     shouldFail("../i", "sc:sd/sd");
692     shouldFail("/i", "sc:sd");
693     shouldFail("/i", "sc:sd/sd");
694     shouldFail("?i", "sc:sd");
695     shouldFail("?i", "sc:sd/sd");
696     shouldFail("http://example example.com", "http://other.com/");
697     shouldFail("http://[www.example.com]/", "about:blank");
698     shouldFail("http://192.168.0.1 hello", "http://other.com/");
699     shouldFail("http://[example.com]", "http://other.com/");
700     shouldFail("i", "sc:sd");
701     shouldFail("i", "sc:sd/sd");
702     shouldFail("i");
703     shouldFail("asdf");
704     shouldFail("~");
705     shouldFail("~", "about:blank");
706     shouldFail("~~~");
707 }
708
709 // These are in the spec but not in the web platform tests.
710 TEST_F(URLParserTest, AdditionalTests)
711 {
712     checkURL("about:\a\aabc", {"about", "", "", "", 0, "%07%07abc", "", "", "about:%07%07abc"});
713     checkURL("notspecial:\t\t\n\t", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
714     checkURLDifferences("notspecial\t\t\n\t:\t\t\n\t/\t\t\n\t/\t\t\n\thost",
715         {"notspecial", "", "", "host", 0, "/", "", "", "notspecial://host/"},
716         {"notspecial", "", "", "host", 0, "", "", "", "notspecial://host"});
717     checkRelativeURL("http:", "http://example.org/foo/bar?query#fragment", {"http", "", "", "example.org", 0, "/foo/bar", "query", "", "http://example.org/foo/bar?query"});
718     checkRelativeURLDifferences("ws:", "http://example.org/foo/bar",
719         {"ws", "", "", "", 0, "", "", "", "ws:"},
720         {"ws", "", "", "", 0, "s:", "", "", "ws:s:"});
721     checkRelativeURL("notspecial:", "http://example.org/foo/bar", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
722 }
723
724 static void checkURL(const String& urlString, const TextEncoding& encoding, const ExpectedParts& parts)
725 {
726     URLParser parser;
727     auto url = parser.parse(urlString, { }, encoding);
728     EXPECT_TRUE(eq(parts.protocol, url.protocol()));
729     EXPECT_TRUE(eq(parts.user, url.user()));
730     EXPECT_TRUE(eq(parts.password, url.pass()));
731     EXPECT_TRUE(eq(parts.host, url.host()));
732     EXPECT_EQ(parts.port, url.port());
733     EXPECT_TRUE(eq(parts.path, url.path()));
734     EXPECT_TRUE(eq(parts.query, url.query()));
735     EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
736     EXPECT_TRUE(eq(parts.string, url.string()));
737 }
738
739 TEST_F(URLParserTest, QueryEncoding)
740 {
741     checkURL(wideString(L"http://host?ß😍#ß😍"), UTF8Encoding(), {"http", "", "", "host", 0, "/", "%C3%9F%F0%9F%98%8D", wideString(L"ß😍"), wideString(L"http://host/?%C3%9F%F0%9F%98%8D#ß😍")});
742     // FIXME: Add tests with other encodings.
743 }
744
745 } // namespace TestWebKitAPI