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