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