/* * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "WTFStringUtilities.h" #include #include #include using namespace WebCore; namespace TestWebKitAPI { class URLParserTest : public testing::Test { public: void SetUp() final { WTF::initializeMainThread(); } }; struct ExpectedParts { String protocol; String user; String password; String host; unsigned short port; String path; String query; String fragment; String string; bool isInvalid() const { return protocol.isEmpty() && user.isEmpty() && password.isEmpty() && host.isEmpty() && !port && path.isEmpty() && query.isEmpty() && fragment.isEmpty(); } }; static bool eq(const String& s1, const String& s2) { EXPECT_STREQ(s1.utf8().data(), s2.utf8().data()); return s1.utf8() == s2.utf8(); } static String insertTabAtLocation(const String& string, size_t location) { ASSERT(location <= string.length()); return makeString(string.substring(0, location), "\t", string.substring(location)); } static ExpectedParts invalidParts(const String& urlStringWithTab) { return {"", "", "", "", 0, "" , "", "", urlStringWithTab}; } enum class TestTabs { No, Yes }; // Inserting tabs between surrogate pairs changes the encoded value instead of being skipped by the URLParser. const TestTabs testTabsValueForSurrogatePairs = TestTabs::No; static void checkURL(const String& urlString, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes) { auto url = URL(URL(), urlString); EXPECT_TRUE(eq(parts.protocol, url.protocol().toString())); EXPECT_TRUE(eq(parts.user, url.user())); EXPECT_TRUE(eq(parts.password, url.pass())); EXPECT_TRUE(eq(parts.host, url.host())); EXPECT_EQ(parts.port, url.port().value_or(0)); EXPECT_TRUE(eq(parts.path, url.path())); EXPECT_TRUE(eq(parts.query, url.query())); EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier())); EXPECT_TRUE(eq(parts.string, url.string())); EXPECT_TRUE(URLParser::internalValuesConsistent(url)); if (testTabs == TestTabs::No) return; for (size_t i = 0; i < urlString.length(); ++i) { String urlStringWithTab = insertTabAtLocation(urlString, i); checkURL(urlStringWithTab, parts.isInvalid() ? invalidParts(urlStringWithTab) : parts, TestTabs::No); } } static void checkRelativeURL(const String& urlString, const String& baseURLString, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes) { auto url = URL(URL(URL(), baseURLString), urlString); EXPECT_TRUE(eq(parts.protocol, url.protocol().toString())); EXPECT_TRUE(eq(parts.user, url.user())); EXPECT_TRUE(eq(parts.password, url.pass())); EXPECT_TRUE(eq(parts.host, url.host())); EXPECT_EQ(parts.port, url.port().value_or(0)); EXPECT_TRUE(eq(parts.path, url.path())); EXPECT_TRUE(eq(parts.query, url.query())); EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier())); EXPECT_TRUE(eq(parts.string, url.string())); EXPECT_TRUE(URLParser::internalValuesConsistent(url)); if (testTabs == TestTabs::No) return; for (size_t i = 0; i < urlString.length(); ++i) { String urlStringWithTab = insertTabAtLocation(urlString, i); checkRelativeURL(urlStringWithTab, baseURLString, parts.isInvalid() ? invalidParts(urlStringWithTab) : parts, TestTabs::No); } } static void checkURLDifferences(const String& urlString, const ExpectedParts& partsNew, const ExpectedParts& partsOld, TestTabs testTabs = TestTabs::Yes) { UNUSED_PARAM(partsOld); // FIXME: Remove all the old expected parts. auto url = URL(URL(), urlString); EXPECT_TRUE(eq(partsNew.protocol, url.protocol().toString())); EXPECT_TRUE(eq(partsNew.user, url.user())); EXPECT_TRUE(eq(partsNew.password, url.pass())); EXPECT_TRUE(eq(partsNew.host, url.host())); EXPECT_EQ(partsNew.port, url.port().value_or(0)); EXPECT_TRUE(eq(partsNew.path, url.path())); EXPECT_TRUE(eq(partsNew.query, url.query())); EXPECT_TRUE(eq(partsNew.fragment, url.fragmentIdentifier())); EXPECT_TRUE(eq(partsNew.string, url.string())); EXPECT_TRUE(URLParser::internalValuesConsistent(url)); if (testTabs == TestTabs::No) return; for (size_t i = 0; i < urlString.length(); ++i) { String urlStringWithTab = insertTabAtLocation(urlString, i); checkURLDifferences(urlStringWithTab, partsNew.isInvalid() ? invalidParts(urlStringWithTab) : partsNew, partsOld.isInvalid() ? invalidParts(urlStringWithTab) : partsOld, TestTabs::No); } } static void checkRelativeURLDifferences(const String& urlString, const String& baseURLString, const ExpectedParts& partsNew, const ExpectedParts& partsOld, TestTabs testTabs = TestTabs::Yes) { UNUSED_PARAM(partsOld); // FIXME: Remove all the old expected parts. auto url = URL(URL(URL(), baseURLString), urlString); EXPECT_TRUE(eq(partsNew.protocol, url.protocol().toString())); EXPECT_TRUE(eq(partsNew.user, url.user())); EXPECT_TRUE(eq(partsNew.password, url.pass())); EXPECT_TRUE(eq(partsNew.host, url.host())); EXPECT_EQ(partsNew.port, url.port().value_or(0)); EXPECT_TRUE(eq(partsNew.path, url.path())); EXPECT_TRUE(eq(partsNew.query, url.query())); EXPECT_TRUE(eq(partsNew.fragment, url.fragmentIdentifier())); EXPECT_TRUE(eq(partsNew.string, url.string())); EXPECT_TRUE(URLParser::internalValuesConsistent(url)); if (testTabs == TestTabs::No) return; for (size_t i = 0; i < urlString.length(); ++i) { String urlStringWithTab = insertTabAtLocation(urlString, i); checkRelativeURLDifferences(urlStringWithTab, baseURLString, partsNew.isInvalid() ? invalidParts(urlStringWithTab) : partsNew, partsOld.isInvalid() ? invalidParts(urlStringWithTab) : partsOld, TestTabs::No); } } static void shouldFail(const String& urlString) { checkURL(urlString, {"", "", "", "", 0, "", "", "", urlString}); } static void shouldFail(const String& urlString, const String& baseString) { checkRelativeURL(urlString, baseString, {"", "", "", "", 0, "", "", "", urlString}); } static void checkURL(const String& urlString, const TextEncoding& encoding, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes) { URLParser parser(urlString, { }, encoding); auto url = parser.result(); EXPECT_TRUE(eq(parts.protocol, url.protocol().toString())); EXPECT_TRUE(eq(parts.user, url.user())); EXPECT_TRUE(eq(parts.password, url.pass())); EXPECT_TRUE(eq(parts.host, url.host())); EXPECT_EQ(parts.port, url.port().value_or(0)); EXPECT_TRUE(eq(parts.path, url.path())); EXPECT_TRUE(eq(parts.query, url.query())); EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier())); EXPECT_TRUE(eq(parts.string, url.string())); if (testTabs == TestTabs::No) return; for (size_t i = 0; i < urlString.length(); ++i) { String urlStringWithTab = insertTabAtLocation(urlString, i); checkURL(urlStringWithTab, encoding, parts.isInvalid() ? invalidParts(urlStringWithTab) : parts, TestTabs::No); } } static void checkURL(const String& urlString, const String& baseURLString, const TextEncoding& encoding, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes) { URLParser baseParser(baseURLString, { }, encoding); URLParser parser(urlString, baseParser.result(), encoding); auto url = parser.result(); EXPECT_TRUE(eq(parts.protocol, url.protocol().toString())); EXPECT_TRUE(eq(parts.user, url.user())); EXPECT_TRUE(eq(parts.password, url.pass())); EXPECT_TRUE(eq(parts.host, url.host())); EXPECT_EQ(parts.port, url.port().value_or(0)); EXPECT_TRUE(eq(parts.path, url.path())); EXPECT_TRUE(eq(parts.query, url.query())); EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier())); EXPECT_TRUE(eq(parts.string, url.string())); if (testTabs == TestTabs::No) return; for (size_t i = 0; i < urlString.length(); ++i) { String urlStringWithTab = insertTabAtLocation(urlString, i); checkURL(urlStringWithTab, baseURLString, encoding, parts.isInvalid() ? invalidParts(urlStringWithTab) : parts, TestTabs::No); } } TEST_F(URLParserTest, Basic) { checkURL("http://user:pass@webkit.org:123/path?query#fragment", {"http", "user", "pass", "webkit.org", 123, "/path", "query", "fragment", "http://user:pass@webkit.org:123/path?query#fragment"}); checkURL("http://user:pass@webkit.org:123/path?query", {"http", "user", "pass", "webkit.org", 123, "/path", "query", "", "http://user:pass@webkit.org:123/path?query"}); checkURL("http://user:pass@webkit.org:123/path", {"http", "user", "pass", "webkit.org", 123, "/path", "", "", "http://user:pass@webkit.org:123/path"}); checkURL("http://user:pass@webkit.org:123/", {"http", "user", "pass", "webkit.org", 123, "/", "", "", "http://user:pass@webkit.org:123/"}); checkURL("http://user:pass@webkit.org:123", {"http", "user", "pass", "webkit.org", 123, "/", "", "", "http://user:pass@webkit.org:123/"}); checkURL("http://user:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); checkURL("http://user:\t\t\tpass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); checkURL("http://us\ter:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); checkURL("http://user:pa\tss@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); checkURL("http://user:pass\t@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); checkURL("http://\tuser:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); checkURL("http://user\t:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); checkURL("http://webkit.org", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"}); checkURL("http://127.0.0.1", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); checkURL("http://webkit.org/", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"}); checkURL("http://webkit.org/path1/path2/index.html", {"http", "", "", "webkit.org", 0, "/path1/path2/index.html", "", "", "http://webkit.org/path1/path2/index.html"}); checkURL("about:blank", {"about", "", "", "", 0, "blank", "", "", "about:blank"}); checkURL("about:blank?query", {"about", "", "", "", 0, "blank", "query", "", "about:blank?query"}); checkURL("about:blank#fragment", {"about", "", "", "", 0, "blank", "", "fragment", "about:blank#fragment"}); checkURL("http://[0:f::f:f:0:0]", {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"}); checkURL("http://[0:f:0:0:f::]", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"}); checkURL("http://[::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); checkURL("http://[0:f:0:0:f::]:", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"}); checkURL("http://[0:f:0:0:f::]:\t", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"}); checkURL("http://[0:f:0:0:f::]\t:", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"}); checkURL("http://\t[::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); checkURL("http://[\t::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); checkURL("http://[:\t:f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); checkURL("http://[::\tf:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); checkURL("http://[::f\t:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); checkURL("http://[::f:\t0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); checkURL("http://example.com/path1/path2/.", {"http", "", "", "example.com", 0, "/path1/path2/", "", "", "http://example.com/path1/path2/"}); checkURL("http://example.com/path1/path2/..", {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"}); checkURL("http://example.com/path1/path2/./path3", {"http", "", "", "example.com", 0, "/path1/path2/path3", "", "", "http://example.com/path1/path2/path3"}); checkURL("http://example.com/path1/path2/.\\path3", {"http", "", "", "example.com", 0, "/path1/path2/path3", "", "", "http://example.com/path1/path2/path3"}); checkURL("http://example.com/path1/path2/../path3", {"http", "", "", "example.com", 0, "/path1/path3", "", "", "http://example.com/path1/path3"}); checkURL("http://example.com/path1/path2/..\\path3", {"http", "", "", "example.com", 0, "/path1/path3", "", "", "http://example.com/path1/path3"}); checkURL("http://example.com/.", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); checkURL("http://example.com/..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); checkURL("http://example.com/./path1", {"http", "", "", "example.com", 0, "/path1", "", "", "http://example.com/path1"}); checkURL("http://example.com/../path1", {"http", "", "", "example.com", 0, "/path1", "", "", "http://example.com/path1"}); checkURL("http://example.com/../path1/../../path2/path3/../path4", {"http", "", "", "example.com", 0, "/path2/path4", "", "", "http://example.com/path2/path4"}); checkURL("http://example.com/path1/.%2", {"http", "", "", "example.com", 0, "/path1/.%2", "", "", "http://example.com/path1/.%2"}); checkURL("http://example.com/path1/%2", {"http", "", "", "example.com", 0, "/path1/%2", "", "", "http://example.com/path1/%2"}); checkURL("http://example.com/path1/%", {"http", "", "", "example.com", 0, "/path1/%", "", "", "http://example.com/path1/%"}); checkURL("http://example.com/path1/.%", {"http", "", "", "example.com", 0, "/path1/.%", "", "", "http://example.com/path1/.%"}); checkURL("http://example.com//.", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"}); checkURL("http://example.com//./", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"}); checkURL("http://example.com//.//", {"http", "", "", "example.com", 0, "///", "", "", "http://example.com///"}); checkURL("http://example.com//..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); checkURL("http://example.com//../", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); checkURL("http://example.com//..//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"}); checkURL("http://example.com//..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); checkURL("http://example.com/.//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"}); checkURL("http://example.com/..//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"}); checkURL("http://example.com/./", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); checkURL("http://example.com/../", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); checkURL("http://example.com/path1/.../path3", {"http", "", "", "example.com", 0, "/path1/.../path3", "", "", "http://example.com/path1/.../path3"}); checkURL("http://example.com/path1/...", {"http", "", "", "example.com", 0, "/path1/...", "", "", "http://example.com/path1/..."}); checkURL("http://example.com/path1/.../", {"http", "", "", "example.com", 0, "/path1/.../", "", "", "http://example.com/path1/.../"}); checkURL("http://example.com/.path1/", {"http", "", "", "example.com", 0, "/.path1/", "", "", "http://example.com/.path1/"}); checkURL("http://example.com/..path1/", {"http", "", "", "example.com", 0, "/..path1/", "", "", "http://example.com/..path1/"}); checkURL("http://example.com/path1/.path2", {"http", "", "", "example.com", 0, "/path1/.path2", "", "", "http://example.com/path1/.path2"}); checkURL("http://example.com/path1/..path2", {"http", "", "", "example.com", 0, "/path1/..path2", "", "", "http://example.com/path1/..path2"}); checkURL("http://example.com/path1/path2/.?query", {"http", "", "", "example.com", 0, "/path1/path2/", "query", "", "http://example.com/path1/path2/?query"}); checkURL("http://example.com/path1/path2/..?query", {"http", "", "", "example.com", 0, "/path1/", "query", "", "http://example.com/path1/?query"}); checkURL("http://example.com/path1/path2/.#fragment", {"http", "", "", "example.com", 0, "/path1/path2/", "", "fragment", "http://example.com/path1/path2/#fragment"}); checkURL("http://example.com/path1/path2/..#fragment", {"http", "", "", "example.com", 0, "/path1/", "", "fragment", "http://example.com/path1/#fragment"}); checkURL("file:", {"file", "", "", "", 0, "/", "", "", "file:///"}); checkURL("file:/", {"file", "", "", "", 0, "/", "", "", "file:///"}); checkURL("file://", {"file", "", "", "", 0, "/", "", "", "file:///"}); checkURL("file:///", {"file", "", "", "", 0, "/", "", "", "file:///"}); checkURL("file:////", {"file", "", "", "", 0, "//", "", "", "file:////"}); // This matches Firefox and URL::parse which I believe are correct, but not Chrome. checkURL("file:/path", {"file", "", "", "", 0, "/path", "", "", "file:///path"}); checkURL("file://host/path", {"file", "", "", "host", 0, "/path", "", "", "file://host/path"}); checkURL("file://host", {"file", "", "", "host", 0, "/", "", "", "file://host/"}); checkURL("file://host/", {"file", "", "", "host", 0, "/", "", "", "file://host/"}); checkURL("file:///path", {"file", "", "", "", 0, "/path", "", "", "file:///path"}); checkURL("file:////path", {"file", "", "", "", 0, "//path", "", "", "file:////path"}); checkURL("file://localhost/path", {"file", "", "", "", 0, "/path", "", "", "file:///path"}); checkURL("file://localhost/", {"file", "", "", "", 0, "/", "", "", "file:///"}); checkURL("file://localhost", {"file", "", "", "", 0, "/", "", "", "file:///"}); checkURL("file://lOcAlHoSt", {"file", "", "", "", 0, "/", "", "", "file:///"}); checkURL("file://lOcAlHoSt/", {"file", "", "", "", 0, "/", "", "", "file:///"}); checkURL("file:/pAtH/", {"file", "", "", "", 0, "/pAtH/", "", "", "file:///pAtH/"}); checkURL("file:/pAtH", {"file", "", "", "", 0, "/pAtH", "", "", "file:///pAtH"}); checkURL("file:?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"}); checkURL("file:#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"}); checkURL("file:?query#fragment", {"file", "", "", "", 0, "/", "query", "fragment", "file:///?query#fragment"}); checkURL("file:#fragment?notquery", {"file", "", "", "", 0, "/", "", "fragment?notquery", "file:///#fragment?notquery"}); checkURL("file:/?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"}); checkURL("file:/#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"}); checkURL("file://?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"}); checkURL("file://#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"}); checkURL("file:///?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"}); checkURL("file:///#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"}); checkURL("file:////?query", {"file", "", "", "", 0, "//", "query", "", "file:////?query"}); checkURL("file:////#fragment", {"file", "", "", "", 0, "//", "", "fragment", "file:////#fragment"}); checkURL("http://host/A b", {"http", "", "", "host", 0, "/A%20b", "", "", "http://host/A%20b"}); checkURL("http://host/a%20B", {"http", "", "", "host", 0, "/a%20B", "", "", "http://host/a%20B"}); checkURL("http://host?q=@ <>!#fragment", {"http", "", "", "host", 0, "/", "q=@%20%3C%3E!", "fragment", "http://host/?q=@%20%3C%3E!#fragment"}); checkURL("http://user:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"}); checkURL("http://user:@\thost", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"}); checkURL("http://user:\t@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"}); checkURL("http://user\t:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"}); checkURL("http://use\tr:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"}); checkURL("http://127.0.0.1:10100/path", {"http", "", "", "127.0.0.1", 10100, "/path", "", "", "http://127.0.0.1:10100/path"}); checkURL("http://127.0.0.1:/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"}); checkURL("http://127.0.0.1\t:/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"}); checkURL("http://127.0.0.1:\t/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"}); checkURL("http://127.0.0.1:/\tpath", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"}); checkURL("http://127.0.0.1:123", {"http", "", "", "127.0.0.1", 123, "/", "", "", "http://127.0.0.1:123/"}); checkURL("http://127.0.0.1:", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); checkURL("http://[0:f::f:f:0:0]:123/path", {"http", "", "", "[0:f::f:f:0:0]", 123, "/path", "", "", "http://[0:f::f:f:0:0]:123/path"}); checkURL("http://[0:f::f:f:0:0]:123", {"http", "", "", "[0:f::f:f:0:0]", 123, "/", "", "", "http://[0:f::f:f:0:0]:123/"}); checkURL("http://[0:f:0:0:f:\t:]:123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"}); checkURL("http://[0:f:0:0:f::\t]:123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"}); checkURL("http://[0:f:0:0:f::]\t:123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"}); checkURL("http://[0:f:0:0:f::]:\t123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"}); checkURL("http://[0:f:0:0:f::]:1\t23", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"}); checkURL("http://[0:f::f:f:0:0]:/path", {"http", "", "", "[0:f::f:f:0:0]", 0, "/path", "", "", "http://[0:f::f:f:0:0]/path"}); checkURL("http://[0:f::f:f:0:0]:", {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"}); checkURL("http://host:10100/path", {"http", "", "", "host", 10100, "/path", "", "", "http://host:10100/path"}); checkURL("http://host:/path", {"http", "", "", "host", 0, "/path", "", "", "http://host/path"}); checkURL("http://host:123", {"http", "", "", "host", 123, "/", "", "", "http://host:123/"}); checkURL("http://host:", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); checkURL("http://hos\tt\n:\t1\n2\t3\t/\npath", {"http", "", "", "host", 123, "/path", "", "", "http://host:123/path"}); checkURL("http://user@example.org/path3", {"http", "user", "", "example.org", 0, "/path3", "", "", "http://user@example.org/path3"}); checkURL("sc:/pa/pa", {"sc", "", "", "", 0, "/pa/pa", "", "", "sc:/pa/pa"}); checkURL("sc:/pa", {"sc", "", "", "", 0, "/pa", "", "", "sc:/pa"}); checkURL("sc:/pa/", {"sc", "", "", "", 0, "/pa/", "", "", "sc:/pa/"}); checkURL("notspecial:/notuser:notpassword@nothost", {"notspecial", "", "", "", 0, "/notuser:notpassword@nothost", "", "", "notspecial:/notuser:notpassword@nothost"}); checkURL("sc://pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"}); checkURL("sc://\tpa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"}); checkURL("sc:/\t/pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"}); checkURL("sc:\t//pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"}); checkURL("http://host \a ", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); checkURL("notspecial:/a", {"notspecial", "", "", "", 0, "/a", "", "", "notspecial:/a"}); checkURL("notspecial:", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"}); checkURL("http:/a", {"http", "", "", "a", 0, "/", "", "", "http://a/"}); checkURL("http://256../", {"http", "", "", "256..", 0, "/", "", "", "http://256../"}); checkURL("http://256..", {"http", "", "", "256..", 0, "/", "", "", "http://256../"}); checkURL("http://127..1/", {"http", "", "", "127..1", 0, "/", "", "", "http://127..1/"}); checkURL("http://127.a.0.1/", {"http", "", "", "127.a.0.1", 0, "/", "", "", "http://127.a.0.1/"}); checkURL("http://127.0.0.1/", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); checkURL("http://12\t7.0.0.1/", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); checkURL("http://127.\t0.0.1/", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); checkURL("http://./", {"http", "", "", ".", 0, "/", "", "", "http://./"}); checkURL("http://.", {"http", "", "", ".", 0, "/", "", "", "http://./"}); checkURL("notspecial:/a", {"notspecial", "", "", "", 0, "/a", "", "", "notspecial:/a"}); checkURL("notspecial:", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"}); checkURL("notspecial:/", {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"}); checkURL("data:image/png;base64,encoded-data-follows-here", {"data", "", "", "", 0, "image/png;base64,encoded-data-follows-here", "", "", "data:image/png;base64,encoded-data-follows-here"}); checkURL("data:image/png;base64,encoded/data-with-slash", {"data", "", "", "", 0, "image/png;base64,encoded/data-with-slash", "", "", "data:image/png;base64,encoded/data-with-slash"}); checkURL("about:~", {"about", "", "", "", 0, "~", "", "", "about:~"}); checkURL("https://@test@test@example:800\\path@end", {"", "", "", "", 0, "", "", "", "https://@test@test@example:800\\path@end"}); checkURL("http://www.example.com/#a\nb\rc\td", {"http", "", "", "www.example.com", 0, "/", "", "abcd", "http://www.example.com/#abcd"}); checkURL("http://[A:b:c:DE:fF:0:1:aC]/", {"http", "", "", "[a:b:c:de:ff:0:1:ac]", 0, "/", "", "", "http://[a:b:c:de:ff:0:1:ac]/"}); checkURL("http:////////user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"}); checkURL("http:////////user:@webkit.org:99#foo", {"http", "user", "", "webkit.org", 99, "/", "", "foo", "http://user@webkit.org:99/#foo"}); checkURL("http:////\t////user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"}); checkURL("http://\t//\\///user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"}); checkURL("http:/\\user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"}); checkURL("http://127.0.0.1", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); checkURLDifferences("http://127.0.0.1.", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}, {"http", "", "", "127.0.0.1.", 0, "/", "", "", "http://127.0.0.1./"}); checkURLDifferences("http://127.0.0.1./", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}, {"http", "", "", "127.0.0.1.", 0, "/", "", "", "http://127.0.0.1./"}); checkURL("http://127.0.0.1../", {"http", "", "", "127.0.0.1..", 0, "/", "", "", "http://127.0.0.1../"}); checkURLDifferences("http://0x100.0/", {"", "", "", "", 0, "", "", "", "http://0x100.0/"}, {"http", "", "", "0x100.0", 0, "/", "", "", "http://0x100.0/"}); checkURLDifferences("http://0.0.0x100.0/", {"", "", "", "", 0, "", "", "", "http://0.0.0x100.0/"}, {"http", "", "", "0.0.0x100.0", 0, "/", "", "", "http://0.0.0x100.0/"}); checkURLDifferences("http://0.0.0.0x100/", {"", "", "", "", 0, "", "", "", "http://0.0.0.0x100/"}, {"http", "", "", "0.0.0.0x100", 0, "/", "", "", "http://0.0.0.0x100/"}); checkURL("http://host:123?", {"http", "", "", "host", 123, "/", "", "", "http://host:123/?"}); checkURL("http://host:123?query", {"http", "", "", "host", 123, "/", "query", "", "http://host:123/?query"}); checkURL("http://host:123#", {"http", "", "", "host", 123, "/", "", "", "http://host:123/#"}); checkURL("http://host:123#fragment", {"http", "", "", "host", 123, "/", "", "fragment", "http://host:123/#fragment"}); checkURLDifferences("foo:////", {"foo", "", "", "", 0, "//", "", "", "foo:////"}, {"foo", "", "", "", 0, "////", "", "", "foo:////"}); checkURLDifferences("foo:///?", {"foo", "", "", "", 0, "/", "", "", "foo:///?"}, {"foo", "", "", "", 0, "///", "", "", "foo:///?"}); checkURLDifferences("foo:///#", {"foo", "", "", "", 0, "/", "", "", "foo:///#"}, {"foo", "", "", "", 0, "///", "", "", "foo:///#"}); checkURLDifferences("foo:///", {"foo", "", "", "", 0, "/", "", "", "foo:///"}, {"foo", "", "", "", 0, "///", "", "", "foo:///"}); checkURLDifferences("foo://?", {"foo", "", "", "", 0, "", "", "", "foo://?"}, {"foo", "", "", "", 0, "//", "", "", "foo://?"}); checkURLDifferences("foo://#", {"foo", "", "", "", 0, "", "", "", "foo://#"}, {"foo", "", "", "", 0, "//", "", "", "foo://#"}); checkURLDifferences("foo://", {"foo", "", "", "", 0, "", "", "", "foo://"}, {"foo", "", "", "", 0, "//", "", "", "foo://"}); checkURL("foo:/?", {"foo", "", "", "", 0, "/", "", "", "foo:/?"}); checkURL("foo:/#", {"foo", "", "", "", 0, "/", "", "", "foo:/#"}); checkURL("foo:/", {"foo", "", "", "", 0, "/", "", "", "foo:/"}); checkURL("foo:?", {"foo", "", "", "", 0, "", "", "", "foo:?"}); checkURL("foo:#", {"foo", "", "", "", 0, "", "", "", "foo:#"}); checkURLDifferences("A://", {"a", "", "", "", 0, "", "", "", "a://"}, {"a", "", "", "", 0, "//", "", "", "a://"}); checkURLDifferences("aA://", {"aa", "", "", "", 0, "", "", "", "aa://"}, {"aa", "", "", "", 0, "//", "", "", "aa://"}); checkURL(utf16String(u"foo://host/#ПП\u0007 a({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, validSurrogateEnd, '\0'}), {"http", "", "", "w", 0, "/%F0%90%85%95", "", "", "http://w/%F0%90%85%95"}, testTabsValueForSurrogatePairs); // URLParser matches Chrome and Firefox but not URL::parse. checkURLDifferences(utf16String<12>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, invalidSurrogateEnd}), {"http", "", "", "w", 0, "/%EF%BF%BDA", "", "", "http://w/%EF%BF%BDA"}, {"http", "", "", "w", 0, "/%ED%A0%80A", "", "", "http://w/%ED%A0%80A"}); checkURLDifferences(utf16String<13>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, invalidSurrogateEnd, '\0'}), {"http", "", "", "w", 0, "/", "%EF%BF%BDA", "", "http://w/?%EF%BF%BDA"}, {"http", "", "", "w", 0, "/", "%ED%A0%80A", "", "http://w/?%ED%A0%80A"}); checkURLDifferences(utf16String<11>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, '\0'}), {"http", "", "", "w", 0, "/%EF%BF%BD", "", "", "http://w/%EF%BF%BD"}, {"http", "", "", "w", 0, "/%ED%A0%80", "", "", "http://w/%ED%A0%80"}); checkURLDifferences(utf16String<12>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, '\0'}), {"http", "", "", "w", 0, "/", "%EF%BF%BD", "", "http://w/?%EF%BF%BD"}, {"http", "", "", "w", 0, "/", "%ED%A0%80", "", "http://w/?%ED%A0%80"}); checkURLDifferences(utf16String<13>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, ' ', '\0'}), {"http", "", "", "w", 0, "/", "%EF%BF%BD", "", "http://w/?%EF%BF%BD"}, {"http", "", "", "w", 0, "/", "%ED%A0%80", "", "http://w/?%ED%A0%80"}); // FIXME: Write more invalid surrogate pair tests based on feedback from https://bugs.webkit.org/show_bug.cgi?id=162105 } TEST_F(URLParserTest, QueryEncoding) { checkURL(utf16String(u"http://host?ß😍#ß😍"), UTF8Encoding(), {"http", "", "", "host", 0, "/", "%C3%9F%F0%9F%98%8D", "%C3%9F%F0%9F%98%8D", utf16String(u"http://host/?%C3%9F%F0%9F%98%8D#%C3%9F%F0%9F%98%8D")}, testTabsValueForSurrogatePairs); TextEncoding latin1(String("latin1")); checkURL("http://host/?query with%20spaces", latin1, {"http", "", "", "host", 0, "/", "query%20with%20spaces", "", "http://host/?query%20with%20spaces"}); checkURL("http://host/?query", latin1, {"http", "", "", "host", 0, "/", "query", "", "http://host/?query"}); checkURL("http://host/?\tquery", latin1, {"http", "", "", "host", 0, "/", "query", "", "http://host/?query"}); checkURL("http://host/?q\tuery", latin1, {"http", "", "", "host", 0, "/", "query", "", "http://host/?query"}); checkURL("http://host/?query with SpAcEs#fragment", latin1, {"http", "", "", "host", 0, "/", "query%20with%20SpAcEs", "fragment", "http://host/?query%20with%20SpAcEs#fragment"}); checkURL("http://host/?que\rry\t\r\n#fragment", latin1, {"http", "", "", "host", 0, "/", "query", "fragment", "http://host/?query#fragment"}); TextEncoding unrecognized(String("unrecognized invalid encoding name")); checkURL("http://host/?query", unrecognized, {"http", "", "", "host", 0, "/", "", "", "http://host/?"}); checkURL("http://host/?", unrecognized, {"http", "", "", "host", 0, "/", "", "", "http://host/?"}); TextEncoding iso88591(String("ISO-8859-1")); String withUmlauts = utf16String<4>({0xDC, 0x430, 0x451, '\0'}); checkURL(makeString("ws://host/path?", withUmlauts), iso88591, {"ws", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "", "ws://host/path?%C3%9C%D0%B0%D1%91"}); checkURL(makeString("wss://host/path?", withUmlauts), iso88591, {"wss", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "", "wss://host/path?%C3%9C%D0%B0%D1%91"}); checkURL(makeString("asdf://host/path?", withUmlauts), iso88591, {"asdf", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "", "asdf://host/path?%C3%9C%D0%B0%D1%91"}); checkURL(makeString("https://host/path?", withUmlauts), iso88591, {"https", "", "", "host", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "", "https://host/path?%DC%26%231072%3B%26%231105%3B"}); checkURL(makeString("gopher://host/path?", withUmlauts), iso88591, {"gopher", "", "", "host", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "", "gopher://host/path?%DC%26%231072%3B%26%231105%3B"}); checkURL(makeString("/path?", withUmlauts, "#fragment"), "ws://example.com/", iso88591, {"ws", "", "", "example.com", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "ws://example.com/path?%C3%9C%D0%B0%D1%91#fragment"}); checkURL(makeString("/path?", withUmlauts, "#fragment"), "wss://example.com/", iso88591, {"wss", "", "", "example.com", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "wss://example.com/path?%C3%9C%D0%B0%D1%91#fragment"}); checkURL(makeString("/path?", withUmlauts, "#fragment"), "asdf://example.com/", iso88591, {"asdf", "", "", "example.com", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "asdf://example.com/path?%C3%9C%D0%B0%D1%91#fragment"}); checkURL(makeString("/path?", withUmlauts, "#fragment"), "https://example.com/", iso88591, {"https", "", "", "example.com", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "fragment", "https://example.com/path?%DC%26%231072%3B%26%231105%3B#fragment"}); checkURL(makeString("/path?", withUmlauts, "#fragment"), "gopher://example.com/", iso88591, {"gopher", "", "", "example.com", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "fragment", "gopher://example.com/path?%DC%26%231072%3B%26%231105%3B#fragment"}); checkURL(makeString("gopher://host/path?", withUmlauts, "#fragment"), "asdf://example.com/?doesntmatter", iso88591, {"gopher", "", "", "host", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "fragment", "gopher://host/path?%DC%26%231072%3B%26%231105%3B#fragment"}); checkURL(makeString("asdf://host/path?", withUmlauts, "#fragment"), "http://example.com/?doesntmatter", iso88591, {"asdf", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "asdf://host/path?%C3%9C%D0%B0%D1%91#fragment"}); checkURL("http://host/?query=foo'bar", UTF8Encoding(), {"http", "", "", "host", 0, "/", "query=foo%27bar", "", "http://host/?query=foo%27bar"}); // FIXME: Add more tests with other encodings and things like non-ascii characters, emoji and unmatched surrogate pairs. } } // namespace TestWebKitAPI