Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Unified Diff: chrome/browser/autocomplete/keyword_provider.cc

Issue 2078021: First pass at experimental omnibox API. There are plenty of rough edges and (Closed)
Patch Set: no prefer_keyword Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/autocomplete/keyword_provider.h ('k') | chrome/browser/cocoa/location_bar_view_mac.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/autocomplete/keyword_provider.cc
diff --git a/chrome/browser/autocomplete/keyword_provider.cc b/chrome/browser/autocomplete/keyword_provider.cc
index 9f841ccea1338ab19188ce9924a4a22d5b11a921..a0130b739fe252d2d24db1807b20f2747d1c1de5 100644
--- a/chrome/browser/autocomplete/keyword_provider.cc
+++ b/chrome/browser/autocomplete/keyword_provider.cc
@@ -8,10 +8,13 @@
#include <vector>
#include "app/l10n_util.h"
+#include "base/string16.h"
#include "base/utf_string_conversions.h"
+#include "chrome/browser/extensions/extension_omnibox_api.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/common/notification_service.h"
#include "grit/generated_resources.h"
#include "net/base/escape.h"
#include "net/base/net_util.h"
@@ -31,13 +34,17 @@ std::wstring KeywordProvider::SplitReplacementStringFromInput(
KeywordProvider::KeywordProvider(ACProviderListener* listener, Profile* profile)
: AutocompleteProvider(listener, profile, "Keyword"),
- model_(NULL) {
+ model_(NULL),
+ current_input_id_(0) {
+ registrar_.Add(this, NotificationType::EXTENSION_OMNIBOX_SUGGESTIONS_READY,
+ Source<Profile>(profile));
}
KeywordProvider::KeywordProvider(ACProviderListener* listener,
TemplateURLModel* model)
: AutocompleteProvider(listener, NULL, "Keyword"),
- model_(model) {
+ model_(model),
+ current_input_id_(0) {
}
@@ -84,6 +91,14 @@ void KeywordProvider::Start(const AutocompleteInput& input,
bool minimal_changes) {
matches_.clear();
+ if (!minimal_changes) {
+ done_ = true;
+
+ // Input has changed. Increment the input ID so that we can discard any
+ // stale extension suggestions that may be incoming.
+ ++current_input_id_;
+ }
+
// Split user input into a keyword and some query input.
//
// We want to suggest keywords even when users have started typing URLs, on
@@ -131,7 +146,33 @@ void KeywordProvider::Start(const AutocompleteInput& input,
if (keyword_matches.front() == keyword) {
matches_.push_back(CreateAutocompleteMatch(model, keyword, input,
keyword.length(),
- remaining_input));
+ remaining_input, -1));
+
+ const TemplateURL* template_url(model->GetTemplateURLForKeyword(keyword));
+ if (profile_ &&
+ !input.synchronous_only() && template_url->IsExtensionKeyword()) {
+ if (minimal_changes) {
+ // If the input hasn't significantly changed, we can just use the
+ // suggestions from last time. We need to readjust the relevance to
+ // ensure it is less than the main match's relevance.
+ for (size_t i = 0; i < extension_suggest_matches_.size(); ++i) {
+ matches_.push_back(extension_suggest_matches_[i]);
+ matches_.back().relevance = matches_[0].relevance - (i + 1);
+ }
+ } else {
+ extension_suggest_last_input_ = input;
+ extension_suggest_matches_.clear();
+
+ bool have_listeners = ExtensionOmniboxEventRouter::OnInputChanged(
+ profile_, template_url->GetExtensionId(),
+ WideToUTF8(remaining_input), current_input_id_);
+
+ // We only have to wait for suggest results if there are actually
+ // extensions listening for input changes.
+ if (have_listeners)
+ done_ = false;
+ }
+ }
} else {
if (keyword_matches.size() > kMaxMatches) {
keyword_matches.erase(keyword_matches.begin() + kMaxMatches,
@@ -141,7 +182,7 @@ void KeywordProvider::Start(const AutocompleteInput& input,
i != keyword_matches.end(); ++i) {
matches_.push_back(CreateAutocompleteMatch(model, *i, input,
keyword.length(),
- remaining_input));
+ remaining_input, -1));
}
}
}
@@ -189,10 +230,12 @@ void KeywordProvider::FillInURLAndContents(
DCHECK(!element->short_name().empty());
DCHECK(element->url());
DCHECK(element->url()->IsValid());
+ int message_id = element->IsExtensionKeyword() ?
+ IDS_EXTENSION_KEYWORD_COMMAND : IDS_KEYWORD_SEARCH;
if (remaining_input.empty()) {
if (element->url()->SupportsReplacement()) {
// No query input; return a generic, no-destination placeholder.
- match->contents.assign(l10n_util::GetStringF(IDS_KEYWORD_SEARCH,
+ match->contents.assign(l10n_util::GetStringF(message_id,
element->AdjustedShortNameForLocaleDirection(),
l10n_util::GetString(IDS_EMPTY_KEYWORD_VALUE)));
match->contents_class.push_back(
@@ -215,7 +258,7 @@ void KeywordProvider::FillInURLAndContents(
*element, remaining_input, TemplateURLRef::NO_SUGGESTIONS_AVAILABLE,
std::wstring())));
std::vector<size_t> content_param_offsets;
- match->contents.assign(l10n_util::GetStringF(IDS_KEYWORD_SEARCH,
+ match->contents.assign(l10n_util::GetStringF(message_id,
element->short_name(),
remaining_input,
&content_param_offsets));
@@ -246,7 +289,8 @@ AutocompleteMatch KeywordProvider::CreateAutocompleteMatch(
const std::wstring keyword,
const AutocompleteInput& input,
size_t prefix_length,
- const std::wstring& remaining_input) {
+ const std::wstring& remaining_input,
+ int relevance) {
DCHECK(model);
// Get keyword data from data store.
const TemplateURL* element(model->GetTemplateURLForKeyword(keyword));
@@ -257,14 +301,17 @@ AutocompleteMatch KeywordProvider::CreateAutocompleteMatch(
// even when [remaining input] is empty, as the user can select the popup
// choice and immediately begin typing in query input.
const bool keyword_complete = (prefix_length == keyword.length());
- AutocompleteMatch result(this,
- CalculateRelevance(input.type(), keyword_complete,
- // When the user wants keyword matches to take
- // preference, score them highly regardless of whether
- // the input provides query text.
- input.prefer_keyword() || !supports_replacement),
- false, supports_replacement ? AutocompleteMatch::SEARCH_OTHER_ENGINE :
- AutocompleteMatch::HISTORY_KEYWORD);
+ if (relevance < 0) {
+ relevance =
+ CalculateRelevance(input.type(), keyword_complete,
+ // When the user wants keyword matches to take
+ // preference, score them highly regardless of
+ // whether the input provides query text.
+ input.prefer_keyword() || !supports_replacement);
+ }
+ AutocompleteMatch result(this, relevance, false,
+ supports_replacement ? AutocompleteMatch::SEARCH_OTHER_ENGINE :
+ AutocompleteMatch::HISTORY_KEYWORD);
result.fill_into_edit.assign(keyword);
if (!remaining_input.empty() || !keyword_complete || supports_replacement)
result.fill_into_edit.push_back(L' ');
@@ -278,12 +325,13 @@ AutocompleteMatch KeywordProvider::CreateAutocompleteMatch(
FillInURLAndContents(remaining_input, element, &result);
// Create popup entry description based on the keyword name.
- result.description.assign(l10n_util::GetStringF(
- IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION, keyword));
+ int message_id = element->IsExtensionKeyword() ?
+ IDS_AUTOCOMPLETE_EXTENSION_KEYWORD_DESCRIPTION :
+ IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION;
+ result.description.assign(l10n_util::GetStringF(message_id, keyword));
if (supports_replacement)
result.template_url = element;
- static const std::wstring kKeywordDesc(l10n_util::GetString(
- IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION));
+ static const std::wstring kKeywordDesc(l10n_util::GetString(message_id));
AutocompleteMatch::ClassifyLocationInString(kKeywordDesc.find(L"%s"),
prefix_length,
result.description.length(),
@@ -294,3 +342,64 @@ AutocompleteMatch KeywordProvider::CreateAutocompleteMatch(
return result;
}
+
+void KeywordProvider::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ // TODO(mpcomplete): consider clamping the number of suggestions to
+ // AutocompleteProvider::kMaxMatches.
+ DCHECK(type == NotificationType::EXTENSION_OMNIBOX_SUGGESTIONS_READY);
+
+ int suggest_id = Details<ExtensionOmniboxSuggestions>(details).ptr()->first;
+ if (suggest_id != current_input_id_)
+ return; // This is an old result. Just ignore.
+
+ const AutocompleteInput& input = extension_suggest_last_input_;
+ std::wstring keyword, remaining_input;
+ if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) {
+ NOTREACHED();
+ return;
+ }
+
+ TemplateURLModel* model =
+ profile_ ? profile_->GetTemplateURLModel() : model_;
+
+ ListValue* suggestions =
+ Details<ExtensionOmniboxSuggestions>(details).ptr()->second;
+ for (size_t i = 0; i < suggestions->GetSize(); ++i) {
+ DictionaryValue* suggestion;
+ string16 content, description;
+ if (!suggestions->GetDictionary(i, &suggestion) ||
+ !suggestion->GetString("content", &content) ||
+ !suggestion->GetString("description", &description))
+ break;
+
+ // We want to order these suggestions in descending order, so start with
+ // the relevance of the first result (added synchronously in Start()),
+ // and subtract 1 for each subsequent suggestion from the extension.
+ // We know that |complete| is true, because we wouldn't get results from
+ // the extension unless the full keyword had been typed.
+ int first_relevance =
+ CalculateRelevance(input.type(), true, input.prefer_keyword());
+ extension_suggest_matches_.push_back(CreateAutocompleteMatch(
+ model, keyword, input, keyword.length(), UTF16ToWide(content),
+ first_relevance - (i + 1)));
+
+ if (!description.empty()) {
+ AutocompleteMatch* match = &extension_suggest_matches_.back();
+ std::vector<size_t> offsets;
+ match->contents.assign(l10n_util::GetStringF(
+ IDS_AUTOCOMPLETE_EXTENSION_KEYWORD_CONTENT,
+ match->contents, UTF16ToWide(description), &offsets));
+ CHECK_EQ(2U, offsets.size()) <<
+ "Expected 2 params for IDS_AUTOCOMPLETE_EXTENSION_KEYWORD_CONTENT";
+ match->contents_class.push_back(
+ ACMatchClassification(offsets[1], ACMatchClassification::NONE));
+ }
+ }
+
+ done_ = true;
+ matches_.insert(matches_.end(), extension_suggest_matches_.begin(),
+ extension_suggest_matches_.end());
+ listener_->OnProviderUpdate(!extension_suggest_matches_.empty());
+}
« no previous file with comments | « chrome/browser/autocomplete/keyword_provider.h ('k') | chrome/browser/cocoa/location_bar_view_mac.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698