url_fixer_unittest.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. // Copyright (c) 2011 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/url_formatter/url_fixer.h"
  5. #include <stddef.h>
  6. #include <stdlib.h>
  7. #include <string>
  8. #include "base/base_paths.h"
  9. #include "base/files/file_path.h"
  10. #include "base/files/file_util.h"
  11. #include "base/files/scoped_temp_dir.h"
  12. #include "base/strings/string_util.h"
  13. #include "base/strings/utf_string_conversions.h"
  14. #include "build/build_config.h"
  15. #include "net/base/filename_util.h"
  16. #include "testing/gtest/include/gtest/gtest.h"
  17. #include "url/gurl.h"
  18. #include "url/third_party/mozilla/url_parse.h"
  19. struct SegmentCase {
  20. const std::string input;
  21. const std::string result;
  22. const url::Component scheme;
  23. const url::Component username;
  24. const url::Component password;
  25. const url::Component host;
  26. const url::Component port;
  27. const url::Component path;
  28. const url::Component query;
  29. const url::Component ref;
  30. };
  31. static const SegmentCase segment_cases[] = {
  32. {
  33. "http://www.google.com/", "http", url::Component(0, 4), // scheme
  34. url::Component(), // username
  35. url::Component(), // password
  36. url::Component(7, 14), // host
  37. url::Component(), // port
  38. url::Component(21, 1), // path
  39. url::Component(), // query
  40. url::Component(), // ref
  41. },
  42. {
  43. "aBoUt:vErSiOn", "about", url::Component(0, 5), // scheme
  44. url::Component(), // username
  45. url::Component(), // password
  46. url::Component(6, 7), // host
  47. url::Component(), // port
  48. url::Component(), // path
  49. url::Component(), // query
  50. url::Component(), // ref
  51. },
  52. {
  53. "about:host/path?query#ref", "about", url::Component(0, 5), // scheme
  54. url::Component(), // username
  55. url::Component(), // password
  56. url::Component(6, 4), // host
  57. url::Component(), // port
  58. url::Component(10, 5), // path
  59. url::Component(16, 5), // query
  60. url::Component(22, 3), // ref
  61. },
  62. {
  63. "about://host/path?query#ref", "about", url::Component(0, 5), // scheme
  64. url::Component(), // username
  65. url::Component(), // password
  66. url::Component(8, 4), // host
  67. url::Component(), // port
  68. url::Component(12, 5), // path
  69. url::Component(18, 5), // query
  70. url::Component(24, 3), // ref
  71. },
  72. {
  73. "chrome:host/path?query#ref", "chrome", url::Component(0, 6), // scheme
  74. url::Component(), // username
  75. url::Component(), // password
  76. url::Component(7, 4), // host
  77. url::Component(), // port
  78. url::Component(11, 5), // path
  79. url::Component(17, 5), // query
  80. url::Component(23, 3), // ref
  81. },
  82. {
  83. "chrome://host/path?query#ref", "chrome",
  84. url::Component(0, 6), // scheme
  85. url::Component(), // username
  86. url::Component(), // password
  87. url::Component(9, 4), // host
  88. url::Component(), // port
  89. url::Component(13, 5), // path
  90. url::Component(19, 5), // query
  91. url::Component(25, 3), // ref
  92. },
  93. {
  94. " www.google.com:124?foo#", "http",
  95. url::Component(), // scheme
  96. url::Component(), // username
  97. url::Component(), // password
  98. url::Component(4, 14), // host
  99. url::Component(19, 3), // port
  100. url::Component(), // path
  101. url::Component(23, 3), // query
  102. url::Component(27, 0), // ref
  103. },
  104. {
  105. "user@www.google.com", "http",
  106. url::Component(), // scheme
  107. url::Component(0, 4), // username
  108. url::Component(), // password
  109. url::Component(5, 14), // host
  110. url::Component(), // port
  111. url::Component(), // path
  112. url::Component(), // query
  113. url::Component(), // ref
  114. },
  115. {
  116. "ftp:/user:P:a$$Wd@..ftp.google.com...::23///pub?foo#bar", "ftp",
  117. url::Component(0, 3), // scheme
  118. url::Component(5, 4), // username
  119. url::Component(10, 7), // password
  120. url::Component(18, 20), // host
  121. url::Component(39, 2), // port
  122. url::Component(41, 6), // path
  123. url::Component(48, 3), // query
  124. url::Component(52, 3), // ref
  125. },
  126. {
  127. "[2001:db8::1]/path", "http",
  128. url::Component(), // scheme
  129. url::Component(), // username
  130. url::Component(), // password
  131. url::Component(0, 13), // host
  132. url::Component(), // port
  133. url::Component(13, 5), // path
  134. url::Component(), // query
  135. url::Component(), // ref
  136. },
  137. {
  138. "[::1]", "http",
  139. url::Component(), // scheme
  140. url::Component(), // username
  141. url::Component(), // password
  142. url::Component(0, 5), // host
  143. url::Component(), // port
  144. url::Component(), // path
  145. url::Component(), // query
  146. url::Component(), // ref
  147. },
  148. // Incomplete IPv6 addresses (will not canonicalize).
  149. {
  150. "[2001:4860:", "http",
  151. url::Component(), // scheme
  152. url::Component(), // username
  153. url::Component(), // password
  154. url::Component(0, 11), // host
  155. url::Component(), // port
  156. url::Component(), // path
  157. url::Component(), // query
  158. url::Component(), // ref
  159. },
  160. {
  161. "[2001:4860:/foo", "http",
  162. url::Component(), // scheme
  163. url::Component(), // username
  164. url::Component(), // password
  165. url::Component(0, 11), // host
  166. url::Component(), // port
  167. url::Component(11, 4), // path
  168. url::Component(), // query
  169. url::Component(), // ref
  170. },
  171. {
  172. "http://:b005::68]", "http", url::Component(0, 4), // scheme
  173. url::Component(), // username
  174. url::Component(), // password
  175. url::Component(7, 10), // host
  176. url::Component(), // port
  177. url::Component(), // path
  178. url::Component(), // query
  179. url::Component(), // ref
  180. },
  181. {
  182. ":b005::68]", "http",
  183. url::Component(), // scheme
  184. url::Component(), // username
  185. url::Component(), // password
  186. url::Component(1, 9), // host
  187. url::Component(), // port
  188. url::Component(), // path
  189. url::Component(), // query
  190. url::Component(), // ref
  191. },
  192. {
  193. "file://host/path/file#ref", "file", url::Component(0, 4), // scheme
  194. url::Component(), // username
  195. url::Component(), // password
  196. url::Component(7, 4), // host
  197. url::Component(), // port
  198. url::Component(11, 10), // path
  199. url::Component(), // query
  200. url::Component(22, 3), // ref
  201. },
  202. {
  203. "file:///notahost/path/file#ref", "file",
  204. url::Component(0, 4), // scheme
  205. url::Component(), // username
  206. url::Component(), // password
  207. url::Component(), // host
  208. url::Component(), // port
  209. url::Component(7, 19), // path
  210. url::Component(), // query
  211. url::Component(27, 3), // ref
  212. },
  213. #if BUILDFLAG(IS_WIN)
  214. {
  215. "c:/notahost/path/file#ref", "file",
  216. url::Component(), // scheme
  217. url::Component(), // username
  218. url::Component(), // password
  219. url::Component(), // host
  220. url::Component(), // port
  221. url::Component(0, 21), // path
  222. url::Component(), // query
  223. url::Component(22, 3), // ref
  224. },
  225. #elif BUILDFLAG(IS_POSIX)
  226. {
  227. "~/notahost/path/file#ref", "file",
  228. url::Component(), // scheme
  229. url::Component(), // username
  230. url::Component(), // password
  231. url::Component(), // host
  232. url::Component(), // port
  233. url::Component(0, 20), // path
  234. url::Component(), // query
  235. url::Component(21, 3), // ref
  236. },
  237. #endif
  238. {
  239. "devtools://bundled/devtools/inspector.html?ws=localhost:9221",
  240. "devtools", url::Component(0, 8), // scheme
  241. url::Component(), // username
  242. url::Component(), // password
  243. url::Component(11, 7), // host
  244. url::Component(), // port
  245. url::Component(18, 24), // path
  246. url::Component(43, 17), // query
  247. url::Component(), // ref
  248. },
  249. };
  250. typedef testing::Test URLFixerTest;
  251. TEST(URLFixerTest, SegmentURL) {
  252. std::string result;
  253. url::Parsed parts;
  254. for (size_t i = 0; i < std::size(segment_cases); ++i) {
  255. SegmentCase value = segment_cases[i];
  256. SCOPED_TRACE(testing::Message() << "test #" << i << ": " << value.input);
  257. result = url_formatter::SegmentURL(value.input, &parts);
  258. EXPECT_EQ(value.result, result);
  259. EXPECT_EQ(value.scheme, parts.scheme);
  260. EXPECT_EQ(value.username, parts.username);
  261. EXPECT_EQ(value.password, parts.password);
  262. EXPECT_EQ(value.host, parts.host);
  263. EXPECT_EQ(value.port, parts.port);
  264. EXPECT_EQ(value.path, parts.path);
  265. EXPECT_EQ(value.query, parts.query);
  266. EXPECT_EQ(value.ref, parts.ref);
  267. }
  268. }
  269. // Creates a file and returns its full name as well as the decomposed
  270. // version. Example:
  271. // full_path = "c:\foo\bar.txt"
  272. // dir = "c:\foo"
  273. // file_name = "bar.txt"
  274. static bool MakeTempFile(const base::FilePath& dir,
  275. const base::FilePath& file_name,
  276. base::FilePath* full_path) {
  277. *full_path = dir.Append(file_name);
  278. return base::WriteFile(*full_path, "", 0) == 0;
  279. }
  280. // Returns true if the given URL is a file: URL that matches the given file
  281. static bool IsMatchingFileURL(const std::string& url,
  282. const base::FilePath& full_file_path) {
  283. if (url.length() <= 8)
  284. return false;
  285. if (std::string("file:///") != url.substr(0, 8))
  286. return false; // no file:/// prefix
  287. if (url.find('\\') != std::string::npos)
  288. return false; // contains backslashes
  289. base::FilePath derived_path;
  290. net::FileURLToFilePath(GURL(url), &derived_path);
  291. return base::FilePath::CompareEqualIgnoreCase(derived_path.value(),
  292. full_file_path.value());
  293. }
  294. struct FixupCase {
  295. const std::string input;
  296. const std::string output;
  297. } fixup_cases[] = {
  298. {"www.google.com", "http://www.google.com/"},
  299. {" www.google.com ", "http://www.google.com/"},
  300. {" foo.com/asdf bar", "http://foo.com/asdf%20%20bar"},
  301. {"..www.google.com..", "http://www.google.com./"},
  302. {"http://......", "http://....../"},
  303. {"http://host.com:ninety-two/", "http://host.com:ninety-two/"},
  304. {"http://host.com:ninety-two?foo", "http://host.com:ninety-two/?foo"},
  305. {"google.com:123", "http://google.com:123/"},
  306. {"about:", "chrome://version/"},
  307. {"about:foo", "chrome://foo/"},
  308. {"about:version", "chrome://version/"},
  309. {"about:blank", "about:blank"},
  310. {"About:blaNk", "about:blank"},
  311. {"about:blank#blah", "about:blank#blah"},
  312. {"about:blank/#blah", "about:blank/#blah"},
  313. {"about:srcdoc", "about:srcdoc"},
  314. {"about:srcdoc#blah", "about:srcdoc#blah"},
  315. {"about:srcdoc/#blah", "about:srcdoc/#blah"},
  316. {"about:usr:pwd@hst:20/pth?qry#ref", "chrome://hst/pth?qry#ref"},
  317. {"about://usr:pwd@hst/pth?qry#ref", "chrome://hst/pth?qry#ref"},
  318. {"chrome:usr:pwd@hst/pth?qry#ref", "chrome://hst/pth?qry#ref"},
  319. {"chrome://usr:pwd@hst/pth?qry#ref", "chrome://hst/pth?qry#ref"},
  320. {"www:123", "http://www:123/"},
  321. {" www:123", "http://www:123/"},
  322. {"www.google.com?foo", "http://www.google.com/?foo"},
  323. {"www.google.com#foo", "http://www.google.com/#foo"},
  324. {"www.google.com?", "http://www.google.com/?"},
  325. {"www.google.com#", "http://www.google.com/#"},
  326. {"www.google.com:123?foo#bar", "http://www.google.com:123/?foo#bar"},
  327. {"user@www.google.com", "http://user@www.google.com/"},
  328. {"\xE6\xB0\xB4.com", "http://xn--1rw.com/"},
  329. // It would be better if this next case got treated as http, but I don't see
  330. // a clean way to guess this isn't the new-and-exciting "user" scheme.
  331. {"user:passwd@www.google.com:8080/", "user:passwd@www.google.com:8080/"},
  332. // {"file:///c:/foo/bar%20baz.txt", "file:///C:/foo/bar%20baz.txt"},
  333. // URLs which end with 0x85 (NEL in ISO-8859).
  334. {"http://example.com/s?q=\xD0\x85", "http://example.com/s?q=%D0%85"},
  335. {"http://example.com/s?q=\xEC\x97\x85", "http://example.com/s?q=%EC%97%85"},
  336. {"http://example.com/s?q=\xF0\x90\x80\x85",
  337. "http://example.com/s?q=%F0%90%80%85"},
  338. // URLs which end with 0xA0 (non-break space in ISO-8859).
  339. {"http://example.com/s?q=\xD0\xA0", "http://example.com/s?q=%D0%A0"},
  340. {"http://example.com/s?q=\xEC\x97\xA0", "http://example.com/s?q=%EC%97%A0"},
  341. {"http://example.com/s?q=\xF0\x90\x80\xA0",
  342. "http://example.com/s?q=%F0%90%80%A0"},
  343. // URLs containing Unicode non-characters.
  344. {"http://example.com/s?q=\xEF\xB7\x90", // U+FDD0
  345. "http://example.com/s?q=%EF%BF%BD"},
  346. {"http://example.com/s?q=\xEF\xBF\xBE", // U+FFFE
  347. "http://example.com/s?q=%EF%BF%BD"},
  348. {"http://example.com/s?q=\xEF\xBF\xBF", // U+FFFF
  349. "http://example.com/s?q=%EF%BF%BD"},
  350. {"http://example.com/s?q=\xF4\x8F\xBF\xBE", // U+10FFFE
  351. "http://example.com/s?q=%EF%BF%BD"},
  352. {"http://example.com/s?q=\xF4\x8F\xBF\xBF", // U+10FFFF
  353. "http://example.com/s?q=%EF%BF%BD"},
  354. // URLs containing IPv6 literals.
  355. {"[2001:db8::2]", "http://[2001:db8::2]/"},
  356. {"[::]:80", "http://[::]/"},
  357. {"[::]:80/path", "http://[::]/path"},
  358. {"[::]:180/path", "http://[::]:180/path"},
  359. // TODO(pmarks): Maybe we should parse bare IPv6 literals someday. Currently
  360. // the first colon is treated as a scheme separator, and we default
  361. // unspecified schemes to "http".
  362. {"::1", "http://:1/"},
  363. // Semicolon as scheme separator for standard schemes.
  364. {"http;//www.google.com/", "http://www.google.com/"},
  365. {"about;help", "chrome://help/"},
  366. // Semicolon in non-standard schemes is not replaced by colon.
  367. {"whatsup;//fool", "http://whatsup%3B//fool"},
  368. // Semicolon left as-is in URL itself.
  369. {"http://host/port?query;moar", "http://host/port?query;moar"},
  370. // Fewer slashes than expected.
  371. {"http;www.google.com/", "http://www.google.com/"},
  372. {"http;/www.google.com/", "http://www.google.com/"},
  373. // Semicolon at start.
  374. {";http://www.google.com/", "http://%3Bhttp//www.google.com/"},
  375. // DevTools scheme.
  376. {"devtools://bundled/devtools/node.html",
  377. "devtools://bundled/devtools/node.html"},
  378. // DevTools scheme with websocket query.
  379. {"devtools://bundled/devtools/inspector.html?ws=ws://localhost:9222/guid",
  380. "devtools://bundled/devtools/inspector.html?ws=ws://localhost:9222/guid"},
  381. // host:123 should be rewritten to http://host:123/, but only if the port
  382. // number is valid - in particular telephone numbers are not port numbers
  383. // (see also SendTabToSelfUtilTest.ShouldNotOfferFeatureForTelephoneLink).
  384. {"host:123", "http://host:123/"},
  385. {"host:80", "http://host/"}, // default port is removed
  386. {"host:9999", "http://host:9999/"},
  387. {"host:00009999", "http://host:9999/"}, // leading zeros are removed
  388. {"host:0", "http://host:0/"}, // min valid port
  389. {"host:65535", "http://host:65535/"}, // max valid port
  390. {"host:-1", "host:-1"},
  391. {"host:65536", "host:65536"},
  392. {"host:18446744073709551619", "host:18446744073709551619"}, // > uint64.max
  393. {"host:", "host:"},
  394. {"host: 123", "host: 123"},
  395. {"host:+123", "host:+123"},
  396. {"host:1.23", "host:1.23"},
  397. {"host:x", "host:x"},
  398. {"host:᠐", "host:%E1%A0%90"}, // non-ASCII digit (U+1810)
  399. {"host:𝟨", "host:%F0%9D%9F%A8"}, // non-ASCII digit (U+1D7E8)
  400. {"tel:12345678901", "tel:12345678901"},
  401. {"tel:123-456-78901", "tel:123-456-78901"},
  402. // Double colon after host should not convert to an empty port.
  403. {"foo.com::/server-redirect?http%3A%2F%2Fbar.com%2Ftitle2.html",
  404. "http://foo.com/server-redirect?http%3A%2F%2Fbar.com%2Ftitle2.html"},
  405. };
  406. TEST(URLFixerTest, FixupURL) {
  407. for (const auto& value : fixup_cases) {
  408. GURL actual_output = url_formatter::FixupURL(value.input, std::string());
  409. EXPECT_EQ(value.output, actual_output.possibly_invalid_spec())
  410. << "input: " << value.input;
  411. // Fixup URL should never translate a valid GURL into an invalid one.
  412. if (GURL(value.input).is_valid())
  413. EXPECT_TRUE(actual_output.is_valid());
  414. }
  415. // Check the TLD-appending functionality.
  416. FixupCase tld_cases[] = {
  417. {"somedomainthatwillnotbeagtld",
  418. "http://www.somedomainthatwillnotbeagtld.com/"},
  419. {"somedomainthatwillnotbeagtld.",
  420. "http://www.somedomainthatwillnotbeagtld.com/"},
  421. {"somedomainthatwillnotbeagtld..",
  422. "http://www.somedomainthatwillnotbeagtld.com/"},
  423. {".somedomainthatwillnotbeagtld",
  424. "http://www.somedomainthatwillnotbeagtld.com/"},
  425. {"www.somedomainthatwillnotbeagtld",
  426. "http://www.somedomainthatwillnotbeagtld.com/"},
  427. {"somedomainthatwillnotbeagtld.com",
  428. "http://somedomainthatwillnotbeagtld.com/"},
  429. {"http://somedomainthatwillnotbeagtld",
  430. "http://www.somedomainthatwillnotbeagtld.com/"},
  431. {"..somedomainthatwillnotbeagtld..",
  432. "http://www.somedomainthatwillnotbeagtld.com/"},
  433. {"http://www.somedomainthatwillnotbeagtld",
  434. "http://www.somedomainthatwillnotbeagtld.com/"},
  435. {"9999999999999999", "http://www.9999999999999999.com/"},
  436. {"somedomainthatwillnotbeagtld/foo",
  437. "http://www.somedomainthatwillnotbeagtld.com/foo"},
  438. {"somedomainthatwillnotbeagtld.com/foo",
  439. "http://somedomainthatwillnotbeagtld.com/foo"},
  440. {"somedomainthatwillnotbeagtld/?foo=.com",
  441. "http://www.somedomainthatwillnotbeagtld.com/?foo=.com"},
  442. {"www.somedomainthatwillnotbeagtld/?foo=www.",
  443. "http://www.somedomainthatwillnotbeagtld.com/?foo=www."},
  444. {"somedomainthatwillnotbeagtld.com/?foo=.com",
  445. "http://somedomainthatwillnotbeagtld.com/?foo=.com"},
  446. {"http://www.somedomainthatwillnotbeagtld.com",
  447. "http://www.somedomainthatwillnotbeagtld.com/"},
  448. {"somedomainthatwillnotbeagtld:123",
  449. "http://www.somedomainthatwillnotbeagtld.com:123/"},
  450. {"http://somedomainthatwillnotbeagtld:123",
  451. "http://www.somedomainthatwillnotbeagtld.com:123/"},
  452. };
  453. for (const auto& value : tld_cases) {
  454. EXPECT_EQ(
  455. value.output,
  456. url_formatter::FixupURL(value.input, "com").possibly_invalid_spec());
  457. }
  458. }
  459. // Test different types of file inputs to URIFixerUpper::FixupURL. This
  460. // doesn't go into the nice array of fixups above since the file input
  461. // has to exist.
  462. TEST(URLFixerTest, FixupFile) {
  463. // this "original" filename is the one we tweak to get all the variations
  464. base::ScopedTempDir temp_dir_;
  465. ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
  466. base::FilePath original;
  467. ASSERT_TRUE(MakeTempFile(
  468. temp_dir_.GetPath(),
  469. base::FilePath(FILE_PATH_LITERAL("url fixer upper existing file.txt")),
  470. &original));
  471. // reference path
  472. GURL golden(net::FilePathToFileURL(original));
  473. // c:\foo\bar.txt -> file:///c:/foo/bar.txt (basic)
  474. GURL fixedup(url_formatter::FixupURL(original.AsUTF8Unsafe(), std::string()));
  475. EXPECT_EQ(golden, fixedup);
  476. // TODO(port): Make some equivalent tests for posix.
  477. #if BUILDFLAG(IS_WIN)
  478. // c|/foo\bar.txt -> file:///c:/foo/bar.txt (pipe allowed instead of colon)
  479. std::string cur(base::WideToUTF8(original.value()));
  480. EXPECT_EQ(':', cur[1]);
  481. cur[1] = '|';
  482. EXPECT_EQ(golden, url_formatter::FixupURL(cur, std::string()));
  483. FixupCase cases[] = {
  484. {"c:\\Non-existent%20file.txt", "file:///C:/Non-existent%2520file.txt"},
  485. // \\foo\bar.txt -> file://foo/bar.txt
  486. // UNC paths, this file won't exist, but since there are no escapes, it
  487. // should be returned just converted to a file: URL.
  488. {"\\\\NonexistentHost\\foo\\bar.txt", "file://nonexistenthost/foo/bar.txt"},
  489. // We do this strictly, like IE8, which only accepts this form using
  490. // backslashes and not forward ones. Turning "//foo" into "http" matches
  491. // Firefox and IE, silly though it may seem (it falls out of adding "http"
  492. // as the default protocol if you haven't entered one).
  493. {"//NonexistentHost\\foo/bar.txt", "http://nonexistenthost/foo/bar.txt"},
  494. {"file:///C:/foo/bar", "file:///C:/foo/bar"},
  495. // Much of the work here comes from GURL's canonicalization stage.
  496. {"file://C:/foo/bar", "file:///C:/foo/bar"},
  497. {"file:c:", "file:///C:"},
  498. {"file:c:WINDOWS", "file:///C:/WINDOWS"},
  499. {"file:c|Program Files", "file:///C:/Program%20Files"},
  500. {"file:/file", "file://file/"},
  501. {"file:////////c:\\foo", "file:///C:/foo"},
  502. {"file://server/folder/file", "file://server/folder/file"},
  503. // These are fixups we don't do, but could consider:
  504. // {"file:///foo:/bar", "file://foo/bar"},
  505. // {"file:/\\/server\\folder/file", "file://server/folder/file"},
  506. };
  507. #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
  508. #if BUILDFLAG(IS_APPLE)
  509. #define HOME "/Users/"
  510. #else
  511. #define HOME "/home/"
  512. #endif
  513. url_formatter::home_directory_override = "/foo";
  514. FixupCase cases[] = {
  515. // File URLs go through GURL, which tries to escape intelligently.
  516. {"/A%20non-existent file.txt", "file:///A%2520non-existent%20file.txt"},
  517. // A plain "/" refers to the root.
  518. {"/", "file:///"},
  519. // These rely on the above home_directory_override.
  520. {"~", "file:///foo"},
  521. {"~/bar", "file:///foo/bar"},
  522. // References to other users' homedirs.
  523. {"~foo", "file://" HOME "foo"},
  524. {"~x/blah", "file://" HOME "x/blah"},
  525. };
  526. #endif
  527. for (const auto& value : cases) {
  528. EXPECT_EQ(value.output, url_formatter::FixupURL(value.input, std::string())
  529. .possibly_invalid_spec());
  530. }
  531. EXPECT_TRUE(base::DeleteFile(original));
  532. }
  533. TEST(URLFixerTest, FixupRelativeFile) {
  534. base::FilePath full_path;
  535. base::FilePath file_part(
  536. FILE_PATH_LITERAL("url_fixer_upper_existing_file.txt"));
  537. base::ScopedTempDir temp_dir_;
  538. ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
  539. ASSERT_TRUE(MakeTempFile(temp_dir_.GetPath(), file_part, &full_path));
  540. full_path = base::MakeAbsoluteFilePath(full_path);
  541. ASSERT_FALSE(full_path.empty());
  542. // make sure we pass through good URLs
  543. for (const auto& value : fixup_cases) {
  544. base::FilePath input = base::FilePath::FromUTF8Unsafe(value.input);
  545. EXPECT_EQ(value.output,
  546. url_formatter::FixupRelativeFile(temp_dir_.GetPath(), input)
  547. .possibly_invalid_spec())
  548. << "input: " << value.input;
  549. }
  550. // make sure the existing file got fixed-up to a file URL, and that there
  551. // are no backslashes
  552. EXPECT_TRUE(IsMatchingFileURL(
  553. url_formatter::FixupRelativeFile(temp_dir_.GetPath(), file_part)
  554. .possibly_invalid_spec(),
  555. full_path));
  556. EXPECT_TRUE(base::DeleteFile(full_path));
  557. // create a filename we know doesn't exist and make sure it doesn't get
  558. // fixed up to a file URL
  559. base::FilePath nonexistent_file(
  560. FILE_PATH_LITERAL("url_fixer_upper_nonexistent_file.txt"));
  561. std::string fixedup(
  562. url_formatter::FixupRelativeFile(temp_dir_.GetPath(), nonexistent_file)
  563. .possibly_invalid_spec());
  564. EXPECT_NE(std::string("file:///"), fixedup.substr(0, 8));
  565. EXPECT_FALSE(IsMatchingFileURL(fixedup, nonexistent_file));
  566. // make a subdir to make sure relative paths with directories work, also
  567. // test spaces:
  568. // "app_dir\url fixer-upper dir\url fixer-upper existing file.txt"
  569. base::FilePath sub_dir(FILE_PATH_LITERAL("url fixer-upper dir"));
  570. base::FilePath sub_file(
  571. FILE_PATH_LITERAL("url fixer-upper existing file.txt"));
  572. base::FilePath new_dir = temp_dir_.GetPath().Append(sub_dir);
  573. base::CreateDirectory(new_dir);
  574. ASSERT_TRUE(MakeTempFile(new_dir, sub_file, &full_path));
  575. full_path = base::MakeAbsoluteFilePath(full_path);
  576. ASSERT_FALSE(full_path.empty());
  577. // test file in the subdir
  578. base::FilePath relative_file = sub_dir.Append(sub_file);
  579. EXPECT_TRUE(IsMatchingFileURL(
  580. url_formatter::FixupRelativeFile(temp_dir_.GetPath(), relative_file)
  581. .possibly_invalid_spec(),
  582. full_path));
  583. // test file in the subdir with different slashes and escaping.
  584. base::FilePath::StringType relative_file_str = sub_dir.value() +
  585. FILE_PATH_LITERAL("/") + sub_file.value();
  586. base::ReplaceSubstringsAfterOffset(&relative_file_str, 0,
  587. FILE_PATH_LITERAL(" "), FILE_PATH_LITERAL("%20"));
  588. EXPECT_TRUE(IsMatchingFileURL(
  589. url_formatter::FixupRelativeFile(temp_dir_.GetPath(),
  590. base::FilePath(relative_file_str))
  591. .possibly_invalid_spec(),
  592. full_path));
  593. // test relative directories and duplicate slashes
  594. // (should resolve to the same file as above)
  595. relative_file_str = sub_dir.value() + FILE_PATH_LITERAL("/../") +
  596. sub_dir.value() + FILE_PATH_LITERAL("///./") + sub_file.value();
  597. EXPECT_TRUE(IsMatchingFileURL(
  598. url_formatter::FixupRelativeFile(temp_dir_.GetPath(),
  599. base::FilePath(relative_file_str))
  600. .possibly_invalid_spec(),
  601. full_path));
  602. // done with the subdir
  603. EXPECT_TRUE(base::DeleteFile(full_path));
  604. EXPECT_TRUE(base::DeletePathRecursively(new_dir));
  605. // Test that an obvious HTTP URL isn't accidentally treated as an absolute
  606. // file path (on account of system-specific craziness).
  607. base::FilePath empty_path;
  608. base::FilePath http_url_path(FILE_PATH_LITERAL("http://../"));
  609. EXPECT_TRUE(url_formatter::FixupRelativeFile(empty_path, http_url_path)
  610. .SchemeIs("http"));
  611. }