Home | History | Annotate | Download | only in autocomplete
      1 // Copyright (c) 2012 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 
      5 #include "chrome/browser/autocomplete/builtin_provider.h"
      6 
      7 #include "base/format_macros.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
     11 #include "chrome/common/url_constants.h"
     12 #include "components/metrics/proto/omnibox_event.pb.h"
     13 #include "components/omnibox/autocomplete_input.h"
     14 #include "components/omnibox/autocomplete_match.h"
     15 #include "components/omnibox/autocomplete_provider.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 #include "url/gurl.h"
     18 
     19 using base::ASCIIToUTF16;
     20 
     21 class BuiltinProviderTest : public testing::Test {
     22  protected:
     23   struct TestData {
     24     const base::string16 input;
     25     const size_t num_results;
     26     const GURL output[3];
     27   };
     28 
     29   BuiltinProviderTest() : provider_(NULL) {}
     30   virtual ~BuiltinProviderTest() {}
     31 
     32   virtual void SetUp() OVERRIDE { provider_ = new BuiltinProvider(); }
     33   virtual void TearDown() OVERRIDE { provider_ = NULL; }
     34 
     35   void RunTest(const TestData cases[], size_t num_cases) {
     36     ACMatches matches;
     37     for (size_t i = 0; i < num_cases; ++i) {
     38       SCOPED_TRACE(base::StringPrintf(
     39           "case %" PRIuS ": %s", i, base::UTF16ToUTF8(cases[i].input).c_str()));
     40       const AutocompleteInput input(cases[i].input, base::string16::npos,
     41                                     base::string16(), GURL(),
     42                                     metrics::OmniboxEventProto::INVALID_SPEC,
     43                                     true, false, true, true,
     44                                     ChromeAutocompleteSchemeClassifier(NULL));
     45       provider_->Start(input, false);
     46       EXPECT_TRUE(provider_->done());
     47       matches = provider_->matches();
     48       EXPECT_EQ(cases[i].num_results, matches.size());
     49       if (matches.size() == cases[i].num_results) {
     50         for (size_t j = 0; j < cases[i].num_results; ++j) {
     51           EXPECT_EQ(cases[i].output[j], matches[j].destination_url);
     52           EXPECT_FALSE(matches[j].allowed_to_be_default_match);
     53         }
     54       }
     55     }
     56   }
     57 
     58  private:
     59   scoped_refptr<BuiltinProvider> provider_;
     60 
     61   DISALLOW_COPY_AND_ASSIGN(BuiltinProviderTest);
     62 };
     63 
     64 #if !defined(OS_ANDROID)
     65 TEST_F(BuiltinProviderTest, TypingScheme) {
     66   const base::string16 kAbout = ASCIIToUTF16(url::kAboutScheme);
     67   const base::string16 kChrome = ASCIIToUTF16(content::kChromeUIScheme);
     68   const base::string16 kSeparator1 = ASCIIToUTF16(":");
     69   const base::string16 kSeparator2 = ASCIIToUTF16(":/");
     70   const base::string16 kSeparator3 =
     71       ASCIIToUTF16(url::kStandardSchemeSeparator);
     72 
     73   // These default URLs should correspond with those in BuiltinProvider::Start.
     74   const GURL kURL1 = GURL(chrome::kChromeUIChromeURLsURL);
     75   const GURL kURL2 = GURL(chrome::kChromeUISettingsURL);
     76   const GURL kURL3 = GURL(chrome::kChromeUIVersionURL);
     77 
     78   TestData typing_scheme_cases[] = {
     79     // Typing an unrelated scheme should give nothing.
     80     {ASCIIToUTF16("h"),        0, {}},
     81     {ASCIIToUTF16("http"),     0, {}},
     82     {ASCIIToUTF16("file"),     0, {}},
     83     {ASCIIToUTF16("abouz"),    0, {}},
     84     {ASCIIToUTF16("aboutt"),   0, {}},
     85     {ASCIIToUTF16("aboutt:"),  0, {}},
     86     {ASCIIToUTF16("chroma"),   0, {}},
     87     {ASCIIToUTF16("chromee"),  0, {}},
     88     {ASCIIToUTF16("chromee:"), 0, {}},
     89 
     90     // Typing a portion of about:// should give the default urls.
     91     {kAbout.substr(0, 1),      3, {kURL1, kURL2, kURL3}},
     92     {ASCIIToUTF16("A"),        3, {kURL1, kURL2, kURL3}},
     93     {kAbout,                   3, {kURL1, kURL2, kURL3}},
     94     {kAbout + kSeparator1,     3, {kURL1, kURL2, kURL3}},
     95     {kAbout + kSeparator2,     3, {kURL1, kURL2, kURL3}},
     96     {kAbout + kSeparator3,     3, {kURL1, kURL2, kURL3}},
     97     {ASCIIToUTF16("aBoUT://"), 3, {kURL1, kURL2, kURL3}},
     98 
     99     // Typing a portion of chrome:// should give the default urls.
    100     {kChrome.substr(0, 1),      3, {kURL1, kURL2, kURL3}},
    101     {ASCIIToUTF16("C"),         3, {kURL1, kURL2, kURL3}},
    102     {kChrome,                   3, {kURL1, kURL2, kURL3}},
    103     {kChrome + kSeparator1,     3, {kURL1, kURL2, kURL3}},
    104     {kChrome + kSeparator2,     3, {kURL1, kURL2, kURL3}},
    105     {kChrome + kSeparator3,     3, {kURL1, kURL2, kURL3}},
    106     {ASCIIToUTF16("ChRoMe://"), 3, {kURL1, kURL2, kURL3}},
    107   };
    108 
    109   RunTest(typing_scheme_cases, arraysize(typing_scheme_cases));
    110 }
    111 #else // Android uses a subset of the URLs
    112 TEST_F(BuiltinProviderTest, TypingScheme) {
    113   const base::string16 kAbout = ASCIIToUTF16(url::kAboutScheme);
    114   const base::string16 kChrome = ASCIIToUTF16(content::kChromeUIScheme);
    115   const base::string16 kSeparator1 = ASCIIToUTF16(":");
    116   const base::string16 kSeparator2 = ASCIIToUTF16(":/");
    117   const base::string16 kSeparator3 =
    118       ASCIIToUTF16(url::kStandardSchemeSeparator);
    119 
    120   // These default URLs should correspond with those in BuiltinProvider::Start.
    121   const GURL kURL1 = GURL(chrome::kChromeUIChromeURLsURL);
    122   const GURL kURL2 = GURL(chrome::kChromeUIVersionURL);
    123 
    124   TestData typing_scheme_cases[] = {
    125     // Typing an unrelated scheme should give nothing.
    126     {ASCIIToUTF16("h"),        0, {}},
    127     {ASCIIToUTF16("http"),     0, {}},
    128     {ASCIIToUTF16("file"),     0, {}},
    129     {ASCIIToUTF16("abouz"),    0, {}},
    130     {ASCIIToUTF16("aboutt"),   0, {}},
    131     {ASCIIToUTF16("aboutt:"),  0, {}},
    132     {ASCIIToUTF16("chroma"),   0, {}},
    133     {ASCIIToUTF16("chromee"),  0, {}},
    134     {ASCIIToUTF16("chromee:"), 0, {}},
    135 
    136     // Typing a portion of about:// should give the default urls.
    137     {kAbout.substr(0, 1),      2, {kURL1, kURL2}},
    138     {ASCIIToUTF16("A"),        2, {kURL1, kURL2}},
    139     {kAbout,                   2, {kURL1, kURL2}},
    140     {kAbout + kSeparator1,     2, {kURL1, kURL2}},
    141     {kAbout + kSeparator2,     2, {kURL1, kURL2}},
    142     {kAbout + kSeparator3,     2, {kURL1, kURL2}},
    143     {ASCIIToUTF16("aBoUT://"), 2, {kURL1, kURL2}},
    144 
    145     // Typing a portion of chrome:// should give the default urls.
    146     {kChrome.substr(0, 1),      2, {kURL1, kURL2}},
    147     {ASCIIToUTF16("C"),         2, {kURL1, kURL2}},
    148     {kChrome,                   2, {kURL1, kURL2}},
    149     {kChrome + kSeparator1,     2, {kURL1, kURL2}},
    150     {kChrome + kSeparator2,     2, {kURL1, kURL2}},
    151     {kChrome + kSeparator3,     2, {kURL1, kURL2}},
    152     {ASCIIToUTF16("ChRoMe://"), 2, {kURL1, kURL2}},
    153   };
    154 
    155   RunTest(typing_scheme_cases, arraysize(typing_scheme_cases));
    156 }
    157 #endif
    158 
    159 TEST_F(BuiltinProviderTest, NonChromeURLs) {
    160   TestData non_chrome_url_cases[] = {
    161     // Typing an unrelated scheme should give nothing.
    162     {ASCIIToUTF16("g@rb@g3"),                      0, {}},
    163     {ASCIIToUTF16("www.google.com"),               0, {}},
    164     {ASCIIToUTF16("http:www.google.com"),          0, {}},
    165     {ASCIIToUTF16("http://www.google.com"),        0, {}},
    166     {ASCIIToUTF16("file:filename"),                0, {}},
    167     {ASCIIToUTF16("scheme:"),                      0, {}},
    168     {ASCIIToUTF16("scheme://"),                    0, {}},
    169     {ASCIIToUTF16("scheme://host"),                0, {}},
    170     {ASCIIToUTF16("scheme:host/path?query#ref"),   0, {}},
    171     {ASCIIToUTF16("scheme://host/path?query#ref"), 0, {}},
    172   };
    173 
    174   RunTest(non_chrome_url_cases, arraysize(non_chrome_url_cases));
    175 }
    176 
    177 TEST_F(BuiltinProviderTest, ChromeURLs) {
    178   const base::string16 kAbout = ASCIIToUTF16(url::kAboutScheme);
    179   const base::string16 kChrome = ASCIIToUTF16(content::kChromeUIScheme);
    180   const base::string16 kSeparator1 = ASCIIToUTF16(":");
    181   const base::string16 kSeparator2 = ASCIIToUTF16(":/");
    182   const base::string16 kSeparator3 =
    183       ASCIIToUTF16(url::kStandardSchemeSeparator);
    184 
    185   // This makes assumptions about the chrome URLs listed by the BuiltinProvider.
    186   // Currently they are derived from chrome::kChromeHostURLs[].
    187   const base::string16 kHostM1 =
    188       ASCIIToUTF16(content::kChromeUIMediaInternalsHost);
    189   const base::string16 kHostM2 =
    190       ASCIIToUTF16(chrome::kChromeUIMemoryHost);
    191   const base::string16 kHostM3 =
    192       ASCIIToUTF16(chrome::kChromeUIMemoryInternalsHost);
    193   const GURL kURLM1 = GURL(kChrome + kSeparator3 + kHostM1);
    194   const GURL kURLM2 = GURL(kChrome + kSeparator3 + kHostM2);
    195   const GURL kURLM3 = GURL(kChrome + kSeparator3 + kHostM3);
    196 
    197   TestData chrome_url_cases[] = {
    198     // Typing an about URL with an unknown host should give nothing.
    199     {kAbout + kSeparator1 + ASCIIToUTF16("host"), 0, {}},
    200     {kAbout + kSeparator2 + ASCIIToUTF16("host"), 0, {}},
    201     {kAbout + kSeparator3 + ASCIIToUTF16("host"), 0, {}},
    202 
    203     // Typing a chrome URL with an unknown host should give nothing.
    204     {kChrome + kSeparator1 + ASCIIToUTF16("host"), 0, {}},
    205     {kChrome + kSeparator2 + ASCIIToUTF16("host"), 0, {}},
    206     {kChrome + kSeparator3 + ASCIIToUTF16("host"), 0, {}},
    207 
    208     // Typing an about URL should provide matching URLs.
    209     {kAbout + kSeparator1 + kHostM1.substr(0, 1), 3, {kURLM1, kURLM2, kURLM3}},
    210     {kAbout + kSeparator2 + kHostM1.substr(0, 2), 3, {kURLM1, kURLM2, kURLM3}},
    211     {kAbout + kSeparator3 + kHostM1.substr(0, 3), 1, {kURLM1}},
    212     {kAbout + kSeparator3 + kHostM2.substr(0, 3), 2, {kURLM2, kURLM3}},
    213     {kAbout + kSeparator3 + kHostM1,              1, {kURLM1}},
    214     {kAbout + kSeparator2 + kHostM2,              2, {kURLM2, kURLM3}},
    215     {kAbout + kSeparator2 + kHostM3,              1, {kURLM3}},
    216 
    217     // Typing a chrome URL should provide matching URLs.
    218     {kChrome + kSeparator1 + kHostM1.substr(0, 1), 3, {kURLM1, kURLM2, kURLM3}},
    219     {kChrome + kSeparator2 + kHostM1.substr(0, 2), 3, {kURLM1, kURLM2, kURLM3}},
    220     {kChrome + kSeparator3 + kHostM1.substr(0, 3), 1, {kURLM1}},
    221     {kChrome + kSeparator3 + kHostM2.substr(0, 3), 2, {kURLM2, kURLM3}},
    222     {kChrome + kSeparator3 + kHostM1,              1, {kURLM1}},
    223     {kChrome + kSeparator2 + kHostM2,              2, {kURLM2, kURLM3}},
    224     {kChrome + kSeparator2 + kHostM3,              1, {kURLM3}},
    225   };
    226 
    227   RunTest(chrome_url_cases, arraysize(chrome_url_cases));
    228 }
    229 
    230 TEST_F(BuiltinProviderTest, AboutBlank) {
    231   const base::string16 kAbout = ASCIIToUTF16(url::kAboutScheme);
    232   const base::string16 kChrome = ASCIIToUTF16(content::kChromeUIScheme);
    233   const base::string16 kAboutBlank = ASCIIToUTF16(url::kAboutBlankURL);
    234   const base::string16 kBlank = ASCIIToUTF16("blank");
    235   const base::string16 kSeparator1 =
    236       ASCIIToUTF16(url::kStandardSchemeSeparator);
    237   const base::string16 kSeparator2 = ASCIIToUTF16(":///");
    238   const base::string16 kSeparator3 = ASCIIToUTF16(";///");
    239 
    240   const GURL kURLBlob = GURL(kChrome + kSeparator1 +
    241                              ASCIIToUTF16(content::kChromeUIBlobInternalsHost));
    242   const GURL kURLBlank = GURL(kAboutBlank);
    243 
    244   TestData about_blank_cases[] = {
    245     // Typing an about:blank prefix should yield about:blank, among other URLs.
    246     {kAboutBlank.substr(0, 8), 2, {kURLBlank, kURLBlob}},
    247     {kAboutBlank.substr(0, 9), 1, {kURLBlank}},
    248 
    249     // Using any separator that is supported by fixup should yield about:blank.
    250     // For now, BuiltinProvider does not suggest url-what-you-typed matches for
    251     // for about:blank; check "about:blan" and "about;blan" substrings instead.
    252     {kAbout + kSeparator2.substr(0, 1) + kBlank.substr(0, 4), 1, {kURLBlank}},
    253     {kAbout + kSeparator2.substr(0, 2) + kBlank,              1, {kURLBlank}},
    254     {kAbout + kSeparator2.substr(0, 3) + kBlank,              1, {kURLBlank}},
    255     {kAbout + kSeparator2 + kBlank,                           1, {kURLBlank}},
    256     {kAbout + kSeparator3.substr(0, 1) + kBlank.substr(0, 4), 1, {kURLBlank}},
    257     {kAbout + kSeparator3.substr(0, 2) + kBlank,              1, {kURLBlank}},
    258     {kAbout + kSeparator3.substr(0, 3) + kBlank,              1, {kURLBlank}},
    259     {kAbout + kSeparator3 + kBlank,                           1, {kURLBlank}},
    260 
    261     // Using the chrome scheme should not yield about:blank.
    262     {kChrome + kSeparator1.substr(0, 1) + kBlank, 0, {}},
    263     {kChrome + kSeparator1.substr(0, 2) + kBlank, 0, {}},
    264     {kChrome + kSeparator1.substr(0, 3) + kBlank, 0, {}},
    265     {kChrome + kSeparator1 + kBlank,              0, {}},
    266 
    267     // Adding trailing text should not yield about:blank.
    268     {kAboutBlank + ASCIIToUTF16("/"),  0, {}},
    269     {kAboutBlank + ASCIIToUTF16("/p"), 0, {}},
    270     {kAboutBlank + ASCIIToUTF16("x"),  0, {}},
    271     {kAboutBlank + ASCIIToUTF16("?q"), 0, {}},
    272     {kAboutBlank + ASCIIToUTF16("#r"), 0, {}},
    273 
    274     // Interrupting "blank" with conflicting text should not yield about:blank.
    275     {kAboutBlank.substr(0, 9) + ASCIIToUTF16("/"),  0, {}},
    276     {kAboutBlank.substr(0, 9) + ASCIIToUTF16("/p"), 0, {}},
    277     {kAboutBlank.substr(0, 9) + ASCIIToUTF16("x"),  0, {}},
    278     {kAboutBlank.substr(0, 9) + ASCIIToUTF16("?q"), 0, {}},
    279     {kAboutBlank.substr(0, 9) + ASCIIToUTF16("#r"), 0, {}},
    280   };
    281 
    282   RunTest(about_blank_cases, arraysize(about_blank_cases));
    283 }
    284 
    285 #if !defined(OS_ANDROID)
    286 // Disabled on Android where we use native UI instead of chrome://settings.
    287 TEST_F(BuiltinProviderTest, ChromeSettingsSubpages) {
    288   // This makes assumptions about the chrome URLs listed by the BuiltinProvider.
    289   // Currently they are derived from chrome::kChromeHostURLs[].
    290   const base::string16 kSettings = ASCIIToUTF16(chrome::kChromeUISettingsURL);
    291   const base::string16 kDefaultPage1 = ASCIIToUTF16(chrome::kAutofillSubPage);
    292   const base::string16 kDefaultPage2 =
    293       ASCIIToUTF16(chrome::kClearBrowserDataSubPage);
    294   const GURL kDefaultURL1 = GURL(kSettings + kDefaultPage1);
    295   const GURL kDefaultURL2 = GURL(kSettings + kDefaultPage2);
    296   const base::string16 kPage1 = ASCIIToUTF16(chrome::kSearchEnginesSubPage);
    297   const base::string16 kPage2 = ASCIIToUTF16(chrome::kSyncSetupSubPage);
    298   const GURL kURL1 = GURL(kSettings + kPage1);
    299   const GURL kURL2 = GURL(kSettings + kPage2);
    300 
    301   TestData settings_subpage_cases[] = {
    302     // Typing the settings path should show settings and the first two subpages.
    303     {kSettings, 3, {GURL(kSettings), kDefaultURL1, kDefaultURL2}},
    304 
    305     // Typing a subpage path should return the appropriate results.
    306     {kSettings + kPage1.substr(0, 1),                   2, {kURL1, kURL2}},
    307     {kSettings + kPage1.substr(0, 2),                   1, {kURL1}},
    308     {kSettings + kPage1.substr(0, kPage1.length() - 1), 1, {kURL1}},
    309     {kSettings + kPage1,                                1, {kURL1}},
    310     {kSettings + kPage2,                                1, {kURL2}},
    311   };
    312 
    313   RunTest(settings_subpage_cases, arraysize(settings_subpage_cases));
    314 }
    315 #endif
    316