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/chrome_autocomplete_scheme_classifier.h" 19#include "chrome/browser/bookmarks/bookmark_model_factory.h" 20#include "chrome/browser/history/history_service.h" 21#include "chrome/browser/history/history_service_factory.h" 22#include "chrome/browser/search/search.h" 23#include "chrome/browser/search_engines/template_url_service_factory.h" 24#include "components/bookmarks/browser/bookmark_model.h" 25#include "components/history/core/browser/url_database.h" 26#include "components/metrics/proto/omnibox_event.pb.h" 27#include "components/omnibox/autocomplete_match.h" 28#include "components/omnibox/autocomplete_provider.h" 29#include "components/search_engines/template_url.h" 30#include "content/public/browser/web_ui.h" 31#include "mojo/common/common_type_converters.h" 32 33namespace mojo { 34 35template <> 36struct TypeConverter<mojo::Array<AutocompleteAdditionalInfoPtr>, 37 AutocompleteMatch::AdditionalInfo> { 38 static mojo::Array<AutocompleteAdditionalInfoPtr> Convert( 39 const AutocompleteMatch::AdditionalInfo& input) { 40 mojo::Array<AutocompleteAdditionalInfoPtr> array(input.size()); 41 size_t index = 0; 42 for (AutocompleteMatch::AdditionalInfo::const_iterator i = input.begin(); 43 i != input.end(); ++i, index++) { 44 AutocompleteAdditionalInfoPtr item(AutocompleteAdditionalInfo::New()); 45 item->key = i->first; 46 item->value = i->second; 47 array[index] = item.Pass(); 48 } 49 return array.Pass(); 50 } 51}; 52 53template <> 54struct TypeConverter<AutocompleteMatchMojoPtr, AutocompleteMatch> { 55 static AutocompleteMatchMojoPtr Convert(const AutocompleteMatch& input) { 56 AutocompleteMatchMojoPtr result(AutocompleteMatchMojo::New()); 57 if (input.provider != NULL) { 58 result->provider_name = input.provider->GetName(); 59 result->provider_done = input.provider->done(); 60 } 61 result->relevance = input.relevance; 62 result->deletable = input.deletable; 63 result->fill_into_edit = mojo::String::From(input.fill_into_edit); 64 result->inline_autocompletion = 65 mojo::String::From(input.inline_autocompletion); 66 result->destination_url = input.destination_url.spec(); 67 result->contents = mojo::String::From(input.contents); 68 // At this time, we're not bothering to send along the long vector that 69 // represent contents classification. i.e., for each character, what 70 // type of text it is. 71 result->description = mojo::String::From(input.description); 72 // At this time, we're not bothering to send along the long vector that 73 // represents description classification. i.e., for each character, what 74 // type of text it is. 75 result->transition = input.transition; 76 result->is_history_what_you_typed_match = 77 input.is_history_what_you_typed_match; 78 result->allowed_to_be_default_match = input.allowed_to_be_default_match; 79 result->type = AutocompleteMatchType::ToString(input.type); 80 if (input.associated_keyword.get() != NULL) { 81 result->associated_keyword = 82 mojo::String::From(input.associated_keyword->keyword); 83 } 84 result->keyword = mojo::String::From(input.keyword); 85 result->duplicates = static_cast<int32>(input.duplicate_matches.size()); 86 result->from_previous = input.from_previous; 87 88 result->additional_info = 89 mojo::Array<AutocompleteAdditionalInfoPtr>::From(input.additional_info); 90 return result.Pass(); 91 } 92}; 93 94template <> 95struct TypeConverter<AutocompleteResultsForProviderMojoPtr, 96 scoped_refptr<AutocompleteProvider> > { 97 static AutocompleteResultsForProviderMojoPtr Convert( 98 const scoped_refptr<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 110OmniboxUIHandler::OmniboxUIHandler(Profile* profile) 111 : profile_(profile) { 112 ResetController(); 113} 114 115OmniboxUIHandler::~OmniboxUIHandler() {} 116 117void 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 143 // Fill AutocompleteMatchMojo::starred. 144 BookmarkModel* bookmark_model = BookmarkModelFactory::GetForProfile(profile_); 145 if (bookmark_model) { 146 for (size_t i = 0; i < result->combined_results.size(); ++i) { 147 result->combined_results[i]->starred = bookmark_model->IsBookmarked( 148 GURL(result->combined_results[i]->destination_url)); 149 } 150 for (size_t i = 0; i < result->results_by_provider.size(); ++i) { 151 const AutocompleteResultsForProviderMojo& result_by_provider = 152 *result->results_by_provider[i]; 153 for (size_t j = 0; j < result_by_provider.results.size(); ++j) { 154 result_by_provider.results[j]->starred = bookmark_model->IsBookmarked( 155 GURL(result_by_provider.results[j]->destination_url)); 156 } 157 } 158 } 159 160 client()->HandleNewAutocompleteResult(result.Pass()); 161} 162 163bool OmniboxUIHandler::LookupIsTypedHost(const base::string16& host, 164 bool* is_typed_host) const { 165 HistoryService* const history_service = 166 HistoryServiceFactory::GetForProfile(profile_, 167 Profile::EXPLICIT_ACCESS); 168 if (!history_service) 169 return false; 170 history::URLDatabase* url_db = history_service->InMemoryDatabase(); 171 if (!url_db) 172 return false; 173 *is_typed_host = url_db->IsTypedHost(base::UTF16ToUTF8(host)); 174 return true; 175} 176 177void OmniboxUIHandler::StartOmniboxQuery(const mojo::String& input_string, 178 int32_t cursor_position, 179 bool prevent_inline_autocomplete, 180 bool prefer_keyword, 181 int32_t page_classification) { 182 // Reset the controller. If we don't do this, then the 183 // AutocompleteController might inappropriately set its |minimal_changes| 184 // variable (or something else) and some providers will short-circuit 185 // important logic and return stale results. In short, we want the 186 // actual results to not depend on the state of the previous request. 187 ResetController(); 188 time_omnibox_started_ = base::Time::Now(); 189 input_ = AutocompleteInput( 190 input_string.To<base::string16>(), cursor_position, base::string16(), 191 GURL(), 192 static_cast<metrics::OmniboxEventProto::PageClassification>( 193 page_classification), 194 prevent_inline_autocomplete, prefer_keyword, true, true, 195 ChromeAutocompleteSchemeClassifier(profile_)); 196 controller_->Start(input_); 197} 198 199void OmniboxUIHandler::ResetController() { 200 controller_.reset(new AutocompleteController(profile_, 201 TemplateURLServiceFactory::GetForProfile(profile_), 202 this, 203 AutocompleteClassifier::kDefaultOmniboxProviders)); 204} 205