elide_url_unittest.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. // Copyright 2014 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/elide_url.h"
  5. #include <stddef.h>
  6. #include "base/cxx17_backports.h"
  7. #include "base/logging.h"
  8. #include "base/run_loop.h"
  9. #include "base/strings/escape.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "base/test/task_environment.h"
  12. #include "build/build_config.h"
  13. #include "components/url_formatter/url_formatter.h"
  14. #include "testing/gtest/include/gtest/gtest.h"
  15. #include "ui/gfx/font_list.h"
  16. #include "ui/gfx/text_elider.h"
  17. #include "ui/gfx/text_utils.h"
  18. #include "url/gurl.h"
  19. #include "url/origin.h"
  20. namespace {
  21. struct Testcase {
  22. const std::string input;
  23. const std::string output;
  24. };
  25. struct ProgressiveTestcase {
  26. const std::string input;
  27. const std::vector<std::string> output;
  28. };
  29. struct UrlComponent {
  30. url::Parsed::ComponentType type;
  31. int begin;
  32. int len;
  33. };
  34. struct ParsingTestcase {
  35. const std::string input;
  36. const std::string output;
  37. const std::vector<UrlComponent> components;
  38. };
  39. #if !BUILDFLAG(IS_ANDROID)
  40. // Returns the width of a utf8 or utf16 string using default UI font, or the
  41. // provided |font_list|.
  42. float GetWidth(const std::string& utf8,
  43. const gfx::FontList& font_list = gfx::FontList()) {
  44. return gfx::GetStringWidthF(base::UTF8ToUTF16(utf8), font_list);
  45. }
  46. float GetWidth(const std::u16string& utf16,
  47. const gfx::FontList& font_list = gfx::FontList()) {
  48. return gfx::GetStringWidthF(utf16, font_list);
  49. }
  50. // Verify that one or more URLs passes through an explicit sequence of elided
  51. // strings as available space progressively decreases. This helps ensure that
  52. // transitional corner cases are handled properly. To be tolerant of
  53. // character-width variation across platforms, the test allows a limited number
  54. // of expected strings to be skipped mid-run. The first and last expected
  55. // strings must be matched. If the algorithm produces a string that isn't in the
  56. // expected string list, the test fill fail. Example test expectations:
  57. //
  58. // google.com/intl/en/.../ads/ <-- Must match.
  59. // google.com/intl/.../ads/
  60. // google.com/.../ads/
  61. // google.com/intl... <- Elider can skip this, in case the 'l' does not fit.
  62. // google.com/int...
  63. // google.com/in... <- Must match.
  64. //
  65. void RunProgressiveElisionTest(
  66. const std::vector<ProgressiveTestcase>& testcases) {
  67. const gfx::FontList font_list;
  68. for (const auto& testcase : testcases) {
  69. SCOPED_TRACE("Eliding " + testcase.input);
  70. const GURL url(testcase.input);
  71. // Occasionally, a parsed URL can grow in length before elision, such as
  72. // when parsing a Windows file path with missing slashes.
  73. ASSERT_FALSE(testcase.output.empty());
  74. float width = std::max(GetWidth(testcase.input, font_list),
  75. GetWidth(testcase.output.front(), font_list));
  76. // Ideally, this test would iterate through all available field widths on a
  77. // per-pixel basis, but this is slow. Instead, compute the next input field
  78. // width as slightly less than the previous elided string. This approach
  79. // misses coverage in cases where a smaller available width generates a
  80. // longer string than some other larger available width, but the tradeoff
  81. // feels acceptable.
  82. int mismatches = 0;
  83. const int kMaxConsecutiveMismatches = 3;
  84. for (size_t i = 0; i < testcase.output.size(); i++) {
  85. const auto& expected = testcase.output[i];
  86. std::u16string expected_utf16 = base::UTF8ToUTF16(expected);
  87. std::u16string elided = url_formatter::ElideUrl(url, font_list, width);
  88. if (expected_utf16 != elided) {
  89. if (i > 0 && i < testcase.output.size() - 1 &&
  90. mismatches < kMaxConsecutiveMismatches) {
  91. mismatches++;
  92. continue;
  93. }
  94. EXPECT_EQ(expected_utf16, elided);
  95. break;
  96. }
  97. mismatches = 0;
  98. float new_width = GetWidth(elided, font_list);
  99. // Elision rounds fractional available widths up.
  100. EXPECT_LE(new_width, std::ceil(width)) << " at " << elided;
  101. width = new_width - 1.0f;
  102. }
  103. }
  104. }
  105. void RunElisionTest(const std::vector<Testcase>& testcases) {
  106. const gfx::FontList font_list;
  107. for (const auto& testcase : testcases) {
  108. SCOPED_TRACE("Eliding " + testcase.input);
  109. const GURL url(testcase.input);
  110. const float available_width = GetWidth(testcase.output, font_list);
  111. EXPECT_EQ(base::UTF8ToUTF16(testcase.output),
  112. url_formatter::ElideUrl(url, font_list, available_width));
  113. }
  114. }
  115. // Test eliding of commonplace URLs.
  116. TEST(TextEliderTest, TestGeneralEliding) {
  117. const std::string kEllipsisStr(gfx::kEllipsis);
  118. const std::vector<ProgressiveTestcase> progressive_testcases = {
  119. // Elide a non-www URL (www URLs are handled differently). In this first
  120. // case, elide down to nothing to test the terminal cases.
  121. {"http://xyz.google.com/foo?bar",
  122. {
  123. /* clang-format off */
  124. "xyz.google.com/foo?bar",
  125. "xyz.google.com/foo?b" + kEllipsisStr,
  126. "xyz.google.com/foo?" + kEllipsisStr,
  127. "xyz.google.com/foo" + kEllipsisStr,
  128. "xyz.google.com/fo" + kEllipsisStr,
  129. "xyz.google.com/f" + kEllipsisStr,
  130. kEllipsisStr + "google.com/foo" + kEllipsisStr,
  131. kEllipsisStr + "google.com/fo" + kEllipsisStr,
  132. kEllipsisStr + "google.com/f" + kEllipsisStr,
  133. kEllipsisStr + "google.com/" + kEllipsisStr,
  134. kEllipsisStr + "google.com" + kEllipsisStr,
  135. kEllipsisStr + "google.co" + kEllipsisStr,
  136. kEllipsisStr + "google.c" + kEllipsisStr,
  137. kEllipsisStr + "google." + kEllipsisStr,
  138. kEllipsisStr + "google" + kEllipsisStr,
  139. kEllipsisStr + "googl" + kEllipsisStr,
  140. kEllipsisStr + "goog" + kEllipsisStr,
  141. kEllipsisStr + "goo" + kEllipsisStr,
  142. kEllipsisStr + "go" + kEllipsisStr,
  143. kEllipsisStr + "g" + kEllipsisStr,
  144. kEllipsisStr + kEllipsisStr,
  145. kEllipsisStr,
  146. ""
  147. /* clang-format on */
  148. }},
  149. // The trailing directory name is preserved
  150. {"http://www.google.com/intl/en/ads/",
  151. {
  152. /* clang-format off */
  153. "www.google.com/intl/en/ads/",
  154. "google.com/intl/en/ads/",
  155. "google.com/intl/" + kEllipsisStr + "/ads/",
  156. "google.com/" + kEllipsisStr + "/ads/",
  157. "google.com/" + kEllipsisStr + "/ad" + kEllipsisStr,
  158. "google.com/" + kEllipsisStr + "/a" + kEllipsisStr,
  159. "google.com/intl/e" + kEllipsisStr,
  160. "google.com/intl/" + kEllipsisStr,
  161. "google.com/intl" + kEllipsisStr,
  162. "google.com/int" + kEllipsisStr,
  163. "google.com/in" + kEllipsisStr,
  164. "google.com/i" + kEllipsisStr,
  165. "google.com/" + kEllipsisStr,
  166. "google.com" + kEllipsisStr,
  167. "google.co" + kEllipsisStr,
  168. "google.c" + kEllipsisStr,
  169. "google." + kEllipsisStr,
  170. "google" + kEllipsisStr,
  171. "googl" + kEllipsisStr,
  172. /* clang-format on */
  173. }},
  174. // Subdomain is completely elided if the last path element doesn't fit.
  175. {"https://subdomain.foo.com/bar/filename.html",
  176. {
  177. /* clang-format off */
  178. "https://subdomain.foo.com/bar/filename.html",
  179. "subdomain.foo.com/bar/filename.html",
  180. "subdomain.foo.com/" + kEllipsisStr + "/filename.html",
  181. kEllipsisStr + "foo.com/bar/filename.html",
  182. kEllipsisStr + "foo.com/" + kEllipsisStr + "/filename.html",
  183. /* clang-format on */
  184. }},
  185. // Path eliding works when a query is present.
  186. {"http://www.g.com/subdir/ads/?query",
  187. {
  188. /* clang-format off */
  189. "www.g.com/subdir/ads/?query",
  190. "www.g.com/subdir/ads/?que" + kEllipsisStr,
  191. "www.g.com/subdir/ads/?qu" + kEllipsisStr,
  192. "www.g.com/subdir/ads/?q" + kEllipsisStr,
  193. "www.g.com/subdir/ads/?" + kEllipsisStr,
  194. "www.g.com/subdir/ads/" + kEllipsisStr,
  195. "www.g.com/subdir/ads" + kEllipsisStr,
  196. "www.g.com/subdir/ad" + kEllipsisStr,
  197. /* clang-format on */
  198. }},
  199. };
  200. RunProgressiveElisionTest(progressive_testcases);
  201. }
  202. // When there is very little space available, the elision code will shorten
  203. // both path AND file name to an ellipsis - ".../...". To avoid this result,
  204. // there is a hack in place that simply treats them as one string in this
  205. // case.
  206. TEST(TextEliderTest, TestTrailingEllipsisSlashEllipsisHack) {
  207. const std::string kEllipsisStr(gfx::kEllipsis);
  208. // Very little space, would cause double ellipsis.
  209. gfx::FontList font_list;
  210. GURL url("http://battersbox.com/directory/foo/peter_paul_and_mary.html");
  211. float available_width = GetWidth(
  212. "battersbox.com/" + kEllipsisStr + "/" + kEllipsisStr, font_list);
  213. // Create the expected string, after elision. Depending on font size, the
  214. // directory might become /dir... or /di... or/d... - it never should be
  215. // shorter than that. (If it is, the font considers d... to be longer
  216. // than .../... - that should never happen).
  217. ASSERT_GT(GetWidth(kEllipsisStr + "/" + kEllipsisStr, font_list),
  218. GetWidth("d" + kEllipsisStr, font_list));
  219. GURL long_url("http://battersbox.com/directorynameisreallylongtoforcetrunc");
  220. std::u16string expected =
  221. url_formatter::ElideUrl(long_url, font_list, available_width);
  222. // Ensure that the expected result still contains part of the directory name.
  223. ASSERT_GT(expected.length(), std::string("battersbox.com/d").length());
  224. EXPECT_EQ(expected, url_formatter::ElideUrl(url, font_list, available_width));
  225. // Regression test for https://crbug.com/756717. An empty path, eliding to a
  226. // width in between the full domain ("www.angelfire.lycos.com") and a bit
  227. // longer than the ETLD+1 ("…lycos.com…/…UV"). This previously crashed due to
  228. // the path being empty.
  229. url = GURL("http://www.angelfire.lycos.com/");
  230. available_width = GetWidth(kEllipsisStr + "angelfire.lycos.com", font_list);
  231. EXPECT_EQ(base::UTF8ToUTF16(kEllipsisStr + "lycos.com"),
  232. url_formatter::ElideUrl(url, font_list, available_width));
  233. // More space available - elide directories, partially elide filename.
  234. const std::vector<Testcase> testcases = {
  235. {"http://battersbox.com/directory/foo/peter_paul_and_mary.html",
  236. "battersbox.com/" + kEllipsisStr + "/peter" + kEllipsisStr},
  237. };
  238. RunElisionTest(testcases);
  239. }
  240. // Test eliding of empty strings, URLs with ports, passwords, queries, etc.
  241. TEST(TextEliderTest, TestElisionSpecialCases) {
  242. #if BUILDFLAG(IS_WIN)
  243. // Needed to bypass DCHECK in GetFallbackFont.
  244. base::test::SingleThreadTaskEnvironment task_environment(
  245. base::test::SingleThreadTaskEnvironment::MainThreadType::UI);
  246. #endif
  247. const std::string kEllipsisStr(gfx::kEllipsis);
  248. const std::vector<Testcase> testcases = {
  249. // URL with "www" subdomain (gets removed specially).
  250. {"http://www.google.com/foo?bar", "www.google.com/foo?bar"},
  251. {"http://www.google.com/foo?bar", "google.com/foo?bar"},
  252. // URL with no path.
  253. {"http://xyz.google.com", kEllipsisStr + "google.com"},
  254. {"https://xyz.google.com", kEllipsisStr + "google.com"},
  255. {"http://a.b.com/pathname/c?d", "a.b.com/" + kEllipsisStr + "/c?d"},
  256. {"", ""},
  257. {"http://foo.bar..example.com...hello/test/filename.html",
  258. "foo.bar..example.com...hello/" + kEllipsisStr + "/filename.html"},
  259. {"http://foo.bar../", "foo.bar.."},
  260. {"http://xn--1lq90i.cn/foo", "\xe5\x8c\x97\xe4\xba\xac.cn/foo"},
  261. {"http://me:mypass@secrethost.com:99/foo?bar#baz",
  262. "secrethost.com:99/foo?bar#baz"},
  263. {"http://me:mypass@ss%xxfdsf.com/foo", "ss%25xxfdsf.com/foo"},
  264. {"mailto:elgoato@elgoato.com", "mailto:elgoato@elgoato.com"},
  265. {"javascript:click(0)", "javascript:click(0)"},
  266. {"https://chess.eecs.berkeley.edu:4430/login/arbitfilename",
  267. "chess.eecs.berkeley.edu:4430/login/arbitfilename"},
  268. {"https://chess.eecs.berkeley.edu:4430/login/arbitfilename",
  269. kEllipsisStr + "berkeley.edu:4430/" + kEllipsisStr + "/arbitfilename"},
  270. // Unescaping.
  271. {"http://www/%E4%BD%A0%E5%A5%BD?"
  272. "q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0\xe4\xbd\xa0\xe4\xbd\xa0",
  273. "www/\xe4\xbd\xa0\xe5\xa5\xbd?q=\xe4\xbd\xa0\xe5\xa5\xbd#\xe4\xbd\xa0" +
  274. kEllipsisStr},
  275. // Invalid unescaping for path. The ref will always be valid UTF-8. We
  276. // don't bother to do too many edge cases, since these are handled by the
  277. // escaper unittest.
  278. {"http://www/%E4%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0",
  279. "www/%E4%A0%E5%A5%BD?q=\xe4\xbd\xa0\xe5\xa5\xbd#\xe4\xbd\xa0"},
  280. };
  281. RunElisionTest(testcases);
  282. }
  283. // Test eliding of file: URLs.
  284. TEST(TextEliderTest, TestFileURLEliding) {
  285. const std::string kEllipsisStr(gfx::kEllipsis);
  286. const std::vector<ProgressiveTestcase> progressive_testcases = {
  287. {"file:///C:/path1/path2/path3/filename",
  288. {
  289. /* clang-format off */
  290. "file:///C:/path1/path2/path3/filename",
  291. "/C:/path1/path2/path3/filename",
  292. "C:/path1/path2/path3/filename",
  293. "C:/path1/path2/" + kEllipsisStr + "/filename",
  294. /* clang-format on */
  295. }},
  296. // GURL parses "file:///C:path" differently on windows than it does on posix.
  297. #if BUILDFLAG(IS_WIN)
  298. {"file:///C:path1/path2/path3/filename",
  299. {
  300. /* clang-format off */
  301. "C:/path1/path2/path3/filename",
  302. "C:/path1/path2/" + kEllipsisStr + "/filename",
  303. "C:/path1/" + kEllipsisStr + "/filename",
  304. "C:/" + kEllipsisStr + "/filename",
  305. /* clang-format on */
  306. }},
  307. #endif // BUILDFLAG(IS_WIN)
  308. {"file://filer/foo/bar/file",
  309. {
  310. /* clang-format off */
  311. "file://filer/foo/bar/file",
  312. "filer/foo/bar/file",
  313. "filer/foo/" + kEllipsisStr + "/file",
  314. "filer/" + kEllipsisStr + "/file",
  315. "filer/foo" + kEllipsisStr,
  316. "filer/fo" + kEllipsisStr,
  317. "filer/f" + kEllipsisStr,
  318. "filer/" + kEllipsisStr,
  319. "filer" + kEllipsisStr,
  320. "file" + kEllipsisStr,
  321. /* clang-format on */
  322. }},
  323. };
  324. RunProgressiveElisionTest(progressive_testcases);
  325. const std::vector<Testcase> testcases = {
  326. // Eliding file URLs with nothing after the ':' shouldn't crash.
  327. {"file:///aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:", "aaa" + kEllipsisStr},
  328. {"file:///aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:/", "aaa" + kEllipsisStr},
  329. };
  330. RunElisionTest(testcases);
  331. }
  332. TEST(TextEliderTest, TestHostEliding) {
  333. const std::string kEllipsisStr(gfx::kEllipsis);
  334. Testcase testcases[] = {
  335. {"http://google.com", "google.com"},
  336. {"http://reallyreallyreallylongdomainname.com",
  337. "reallyreallyreallylongdomainname.com"},
  338. {"http://foo", "foo"},
  339. {"http://foo.bar", "foo.bar"},
  340. {"http://subdomain.google.com", kEllipsisStr + ".google.com"},
  341. {"http://a.b.c.d.e.f.com", kEllipsisStr + "f.com"},
  342. {"http://subdomain.foo.bar", kEllipsisStr + "in.foo.bar"},
  343. {"http://subdomain.reallylongdomainname.com",
  344. kEllipsisStr + "ain.reallylongdomainname.com"},
  345. {"http://a.b.c.d.e.f.com", kEllipsisStr + ".e.f.com"},
  346. // IDN - Greek alpha.beta.gamma.delta.epsilon.zeta.com
  347. {"http://xn--mxa.xn--nxa.xn--oxa.xn--pxa.xn--qxa.xn--rxa.com",
  348. kEllipsisStr + ".\xCE\xB5.\xCE\xB6.com"},
  349. };
  350. for (const auto& testcase : testcases) {
  351. const float available_width = GetWidth(testcase.output);
  352. EXPECT_EQ(base::UTF8ToUTF16(testcase.output),
  353. url_formatter::ElideHost(GURL(testcase.input), gfx::FontList(),
  354. available_width));
  355. }
  356. // Trying to elide to a really short length will still keep the full TLD+1
  357. EXPECT_EQ(u"google.com", url_formatter::ElideHost(GURL("http://google.com"),
  358. gfx::FontList(), 2));
  359. EXPECT_EQ(base::UTF8ToUTF16(kEllipsisStr + ".google.com"),
  360. url_formatter::ElideHost(GURL("http://subdomain.google.com"),
  361. gfx::FontList(), 2));
  362. EXPECT_EQ(u"foo.bar", url_formatter::ElideHost(GURL("http://foo.bar"),
  363. gfx::FontList(), 2));
  364. }
  365. #endif // !BUILDFLAG(IS_ANDROID)
  366. struct OriginTestData {
  367. const char* const description;
  368. const char* const input;
  369. const char16_t* const output;
  370. const char16_t* const output_omit_web_scheme;
  371. const char16_t* const output_omit_cryptographic_scheme;
  372. };
  373. // Common test data for both FormatUrlForSecurityDisplay() and
  374. // FormatOriginForSecurityDisplay()
  375. const OriginTestData common_tests[] = {
  376. {"Empty URL", "", u"", u"", u""},
  377. {"HTTP URL", "http://www.google.com/", u"http://www.google.com",
  378. u"www.google.com", u"http://www.google.com"},
  379. {"HTTPS URL", "https://www.google.com/", u"https://www.google.com",
  380. u"www.google.com", u"www.google.com"},
  381. {"Standard HTTP port", "http://www.google.com:80/",
  382. u"http://www.google.com", u"www.google.com", u"http://www.google.com"},
  383. {"Standard HTTPS port", "https://www.google.com:443/",
  384. u"https://www.google.com", u"www.google.com", u"www.google.com"},
  385. {"Standard HTTP port, IDN Chinese",
  386. "http://\xe4\xb8\xad\xe5\x9b\xbd.icom.museum:80",
  387. u"http://中国.icom.museum", u"中国.icom.museum",
  388. u"http://中国.icom.museum"},
  389. {"HTTP URL, IDN Hebrew (RTL)",
  390. "http://"
  391. "\xd7\x90\xd7\x99\xd7\xa7\xd7\x95\xd7\xb4\xd7\x9d."
  392. "\xd7\x99\xd7\xa9\xd7\xa8\xd7\x90\xd7\x9c.museum/",
  393. u"http://xn--4dbklr2c8d.xn--4dbrk0ce.museum",
  394. u"xn--4dbklr2c8d.xn--4dbrk0ce.museum",
  395. u"http://xn--4dbklr2c8d.xn--4dbrk0ce.museum"},
  396. {"HTTP URL with query string, IDN Arabic (RTL)",
  397. "http://\xd9\x85\xd8\xb5\xd8\xb1.icom.museum/foo.html?yes=no",
  398. u"http://xn--wgbh1c.icom.museum", u"xn--wgbh1c.icom.museum",
  399. u"http://xn--wgbh1c.icom.museum"},
  400. {"Non-standard HTTP port", "http://www.google.com:9000/",
  401. u"http://www.google.com:9000", u"www.google.com:9000",
  402. u"http://www.google.com:9000"},
  403. {"Non-standard HTTPS port", "https://www.google.com:9000/",
  404. u"https://www.google.com:9000", u"www.google.com:9000",
  405. u"www.google.com:9000"},
  406. {"HTTP URL with path", "http://www.google.com/test.html",
  407. u"http://www.google.com", u"www.google.com", u"http://www.google.com"},
  408. {"HTTPS URL with path", "https://www.google.com/test.html",
  409. u"https://www.google.com", u"www.google.com", u"www.google.com"},
  410. {"Unusual secure scheme (wss)", "wss://www.google.com/",
  411. u"wss://www.google.com", u"wss://www.google.com", u"www.google.com"},
  412. {"Unusual non-secure scheme (ftp)", "ftp://www.google.com/",
  413. u"ftp://www.google.com", u"ftp://www.google.com", u"ftp://www.google.com"},
  414. {"Unlisted scheme (chrome)", "chrome://version", u"chrome://version",
  415. u"chrome://version", u"chrome://version"},
  416. {"HTTP IP address", "http://173.194.65.103", u"http://173.194.65.103",
  417. u"173.194.65.103", u"http://173.194.65.103"},
  418. {"HTTPS IP address", "https://173.194.65.103", u"https://173.194.65.103",
  419. u"173.194.65.103", u"173.194.65.103"},
  420. {"HTTP IPv6 address", "http://[FE80:0000:0000:0000:0202:B3FF:FE1E:8329]/",
  421. u"http://[fe80::202:b3ff:fe1e:8329]", u"[fe80::202:b3ff:fe1e:8329]",
  422. u"http://[fe80::202:b3ff:fe1e:8329]"},
  423. {"HTTPs IPv6 address", "https://[FE80:0000:0000:0000:0202:B3FF:FE1E:8329]/",
  424. u"https://[fe80::202:b3ff:fe1e:8329]", u"[fe80::202:b3ff:fe1e:8329]",
  425. u"[fe80::202:b3ff:fe1e:8329]"},
  426. {"HTTP IPv6 address with port",
  427. "http://[FE80:0000:0000:0000:0202:B3FF:FE1E:8329]:80/",
  428. u"http://[fe80::202:b3ff:fe1e:8329]", u"[fe80::202:b3ff:fe1e:8329]",
  429. u"http://[fe80::202:b3ff:fe1e:8329]"},
  430. {"HTTPs IPv6 address with port",
  431. "https://[FE80:0000:0000:0000:0202:B3FF:FE1E:8329]:443/",
  432. u"https://[fe80::202:b3ff:fe1e:8329]", u"[fe80::202:b3ff:fe1e:8329]",
  433. u"[fe80::202:b3ff:fe1e:8329]"},
  434. {"HTTPS IP address, non-default port", "https://173.194.65.103:8443",
  435. u"https://173.194.65.103:8443", u"173.194.65.103:8443",
  436. u"173.194.65.103:8443"},
  437. {"Invalid host 1", "https://www.cyber../wow.php", u"https://www.cyber..",
  438. u"www.cyber..", u"www.cyber.."},
  439. {"Invalid host 2", "https://www...cyber/wow.php", u"https://www...cyber",
  440. u"www...cyber", u"www...cyber"},
  441. {"Invalid port 3", "https://173.194.65.103:/hello.aspx",
  442. u"https://173.194.65.103", u"173.194.65.103", u"173.194.65.103"},
  443. {"Trailing dot in DNS name", "https://www.example.com./get/goat",
  444. u"https://www.example.com.", u"www.example.com.", u"www.example.com."}};
  445. TEST(TextEliderTest, FormatUrlForSecurityDisplay) {
  446. for (const auto& common_test : common_tests) {
  447. std::u16string formatted =
  448. url_formatter::FormatUrlForSecurityDisplay(GURL(common_test.input));
  449. EXPECT_EQ(common_test.output, formatted) << common_test.description;
  450. std::u16string formatted_omit_web_scheme =
  451. url_formatter::FormatUrlForSecurityDisplay(
  452. GURL(common_test.input),
  453. url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
  454. EXPECT_EQ(common_test.output_omit_web_scheme, formatted_omit_web_scheme)
  455. << common_test.description;
  456. std::u16string formatted_omit_cryptographic_scheme =
  457. url_formatter::FormatUrlForSecurityDisplay(
  458. GURL(common_test.input),
  459. url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
  460. EXPECT_EQ(common_test.output_omit_cryptographic_scheme,
  461. formatted_omit_cryptographic_scheme)
  462. << common_test.description;
  463. }
  464. const OriginTestData tests[] = {
  465. {"File URI", "file:///usr/example/file.html",
  466. u"file:///usr/example/file.html", u"file:///usr/example/file.html",
  467. u"file:///usr/example/file.html"},
  468. {"File URI with hostname", "file://localhost/usr/example/file.html",
  469. u"file:///usr/example/file.html", u"file:///usr/example/file.html",
  470. u"file:///usr/example/file.html"},
  471. {"UNC File URI 1", "file:///CONTOSO/accounting/money.xls",
  472. u"file:///CONTOSO/accounting/money.xls",
  473. u"file:///CONTOSO/accounting/money.xls",
  474. u"file:///CONTOSO/accounting/money.xls"},
  475. {"UNC File URI 2",
  476. "file:///C:/Program%20Files/Music/Web%20Sys/main.html?REQUEST=RADIO",
  477. u"file:///C:/Program%20Files/Music/Web%20Sys/main.html",
  478. u"file:///C:/Program%20Files/Music/Web%20Sys/main.html",
  479. u"file:///C:/Program%20Files/Music/Web%20Sys/main.html"},
  480. {"Invalid IPv6 address", "https://[2001:db8:0:1]/",
  481. u"https://[2001:db8:0:1]", u"https://[2001:db8:0:1]",
  482. u"https://[2001:db8:0:1]"},
  483. {"HTTP filesystem: URL with path",
  484. "filesystem:http://www.google.com/temporary/test.html",
  485. u"filesystem:http://www.google.com", u"filesystem:http://www.google.com",
  486. u"filesystem:http://www.google.com"},
  487. {"File filesystem: URL with path",
  488. "filesystem:file://localhost/temporary/stuff/"
  489. "test.html?z=fun&goat=billy",
  490. u"filesystem:file:///temporary/stuff/test.html",
  491. u"filesystem:file:///temporary/stuff/test.html",
  492. u"filesystem:file:///temporary/stuff/test.html"},
  493. {"Invalid scheme 1", "twelve://www.cyber.org/wow.php",
  494. u"twelve://www.cyber.org/wow.php", u"twelve://www.cyber.org/wow.php",
  495. u"twelve://www.cyber.org/wow.php"},
  496. {"Invalid scheme 2", "://www.cyber.org/wow.php",
  497. u"://www.cyber.org/wow.php", u"://www.cyber.org/wow.php",
  498. u"://www.cyber.org/wow.php"},
  499. {"Invalid port 1", "https://173.194.65.103:000",
  500. u"https://173.194.65.103:0", u"173.194.65.103:0", u"173.194.65.103:0"},
  501. {"Invalid port 2", "https://173.194.65.103:gruffle",
  502. u"https://173.194.65.103:gruffle", u"https://173.194.65.103:gruffle",
  503. u"https://173.194.65.103:gruffle"},
  504. {"Blob URL",
  505. "blob:http://www.html5rocks.com/4d4ff040-6d61-4446-86d3-13ca07ec9ab9",
  506. u"blob:http://www.html5rocks.com/"
  507. u"4d4ff040-6d61-4446-86d3-13ca07ec9ab9",
  508. u"blob:http://www.html5rocks.com/"
  509. u"4d4ff040-6d61-4446-86d3-13ca07ec9ab9",
  510. u"blob:http://www.html5rocks.com/"
  511. u"4d4ff040-6d61-4446-86d3-13ca07ec9ab9"}};
  512. for (const auto& test : tests) {
  513. std::u16string formatted =
  514. url_formatter::FormatUrlForSecurityDisplay(GURL(test.input));
  515. EXPECT_EQ(test.output, formatted) << test.description;
  516. std::u16string formatted_omit_web_scheme =
  517. url_formatter::FormatUrlForSecurityDisplay(
  518. GURL(test.input),
  519. url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
  520. EXPECT_EQ(test.output_omit_web_scheme, formatted_omit_web_scheme)
  521. << test.description;
  522. std::u16string formatted_omit_cryptographic_scheme =
  523. url_formatter::FormatUrlForSecurityDisplay(
  524. GURL(test.input), url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
  525. EXPECT_EQ(test.output_omit_cryptographic_scheme,
  526. formatted_omit_cryptographic_scheme)
  527. << test.description;
  528. }
  529. std::u16string formatted = url_formatter::FormatUrlForSecurityDisplay(GURL());
  530. EXPECT_EQ(std::u16string(), formatted)
  531. << "Explicitly test the 0-argument GURL constructor";
  532. std::u16string formatted_omit_scheme =
  533. url_formatter::FormatUrlForSecurityDisplay(
  534. GURL(), url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
  535. EXPECT_EQ(std::u16string(), formatted_omit_scheme)
  536. << "Explicitly test the 0-argument GURL constructor";
  537. formatted_omit_scheme = url_formatter::FormatUrlForSecurityDisplay(
  538. GURL(), url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
  539. EXPECT_EQ(std::u16string(), formatted_omit_scheme)
  540. << "Explicitly test the 0-argument GURL constructor";
  541. }
  542. TEST(TextEliderTest, FormatOriginForSecurityDisplay) {
  543. for (const auto& common_test : common_tests) {
  544. std::u16string formatted = url_formatter::FormatOriginForSecurityDisplay(
  545. url::Origin::Create(GURL(common_test.input)));
  546. EXPECT_EQ(common_test.output, formatted) << common_test.description;
  547. std::u16string formatted_omit_web_scheme =
  548. url_formatter::FormatOriginForSecurityDisplay(
  549. url::Origin::Create(GURL(common_test.input)),
  550. url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
  551. EXPECT_EQ(common_test.output_omit_web_scheme, formatted_omit_web_scheme)
  552. << common_test.description;
  553. std::u16string formatted_omit_cryptographic_scheme =
  554. url_formatter::FormatOriginForSecurityDisplay(
  555. url::Origin::Create(GURL(common_test.input)),
  556. url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
  557. EXPECT_EQ(common_test.output_omit_cryptographic_scheme,
  558. formatted_omit_cryptographic_scheme)
  559. << common_test.description;
  560. }
  561. const OriginTestData tests[] = {
  562. {"File URI", "file:///usr/example/file.html", u"file://", u"file://",
  563. u"file://"},
  564. {"File URI with hostname", "file://localhost/usr/example/file.html",
  565. u"file://localhost", u"file://localhost", u"file://localhost"},
  566. {"UNC File URI 1", "file:///CONTOSO/accounting/money.xls", u"file://",
  567. u"file://", u"file://"},
  568. {"UNC File URI 2",
  569. "file:///C:/Program%20Files/Music/Web%20Sys/main.html?REQUEST=RADIO",
  570. u"file://", u"file://", u"file://"},
  571. {"Invalid IPv6 address", "https://[2001:db8:0:1]/", u"", u"", u""},
  572. {"HTTP filesystem: URL with path",
  573. "filesystem:http://www.google.com/temporary/test.html",
  574. u"http://www.google.com", u"www.google.com", u"http://www.google.com"},
  575. {"File filesystem: URL with path",
  576. "filesystem:file://localhost/temporary/stuff/test.html?z=fun&goat=billy",
  577. u"file://", u"file://", u"file://"},
  578. {"Invalid scheme 1", "twelve://www.cyber.org/wow.php", u"", u"", u""},
  579. {"Invalid scheme 2", "://www.cyber.org/wow.php", u"", u"", u""},
  580. {"Invalid port 1", "https://173.194.65.103:99999", u"", u"", u""},
  581. {"Invalid port 2", "https://173.194.65.103:gruffle", u"", u"", u""},
  582. {"Blob URL",
  583. "blob:http://www.html5rocks.com/4d4ff040-6d61-4446-86d3-13ca07ec9ab9",
  584. u"http://www.html5rocks.com", u"www.html5rocks.com",
  585. u"http://www.html5rocks.com"}};
  586. for (const auto& test : tests) {
  587. std::u16string formatted = url_formatter::FormatOriginForSecurityDisplay(
  588. url::Origin::Create(GURL(test.input)));
  589. EXPECT_EQ(test.output, formatted) << test.description;
  590. std::u16string formatted_omit_web_scheme =
  591. url_formatter::FormatOriginForSecurityDisplay(
  592. url::Origin::Create(GURL(test.input)),
  593. url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
  594. EXPECT_EQ(test.output_omit_web_scheme, formatted_omit_web_scheme)
  595. << test.description;
  596. std::u16string formatted_omit_cryptographic_scheme =
  597. url_formatter::FormatOriginForSecurityDisplay(
  598. url::Origin::Create(GURL(test.input)),
  599. url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
  600. EXPECT_EQ(test.output_omit_cryptographic_scheme,
  601. formatted_omit_cryptographic_scheme)
  602. << test.description;
  603. }
  604. std::u16string formatted = url_formatter::FormatOriginForSecurityDisplay(
  605. url::Origin::Create(GURL()));
  606. EXPECT_EQ(std::u16string(), formatted)
  607. << "Explicitly test the url::Origin which takes an empty, invalid URL";
  608. std::u16string formatted_omit_scheme =
  609. url_formatter::FormatOriginForSecurityDisplay(
  610. url::Origin::Create(GURL()),
  611. url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
  612. EXPECT_EQ(std::u16string(), formatted_omit_scheme)
  613. << "Explicitly test the url::Origin which takes an empty, invalid URL";
  614. formatted_omit_scheme = url_formatter::FormatOriginForSecurityDisplay(
  615. url::Origin::Create(GURL()),
  616. url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
  617. EXPECT_EQ(std::u16string(), formatted_omit_scheme)
  618. << "Explicitly test the url::Origin which takes an empty, invalid URL";
  619. }
  620. TEST(TextEliderTest, FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains) {
  621. EXPECT_EQ(
  622. u"google.com",
  623. url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
  624. GURL("http://user:pass@google.com/path")));
  625. EXPECT_EQ(
  626. u"chrome://version",
  627. url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
  628. GURL("chrome://version")));
  629. EXPECT_EQ(
  630. u"äää.de",
  631. url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
  632. GURL("https://äää.de")));
  633. EXPECT_EQ(
  634. u"xn--4caaa.com",
  635. url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
  636. GURL("https://äää.com")));
  637. EXPECT_EQ(
  638. u"مثال.إختبار",
  639. url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
  640. GURL("https://xn--mgbh0fb.xn--kgbechtv/")));
  641. }
  642. TEST(TextEliderTest,
  643. FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix) {
  644. #if BUILDFLAG(IS_IOS)
  645. EXPECT_EQ(
  646. u"google.com",
  647. url_formatter::
  648. FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix(
  649. GURL("http://m.google.com/example")));
  650. EXPECT_EQ(
  651. u"google.com",
  652. url_formatter::
  653. FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix(
  654. GURL("http://www.m.google.com/example")));
  655. EXPECT_EQ(
  656. u"google.com",
  657. url_formatter::
  658. FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix(
  659. GURL("http://m.www.google.com/example")));
  660. #else
  661. GTEST_SKIP();
  662. #endif
  663. }
  664. } // namespace