80fdb5f8baada12b2af010428a3f25a18fd41e9f
[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     bool wasEnabled = URLParser::enabled();
63     URLParser::setEnabled(true);
64     auto url = URL(URL(), urlString);
65     URLParser::setEnabled(false);
66     auto oldURL = URL(URL(), urlString);
67     URLParser::setEnabled(wasEnabled);
68     
69     EXPECT_TRUE(eq(parts.protocol, url.protocol()));
70     EXPECT_TRUE(eq(parts.user, url.user()));
71     EXPECT_TRUE(eq(parts.password, url.pass()));
72     EXPECT_TRUE(eq(parts.host, url.host()));
73     EXPECT_EQ(parts.port, url.port());
74     EXPECT_TRUE(eq(parts.path, url.path()));
75     EXPECT_TRUE(eq(parts.query, url.query()));
76     EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
77     EXPECT_TRUE(eq(parts.string, url.string()));
78     
79     EXPECT_TRUE(eq(parts.protocol, oldURL.protocol()));
80     EXPECT_TRUE(eq(parts.user, oldURL.user()));
81     EXPECT_TRUE(eq(parts.password, oldURL.pass()));
82     EXPECT_TRUE(eq(parts.host, oldURL.host()));
83     EXPECT_EQ(parts.port, oldURL.port());
84     EXPECT_TRUE(eq(parts.path, oldURL.path()));
85     EXPECT_TRUE(eq(parts.query, oldURL.query()));
86     EXPECT_TRUE(eq(parts.fragment, oldURL.fragmentIdentifier()));
87     EXPECT_TRUE(eq(parts.string, oldURL.string()));
88     
89     EXPECT_TRUE(URLParser::allValuesEqual(url, oldURL));
90     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
91     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
92 }
93
94 template<size_t length>
95 static String wideString(const wchar_t (&url)[length])
96 {
97     StringBuilder builder;
98     builder.reserveCapacity(length - 1);
99     for (size_t i = 0; i < length - 1; ++i)
100         builder.append(url[i]);
101     return builder.toString();
102 }
103
104 TEST_F(URLParserTest, Basic)
105 {
106     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"});
107     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"});
108     checkURL("http://user:pass@webkit.org:123/path", {"http", "user", "pass", "webkit.org", 123, "/path", "", "", "http://user:pass@webkit.org:123/path"});
109     checkURL("http://user:pass@webkit.org:123/", {"http", "user", "pass", "webkit.org", 123, "/", "", "", "http://user:pass@webkit.org:123/"});
110     checkURL("http://user:pass@webkit.org:123", {"http", "user", "pass", "webkit.org", 123, "/", "", "", "http://user:pass@webkit.org:123/"});
111     checkURL("http://user:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"});
112     checkURL("http://webkit.org", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"});
113     checkURL("http://127.0.0.1", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"});
114     checkURL("http://webkit.org/", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"});
115     checkURL("http://webkit.org/path1/path2/index.html", {"http", "", "", "webkit.org", 0, "/path1/path2/index.html", "", "", "http://webkit.org/path1/path2/index.html"});
116     checkURL("about:blank", {"about", "", "", "", 0, "blank", "", "", "about:blank"});
117     checkURL("about:blank?query", {"about", "", "", "", 0, "blank", "query", "", "about:blank?query"});
118     checkURL("about:blank#fragment", {"about", "", "", "", 0, "blank", "", "fragment", "about:blank#fragment"});
119     checkURL("http://[0:f::f:f:0:0]", {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"});
120     checkURL("http://[0:f:0:0:f::]", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"});
121     checkURL("http://[::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"});
122     checkURL("http://example.com/path1/path2/.", {"http", "", "", "example.com", 0, "/path1/path2/", "", "", "http://example.com/path1/path2/"});
123     checkURL("http://example.com/path1/path2/..", {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"});
124     checkURL("http://example.com/path1/path2/./path3", {"http", "", "", "example.com", 0, "/path1/path2/path3", "", "", "http://example.com/path1/path2/path3"});
125     checkURL("http://example.com/path1/path2/.\\path3", {"http", "", "", "example.com", 0, "/path1/path2/path3", "", "", "http://example.com/path1/path2/path3"});
126     checkURL("http://example.com/path1/path2/../path3", {"http", "", "", "example.com", 0, "/path1/path3", "", "", "http://example.com/path1/path3"});
127     checkURL("http://example.com/path1/path2/..\\path3", {"http", "", "", "example.com", 0, "/path1/path3", "", "", "http://example.com/path1/path3"});
128     checkURL("http://example.com/.", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
129     checkURL("http://example.com/..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
130     checkURL("http://example.com/./path1", {"http", "", "", "example.com", 0, "/path1", "", "", "http://example.com/path1"});
131     checkURL("http://example.com/../path1", {"http", "", "", "example.com", 0, "/path1", "", "", "http://example.com/path1"});
132     checkURL("http://example.com/../path1/../../path2/path3/../path4", {"http", "", "", "example.com", 0, "/path2/path4", "", "", "http://example.com/path2/path4"});
133     checkURL("http://example.com/path1/.%2", {"http", "", "", "example.com", 0, "/path1/.%2", "", "", "http://example.com/path1/.%2"});
134     checkURL("http://example.com/path1/%2", {"http", "", "", "example.com", 0, "/path1/%2", "", "", "http://example.com/path1/%2"});
135     checkURL("http://example.com/path1/%", {"http", "", "", "example.com", 0, "/path1/%", "", "", "http://example.com/path1/%"});
136     checkURL("http://example.com/path1/.%", {"http", "", "", "example.com", 0, "/path1/.%", "", "", "http://example.com/path1/.%"});
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/.//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
145     checkURL("http://example.com/..//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"});
146     checkURL("http://example.com/./", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
147     checkURL("http://example.com/../", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
148     checkURL("http://example.com/path1/.../path3", {"http", "", "", "example.com", 0, "/path1/.../path3", "", "", "http://example.com/path1/.../path3"});
149     checkURL("http://example.com/path1/...", {"http", "", "", "example.com", 0, "/path1/...", "", "", "http://example.com/path1/..."});
150     checkURL("http://example.com/path1/.../", {"http", "", "", "example.com", 0, "/path1/.../", "", "", "http://example.com/path1/.../"});
151     checkURL("http://example.com/.path1/", {"http", "", "", "example.com", 0, "/.path1/", "", "", "http://example.com/.path1/"});
152     checkURL("http://example.com/..path1/", {"http", "", "", "example.com", 0, "/..path1/", "", "", "http://example.com/..path1/"});
153     checkURL("http://example.com/path1/.path2", {"http", "", "", "example.com", 0, "/path1/.path2", "", "", "http://example.com/path1/.path2"});
154     checkURL("http://example.com/path1/..path2", {"http", "", "", "example.com", 0, "/path1/..path2", "", "", "http://example.com/path1/..path2"});
155     checkURL("http://example.com/path1/path2/.?query", {"http", "", "", "example.com", 0, "/path1/path2/", "query", "", "http://example.com/path1/path2/?query"});
156     checkURL("http://example.com/path1/path2/..?query", {"http", "", "", "example.com", 0, "/path1/", "query", "", "http://example.com/path1/?query"});
157     checkURL("http://example.com/path1/path2/.#fragment", {"http", "", "", "example.com", 0, "/path1/path2/", "", "fragment", "http://example.com/path1/path2/#fragment"});
158     checkURL("http://example.com/path1/path2/..#fragment", {"http", "", "", "example.com", 0, "/path1/", "", "fragment", "http://example.com/path1/#fragment"});
159
160     checkURL("file:", {"file", "", "", "", 0, "/", "", "", "file:///"});
161     checkURL("file:/", {"file", "", "", "", 0, "/", "", "", "file:///"});
162     checkURL("file://", {"file", "", "", "", 0, "/", "", "", "file:///"});
163     checkURL("file:///", {"file", "", "", "", 0, "/", "", "", "file:///"});
164     checkURL("file:////", {"file", "", "", "", 0, "//", "", "", "file:////"}); // This matches Firefox and URL::parse which I believe are correct, but not Chrome.
165     checkURL("file:/path", {"file", "", "", "", 0, "/path", "", "", "file:///path"});
166     checkURL("file://host/path", {"file", "", "", "host", 0, "/path", "", "", "file://host/path"});
167     checkURL("file://host", {"file", "", "", "host", 0, "/", "", "", "file://host/"});
168     checkURL("file://host/", {"file", "", "", "host", 0, "/", "", "", "file://host/"});
169     checkURL("file:///path", {"file", "", "", "", 0, "/path", "", "", "file:///path"});
170     checkURL("file:////path", {"file", "", "", "", 0, "//path", "", "", "file:////path"});
171     checkURL("file://localhost/path", {"file", "", "", "", 0, "/path", "", "", "file:///path"});
172     checkURL("file://localhost/", {"file", "", "", "", 0, "/", "", "", "file:///"});
173     checkURL("file://localhost", {"file", "", "", "", 0, "/", "", "", "file:///"});
174     checkURL("file://lOcAlHoSt", {"file", "", "", "", 0, "/", "", "", "file:///"});
175     checkURL("file://lOcAlHoSt/", {"file", "", "", "", 0, "/", "", "", "file:///"});
176     checkURL("file:/pAtH/", {"file", "", "", "", 0, "/pAtH/", "", "", "file:///pAtH/"});
177     checkURL("file:/pAtH", {"file", "", "", "", 0, "/pAtH", "", "", "file:///pAtH"});
178     checkURL("file:?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"});
179     checkURL("file:#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"});
180     checkURL("file:?query#fragment", {"file", "", "", "", 0, "/", "query", "fragment", "file:///?query#fragment"});
181     checkURL("file:#fragment?notquery", {"file", "", "", "", 0, "/", "", "fragment?notquery", "file:///#fragment?notquery"});
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("file:///?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"});
187     checkURL("file:///#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"});
188     checkURL("file:////?query", {"file", "", "", "", 0, "//", "query", "", "file:////?query"});
189     checkURL("file:////#fragment", {"file", "", "", "", 0, "//", "", "fragment", "file:////#fragment"});
190     checkURL("http://host/A b", {"http", "", "", "host", 0, "/A%20b", "", "", "http://host/A%20b"});
191     checkURL("http://host/a%20B", {"http", "", "", "host", 0, "/a%20B", "", "", "http://host/a%20B"});
192     checkURL("http://host?q=@ <>!#fragment", {"http", "", "", "host", 0, "/", "q=@%20%3C%3E!", "fragment", "http://host/?q=@%20%3C%3E!#fragment"});
193     checkURL("http://user:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"});
194     checkURL("http://127.0.0.1:10100/path", {"http", "", "", "127.0.0.1", 10100, "/path", "", "", "http://127.0.0.1:10100/path"});
195     checkURL("http://127.0.0.1:/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"});
196     checkURL("http://127.0.0.1:123", {"http", "", "", "127.0.0.1", 123, "/", "", "", "http://127.0.0.1:123/"});
197     checkURL("http://127.0.0.1:", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"});
198     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"});
199     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/"});
200     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"});
201     checkURL("http://[0:f::f:f:0:0]:", {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"});
202     checkURL("http://host:10100/path", {"http", "", "", "host", 10100, "/path", "", "", "http://host:10100/path"});
203     checkURL("http://host:/path", {"http", "", "", "host", 0, "/path", "", "", "http://host/path"});
204     checkURL("http://host:123", {"http", "", "", "host", 123, "/", "", "", "http://host:123/"});
205     checkURL("http://host:", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
206     checkURL("http://hos\tt\n:\t1\n2\t3\t/\npath", {"http", "", "", "host", 123, "/path", "", "", "http://host:123/path"});
207     checkURL("http://user@example.org/path3", {"http", "user", "", "example.org", 0, "/path3", "", "", "http://user@example.org/path3"});
208     checkURL("sc:/pa/pa", {"sc", "", "", "", 0, "/pa/pa", "", "", "sc:/pa/pa"});
209     checkURL("sc:/pa", {"sc", "", "", "", 0, "/pa", "", "", "sc:/pa"});
210     checkURL("sc:/pa/", {"sc", "", "", "", 0, "/pa/", "", "", "sc:/pa/"});
211     checkURL("notspecial:/notuser:notpassword@nothost", {"notspecial", "", "", "", 0, "/notuser:notpassword@nothost", "", "", "notspecial:/notuser:notpassword@nothost"});
212     checkURL("sc://pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"});
213     checkURL("http://host   \a   ", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
214     checkURL("notspecial:/a", {"notspecial", "", "", "", 0, "/a", "", "", "notspecial:/a"});
215     checkURL("notspecial:", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
216     checkURL("http:/a", {"http", "", "", "a", 0, "/", "", "", "http://a/"});
217     checkURL("http://256/", {"http", "", "", "256", 0, "/", "", "", "http://256/"});
218     checkURL("http://256./", {"http", "", "", "256.", 0, "/", "", "", "http://256./"});
219     checkURL("http://123.256/", {"http", "", "", "123.256", 0, "/", "", "", "http://123.256/"});
220     checkURL("notspecial:/a", {"notspecial", "", "", "", 0, "/a", "", "", "notspecial:/a"});
221     checkURL("notspecial:", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
222     checkURL("notspecial:/", {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"});
223
224     // This disagrees with the web platform test for http://:@www.example.com but agrees with Chrome and URL::parse,
225     // and Firefox fails the web platform test differently. Maybe the web platform test ought to be changed.
226     checkURL("http://:@host", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
227 }
228
229 static void checkRelativeURL(const String& urlString, const String& baseURLString, const ExpectedParts& parts)
230 {
231     bool wasEnabled = URLParser::enabled();
232     URLParser::setEnabled(true);
233     auto url = URL(URL(URL(), baseURLString), urlString);
234     URLParser::setEnabled(false);
235     auto oldURL = URL(URL(URL(), baseURLString), urlString);
236     URLParser::setEnabled(wasEnabled);
237
238     EXPECT_TRUE(eq(parts.protocol, url.protocol()));
239     EXPECT_TRUE(eq(parts.user, url.user()));
240     EXPECT_TRUE(eq(parts.password, url.pass()));
241     EXPECT_TRUE(eq(parts.host, url.host()));
242     EXPECT_EQ(parts.port, url.port());
243     EXPECT_TRUE(eq(parts.path, url.path()));
244     EXPECT_TRUE(eq(parts.query, url.query()));
245     EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
246     EXPECT_TRUE(eq(parts.string, url.string()));
247
248     EXPECT_TRUE(eq(parts.protocol, oldURL.protocol()));
249     EXPECT_TRUE(eq(parts.user, oldURL.user()));
250     EXPECT_TRUE(eq(parts.password, oldURL.pass()));
251     EXPECT_TRUE(eq(parts.host, oldURL.host()));
252     EXPECT_EQ(parts.port, oldURL.port());
253     EXPECT_TRUE(eq(parts.path, oldURL.path()));
254     EXPECT_TRUE(eq(parts.query, oldURL.query()));
255     EXPECT_TRUE(eq(parts.fragment, oldURL.fragmentIdentifier()));
256     EXPECT_TRUE(eq(parts.string, oldURL.string()));
257
258     EXPECT_TRUE(URLParser::allValuesEqual(url, oldURL));
259     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
260     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
261 }
262
263 TEST_F(URLParserTest, ParseRelative)
264 {
265     checkRelativeURL("/index.html", "http://webkit.org/path1/path2/", {"http", "", "", "webkit.org", 0, "/index.html", "", "", "http://webkit.org/index.html"});
266     checkRelativeURL("http://whatwg.org/index.html", "http://webkit.org/path1/path2/", {"http", "", "", "whatwg.org", 0, "/index.html", "", "", "http://whatwg.org/index.html"});
267     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"});
268     checkRelativeURL("//whatwg.org/index.html", "https://www.webkit.org/path", {"https", "", "", "whatwg.org", 0, "/index.html", "", "", "https://whatwg.org/index.html"});
269     checkRelativeURL("http://example\t.\norg", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/", "", "", "http://example.org/"});
270     checkRelativeURL("test", "file:///path1/path2", {"file", "", "", "", 0, "/path1/test", "", "", "file:///path1/test"});
271     checkRelativeURL(wideString(L"http://www.foo。bar.com"), "http://other.com/", {"http", "", "", "www.foo.bar.com", 0, "/", "", "", "http://www.foo.bar.com/"});
272     checkRelativeURL(wideString(L"sc://ñ.test/"), "about:blank", {"sc", "", "", "xn--ida.test", 0, "/", "", "", "sc://xn--ida.test/"});
273     checkRelativeURL("#fragment", "http://host/path", {"http", "", "", "host", 0, "/path", "", "fragment", "http://host/path#fragment"});
274     checkRelativeURL("?query", "http://host/path", {"http", "", "", "host", 0, "/path", "query", "", "http://host/path?query"});
275     checkRelativeURL("?query#fragment", "http://host/path", {"http", "", "", "host", 0, "/path", "query", "fragment", "http://host/path?query#fragment"});
276     checkRelativeURL(wideString(L"?β"), "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "%CE%B2", "", "http://example.org/foo/bar?%CE%B2"});
277     checkRelativeURL("?", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar?"});
278     checkRelativeURL("#", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar#"});
279     checkRelativeURL("?#", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar?#"});
280     checkRelativeURL("#?", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "?", "http://example.org/foo/bar#?"});
281     checkRelativeURL("/", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/", "", "", "http://example.org/"});
282     checkRelativeURL("http://@host", "about:blank", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
283     checkRelativeURL("http://:@host", "about:blank", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
284     checkRelativeURL("http://foo.com/\\@", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "//@", "", "", "http://foo.com//@"});
285     checkRelativeURL("\\@", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/@", "", "", "http://example.org/@"});
286     checkRelativeURL("/path3", "http://user@example.org/path1/path2", {"http", "user", "", "example.org", 0, "/path3", "", "", "http://user@example.org/path3"});
287     checkRelativeURL("", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
288     checkRelativeURL("  \a  \t\n", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
289     checkRelativeURL(":foo.com\\", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/:foo.com/", "", "", "http://example.org/foo/:foo.com/"});
290     checkRelativeURL("http:/example.com/", "about:blank", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
291     checkRelativeURL("http:example.com/", "about:blank", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
292     checkRelativeURL("http:\\\\foo.com\\", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"});
293     checkRelativeURL("http:\\\\foo.com/", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"});
294     checkRelativeURL("http:\\\\foo.com", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"});
295     checkRelativeURL("http://ExAmPlE.CoM", "http://other.com", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
296     checkRelativeURL("http:", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"});
297     checkRelativeURL("#x", "data:,", {"data", "", "", "", 0, ",", "", "x", "data:,#x"});
298     checkRelativeURL("#x", "about:blank", {"about", "", "", "", 0, "blank", "", "x", "about:blank#x"});
299     checkRelativeURL("  foo.com  ", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/foo.com", "", "", "http://example.org/foo/foo.com"});
300     checkRelativeURL(" \a baz", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/baz", "", "", "http://example.org/foo/baz"});
301     checkRelativeURL("~", "http://example.org", {"http", "", "", "example.org", 0, "/~", "", "", "http://example.org/~"});
302     checkRelativeURL("notspecial:", "about:blank", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
303     checkRelativeURL("notspecial:", "http://host", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
304     checkRelativeURL("http:", "http://host", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
305     checkRelativeURL("i", "sc:/pa/po", {"sc", "", "", "", 0, "/pa/i", "", "", "sc:/pa/i"});
306     checkRelativeURL("i    ", "sc:/pa/po", {"sc", "", "", "", 0, "/pa/i", "", "", "sc:/pa/i"});
307     checkRelativeURL("i\t\n  ", "sc:/pa/po", {"sc", "", "", "", 0, "/pa/i", "", "", "sc:/pa/i"});
308     checkRelativeURL("i", "sc://ho/pa", {"sc", "", "", "ho", 0, "/i", "", "", "sc://ho/i"});
309     checkRelativeURL("!", "sc://ho/pa", {"sc", "", "", "ho", 0, "/!", "", "", "sc://ho/!"});
310     checkRelativeURL("!", "sc:/ho/pa", {"sc", "", "", "", 0, "/ho/!", "", "", "sc:/ho/!"});
311     checkRelativeURL("notspecial:/", "about:blank", {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"});
312     checkRelativeURL("notspecial:/", "http://host", {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"});
313     checkRelativeURL("foo:/", "http://example.org/foo/bar", {"foo", "", "", "", 0, "/", "", "", "foo:/"});
314
315     // The checking of slashes in SpecialAuthoritySlashes needed to get this to pass contradicts what is in the spec,
316     // but it is included in the web platform tests.
317     checkRelativeURL("http:\\\\host\\foo", "about:blank", {"http", "", "", "host", 0, "/foo", "", "", "http://host/foo"});
318 }
319
320 static void checkURLDifferences(const String& urlString, const ExpectedParts& partsNew, const ExpectedParts& partsOld)
321 {
322     bool wasEnabled = URLParser::enabled();
323     URLParser::setEnabled(true);
324     auto url = URL(URL(), urlString);
325     URLParser::setEnabled(false);
326     auto oldURL = URL(URL(), urlString);
327     URLParser::setEnabled(wasEnabled);
328
329     EXPECT_TRUE(eq(partsNew.protocol, url.protocol()));
330     EXPECT_TRUE(eq(partsNew.user, url.user()));
331     EXPECT_TRUE(eq(partsNew.password, url.pass()));
332     EXPECT_TRUE(eq(partsNew.host, url.host()));
333     EXPECT_EQ(partsNew.port, url.port());
334     EXPECT_TRUE(eq(partsNew.path, url.path()));
335     EXPECT_TRUE(eq(partsNew.query, url.query()));
336     EXPECT_TRUE(eq(partsNew.fragment, url.fragmentIdentifier()));
337     EXPECT_TRUE(eq(partsNew.string, url.string()));
338     
339     EXPECT_TRUE(eq(partsOld.protocol, oldURL.protocol()));
340     EXPECT_TRUE(eq(partsOld.user, oldURL.user()));
341     EXPECT_TRUE(eq(partsOld.password, oldURL.pass()));
342     EXPECT_TRUE(eq(partsOld.host, oldURL.host()));
343     EXPECT_EQ(partsOld.port, oldURL.port());
344     EXPECT_TRUE(eq(partsOld.path, oldURL.path()));
345     EXPECT_TRUE(eq(partsOld.query, oldURL.query()));
346     EXPECT_TRUE(eq(partsOld.fragment, oldURL.fragmentIdentifier()));
347     EXPECT_TRUE(eq(partsOld.string, oldURL.string()));
348     
349     EXPECT_FALSE(URLParser::allValuesEqual(url, oldURL));
350     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
351     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
352 }
353
354 static void checkRelativeURLDifferences(const String& urlString, const String& baseURLString, const ExpectedParts& partsNew, const ExpectedParts& partsOld)
355 {
356     bool wasEnabled = URLParser::enabled();
357     URLParser::setEnabled(true);
358     auto url = URL(URL(URL(), baseURLString), urlString);
359     URLParser::setEnabled(false);
360     auto oldURL = URL(URL(URL(), baseURLString), urlString);
361     URLParser::setEnabled(wasEnabled);
362
363     EXPECT_TRUE(eq(partsNew.protocol, url.protocol()));
364     EXPECT_TRUE(eq(partsNew.user, url.user()));
365     EXPECT_TRUE(eq(partsNew.password, url.pass()));
366     EXPECT_TRUE(eq(partsNew.host, url.host()));
367     EXPECT_EQ(partsNew.port, url.port());
368     EXPECT_TRUE(eq(partsNew.path, url.path()));
369     EXPECT_TRUE(eq(partsNew.query, url.query()));
370     EXPECT_TRUE(eq(partsNew.fragment, url.fragmentIdentifier()));
371     EXPECT_TRUE(eq(partsNew.string, url.string()));
372     
373     EXPECT_TRUE(eq(partsOld.protocol, oldURL.protocol()));
374     EXPECT_TRUE(eq(partsOld.user, oldURL.user()));
375     EXPECT_TRUE(eq(partsOld.password, oldURL.pass()));
376     EXPECT_TRUE(eq(partsOld.host, oldURL.host()));
377     EXPECT_EQ(partsOld.port, oldURL.port());
378     EXPECT_TRUE(eq(partsOld.path, oldURL.path()));
379     EXPECT_TRUE(eq(partsOld.query, oldURL.query()));
380     EXPECT_TRUE(eq(partsOld.fragment, oldURL.fragmentIdentifier()));
381     EXPECT_TRUE(eq(partsOld.string, oldURL.string()));
382     
383     EXPECT_FALSE(URLParser::allValuesEqual(url, oldURL));
384     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
385     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
386 }
387
388 // These are differences between the new URLParser and the old URL::parse which make URLParser more standards compliant.
389 TEST_F(URLParserTest, ParserDifferences)
390 {
391     checkURLDifferences("http://127.0.1",
392         {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"},
393         {"http", "", "", "127.0.1", 0, "/", "", "", "http://127.0.1/"});
394     checkURLDifferences("http://011.11.0X11.0x011",
395         {"http", "", "", "9.11.17.17", 0, "/", "", "", "http://9.11.17.17/"},
396         {"http", "", "", "011.11.0x11.0x011", 0, "/", "", "", "http://011.11.0x11.0x011/"});
397     checkURLDifferences("http://[1234:0078:90AB:CdEf:0123:0007:89AB:0000]",
398         {"http", "", "", "[1234:78:90ab:cdef:123:7:89ab:0]", 0, "/", "", "", "http://[1234:78:90ab:cdef:123:7:89ab:0]/"},
399         {"http", "", "", "[1234:0078:90ab:cdef:0123:0007:89ab:0000]", 0, "/", "", "", "http://[1234:0078:90ab:cdef:0123:0007:89ab:0000]/"});
400     checkURLDifferences("http://[0:f:0:0:f:f:0:0]",
401         {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"},
402         {"http", "", "", "[0:f:0:0:f:f:0:0]", 0, "/", "", "", "http://[0:f:0:0:f:f:0:0]/"});
403     checkURLDifferences("http://[0:f:0:0:f:0:0:0]",
404         {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"},
405         {"http", "", "", "[0:f:0:0:f:0:0:0]", 0, "/", "", "", "http://[0:f:0:0:f:0:0:0]/"});
406     checkURLDifferences("http://[0:0:f:0:0:f:0:0]",
407         {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"},
408         {"http", "", "", "[0:0:f:0:0:f:0:0]", 0, "/", "", "", "http://[0:0:f:0:0:f:0:0]/"});
409     checkURLDifferences("http://example.com/path1/.%2e",
410         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
411         {"http", "", "", "example.com", 0, "/path1/.%2e", "", "", "http://example.com/path1/.%2e"});
412     checkURLDifferences("http://example.com/path1/.%2E",
413         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
414         {"http", "", "", "example.com", 0, "/path1/.%2E", "", "", "http://example.com/path1/.%2E"});
415     checkURLDifferences("http://example.com/path1/.%2E/",
416         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
417         {"http", "", "", "example.com", 0, "/path1/.%2E/", "", "", "http://example.com/path1/.%2E/"});
418     checkURLDifferences("http://example.com/path1/%2e.",
419         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
420         {"http", "", "", "example.com", 0, "/path1/%2e.", "", "", "http://example.com/path1/%2e."});
421     checkURLDifferences("http://example.com/path1/%2E%2e",
422         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"},
423         {"http", "", "", "example.com", 0, "/path1/%2E%2e", "", "", "http://example.com/path1/%2E%2e"});
424     checkURLDifferences("http://example.com/path1/%2e",
425         {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"},
426         {"http", "", "", "example.com", 0, "/path1/%2e", "", "", "http://example.com/path1/%2e"});
427     checkURLDifferences("http://example.com/path1/%2E",
428         {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"},
429         {"http", "", "", "example.com", 0, "/path1/%2E", "", "", "http://example.com/path1/%2E"});
430     checkURLDifferences("http://example.com/path1/%2E/",
431         {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"},
432         {"http", "", "", "example.com", 0, "/path1/%2E/", "", "", "http://example.com/path1/%2E/"});
433     checkURLDifferences("http://example.com/path1/path2/%2e?query",
434         {"http", "", "", "example.com", 0, "/path1/path2/", "query", "", "http://example.com/path1/path2/?query"},
435         {"http", "", "", "example.com", 0, "/path1/path2/%2e", "query", "", "http://example.com/path1/path2/%2e?query"});
436     checkURLDifferences("http://example.com/path1/path2/%2e%2e?query",
437         {"http", "", "", "example.com", 0, "/path1/", "query", "", "http://example.com/path1/?query"},
438         {"http", "", "", "example.com", 0, "/path1/path2/%2e%2e", "query", "", "http://example.com/path1/path2/%2e%2e?query"});
439     checkURLDifferences("http://example.com/path1/path2/%2e#fragment",
440         {"http", "", "", "example.com", 0, "/path1/path2/", "", "fragment", "http://example.com/path1/path2/#fragment"},
441         {"http", "", "", "example.com", 0, "/path1/path2/%2e", "", "fragment", "http://example.com/path1/path2/%2e#fragment"});
442     checkURLDifferences("http://example.com/path1/path2/%2e%2e#fragment",
443         {"http", "", "", "example.com", 0, "/path1/", "", "fragment", "http://example.com/path1/#fragment"},
444         {"http", "", "", "example.com", 0, "/path1/path2/%2e%2e", "", "fragment", "http://example.com/path1/path2/%2e%2e#fragment"});
445     checkURLDifferences("file://[0:a:0:0:b:c:0:0]/path",
446         {"file", "", "", "[0:a::b:c:0:0]", 0, "/path", "", "", "file://[0:a::b:c:0:0]/path"},
447         {"file", "", "", "[0:a:0:0:b:c:0:0]", 0, "/path", "", "", "file://[0:a:0:0:b:c:0:0]/path"});
448     checkRelativeURLDifferences(wideString(L"#β"), "http://example.org/foo/bar",
449         {"http", "", "", "example.org", 0, "/foo/bar", "", wideString(L"β"), wideString(L"http://example.org/foo/bar#β")},
450         {"http", "", "", "example.org", 0, "/foo/bar", "", "%CE%B2", "http://example.org/foo/bar#%CE%B2"});
451     checkURLDifferences("http://",
452         {"", "", "", "", 0, "", "", "", "http://"},
453         {"http", "", "", "", 0, "/", "", "", "http:/"});
454     checkRelativeURLDifferences("//", "https://www.webkit.org/path",
455         {"", "", "", "", 0, "", "", "", "//"},
456         {"https", "", "", "", 0, "/", "", "", "https:/"});
457     checkURLDifferences("http://127.0.0.1:65536/path",
458         {"", "", "", "", 0, "", "", "", "http://127.0.0.1:65536/path"},
459         {"http", "", "", "127.0.0.1", 65535, "/path", "", "", "http://127.0.0.1:65536/path"});
460     checkURLDifferences("http://host:65536",
461         {"", "", "", "", 0, "", "", "", "http://host:65536"},
462         {"http", "", "", "host", 65535, "/", "", "", "http://host:65536/"});
463     checkURLDifferences("http://127.0.0.1:65536",
464         {"", "", "", "", 0, "", "", "", "http://127.0.0.1:65536"},
465         {"http", "", "", "127.0.0.1", 65535, "/", "", "", "http://127.0.0.1:65536/"});
466     checkURLDifferences("http://[0:f::f:f:0:0]:65536",
467         {"", "", "", "", 0, "", "", "", "http://[0:f::f:f:0:0]:65536"},
468         {"http", "", "", "[0:f::f:f:0:0]", 65535, "/", "", "", "http://[0:f::f:f:0:0]:65536/"});
469     checkRelativeURLDifferences(":foo.com\\", "notspecial://example.org/foo/bar",
470         {"notspecial", "", "", "example.org", 0, "/foo/:foo.com\\", "", "", "notspecial://example.org/foo/:foo.com\\"},
471         {"notspecial", "", "", "example.org", 0, "/foo/:foo.com/", "", "", "notspecial://example.org/foo/:foo.com/"});
472     checkURLDifferences("sc://pa",
473         {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"},
474         {"sc", "", "", "pa", 0, "", "", "", "sc://pa"});
475     checkRelativeURLDifferences("notspecial:\\\\foo.com\\", "http://example.org/foo/bar",
476         {"notspecial", "", "", "", 0, "\\\\foo.com\\", "", "", "notspecial:\\\\foo.com\\"},
477         {"notspecial", "", "", "foo.com", 0, "/", "", "", "notspecial://foo.com/"});
478     checkRelativeURLDifferences("notspecial:\\\\foo.com/", "http://example.org/foo/bar",
479         {"notspecial", "", "", "", 0, "\\\\foo.com/", "", "", "notspecial:\\\\foo.com/"},
480         {"notspecial", "", "", "foo.com", 0, "/", "", "", "notspecial://foo.com/"});
481     checkRelativeURLDifferences("notspecial:\\\\foo.com", "http://example.org/foo/bar",
482         {"notspecial", "", "", "", 0, "\\\\foo.com", "", "", "notspecial:\\\\foo.com"},
483         {"notspecial", "", "", "foo.com", 0, "", "", "", "notspecial://foo.com"});
484     checkURLDifferences("file://notuser:notpassword@test",
485         {"", "", "", "", 0, "", "", "", "file://notuser:notpassword@test"},
486         {"file", "notuser", "notpassword", "test", 0, "/", "", "", "file://notuser:notpassword@test/"});
487     checkURLDifferences("file://notuser:notpassword@test/",
488         {"", "", "", "", 0, "", "", "", "file://notuser:notpassword@test/"},
489         {"file", "notuser", "notpassword", "test", 0, "/", "", "", "file://notuser:notpassword@test/"});
490     checkRelativeURLDifferences("http:/", "about:blank",
491         {"", "", "", "", 0, "", "", "", "http:/"},
492         {"http", "", "", "", 0, "/", "", "", "http:/"});
493     checkRelativeURLDifferences("http:", "about:blank",
494         {"http", "", "", "", 0, "", "", "", "http:"},
495         {"http", "", "", "", 0, "/", "", "", "http:/"});
496     checkRelativeURLDifferences("http:/", "http://host",
497         {"", "", "", "", 0, "", "", "", "http:/"},
498         {"http", "", "", "", 0, "/", "", "", "http:/"});
499     checkURLDifferences("http:/",
500         {"", "", "", "", 0, "", "", "", "http:/"},
501         {"http", "", "", "", 0, "/", "", "", "http:/"});
502     checkURLDifferences("http:",
503         {"http", "", "", "", 0, "", "", "", "http:"},
504         {"http", "", "", "", 0, "/", "", "", "http:/"});
505     checkRelativeURLDifferences("http:/example.com/", "http://example.org/foo/bar",
506         {"http", "", "", "example.org", 0, "/example.com/", "", "", "http://example.org/example.com/"},
507         {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"});
508     
509     // This behavior matches Chrome and Firefox, but not WebKit using URL::parse.
510     // The behavior of URL::parse is clearly wrong because reparsing file://path would make path the host.
511     // The spec is unclear.
512     checkURLDifferences("file:path",
513         {"file", "", "", "", 0, "/path", "", "", "file:///path"},
514         {"file", "", "", "", 0, "path", "", "", "file://path"});
515     checkURLDifferences("file:pAtH",
516         {"file", "", "", "", 0, "/pAtH", "", "", "file:///pAtH"},
517         {"file", "", "", "", 0, "pAtH", "", "", "file://pAtH"});
518     checkURLDifferences("file:pAtH/",
519         {"file", "", "", "", 0, "/pAtH/", "", "", "file:///pAtH/"},
520         {"file", "", "", "", 0, "pAtH/", "", "", "file://pAtH/"});
521     
522     // FIXME: Fix and test incomplete percent encoded characters in the middle and end of the input string.
523     // FIXME: Fix and test percent encoded upper case characters in the host.
524     checkURLDifferences("http://host%73",
525         {"http", "", "", "hosts", 0, "/", "", "", "http://hosts/"},
526         {"http", "", "", "host%73", 0, "/", "", "", "http://host%73/"});
527     
528     // URLParser matches Chrome and the spec, but not URL::parse or Firefox.
529     checkURLDifferences(wideString(L"http://0Xc0.0250.01"),
530         {"http", "", "", "192.168.0.1", 0, "/", "", "", "http://192.168.0.1/"},
531         {"http", "", "", "0xc0.0250.01", 0, "/", "", "", "http://0xc0.0250.01/"});
532     checkURLDifferences("http://host/path%2e.%2E",
533         {"http", "", "", "host", 0, "/path...", "", "", "http://host/path..."},
534         {"http", "", "", "host", 0, "/path%2e.%2E", "", "", "http://host/path%2e.%2E"});
535
536     checkRelativeURLDifferences(wideString(L"http://foo:💩@example.com/bar"), "http://other.com/",
537         {"http", "foo", wideString(L"💩"), "example.com", 0, "/bar", "", "", "http://foo:%F0%9F%92%A9@example.com/bar"},
538         {"", "", "", "", 0, "", "", "", wideString(L"http://foo:💩@example.com/bar")});
539     checkRelativeURLDifferences("http://&a:foo(b]c@d:2/", "http://example.org/foo/bar",
540         {"http", "&a", "foo(b]c", "d", 2, "/", "", "", "http://&a:foo(b%5Dc@d:2/"},
541         {"", "", "", "", 0, "", "", "", "http://&a:foo(b]c@d:2/"});
542     checkRelativeURLDifferences("http://`{}:`{}@h/`{}?`{}", "http://doesnotmatter/",
543         {"http", "`{}", "`{}", "h", 0, "/%60%7B%7D", "`{}", "", "http://%60%7B%7D:%60%7B%7D@h/%60%7B%7D?`{}"},
544         {"", "", "", "", 0, "", "", "", "http://`{}:`{}@h/`{}?`{}"});
545     checkURLDifferences("http://[0:f::f::f]",
546         {"", "", "", "", 0, "" , "", "", "http://[0:f::f::f]"},
547         {"http", "", "", "[0:f::f::f]", 0, "/" , "", "", "http://[0:f::f::f]/"});
548     checkURLDifferences("http://123",
549         {"http", "", "", "0.0.0.123", 0, "/", "", "", "http://0.0.0.123/"},
550         {"http", "", "", "123", 0, "/", "", "", "http://123/"});
551     checkURLDifferences("http://123.234/",
552         {"http", "", "", "123.0.0.234", 0, "/", "", "", "http://123.0.0.234/"},
553         {"http", "", "", "123.234", 0, "/", "", "", "http://123.234/"});
554     checkURLDifferences("http://123.234.012",
555         {"http", "", "", "123.234.0.10", 0, "/", "", "", "http://123.234.0.10/"},
556         {"http", "", "", "123.234.012", 0, "/", "", "", "http://123.234.012/"});
557     checkURLDifferences("http://123.234.12",
558         {"http", "", "", "123.234.0.12", 0, "/", "", "", "http://123.234.0.12/"},
559         {"http", "", "", "123.234.12", 0, "/", "", "", "http://123.234.12/"});
560     checkRelativeURLDifferences("file:c:\\foo\\bar.html", "file:///tmp/mock/path",
561         {"file", "", "", "", 0, "/c:/foo/bar.html", "", "", "file:///c:/foo/bar.html"},
562         {"file", "", "", "", 0, "/tmp/mock/c:/foo/bar.html", "", "", "file:///tmp/mock/c:/foo/bar.html"});
563     checkRelativeURLDifferences("  File:c|////foo\\bar.html", "file:///tmp/mock/path",
564         {"file", "", "", "", 0, "/c:////foo/bar.html", "", "", "file:///c:////foo/bar.html"},
565         {"file", "", "", "", 0, "/tmp/mock/c|////foo/bar.html", "", "", "file:///tmp/mock/c|////foo/bar.html"});
566     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",
567         {"file", "", "", "", 0, "/c:////foo/bar.html", "", "", "file:///c:////foo/bar.html"},
568         {"file", "", "", "", 0, "/tmp/mock/c|////foo/bar.html", "", "", "file:///tmp/mock/c|////foo/bar.html"});
569     checkRelativeURLDifferences("C|/foo/bar", "file:///tmp/mock/path",
570         {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"},
571         {"file", "", "", "", 0, "/tmp/mock/C|/foo/bar", "", "", "file:///tmp/mock/C|/foo/bar"});
572     checkRelativeURLDifferences("/C|/foo/bar", "file:///tmp/mock/path",
573         {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"},
574         {"file", "", "", "", 0, "/C|/foo/bar", "", "", "file:///C|/foo/bar"});
575     checkRelativeURLDifferences("https://@test@test@example:800/", "http://doesnotmatter/",
576         {"https", "@test@test", "", "example", 800, "/", "", "", "https://%40test%40test@example:800/"},
577         {"", "", "", "", 0, "", "", "", "https://@test@test@example:800/"});
578     checkRelativeURLDifferences("foo://", "http://example.org/foo/bar",
579         {"foo", "", "", "", 0, "/", "", "", "foo:///"},
580         {"foo", "", "", "", 0, "//", "", "", "foo://"});
581     checkURLDifferences(wideString(L"http://host?ß😍#ß😍"),
582         {"http", "", "", "host", 0, "/", "%C3%9F%F0%9F%98%8D", wideString(L"ß😍"), wideString(L"http://host/?%C3%9F%F0%9F%98%8D#ß😍")},
583         {"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"});
584 }
585
586 TEST_F(URLParserTest, DefaultPort)
587 {
588     checkURL("FtP://host:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
589     checkURL("ftp://host:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"});
590     checkURL("ftp://host:22/", {"ftp", "", "", "host", 22, "/", "", "", "ftp://host:22/"});
591     checkURLDifferences("ftp://host:21",
592         {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"},
593         {"ftp", "", "", "host", 0, "", "", "", "ftp://host"});
594     checkURLDifferences("ftp://host:22",
595         {"ftp", "", "", "host", 22, "/", "", "", "ftp://host:22/"},
596         {"ftp", "", "", "host", 22, "", "", "", "ftp://host:22"});
597     
598     checkURL("gOpHeR://host:70/", {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"});
599     checkURL("gopher://host:70/", {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"});
600     checkURL("gopher://host:71/", {"gopher", "", "", "host", 71, "/", "", "", "gopher://host:71/"});
601     // Spec, Chrome, Firefox, and URLParser have "/", URL::parse does not.
602     // Spec, Chrome, URLParser, URL::parse recognize gopher default port, Firefox does not.
603     checkURLDifferences("gopher://host:70",
604         {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"},
605         {"gopher", "", "", "host", 0, "", "", "", "gopher://host"});
606     checkURLDifferences("gopher://host:71",
607         {"gopher", "", "", "host", 71, "/", "", "", "gopher://host:71/"},
608         {"gopher", "", "", "host", 71, "", "", "", "gopher://host:71"});
609     
610     checkURL("hTtP://host:80", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
611     checkURL("http://host:80", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
612     checkURL("http://host:80/", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
613     checkURL("http://host:81", {"http", "", "", "host", 81, "/", "", "", "http://host:81/"});
614     checkURL("http://host:81/", {"http", "", "", "host", 81, "/", "", "", "http://host:81/"});
615     
616     checkURL("hTtPs://host:443", {"https", "", "", "host", 0, "/", "", "", "https://host/"});
617     checkURL("https://host:443", {"https", "", "", "host", 0, "/", "", "", "https://host/"});
618     checkURL("https://host:443/", {"https", "", "", "host", 0, "/", "", "", "https://host/"});
619     checkURL("https://host:444", {"https", "", "", "host", 444, "/", "", "", "https://host:444/"});
620     checkURL("https://host:444/", {"https", "", "", "host", 444, "/", "", "", "https://host:444/"});
621     
622     checkURL("wS://host:80/", {"ws", "", "", "host", 0, "/", "", "", "ws://host/"});
623     checkURL("ws://host:80/", {"ws", "", "", "host", 0, "/", "", "", "ws://host/"});
624     checkURL("ws://host:81/", {"ws", "", "", "host", 81, "/", "", "", "ws://host:81/"});
625     // URLParser matches Chrome and Firefox, but not URL::parse
626     checkURLDifferences("ws://host:80",
627         {"ws", "", "", "host", 0, "/", "", "", "ws://host/"},
628         {"ws", "", "", "host", 0, "", "", "", "ws://host"});
629     checkURLDifferences("ws://host:81",
630         {"ws", "", "", "host", 81, "/", "", "", "ws://host:81/"},
631         {"ws", "", "", "host", 81, "", "", "", "ws://host:81"});
632     
633     checkURL("WsS://host:443/", {"wss", "", "", "host", 0, "/", "", "", "wss://host/"});
634     checkURL("wss://host:443/", {"wss", "", "", "host", 0, "/", "", "", "wss://host/"});
635     checkURL("wss://host:444/", {"wss", "", "", "host", 444, "/", "", "", "wss://host:444/"});
636     // URLParser matches Chrome and Firefox, but not URL::parse
637     checkURLDifferences("wss://host:443",
638         {"wss", "", "", "host", 0, "/", "", "", "wss://host/"},
639         {"wss", "", "", "host", 0, "", "", "", "wss://host"});
640     checkURLDifferences("wss://host:444",
641         {"wss", "", "", "host", 444, "/", "", "", "wss://host:444/"},
642         {"wss", "", "", "host", 444, "", "", "", "wss://host:444"});
643
644     // 990 is the default ftps port in URL::parse, but it's not in the URL spec. Maybe it should be.
645     checkURL("fTpS://host:990/", {"ftps", "", "", "host", 990, "/", "", "", "ftps://host:990/"});
646     checkURL("ftps://host:990/", {"ftps", "", "", "host", 990, "/", "", "", "ftps://host:990/"});
647     checkURL("ftps://host:991/", {"ftps", "", "", "host", 991, "/", "", "", "ftps://host:991/"});
648     checkURLDifferences("ftps://host:990",
649         {"ftps", "", "", "host", 990, "/", "", "", "ftps://host:990/"},
650         {"ftps", "", "", "host", 990, "", "", "", "ftps://host:990"});
651     checkURLDifferences("ftps://host:991",
652         {"ftps", "", "", "host", 991, "/", "", "", "ftps://host:991/"},
653         {"ftps", "", "", "host", 991, "", "", "", "ftps://host:991"});
654
655     checkURL("uNkNoWn://host:80/", {"unknown", "", "", "host", 80, "/", "", "", "unknown://host:80/"});
656     checkURL("unknown://host:80/", {"unknown", "", "", "host", 80, "/", "", "", "unknown://host:80/"});
657     checkURL("unknown://host:81/", {"unknown", "", "", "host", 81, "/", "", "", "unknown://host:81/"});
658     checkURLDifferences("unknown://host:80",
659         {"unknown", "", "", "host", 80, "/", "", "", "unknown://host:80/"},
660         {"unknown", "", "", "host", 80, "", "", "", "unknown://host:80"});
661     checkURLDifferences("unknown://host:81",
662         {"unknown", "", "", "host", 81, "/", "", "", "unknown://host:81/"},
663         {"unknown", "", "", "host", 81, "", "", "", "unknown://host:81"});
664     checkURLDifferences("http://%48OsT",
665         {"http", "", "", "host", 0, "/", "", "", "http://host/"},
666         {"http", "", "", "%48ost", 0, "/", "", "", "http://%48ost/"});
667     checkURLDifferences("http://host/`",
668         {"http", "", "", "host", 0, "/%60", "", "", "http://host/%60"},
669         {"http", "", "", "host", 0, "/`", "", "", "http://host/`"});
670 }
671     
672 static void shouldFail(const String& urlString)
673 {
674     checkURL(urlString, {"", "", "", "", 0, "", "", "", urlString});
675 }
676
677 static void shouldFail(const String& urlString, const String& baseString)
678 {
679     checkRelativeURL(urlString, baseString, {"", "", "", "", 0, "", "", "", urlString});
680 }
681
682 TEST_F(URLParserTest, ParserFailures)
683 {
684     shouldFail("    ");
685     shouldFail("  \a  ");
686     shouldFail("");
687     shouldFail("http://127.0.0.1:abc");
688     shouldFail("http://host:abc");
689     shouldFail("http://a:@", "about:blank");
690     shouldFail("http://:b@", "about:blank");
691     shouldFail("http://:@", "about:blank");
692     shouldFail("http://a:@");
693     shouldFail("http://:b@");
694     shouldFail("http://@");
695     shouldFail("http://[0:f::f:f:0:0]:abc");
696     shouldFail("../i", "sc:sd");
697     shouldFail("../i", "sc:sd/sd");
698     shouldFail("/i", "sc:sd");
699     shouldFail("/i", "sc:sd/sd");
700     shouldFail("?i", "sc:sd");
701     shouldFail("?i", "sc:sd/sd");
702     shouldFail("http://example example.com", "http://other.com/");
703     shouldFail("http://[www.example.com]/", "about:blank");
704     shouldFail("http://192.168.0.1 hello", "http://other.com/");
705     shouldFail("http://[example.com]", "http://other.com/");
706     shouldFail("i", "sc:sd");
707     shouldFail("i", "sc:sd/sd");
708     shouldFail("i");
709     shouldFail("asdf");
710     shouldFail("~");
711     shouldFail("~", "about:blank");
712     shouldFail("~~~");
713 }
714
715 // These are in the spec but not in the web platform tests.
716 TEST_F(URLParserTest, AdditionalTests)
717 {
718     checkURL("about:\a\aabc", {"about", "", "", "", 0, "%07%07abc", "", "", "about:%07%07abc"});
719     checkURL("notspecial:\t\t\n\t", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
720     checkURLDifferences("notspecial\t\t\n\t:\t\t\n\t/\t\t\n\t/\t\t\n\thost",
721         {"notspecial", "", "", "host", 0, "/", "", "", "notspecial://host/"},
722         {"notspecial", "", "", "host", 0, "", "", "", "notspecial://host"});
723     checkRelativeURL("http:", "http://example.org/foo/bar?query#fragment", {"http", "", "", "example.org", 0, "/foo/bar", "query", "", "http://example.org/foo/bar?query"});
724     checkRelativeURLDifferences("ws:", "http://example.org/foo/bar",
725         {"ws", "", "", "", 0, "", "", "", "ws:"},
726         {"ws", "", "", "", 0, "s:", "", "", "ws:s:"});
727     checkRelativeURL("notspecial:", "http://example.org/foo/bar", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"});
728     
729     const wchar_t surrogateBegin = 0xD800;
730     const wchar_t validSurrogateEnd = 0xDD55;
731     const wchar_t invalidSurrogateEnd = 'A';
732     checkURL(wideString<12>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, validSurrogateEnd, '\0'}),
733         {"http", "", "", "w", 0, "/%F0%90%85%95", "", "", "http://w/%F0%90%85%95"});
734     
735     // URLParser matches Chrome and Firefox but not URL::parse.
736     checkURLDifferences(wideString<12>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, invalidSurrogateEnd}),
737         {"http", "", "", "w", 0, "/%EF%BF%BDA", "", "", "http://w/%EF%BF%BDA"},
738         {"http", "", "", "w", 0, "/%ED%A0%80A", "", "", "http://w/%ED%A0%80A"});
739     checkURLDifferences(wideString<13>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, invalidSurrogateEnd, '\0'}),
740         {"http", "", "", "w", 0, "/", "%EF%BF%BDA", "", "http://w/?%EF%BF%BDA"},
741         {"http", "", "", "w", 0, "/", "%ED%A0%80A", "", "http://w/?%ED%A0%80A"});
742     checkURLDifferences(wideString<11>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, '\0'}),
743         {"http", "", "", "w", 0, "/%EF%BF%BD", "", "", "http://w/%EF%BF%BD"},
744         {"http", "", "", "w", 0, "/%ED%A0%80", "", "", "http://w/%ED%A0%80"});
745     checkURLDifferences(wideString<12>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, '\0'}),
746         {"http", "", "", "w", 0, "/", "%EF%BF%BD", "", "http://w/?%EF%BF%BD"},
747         {"http", "", "", "w", 0, "/", "%ED%A0%80", "", "http://w/?%ED%A0%80"});
748     checkURLDifferences(wideString<13>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, ' ', '\0'}),
749         {"http", "", "", "w", 0, "/", "%EF%BF%BD", "", "http://w/?%EF%BF%BD"},
750         {"http", "", "", "w", 0, "/", "%ED%A0%80", "", "http://w/?%ED%A0%80"});
751 }
752
753 static void checkURL(const String& urlString, const TextEncoding& encoding, const ExpectedParts& parts)
754 {
755     URLParser parser;
756     auto url = parser.parse(urlString, { }, encoding);
757     EXPECT_TRUE(eq(parts.protocol, url.protocol()));
758     EXPECT_TRUE(eq(parts.user, url.user()));
759     EXPECT_TRUE(eq(parts.password, url.pass()));
760     EXPECT_TRUE(eq(parts.host, url.host()));
761     EXPECT_EQ(parts.port, url.port());
762     EXPECT_TRUE(eq(parts.path, url.path()));
763     EXPECT_TRUE(eq(parts.query, url.query()));
764     EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
765     EXPECT_TRUE(eq(parts.string, url.string()));
766 }
767
768 TEST_F(URLParserTest, QueryEncoding)
769 {
770     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#ß😍")});
771     // FIXME: Add tests with other encodings.
772 }
773
774 } // namespace TestWebKitAPI