xref: /aosp_15_r20/external/cronet/url/url_util_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2013 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #include "url/url_util.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include <stddef.h>
8*6777b538SAndroid Build Coastguard Worker 
9*6777b538SAndroid Build Coastguard Worker #include <optional>
10*6777b538SAndroid Build Coastguard Worker #include <string_view>
11*6777b538SAndroid Build Coastguard Worker 
12*6777b538SAndroid Build Coastguard Worker #include "base/test/scoped_feature_list.h"
13*6777b538SAndroid Build Coastguard Worker #include "build/build_config.h"
14*6777b538SAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest-message.h"
15*6777b538SAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest.h"
16*6777b538SAndroid Build Coastguard Worker #include "url/third_party/mozilla/url_parse.h"
17*6777b538SAndroid Build Coastguard Worker #include "url/url_canon.h"
18*6777b538SAndroid Build Coastguard Worker #include "url/url_canon_stdstring.h"
19*6777b538SAndroid Build Coastguard Worker #include "url/url_features.h"
20*6777b538SAndroid Build Coastguard Worker #include "url/url_test_utils.h"
21*6777b538SAndroid Build Coastguard Worker 
22*6777b538SAndroid Build Coastguard Worker namespace url {
23*6777b538SAndroid Build Coastguard Worker 
24*6777b538SAndroid Build Coastguard Worker class URLUtilTest : public testing::Test {
25*6777b538SAndroid Build Coastguard Worker  public:
26*6777b538SAndroid Build Coastguard Worker   URLUtilTest() = default;
27*6777b538SAndroid Build Coastguard Worker 
28*6777b538SAndroid Build Coastguard Worker   URLUtilTest(const URLUtilTest&) = delete;
29*6777b538SAndroid Build Coastguard Worker   URLUtilTest& operator=(const URLUtilTest&) = delete;
30*6777b538SAndroid Build Coastguard Worker 
31*6777b538SAndroid Build Coastguard Worker   ~URLUtilTest() override = default;
32*6777b538SAndroid Build Coastguard Worker 
33*6777b538SAndroid Build Coastguard Worker  private:
34*6777b538SAndroid Build Coastguard Worker   ScopedSchemeRegistryForTests scoped_registry_;
35*6777b538SAndroid Build Coastguard Worker };
36*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,FindAndCompareScheme)37*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, FindAndCompareScheme) {
38*6777b538SAndroid Build Coastguard Worker   Component found_scheme;
39*6777b538SAndroid Build Coastguard Worker 
40*6777b538SAndroid Build Coastguard Worker   // Simple case where the scheme is found and matches.
41*6777b538SAndroid Build Coastguard Worker   const char kStr1[] = "http://www.com/";
42*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(FindAndCompareScheme(kStr1, static_cast<int>(strlen(kStr1)),
43*6777b538SAndroid Build Coastguard Worker                                    "http", nullptr));
44*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(FindAndCompareScheme(
45*6777b538SAndroid Build Coastguard Worker       kStr1, static_cast<int>(strlen(kStr1)), "http", &found_scheme));
46*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(found_scheme == Component(0, 4));
47*6777b538SAndroid Build Coastguard Worker 
48*6777b538SAndroid Build Coastguard Worker   // A case where the scheme is found and doesn't match.
49*6777b538SAndroid Build Coastguard Worker   EXPECT_FALSE(FindAndCompareScheme(
50*6777b538SAndroid Build Coastguard Worker       kStr1, static_cast<int>(strlen(kStr1)), "https", &found_scheme));
51*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(found_scheme == Component(0, 4));
52*6777b538SAndroid Build Coastguard Worker 
53*6777b538SAndroid Build Coastguard Worker   // A case where there is no scheme.
54*6777b538SAndroid Build Coastguard Worker   const char kStr2[] = "httpfoobar";
55*6777b538SAndroid Build Coastguard Worker   EXPECT_FALSE(FindAndCompareScheme(
56*6777b538SAndroid Build Coastguard Worker       kStr2, static_cast<int>(strlen(kStr2)), "http", &found_scheme));
57*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(found_scheme == Component());
58*6777b538SAndroid Build Coastguard Worker 
59*6777b538SAndroid Build Coastguard Worker   // When there is an empty scheme, it should match the empty scheme.
60*6777b538SAndroid Build Coastguard Worker   const char kStr3[] = ":foo.com/";
61*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(FindAndCompareScheme(
62*6777b538SAndroid Build Coastguard Worker       kStr3, static_cast<int>(strlen(kStr3)), "", &found_scheme));
63*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(found_scheme == Component(0, 0));
64*6777b538SAndroid Build Coastguard Worker 
65*6777b538SAndroid Build Coastguard Worker   // But when there is no scheme, it should fail.
66*6777b538SAndroid Build Coastguard Worker   EXPECT_FALSE(FindAndCompareScheme("", 0, "", &found_scheme));
67*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(found_scheme == Component());
68*6777b538SAndroid Build Coastguard Worker 
69*6777b538SAndroid Build Coastguard Worker   // When there is a whitespace char in scheme, it should canonicalize the URL
70*6777b538SAndroid Build Coastguard Worker   // before comparison.
71*6777b538SAndroid Build Coastguard Worker   const char whtspc_str[] = " \r\n\tjav\ra\nscri\tpt:alert(1)";
72*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(FindAndCompareScheme(whtspc_str,
73*6777b538SAndroid Build Coastguard Worker                                    static_cast<int>(strlen(whtspc_str)),
74*6777b538SAndroid Build Coastguard Worker                                    "javascript", &found_scheme));
75*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(found_scheme == Component(1, 10));
76*6777b538SAndroid Build Coastguard Worker 
77*6777b538SAndroid Build Coastguard Worker   // Control characters should be stripped out on the ends, and kept in the
78*6777b538SAndroid Build Coastguard Worker   // middle.
79*6777b538SAndroid Build Coastguard Worker   const char ctrl_str[] = "\02jav\02scr\03ipt:alert(1)";
80*6777b538SAndroid Build Coastguard Worker   EXPECT_FALSE(FindAndCompareScheme(ctrl_str,
81*6777b538SAndroid Build Coastguard Worker                                     static_cast<int>(strlen(ctrl_str)),
82*6777b538SAndroid Build Coastguard Worker                                     "javascript", &found_scheme));
83*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(found_scheme == Component(1, 11));
84*6777b538SAndroid Build Coastguard Worker }
85*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,IsStandard)86*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, IsStandard) {
87*6777b538SAndroid Build Coastguard Worker   const char kHTTPScheme[] = "http";
88*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(IsStandard(kHTTPScheme, Component(0, strlen(kHTTPScheme))));
89*6777b538SAndroid Build Coastguard Worker 
90*6777b538SAndroid Build Coastguard Worker   const char kFooScheme[] = "foo";
91*6777b538SAndroid Build Coastguard Worker   EXPECT_FALSE(IsStandard(kFooScheme, Component(0, strlen(kFooScheme))));
92*6777b538SAndroid Build Coastguard Worker }
93*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,IsReferrerScheme)94*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, IsReferrerScheme) {
95*6777b538SAndroid Build Coastguard Worker   const char kHTTPScheme[] = "http";
96*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(IsReferrerScheme(kHTTPScheme, Component(0, strlen(kHTTPScheme))));
97*6777b538SAndroid Build Coastguard Worker 
98*6777b538SAndroid Build Coastguard Worker   const char kFooScheme[] = "foo";
99*6777b538SAndroid Build Coastguard Worker   EXPECT_FALSE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
100*6777b538SAndroid Build Coastguard Worker }
101*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,AddReferrerScheme)102*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, AddReferrerScheme) {
103*6777b538SAndroid Build Coastguard Worker   static const char kFooScheme[] = "foo";
104*6777b538SAndroid Build Coastguard Worker   EXPECT_FALSE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
105*6777b538SAndroid Build Coastguard Worker 
106*6777b538SAndroid Build Coastguard Worker   url::ScopedSchemeRegistryForTests scoped_registry;
107*6777b538SAndroid Build Coastguard Worker   AddReferrerScheme(kFooScheme, url::SCHEME_WITH_HOST);
108*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
109*6777b538SAndroid Build Coastguard Worker }
110*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,ShutdownCleansUpSchemes)111*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, ShutdownCleansUpSchemes) {
112*6777b538SAndroid Build Coastguard Worker   static const char kFooScheme[] = "foo";
113*6777b538SAndroid Build Coastguard Worker   EXPECT_FALSE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
114*6777b538SAndroid Build Coastguard Worker 
115*6777b538SAndroid Build Coastguard Worker   {
116*6777b538SAndroid Build Coastguard Worker     url::ScopedSchemeRegistryForTests scoped_registry;
117*6777b538SAndroid Build Coastguard Worker     AddReferrerScheme(kFooScheme, url::SCHEME_WITH_HOST);
118*6777b538SAndroid Build Coastguard Worker     EXPECT_TRUE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
119*6777b538SAndroid Build Coastguard Worker   }
120*6777b538SAndroid Build Coastguard Worker 
121*6777b538SAndroid Build Coastguard Worker   EXPECT_FALSE(IsReferrerScheme(kFooScheme, Component(0, strlen(kFooScheme))));
122*6777b538SAndroid Build Coastguard Worker }
123*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,GetStandardSchemeType)124*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, GetStandardSchemeType) {
125*6777b538SAndroid Build Coastguard Worker   url::SchemeType scheme_type;
126*6777b538SAndroid Build Coastguard Worker 
127*6777b538SAndroid Build Coastguard Worker   const char kHTTPScheme[] = "http";
128*6777b538SAndroid Build Coastguard Worker   scheme_type = url::SCHEME_WITHOUT_AUTHORITY;
129*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(GetStandardSchemeType(kHTTPScheme,
130*6777b538SAndroid Build Coastguard Worker                                     Component(0, strlen(kHTTPScheme)),
131*6777b538SAndroid Build Coastguard Worker                                     &scheme_type));
132*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ(url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION, scheme_type);
133*6777b538SAndroid Build Coastguard Worker 
134*6777b538SAndroid Build Coastguard Worker   const char kFilesystemScheme[] = "filesystem";
135*6777b538SAndroid Build Coastguard Worker   scheme_type = url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION;
136*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(GetStandardSchemeType(kFilesystemScheme,
137*6777b538SAndroid Build Coastguard Worker                                     Component(0, strlen(kFilesystemScheme)),
138*6777b538SAndroid Build Coastguard Worker                                     &scheme_type));
139*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ(url::SCHEME_WITHOUT_AUTHORITY, scheme_type);
140*6777b538SAndroid Build Coastguard Worker 
141*6777b538SAndroid Build Coastguard Worker   const char kFooScheme[] = "foo";
142*6777b538SAndroid Build Coastguard Worker   scheme_type = url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION;
143*6777b538SAndroid Build Coastguard Worker   EXPECT_FALSE(GetStandardSchemeType(kFooScheme,
144*6777b538SAndroid Build Coastguard Worker                                      Component(0, strlen(kFooScheme)),
145*6777b538SAndroid Build Coastguard Worker                                      &scheme_type));
146*6777b538SAndroid Build Coastguard Worker }
147*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,GetStandardSchemes)148*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, GetStandardSchemes) {
149*6777b538SAndroid Build Coastguard Worker   std::vector<std::string> expected = {
150*6777b538SAndroid Build Coastguard Worker       kHttpsScheme, kHttpScheme, kFileScheme,       kFtpScheme,
151*6777b538SAndroid Build Coastguard Worker       kWssScheme,   kWsScheme,   kFileSystemScheme, "foo",
152*6777b538SAndroid Build Coastguard Worker   };
153*6777b538SAndroid Build Coastguard Worker   AddStandardScheme("foo", url::SCHEME_WITHOUT_AUTHORITY);
154*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ(expected, GetStandardSchemes());
155*6777b538SAndroid Build Coastguard Worker }
156*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,ReplaceComponents)157*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, ReplaceComponents) {
158*6777b538SAndroid Build Coastguard Worker   Parsed parsed;
159*6777b538SAndroid Build Coastguard Worker   RawCanonOutputT<char> output;
160*6777b538SAndroid Build Coastguard Worker   Parsed new_parsed;
161*6777b538SAndroid Build Coastguard Worker 
162*6777b538SAndroid Build Coastguard Worker   // Check that the following calls do not cause crash
163*6777b538SAndroid Build Coastguard Worker   Replacements<char> replacements;
164*6777b538SAndroid Build Coastguard Worker   replacements.SetRef("test", Component(0, 4));
165*6777b538SAndroid Build Coastguard Worker   ReplaceComponents(nullptr, 0, parsed, replacements, nullptr, &output,
166*6777b538SAndroid Build Coastguard Worker                     &new_parsed);
167*6777b538SAndroid Build Coastguard Worker   ReplaceComponents("", 0, parsed, replacements, nullptr, &output, &new_parsed);
168*6777b538SAndroid Build Coastguard Worker   replacements.ClearRef();
169*6777b538SAndroid Build Coastguard Worker   replacements.SetHost("test", Component(0, 4));
170*6777b538SAndroid Build Coastguard Worker   ReplaceComponents(nullptr, 0, parsed, replacements, nullptr, &output,
171*6777b538SAndroid Build Coastguard Worker                     &new_parsed);
172*6777b538SAndroid Build Coastguard Worker   ReplaceComponents("", 0, parsed, replacements, nullptr, &output, &new_parsed);
173*6777b538SAndroid Build Coastguard Worker 
174*6777b538SAndroid Build Coastguard Worker   replacements.ClearHost();
175*6777b538SAndroid Build Coastguard Worker   ReplaceComponents(nullptr, 0, parsed, replacements, nullptr, &output,
176*6777b538SAndroid Build Coastguard Worker                     &new_parsed);
177*6777b538SAndroid Build Coastguard Worker   ReplaceComponents("", 0, parsed, replacements, nullptr, &output, &new_parsed);
178*6777b538SAndroid Build Coastguard Worker   ReplaceComponents(nullptr, 0, parsed, replacements, nullptr, &output,
179*6777b538SAndroid Build Coastguard Worker                     &new_parsed);
180*6777b538SAndroid Build Coastguard Worker   ReplaceComponents("", 0, parsed, replacements, nullptr, &output, &new_parsed);
181*6777b538SAndroid Build Coastguard Worker }
182*6777b538SAndroid Build Coastguard Worker 
CheckReplaceScheme(const char * base_url,const char * scheme)183*6777b538SAndroid Build Coastguard Worker static std::string CheckReplaceScheme(const char* base_url,
184*6777b538SAndroid Build Coastguard Worker                                       const char* scheme) {
185*6777b538SAndroid Build Coastguard Worker   // Make sure the input is canonicalized.
186*6777b538SAndroid Build Coastguard Worker   RawCanonOutput<32> original;
187*6777b538SAndroid Build Coastguard Worker   Parsed original_parsed;
188*6777b538SAndroid Build Coastguard Worker   Canonicalize(base_url, strlen(base_url), true, nullptr, &original,
189*6777b538SAndroid Build Coastguard Worker                &original_parsed);
190*6777b538SAndroid Build Coastguard Worker 
191*6777b538SAndroid Build Coastguard Worker   Replacements<char> replacements;
192*6777b538SAndroid Build Coastguard Worker   replacements.SetScheme(scheme, Component(0, strlen(scheme)));
193*6777b538SAndroid Build Coastguard Worker 
194*6777b538SAndroid Build Coastguard Worker   std::string output_string;
195*6777b538SAndroid Build Coastguard Worker   StdStringCanonOutput output(&output_string);
196*6777b538SAndroid Build Coastguard Worker   Parsed output_parsed;
197*6777b538SAndroid Build Coastguard Worker   ReplaceComponents(original.data(), original.length(), original_parsed,
198*6777b538SAndroid Build Coastguard Worker                     replacements, nullptr, &output, &output_parsed);
199*6777b538SAndroid Build Coastguard Worker 
200*6777b538SAndroid Build Coastguard Worker   output.Complete();
201*6777b538SAndroid Build Coastguard Worker   return output_string;
202*6777b538SAndroid Build Coastguard Worker }
203*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,ReplaceScheme)204*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, ReplaceScheme) {
205*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ("https://google.com/",
206*6777b538SAndroid Build Coastguard Worker             CheckReplaceScheme("http://google.com/", "https"));
207*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ("file://google.com/",
208*6777b538SAndroid Build Coastguard Worker             CheckReplaceScheme("http://google.com/", "file"));
209*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ("http://home/Build",
210*6777b538SAndroid Build Coastguard Worker             CheckReplaceScheme("file:///Home/Build", "http"));
211*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ("javascript:foo",
212*6777b538SAndroid Build Coastguard Worker             CheckReplaceScheme("about:foo", "javascript"));
213*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ("://google.com/",
214*6777b538SAndroid Build Coastguard Worker             CheckReplaceScheme("http://google.com/", ""));
215*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ("http://google.com/",
216*6777b538SAndroid Build Coastguard Worker             CheckReplaceScheme("about:google.com", "http"));
217*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ("http:", CheckReplaceScheme("", "http"));
218*6777b538SAndroid Build Coastguard Worker 
219*6777b538SAndroid Build Coastguard Worker #ifdef WIN32
220*6777b538SAndroid Build Coastguard Worker   // Magic Windows drive letter behavior when converting to a file URL.
221*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ("file:///E:/foo/",
222*6777b538SAndroid Build Coastguard Worker             CheckReplaceScheme("http://localhost/e:foo/", "file"));
223*6777b538SAndroid Build Coastguard Worker #endif
224*6777b538SAndroid Build Coastguard Worker 
225*6777b538SAndroid Build Coastguard Worker   // This will probably change to "about://google.com/" when we fix
226*6777b538SAndroid Build Coastguard Worker   // http://crbug.com/160 which should also be an acceptable result.
227*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ("about://google.com/",
228*6777b538SAndroid Build Coastguard Worker             CheckReplaceScheme("http://google.com/", "about"));
229*6777b538SAndroid Build Coastguard Worker 
230*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ("http://example.com/%20hello%20#%20world",
231*6777b538SAndroid Build Coastguard Worker             CheckReplaceScheme("myscheme:example.com/ hello # world ", "http"));
232*6777b538SAndroid Build Coastguard Worker }
233*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,DecodeURLEscapeSequences)234*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, DecodeURLEscapeSequences) {
235*6777b538SAndroid Build Coastguard Worker   struct DecodeCase {
236*6777b538SAndroid Build Coastguard Worker     const char* input;
237*6777b538SAndroid Build Coastguard Worker     const char* output;
238*6777b538SAndroid Build Coastguard Worker   } decode_cases[] = {
239*6777b538SAndroid Build Coastguard Worker       {"hello, world", "hello, world"},
240*6777b538SAndroid Build Coastguard Worker       {"%01%02%03%04%05%06%07%08%09%0a%0B%0C%0D%0e%0f/",
241*6777b538SAndroid Build Coastguard Worker        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0B\x0C\x0D\x0e\x0f/"},
242*6777b538SAndroid Build Coastguard Worker       {"%10%11%12%13%14%15%16%17%18%19%1a%1B%1C%1D%1e%1f/",
243*6777b538SAndroid Build Coastguard Worker        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1B\x1C\x1D\x1e\x1f/"},
244*6777b538SAndroid Build Coastguard Worker       {"%20%21%22%23%24%25%26%27%28%29%2a%2B%2C%2D%2e%2f/",
245*6777b538SAndroid Build Coastguard Worker        " !\"#$%&'()*+,-.//"},
246*6777b538SAndroid Build Coastguard Worker       {"%30%31%32%33%34%35%36%37%38%39%3a%3B%3C%3D%3e%3f/",
247*6777b538SAndroid Build Coastguard Worker        "0123456789:;<=>?/"},
248*6777b538SAndroid Build Coastguard Worker       {"%40%41%42%43%44%45%46%47%48%49%4a%4B%4C%4D%4e%4f/",
249*6777b538SAndroid Build Coastguard Worker        "@ABCDEFGHIJKLMNO/"},
250*6777b538SAndroid Build Coastguard Worker       {"%50%51%52%53%54%55%56%57%58%59%5a%5B%5C%5D%5e%5f/",
251*6777b538SAndroid Build Coastguard Worker        "PQRSTUVWXYZ[\\]^_/"},
252*6777b538SAndroid Build Coastguard Worker       {"%60%61%62%63%64%65%66%67%68%69%6a%6B%6C%6D%6e%6f/",
253*6777b538SAndroid Build Coastguard Worker        "`abcdefghijklmno/"},
254*6777b538SAndroid Build Coastguard Worker       {"%70%71%72%73%74%75%76%77%78%79%7a%7B%7C%7D%7e%7f/",
255*6777b538SAndroid Build Coastguard Worker        "pqrstuvwxyz{|}~\x7f/"},
256*6777b538SAndroid Build Coastguard Worker       {"%e4%bd%a0%e5%a5%bd", "\xe4\xbd\xa0\xe5\xa5\xbd"},
257*6777b538SAndroid Build Coastguard Worker       // U+FFFF (Noncharacter) should not be replaced with U+FFFD (Replacement
258*6777b538SAndroid Build Coastguard Worker       // Character) (http://crbug.com/1416021)
259*6777b538SAndroid Build Coastguard Worker       {"%ef%bf%bf", "\xef\xbf\xbf"},
260*6777b538SAndroid Build Coastguard Worker       // U+FDD0 (Noncharacter)
261*6777b538SAndroid Build Coastguard Worker       {"%ef%b7%90", "\xef\xb7\x90"},
262*6777b538SAndroid Build Coastguard Worker       // U+FFFD (Replacement Character)
263*6777b538SAndroid Build Coastguard Worker       {"%ef%bf%bd", "\xef\xbf\xbd"},
264*6777b538SAndroid Build Coastguard Worker   };
265*6777b538SAndroid Build Coastguard Worker 
266*6777b538SAndroid Build Coastguard Worker   for (const auto& decode_case : decode_cases) {
267*6777b538SAndroid Build Coastguard Worker     RawCanonOutputT<char16_t> output;
268*6777b538SAndroid Build Coastguard Worker     DecodeURLEscapeSequences(decode_case.input,
269*6777b538SAndroid Build Coastguard Worker                              DecodeURLMode::kUTF8OrIsomorphic, &output);
270*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(decode_case.output, base::UTF16ToUTF8(std::u16string(
271*6777b538SAndroid Build Coastguard Worker                                       output.data(), output.length())));
272*6777b538SAndroid Build Coastguard Worker 
273*6777b538SAndroid Build Coastguard Worker     RawCanonOutputT<char16_t> output_utf8;
274*6777b538SAndroid Build Coastguard Worker     DecodeURLEscapeSequences(decode_case.input, DecodeURLMode::kUTF8,
275*6777b538SAndroid Build Coastguard Worker                              &output_utf8);
276*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(decode_case.output,
277*6777b538SAndroid Build Coastguard Worker               base::UTF16ToUTF8(
278*6777b538SAndroid Build Coastguard Worker                   std::u16string(output_utf8.data(), output_utf8.length())));
279*6777b538SAndroid Build Coastguard Worker   }
280*6777b538SAndroid Build Coastguard Worker 
281*6777b538SAndroid Build Coastguard Worker   // Our decode should decode %00
282*6777b538SAndroid Build Coastguard Worker   const char zero_input[] = "%00";
283*6777b538SAndroid Build Coastguard Worker   RawCanonOutputT<char16_t> zero_output;
284*6777b538SAndroid Build Coastguard Worker   DecodeURLEscapeSequences(zero_input, DecodeURLMode::kUTF8, &zero_output);
285*6777b538SAndroid Build Coastguard Worker   EXPECT_NE("%00", base::UTF16ToUTF8(std::u16string(zero_output.data(),
286*6777b538SAndroid Build Coastguard Worker                                                     zero_output.length())));
287*6777b538SAndroid Build Coastguard Worker 
288*6777b538SAndroid Build Coastguard Worker   // Test the error behavior for invalid UTF-8.
289*6777b538SAndroid Build Coastguard Worker   struct Utf8DecodeCase {
290*6777b538SAndroid Build Coastguard Worker     const char* input;
291*6777b538SAndroid Build Coastguard Worker     std::vector<char16_t> expected_iso;
292*6777b538SAndroid Build Coastguard Worker     std::vector<char16_t> expected_utf8;
293*6777b538SAndroid Build Coastguard Worker   } utf8_decode_cases[] = {
294*6777b538SAndroid Build Coastguard Worker       // %e5%a5%bd is a valid UTF-8 sequence. U+597D
295*6777b538SAndroid Build Coastguard Worker       {"%e4%a0%e5%a5%bd",
296*6777b538SAndroid Build Coastguard Worker        {0x00e4, 0x00a0, 0x00e5, 0x00a5, 0x00bd, 0},
297*6777b538SAndroid Build Coastguard Worker        {0xfffd, 0x597d, 0}},
298*6777b538SAndroid Build Coastguard Worker       {"%e5%a5%bd%e4%a0",
299*6777b538SAndroid Build Coastguard Worker        {0x00e5, 0x00a5, 0x00bd, 0x00e4, 0x00a0, 0},
300*6777b538SAndroid Build Coastguard Worker        {0x597d, 0xfffd, 0}},
301*6777b538SAndroid Build Coastguard Worker       {"%e4%a0%e5%bd",
302*6777b538SAndroid Build Coastguard Worker        {0x00e4, 0x00a0, 0x00e5, 0x00bd, 0},
303*6777b538SAndroid Build Coastguard Worker        {0xfffd, 0xfffd, 0}},
304*6777b538SAndroid Build Coastguard Worker   };
305*6777b538SAndroid Build Coastguard Worker 
306*6777b538SAndroid Build Coastguard Worker   for (const auto& utf8_decode_case : utf8_decode_cases) {
307*6777b538SAndroid Build Coastguard Worker     RawCanonOutputT<char16_t> output_iso;
308*6777b538SAndroid Build Coastguard Worker     DecodeURLEscapeSequences(utf8_decode_case.input,
309*6777b538SAndroid Build Coastguard Worker                              DecodeURLMode::kUTF8OrIsomorphic, &output_iso);
310*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(std::u16string(utf8_decode_case.expected_iso.data()),
311*6777b538SAndroid Build Coastguard Worker               std::u16string(output_iso.data(), output_iso.length()));
312*6777b538SAndroid Build Coastguard Worker 
313*6777b538SAndroid Build Coastguard Worker     RawCanonOutputT<char16_t> output_utf8;
314*6777b538SAndroid Build Coastguard Worker     DecodeURLEscapeSequences(utf8_decode_case.input, DecodeURLMode::kUTF8,
315*6777b538SAndroid Build Coastguard Worker                              &output_utf8);
316*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(std::u16string(utf8_decode_case.expected_utf8.data()),
317*6777b538SAndroid Build Coastguard Worker               std::u16string(output_utf8.data(), output_utf8.length()));
318*6777b538SAndroid Build Coastguard Worker   }
319*6777b538SAndroid Build Coastguard Worker }
320*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,TestEncodeURIComponent)321*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, TestEncodeURIComponent) {
322*6777b538SAndroid Build Coastguard Worker   struct EncodeCase {
323*6777b538SAndroid Build Coastguard Worker     const char* input;
324*6777b538SAndroid Build Coastguard Worker     const char* output;
325*6777b538SAndroid Build Coastguard Worker   } encode_cases[] = {
326*6777b538SAndroid Build Coastguard Worker     {"hello, world", "hello%2C%20world"},
327*6777b538SAndroid Build Coastguard Worker     {"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
328*6777b538SAndroid Build Coastguard Worker      "%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F"},
329*6777b538SAndroid Build Coastguard Worker     {"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
330*6777b538SAndroid Build Coastguard Worker      "%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F"},
331*6777b538SAndroid Build Coastguard Worker     {" !\"#$%&'()*+,-./",
332*6777b538SAndroid Build Coastguard Worker      "%20!%22%23%24%25%26%27()*%2B%2C-.%2F"},
333*6777b538SAndroid Build Coastguard Worker     {"0123456789:;<=>?",
334*6777b538SAndroid Build Coastguard Worker      "0123456789%3A%3B%3C%3D%3E%3F"},
335*6777b538SAndroid Build Coastguard Worker     {"@ABCDEFGHIJKLMNO",
336*6777b538SAndroid Build Coastguard Worker      "%40ABCDEFGHIJKLMNO"},
337*6777b538SAndroid Build Coastguard Worker     {"PQRSTUVWXYZ[\\]^_",
338*6777b538SAndroid Build Coastguard Worker      "PQRSTUVWXYZ%5B%5C%5D%5E_"},
339*6777b538SAndroid Build Coastguard Worker     {"`abcdefghijklmno",
340*6777b538SAndroid Build Coastguard Worker      "%60abcdefghijklmno"},
341*6777b538SAndroid Build Coastguard Worker     {"pqrstuvwxyz{|}~\x7f",
342*6777b538SAndroid Build Coastguard Worker      "pqrstuvwxyz%7B%7C%7D~%7F"},
343*6777b538SAndroid Build Coastguard Worker   };
344*6777b538SAndroid Build Coastguard Worker 
345*6777b538SAndroid Build Coastguard Worker   for (const auto& encode_case : encode_cases) {
346*6777b538SAndroid Build Coastguard Worker     RawCanonOutputT<char> buffer;
347*6777b538SAndroid Build Coastguard Worker     EncodeURIComponent(encode_case.input, &buffer);
348*6777b538SAndroid Build Coastguard Worker     std::string output(buffer.data(), buffer.length());
349*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(encode_case.output, output);
350*6777b538SAndroid Build Coastguard Worker   }
351*6777b538SAndroid Build Coastguard Worker }
352*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,TestResolveRelativeWithNonStandardBase)353*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, TestResolveRelativeWithNonStandardBase) {
354*6777b538SAndroid Build Coastguard Worker   // This tests non-standard (in the sense that IsStandard() == false)
355*6777b538SAndroid Build Coastguard Worker   // hierarchical schemes.
356*6777b538SAndroid Build Coastguard Worker   struct ResolveRelativeCase {
357*6777b538SAndroid Build Coastguard Worker     const char* base;
358*6777b538SAndroid Build Coastguard Worker     const char* rel;
359*6777b538SAndroid Build Coastguard Worker     bool is_valid;
360*6777b538SAndroid Build Coastguard Worker     const char* out;
361*6777b538SAndroid Build Coastguard Worker   } resolve_non_standard_cases[] = {
362*6777b538SAndroid Build Coastguard Worker       // Resolving a relative path against a non-hierarchical URL should fail.
363*6777b538SAndroid Build Coastguard Worker       {"scheme:opaque_data", "/path", false, ""},
364*6777b538SAndroid Build Coastguard Worker       // Resolving a relative path against a non-standard authority-based base
365*6777b538SAndroid Build Coastguard Worker       // URL doesn't alter the authority section.
366*6777b538SAndroid Build Coastguard Worker       {"scheme://Authority/", "../path", true, "scheme://Authority/path"},
367*6777b538SAndroid Build Coastguard Worker       // A non-standard hierarchical base is resolved with path URL
368*6777b538SAndroid Build Coastguard Worker       // canonicalization rules.
369*6777b538SAndroid Build Coastguard Worker       {"data:/Blah:Blah/", "file.html", true, "data:/Blah:Blah/file.html"},
370*6777b538SAndroid Build Coastguard Worker       {"data:/Path/../part/part2", "file.html", true,
371*6777b538SAndroid Build Coastguard Worker        "data:/Path/../part/file.html"},
372*6777b538SAndroid Build Coastguard Worker       {"data://text/html,payload", "//user:pass@host:33////payload22", true,
373*6777b538SAndroid Build Coastguard Worker        "data://user:pass@host:33////payload22"},
374*6777b538SAndroid Build Coastguard Worker       // Path URL canonicalization rules also apply to non-standard authority-
375*6777b538SAndroid Build Coastguard Worker       // based URLs.
376*6777b538SAndroid Build Coastguard Worker       {"custom://Authority/", "file.html", true,
377*6777b538SAndroid Build Coastguard Worker        "custom://Authority/file.html"},
378*6777b538SAndroid Build Coastguard Worker       {"custom://Authority/", "other://Auth/", true, "other://Auth/"},
379*6777b538SAndroid Build Coastguard Worker       {"custom://Authority/", "../../file.html", true,
380*6777b538SAndroid Build Coastguard Worker        "custom://Authority/file.html"},
381*6777b538SAndroid Build Coastguard Worker       {"custom://Authority/path/", "file.html", true,
382*6777b538SAndroid Build Coastguard Worker        "custom://Authority/path/file.html"},
383*6777b538SAndroid Build Coastguard Worker       {"custom://Authority:NoCanon/path/", "file.html", true,
384*6777b538SAndroid Build Coastguard Worker        "custom://Authority:NoCanon/path/file.html"},
385*6777b538SAndroid Build Coastguard Worker       // A path with an authority section gets canonicalized under standard URL
386*6777b538SAndroid Build Coastguard Worker       // rules, even though the base was non-standard.
387*6777b538SAndroid Build Coastguard Worker       {"content://content.Provider/", "//other.Provider", true,
388*6777b538SAndroid Build Coastguard Worker        "content://other.provider/"},
389*6777b538SAndroid Build Coastguard Worker       // Resolving an absolute URL doesn't cause canonicalization of the
390*6777b538SAndroid Build Coastguard Worker       // result.
391*6777b538SAndroid Build Coastguard Worker       {"about:blank", "custom://Authority", true, "custom://Authority"},
392*6777b538SAndroid Build Coastguard Worker       // Fragment URLs can be resolved against a non-standard base.
393*6777b538SAndroid Build Coastguard Worker       {"scheme://Authority/path", "#fragment", true,
394*6777b538SAndroid Build Coastguard Worker        "scheme://Authority/path#fragment"},
395*6777b538SAndroid Build Coastguard Worker       {"scheme://Authority/", "#fragment", true,
396*6777b538SAndroid Build Coastguard Worker        "scheme://Authority/#fragment"},
397*6777b538SAndroid Build Coastguard Worker       // Test resolving a fragment (only) against any kind of base-URL.
398*6777b538SAndroid Build Coastguard Worker       {"about:blank", "#id42", true, "about:blank#id42"},
399*6777b538SAndroid Build Coastguard Worker       {"about:blank", " #id42", true, "about:blank#id42"},
400*6777b538SAndroid Build Coastguard Worker       {"about:blank#oldfrag", "#newfrag", true, "about:blank#newfrag"},
401*6777b538SAndroid Build Coastguard Worker       {"about:blank", " #id:42", true, "about:blank#id:42"},
402*6777b538SAndroid Build Coastguard Worker       // A surprising side effect of allowing fragments to resolve against
403*6777b538SAndroid Build Coastguard Worker       // any URL scheme is we might break javascript: URLs by doing so...
404*6777b538SAndroid Build Coastguard Worker       {"javascript:alert('foo#bar')", "#badfrag", true,
405*6777b538SAndroid Build Coastguard Worker        "javascript:alert('foo#badfrag"},
406*6777b538SAndroid Build Coastguard Worker   };
407*6777b538SAndroid Build Coastguard Worker 
408*6777b538SAndroid Build Coastguard Worker   for (const auto& test : resolve_non_standard_cases) {
409*6777b538SAndroid Build Coastguard Worker     SCOPED_TRACE(testing::Message()
410*6777b538SAndroid Build Coastguard Worker                  << "base: " << test.base << ", rel: " << test.rel);
411*6777b538SAndroid Build Coastguard Worker 
412*6777b538SAndroid Build Coastguard Worker     Parsed base_parsed;
413*6777b538SAndroid Build Coastguard Worker     if (url::IsUsingStandardCompliantNonSpecialSchemeURLParsing()) {
414*6777b538SAndroid Build Coastguard Worker       ParseNonSpecialURL(test.base, strlen(test.base), &base_parsed);
415*6777b538SAndroid Build Coastguard Worker     } else {
416*6777b538SAndroid Build Coastguard Worker       ParsePathURL(test.base, strlen(test.base), /*trim_path_end=*/true,
417*6777b538SAndroid Build Coastguard Worker                    &base_parsed);
418*6777b538SAndroid Build Coastguard Worker     }
419*6777b538SAndroid Build Coastguard Worker 
420*6777b538SAndroid Build Coastguard Worker     std::string resolved;
421*6777b538SAndroid Build Coastguard Worker     StdStringCanonOutput output(&resolved);
422*6777b538SAndroid Build Coastguard Worker     Parsed resolved_parsed;
423*6777b538SAndroid Build Coastguard Worker     bool valid =
424*6777b538SAndroid Build Coastguard Worker         ResolveRelative(test.base, strlen(test.base), base_parsed, test.rel,
425*6777b538SAndroid Build Coastguard Worker                         strlen(test.rel), nullptr, &output, &resolved_parsed);
426*6777b538SAndroid Build Coastguard Worker     output.Complete();
427*6777b538SAndroid Build Coastguard Worker 
428*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(test.is_valid, valid);
429*6777b538SAndroid Build Coastguard Worker     if (test.is_valid && valid) {
430*6777b538SAndroid Build Coastguard Worker       EXPECT_EQ(test.out, resolved);
431*6777b538SAndroid Build Coastguard Worker     }
432*6777b538SAndroid Build Coastguard Worker   }
433*6777b538SAndroid Build Coastguard Worker }
434*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,PotentiallyDanglingMarkup)435*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, PotentiallyDanglingMarkup) {
436*6777b538SAndroid Build Coastguard Worker   struct ResolveRelativeCase {
437*6777b538SAndroid Build Coastguard Worker     const char* base;
438*6777b538SAndroid Build Coastguard Worker     const char* rel;
439*6777b538SAndroid Build Coastguard Worker     bool potentially_dangling_markup;
440*6777b538SAndroid Build Coastguard Worker     const char* out;
441*6777b538SAndroid Build Coastguard Worker   } cases[] = {
442*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/path<", false, "https://example.com/path%3C"},
443*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "\n/path<", true, "https://example.com/path%3C"},
444*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "\r/path<", true, "https://example.com/path%3C"},
445*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "\t/path<", true, "https://example.com/path%3C"},
446*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/pa\nth<", true, "https://example.com/path%3C"},
447*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/pa\rth<", true, "https://example.com/path%3C"},
448*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/pa\tth<", true, "https://example.com/path%3C"},
449*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/path\n<", true, "https://example.com/path%3C"},
450*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/path\r<", true, "https://example.com/path%3C"},
451*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/path\r<", true, "https://example.com/path%3C"},
452*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "\n/<path", true, "https://example.com/%3Cpath"},
453*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "\r/<path", true, "https://example.com/%3Cpath"},
454*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "\t/<path", true, "https://example.com/%3Cpath"},
455*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/<pa\nth", true, "https://example.com/%3Cpath"},
456*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/<pa\rth", true, "https://example.com/%3Cpath"},
457*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/<pa\tth", true, "https://example.com/%3Cpath"},
458*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/<path\n", true, "https://example.com/%3Cpath"},
459*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/<path\r", true, "https://example.com/%3Cpath"},
460*6777b538SAndroid Build Coastguard Worker       {"https://example.com/", "/<path\r", true, "https://example.com/%3Cpath"},
461*6777b538SAndroid Build Coastguard Worker   };
462*6777b538SAndroid Build Coastguard Worker 
463*6777b538SAndroid Build Coastguard Worker   for (const auto& test : cases) {
464*6777b538SAndroid Build Coastguard Worker     SCOPED_TRACE(::testing::Message() << test.base << ", " << test.rel);
465*6777b538SAndroid Build Coastguard Worker     Parsed base_parsed;
466*6777b538SAndroid Build Coastguard Worker     ParseStandardURL(test.base, strlen(test.base), &base_parsed);
467*6777b538SAndroid Build Coastguard Worker 
468*6777b538SAndroid Build Coastguard Worker     std::string resolved;
469*6777b538SAndroid Build Coastguard Worker     StdStringCanonOutput output(&resolved);
470*6777b538SAndroid Build Coastguard Worker     Parsed resolved_parsed;
471*6777b538SAndroid Build Coastguard Worker     bool valid =
472*6777b538SAndroid Build Coastguard Worker         ResolveRelative(test.base, strlen(test.base), base_parsed, test.rel,
473*6777b538SAndroid Build Coastguard Worker                         strlen(test.rel), nullptr, &output, &resolved_parsed);
474*6777b538SAndroid Build Coastguard Worker     ASSERT_TRUE(valid);
475*6777b538SAndroid Build Coastguard Worker     output.Complete();
476*6777b538SAndroid Build Coastguard Worker 
477*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(test.potentially_dangling_markup,
478*6777b538SAndroid Build Coastguard Worker               resolved_parsed.potentially_dangling_markup);
479*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(test.out, resolved);
480*6777b538SAndroid Build Coastguard Worker   }
481*6777b538SAndroid Build Coastguard Worker }
482*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,PotentiallyDanglingMarkupAfterReplacement)483*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, PotentiallyDanglingMarkupAfterReplacement) {
484*6777b538SAndroid Build Coastguard Worker   // Parse a URL with potentially dangling markup.
485*6777b538SAndroid Build Coastguard Worker   Parsed original_parsed;
486*6777b538SAndroid Build Coastguard Worker   RawCanonOutput<32> original;
487*6777b538SAndroid Build Coastguard Worker   const char* url = "htt\nps://example.com/<path";
488*6777b538SAndroid Build Coastguard Worker   Canonicalize(url, strlen(url), false, nullptr, &original, &original_parsed);
489*6777b538SAndroid Build Coastguard Worker   ASSERT_TRUE(original_parsed.potentially_dangling_markup);
490*6777b538SAndroid Build Coastguard Worker 
491*6777b538SAndroid Build Coastguard Worker   // Perform a replacement, and validate that the potentially_dangling_markup
492*6777b538SAndroid Build Coastguard Worker   // flag carried over to the new Parsed object.
493*6777b538SAndroid Build Coastguard Worker   Replacements<char> replacements;
494*6777b538SAndroid Build Coastguard Worker   replacements.ClearRef();
495*6777b538SAndroid Build Coastguard Worker   Parsed replaced_parsed;
496*6777b538SAndroid Build Coastguard Worker   RawCanonOutput<32> replaced;
497*6777b538SAndroid Build Coastguard Worker   ReplaceComponents(original.data(), original.length(), original_parsed,
498*6777b538SAndroid Build Coastguard Worker                     replacements, nullptr, &replaced, &replaced_parsed);
499*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(replaced_parsed.potentially_dangling_markup);
500*6777b538SAndroid Build Coastguard Worker }
501*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,PotentiallyDanglingMarkupAfterSchemeOnlyReplacement)502*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, PotentiallyDanglingMarkupAfterSchemeOnlyReplacement) {
503*6777b538SAndroid Build Coastguard Worker   // Parse a URL with potentially dangling markup.
504*6777b538SAndroid Build Coastguard Worker   Parsed original_parsed;
505*6777b538SAndroid Build Coastguard Worker   RawCanonOutput<32> original;
506*6777b538SAndroid Build Coastguard Worker   const char* url = "http://example.com/\n/<path";
507*6777b538SAndroid Build Coastguard Worker   Canonicalize(url, strlen(url), false, nullptr, &original, &original_parsed);
508*6777b538SAndroid Build Coastguard Worker   ASSERT_TRUE(original_parsed.potentially_dangling_markup);
509*6777b538SAndroid Build Coastguard Worker 
510*6777b538SAndroid Build Coastguard Worker   // Perform a replacement, and validate that the potentially_dangling_markup
511*6777b538SAndroid Build Coastguard Worker   // flag carried over to the new Parsed object.
512*6777b538SAndroid Build Coastguard Worker   Replacements<char> replacements;
513*6777b538SAndroid Build Coastguard Worker   const char* new_scheme = "https";
514*6777b538SAndroid Build Coastguard Worker   replacements.SetScheme(new_scheme, Component(0, strlen(new_scheme)));
515*6777b538SAndroid Build Coastguard Worker   Parsed replaced_parsed;
516*6777b538SAndroid Build Coastguard Worker   RawCanonOutput<32> replaced;
517*6777b538SAndroid Build Coastguard Worker   ReplaceComponents(original.data(), original.length(), original_parsed,
518*6777b538SAndroid Build Coastguard Worker                     replacements, nullptr, &replaced, &replaced_parsed);
519*6777b538SAndroid Build Coastguard Worker   EXPECT_TRUE(replaced_parsed.potentially_dangling_markup);
520*6777b538SAndroid Build Coastguard Worker }
521*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,TestDomainIs)522*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, TestDomainIs) {
523*6777b538SAndroid Build Coastguard Worker   const struct {
524*6777b538SAndroid Build Coastguard Worker     const char* canonicalized_host;
525*6777b538SAndroid Build Coastguard Worker     const char* lower_ascii_domain;
526*6777b538SAndroid Build Coastguard Worker     bool expected_domain_is;
527*6777b538SAndroid Build Coastguard Worker   } kTestCases[] = {
528*6777b538SAndroid Build Coastguard Worker       {"google.com", "google.com", true},
529*6777b538SAndroid Build Coastguard Worker       {"www.google.com", "google.com", true},      // Subdomain is ignored.
530*6777b538SAndroid Build Coastguard Worker       {"www.google.com.cn", "google.com", false},  // Different TLD.
531*6777b538SAndroid Build Coastguard Worker       {"www.google.comm", "google.com", false},
532*6777b538SAndroid Build Coastguard Worker       {"www.iamnotgoogle.com", "google.com", false},  // Different hostname.
533*6777b538SAndroid Build Coastguard Worker       {"www.google.com", "Google.com", false},  // The input is not lower-cased.
534*6777b538SAndroid Build Coastguard Worker 
535*6777b538SAndroid Build Coastguard Worker       // If the host ends with a dot, it matches domains with or without a dot.
536*6777b538SAndroid Build Coastguard Worker       {"www.google.com.", "google.com", true},
537*6777b538SAndroid Build Coastguard Worker       {"www.google.com.", "google.com.", true},
538*6777b538SAndroid Build Coastguard Worker       {"www.google.com.", ".com", true},
539*6777b538SAndroid Build Coastguard Worker       {"www.google.com.", ".com.", true},
540*6777b538SAndroid Build Coastguard Worker 
541*6777b538SAndroid Build Coastguard Worker       // But, if the host doesn't end with a dot and the input domain does, then
542*6777b538SAndroid Build Coastguard Worker       // it's considered to not match.
543*6777b538SAndroid Build Coastguard Worker       {"www.google.com", "google.com.", false},
544*6777b538SAndroid Build Coastguard Worker 
545*6777b538SAndroid Build Coastguard Worker       // If the host ends with two dots, it doesn't match.
546*6777b538SAndroid Build Coastguard Worker       {"www.google.com..", "google.com", false},
547*6777b538SAndroid Build Coastguard Worker 
548*6777b538SAndroid Build Coastguard Worker       // Empty parameters.
549*6777b538SAndroid Build Coastguard Worker       {"www.google.com", "", false},
550*6777b538SAndroid Build Coastguard Worker       {"", "www.google.com", false},
551*6777b538SAndroid Build Coastguard Worker       {"", "", false},
552*6777b538SAndroid Build Coastguard Worker   };
553*6777b538SAndroid Build Coastguard Worker 
554*6777b538SAndroid Build Coastguard Worker   for (const auto& test_case : kTestCases) {
555*6777b538SAndroid Build Coastguard Worker     SCOPED_TRACE(testing::Message() << "(host, domain): ("
556*6777b538SAndroid Build Coastguard Worker                                     << test_case.canonicalized_host << ", "
557*6777b538SAndroid Build Coastguard Worker                                     << test_case.lower_ascii_domain << ")");
558*6777b538SAndroid Build Coastguard Worker 
559*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(
560*6777b538SAndroid Build Coastguard Worker         test_case.expected_domain_is,
561*6777b538SAndroid Build Coastguard Worker         DomainIs(test_case.canonicalized_host, test_case.lower_ascii_domain));
562*6777b538SAndroid Build Coastguard Worker   }
563*6777b538SAndroid Build Coastguard Worker }
564*6777b538SAndroid Build Coastguard Worker 
565*6777b538SAndroid Build Coastguard Worker namespace {
CanonicalizeSpec(std::string_view spec,bool trim_path_end)566*6777b538SAndroid Build Coastguard Worker std::optional<std::string> CanonicalizeSpec(std::string_view spec,
567*6777b538SAndroid Build Coastguard Worker                                             bool trim_path_end) {
568*6777b538SAndroid Build Coastguard Worker   std::string canonicalized;
569*6777b538SAndroid Build Coastguard Worker   StdStringCanonOutput output(&canonicalized);
570*6777b538SAndroid Build Coastguard Worker   Parsed parsed;
571*6777b538SAndroid Build Coastguard Worker   if (!Canonicalize(spec.data(), spec.size(), trim_path_end,
572*6777b538SAndroid Build Coastguard Worker                     /*charset_converter=*/nullptr, &output, &parsed)) {
573*6777b538SAndroid Build Coastguard Worker     return {};
574*6777b538SAndroid Build Coastguard Worker   }
575*6777b538SAndroid Build Coastguard Worker   output.Complete();  // Must be called before string is used.
576*6777b538SAndroid Build Coastguard Worker   return canonicalized;
577*6777b538SAndroid Build Coastguard Worker }
578*6777b538SAndroid Build Coastguard Worker }  // namespace
579*6777b538SAndroid Build Coastguard Worker 
580*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
581*6777b538SAndroid Build Coastguard Worker // Regression test for https://crbug.com/1252658.
TEST_F(URLUtilTest,TestCanonicalizeWindowsPathWithLeadingNUL)582*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, TestCanonicalizeWindowsPathWithLeadingNUL) {
583*6777b538SAndroid Build Coastguard Worker   auto PrefixWithNUL = [](std::string&& s) -> std::string { return '\0' + s; };
584*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ(CanonicalizeSpec(PrefixWithNUL("w:"), /*trim_path_end=*/false),
585*6777b538SAndroid Build Coastguard Worker             std::make_optional("file:///W:"));
586*6777b538SAndroid Build Coastguard Worker   EXPECT_EQ(CanonicalizeSpec(PrefixWithNUL("\\\\server\\share"),
587*6777b538SAndroid Build Coastguard Worker                              /*trim_path_end=*/false),
588*6777b538SAndroid Build Coastguard Worker             std::make_optional("file://server/share"));
589*6777b538SAndroid Build Coastguard Worker }
590*6777b538SAndroid Build Coastguard Worker #endif
591*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,TestCanonicalizeIdempotencyWithLeadingControlCharacters)592*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, TestCanonicalizeIdempotencyWithLeadingControlCharacters) {
593*6777b538SAndroid Build Coastguard Worker   std::string spec = "_w:";
594*6777b538SAndroid Build Coastguard Worker   // Loop over all C0 control characters and the space character.
595*6777b538SAndroid Build Coastguard Worker   for (char c = '\0'; c <= ' '; c++) {
596*6777b538SAndroid Build Coastguard Worker     SCOPED_TRACE(testing::Message() << "c: " << c);
597*6777b538SAndroid Build Coastguard Worker 
598*6777b538SAndroid Build Coastguard Worker     // Overwrite the first character of `spec`. Note that replacing the first
599*6777b538SAndroid Build Coastguard Worker     // character with NUL will not change the length!
600*6777b538SAndroid Build Coastguard Worker     spec[0] = c;
601*6777b538SAndroid Build Coastguard Worker 
602*6777b538SAndroid Build Coastguard Worker     for (bool trim_path_end : {false, true}) {
603*6777b538SAndroid Build Coastguard Worker       SCOPED_TRACE(testing::Message() << "trim_path_end: " << trim_path_end);
604*6777b538SAndroid Build Coastguard Worker 
605*6777b538SAndroid Build Coastguard Worker       std::optional<std::string> canonicalized =
606*6777b538SAndroid Build Coastguard Worker           CanonicalizeSpec(spec, trim_path_end);
607*6777b538SAndroid Build Coastguard Worker       ASSERT_TRUE(canonicalized);
608*6777b538SAndroid Build Coastguard Worker       EXPECT_EQ(canonicalized, CanonicalizeSpec(*canonicalized, trim_path_end));
609*6777b538SAndroid Build Coastguard Worker     }
610*6777b538SAndroid Build Coastguard Worker   }
611*6777b538SAndroid Build Coastguard Worker }
612*6777b538SAndroid Build Coastguard Worker 
TEST_F(URLUtilTest,TestHasInvalidURLEscapeSequences)613*6777b538SAndroid Build Coastguard Worker TEST_F(URLUtilTest, TestHasInvalidURLEscapeSequences) {
614*6777b538SAndroid Build Coastguard Worker   struct TestCase {
615*6777b538SAndroid Build Coastguard Worker     const char* input;
616*6777b538SAndroid Build Coastguard Worker     bool is_invalid;
617*6777b538SAndroid Build Coastguard Worker   } cases[] = {
618*6777b538SAndroid Build Coastguard Worker       // Edge cases.
619*6777b538SAndroid Build Coastguard Worker       {"", false},
620*6777b538SAndroid Build Coastguard Worker       {"%", true},
621*6777b538SAndroid Build Coastguard Worker 
622*6777b538SAndroid Build Coastguard Worker       // Single regular chars with no escaping are valid.
623*6777b538SAndroid Build Coastguard Worker       {"a", false},
624*6777b538SAndroid Build Coastguard Worker       {"g", false},
625*6777b538SAndroid Build Coastguard Worker       {"A", false},
626*6777b538SAndroid Build Coastguard Worker       {"G", false},
627*6777b538SAndroid Build Coastguard Worker       {":", false},
628*6777b538SAndroid Build Coastguard Worker       {"]", false},
629*6777b538SAndroid Build Coastguard Worker       {"\x00", false},      // ASCII 'NUL' char
630*6777b538SAndroid Build Coastguard Worker       {"\x01", false},      // ASCII 'SOH' char
631*6777b538SAndroid Build Coastguard Worker       {"\xC2\xA3", false},  // UTF-8 encoded '£'.
632*6777b538SAndroid Build Coastguard Worker 
633*6777b538SAndroid Build Coastguard Worker       // Longer strings without escaping are valid.
634*6777b538SAndroid Build Coastguard Worker       {"Hello world", false},
635*6777b538SAndroid Build Coastguard Worker       {"Here: [%25] <-- a percent-encoded percent character.", false},
636*6777b538SAndroid Build Coastguard Worker 
637*6777b538SAndroid Build Coastguard Worker       // Valid %-escaped sequences ('%' followed by two hex digits).
638*6777b538SAndroid Build Coastguard Worker       {"%00", false},
639*6777b538SAndroid Build Coastguard Worker       {"%20", false},
640*6777b538SAndroid Build Coastguard Worker       {"%02", false},
641*6777b538SAndroid Build Coastguard Worker       {"%ff", false},
642*6777b538SAndroid Build Coastguard Worker       {"%FF", false},
643*6777b538SAndroid Build Coastguard Worker       {"%0a", false},
644*6777b538SAndroid Build Coastguard Worker       {"%0A", false},
645*6777b538SAndroid Build Coastguard Worker       {"abc%FF", false},
646*6777b538SAndroid Build Coastguard Worker       {"%FFabc", false},
647*6777b538SAndroid Build Coastguard Worker       {"abc%FFabc", false},
648*6777b538SAndroid Build Coastguard Worker       {"hello %FF world", false},
649*6777b538SAndroid Build Coastguard Worker       {"%20hello%20world", false},
650*6777b538SAndroid Build Coastguard Worker       {"%25", false},
651*6777b538SAndroid Build Coastguard Worker       {"%25%25", false},
652*6777b538SAndroid Build Coastguard Worker       {"%250", false},
653*6777b538SAndroid Build Coastguard Worker       {"%259", false},
654*6777b538SAndroid Build Coastguard Worker       {"%25A", false},
655*6777b538SAndroid Build Coastguard Worker       {"%25F", false},
656*6777b538SAndroid Build Coastguard Worker       {"%0a:", false},
657*6777b538SAndroid Build Coastguard Worker 
658*6777b538SAndroid Build Coastguard Worker       // '%' followed by a single character is never a valid sequence.
659*6777b538SAndroid Build Coastguard Worker       {"%%", true},
660*6777b538SAndroid Build Coastguard Worker       {"%2", true},
661*6777b538SAndroid Build Coastguard Worker       {"%a", true},
662*6777b538SAndroid Build Coastguard Worker       {"%A", true},
663*6777b538SAndroid Build Coastguard Worker       {"%g", true},
664*6777b538SAndroid Build Coastguard Worker       {"%G", true},
665*6777b538SAndroid Build Coastguard Worker       {"%:", true},
666*6777b538SAndroid Build Coastguard Worker       {"%[", true},
667*6777b538SAndroid Build Coastguard Worker       {"%F", true},
668*6777b538SAndroid Build Coastguard Worker       {"%\xC2\xA3", true},  //% followed by UTF-8 encoded '£'.
669*6777b538SAndroid Build Coastguard Worker 
670*6777b538SAndroid Build Coastguard Worker       // String ends on a potential escape sequence but without two hex-digits
671*6777b538SAndroid Build Coastguard Worker       // is invalid.
672*6777b538SAndroid Build Coastguard Worker       {"abc%", true},
673*6777b538SAndroid Build Coastguard Worker       {"abc%%", true},
674*6777b538SAndroid Build Coastguard Worker       {"abc%%%", true},
675*6777b538SAndroid Build Coastguard Worker       {"abc%a", true},
676*6777b538SAndroid Build Coastguard Worker 
677*6777b538SAndroid Build Coastguard Worker       // One hex and one non-hex digit is invalid.
678*6777b538SAndroid Build Coastguard Worker       {"%a:", true},
679*6777b538SAndroid Build Coastguard Worker       {"%:a", true},
680*6777b538SAndroid Build Coastguard Worker       {"%::", true},
681*6777b538SAndroid Build Coastguard Worker       {"%ag", true},
682*6777b538SAndroid Build Coastguard Worker       {"%ga", true},
683*6777b538SAndroid Build Coastguard Worker       {"%-1", true},
684*6777b538SAndroid Build Coastguard Worker       {"%1-", true},
685*6777b538SAndroid Build Coastguard Worker       {"%0\xC2\xA3", true},  // %0£.
686*6777b538SAndroid Build Coastguard Worker   };
687*6777b538SAndroid Build Coastguard Worker 
688*6777b538SAndroid Build Coastguard Worker   for (TestCase test_case : cases) {
689*6777b538SAndroid Build Coastguard Worker     const char* input = test_case.input;
690*6777b538SAndroid Build Coastguard Worker     bool result = HasInvalidURLEscapeSequences(input);
691*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(test_case.is_invalid, result)
692*6777b538SAndroid Build Coastguard Worker         << "Invalid result for '" << input << "'";
693*6777b538SAndroid Build Coastguard Worker   }
694*6777b538SAndroid Build Coastguard Worker }
695*6777b538SAndroid Build Coastguard Worker 
696*6777b538SAndroid Build Coastguard Worker class URLUtilTypedTest : public ::testing::TestWithParam<bool> {
697*6777b538SAndroid Build Coastguard Worker  public:
URLUtilTypedTest()698*6777b538SAndroid Build Coastguard Worker   URLUtilTypedTest()
699*6777b538SAndroid Build Coastguard Worker       : use_standard_compliant_non_special_scheme_url_parsing_(GetParam()) {
700*6777b538SAndroid Build Coastguard Worker     if (use_standard_compliant_non_special_scheme_url_parsing_) {
701*6777b538SAndroid Build Coastguard Worker       scoped_feature_list_.InitAndEnableFeature(
702*6777b538SAndroid Build Coastguard Worker           kStandardCompliantNonSpecialSchemeURLParsing);
703*6777b538SAndroid Build Coastguard Worker     } else {
704*6777b538SAndroid Build Coastguard Worker       scoped_feature_list_.InitAndDisableFeature(
705*6777b538SAndroid Build Coastguard Worker           kStandardCompliantNonSpecialSchemeURLParsing);
706*6777b538SAndroid Build Coastguard Worker     }
707*6777b538SAndroid Build Coastguard Worker   }
708*6777b538SAndroid Build Coastguard Worker 
709*6777b538SAndroid Build Coastguard Worker  protected:
710*6777b538SAndroid Build Coastguard Worker   struct URLCase {
711*6777b538SAndroid Build Coastguard Worker     const std::string_view input;
712*6777b538SAndroid Build Coastguard Worker     const std::string_view expected;
713*6777b538SAndroid Build Coastguard Worker     bool expected_success;
714*6777b538SAndroid Build Coastguard Worker   };
715*6777b538SAndroid Build Coastguard Worker 
716*6777b538SAndroid Build Coastguard Worker   struct ResolveRelativeCase {
717*6777b538SAndroid Build Coastguard Worker     const std::string_view base;
718*6777b538SAndroid Build Coastguard Worker     const std::string_view rel;
719*6777b538SAndroid Build Coastguard Worker     std::optional<std::string_view> expected;
720*6777b538SAndroid Build Coastguard Worker   };
721*6777b538SAndroid Build Coastguard Worker 
TestCanonicalize(const URLCase & url_case)722*6777b538SAndroid Build Coastguard Worker   void TestCanonicalize(const URLCase& url_case) {
723*6777b538SAndroid Build Coastguard Worker     std::string canonicalized;
724*6777b538SAndroid Build Coastguard Worker     StdStringCanonOutput output(&canonicalized);
725*6777b538SAndroid Build Coastguard Worker     Parsed parsed;
726*6777b538SAndroid Build Coastguard Worker     bool success =
727*6777b538SAndroid Build Coastguard Worker         Canonicalize(url_case.input.data(), url_case.input.size(),
728*6777b538SAndroid Build Coastguard Worker                      /*trim_path_end=*/false,
729*6777b538SAndroid Build Coastguard Worker                      /*charset_converter=*/nullptr, &output, &parsed);
730*6777b538SAndroid Build Coastguard Worker     output.Complete();
731*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(success, url_case.expected_success);
732*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(output.view(), url_case.expected);
733*6777b538SAndroid Build Coastguard Worker   }
734*6777b538SAndroid Build Coastguard Worker 
TestResolveRelative(const ResolveRelativeCase & test)735*6777b538SAndroid Build Coastguard Worker   void TestResolveRelative(const ResolveRelativeCase& test) {
736*6777b538SAndroid Build Coastguard Worker     SCOPED_TRACE(testing::Message()
737*6777b538SAndroid Build Coastguard Worker                  << "base: " << test.base << ", rel: " << test.rel);
738*6777b538SAndroid Build Coastguard Worker 
739*6777b538SAndroid Build Coastguard Worker     Parsed base_parsed;
740*6777b538SAndroid Build Coastguard Worker     if (url::IsUsingStandardCompliantNonSpecialSchemeURLParsing()) {
741*6777b538SAndroid Build Coastguard Worker       ParseNonSpecialURL(test.base.data(), test.base.size(), &base_parsed);
742*6777b538SAndroid Build Coastguard Worker     } else {
743*6777b538SAndroid Build Coastguard Worker       ParsePathURL(test.base.data(), test.base.size(), /*trim_path_end=*/true,
744*6777b538SAndroid Build Coastguard Worker                    &base_parsed);
745*6777b538SAndroid Build Coastguard Worker     }
746*6777b538SAndroid Build Coastguard Worker 
747*6777b538SAndroid Build Coastguard Worker     std::string resolved;
748*6777b538SAndroid Build Coastguard Worker     StdStringCanonOutput output(&resolved);
749*6777b538SAndroid Build Coastguard Worker 
750*6777b538SAndroid Build Coastguard Worker     Parsed resolved_parsed;
751*6777b538SAndroid Build Coastguard Worker     bool valid = ResolveRelative(test.base.data(), test.base.size(),
752*6777b538SAndroid Build Coastguard Worker                                  base_parsed, test.rel.data(), test.rel.size(),
753*6777b538SAndroid Build Coastguard Worker                                  nullptr, &output, &resolved_parsed);
754*6777b538SAndroid Build Coastguard Worker     output.Complete();
755*6777b538SAndroid Build Coastguard Worker 
756*6777b538SAndroid Build Coastguard Worker     if (valid) {
757*6777b538SAndroid Build Coastguard Worker       ASSERT_TRUE(test.expected);
758*6777b538SAndroid Build Coastguard Worker       EXPECT_EQ(resolved, *test.expected);
759*6777b538SAndroid Build Coastguard Worker     } else {
760*6777b538SAndroid Build Coastguard Worker       EXPECT_FALSE(test.expected);
761*6777b538SAndroid Build Coastguard Worker     }
762*6777b538SAndroid Build Coastguard Worker   }
763*6777b538SAndroid Build Coastguard Worker 
764*6777b538SAndroid Build Coastguard Worker   bool use_standard_compliant_non_special_scheme_url_parsing_;
765*6777b538SAndroid Build Coastguard Worker 
766*6777b538SAndroid Build Coastguard Worker  private:
767*6777b538SAndroid Build Coastguard Worker   base::test::ScopedFeatureList scoped_feature_list_;
768*6777b538SAndroid Build Coastguard Worker };
769*6777b538SAndroid Build Coastguard Worker 
TEST_P(URLUtilTypedTest,TestNoRefComponent)770*6777b538SAndroid Build Coastguard Worker TEST_P(URLUtilTypedTest, TestNoRefComponent) {
771*6777b538SAndroid Build Coastguard Worker   // This test was originally written before full support for non-special URLs
772*6777b538SAndroid Build Coastguard Worker   // became available. We need a flag-dependent test here because the test uses
773*6777b538SAndroid Build Coastguard Worker   // an internal parse function. See http://crbug.com/40063064 for details.
774*6777b538SAndroid Build Coastguard Worker   //
775*6777b538SAndroid Build Coastguard Worker   // The test case corresponds to the following user scenario:
776*6777b538SAndroid Build Coastguard Worker   //
777*6777b538SAndroid Build Coastguard Worker   // > const url = new URL("any#body", "mailto://to/");
778*6777b538SAndroid Build Coastguard Worker   // > assertEquals(url.href, "mailto://to/any#body");
779*6777b538SAndroid Build Coastguard Worker   //
780*6777b538SAndroid Build Coastguard Worker   // TODO(crbug.com/40063064): Remove this test once the flag is enabled.
781*6777b538SAndroid Build Coastguard Worker   const std::string_view base = "mailto://to/";
782*6777b538SAndroid Build Coastguard Worker   const std::string_view rel = "any#body";
783*6777b538SAndroid Build Coastguard Worker   if (use_standard_compliant_non_special_scheme_url_parsing_) {
784*6777b538SAndroid Build Coastguard Worker     // We probably don't need to test with the flag enabled, however, including
785*6777b538SAndroid Build Coastguard Worker     // a test with the flag enabled would be beneficial for comparison purposes,
786*6777b538SAndroid Build Coastguard Worker     // at least until we enable the flag by default.
787*6777b538SAndroid Build Coastguard Worker     Parsed base_parsed;
788*6777b538SAndroid Build Coastguard Worker     ParseNonSpecialURL(base.data(), base.size(), &base_parsed);
789*6777b538SAndroid Build Coastguard Worker 
790*6777b538SAndroid Build Coastguard Worker     std::string resolved;
791*6777b538SAndroid Build Coastguard Worker     StdStringCanonOutput output(&resolved);
792*6777b538SAndroid Build Coastguard Worker     Parsed resolved_parsed;
793*6777b538SAndroid Build Coastguard Worker 
794*6777b538SAndroid Build Coastguard Worker     bool valid =
795*6777b538SAndroid Build Coastguard Worker         ResolveRelative(base.data(), base.size(), base_parsed, rel.data(),
796*6777b538SAndroid Build Coastguard Worker                         rel.size(), nullptr, &output, &resolved_parsed);
797*6777b538SAndroid Build Coastguard Worker     EXPECT_TRUE(valid);
798*6777b538SAndroid Build Coastguard Worker     // Note: If the flag is enabled and the correct parsing function is used,
799*6777b538SAndroid Build Coastguard Worker     // resolved_parsed.ref becomes valid correctly.
800*6777b538SAndroid Build Coastguard Worker     EXPECT_TRUE(resolved_parsed.ref.is_valid());
801*6777b538SAndroid Build Coastguard Worker     output.Complete();
802*6777b538SAndroid Build Coastguard Worker     EXPECT_EQ(resolved, "mailto://to/any#body");
803*6777b538SAndroid Build Coastguard Worker   } else {
804*6777b538SAndroid Build Coastguard Worker     // Note: See the description of https://codereview.chromium.org/767713002/
805*6777b538SAndroid Build Coastguard Worker     // for the intention of this test, which added this test to record a wrong
806*6777b538SAndroid Build Coastguard Worker     // result if a wrong parser function is used. I kept the following original
807*6777b538SAndroid Build Coastguard Worker     // comment as is:
808*6777b538SAndroid Build Coastguard Worker     //
809*6777b538SAndroid Build Coastguard Worker     // The hash-mark must be ignored when mailto: scheme is parsed,
810*6777b538SAndroid Build Coastguard Worker     // even if the URL has a base and relative part.
811*6777b538SAndroid Build Coastguard Worker     Parsed base_parsed;
812*6777b538SAndroid Build Coastguard Worker     ParsePathURL(base.data(), base.size(), false, &base_parsed);
813*6777b538SAndroid Build Coastguard Worker 
814*6777b538SAndroid Build Coastguard Worker     std::string resolved;
815*6777b538SAndroid Build Coastguard Worker     StdStringCanonOutput output(&resolved);
816*6777b538SAndroid Build Coastguard Worker     Parsed resolved_parsed;
817*6777b538SAndroid Build Coastguard Worker 
818*6777b538SAndroid Build Coastguard Worker     bool valid =
819*6777b538SAndroid Build Coastguard Worker         ResolveRelative(base.data(), base.size(), base_parsed, rel.data(),
820*6777b538SAndroid Build Coastguard Worker                         rel.size(), nullptr, &output, &resolved_parsed);
821*6777b538SAndroid Build Coastguard Worker     EXPECT_TRUE(valid);
822*6777b538SAndroid Build Coastguard Worker     EXPECT_FALSE(resolved_parsed.ref.is_valid());
823*6777b538SAndroid Build Coastguard Worker   }
824*6777b538SAndroid Build Coastguard Worker }
825*6777b538SAndroid Build Coastguard Worker 
TEST_P(URLUtilTypedTest,Cannolicalize)826*6777b538SAndroid Build Coastguard Worker TEST_P(URLUtilTypedTest, Cannolicalize) {
827*6777b538SAndroid Build Coastguard Worker   // Verify that the feature flag changes canonicalization behavior,
828*6777b538SAndroid Build Coastguard Worker   // focusing on key cases here as comprehesive testing is covered in other unit
829*6777b538SAndroid Build Coastguard Worker   // tests.
830*6777b538SAndroid Build Coastguard Worker   if (use_standard_compliant_non_special_scheme_url_parsing_) {
831*6777b538SAndroid Build Coastguard Worker     URLCase cases[] = {
832*6777b538SAndroid Build Coastguard Worker         {"git://host/..", "git://host/", true},
833*6777b538SAndroid Build Coastguard Worker         {"git:// /", "git:///", false},
834*6777b538SAndroid Build Coastguard Worker         {"git:/..", "git:/", true},
835*6777b538SAndroid Build Coastguard Worker         {"mailto:/..", "mailto:/", true},
836*6777b538SAndroid Build Coastguard Worker     };
837*6777b538SAndroid Build Coastguard Worker     for (const auto& i : cases) {
838*6777b538SAndroid Build Coastguard Worker       TestCanonicalize(i);
839*6777b538SAndroid Build Coastguard Worker     }
840*6777b538SAndroid Build Coastguard Worker   } else {
841*6777b538SAndroid Build Coastguard Worker     // Every non-special URL is considered as an opaque path if the feature is
842*6777b538SAndroid Build Coastguard Worker     // disabled.
843*6777b538SAndroid Build Coastguard Worker     URLCase cases[] = {
844*6777b538SAndroid Build Coastguard Worker         {"git://host/..", "git://host/..", true},
845*6777b538SAndroid Build Coastguard Worker         {"git:// /", "git:// /", true},
846*6777b538SAndroid Build Coastguard Worker         {"git:/..", "git:/..", true},
847*6777b538SAndroid Build Coastguard Worker         {"mailto:/..", "mailto:/..", true},
848*6777b538SAndroid Build Coastguard Worker     };
849*6777b538SAndroid Build Coastguard Worker     for (const auto& i : cases) {
850*6777b538SAndroid Build Coastguard Worker       TestCanonicalize(i);
851*6777b538SAndroid Build Coastguard Worker     }
852*6777b538SAndroid Build Coastguard Worker   }
853*6777b538SAndroid Build Coastguard Worker }
854*6777b538SAndroid Build Coastguard Worker 
TEST_P(URLUtilTypedTest,TestResolveRelativeWithNonSpecialBase)855*6777b538SAndroid Build Coastguard Worker TEST_P(URLUtilTypedTest, TestResolveRelativeWithNonSpecialBase) {
856*6777b538SAndroid Build Coastguard Worker   // Test flag-dependent behaviors. Existing tests in
857*6777b538SAndroid Build Coastguard Worker   // URLUtilTest::TestResolveRelativeWithNonStandardBase cover common cases.
858*6777b538SAndroid Build Coastguard Worker   //
859*6777b538SAndroid Build Coastguard Worker   // TODO(crbug.com/1416006): Test common cases in this typed test too.
860*6777b538SAndroid Build Coastguard Worker   if (use_standard_compliant_non_special_scheme_url_parsing_) {
861*6777b538SAndroid Build Coastguard Worker     ResolveRelativeCase cases[] = {
862*6777b538SAndroid Build Coastguard Worker         {"scheme://Authority", "path", "scheme://Authority/path"},
863*6777b538SAndroid Build Coastguard Worker     };
864*6777b538SAndroid Build Coastguard Worker     for (const auto& i : cases) {
865*6777b538SAndroid Build Coastguard Worker       TestResolveRelative(i);
866*6777b538SAndroid Build Coastguard Worker     }
867*6777b538SAndroid Build Coastguard Worker   } else {
868*6777b538SAndroid Build Coastguard Worker     ResolveRelativeCase cases[] = {
869*6777b538SAndroid Build Coastguard Worker         // It's still possible to get an invalid path URL.
870*6777b538SAndroid Build Coastguard Worker         //
871*6777b538SAndroid Build Coastguard Worker         // Note: If the flag is enabled, "custom://Invalid:!#Auth/" is an
872*6777b538SAndroid Build Coastguard Worker         // invalid URL.
873*6777b538SAndroid Build Coastguard Worker         // ResolveRelative() should be never called.
874*6777b538SAndroid Build Coastguard Worker         {"custom://Invalid:!#Auth/", "file.html", std::nullopt},
875*6777b538SAndroid Build Coastguard Worker 
876*6777b538SAndroid Build Coastguard Worker         // Resolving should fail if the base URL is authority-based but is
877*6777b538SAndroid Build Coastguard Worker         // missing a path component (the '/' at the end).
878*6777b538SAndroid Build Coastguard Worker         {"scheme://Authority", "path", std::nullopt},
879*6777b538SAndroid Build Coastguard Worker 
880*6777b538SAndroid Build Coastguard Worker         // In this case, the backslashes will not be canonicalized because it's
881*6777b538SAndroid Build Coastguard Worker         // a non-standard URL, but they will be treated as a path separators,
882*6777b538SAndroid Build Coastguard Worker         // giving the base URL here a path of "\".
883*6777b538SAndroid Build Coastguard Worker         //
884*6777b538SAndroid Build Coastguard Worker         // The result here is somewhat arbitrary. One could argue it should be
885*6777b538SAndroid Build Coastguard Worker         // either "aaa://a\" or "aaa://a/" since the path is being replaced with
886*6777b538SAndroid Build Coastguard Worker         // the "current directory". But in the context of resolving on data
887*6777b538SAndroid Build Coastguard Worker         // URLs, adding the requested dot doesn't seem wrong either.
888*6777b538SAndroid Build Coastguard Worker         //
889*6777b538SAndroid Build Coastguard Worker         // Note: If the flag is enabled, "aaa://a\\" is an invalid URL.
890*6777b538SAndroid Build Coastguard Worker         // ResolveRelative() should be never called.
891*6777b538SAndroid Build Coastguard Worker         {"aaa://a\\", "aaa:.", "aaa://a\\."}};
892*6777b538SAndroid Build Coastguard Worker     for (const auto& i : cases) {
893*6777b538SAndroid Build Coastguard Worker       TestResolveRelative(i);
894*6777b538SAndroid Build Coastguard Worker     }
895*6777b538SAndroid Build Coastguard Worker   }
896*6777b538SAndroid Build Coastguard Worker }
897*6777b538SAndroid Build Coastguard Worker 
898*6777b538SAndroid Build Coastguard Worker INSTANTIATE_TEST_SUITE_P(All, URLUtilTypedTest, ::testing::Bool());
899*6777b538SAndroid Build Coastguard Worker 
900*6777b538SAndroid Build Coastguard Worker }  // namespace url
901