Move Webstore URL concepts to //extensions and out
[chromium-blink-merge.git] / chrome / browser / autocomplete / keyword_extensions_delegate_impl.cc
blob3af9ed9e105a71ce04c1f1efc8d039fb66884e5e
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.
5 #include "chrome/browser/autocomplete/keyword_extensions_delegate_impl.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/extensions/extension_util.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "content/public/browser/notification_details.h"
13 #include "content/public/browser/notification_source.h"
14 #include "extensions/browser/extension_registry.h"
15 #include "extensions/browser/notification_types.h"
17 namespace omnibox_api = extensions::api::omnibox;
19 int KeywordExtensionsDelegateImpl::global_input_uid_ = 0;
21 KeywordExtensionsDelegateImpl::KeywordExtensionsDelegateImpl(
22 Profile* profile,
23 KeywordProvider* provider)
24 : KeywordExtensionsDelegate(provider),
25 profile_(profile),
26 provider_(provider) {
27 DCHECK(provider_);
29 current_input_id_ = 0;
30 // Extension suggestions always come from the original profile, since that's
31 // where extensions run. We use the input ID to distinguish whether the
32 // suggestions are meant for us.
33 registrar_.Add(this,
34 extensions::NOTIFICATION_EXTENSION_OMNIBOX_SUGGESTIONS_READY,
35 content::Source<Profile>(profile_->GetOriginalProfile()));
36 registrar_.Add(
37 this,
38 extensions::NOTIFICATION_EXTENSION_OMNIBOX_DEFAULT_SUGGESTION_CHANGED,
39 content::Source<Profile>(profile_->GetOriginalProfile()));
40 registrar_.Add(this,
41 extensions::NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED,
42 content::Source<Profile>(profile_));
45 KeywordExtensionsDelegateImpl::~KeywordExtensionsDelegateImpl() {
48 void KeywordExtensionsDelegateImpl::IncrementInputId() {
49 current_input_id_ = ++global_input_uid_;
52 bool KeywordExtensionsDelegateImpl::IsEnabledExtension(
53 const std::string& extension_id) {
54 const extensions::Extension* extension =
55 extensions::ExtensionRegistry::Get(
56 profile_)->enabled_extensions().GetByID(extension_id);
57 return extension &&
58 (!profile_->IsOffTheRecord() ||
59 extensions::util::IsIncognitoEnabled(extension_id, profile_));
62 bool KeywordExtensionsDelegateImpl::Start(
63 const AutocompleteInput& input,
64 bool minimal_changes,
65 const TemplateURL* template_url,
66 const base::string16& remaining_input) {
67 DCHECK(template_url);
69 if (input.want_asynchronous_matches()) {
70 std::string extension_id = template_url->GetExtensionId();
71 if (extension_id != current_keyword_extension_id_)
72 MaybeEndExtensionKeywordMode();
73 if (current_keyword_extension_id_.empty())
74 EnterExtensionKeywordMode(extension_id);
77 extensions::ApplyDefaultSuggestionForExtensionKeyword(
78 profile_, template_url, remaining_input, &matches()->front());
80 if (minimal_changes) {
81 // If the input hasn't significantly changed, we can just use the
82 // suggestions from last time. We need to readjust the relevance to
83 // ensure it is less than the main match's relevance.
84 for (size_t i = 0; i < extension_suggest_matches_.size(); ++i) {
85 matches()->push_back(extension_suggest_matches_[i]);
86 matches()->back().relevance = matches()->front().relevance - (i + 1);
88 } else if (input.want_asynchronous_matches()) {
89 extension_suggest_last_input_ = input;
90 extension_suggest_matches_.clear();
92 // We only have to wait for suggest results if there are actually
93 // extensions listening for input changes.
94 if (extensions::ExtensionOmniboxEventRouter::OnInputChanged(
95 profile_, template_url->GetExtensionId(),
96 base::UTF16ToUTF8(remaining_input), current_input_id_))
97 set_done(false);
99 return input.want_asynchronous_matches();
102 void KeywordExtensionsDelegateImpl::EnterExtensionKeywordMode(
103 const std::string& extension_id) {
104 DCHECK(current_keyword_extension_id_.empty());
105 current_keyword_extension_id_ = extension_id;
107 extensions::ExtensionOmniboxEventRouter::OnInputStarted(
108 profile_, current_keyword_extension_id_);
111 void KeywordExtensionsDelegateImpl::MaybeEndExtensionKeywordMode() {
112 if (!current_keyword_extension_id_.empty()) {
113 extensions::ExtensionOmniboxEventRouter::OnInputCancelled(
114 profile_, current_keyword_extension_id_);
115 current_keyword_extension_id_.clear();
119 void KeywordExtensionsDelegateImpl::Observe(
120 int type,
121 const content::NotificationSource& source,
122 const content::NotificationDetails& details) {
123 TemplateURLService* model = provider_->GetTemplateURLService();
124 const AutocompleteInput& input = extension_suggest_last_input_;
126 switch (type) {
127 case extensions::NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED:
128 // Input has been accepted, so we're done with this input session. Ensure
129 // we don't send the OnInputCancelled event, or handle any more stray
130 // suggestions_ready events.
131 current_keyword_extension_id_.clear();
132 current_input_id_ = 0;
133 return;
135 case extensions::NOTIFICATION_EXTENSION_OMNIBOX_DEFAULT_SUGGESTION_CHANGED
137 // It's possible to change the default suggestion while not in an editing
138 // session.
139 base::string16 keyword, remaining_input;
140 if (matches()->empty() || current_keyword_extension_id_.empty() ||
141 !KeywordProvider::ExtractKeywordFromInput(
142 input, &keyword, &remaining_input))
143 return;
145 const TemplateURL* template_url(
146 model->GetTemplateURLForKeyword(keyword));
147 extensions::ApplyDefaultSuggestionForExtensionKeyword(
148 profile_, template_url, remaining_input, &matches()->front());
149 OnProviderUpdate(true);
150 return;
153 case extensions::NOTIFICATION_EXTENSION_OMNIBOX_SUGGESTIONS_READY: {
154 const omnibox_api::SendSuggestions::Params& suggestions =
155 *content::Details<
156 omnibox_api::SendSuggestions::Params>(details).ptr();
157 if (suggestions.request_id != current_input_id_)
158 return; // This is an old result. Just ignore.
160 base::string16 keyword, remaining_input;
161 bool result = KeywordProvider::ExtractKeywordFromInput(
162 input, &keyword, &remaining_input);
163 DCHECK(result);
164 const TemplateURL* template_url =
165 model->GetTemplateURLForKeyword(keyword);
167 // TODO(mpcomplete): consider clamping the number of suggestions to
168 // AutocompleteProvider::kMaxMatches.
169 for (size_t i = 0; i < suggestions.suggest_results.size(); ++i) {
170 const omnibox_api::SuggestResult& suggestion =
171 *suggestions.suggest_results[i];
172 // We want to order these suggestions in descending order, so start with
173 // the relevance of the first result (added synchronously in Start()),
174 // and subtract 1 for each subsequent suggestion from the extension.
175 // We recompute the first match's relevance; we know that |complete|
176 // is true, because we wouldn't get results from the extension unless
177 // the full keyword had been typed.
178 int first_relevance = KeywordProvider::CalculateRelevance(
179 input.type(), true, true, input.prefer_keyword(),
180 input.allow_exact_keyword_match());
181 // Because these matches are async, we should never let them become the
182 // default match, lest we introduce race conditions in the omnibox user
183 // interaction.
184 extension_suggest_matches_.push_back(
185 provider_->CreateAutocompleteMatch(
186 template_url, input, keyword.length(),
187 base::UTF8ToUTF16(suggestion.content), false,
188 first_relevance - (i + 1)));
190 AutocompleteMatch* match = &extension_suggest_matches_.back();
191 match->contents.assign(base::UTF8ToUTF16(suggestion.description));
192 match->contents_class =
193 extensions::StyleTypesToACMatchClassifications(suggestion);
196 set_done(true);
197 matches()->insert(matches()->end(),
198 extension_suggest_matches_.begin(),
199 extension_suggest_matches_.end());
200 OnProviderUpdate(!extension_suggest_matches_.empty());
201 return;
204 default:
205 NOTREACHED();
206 return;
210 void KeywordExtensionsDelegateImpl::OnProviderUpdate(bool updated_matches) {
211 provider_->listener_->OnProviderUpdate(updated_matches);