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.
5 #include "components/omnibox/browser/builtin_provider.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "components/metrics/proto/omnibox_input_type.pb.h"
12 #include "components/omnibox/browser/autocomplete_input.h"
13 #include "components/omnibox/browser/autocomplete_provider_client.h"
14 #include "components/omnibox/browser/history_provider.h"
15 #include "components/url_formatter/url_fixer.h"
17 const int BuiltinProvider::kRelevance
= 860;
19 BuiltinProvider::BuiltinProvider(AutocompleteProviderClient
* client
)
20 : AutocompleteProvider(AutocompleteProvider::TYPE_BUILTIN
),
22 builtins_
= client_
->GetBuiltinURLs();
25 void BuiltinProvider::Start(const AutocompleteInput
& input
,
26 bool minimal_changes
) {
28 if (input
.from_omnibox_focus() ||
29 (input
.type() == metrics::OmniboxInputType::INVALID
) ||
30 (input
.type() == metrics::OmniboxInputType::FORCED_QUERY
) ||
31 (input
.type() == metrics::OmniboxInputType::QUERY
))
34 const size_t kAboutSchemeLength
= strlen(url::kAboutScheme
);
35 const base::string16 kAbout
=
36 base::ASCIIToUTF16(url::kAboutScheme
) +
37 base::ASCIIToUTF16(url::kStandardSchemeSeparator
);
38 const base::string16 embedderAbout
=
39 base::UTF8ToUTF16(client_
->GetEmbedderRepresentationOfAboutScheme()) +
40 base::ASCIIToUTF16(url::kStandardSchemeSeparator
);
42 const int kUrl
= ACMatchClassification::URL
;
43 const int kMatch
= kUrl
| ACMatchClassification::MATCH
;
45 const base::string16 text
= input
.text();
46 bool starting_about
= base::StartsWith(embedderAbout
, text
,
47 base::CompareCase::INSENSITIVE_ASCII
);
49 base::StartsWith(kAbout
, text
, base::CompareCase::INSENSITIVE_ASCII
)) {
50 ACMatchClassifications styles
;
51 // Highlight the input portion matching |embedderAbout|; or if the user has
52 // input "about:" (with optional slashes), highlight the whole
54 bool highlight
= starting_about
|| text
.length() > kAboutSchemeLength
;
55 styles
.push_back(ACMatchClassification(0, highlight
? kMatch
: kUrl
));
56 size_t offset
= starting_about
? text
.length() : embedderAbout
.length();
58 styles
.push_back(ACMatchClassification(offset
, kUrl
));
59 // Include some common builtin URLs as the user types the scheme.
60 for (base::string16 url
: client_
->GetBuiltinsToProvideAsUserTypes())
61 AddMatch(url
, base::string16(), styles
);
63 // Match input about: or |embedderAbout| URL input against builtin URLs.
64 GURL url
= url_formatter::FixupURL(base::UTF16ToUTF8(text
), std::string());
65 // BuiltinProvider doesn't know how to suggest valid ?query or #fragment
66 // extensions to builtin URLs.
68 client_
->GetEmbedderRepresentationOfAboutScheme().c_str()) &&
69 url
.has_host() && !url
.has_query() && !url
.has_ref()) {
70 // Suggest about:blank for substrings, taking URL fixup into account.
71 // Chrome does not support trailing slashes or paths for about:blank.
72 const base::string16 blank_host
= base::ASCIIToUTF16("blank");
73 const base::string16 host
= base::UTF8ToUTF16(url
.host());
74 if (base::StartsWith(text
, base::ASCIIToUTF16(url::kAboutScheme
),
75 base::CompareCase::INSENSITIVE_ASCII
) &&
76 base::StartsWith(blank_host
, host
,
77 base::CompareCase::INSENSITIVE_ASCII
) &&
78 (url
.path().length() <= 1) &&
79 !base::EndsWith(text
, base::ASCIIToUTF16("/"),
80 base::CompareCase::SENSITIVE
)) {
81 ACMatchClassifications styles
;
82 styles
.push_back(ACMatchClassification(0, kMatch
));
83 base::string16 match
= base::ASCIIToUTF16(url::kAboutBlankURL
);
84 // Measure the length of the matching host after the "about:" scheme.
85 const size_t corrected_length
= kAboutSchemeLength
+ 1 + host
.length();
86 if (blank_host
.length() > host
.length())
87 styles
.push_back(ACMatchClassification(corrected_length
, kUrl
));
88 AddMatch(match
, match
.substr(corrected_length
), styles
);
91 // Include the path for sub-pages (e.g. "chrome://settings/browser").
92 base::string16 host_and_path
= base::UTF8ToUTF16(url
.host() + url
.path());
93 base::TrimString(host_and_path
, base::ASCIIToUTF16("/"), &host_and_path
);
94 size_t match_length
= embedderAbout
.length() + host_and_path
.length();
95 for (Builtins::const_iterator
i(builtins_
.begin());
96 (i
!= builtins_
.end()) && (matches_
.size() < kMaxMatches
); ++i
) {
97 if (base::StartsWith(*i
, host_and_path
,
98 base::CompareCase::INSENSITIVE_ASCII
)) {
99 ACMatchClassifications styles
;
100 // Highlight |embedderAbout|, even for input "about:foo".
101 styles
.push_back(ACMatchClassification(0, kMatch
));
102 base::string16 match_string
= embedderAbout
+ *i
;
103 if (match_string
.length() > match_length
)
104 styles
.push_back(ACMatchClassification(match_length
, kUrl
));
105 AddMatch(match_string
, match_string
.substr(match_length
), styles
);
111 for (size_t i
= 0; i
< matches_
.size(); ++i
)
112 matches_
[i
].relevance
= kRelevance
+ matches_
.size() - (i
+ 1);
113 if (!HistoryProvider::PreventInlineAutocomplete(input
) &&
114 (matches_
.size() == 1)) {
115 // If there's only one possible completion of the user's input and
116 // allowing completions is okay, give the match a high enough score to
117 // allow it to beat url-what-you-typed and be inlined.
118 matches_
[0].relevance
= 1250;
119 matches_
[0].allowed_to_be_default_match
= true;
123 BuiltinProvider::~BuiltinProvider() {}
125 void BuiltinProvider::AddMatch(const base::string16
& match_string
,
126 const base::string16
& inline_completion
,
127 const ACMatchClassifications
& styles
) {
128 AutocompleteMatch
match(this, kRelevance
, false,
129 AutocompleteMatchType::NAVSUGGEST
);
130 match
.fill_into_edit
= match_string
;
131 match
.inline_autocompletion
= inline_completion
;
132 match
.destination_url
= GURL(match_string
);
133 match
.contents
= match_string
;
134 match
.contents_class
= styles
;
135 matches_
.push_back(match
);