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