| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/omnibox/browser/autocomplete_input.h" |
| |
| #include <stddef.h> |
| |
| #include <array> |
| #include <string> |
| |
| #include "base/logging.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "components/omnibox/browser/test_scheme_classifier.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/metrics_proto/omnibox_event.pb.h" |
| #include "third_party/metrics_proto/omnibox_input_type.pb.h" |
| #include "url/third_party/mozilla/url_parse.h" |
| |
| using base::ASCIIToUTF16; |
| using metrics::OmniboxEventProto; |
| |
| TEST(AutocompleteInputTest, InputType) { |
| struct test_data { |
| const std::u16string input; |
| const metrics::OmniboxInputType type; |
| }; |
| auto input_cases = std::to_array<test_data>({ |
| {std::u16string(), metrics::OmniboxInputType::EMPTY}, |
| {u"?", metrics::OmniboxInputType::QUERY}, |
| {u"?foo", metrics::OmniboxInputType::QUERY}, |
| {u"?foo bar", metrics::OmniboxInputType::QUERY}, |
| {u"?http://foo.com/bar", metrics::OmniboxInputType::QUERY}, |
| {u"foo", metrics::OmniboxInputType::UNKNOWN}, |
| {u"foo._", metrics::OmniboxInputType::QUERY}, |
| {u"foo.c", metrics::OmniboxInputType::UNKNOWN}, |
| {u"foo.com", metrics::OmniboxInputType::URL}, |
| {u"-foo.com", metrics::OmniboxInputType::URL}, |
| {u"foo-.com", metrics::OmniboxInputType::URL}, |
| {u"foo_.com", metrics::OmniboxInputType::URL}, |
| {u"foo.-com", metrics::OmniboxInputType::QUERY}, |
| {u"foo/", metrics::OmniboxInputType::URL}, |
| {u"foo/bar", metrics::OmniboxInputType::UNKNOWN}, |
| {u"foo/bar%00", metrics::OmniboxInputType::UNKNOWN}, |
| {u"foo/bar/", metrics::OmniboxInputType::URL}, |
| {u"foo/bar baz\\", metrics::OmniboxInputType::URL}, |
| {u"foo.com/bar", metrics::OmniboxInputType::URL}, |
| {u"foo;bar", metrics::OmniboxInputType::QUERY}, |
| {u"foo/bar baz", metrics::OmniboxInputType::UNKNOWN}, |
| {u"foo bar.com", metrics::OmniboxInputType::QUERY}, |
| {u"foo bar", metrics::OmniboxInputType::QUERY}, |
| {u"foo+bar", metrics::OmniboxInputType::QUERY}, |
| {u"foo+bar.com", metrics::OmniboxInputType::UNKNOWN}, |
| {u"\"foo:bar\"", metrics::OmniboxInputType::QUERY}, |
| {u"link:foo.com", metrics::OmniboxInputType::UNKNOWN}, |
| {u"foo:81", metrics::OmniboxInputType::URL}, |
| {u"www.foo.com:81", metrics::OmniboxInputType::URL}, |
| {u"foo.com:123456", metrics::OmniboxInputType::QUERY}, |
| {u"foo.com:abc", metrics::OmniboxInputType::QUERY}, |
| {u"1.2.3.4:abc", metrics::OmniboxInputType::QUERY}, |
| {u"user@foo", metrics::OmniboxInputType::UNKNOWN}, |
| {u"user@foo.com", metrics::OmniboxInputType::UNKNOWN}, |
| {u"user@foo/", metrics::OmniboxInputType::URL}, |
| {u"user@foo/z", metrics::OmniboxInputType::URL}, |
| {u"user@foo/z z", metrics::OmniboxInputType::URL}, |
| {u"user@foo.com/z", metrics::OmniboxInputType::URL}, |
| {u"user @foo/", metrics::OmniboxInputType::UNKNOWN}, |
| {u"us er@foo/z", metrics::OmniboxInputType::UNKNOWN}, |
| {u"u ser@foo/z z", metrics::OmniboxInputType::UNKNOWN}, |
| {u"us er@foo.com/z", metrics::OmniboxInputType::UNKNOWN}, |
| {u"user:pass@", metrics::OmniboxInputType::UNKNOWN}, |
| {u"user:pass@!foo.com", metrics::OmniboxInputType::UNKNOWN}, |
| {u"user:pass@foo", metrics::OmniboxInputType::URL}, |
| {u"user:pass@foo.c", metrics::OmniboxInputType::URL}, |
| {u"user:pass@foo.com", metrics::OmniboxInputType::URL}, |
| {u"space user:pass@foo", metrics::OmniboxInputType::UNKNOWN}, |
| {u"space user:pass@foo.c", metrics::OmniboxInputType::UNKNOWN}, |
| {u"space user:pass@foo.com", metrics::OmniboxInputType::UNKNOWN}, |
| {u"user:pass@foo.com:81", metrics::OmniboxInputType::URL}, |
| {u"user:pass@foo:81", metrics::OmniboxInputType::URL}, |
| {u".1", metrics::OmniboxInputType::QUERY}, |
| {u".1/3", metrics::OmniboxInputType::QUERY}, |
| {u"1.2", metrics::OmniboxInputType::QUERY}, |
| {u".1.2", metrics::OmniboxInputType::UNKNOWN}, |
| {u"1.2/", metrics::OmniboxInputType::URL}, |
| {u"1.2/45", metrics::OmniboxInputType::QUERY}, |
| {u"6008/32768", metrics::OmniboxInputType::QUERY}, |
| {u"12345678/", metrics::OmniboxInputType::QUERY}, |
| {u"123456789/", metrics::OmniboxInputType::URL}, |
| {u"1.2:45", metrics::OmniboxInputType::QUERY}, |
| {u"user@1.2:45", metrics::OmniboxInputType::QUERY}, |
| {u"user@foo:45", metrics::OmniboxInputType::URL}, |
| {u"user:pass@1.2:45", metrics::OmniboxInputType::URL}, |
| {u"host?query", metrics::OmniboxInputType::UNKNOWN}, |
| {u"host#", metrics::OmniboxInputType::UNKNOWN}, |
| {u"host#ref", metrics::OmniboxInputType::UNKNOWN}, |
| {u"host# ref", metrics::OmniboxInputType::UNKNOWN}, |
| {u"host/page.html", metrics::OmniboxInputType::UNKNOWN}, |
| {u"host/#ref", metrics::OmniboxInputType::URL}, |
| {u"host/?#ref", metrics::OmniboxInputType::URL}, |
| {u"host/?#", metrics::OmniboxInputType::URL}, |
| {u"host.com#ref", metrics::OmniboxInputType::URL}, |
| {u"http://host#ref", metrics::OmniboxInputType::URL}, |
| {u"host/path?query", metrics::OmniboxInputType::URL}, |
| {u"host/path#ref", metrics::OmniboxInputType::URL}, |
| {u"en.wikipedia.org/wiki/Jim Beam", metrics::OmniboxInputType::URL}, |
| // In Chrome itself, mailto: will get handled by ShellExecute, but in |
| // unittest mode, we don't have the data loaded in the external protocol |
| // handler to know this. |
| // { u"mailto:abuse@foo.com", metrics::OmniboxInputType::URL }, |
| {u"view-source:http://www.foo.com/", metrics::OmniboxInputType::URL}, |
| {u"javascript", metrics::OmniboxInputType::UNKNOWN}, |
| {u"javascript:alert(\"Hi there\");", metrics::OmniboxInputType::URL}, |
| {u"javascript:alert%28\"Hi there\"%29;", metrics::OmniboxInputType::URL}, |
| {u"javascript:foo", metrics::OmniboxInputType::UNKNOWN}, |
| {u"javascript:foo;", metrics::OmniboxInputType::URL}, |
| {u"javascript:\"foo\"", metrics::OmniboxInputType::URL}, |
| {u"javascript:", metrics::OmniboxInputType::UNKNOWN}, |
| {u"javascript:the cromulent parts", metrics::OmniboxInputType::UNKNOWN}, |
| {u"javascript:foo.getter", metrics::OmniboxInputType::URL}, |
| {u"JavaScript:Tutorials", metrics::OmniboxInputType::UNKNOWN}, |
| #if BUILDFLAG(IS_WIN) |
| {u"C:\\Program Files", metrics::OmniboxInputType::URL}, |
| {u"\\\\Server\\Folder\\File", metrics::OmniboxInputType::URL}, |
| #endif // BUILDFLAG(IS_WIN) |
| #if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) |
| {u"file:///foo", metrics::OmniboxInputType::QUERY}, |
| {u"/foo", metrics::OmniboxInputType::QUERY}, |
| #else |
| {u"file:///foo", metrics::OmniboxInputType::URL}, |
| #endif // BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) |
| {u"http:foo", metrics::OmniboxInputType::URL}, |
| {u"http://foo", metrics::OmniboxInputType::URL}, |
| {u"http://foo._", metrics::OmniboxInputType::UNKNOWN}, |
| {u"http://foo.c", metrics::OmniboxInputType::URL}, |
| {u"http://foo.com", metrics::OmniboxInputType::URL}, |
| {u"http://foo_bar.com", metrics::OmniboxInputType::URL}, |
| {u"http://foo/bar%00", metrics::OmniboxInputType::URL}, |
| {u"http://foo/bar baz", metrics::OmniboxInputType::URL}, |
| {u"http://-foo.com", metrics::OmniboxInputType::URL}, |
| {u"http://foo-.com", metrics::OmniboxInputType::URL}, |
| {u"http://foo_.com", metrics::OmniboxInputType::URL}, |
| {u"http://foo.-com", metrics::OmniboxInputType::UNKNOWN}, |
| {u"http://_foo_.com", metrics::OmniboxInputType::URL}, |
| {u"http://foo.com:abc", metrics::OmniboxInputType::QUERY}, |
| {u"http://foo.com:123456", metrics::OmniboxInputType::QUERY}, |
| {u"http://1.2.3.4:abc", metrics::OmniboxInputType::QUERY}, |
| {u"http:user@foo.com", metrics::OmniboxInputType::URL}, |
| {u"http://user@foo.com", metrics::OmniboxInputType::URL}, |
| {u"http://space user@foo.com", metrics::OmniboxInputType::URL}, |
| {u"http://user:pass@foo", metrics::OmniboxInputType::URL}, |
| {u"http://space user:pass@foo", metrics::OmniboxInputType::URL}, |
| {u"http:space user:pass@foo", metrics::OmniboxInputType::URL}, |
| {u"http:user:pass@foo.com", metrics::OmniboxInputType::URL}, |
| {u"http://user:pass@foo.com", metrics::OmniboxInputType::URL}, |
| {u"http://1.2", metrics::OmniboxInputType::URL}, |
| {u"http:user@1.2", metrics::OmniboxInputType::URL}, |
| {u"http://1.2/45", metrics::OmniboxInputType::URL}, |
| {u"http:ps/2 games", metrics::OmniboxInputType::URL}, |
| {u"https://foo.com", metrics::OmniboxInputType::URL}, |
| {u"127.0.0.1", metrics::OmniboxInputType::URL}, |
| {u"127.0.1", metrics::OmniboxInputType::QUERY}, |
| {u"127.0.1/", metrics::OmniboxInputType::URL}, |
| {u"0.0.0", metrics::OmniboxInputType::QUERY}, |
| {u"0.0.0.0", metrics::OmniboxInputType::URL}, |
| {u"0.0.0.1", metrics::OmniboxInputType::QUERY}, |
| {u"http://0.0.0.1/", metrics::OmniboxInputType::QUERY}, |
| {u"browser.tabs.closeButtons", metrics::OmniboxInputType::UNKNOWN}, |
| {u"\u6d4b\u8bd5", metrics::OmniboxInputType::UNKNOWN}, |
| {u"[2001:]", metrics::OmniboxInputType::QUERY}, |
| {u"[2001:dB8::1]", metrics::OmniboxInputType::URL}, |
| {u"192.168.0.256", metrics::OmniboxInputType::QUERY}, |
| {u"[foo.com]", metrics::OmniboxInputType::QUERY}, |
| {u"filesystem:http://a.com/t/bar", metrics::OmniboxInputType::URL}, |
| {u"filesystem:http://a.com/", metrics::OmniboxInputType::QUERY}, |
| {u"filesystem:file://", metrics::OmniboxInputType::QUERY}, |
| {u"filesystem:http", metrics::OmniboxInputType::QUERY}, |
| {u"filesystem:", metrics::OmniboxInputType::QUERY}, |
| {u"chrome-search://", metrics::OmniboxInputType::QUERY}, |
| {u"chrome-devtools:", metrics::OmniboxInputType::UNKNOWN}, |
| {u"chrome-devtools://", metrics::OmniboxInputType::UNKNOWN}, |
| {u"chrome-devtools://x", metrics::OmniboxInputType::UNKNOWN}, |
| {u"devtools:", metrics::OmniboxInputType::QUERY}, |
| {u"devtools://", metrics::OmniboxInputType::QUERY}, |
| {u"devtools://x", metrics::OmniboxInputType::URL}, |
| {u"about://f;", metrics::OmniboxInputType::URL}, |
| {u"://w", metrics::OmniboxInputType::UNKNOWN}, |
| {u":w", metrics::OmniboxInputType::UNKNOWN}, |
| {u".\u062A", metrics::OmniboxInputType::UNKNOWN}, |
| // These tests are for https://tools.ietf.org/html/rfc6761. |
| {u"localhost", metrics::OmniboxInputType::URL}, |
| {u"localhost:8080", metrics::OmniboxInputType::URL}, |
| {u"foo.localhost", metrics::OmniboxInputType::URL}, |
| {u"foo localhost", metrics::OmniboxInputType::QUERY}, |
| {u"foo.example", metrics::OmniboxInputType::URL}, |
| {u"foo example", metrics::OmniboxInputType::QUERY}, |
| {u"http://example/", metrics::OmniboxInputType::URL}, |
| {u"example", metrics::OmniboxInputType::UNKNOWN}, |
| {u"example ", metrics::OmniboxInputType::UNKNOWN}, |
| {u" example", metrics::OmniboxInputType::UNKNOWN}, |
| {u" example ", metrics::OmniboxInputType::UNKNOWN}, |
| {u"example.", metrics::OmniboxInputType::UNKNOWN}, |
| {u".example", metrics::OmniboxInputType::UNKNOWN}, |
| {u".example.", metrics::OmniboxInputType::UNKNOWN}, |
| {u"example:", metrics::OmniboxInputType::UNKNOWN}, |
| {u"example:80/ ", metrics::OmniboxInputType::URL}, |
| {u"http://foo.invalid", metrics::OmniboxInputType::UNKNOWN}, |
| {u"foo.invalid/", metrics::OmniboxInputType::QUERY}, |
| {u"foo.invalid", metrics::OmniboxInputType::QUERY}, |
| {u"foo invalid", metrics::OmniboxInputType::QUERY}, |
| {u"invalid", metrics::OmniboxInputType::UNKNOWN}, |
| {u"foo.test", metrics::OmniboxInputType::URL}, |
| {u"foo test", metrics::OmniboxInputType::QUERY}, |
| {u"test", metrics::OmniboxInputType::UNKNOWN}, |
| {u"test..", metrics::OmniboxInputType::UNKNOWN}, |
| {u"..test", metrics::OmniboxInputType::UNKNOWN}, |
| {u"test:80/", metrics::OmniboxInputType::URL}, |
| {u"foo.local", metrics::OmniboxInputType::URL}, |
| {u"foo local", metrics::OmniboxInputType::QUERY}, |
| {u"local", metrics::OmniboxInputType::UNKNOWN}, |
| {u".local", metrics::OmniboxInputType::UNKNOWN}, |
| }); |
| |
| for (size_t i = 0; i < std::size(input_cases); ++i) { |
| SCOPED_TRACE(input_cases[i].input); |
| AutocompleteInput input(input_cases[i].input, |
| metrics::OmniboxEventProto::OTHER, |
| TestSchemeClassifier()); |
| input.set_prevent_inline_autocomplete(true); |
| EXPECT_EQ(input_cases[i].type, input.type()); |
| } |
| } |
| |
| TEST(AutocompleteInputTest, InputTypeWithDesiredTLD) { |
| struct test_data { |
| const std::u16string input; |
| const metrics::OmniboxInputType type; |
| const std::string spec; // Unused if not a URL. |
| }; |
| auto input_cases = std::to_array<test_data>({ |
| {u"401k", metrics::OmniboxInputType::URL, |
| std::string("http://www.401k.com/")}, |
| {u"56", metrics::OmniboxInputType::URL, |
| std::string("http://www.56.com/")}, |
| {u"1.2", metrics::OmniboxInputType::URL, |
| std::string("http://www.1.2.com/")}, |
| {u"1.2/3.4", metrics::OmniboxInputType::URL, |
| std::string("http://www.1.2.com/3.4")}, |
| {u"192.168.0.1", metrics::OmniboxInputType::URL, |
| std::string("http://www.192.168.0.1.com/")}, |
| {u"999999999999999", metrics::OmniboxInputType::URL, |
| std::string("http://www.999999999999999.com/")}, |
| {u"x@y", metrics::OmniboxInputType::URL, |
| std::string("http://x@www.y.com/")}, |
| {u"x@y.com", metrics::OmniboxInputType::URL, |
| std::string("http://x@y.com/")}, |
| {u"space user@y", metrics::OmniboxInputType::UNKNOWN, std::string()}, |
| {u"y/z z", metrics::OmniboxInputType::URL, |
| std::string("http://www.y.com/z%20z")}, |
| {u"abc.com", metrics::OmniboxInputType::URL, |
| std::string("http://abc.com/")}, |
| {u"foo bar", metrics::OmniboxInputType::QUERY, std::string()}, |
| }); |
| |
| for (size_t i = 0; i < std::size(input_cases); ++i) { |
| SCOPED_TRACE(input_cases[i].input); |
| AutocompleteInput input(input_cases[i].input, std::u16string::npos, "com", |
| metrics::OmniboxEventProto::OTHER, |
| TestSchemeClassifier()); |
| input.set_prevent_inline_autocomplete(true); |
| EXPECT_EQ(input_cases[i].type, input.type()); |
| if (input_cases[i].type == metrics::OmniboxInputType::URL) |
| EXPECT_EQ(input_cases[i].spec, input.canonicalized_url().spec()); |
| } |
| } |
| |
| // This tests for a regression where certain input in the omnibox caused us to |
| // crash. As long as the test completes without crashing, we're fine. |
| TEST(AutocompleteInputTest, InputCrash) { |
| AutocompleteInput input(u"\uff65@s", metrics::OmniboxEventProto::OTHER, |
| TestSchemeClassifier()); |
| // Not strictly necessary, but let's be thorough. |
| input.set_prevent_inline_autocomplete(true); |
| } |
| |
| TEST(AutocompleteInputTest, ParseForEmphasizeComponent) { |
| using url::Component; |
| Component kInvalidComponent(0, -1); |
| struct test_data { |
| const std::u16string input; |
| const Component scheme; |
| const Component host; |
| }; |
| auto input_cases = std::to_array<test_data>({ |
| {std::u16string(), kInvalidComponent, kInvalidComponent}, |
| {u"?", kInvalidComponent, kInvalidComponent}, |
| {u"?http://foo.com/bar", kInvalidComponent, kInvalidComponent}, |
| {u"foo/bar baz", kInvalidComponent, Component(0, 3)}, |
| {u"http://foo/bar baz", Component(0, 4), Component(7, 3)}, |
| {u"link:foo.com", Component(0, 4), kInvalidComponent}, |
| {u"www.foo.com:81", kInvalidComponent, Component(0, 11)}, |
| {u"\u6d4b\u8bd5", kInvalidComponent, Component(0, 2)}, |
| {u"view-source:http://www.foo.com/", Component(12, 4), Component(19, 11)}, |
| {u"view-source:https://example.com/", Component(12, 5), |
| Component(20, 11)}, |
| {u"view-source:www.foo.com", kInvalidComponent, Component(12, 11)}, |
| {u"view-source:", Component(0, 11), kInvalidComponent}, |
| {u"view-source:garbage", kInvalidComponent, Component(12, 7)}, |
| {u"view-source:http://http://foo", Component(12, 4), Component(19, 4)}, |
| {u"view-source:view-source:http://example.com/", Component(12, 11), |
| kInvalidComponent}, |
| {u"blob:http://www.foo.com/", Component(5, 4), Component(12, 11)}, |
| {u"blob:https://example.com/", Component(5, 5), Component(13, 11)}, |
| {u"blob:www.foo.com", kInvalidComponent, Component(5, 11)}, |
| {u"blob:", Component(0, 4), kInvalidComponent}, |
| {u"blob:garbage", kInvalidComponent, Component(5, 7)}, |
| }); |
| |
| for (size_t i = 0; i < std::size(input_cases); ++i) { |
| SCOPED_TRACE(input_cases[i].input); |
| Component scheme, host; |
| AutocompleteInput::ParseForEmphasizeComponents( |
| input_cases[i].input, TestSchemeClassifier(), &scheme, &host); |
| EXPECT_EQ(input_cases[i].scheme.begin, scheme.begin); |
| EXPECT_EQ(input_cases[i].scheme.len, scheme.len); |
| EXPECT_EQ(input_cases[i].host.begin, host.begin); |
| EXPECT_EQ(input_cases[i].host.len, host.len); |
| } |
| } |
| |
| TEST(AutocompleteInputTest, InputTypeWithCursorPosition) { |
| struct test_data { |
| const std::u16string input; |
| size_t cursor_position; |
| const std::u16string normalized_input; |
| size_t normalized_cursor_position; |
| }; |
| auto input_cases = std::to_array<test_data>({ |
| {u"foo bar", std::u16string::npos, u"foo bar", std::u16string::npos}, |
| |
| // Regular case, no changes. |
| {u"foo bar", 3, u"foo bar", 3}, |
| |
| // Extra leading space. |
| {u" foo bar", 3, u"foo bar", 1}, |
| {u" foo bar", 3, u"foo bar", 0}, |
| {u" foo bar ", 2, u"foo bar ", 0}, |
| |
| // A leading '?' used to be a magic character indicating the following |
| // input should be treated as a "forced query", but now if such a string |
| // reaches the AutocompleteInput parser the '?' should just be treated |
| // like a normal character. |
| {u"?foo bar", 2, u"?foo bar", 2}, |
| {u" ?foo bar", 4, u"?foo bar", 2}, |
| {u"? foo bar", 4, u"? foo bar", 4}, |
| {u" ? foo bar", 6, u"? foo bar", 4}, |
| }); |
| |
| for (size_t i = 0; i < std::size(input_cases); ++i) { |
| SCOPED_TRACE(input_cases[i].input); |
| AutocompleteInput input( |
| input_cases[i].input, input_cases[i].cursor_position, |
| metrics::OmniboxEventProto::OTHER, TestSchemeClassifier()); |
| input.set_prevent_inline_autocomplete(true); |
| EXPECT_EQ(input_cases[i].normalized_input, input.text()); |
| EXPECT_EQ(input_cases[i].normalized_cursor_position, |
| input.cursor_position()); |
| } |
| } |
| |
| TEST(AutocompleteInputTest, UpgradeTypedNavigationsToHttps) { |
| struct TestData { |
| const std::u16string input; |
| const GURL expected_url; |
| bool expected_added_default_scheme_to_typed_url; |
| }; |
| |
| const TestData test_cases[] = { |
| {u"example.com", GURL("https://example.com"), true}, |
| // If the hostname has a port specified, the URL shouldn't be upgraded |
| // to HTTPS because we can't assume that the HTTPS site is served over the |
| // default SSL port. Port 80 is dropped in URLs so it's still upgraded. |
| {u"example.com:80", GURL("https://example.com"), true}, |
| {u"example.com:8080", GURL("http://example.com:8080"), false}, |
| // Non-URL inputs shouldn't be upgraded. |
| {u"example query", GURL(), false}, |
| // IP addresses shouldn't be upgraded. |
| {u"127.0.0.1", GURL("http://127.0.0.1"), false}, |
| {u"127.0.0.1:80", GURL("http://127.0.0.1:80"), false}, |
| {u"127.0.0.1:8080", GURL("http://127.0.0.1:8080"), false}, |
| // Non-unique hostnames shouldn't be upgraded. |
| {u"site.test", GURL("http://site.test"), false}, |
| // This non-unique hostname is a regression test for |
| // https://crbug.com/1224724. The slash is provided at the end of the |
| // input query since otherwise the input gets classified as a non-URL and |
| // the autocomplete code doesn't progress to the HTTPS upgrading logic |
| // where the bug was. |
| {u"dotlesshostname/", GURL("http://dotlesshostname/"), false}, |
| {u"http://dotlesshostname/", GURL("http://dotlesshostname/"), false}, |
| {u"https://dotlesshostname/", GURL("https://dotlesshostname/"), false}, |
| // Fully typed URLs shouldn't be upgraded. |
| {u"http://example.com", GURL("http://example.com"), false}, |
| {u"HTTP://EXAMPLE.COM", GURL("http://example.com"), false}, |
| {u"http://example.com:80", GURL("http://example.com"), false}, |
| {u"HTTP://EXAMPLE.COM:80", GURL("http://example.com"), false}, |
| {u"http://example.com:8080", GURL("http://example.com:8080"), false}, |
| {u"HTTP://EXAMPLE.COM:8080", GURL("http://example.com:8080"), false}, |
| }; |
| for (const TestData& test_case : test_cases) { |
| AutocompleteInput input(test_case.input, std::u16string::npos, |
| metrics::OmniboxEventProto::OTHER, |
| TestSchemeClassifier(), |
| /*should_use_https_as_default_scheme=*/true); |
| EXPECT_EQ(test_case.expected_url, input.canonicalized_url()) |
| << test_case.input; |
| EXPECT_EQ(test_case.expected_added_default_scheme_to_typed_url, |
| input.added_default_scheme_to_typed_url()); |
| } |
| |
| // Try the same test cases with a non-zero HTTPS port passed to |
| // AutocompleteInput. When a non-zero HTTPS port is used, AutoCompleteInput |
| // should use that port to replace the port of the HTTP URL when upgrading |
| // the URL. |
| // We don't check the default port 80 being upgraded in these test case, |
| // because the default port will be dropped by GURL and we'll end up with |
| // example.com. A hostname without a port is not a valid input when using a |
| // non-zero value for https_port_for_testing. |
| int https_port_for_testing = 12345; |
| const TestData test_cases_non_default_port[] = { |
| {u"example.com:8080", GURL("https://example.com:12345"), true}, |
| // Non-URL inputs shouldn't be upgraded. |
| {u"example query", GURL(), false}, |
| // Non-unique hostnames shouldn't be upgraded. |
| {u"site.test", GURL("http://site.test"), false}, |
| |
| #if !BUILDFLAG(IS_IOS) |
| // IP addresses shouldn't be upgraded. |
| {u"127.0.0.1", GURL("http://127.0.0.1"), false}, |
| {u"127.0.0.1:80", GURL("http://127.0.0.1:80"), false}, |
| {u"127.0.0.1:8080", GURL("http://127.0.0.1:8080"), false}, |
| #else |
| // On iOS, IP addresses will be upgraded in tests if the hostname has a |
| // non-default port. |
| {u"127.0.0.1:8080", GURL("https://127.0.0.1:12345"), true}, |
| #endif |
| // |
| // Fully typed URLs shouldn't be upgraded. |
| {u"http://example.com", GURL("http://example.com"), false}, |
| {u"HTTP://EXAMPLE.COM", GURL("http://example.com"), false}, |
| {u"http://example.com:80", GURL("http://example.com"), false}, |
| {u"HTTP://EXAMPLE.COM:80", GURL("http://example.com"), false}, |
| {u"http://example.com:8080", GURL("http://example.com:8080"), false}, |
| {u"HTTP://EXAMPLE.COM:8080", GURL("http://example.com:8080"), false} |
| }; |
| for (const TestData& test_case : test_cases_non_default_port) { |
| AutocompleteInput input( |
| test_case.input, std::u16string::npos, |
| metrics::OmniboxEventProto::OTHER, TestSchemeClassifier(), |
| /*should_use_https_as_default_scheme=*/true, https_port_for_testing); |
| EXPECT_EQ(test_case.expected_url, input.canonicalized_url()) |
| << test_case.input; |
| EXPECT_EQ(test_case.expected_added_default_scheme_to_typed_url, |
| input.added_default_scheme_to_typed_url()); |
| } |
| |
| #if BUILDFLAG(IS_IOS) |
| AutocompleteInput fake_http_input( |
| u"127.0.0.1:8080", std::u16string::npos, |
| metrics::OmniboxEventProto::OTHER, TestSchemeClassifier(), |
| /*should_use_https_as_default_scheme=*/true, |
| /*https_port_for_testing=*/12345, |
| /*use_fake_https_for_https_upgrade_testing=*/true); |
| EXPECT_EQ(GURL("http://127.0.0.1:12345"), |
| fake_http_input.canonicalized_url()); |
| EXPECT_TRUE(fake_http_input.added_default_scheme_to_typed_url()); |
| #endif |
| } |
| |
| TEST(AutocompleteInputTest, TypedURLHadHTTPSchemeTest) { |
| struct TestData { |
| const std::u16string input; |
| bool expected_typed_url_had_http_scheme; |
| }; |
| |
| const TestData test_cases[] = { |
| {u"example.com", false}, |
| {u"example.com:80", false}, |
| {u"example.com:8080", false}, |
| {u"example query", false}, |
| {u"http example query", false}, |
| {u"127.0.0.1", false}, |
| {u"127.0.0.1:80", false}, |
| {u"127.0.0.1:8080", false}, |
| {u"http://127.0.0.1:8080", true}, |
| {u"https://127.0.0.1:8080", false}, |
| {u"dotlesshostname/", false}, |
| {u"http://dotlesshostname/", true}, |
| {u"https://dotlesshostname/", false}, |
| {u"http://example.com", true}, |
| {u"HTTP://EXAMPLE.COM", true}, |
| {u"http://example.com:80", true}, |
| {u"HTTP://EXAMPLE.COM:80", true}, |
| {u"http://example.com:8080", true}, |
| {u"HTTP://EXAMPLE.COM:8080", true}, |
| {u"HTTPS://EXAMPLE.COM", false}, |
| }; |
| for (const TestData& test_case : test_cases) { |
| AutocompleteInput input(test_case.input, std::u16string::npos, |
| metrics::OmniboxEventProto::OTHER, |
| TestSchemeClassifier(), |
| /*should_use_https_as_default_scheme=*/true); |
| EXPECT_EQ(test_case.expected_typed_url_had_http_scheme, |
| input.typed_url_had_http_scheme()); |
| } |
| } |
| |
| TEST(AutocompleteInputTest, GetFeaturedKeywordMode) { |
| struct TestData { |
| const std::u16string input; |
| AutocompleteInput::FeaturedKeywordMode expected_mode; |
| }; |
| |
| const TestData test_cases[] = { |
| {u"", AutocompleteInput::FeaturedKeywordMode::kFalse}, |
| {u"@", AutocompleteInput::FeaturedKeywordMode::kExact}, |
| {u"@x", AutocompleteInput::FeaturedKeywordMode::kPrefix}, |
| {u"x@", AutocompleteInput::FeaturedKeywordMode::kFalse}, |
| }; |
| for (const TestData& test_case : test_cases) { |
| AutocompleteInput input(test_case.input, std::u16string::npos, |
| metrics::OmniboxEventProto::OTHER, |
| TestSchemeClassifier(), |
| /*should_use_https_as_default_scheme=*/true); |
| EXPECT_EQ(input.GetFeaturedKeywordMode(), test_case.expected_mode); |
| } |
| } |
| |
| TEST(AutocompleteInputTest, ParseUrlLookalikeWithCredentials) { |
| std::u16string input = u"login:password@domain:1234/path"; |
| std::u16string scheme; |
| url::Parsed parts; |
| GURL canonicalized_url; |
| auto input_type = AutocompleteInput::Parse( |
| input, "", TestSchemeClassifier(), &parts, &scheme, &canonicalized_url); |
| |
| EXPECT_TRUE(parts.username.is_nonempty()); |
| EXPECT_TRUE(parts.password.is_nonempty()); |
| EXPECT_EQ(metrics::OmniboxInputType::URL, input_type); |
| EXPECT_EQ("http://login:password@domain:1234/path", canonicalized_url.spec()); |
| } |
| |
| TEST(AutocompleteInputTest, ParseUrlLookalikeWithScheme) { |
| std::u16string input = u"http://login:pass word@domain:1234/path"; |
| std::u16string scheme; |
| url::Parsed parts; |
| GURL canonicalized_url; |
| auto input_type = AutocompleteInput::Parse( |
| input, "", TestSchemeClassifier(), &parts, &scheme, &canonicalized_url); |
| |
| EXPECT_TRUE(parts.username.is_nonempty()); |
| EXPECT_TRUE(parts.password.is_nonempty()); |
| EXPECT_EQ(metrics::OmniboxInputType::URL, input_type); |
| EXPECT_EQ("http://login:pass%20word@domain:1234/path", |
| canonicalized_url.spec()); |
| } |
| |
| TEST(AutocompleteInputTest, ParseUrlLookalikeWithSearchQuery) { |
| std::u16string input = u"site:wikipedia.org ch4@zeolite"; |
| std::u16string scheme; |
| url::Parsed parts; |
| GURL canonicalized_url; |
| auto input_type = AutocompleteInput::Parse( |
| input, "", TestSchemeClassifier(), &parts, &scheme, &canonicalized_url); |
| |
| EXPECT_FALSE(parts.username.is_nonempty()); |
| EXPECT_FALSE(parts.password.is_nonempty()); |
| EXPECT_EQ(metrics::OmniboxInputType::QUERY, input_type); |
| EXPECT_FALSE(canonicalized_url.is_valid()); |
| } |