omnibox_provider.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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  virtual ChromeSearchResultType GetType() OVERRIDE {
96    return OMNIBOX_SEARCH_RESULT;
97  }
98
99 private:
100  void UpdateIcon() {
101    int resource_id = match_.starred ?
102        IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match_.type);
103    SetIcon(*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
104        resource_id));
105  }
106
107  void UpdateTitleAndDetails() {
108    set_title(match_.contents);
109    SearchResult::Tags title_tags;
110    ACMatchClassificationsToTags(match_.contents,
111                                 match_.contents_class,
112                                 &title_tags);
113    set_title_tags(title_tags);
114
115    set_details(match_.description);
116    SearchResult::Tags details_tags;
117    ACMatchClassificationsToTags(match_.description,
118                                 match_.description_class,
119                                 &details_tags);
120    set_details_tags(details_tags);
121  }
122
123  Profile* profile_;
124  AutocompleteMatch match_;
125
126  DISALLOW_COPY_AND_ASSIGN(OmniboxResult);
127};
128
129}  // namespace
130
131OmniboxProvider::OmniboxProvider(Profile* profile)
132    : profile_(profile),
133      controller_(new AutocompleteController(
134          profile,
135          this,
136          AutocompleteClassifier::kDefaultOmniboxProviders)) {
137}
138
139OmniboxProvider::~OmniboxProvider() {}
140
141void OmniboxProvider::Start(const base::string16& query) {
142  controller_->Start(AutocompleteInput(query,
143                                       base::string16::npos,
144                                       base::string16(),
145                                       GURL(),
146                                       false,
147                                       false,
148                                       true,
149                                       AutocompleteInput::ALL_MATCHES));
150}
151
152void OmniboxProvider::Stop() {
153  controller_->Stop(false);
154}
155
156void OmniboxProvider::PopulateFromACResult(const AutocompleteResult& result) {
157  ClearResults();
158  for (ACMatches::const_iterator it = result.begin();
159       it != result.end();
160       ++it) {
161    if (!it->destination_url.is_valid())
162      continue;
163
164    Add(scoped_ptr<ChromeSearchResult>(
165        new OmniboxResult(profile_, *it)).Pass());
166  }
167}
168
169void OmniboxProvider::OnResultChanged(bool default_match_changed) {
170  const AutocompleteResult& result = controller_->result();
171  PopulateFromACResult(result);
172}
173
174}  // namespace app_list
175