omnibox_ui_handler.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright (c) 2012 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/webui/omnibox/omnibox_ui_handler.h"
6
7#include <string>
8
9#include "base/auto_reset.h"
10#include "base/bind.h"
11#include "base/strings/string16.h"
12#include "base/strings/stringprintf.h"
13#include "base/strings/utf_string_conversions.h"
14#include "base/time/time.h"
15#include "base/values.h"
16#include "chrome/browser/autocomplete/autocomplete_classifier.h"
17#include "chrome/browser/autocomplete/autocomplete_controller.h"
18#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
19#include "chrome/browser/bookmarks/bookmark_model_factory.h"
20#include "chrome/browser/history/history_service.h"
21#include "chrome/browser/history/history_service_factory.h"
22#include "chrome/browser/search/search.h"
23#include "chrome/browser/search_engines/template_url_service_factory.h"
24#include "components/bookmarks/browser/bookmark_model.h"
25#include "components/history/core/browser/url_database.h"
26#include "components/metrics/proto/omnibox_event.pb.h"
27#include "components/omnibox/autocomplete_match.h"
28#include "components/omnibox/autocomplete_provider.h"
29#include "components/search_engines/template_url.h"
30#include "content/public/browser/web_ui.h"
31#include "mojo/common/common_type_converters.h"
32
33namespace mojo {
34
35template <>
36class TypeConverter<mojo::Array<AutocompleteAdditionalInfoPtr>,
37                    AutocompleteMatch::AdditionalInfo> {
38 public:
39  static mojo::Array<AutocompleteAdditionalInfoPtr> ConvertFrom(
40      const AutocompleteMatch::AdditionalInfo& input) {
41    mojo::Array<AutocompleteAdditionalInfoPtr> array(input.size());
42    size_t index = 0;
43    for (AutocompleteMatch::AdditionalInfo::const_iterator i = input.begin();
44         i != input.end(); ++i, index++) {
45      AutocompleteAdditionalInfoPtr item(AutocompleteAdditionalInfo::New());
46      item->key = i->first;
47      item->value = i->second;
48      array[index] = item.Pass();
49    }
50    return array.Pass();
51  }
52};
53
54template <>
55class TypeConverter<AutocompleteMatchMojoPtr, AutocompleteMatch> {
56 public:
57  static AutocompleteMatchMojoPtr ConvertFrom(const AutocompleteMatch& input) {
58    AutocompleteMatchMojoPtr result(AutocompleteMatchMojo::New());
59    if (input.provider != NULL) {
60      result->provider_name = input.provider->GetName();
61      result->provider_done = input.provider->done();
62    }
63    result->relevance = input.relevance;
64    result->deletable = input.deletable;
65    result->fill_into_edit = mojo::String::From(input.fill_into_edit);
66    result->inline_autocompletion =
67        mojo::String::From(input.inline_autocompletion);
68    result->destination_url = input.destination_url.spec();
69    result->contents = mojo::String::From(input.contents);
70    // At this time, we're not bothering to send along the long vector that
71    // represent contents classification.  i.e., for each character, what
72    // type of text it is.
73    result->description = mojo::String::From(input.description);
74    // At this time, we're not bothering to send along the long vector that
75    // represents description classification.  i.e., for each character, what
76    // type of text it is.
77    result->transition = input.transition;
78    result->is_history_what_you_typed_match =
79        input.is_history_what_you_typed_match;
80    result->allowed_to_be_default_match = input.allowed_to_be_default_match;
81    result->type = AutocompleteMatchType::ToString(input.type);
82    if (input.associated_keyword.get() != NULL) {
83      result->associated_keyword =
84          mojo::String::From(input.associated_keyword->keyword);
85    }
86    result->keyword = mojo::String::From(input.keyword);
87    result->duplicates = static_cast<int32>(input.duplicate_matches.size());
88    result->from_previous = input.from_previous;
89
90    result->additional_info =
91        mojo::Array<AutocompleteAdditionalInfoPtr>::From(input.additional_info);
92    return result.Pass();
93  }
94};
95
96template <>
97class TypeConverter<AutocompleteResultsForProviderMojoPtr,
98                    scoped_refptr<AutocompleteProvider> > {
99 public:
100  static AutocompleteResultsForProviderMojoPtr ConvertFrom(
101      const scoped_refptr<AutocompleteProvider>& input) {
102    AutocompleteResultsForProviderMojoPtr result(
103        AutocompleteResultsForProviderMojo::New());
104    result->provider_name = input->GetName();
105    result->results =
106        mojo::Array<AutocompleteMatchMojoPtr>::From(input->matches());
107    return result.Pass();
108  }
109};
110
111}  // namespace mojo
112
113OmniboxUIHandler::OmniboxUIHandler(Profile* profile)
114    : profile_(profile) {
115  ResetController();
116}
117
118OmniboxUIHandler::~OmniboxUIHandler() {}
119
120void OmniboxUIHandler::OnResultChanged(bool default_match_changed) {
121  OmniboxResultMojoPtr result(OmniboxResultMojo::New());
122  result->done = controller_->done();
123  result->time_since_omnibox_started_ms =
124      (base::Time::Now() - time_omnibox_started_).InMilliseconds();
125  const base::string16 host = input_.text().substr(
126      input_.parts().host.begin,
127      input_.parts().host.len);
128  result->host = mojo::String::From(host);
129  bool is_typed_host;
130  if (!LookupIsTypedHost(host, &is_typed_host))
131    is_typed_host = false;
132  result->is_typed_host = is_typed_host;
133
134  {
135    // Copy to an ACMatches to make conversion easier. Since this isn't
136    // performance critical we don't worry about the cost here.
137    ACMatches matches(controller_->result().begin(),
138                      controller_->result().end());
139    result->combined_results =
140        mojo::Array<AutocompleteMatchMojoPtr>::From(matches);
141  }
142  result->results_by_provider =
143      mojo::Array<AutocompleteResultsForProviderMojoPtr>::From(
144          controller_->providers());
145
146  // Fill AutocompleteMatchMojo::starred.
147  BookmarkModel* bookmark_model = BookmarkModelFactory::GetForProfile(profile_);
148  if (bookmark_model) {
149    for (size_t i =  0; i < result->combined_results.size(); ++i) {
150      result->combined_results[i]->starred = bookmark_model->IsBookmarked(
151          GURL(result->combined_results[i]->destination_url));
152    }
153    for (size_t i = 0; i < result->results_by_provider.size(); ++i) {
154      const AutocompleteResultsForProviderMojo& result_by_provider =
155          *result->results_by_provider[i];
156      for (size_t j = 0; j < result_by_provider.results.size(); ++j) {
157        result_by_provider.results[j]->starred = bookmark_model->IsBookmarked(
158            GURL(result_by_provider.results[j]->destination_url));
159      }
160    }
161  }
162
163  client()->HandleNewAutocompleteResult(result.Pass());
164}
165
166bool OmniboxUIHandler::LookupIsTypedHost(const base::string16& host,
167                                         bool* is_typed_host) const {
168  HistoryService* const history_service =
169      HistoryServiceFactory::GetForProfile(profile_,
170                                           Profile::EXPLICIT_ACCESS);
171  if (!history_service)
172    return false;
173  history::URLDatabase* url_db = history_service->InMemoryDatabase();
174  if (!url_db)
175    return false;
176  *is_typed_host = url_db->IsTypedHost(base::UTF16ToUTF8(host));
177  return true;
178}
179
180void OmniboxUIHandler::StartOmniboxQuery(const mojo::String& input_string,
181                                         int32_t cursor_position,
182                                         bool prevent_inline_autocomplete,
183                                         bool prefer_keyword,
184                                         int32_t page_classification) {
185  // Reset the controller.  If we don't do this, then the
186  // AutocompleteController might inappropriately set its |minimal_changes|
187  // variable (or something else) and some providers will short-circuit
188  // important logic and return stale results.  In short, we want the
189  // actual results to not depend on the state of the previous request.
190  ResetController();
191  time_omnibox_started_ = base::Time::Now();
192  input_ = AutocompleteInput(
193      input_string.To<base::string16>(), cursor_position, base::string16(),
194      GURL(),
195      static_cast<metrics::OmniboxEventProto::PageClassification>(
196          page_classification),
197      prevent_inline_autocomplete, prefer_keyword, true, true,
198      ChromeAutocompleteSchemeClassifier(profile_));
199  controller_->Start(input_);
200}
201
202void OmniboxUIHandler::ResetController() {
203  controller_.reset(new AutocompleteController(profile_,
204          TemplateURLServiceFactory::GetForProfile(profile_),
205          this,
206          AutocompleteClassifier::kDefaultOmniboxProviders));
207}
208