Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / omnibox / omnibox_ui_handler.cc
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/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"
27
28 OmniboxUIHandler::OmniboxUIHandler(Profile* profile): profile_(profile) {
29   ResetController();
30 }
31
32 OmniboxUIHandler::~OmniboxUIHandler() {}
33
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) {
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);
87   bool is_typed_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);
93   }
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);
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->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,
155                         j->second);
156     }
157   }
158   output->SetInteger(prefix + ".num_items", i);
159 }
160
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)
167     return false;
168   history::URLDatabase* url_db = history_service->InMemoryDatabase();
169   if (!url_db)
170     return false;
171   *is_typed_host = url_db->IsTypedHost(base::UTF16ToUTF8(host));
172   return true;
173 }
174
175 void OmniboxUIHandler::StartOmniboxQuery(const base::ListValue* input) {
176   DCHECK_EQ(5u, input->GetSize());
177   base::string16 input_string;
178   bool return_val = input->GetString(0, &input_string);
179   DCHECK(return_val);
180   int cursor_position;
181   return_val = input->GetInteger(1, &cursor_position);
182   DCHECK(return_val);
183   bool prevent_inline_autocomplete;
184   return_val = input->GetBoolean(2, &prevent_inline_autocomplete);
185   DCHECK(return_val);
186   bool prefer_keyword;
187   return_val = input->GetBoolean(3, &prefer_keyword);
188   DCHECK(return_val);
189   int current_page_classification;
190   return_val = input->GetInteger(4, &current_page_classification);
191   DCHECK(return_val);
192   // Reset the controller.  If we don't do this, then the
193   // AutocompleteController might inappropriately set its |minimal_changes|
194   // variable (or something else) and some providers will short-circuit
195   // important logic and return stale results.  In short, we want the
196   // actual results to not depend on the state of the previous request.
197   ResetController();
198   time_omnibox_started_ = base::Time::Now();
199   controller_->Start(AutocompleteInput(
200       input_string,
201       cursor_position,
202       base::string16(),  // user's desired tld (top-level domain)
203       GURL(),
204       static_cast<AutocompleteInput::PageClassification>(
205           current_page_classification),
206       prevent_inline_autocomplete,
207       prefer_keyword,
208       true,  // allow exact keyword matches
209       AutocompleteInput::ALL_MATCHES));  // want all matches
210 }
211
212 void OmniboxUIHandler::ResetController() {
213   controller_.reset(new AutocompleteController(profile_, this,
214           AutocompleteClassifier::kDefaultOmniboxProviders));
215 }