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.
5 #include "chrome/browser/ui/webui/omnibox/omnibox_ui_handler.h"
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/autocomplete_input.h"
19 #include "chrome/browser/autocomplete/autocomplete_match.h"
20 #include "chrome/browser/autocomplete/autocomplete_provider.h"
21 #include "chrome/browser/history/history_service.h"
22 #include "chrome/browser/history/history_service_factory.h"
23 #include "chrome/browser/history/url_database.h"
24 #include "chrome/browser/search/search.h"
25 #include "chrome/browser/search_engines/template_url.h"
26 #include "content/public/browser/web_ui.h"
28 OmniboxUIHandler::OmniboxUIHandler(Profile
* profile
): profile_(profile
) {
32 OmniboxUIHandler::~OmniboxUIHandler() {}
34 void OmniboxUIHandler::RegisterMessages() {
35 web_ui()->RegisterMessageCallback("startOmniboxQuery",
36 base::Bind(&OmniboxUIHandler::StartOmniboxQuery
,
37 base::Unretained(this)));
40 // This function gets called when the AutocompleteController possibly
41 // has new results. We package those results in a DictionaryValue
42 // object result_to_output and call the javascript function
43 // handleNewAutocompleteResult. Here's an example populated
44 // result_to_output object:
47 // 'time_since_omnibox_started_ms': 15,
49 // 'is_typed_host': false,
50 // 'combined_results' : {
53 // 'destination_url': 'http://mail.google.com',
54 // 'provider_name': 'HistoryURL',
63 // 'results_by_provider': {
75 // For reference, the javascript code that unpacks this object and
76 // displays it is in chrome/browser/resources/omnibox.js
77 void OmniboxUIHandler::OnResultChanged(bool default_match_changed
) {
78 base::DictionaryValue result_to_output
;
79 // Fill in general information.
80 result_to_output
.SetBoolean("done", controller_
->done());
81 result_to_output
.SetInteger("time_since_omnibox_started_ms",
82 (base::Time::Now() - time_omnibox_started_
).InMilliseconds());
83 const base::string16
& host
= controller_
->input().text().substr(
84 controller_
->input().parts().host
.begin
,
85 controller_
->input().parts().host
.len
);
86 result_to_output
.SetString("host", host
);
88 if (LookupIsTypedHost(host
, &is_typed_host
)) {
89 // If we successfully looked up whether the host part of the omnibox
90 // input (this interprets the input as a host plus optional path) as
91 // a typed host, then record this information in the output.
92 result_to_output
.SetBoolean("is_typed_host", is_typed_host
);
94 // Fill in the merged/combined results the controller has provided.
95 AddResultToDictionary("combined_results", controller_
->result().begin(),
96 controller_
->result().end(), &result_to_output
);
97 // Fill results from each individual provider as well.
98 for (ACProviders::const_iterator
it(controller_
->providers()->begin());
99 it
!= controller_
->providers()->end(); ++it
) {
100 AddResultToDictionary(
101 std::string("results_by_provider.") + (*it
)->GetName(),
102 (*it
)->matches().begin(), (*it
)->matches().end(), &result_to_output
);
104 // Add done; send the results.
105 web_ui()->CallJavascriptFunction("omniboxDebug.handleNewAutocompleteResult",
109 // For details on the format of the DictionaryValue that this function
110 // populates, see the comments by OnResultChanged().
111 void OmniboxUIHandler::AddResultToDictionary(const std::string
& prefix
,
112 ACMatches::const_iterator it
,
113 ACMatches::const_iterator end
,
114 base::DictionaryValue
* output
) {
116 for (; it
!= end
; ++it
, ++i
) {
117 std::string
item_prefix(prefix
+ base::StringPrintf(".item_%d", i
));
118 if (it
->provider
!= NULL
) {
119 output
->SetString(item_prefix
+ ".provider_name",
120 it
->provider
->GetName());
121 output
->SetBoolean(item_prefix
+ ".provider_done", it
->provider
->done());
123 output
->SetInteger(item_prefix
+ ".relevance", it
->relevance
);
124 output
->SetBoolean(item_prefix
+ ".deletable", it
->deletable
);
125 output
->SetString(item_prefix
+ ".fill_into_edit", it
->fill_into_edit
);
126 output
->SetString(item_prefix
+ ".inline_autocompletion",
127 it
->inline_autocompletion
);
128 output
->SetString(item_prefix
+ ".destination_url",
129 it
->destination_url
.spec());
130 output
->SetString(item_prefix
+ ".contents", it
->contents
);
131 // At this time, we're not bothering to send along the long vector that
132 // represent contents classification. i.e., for each character, what
133 // type of text it is.
134 output
->SetString(item_prefix
+ ".description", it
->description
);
135 // At this time, we're not bothering to send along the long vector that
136 // represents description classification. i.e., for each character, what
137 // type of text it is.
138 output
->SetInteger(item_prefix
+ ".transition", it
->transition
);
139 output
->SetBoolean(item_prefix
+ ".is_history_what_you_typed_match",
140 it
->is_history_what_you_typed_match
);
141 output
->SetBoolean(item_prefix
+ ".allowed_to_be_default_match",
142 it
->allowed_to_be_default_match
);
143 output
->SetString(item_prefix
+ ".type",
144 AutocompleteMatchType::ToString(it
->type
));
145 if (it
->associated_keyword
.get() != NULL
) {
146 output
->SetString(item_prefix
+ ".associated_keyword",
147 it
->associated_keyword
->keyword
);
149 output
->SetString(item_prefix
+ ".keyword", it
->keyword
);
150 output
->SetBoolean(item_prefix
+ ".starred", it
->starred
);
151 output
->SetBoolean(item_prefix
+ ".from_previous", it
->from_previous
);
152 for (AutocompleteMatch::AdditionalInfo::const_iterator j
=
153 it
->additional_info
.begin(); j
!= it
->additional_info
.end(); ++j
) {
154 output
->SetString(item_prefix
+ ".additional_info." + j
->first
,
158 output
->SetInteger(prefix
+ ".num_items", i
);
161 bool OmniboxUIHandler::LookupIsTypedHost(const base::string16
& host
,
162 bool* is_typed_host
) const {
163 HistoryService
* const history_service
=
164 HistoryServiceFactory::GetForProfile(profile_
,
165 Profile::EXPLICIT_ACCESS
);
166 if (!history_service
)
168 history::URLDatabase
* url_db
= history_service
->InMemoryDatabase();
171 *is_typed_host
= url_db
->IsTypedHost(base::UTF16ToUTF8(host
));
175 void OmniboxUIHandler::StartOmniboxQuery(const base::ListValue
* input
) {
176 DCHECK_EQ(4u, input
->GetSize());
177 base::string16 input_string
;
178 bool return_val
= input
->GetString(0, &input_string
);
181 return_val
= input
->GetInteger(1, &cursor_position
);
183 bool prevent_inline_autocomplete
;
184 return_val
= input
->GetBoolean(2, &prevent_inline_autocomplete
);
187 return_val
= input
->GetBoolean(3, &prefer_keyword
);
189 // Reset the controller. If we don't do this, then the
190 // AutocompleteController might inappropriately set its |minimal_changes|
191 // variable (or something else) and some providers will short-circuit
192 // important logic and return stale results. In short, we want the
193 // actual results to not depend on the state of the previous request.
195 time_omnibox_started_
= base::Time::Now();
196 controller_
->Start(AutocompleteInput(
199 base::string16(), // user's desired tld (top-level domain)
201 AutocompleteInput::INVALID_SPEC
,
202 prevent_inline_autocomplete
,
204 true, // allow exact keyword matches
205 AutocompleteInput::ALL_MATCHES
)); // want all matches
208 void OmniboxUIHandler::ResetController() {
209 controller_
.reset(new AutocompleteController(profile_
, this,
210 AutocompleteClassifier::kDefaultOmniboxProviders
));