OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/webui/omnibox/omnibox_ui_handler.h" | 5 #include "chrome/browser/ui/webui/omnibox/omnibox_ui_handler.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/auto_reset.h" | 9 #include "base/auto_reset.h" |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/strings/string16.h" | 11 #include "base/strings/string16.h" |
12 #include "base/strings/stringprintf.h" | 12 #include "base/strings/stringprintf.h" |
13 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
14 #include "base/time/time.h" | 14 #include "base/time/time.h" |
15 #include "base/values.h" | 15 #include "base/values.h" |
16 #include "chrome/browser/autocomplete/autocomplete_classifier.h" | 16 #include "chrome/browser/autocomplete/autocomplete_classifier.h" |
17 #include "chrome/browser/autocomplete/autocomplete_controller.h" | 17 #include "chrome/browser/autocomplete/autocomplete_controller.h" |
18 #include "chrome/browser/autocomplete/autocomplete_input.h" | 18 #include "chrome/browser/autocomplete/autocomplete_input.h" |
19 #include "chrome/browser/autocomplete/autocomplete_match.h" | 19 #include "chrome/browser/autocomplete/autocomplete_match.h" |
20 #include "chrome/browser/autocomplete/autocomplete_provider.h" | 20 #include "chrome/browser/autocomplete/autocomplete_provider.h" |
21 #include "chrome/browser/history/history_service.h" | 21 #include "chrome/browser/history/history_service.h" |
22 #include "chrome/browser/history/history_service_factory.h" | 22 #include "chrome/browser/history/history_service_factory.h" |
23 #include "chrome/browser/history/url_database.h" | 23 #include "chrome/browser/history/url_database.h" |
24 #include "chrome/browser/search/search.h" | 24 #include "chrome/browser/search/search.h" |
25 #include "chrome/browser/search_engines/template_url.h" | 25 #include "chrome/browser/search_engines/template_url.h" |
26 #include "content/public/browser/web_ui.h" | 26 #include "content/public/browser/web_ui.h" |
27 #include "mojo/common/common_type_converters.h" | |
28 #include "mojo/public/cpp/bindings/allocation_scope.h" | |
27 | 29 |
28 OmniboxUIHandler::OmniboxUIHandler(Profile* profile): profile_(profile) { | 30 // TODO(sky): remove these namespaces. |
31 using chrome::AutocompleteAdditionalInfo; | |
32 using chrome::AutocompleteMatchMojo; | |
33 using chrome::AutocompleteResultsForProviderMojo; | |
34 using chrome::OmniboxResultMojo; | |
35 | |
36 namespace mojo { | |
37 | |
38 template <> | |
39 class TypeConverter<mojo::Array<AutocompleteAdditionalInfo>, | |
40 AutocompleteMatch::AdditionalInfo> { | |
41 public: | |
42 static mojo::Array<AutocompleteAdditionalInfo> ConvertFrom( | |
43 const AutocompleteMatch::AdditionalInfo& input, | |
44 Buffer* buf) { | |
45 mojo::Array<AutocompleteAdditionalInfo>::Builder array_builder( | |
46 input.size()); | |
darin (slow to review)
2014/04/07 14:35:26
nit: consider passing |buf| to each of the Builder
sky
2014/04/07 14:53:23
Done.
| |
47 size_t index = 0; | |
48 for (AutocompleteMatch::AdditionalInfo::const_iterator i = input.begin(); | |
49 i != input.end(); ++i, index++) { | |
50 AutocompleteAdditionalInfo::Builder item_builder; | |
51 item_builder.set_key(i->first); | |
52 item_builder.set_value(i->second); | |
53 array_builder[index] = item_builder.Finish(); | |
54 } | |
55 return array_builder.Finish(); | |
56 } | |
57 }; | |
58 | |
59 template <> | |
60 class TypeConverter<AutocompleteMatchMojo, AutocompleteMatch> { | |
61 public: | |
62 static AutocompleteMatchMojo ConvertFrom(const AutocompleteMatch& input, | |
63 Buffer* buf) { | |
64 AutocompleteMatchMojo::Builder builder; | |
65 if (input.provider != NULL) { | |
66 builder.set_provider_name(input.provider->GetName()); | |
67 builder.set_provider_done(input.provider->done()); | |
68 } | |
69 builder.set_relevance(input.relevance); | |
70 builder.set_deletable(input.deletable); | |
71 builder.set_fill_into_edit(input.fill_into_edit); | |
72 builder.set_inline_autocompletion(input.inline_autocompletion); | |
73 builder.set_destination_url(input.destination_url.spec()); | |
74 builder.set_contents(input.contents); | |
75 // At this time, we're not bothering to send along the long vector that | |
76 // represent contents classification. i.e., for each character, what | |
77 // type of text it is. | |
78 builder.set_description(input.description); | |
79 // At this time, we're not bothering to send along the long vector that | |
80 // represents description classification. i.e., for each character, what | |
81 // type of text it is. | |
82 builder.set_transition(input.transition); | |
83 builder.set_is_history_what_you_typed_match( | |
84 input.is_history_what_you_typed_match); | |
85 builder.set_allowed_to_be_default_match(input.allowed_to_be_default_match); | |
86 builder.set_type(AutocompleteMatchType::ToString(input.type)); | |
87 if (input.associated_keyword.get() != NULL) | |
88 builder.set_associated_keyword(input.associated_keyword->keyword); | |
89 builder.set_keyword(input.keyword); | |
90 builder.set_starred(input.starred); | |
91 builder.set_duplicates(static_cast<int>(input.duplicate_matches.size())); | |
darin (slow to review)
2014/04/07 14:35:26
nit: you might want to be explicit here: int -> in
sky
2014/04/07 14:53:23
Done.
| |
92 builder.set_from_previous(input.from_previous); | |
93 | |
94 builder.set_additional_info(input.additional_info); | |
95 return builder.Finish(); | |
96 } | |
97 }; | |
98 | |
99 template <> | |
100 class TypeConverter<AutocompleteResultsForProviderMojo, AutocompleteProvider*> { | |
101 public: | |
102 static AutocompleteResultsForProviderMojo ConvertFrom( | |
103 const AutocompleteProvider* input, | |
104 Buffer* buf) { | |
105 AutocompleteResultsForProviderMojo::Builder builder; | |
106 builder.set_provider_name(input->GetName()); | |
107 builder.set_results(input->matches()); | |
108 return builder.Finish(); | |
109 } | |
110 }; | |
111 | |
112 } // namespace mojo | |
113 | |
114 OmniboxUIHandler::OmniboxUIHandler(chrome::ScopedOmniboxPageHandle handle, | |
115 Profile* profile) | |
116 : page_(handle.Pass(), this), | |
117 profile_(profile) { | |
29 ResetController(); | 118 ResetController(); |
30 } | 119 } |
31 | 120 |
32 OmniboxUIHandler::~OmniboxUIHandler() {} | 121 OmniboxUIHandler::~OmniboxUIHandler() {} |
33 | 122 |
34 void OmniboxUIHandler::RegisterMessages() { | |
35 web_ui()->RegisterMessageCallback("startOmniboxQuery", | |
36 base::Bind(&OmniboxUIHandler::StartOmniboxQuery, | |
37 base::Unretained(this))); | |
38 } | |
39 | |
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: | |
45 // { | |
46 // 'done': false, | |
47 // 'time_since_omnibox_started_ms': 15, | |
48 // 'host': 'mai', | |
49 // 'is_typed_host': false, | |
50 // 'combined_results' : { | |
51 // 'num_items': 4, | |
52 // 'item_0': { | |
53 // 'destination_url': 'http://mail.google.com', | |
54 // 'provider_name': 'HistoryURL', | |
55 // 'relevance': 1410, | |
56 // ... | |
57 // } | |
58 // 'item_1: { | |
59 // ... | |
60 // } | |
61 // ... | |
62 // } | |
63 // 'results_by_provider': { | |
64 // 'HistoryURL' : { | |
65 // 'num_items': 3, | |
66 // ... | |
67 // } | |
68 // 'Search' : { | |
69 // 'num_items': 1, | |
70 // ... | |
71 // } | |
72 // ... | |
73 // } | |
74 // } | |
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) { | 123 void OmniboxUIHandler::OnResultChanged(bool default_match_changed) { |
78 base::DictionaryValue result_to_output; | 124 mojo::AllocationScope scope; |
79 // Fill in general information. | 125 OmniboxResultMojo::Builder builder; |
80 result_to_output.SetBoolean("done", controller_->done()); | 126 builder.set_done(controller_->done()); |
81 result_to_output.SetInteger("time_since_omnibox_started_ms", | 127 builder.set_time_since_omnibox_started_ms( |
82 (base::Time::Now() - time_omnibox_started_).InMilliseconds()); | 128 (base::Time::Now() - time_omnibox_started_).InMilliseconds()); |
83 const base::string16& host = controller_->input().text().substr( | 129 const base::string16 host = controller_->input().text().substr( |
84 controller_->input().parts().host.begin, | 130 controller_->input().parts().host.begin, |
85 controller_->input().parts().host.len); | 131 controller_->input().parts().host.len); |
86 result_to_output.SetString("host", host); | 132 builder.set_host(host); |
87 bool is_typed_host; | 133 bool is_typed_host; |
88 if (LookupIsTypedHost(host, &is_typed_host)) { | 134 if (!LookupIsTypedHost(host, &is_typed_host)) |
89 // If we successfully looked up whether the host part of the omnibox | 135 is_typed_host = false; |
90 // input (this interprets the input as a host plus optional path) as | 136 builder.set_is_typed_host(is_typed_host); |
91 // a typed host, then record this information in the output. | 137 |
92 result_to_output.SetBoolean("is_typed_host", is_typed_host); | 138 { |
139 // Copy to an ACMatches to make conversion easier. Since this isn't | |
140 // performance critical we don't worry about the cost here. | |
141 ACMatches matches(controller_->result().begin(), | |
142 controller_->result().end()); | |
143 builder.set_combined_results(matches); | |
93 } | 144 } |
94 // Fill in the merged/combined results the controller has provided. | 145 builder.set_results_by_provider(*controller_->providers()); |
95 AddResultToDictionary("combined_results", controller_->result().begin(), | 146 page_->HandleNewAutocompleteResult(builder.Finish()); |
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); | |
103 } | |
104 // Add done; send the results. | |
105 web_ui()->CallJavascriptFunction("omniboxDebug.handleNewAutocompleteResult", | |
106 result_to_output); | |
107 } | |
108 | |
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) { | |
115 int i = 0; | |
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()); | |
122 } | |
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); | |
148 } | |
149 output->SetString(item_prefix + ".keyword", it->keyword); | |
150 output->SetBoolean(item_prefix + ".starred", it->starred); | |
151 output->SetInteger(item_prefix + ".duplicates", | |
152 static_cast<int>(it->duplicate_matches.size())); | |
153 output->SetBoolean(item_prefix + ".from_previous", it->from_previous); | |
154 for (AutocompleteMatch::AdditionalInfo::const_iterator j = | |
155 it->additional_info.begin(); j != it->additional_info.end(); ++j) { | |
156 output->SetString(item_prefix + ".additional_info." + j->first, | |
157 j->second); | |
158 } | |
159 } | |
160 output->SetInteger(prefix + ".num_items", i); | |
161 } | 147 } |
162 | 148 |
163 bool OmniboxUIHandler::LookupIsTypedHost(const base::string16& host, | 149 bool OmniboxUIHandler::LookupIsTypedHost(const base::string16& host, |
164 bool* is_typed_host) const { | 150 bool* is_typed_host) const { |
165 HistoryService* const history_service = | 151 HistoryService* const history_service = |
166 HistoryServiceFactory::GetForProfile(profile_, | 152 HistoryServiceFactory::GetForProfile(profile_, |
167 Profile::EXPLICIT_ACCESS); | 153 Profile::EXPLICIT_ACCESS); |
168 if (!history_service) | 154 if (!history_service) |
169 return false; | 155 return false; |
170 history::URLDatabase* url_db = history_service->InMemoryDatabase(); | 156 history::URLDatabase* url_db = history_service->InMemoryDatabase(); |
171 if (!url_db) | 157 if (!url_db) |
172 return false; | 158 return false; |
173 *is_typed_host = url_db->IsTypedHost(base::UTF16ToUTF8(host)); | 159 *is_typed_host = url_db->IsTypedHost(base::UTF16ToUTF8(host)); |
174 return true; | 160 return true; |
175 } | 161 } |
176 | 162 |
177 void OmniboxUIHandler::StartOmniboxQuery(const base::ListValue* input) { | 163 void OmniboxUIHandler::StartOmniboxQuery(const mojo::String& input_string, |
178 DCHECK_EQ(5u, input->GetSize()); | 164 int32_t cursor_position, |
179 base::string16 input_string; | 165 bool prevent_inline_autocomplete, |
180 bool return_val = input->GetString(0, &input_string); | 166 bool prefer_keyword, |
181 DCHECK(return_val); | 167 int32_t page_classification) { |
182 int cursor_position; | |
183 return_val = input->GetInteger(1, &cursor_position); | |
184 DCHECK(return_val); | |
185 bool prevent_inline_autocomplete; | |
186 return_val = input->GetBoolean(2, &prevent_inline_autocomplete); | |
187 DCHECK(return_val); | |
188 bool prefer_keyword; | |
189 return_val = input->GetBoolean(3, &prefer_keyword); | |
190 DCHECK(return_val); | |
191 int current_page_classification; | |
192 return_val = input->GetInteger(4, ¤t_page_classification); | |
193 DCHECK(return_val); | |
194 // Reset the controller. If we don't do this, then the | 168 // Reset the controller. If we don't do this, then the |
195 // AutocompleteController might inappropriately set its |minimal_changes| | 169 // AutocompleteController might inappropriately set its |minimal_changes| |
196 // variable (or something else) and some providers will short-circuit | 170 // variable (or something else) and some providers will short-circuit |
197 // important logic and return stale results. In short, we want the | 171 // important logic and return stale results. In short, we want the |
198 // actual results to not depend on the state of the previous request. | 172 // actual results to not depend on the state of the previous request. |
199 ResetController(); | 173 ResetController(); |
200 time_omnibox_started_ = base::Time::Now(); | 174 time_omnibox_started_ = base::Time::Now(); |
201 controller_->Start(AutocompleteInput( | 175 controller_->Start(AutocompleteInput( |
202 input_string, | 176 input_string, |
203 cursor_position, | 177 cursor_position, |
204 base::string16(), // user's desired tld (top-level domain) | 178 base::string16(), // user's desired tld (top-level domain) |
205 GURL(), | 179 GURL(), |
206 static_cast<AutocompleteInput::PageClassification>( | 180 static_cast<AutocompleteInput::PageClassification>( |
207 current_page_classification), | 181 page_classification), |
208 prevent_inline_autocomplete, | 182 prevent_inline_autocomplete, |
209 prefer_keyword, | 183 prefer_keyword, |
210 true, // allow exact keyword matches | 184 true, // allow exact keyword matches |
211 AutocompleteInput::ALL_MATCHES)); // want all matches | 185 AutocompleteInput::ALL_MATCHES)); // want all matches |
212 } | 186 } |
213 | 187 |
214 void OmniboxUIHandler::ResetController() { | 188 void OmniboxUIHandler::ResetController() { |
215 controller_.reset(new AutocompleteController(profile_, this, | 189 controller_.reset(new AutocompleteController(profile_, this, |
216 AutocompleteClassifier::kDefaultOmniboxProviders)); | 190 AutocompleteClassifier::kDefaultOmniboxProviders)); |
217 } | 191 } |
OLD | NEW |