builtin_provider.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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 <algorithm>
8
9#include "base/strings/string_util.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/autocomplete/autocomplete_input.h"
12#include "chrome/browser/autocomplete/history_provider.h"
13#include "chrome/common/url_constants.h"
14#include "components/metrics/proto/omnibox_input_type.pb.h"
15#include "components/url_fixer/url_fixer.h"
16
17namespace {
18
19#if !defined(OS_ANDROID)
20// This list should be kept in sync with chrome/common/url_constants.h.
21// Only include useful sub-pages, confirmation alerts are not useful.
22const char* const kChromeSettingsSubPages[] = {
23  chrome::kAutofillSubPage,
24  chrome::kClearBrowserDataSubPage,
25  chrome::kContentSettingsSubPage,
26  chrome::kContentSettingsExceptionsSubPage,
27  chrome::kImportDataSubPage,
28  chrome::kLanguageOptionsSubPage,
29  chrome::kPasswordManagerSubPage,
30  chrome::kResetProfileSettingsSubPage,
31  chrome::kSearchEnginesSubPage,
32  chrome::kSyncSetupSubPage,
33#if defined(OS_CHROMEOS)
34  chrome::kInternetOptionsSubPage,
35#endif
36};
37#endif // !defined(OS_ANDROID)
38
39}  // namespace
40
41const int BuiltinProvider::kRelevance = 860;
42
43BuiltinProvider::BuiltinProvider(AutocompleteProviderListener* listener,
44                                 Profile* profile)
45    : AutocompleteProvider(listener, profile,
46          AutocompleteProvider::TYPE_BUILTIN) {
47  std::vector<std::string> builtins(
48      chrome::kChromeHostURLs,
49      chrome::kChromeHostURLs + chrome::kNumberOfChromeHostURLs);
50  std::sort(builtins.begin(), builtins.end());
51  for (std::vector<std::string>::iterator i(builtins.begin());
52       i != builtins.end(); ++i)
53    builtins_.push_back(base::ASCIIToUTF16(*i));
54
55#if !defined(OS_ANDROID)
56  base::string16 settings(base::ASCIIToUTF16(chrome::kChromeUISettingsHost) +
57                          base::ASCIIToUTF16("/"));
58  for (size_t i = 0; i < arraysize(kChromeSettingsSubPages); i++) {
59    builtins_.push_back(
60        settings + base::ASCIIToUTF16(kChromeSettingsSubPages[i]));
61  }
62#endif
63}
64
65void BuiltinProvider::Start(const AutocompleteInput& input,
66                            bool minimal_changes) {
67  matches_.clear();
68  if ((input.type() == metrics::OmniboxInputType::INVALID) ||
69      (input.type() == metrics::OmniboxInputType::FORCED_QUERY) ||
70      (input.type() == metrics::OmniboxInputType::QUERY))
71    return;
72
73  const size_t kAboutSchemeLength = strlen(url::kAboutScheme);
74  const base::string16 kAbout =
75      base::ASCIIToUTF16(url::kAboutScheme) +
76      base::ASCIIToUTF16(url::kStandardSchemeSeparator);
77  const base::string16 kChrome = base::ASCIIToUTF16(content::kChromeUIScheme) +
78      base::ASCIIToUTF16(url::kStandardSchemeSeparator);
79
80  const int kUrl = ACMatchClassification::URL;
81  const int kMatch = kUrl | ACMatchClassification::MATCH;
82
83  base::string16 text = input.text();
84  bool starting_chrome = StartsWith(kChrome, text, false);
85  if (starting_chrome || StartsWith(kAbout, text, false)) {
86    ACMatchClassifications styles;
87    // Highlight the input portion matching "chrome://"; or if the user has
88    // input "about:" (with optional slashes), highlight the whole "chrome://".
89    bool highlight = starting_chrome || text.length() > kAboutSchemeLength;
90    styles.push_back(ACMatchClassification(0, highlight ? kMatch : kUrl));
91    size_t offset = starting_chrome ? text.length() : kChrome.length();
92    if (highlight)
93      styles.push_back(ACMatchClassification(offset, kUrl));
94    // Include some common builtin chrome URLs as the user types the scheme.
95    AddMatch(base::ASCIIToUTF16(chrome::kChromeUIChromeURLsURL),
96             base::string16(), styles);
97#if !defined(OS_ANDROID)
98    AddMatch(base::ASCIIToUTF16(chrome::kChromeUISettingsURL),
99             base::string16(), styles);
100#endif
101    AddMatch(base::ASCIIToUTF16(chrome::kChromeUIVersionURL),
102             base::string16(), styles);
103  } else {
104    // Match input about: or chrome: URL input against builtin chrome URLs.
105    GURL url = url_fixer::FixupURL(base::UTF16ToUTF8(text), std::string());
106    // BuiltinProvider doesn't know how to suggest valid ?query or #fragment
107    // extensions to chrome: URLs.
108    if (url.SchemeIs(content::kChromeUIScheme) && url.has_host() &&
109        !url.has_query() && !url.has_ref()) {
110      // Suggest about:blank for substrings, taking URL fixup into account.
111      // Chrome does not support trailing slashes or paths for about:blank.
112      const base::string16 blank_host = base::ASCIIToUTF16("blank");
113      const base::string16 host = base::UTF8ToUTF16(url.host());
114      if (StartsWith(text, base::ASCIIToUTF16(url::kAboutScheme), false) &&
115          StartsWith(blank_host, host, false) && (url.path().length() <= 1) &&
116          !EndsWith(text, base::ASCIIToUTF16("/"), false)) {
117        ACMatchClassifications styles;
118        styles.push_back(ACMatchClassification(0, kMatch));
119        base::string16 match = base::ASCIIToUTF16(url::kAboutBlankURL);
120        // Measure the length of the matching host after the "about:" scheme.
121        const size_t corrected_length = kAboutSchemeLength + 1 + host.length();
122        if (blank_host.length() > host.length())
123          styles.push_back(ACMatchClassification(corrected_length, kUrl));
124        AddMatch(match, match.substr(corrected_length), styles);
125      }
126
127      // Include the path for sub-pages (e.g. "chrome://settings/browser").
128      base::string16 host_and_path = base::UTF8ToUTF16(url.host() + url.path());
129      base::TrimString(host_and_path, base::ASCIIToUTF16("/"), &host_and_path);
130      size_t match_length = kChrome.length() + host_and_path.length();
131      for (Builtins::const_iterator i(builtins_.begin());
132          (i != builtins_.end()) && (matches_.size() < kMaxMatches); ++i) {
133        if (StartsWith(*i, host_and_path, false)) {
134          ACMatchClassifications styles;
135          // Highlight the "chrome://" scheme, even for input "about:foo".
136          styles.push_back(ACMatchClassification(0, kMatch));
137          base::string16 match_string = kChrome + *i;
138          if (match_string.length() > match_length)
139            styles.push_back(ACMatchClassification(match_length, kUrl));
140          AddMatch(match_string, match_string.substr(match_length), styles);
141        }
142      }
143    }
144  }
145
146  for (size_t i = 0; i < matches_.size(); ++i)
147    matches_[i].relevance = kRelevance + matches_.size() - (i + 1);
148  if (!HistoryProvider::PreventInlineAutocomplete(input) &&
149      (matches_.size() == 1)) {
150    // If there's only one possible completion of the user's input and
151    // allowing completions is okay, give the match a high enough score to
152    // allow it to beat url-what-you-typed and be inlined.
153    matches_[0].relevance = 1250;
154    matches_[0].allowed_to_be_default_match = true;
155  }
156}
157
158BuiltinProvider::~BuiltinProvider() {}
159
160void BuiltinProvider::AddMatch(const base::string16& match_string,
161                               const base::string16& inline_completion,
162                               const ACMatchClassifications& styles) {
163  AutocompleteMatch match(this, kRelevance, false,
164                          AutocompleteMatchType::NAVSUGGEST);
165  match.fill_into_edit = match_string;
166  match.inline_autocompletion = inline_completion;
167  match.destination_url = GURL(match_string);
168  match.contents = match_string;
169  match.contents_class = styles;
170  matches_.push_back(match);
171}
172