omnibox_provider.cc revision d3868032626d59662ff73b372b5d584c1d144c53
1// Copyright 2013 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/ui/app_list/search/omnibox_provider.h"
6
7#include "chrome/browser/autocomplete/autocomplete_classifier.h"
8#include "chrome/browser/autocomplete/autocomplete_controller.h"
9#include "chrome/browser/autocomplete/autocomplete_input.h"
10#include "chrome/browser/autocomplete/autocomplete_match.h"
11#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
12#include "chrome/browser/ui/browser_navigator.h"
13#include "chrome/common/metrics/proto/omnibox_event.pb.h"
14#include "grit/theme_resources.h"
15#include "ui/base/resource/resource_bundle.h"
16
17namespace app_list {
18
19namespace {
20
21int ACMatchStyleToTagStyle(int styles) {
22  int tag_styles = 0;
23  if (styles & ACMatchClassification::URL)
24    tag_styles |= SearchResult::Tag::URL;
25  if (styles & ACMatchClassification::MATCH)
26    tag_styles |= SearchResult::Tag::MATCH;
27  if (styles & ACMatchClassification::DIM)
28    tag_styles |= SearchResult::Tag::DIM;
29
30  return tag_styles;
31}
32
33// Translates ACMatchClassifications into SearchResult tags.
34void ACMatchClassificationsToTags(
35    const string16& text,
36    const ACMatchClassifications& text_classes,
37    SearchResult::Tags* tags) {
38  int tag_styles = SearchResult::Tag::NONE;
39  size_t tag_start = 0;
40
41  for (size_t i = 0; i < text_classes.size(); ++i) {
42    const ACMatchClassification& text_class = text_classes[i];
43
44    // Closes current tag.
45    if (tag_styles != SearchResult::Tag::NONE) {
46      tags->push_back(SearchResult::Tag(
47          tag_styles, tag_start, text_class.offset));
48      tag_styles = SearchResult::Tag::NONE;
49    }
50
51    if (text_class.style == ACMatchClassification::NONE)
52      continue;
53
54    tag_start = text_class.offset;
55    tag_styles = ACMatchStyleToTagStyle(text_class.style);
56  }
57
58  if (tag_styles != SearchResult::Tag::NONE) {
59    tags->push_back(SearchResult::Tag(
60        tag_styles, tag_start, text.length()));
61  }
62}
63
64class OmniboxResult : public ChromeSearchResult {
65 public:
66  OmniboxResult(Profile* profile, const AutocompleteMatch& match)
67      : profile_(profile),
68        match_(match) {
69    set_id(match.destination_url.spec());
70
71    // Derive relevance from omnibox relevance and normalize it to [0, 1].
72    // The magic number 1500 is the highest score of an omnibox result.
73    // See comments in autocomplete_provider.h.
74    set_relevance(match.relevance / 1500.0);
75
76    UpdateIcon();
77    UpdateTitleAndDetails();
78  }
79  virtual ~OmniboxResult() {}
80
81  // ChromeSearchResult overides:
82  virtual void Open(int event_flags) OVERRIDE {
83    chrome::NavigateParams params(profile_,
84                                  match_.destination_url,
85                                  match_.transition);
86    params.disposition = ui::DispositionFromEventFlags(event_flags);
87    chrome::Navigate(&params);
88  }
89
90  virtual void InvokeAction(int action_index, int event_flags) OVERRIDE {}
91
92  virtual scoped_ptr<ChromeSearchResult> Duplicate() OVERRIDE {
93    return scoped_ptr<ChromeSearchResult>(
94        new OmniboxResult(profile_, match_)).Pass();
95  }
96
97  virtual ChromeSearchResultType GetType() OVERRIDE {
98    return OMNIBOX_SEARCH_RESULT;
99  }
100
101 private:
102  void UpdateIcon() {
103    int resource_id = match_.starred ?
104        IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match_.type);
105    SetIcon(*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
106        resource_id));
107  }
108
109  void UpdateTitleAndDetails() {
110    set_title(match_.contents);
111    SearchResult::Tags title_tags;
112    ACMatchClassificationsToTags(match_.contents,
113                                 match_.contents_class,
114                                 &title_tags);
115    set_title_tags(title_tags);
116
117    set_details(match_.description);
118    SearchResult::Tags details_tags;
119    ACMatchClassificationsToTags(match_.description,
120                                 match_.description_class,
121                                 &details_tags);
122    set_details_tags(details_tags);
123  }
124
125  Profile* profile_;
126  AutocompleteMatch match_;
127
128  DISALLOW_COPY_AND_ASSIGN(OmniboxResult);
129};
130
131}  // namespace
132
133OmniboxProvider::OmniboxProvider(Profile* profile)
134    : profile_(profile),
135      controller_(new AutocompleteController(
136          profile,
137          this,
138          AutocompleteClassifier::kDefaultOmniboxProviders)) {
139}
140
141OmniboxProvider::~OmniboxProvider() {}
142
143void OmniboxProvider::Start(const base::string16& query) {
144  controller_->Start(AutocompleteInput(query,
145                                       base::string16::npos,
146                                       base::string16(),
147                                       GURL(),
148                                       AutocompleteInput::INVALID_SPEC,
149                                       false,
150                                       false,
151                                       true,
152                                       AutocompleteInput::ALL_MATCHES));
153}
154
155void OmniboxProvider::Stop() {
156  controller_->Stop(false);
157}
158
159void OmniboxProvider::PopulateFromACResult(const AutocompleteResult& result) {
160  ClearResults();
161  for (ACMatches::const_iterator it = result.begin();
162       it != result.end();
163       ++it) {
164    if (!it->destination_url.is_valid())
165      continue;
166
167    Add(scoped_ptr<ChromeSearchResult>(
168        new OmniboxResult(profile_, *it)).Pass());
169  }
170}
171
172void OmniboxProvider::OnResultChanged(bool default_match_changed) {
173  const AutocompleteResult& result = controller_->result();
174  PopulateFromACResult(result);
175}
176
177}  // namespace app_list
178