| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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/autocomplete/search_provider.h" | 5 #include "chrome/browser/autocomplete/search_provider.h" |
| 6 | 6 |
| 7 #include "base/message_loop.h" | 7 #include "base/message_loop.h" |
| 8 #include "base/string_util.h" | 8 #include "base/string_util.h" |
| 9 #include "chrome/browser/autocomplete/keyword_provider.h" |
| 9 #include "chrome/browser/browser_process.h" | 10 #include "chrome/browser/browser_process.h" |
| 10 #include "chrome/browser/google_util.h" | 11 #include "chrome/browser/google_util.h" |
| 11 #include "chrome/browser/net/url_fixer_upper.h" | 12 #include "chrome/browser/net/url_fixer_upper.h" |
| 12 #include "chrome/browser/profile.h" | 13 #include "chrome/browser/profile.h" |
| 13 #include "chrome/browser/search_engines/template_url_model.h" | 14 #include "chrome/browser/search_engines/template_url_model.h" |
| 14 #include "chrome/common/json_value_serializer.h" | 15 #include "chrome/common/json_value_serializer.h" |
| 15 #include "chrome/common/l10n_util.h" | 16 #include "chrome/common/l10n_util.h" |
| 16 #include "chrome/common/pref_names.h" | 17 #include "chrome/common/pref_names.h" |
| 17 #include "chrome/common/pref_service.h" | 18 #include "chrome/common/pref_service.h" |
| 18 #include "chrome/common/url_constants.h" | 19 #include "chrome/common/url_constants.h" |
| 19 #include "googleurl/src/url_util.h" | 20 #include "googleurl/src/url_util.h" |
| 20 #include "grit/generated_resources.h" | 21 #include "grit/generated_resources.h" |
| 21 #include "net/base/escape.h" | 22 #include "net/base/escape.h" |
| 22 #include "net/http/http_response_headers.h" | 23 #include "net/http/http_response_headers.h" |
| 23 #include "net/url_request/url_request_status.h" | 24 #include "net/url_request/url_request_status.h" |
| 24 | 25 |
| 25 using base::Time; | 26 using base::Time; |
| 26 using base::TimeDelta; | 27 using base::TimeDelta; |
| 27 | 28 |
| 29 void SearchProvider::Providers::Set(const TemplateURL* default_provider, |
| 30 const TemplateURL* keyword_provider) { |
| 31 // TODO(pkasting): http://b/1162970 We shouldn't need to structure-copy |
| 32 // this. Nor should we need |default_provider_| and |keyword_provider_| |
| 33 // just to know whether the provider changed. |
| 34 default_provider_ = default_provider; |
| 35 if (default_provider) |
| 36 cached_default_provider_ = *default_provider; |
| 37 keyword_provider_ = keyword_provider; |
| 38 if (keyword_provider) |
| 39 cached_keyword_provider_ = *keyword_provider; |
| 40 } |
| 41 |
| 28 void SearchProvider::Start(const AutocompleteInput& input, | 42 void SearchProvider::Start(const AutocompleteInput& input, |
| 29 bool minimal_changes) { | 43 bool minimal_changes) { |
| 30 matches_.clear(); | 44 matches_.clear(); |
| 31 | 45 |
| 32 // Can't return search/suggest results for bogus input or without a profile. | 46 // Can't return search/suggest results for bogus input or without a profile. |
| 33 if (!profile_ || (input.type() == AutocompleteInput::INVALID)) { | 47 if (!profile_ || (input.type() == AutocompleteInput::INVALID)) { |
| 34 Stop(); | 48 Stop(); |
| 35 return; | 49 return; |
| 36 } | 50 } |
| 37 | 51 |
| 38 // Can't search without a default provider. | 52 keyword_input_text_.clear(); |
| 39 const TemplateURL* const current_default_provider = | 53 const TemplateURL* keyword_provider = |
| 54 KeywordProvider::GetSubstitutingTemplateURLForInput(profile_, input, |
| 55 &keyword_input_text_); |
| 56 if (!TemplateURL::SupportsReplacement(keyword_provider) || |
| 57 keyword_input_text_.empty()) { |
| 58 keyword_provider = NULL; |
| 59 } |
| 60 |
| 61 const TemplateURL* default_provider = |
| 40 profile_->GetTemplateURLModel()->GetDefaultSearchProvider(); | 62 profile_->GetTemplateURLModel()->GetDefaultSearchProvider(); |
| 41 // TODO(pkasting): http://b/1155786 Eventually we should not need all these | 63 if (!TemplateURL::SupportsReplacement(default_provider)) |
| 42 // checks. | 64 default_provider = NULL; |
| 43 if (!current_default_provider || !current_default_provider->url() || | 65 |
| 44 !current_default_provider->url()->SupportsReplacement()) { | 66 if (keyword_provider == default_provider) |
| 67 keyword_provider = NULL; // No use in querying the same provider twice. |
| 68 |
| 69 if (!default_provider && !keyword_provider) { |
| 70 // No valid providers. |
| 45 Stop(); | 71 Stop(); |
| 46 return; | 72 return; |
| 47 } | 73 } |
| 48 | 74 |
| 49 // If we're still running an old query but have since changed the query text | 75 // If we're still running an old query but have since changed the query text |
| 50 // or the default provider, abort the query. | 76 // or the providers, abort the query. |
| 51 if (!done_ && (!minimal_changes || | 77 if (!done_ && (!minimal_changes || |
| 52 (last_default_provider_ != current_default_provider))) | 78 !providers_.equals(default_provider, keyword_provider))) { |
| 53 Stop(); | 79 Stop(); |
| 80 } |
| 54 | 81 |
| 55 // TODO(pkasting): http://b/1162970 We shouldn't need to structure-copy this. | 82 providers_.Set(default_provider, keyword_provider); |
| 56 // Nor should we need |last_default_provider_| just to know whether the | |
| 57 // provider changed. | |
| 58 default_provider_ = *current_default_provider; | |
| 59 last_default_provider_ = current_default_provider; | |
| 60 | 83 |
| 61 if (input.text().empty()) { | 84 if (input.text().empty()) { |
| 62 // User typed "?" alone. Give them a placeholder result indicating what | 85 // User typed "?" alone. Give them a placeholder result indicating what |
| 63 // this syntax does. | 86 // this syntax does. |
| 64 AutocompleteMatch match(this, 0, false, | 87 if (default_provider) { |
| 65 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED); | 88 AutocompleteMatch match(this, 0, false, |
| 66 static const std::wstring kNoQueryInput( | 89 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED); |
| 67 l10n_util::GetString(IDS_AUTOCOMPLETE_NO_QUERY)); | 90 static const std::wstring kNoQueryInput( |
| 68 match.contents.assign(l10n_util::GetStringF( | 91 l10n_util::GetString(IDS_AUTOCOMPLETE_NO_QUERY)); |
| 69 IDS_AUTOCOMPLETE_SEARCH_CONTENTS, default_provider_.short_name(), | 92 match.contents.assign(l10n_util::GetStringF( |
| 70 kNoQueryInput)); | 93 IDS_AUTOCOMPLETE_SEARCH_CONTENTS, default_provider->short_name(), |
| 71 match.contents_class.push_back( | 94 kNoQueryInput)); |
| 72 ACMatchClassification(0, ACMatchClassification::DIM)); | 95 match.contents_class.push_back( |
| 73 matches_.push_back(match); | 96 ACMatchClassification(0, ACMatchClassification::DIM)); |
| 97 matches_.push_back(match); |
| 98 } |
| 74 Stop(); | 99 Stop(); |
| 75 return; | 100 return; |
| 76 } | 101 } |
| 77 | 102 |
| 78 input_ = input; | 103 input_ = input; |
| 79 | 104 |
| 80 StartOrStopHistoryQuery(minimal_changes); | 105 StartOrStopHistoryQuery(minimal_changes); |
| 81 StartOrStopSuggestQuery(minimal_changes); | 106 StartOrStopSuggestQuery(minimal_changes); |
| 82 ConvertResultsToAutocompleteMatches(); | 107 ConvertResultsToAutocompleteMatches(); |
| 83 } | 108 } |
| 84 | 109 |
| 85 void SearchProvider::Run() { | 110 void SearchProvider::Run() { |
| 86 // Start a new request with the current input. | 111 // Start a new request with the current input. |
| 87 DCHECK(!done_); | 112 DCHECK(!done_); |
| 88 const TemplateURLRef* const suggestions_url = | 113 suggest_results_pending_ = 0; |
| 89 default_provider_.suggestions_url(); | 114 if (providers_.valid_suggest_for_keyword_provider()) { |
| 90 DCHECK(suggestions_url->SupportsReplacement()); | 115 suggest_results_pending_++; |
| 91 fetcher_.reset(new URLFetcher(suggestions_url->ReplaceSearchTerms( | 116 keyword_fetcher_.reset( |
| 92 default_provider_, input_.text(), | 117 CreateSuggestFetcher(providers_.keyword_provider(), |
| 93 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring()), | 118 keyword_input_text_)); |
| 94 URLFetcher::GET, this)); | 119 } |
| 95 fetcher_->set_request_context(profile_->GetRequestContext()); | 120 if (providers_.valid_suggest_for_default_provider()) { |
| 96 fetcher_->Start(); | 121 suggest_results_pending_++; |
| 122 default_fetcher_.reset( |
| 123 CreateSuggestFetcher(providers_.default_provider(), input_.text())); |
| 124 } |
| 125 // We should only get here if we have a suggest url for the keyword or default |
| 126 // providers. |
| 127 DCHECK(suggest_results_pending_ > 0); |
| 97 } | 128 } |
| 98 | 129 |
| 99 void SearchProvider::Stop() { | 130 void SearchProvider::Stop() { |
| 100 StopHistory(); | 131 StopHistory(); |
| 101 StopSuggest(); | 132 StopSuggest(); |
| 102 done_ = true; | 133 done_ = true; |
| 103 } | 134 } |
| 104 | 135 |
| 105 void SearchProvider::OnURLFetchComplete(const URLFetcher* source, | 136 void SearchProvider::OnURLFetchComplete(const URLFetcher* source, |
| 106 const GURL& url, | 137 const GURL& url, |
| 107 const URLRequestStatus& status, | 138 const URLRequestStatus& status, |
| 108 int response_code, | 139 int response_code, |
| 109 const ResponseCookies& cookie, | 140 const ResponseCookies& cookie, |
| 110 const std::string& data) { | 141 const std::string& data) { |
| 111 DCHECK(!done_); | 142 DCHECK(!done_); |
| 112 suggest_results_pending_ = false; | 143 suggest_results_pending_--; |
| 113 suggest_results_.clear(); | 144 DCHECK(suggest_results_pending_ >= 0); // Should never go negative. |
| 114 navigation_results_.clear(); | |
| 115 const net::HttpResponseHeaders* const response_headers = | 145 const net::HttpResponseHeaders* const response_headers = |
| 116 source->response_headers(); | 146 source->response_headers(); |
| 117 std::string json_data(data); | 147 std::string json_data(data); |
| 118 // JSON is supposed to be UTF-8, but some suggest service providers send JSON | 148 // JSON is supposed to be UTF-8, but some suggest service providers send JSON |
| 119 // files in non-UTF-8 encodings. The actual encoding is usually specified in | 149 // files in non-UTF-8 encodings. The actual encoding is usually specified in |
| 120 // the Content-Type header field. | 150 // the Content-Type header field. |
| 121 if (response_headers) { | 151 if (response_headers) { |
| 122 std::string charset; | 152 std::string charset; |
| 123 if (response_headers->GetCharset(&charset)) { | 153 if (response_headers->GetCharset(&charset)) { |
| 124 std::wstring wide_data; | 154 std::wstring wide_data; |
| 125 // TODO(jungshik): Switch to CodePageToUTF8 after it's added. | 155 // TODO(jungshik): Switch to CodePageToUTF8 after it's added. |
| 126 if (CodepageToWide(data, charset.c_str(), | 156 if (CodepageToWide(data, charset.c_str(), |
| 127 OnStringUtilConversionError::FAIL, &wide_data)) | 157 OnStringUtilConversionError::FAIL, &wide_data)) |
| 128 json_data = WideToUTF8(wide_data); | 158 json_data = WideToUTF8(wide_data); |
| 129 } | 159 } |
| 130 } | 160 } |
| 131 | 161 |
| 162 bool is_keyword_results = (source == keyword_fetcher_.get()); |
| 163 SuggestResults* suggest_results = is_keyword_results ? |
| 164 &keyword_suggest_results_ : &default_suggest_results_; |
| 165 |
| 132 if (status.is_success() && response_code == 200) { | 166 if (status.is_success() && response_code == 200) { |
| 133 JSONStringValueSerializer deserializer(json_data); | 167 JSONStringValueSerializer deserializer(json_data); |
| 134 deserializer.set_allow_trailing_comma(true); | 168 deserializer.set_allow_trailing_comma(true); |
| 135 scoped_ptr<Value> root_val(deserializer.Deserialize(NULL)); | 169 scoped_ptr<Value> root_val(deserializer.Deserialize(NULL)); |
| 170 const std::wstring& input_text = |
| 171 is_keyword_results ? keyword_input_text_ : input_.text(); |
| 136 have_suggest_results_ = | 172 have_suggest_results_ = |
| 137 root_val.get() && ParseSuggestResults(root_val.get()); | 173 root_val.get() && |
| 174 ParseSuggestResults(root_val.get(), is_keyword_results, input_text, |
| 175 suggest_results); |
| 138 } | 176 } |
| 139 | 177 |
| 140 ConvertResultsToAutocompleteMatches(); | 178 ConvertResultsToAutocompleteMatches(); |
| 141 listener_->OnProviderUpdate(!suggest_results_.empty()); | 179 listener_->OnProviderUpdate(!suggest_results->empty()); |
| 142 } | 180 } |
| 143 | 181 |
| 144 void SearchProvider::StartOrStopHistoryQuery(bool minimal_changes) { | 182 void SearchProvider::StartOrStopHistoryQuery(bool minimal_changes) { |
| 145 // For the minimal_changes case, if we finished the previous query and still | 183 // For the minimal_changes case, if we finished the previous query and still |
| 146 // have its results, or are allowed to keep running it, just do that, rather | 184 // have its results, or are allowed to keep running it, just do that, rather |
| 147 // than starting a new query. | 185 // than starting a new query. |
| 148 if (minimal_changes && | 186 if (minimal_changes && |
| 149 (have_history_results_ || (!done_ && !input_.synchronous_only()))) | 187 (have_history_results_ || (!done_ && !input_.synchronous_only()))) |
| 150 return; | 188 return; |
| 151 | 189 |
| 152 // We can't keep running any previous query, so halt it. | 190 // We can't keep running any previous query, so halt it. |
| 153 StopHistory(); | 191 StopHistory(); |
| 154 | 192 |
| 155 // We can't start a new query if we're only allowed synchronous results. | 193 // We can't start a new query if we're only allowed synchronous results. |
| 156 if (input_.synchronous_only()) | 194 if (input_.synchronous_only()) |
| 157 return; | 195 return; |
| 158 | 196 |
| 159 // Start the history query. | 197 // Request history for both the keyword and default provider. |
| 160 HistoryService* const history_service = | 198 if (providers_.valid_keyword_provider()) { |
| 161 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | 199 ScheduleHistoryQuery(providers_.keyword_provider().id(), |
| 162 history_service->GetMostRecentKeywordSearchTerms(default_provider_.id(), | 200 keyword_input_text_); |
| 163 input_.text(), static_cast<int>(max_matches()), | 201 } |
| 164 &history_request_consumer_, | 202 if (providers_.valid_default_provider()) { |
| 165 NewCallback(this, &SearchProvider::OnGotMostRecentKeywordSearchTerms)); | 203 ScheduleHistoryQuery(providers_.default_provider().id(), |
| 166 history_request_pending_ = true; | 204 input_.text()); |
| 205 } |
| 167 } | 206 } |
| 168 | 207 |
| 169 void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) { | 208 void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) { |
| 170 // Don't send any queries to the server until some time has elapsed after | 209 // Don't send any queries to the server until some time has elapsed after |
| 171 // the last keypress, to avoid flooding the server with requests we are | 210 // the last keypress, to avoid flooding the server with requests we are |
| 172 // likely to end up throwing away anyway. | 211 // likely to end up throwing away anyway. |
| 173 static const int kQueryDelayMs = 200; | 212 static const int kQueryDelayMs = 200; |
| 174 | 213 |
| 175 if (!IsQuerySuitableForSuggest()) { | 214 if (!IsQuerySuitableForSuggest()) { |
| 176 StopSuggest(); | 215 StopSuggest(); |
| 177 return; | 216 return; |
| 178 } | 217 } |
| 179 | 218 |
| 180 // For the minimal_changes case, if we finished the previous query and still | 219 // For the minimal_changes case, if we finished the previous query and still |
| 181 // have its results, or are allowed to keep running it, just do that, rather | 220 // have its results, or are allowed to keep running it, just do that, rather |
| 182 // than starting a new query. | 221 // than starting a new query. |
| 183 if (minimal_changes && | 222 if (minimal_changes && |
| 184 (have_suggest_results_ || (!done_ && !input_.synchronous_only()))) | 223 (have_suggest_results_ || (!done_ && !input_.synchronous_only()))) |
| 185 return; | 224 return; |
| 186 | 225 |
| 187 // We can't keep running any previous query, so halt it. | 226 // We can't keep running any previous query, so halt it. |
| 188 StopSuggest(); | 227 StopSuggest(); |
| 189 | 228 |
| 190 // We can't start a new query if we're only allowed synchronous results. | 229 // We can't start a new query if we're only allowed synchronous results. |
| 191 if (input_.synchronous_only()) | 230 if (input_.synchronous_only()) |
| 192 return; | 231 return; |
| 193 | 232 |
| 233 // We'll have at least one pending fetch. Set it to 1 now, but the value is |
| 234 // correctly set in Run. As Run isn't invoked immediately we need to set this |
| 235 // now, else we won't think we're waiting on results from the server when we |
| 236 // really are. |
| 237 suggest_results_pending_ = 1; |
| 238 |
| 194 // Kick off a timer that will start the URL fetch if it completes before | 239 // Kick off a timer that will start the URL fetch if it completes before |
| 195 // the user types another character. | 240 // the user types another character. |
| 196 suggest_results_pending_ = true; | |
| 197 | |
| 198 timer_.Stop(); | |
| 199 timer_.Start(TimeDelta::FromMilliseconds(kQueryDelayMs), this, | 241 timer_.Start(TimeDelta::FromMilliseconds(kQueryDelayMs), this, |
| 200 &SearchProvider::Run); | 242 &SearchProvider::Run); |
| 201 } | 243 } |
| 202 | 244 |
| 203 bool SearchProvider::IsQuerySuitableForSuggest() const { | 245 bool SearchProvider::IsQuerySuitableForSuggest() const { |
| 204 // Don't run Suggest when off the record, the engine doesn't support it, or | 246 // Don't run Suggest when off the record, the engine doesn't support it, or |
| 205 // the user has disabled it. | 247 // the user has disabled it. |
| 206 if (profile_->IsOffTheRecord() || | 248 if (profile_->IsOffTheRecord() || |
| 207 !default_provider_.suggestions_url() || | 249 (!providers_.valid_suggest_for_keyword_provider() && |
| 250 !providers_.valid_suggest_for_default_provider()) || |
| 208 !profile_->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled)) | 251 !profile_->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled)) |
| 209 return false; | 252 return false; |
| 210 | 253 |
| 211 // If the input type is URL, we take extra care so that private data in URL | 254 // If the input type is URL, we take extra care so that private data in URL |
| 212 // isn't sent to the server. | 255 // isn't sent to the server. |
| 213 if (input_.type() == AutocompleteInput::URL) { | 256 if (input_.type() == AutocompleteInput::URL) { |
| 214 // Don't query the server for URLs that aren't http/https/ftp. Sending | 257 // Don't query the server for URLs that aren't http/https/ftp. Sending |
| 215 // things like file: and data: is both a waste of time and a disclosure of | 258 // things like file: and data: is both a waste of time and a disclosure of |
| 216 // potentially private, local data. | 259 // potentially private, local data. |
| 217 if ((input_.scheme() != L"http") && (input_.scheme() != L"https") && | 260 if ((input_.scheme() != L"http") && (input_.scheme() != L"https") && |
| (...skipping 18 matching lines...) Expand all Loading... |
| 236 if (input_.scheme() == L"https" && parts.path.is_nonempty()) | 279 if (input_.scheme() == L"https" && parts.path.is_nonempty()) |
| 237 return false; | 280 return false; |
| 238 } | 281 } |
| 239 | 282 |
| 240 return true; | 283 return true; |
| 241 } | 284 } |
| 242 | 285 |
| 243 void SearchProvider::StopHistory() { | 286 void SearchProvider::StopHistory() { |
| 244 history_request_consumer_.CancelAllRequests(); | 287 history_request_consumer_.CancelAllRequests(); |
| 245 history_request_pending_ = false; | 288 history_request_pending_ = false; |
| 246 history_results_.clear(); | 289 keyword_history_results_.clear(); |
| 290 default_history_results_.clear(); |
| 247 have_history_results_ = false; | 291 have_history_results_ = false; |
| 248 } | 292 } |
| 249 | 293 |
| 250 void SearchProvider::StopSuggest() { | 294 void SearchProvider::StopSuggest() { |
| 251 suggest_results_pending_ = false; | 295 suggest_results_pending_ = 0; |
| 252 timer_.Stop(); | 296 timer_.Stop(); |
| 253 fetcher_.reset(); // Stop any in-progress URL fetch. | 297 // Stop any in-progress URL fetches. |
| 254 suggest_results_.clear(); | 298 keyword_fetcher_.reset(); |
| 299 default_fetcher_.reset(); |
| 300 keyword_suggest_results_.clear(); |
| 301 default_suggest_results_.clear(); |
| 302 keyword_navigation_results_.clear(); |
| 303 default_navigation_results_.clear(); |
| 255 have_suggest_results_ = false; | 304 have_suggest_results_ = false; |
| 256 } | 305 } |
| 257 | 306 |
| 307 void SearchProvider::ScheduleHistoryQuery(TemplateURL::IDType search_id, |
| 308 const std::wstring& text) { |
| 309 DCHECK(!text.empty()); |
| 310 HistoryService* const history_service = |
| 311 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); |
| 312 HistoryService::Handle request_handle = |
| 313 history_service->GetMostRecentKeywordSearchTerms( |
| 314 search_id, text, static_cast<int>(max_matches()), |
| 315 &history_request_consumer_, |
| 316 NewCallback(this, |
| 317 &SearchProvider::OnGotMostRecentKeywordSearchTerms)); |
| 318 history_request_consumer_.SetClientData(history_service, request_handle, |
| 319 search_id); |
| 320 history_request_pending_ = true; |
| 321 } |
| 322 |
| 258 void SearchProvider::OnGotMostRecentKeywordSearchTerms( | 323 void SearchProvider::OnGotMostRecentKeywordSearchTerms( |
| 259 CancelableRequestProvider::Handle handle, | 324 CancelableRequestProvider::Handle handle, |
| 260 HistoryResults* results) { | 325 HistoryResults* results) { |
| 261 history_request_pending_ = false; | 326 HistoryService* history_service = |
| 262 have_history_results_ = true; | 327 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); |
| 263 history_results_ = *results; | 328 DCHECK(history_service); |
| 329 if (providers_.valid_keyword_provider() && |
| 330 (providers_.keyword_provider().id() == |
| 331 history_request_consumer_.GetClientData(history_service, handle))) { |
| 332 keyword_history_results_ = *results; |
| 333 } else { |
| 334 default_history_results_ = *results; |
| 335 } |
| 264 ConvertResultsToAutocompleteMatches(); | 336 ConvertResultsToAutocompleteMatches(); |
| 265 listener_->OnProviderUpdate(!history_results_.empty()); | 337 listener_->OnProviderUpdate(!results->empty()); |
| 338 |
| 339 if (history_request_consumer_.PendingRequestCount() == 1) { |
| 340 // Requests are removed AFTER the callback is invoked. If the count == 1, |
| 341 // it means no more history requests are pending. |
| 342 history_request_pending_ = false; |
| 343 have_history_results_ = true; |
| 344 } |
| 266 } | 345 } |
| 267 | 346 |
| 268 bool SearchProvider::ParseSuggestResults(Value* root_val) { | 347 URLFetcher* SearchProvider::CreateSuggestFetcher(const TemplateURL& provider, |
| 348 const std::wstring& text) { |
| 349 const TemplateURLRef* const suggestions_url = provider.suggestions_url(); |
| 350 DCHECK(suggestions_url->SupportsReplacement()); |
| 351 URLFetcher* fetcher = new URLFetcher(suggestions_url->ReplaceSearchTerms( |
| 352 provider, text, TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring()), |
| 353 URLFetcher::GET, this); |
| 354 fetcher->set_request_context(profile_->GetRequestContext()); |
| 355 fetcher->Start(); |
| 356 return fetcher; |
| 357 } |
| 358 |
| 359 bool SearchProvider::ParseSuggestResults(Value* root_val, |
| 360 bool is_keyword, |
| 361 const std::wstring& input_text, |
| 362 SuggestResults* suggest_results) { |
| 269 if (!root_val->IsType(Value::TYPE_LIST)) | 363 if (!root_val->IsType(Value::TYPE_LIST)) |
| 270 return false; | 364 return false; |
| 271 ListValue* root_list = static_cast<ListValue*>(root_val); | 365 ListValue* root_list = static_cast<ListValue*>(root_val); |
| 272 | 366 |
| 273 Value* query_val; | 367 Value* query_val; |
| 274 std::wstring query_str; | 368 std::wstring query_str; |
| 275 Value* result_val; | 369 Value* result_val; |
| 276 if ((root_list->GetSize() < 2) || !root_list->Get(0, &query_val) || | 370 if ((root_list->GetSize() < 2) || !root_list->Get(0, &query_val) || |
| 277 !query_val->GetAsString(&query_str) || (query_str != input_.text()) || | 371 !query_val->GetAsString(&query_str) || (query_str != input_text) || |
| 278 !root_list->Get(1, &result_val) || !result_val->IsType(Value::TYPE_LIST)) | 372 !root_list->Get(1, &result_val) || !result_val->IsType(Value::TYPE_LIST)) |
| 279 return false; | 373 return false; |
| 280 | 374 |
| 281 ListValue* description_list = NULL; | 375 ListValue* description_list = NULL; |
| 282 if (root_list->GetSize() > 2) { | 376 if (root_list->GetSize() > 2) { |
| 283 // 3rd element: Description list. | 377 // 3rd element: Description list. |
| 284 Value* description_val; | 378 Value* description_val; |
| 285 if (root_list->Get(2, &description_val) && | 379 if (root_list->Get(2, &description_val) && |
| 286 description_val->IsType(Value::TYPE_LIST)) | 380 description_val->IsType(Value::TYPE_LIST)) |
| 287 description_list = static_cast<ListValue*>(description_val); | 381 description_list = static_cast<ListValue*>(description_val); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 315 if (!result_list->Get(i, &suggestion_val) || | 409 if (!result_list->Get(i, &suggestion_val) || |
| 316 !suggestion_val->GetAsString(&suggestion_str)) | 410 !suggestion_val->GetAsString(&suggestion_str)) |
| 317 return false; | 411 return false; |
| 318 | 412 |
| 319 Value* type_val; | 413 Value* type_val; |
| 320 std::wstring type_str; | 414 std::wstring type_str; |
| 321 if (type_list && type_list->Get(i, &type_val) && | 415 if (type_list && type_list->Get(i, &type_val) && |
| 322 type_val->GetAsString(&type_str) && (type_str == L"NAVIGATION")) { | 416 type_val->GetAsString(&type_str) && (type_str == L"NAVIGATION")) { |
| 323 Value* site_val; | 417 Value* site_val; |
| 324 std::wstring site_name; | 418 std::wstring site_name; |
| 325 if ((navigation_results_.size() < max_matches()) && | 419 NavigationResults& navigation_results = |
| 420 is_keyword ? keyword_navigation_results_ : |
| 421 default_navigation_results_; |
| 422 if ((navigation_results.size() < max_matches()) && |
| 326 description_list && description_list->Get(i, &site_val) && | 423 description_list && description_list->Get(i, &site_val) && |
| 327 site_val->IsType(Value::TYPE_STRING) && | 424 site_val->IsType(Value::TYPE_STRING) && |
| 328 site_val->GetAsString(&site_name)) { | 425 site_val->GetAsString(&site_name)) { |
| 329 // We can't blindly trust the URL coming from the server to be valid. | 426 // We can't blindly trust the URL coming from the server to be valid. |
| 330 GURL result_url = | 427 GURL result_url = |
| 331 GURL(URLFixerUpper::FixupURL(WideToUTF8(suggestion_str), | 428 GURL(URLFixerUpper::FixupURL(WideToUTF8(suggestion_str), |
| 332 std::string())); | 429 std::string())); |
| 333 if (result_url.is_valid()) { | 430 if (result_url.is_valid()) |
| 334 navigation_results_.push_back(NavigationResult(result_url, | 431 navigation_results.push_back(NavigationResult(result_url, site_name)); |
| 335 site_name)); | |
| 336 } | |
| 337 } | 432 } |
| 338 } else { | 433 } else { |
| 339 // TODO(kochi): Currently we treat a calculator result as a query, but it | 434 // TODO(kochi): Currently we treat a calculator result as a query, but it |
| 340 // is better to have better presentation for caluculator results. | 435 // is better to have better presentation for caluculator results. |
| 341 if (suggest_results_.size() < max_matches()) | 436 if (suggest_results->size() < max_matches()) |
| 342 suggest_results_.push_back(suggestion_str); | 437 suggest_results->push_back(suggestion_str); |
| 343 } | 438 } |
| 344 } | 439 } |
| 345 | 440 |
| 346 return true; | 441 return true; |
| 347 } | 442 } |
| 348 | 443 |
| 349 void SearchProvider::ConvertResultsToAutocompleteMatches() { | 444 void SearchProvider::ConvertResultsToAutocompleteMatches() { |
| 350 // Convert all the results to matches and add them to a map, so we can keep | 445 // Convert all the results to matches and add them to a map, so we can keep |
| 351 // the most relevant match for each result. | 446 // the most relevant match for each result. |
| 352 MatchMap map; | 447 MatchMap map; |
| 353 const int did_not_accept_suggestion = suggest_results_.empty() ? | 448 const Time no_time; |
| 449 int did_not_accept_keyword_suggestion = keyword_suggest_results_.empty() ? |
| 354 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE : | 450 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE : |
| 355 TemplateURLRef::NO_SUGGESTION_CHOSEN; | 451 TemplateURLRef::NO_SUGGESTION_CHOSEN; |
| 356 const Time no_time; | 452 // Keyword what you typed results are handled by the KeywordProvider. |
| 357 AddMatchToMap(input_.text(), CalculateRelevanceForWhatYouTyped(), | |
| 358 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, | |
| 359 did_not_accept_suggestion, &map); | |
| 360 | 453 |
| 361 for (HistoryResults::const_iterator i(history_results_.begin()); | 454 int did_not_accept_default_suggestion = default_suggest_results_.empty() ? |
| 362 i != history_results_.end(); ++i) { | 455 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE : |
| 363 AddMatchToMap(i->term, CalculateRelevanceForHistory(i->time), | 456 TemplateURLRef::NO_SUGGESTION_CHOSEN; |
| 364 AutocompleteMatch::SEARCH_HISTORY, did_not_accept_suggestion, | 457 if (providers_.valid_default_provider()) { |
| 365 &map); | 458 AddMatchToMap(input_.text(), CalculateRelevanceForWhatYouTyped(), |
| 459 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, |
| 460 did_not_accept_default_suggestion, false, &map); |
| 366 } | 461 } |
| 367 | 462 |
| 368 for (size_t i = 0; i < suggest_results_.size(); ++i) { | 463 AddHistoryResultsToMap(keyword_history_results_, true, |
| 369 AddMatchToMap(suggest_results_[i], CalculateRelevanceForSuggestion(i), | 464 did_not_accept_keyword_suggestion, &map); |
| 370 AutocompleteMatch::SEARCH_SUGGEST, | 465 AddHistoryResultsToMap(default_history_results_, false, |
| 371 static_cast<int>(i), &map); | 466 did_not_accept_default_suggestion, &map); |
| 372 } | 467 |
| 468 AddSuggestResultsToMap(keyword_suggest_results_, true, |
| 469 did_not_accept_keyword_suggestion, &map); |
| 470 AddSuggestResultsToMap(default_suggest_results_, false, |
| 471 did_not_accept_default_suggestion, &map); |
| 373 | 472 |
| 374 // Now add the most relevant matches from the map to |matches_|. | 473 // Now add the most relevant matches from the map to |matches_|. |
| 375 matches_.clear(); | 474 matches_.clear(); |
| 376 for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i) | 475 for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i) |
| 377 matches_.push_back(i->second); | 476 matches_.push_back(i->second); |
| 378 | 477 |
| 379 if (!navigation_results_.empty()) { | 478 AddNavigationResultsToMatches(keyword_navigation_results_, true); |
| 380 // TODO(kochi): http://b/1170574 We add only one results for navigational | 479 AddNavigationResultsToMatches(default_navigation_results_, false); |
| 381 // suggestions. If we can get more useful information about the score, | |
| 382 // consider adding more results. | |
| 383 matches_.push_back(NavigationToMatch(navigation_results_.front(), | |
| 384 CalculateRelevanceForNavigation(0))); | |
| 385 } | |
| 386 | 480 |
| 387 const size_t max_total_matches = max_matches() + 1; // 1 for "what you typed" | 481 const size_t max_total_matches = max_matches() + 1; // 1 for "what you typed" |
| 388 std::partial_sort(matches_.begin(), | 482 std::partial_sort(matches_.begin(), |
| 389 matches_.begin() + std::min(max_total_matches, matches_.size()), | 483 matches_.begin() + std::min(max_total_matches, matches_.size()), |
| 390 matches_.end(), &AutocompleteMatch::MoreRelevant); | 484 matches_.end(), &AutocompleteMatch::MoreRelevant); |
| 391 if (matches_.size() > max_total_matches) | 485 if (matches_.size() > max_total_matches) |
| 392 matches_.erase(matches_.begin() + max_total_matches, matches_.end()); | 486 matches_.erase(matches_.begin() + max_total_matches, matches_.end()); |
| 393 | 487 |
| 394 UpdateStarredStateOfMatches(); | 488 UpdateStarredStateOfMatches(); |
| 395 | 489 |
| 396 // We're done when both asynchronous subcomponents have finished. We can't | 490 // We're done when both asynchronous subcomponents have finished. We can't |
| 397 // use CancelableRequestConsumer.HasPendingRequests() for history requests | 491 // use CancelableRequestConsumer.HasPendingRequests() for history requests |
| 398 // here. A pending request is not cleared until after the completion | 492 // here. A pending request is not cleared until after the completion |
| 399 // callback has returned, but we've reached here from inside that callback. | 493 // callback has returned, but we've reached here from inside that callback. |
| 400 // HasPendingRequests() would therefore return true, and if this is the last | 494 // HasPendingRequests() would therefore return true, and if this is the last |
| 401 // thing left to calculate for this query, we'll never mark the query "done". | 495 // thing left to calculate for this query, we'll never mark the query "done". |
| 402 done_ = !history_request_pending_ && | 496 done_ = !history_request_pending_ && !suggest_results_pending_; |
| 403 !suggest_results_pending_; | 497 } |
| 498 |
| 499 void SearchProvider::AddNavigationResultsToMatches( |
| 500 const NavigationResults& navigation_results, |
| 501 bool is_keyword) { |
| 502 if (!navigation_results.empty()) { |
| 503 // TODO(kochi): http://b/1170574 We add only one results for navigational |
| 504 // suggestions. If we can get more useful information about the score, |
| 505 // consider adding more results. |
| 506 matches_.push_back( |
| 507 NavigationToMatch(navigation_results.front(), |
| 508 CalculateRelevanceForNavigation(0, is_keyword), |
| 509 is_keyword)); |
| 510 } |
| 511 } |
| 512 |
| 513 void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, |
| 514 bool is_keyword, |
| 515 int did_not_accept_suggestion, |
| 516 MatchMap* map) { |
| 517 for (HistoryResults::const_iterator i(results.begin()); i != results.end(); |
| 518 ++i) { |
| 519 AddMatchToMap(i->term, CalculateRelevanceForHistory(i->time, is_keyword), |
| 520 AutocompleteMatch::SEARCH_HISTORY, did_not_accept_suggestion, |
| 521 is_keyword, map); |
| 522 } |
| 523 } |
| 524 |
| 525 void SearchProvider::AddSuggestResultsToMap( |
| 526 const SuggestResults& suggest_results, |
| 527 bool is_keyword, |
| 528 int did_not_accept_suggestion, |
| 529 MatchMap* map) { |
| 530 for (size_t i = 0; i < suggest_results.size(); ++i) { |
| 531 AddMatchToMap(suggest_results[i], |
| 532 CalculateRelevanceForSuggestion(suggest_results, i, |
| 533 is_keyword), |
| 534 AutocompleteMatch::SEARCH_SUGGEST, |
| 535 static_cast<int>(i), is_keyword, map); |
| 536 } |
| 404 } | 537 } |
| 405 | 538 |
| 406 int SearchProvider::CalculateRelevanceForWhatYouTyped() const { | 539 int SearchProvider::CalculateRelevanceForWhatYouTyped() const { |
| 407 switch (input_.type()) { | 540 switch (input_.type()) { |
| 408 case AutocompleteInput::UNKNOWN: | 541 case AutocompleteInput::UNKNOWN: |
| 409 return 1300; | 542 return providers_.valid_keyword_provider() ? 250 : 1300; |
| 410 | 543 |
| 411 case AutocompleteInput::REQUESTED_URL: | 544 case AutocompleteInput::REQUESTED_URL: |
| 412 return 1200; | 545 return providers_.valid_keyword_provider() ? 250 : 1200; |
| 413 | 546 |
| 414 case AutocompleteInput::URL: | 547 case AutocompleteInput::URL: |
| 415 return 850; | 548 return providers_.valid_keyword_provider() ? 250 : 850; |
| 416 | 549 |
| 417 case AutocompleteInput::QUERY: | 550 case AutocompleteInput::QUERY: |
| 418 return 1300; | 551 return providers_.valid_keyword_provider() ? 250 : 1300; |
| 419 | 552 |
| 420 case AutocompleteInput::FORCED_QUERY: | 553 case AutocompleteInput::FORCED_QUERY: |
| 421 return 1500; | 554 return providers_.valid_keyword_provider() ? 250 : 1500; |
| 422 | 555 |
| 423 default: | 556 default: |
| 424 NOTREACHED(); | 557 NOTREACHED(); |
| 425 return 0; | 558 return 0; |
| 426 } | 559 } |
| 427 } | 560 } |
| 428 | 561 |
| 429 int SearchProvider::CalculateRelevanceForHistory(const Time& time) const { | 562 int SearchProvider::CalculateRelevanceForHistory(const Time& time, |
| 563 bool is_keyword) const { |
| 430 // The relevance of past searches falls off over time. This curve is chosen | 564 // The relevance of past searches falls off over time. This curve is chosen |
| 431 // so that the relevance of a search 15 minutes ago is discounted about 50 | 565 // so that the relevance of a search 15 minutes ago is discounted about 50 |
| 432 // points, while the relevance of a search two weeks ago is discounted about | 566 // points, while the relevance of a search two weeks ago is discounted about |
| 433 // 450 points. | 567 // 450 points. |
| 434 const double elapsed_time = std::max((Time::Now() - time).InSecondsF(), 0.); | 568 const double elapsed_time = std::max((Time::Now() - time).InSecondsF(), 0.); |
| 435 const int score_discount = static_cast<int>(6.5 * pow(elapsed_time, 0.3)); | 569 const int score_discount = static_cast<int>(6.5 * pow(elapsed_time, 0.3)); |
| 436 | 570 |
| 437 // Don't let scores go below 0. Negative relevance scores are meaningful in | 571 // Don't let scores go below 0. Negative relevance scores are meaningful in |
| 438 // a different way. | 572 // a different way. |
| 439 int base_score; | 573 int base_score; |
| 574 bool is_primary = providers_.is_primary_provider(is_keyword); |
| 440 switch (input_.type()) { | 575 switch (input_.type()) { |
| 441 case AutocompleteInput::UNKNOWN: | 576 case AutocompleteInput::UNKNOWN: |
| 442 case AutocompleteInput::REQUESTED_URL: | 577 case AutocompleteInput::REQUESTED_URL: |
| 443 base_score = 1050; | 578 base_score = is_primary ? 1050 : 200; |
| 444 break; | 579 break; |
| 445 | 580 |
| 446 case AutocompleteInput::URL: | 581 case AutocompleteInput::URL: |
| 447 base_score = 750; | 582 base_score = is_primary ? 750 : 200; |
| 448 break; | 583 break; |
| 449 | 584 |
| 450 case AutocompleteInput::QUERY: | 585 case AutocompleteInput::QUERY: |
| 451 case AutocompleteInput::FORCED_QUERY: | 586 case AutocompleteInput::FORCED_QUERY: |
| 452 base_score = 1250; | 587 base_score = is_primary ? 1250 : 200; |
| 453 break; | 588 break; |
| 454 | 589 |
| 455 default: | 590 default: |
| 456 NOTREACHED(); | 591 NOTREACHED(); |
| 457 base_score = 0; | 592 base_score = 0; |
| 458 break; | 593 break; |
| 459 } | 594 } |
| 460 return std::max(0, base_score - score_discount); | 595 return std::max(0, base_score - score_discount); |
| 461 } | 596 } |
| 462 | 597 |
| 463 int SearchProvider::CalculateRelevanceForSuggestion( | 598 int SearchProvider::CalculateRelevanceForSuggestion( |
| 464 size_t suggestion_number) const { | 599 const SuggestResults& suggest_results, |
| 465 DCHECK(suggestion_number < suggest_results_.size()); | 600 size_t suggestion_number, |
| 601 bool is_keyword) const { |
| 602 DCHECK(suggestion_number < suggest_results.size()); |
| 603 bool is_primary = providers_.is_primary_provider(is_keyword); |
| 466 const int suggestion_value = | 604 const int suggestion_value = |
| 467 static_cast<int>(suggest_results_.size() - 1 - suggestion_number); | 605 static_cast<int>(suggest_results.size() - 1 - suggestion_number); |
| 468 switch (input_.type()) { | 606 switch (input_.type()) { |
| 469 case AutocompleteInput::UNKNOWN: | 607 case AutocompleteInput::UNKNOWN: |
| 470 case AutocompleteInput::REQUESTED_URL: | 608 case AutocompleteInput::REQUESTED_URL: |
| 471 return 600 + suggestion_value; | 609 return suggestion_value + (is_primary ? 600 : 100); |
| 472 | 610 |
| 473 case AutocompleteInput::URL: | 611 case AutocompleteInput::URL: |
| 474 return 300 + suggestion_value; | 612 return suggestion_value + (is_primary ? 300 : 100); |
| 475 | 613 |
| 476 case AutocompleteInput::QUERY: | 614 case AutocompleteInput::QUERY: |
| 477 case AutocompleteInput::FORCED_QUERY: | 615 case AutocompleteInput::FORCED_QUERY: |
| 478 return 800 + suggestion_value; | 616 return suggestion_value + (is_primary ? 800 : 100); |
| 479 | 617 |
| 480 default: | 618 default: |
| 481 NOTREACHED(); | 619 NOTREACHED(); |
| 482 return 0; | 620 return 0; |
| 483 } | 621 } |
| 484 } | 622 } |
| 485 | 623 |
| 486 int SearchProvider::CalculateRelevanceForNavigation( | 624 int SearchProvider::CalculateRelevanceForNavigation( |
| 487 size_t suggestion_number) const { | 625 size_t suggestion_number, |
| 488 DCHECK(suggestion_number < navigation_results_.size()); | 626 bool is_keyword) const { |
| 627 DCHECK( |
| 628 (is_keyword && suggestion_number < keyword_navigation_results_.size()) || |
| 629 (!is_keyword && suggestion_number < default_navigation_results_.size())); |
| 489 // TODO(kochi): http://b/784900 Use relevance score from the NavSuggest | 630 // TODO(kochi): http://b/784900 Use relevance score from the NavSuggest |
| 490 // server if possible. | 631 // server if possible. |
| 632 bool is_primary = providers_.is_primary_provider(is_keyword); |
| 491 switch (input_.type()) { | 633 switch (input_.type()) { |
| 492 case AutocompleteInput::QUERY: | 634 case AutocompleteInput::QUERY: |
| 493 case AutocompleteInput::FORCED_QUERY: | 635 case AutocompleteInput::FORCED_QUERY: |
| 494 return 1000 + static_cast<int>(suggestion_number); | 636 return static_cast<int>(suggestion_number) + (is_primary ? 1000 : 150); |
| 495 | 637 |
| 496 default: | 638 default: |
| 497 return 800 + static_cast<int>(suggestion_number); | 639 return static_cast<int>(suggestion_number) + (is_primary ? 800 : 150); |
| 498 } | 640 } |
| 499 } | 641 } |
| 500 | 642 |
| 501 void SearchProvider::AddMatchToMap(const std::wstring& query_string, | 643 void SearchProvider::AddMatchToMap(const std::wstring& query_string, |
| 502 int relevance, | 644 int relevance, |
| 503 AutocompleteMatch::Type type, | 645 AutocompleteMatch::Type type, |
| 504 int accepted_suggestion, | 646 int accepted_suggestion, |
| 647 bool is_keyword, |
| 505 MatchMap* map) { | 648 MatchMap* map) { |
| 649 const std::wstring& input_text = |
| 650 is_keyword ? keyword_input_text_ : input_.text(); |
| 506 AutocompleteMatch match(this, relevance, false, type); | 651 AutocompleteMatch match(this, relevance, false, type); |
| 507 std::vector<size_t> content_param_offsets; | 652 std::vector<size_t> content_param_offsets; |
| 653 const TemplateURL& provider = is_keyword ? providers_.keyword_provider() : |
| 654 providers_.default_provider(); |
| 508 match.contents.assign(l10n_util::GetStringF(IDS_AUTOCOMPLETE_SEARCH_CONTENTS, | 655 match.contents.assign(l10n_util::GetStringF(IDS_AUTOCOMPLETE_SEARCH_CONTENTS, |
| 509 default_provider_.short_name(), | 656 provider.short_name(), |
| 510 query_string, | 657 query_string, |
| 511 &content_param_offsets)); | 658 &content_param_offsets)); |
| 512 if (content_param_offsets.size() == 2) { | 659 if (content_param_offsets.size() == 2) { |
| 513 AutocompleteMatch::ClassifyLocationInString(content_param_offsets[1], | 660 AutocompleteMatch::ClassifyLocationInString(content_param_offsets[1], |
| 514 query_string.length(), | 661 query_string.length(), |
| 515 match.contents.length(), | 662 match.contents.length(), |
| 516 ACMatchClassification::NONE, | 663 ACMatchClassification::NONE, |
| 517 &match.contents_class); | 664 &match.contents_class); |
| 518 } else { | 665 } else { |
| 519 // |content_param_offsets| should only not be 2 if: | 666 // |content_param_offsets| should only not be 2 if: |
| (...skipping 12 matching lines...) Expand all Loading... |
| 532 // suggestion, non-Search results will suddenly appear. | 679 // suggestion, non-Search results will suddenly appear. |
| 533 size_t search_start = 0; | 680 size_t search_start = 0; |
| 534 if (input_.type() == AutocompleteInput::FORCED_QUERY) { | 681 if (input_.type() == AutocompleteInput::FORCED_QUERY) { |
| 535 match.fill_into_edit.assign(L"?"); | 682 match.fill_into_edit.assign(L"?"); |
| 536 ++search_start; | 683 ++search_start; |
| 537 } | 684 } |
| 538 match.fill_into_edit.append(query_string); | 685 match.fill_into_edit.append(query_string); |
| 539 // NOTE: All Google suggestions currently start with the original input, but | 686 // NOTE: All Google suggestions currently start with the original input, but |
| 540 // not all Yahoo! suggestions do. | 687 // not all Yahoo! suggestions do. |
| 541 if (!input_.prevent_inline_autocomplete() && | 688 if (!input_.prevent_inline_autocomplete() && |
| 542 !match.fill_into_edit.compare(search_start, input_.text().length(), | 689 !match.fill_into_edit.compare(search_start, input_text.length(), |
| 543 input_.text())) | 690 input_text)) |
| 544 match.inline_autocomplete_offset = search_start + input_.text().length(); | 691 match.inline_autocomplete_offset = search_start + input_text.length(); |
| 545 | 692 |
| 546 const TemplateURLRef* const search_url = default_provider_.url(); | 693 const TemplateURLRef* const search_url = provider.url(); |
| 547 DCHECK(search_url->SupportsReplacement()); | 694 DCHECK(search_url->SupportsReplacement()); |
| 548 match.destination_url = search_url->ReplaceSearchTerms(default_provider_, | 695 match.destination_url = search_url->ReplaceSearchTerms(provider, |
| 549 query_string, | 696 query_string, |
| 550 accepted_suggestion, | 697 accepted_suggestion, |
| 551 input_.text()); | 698 input_text); |
| 552 | 699 |
| 553 // Search results don't look like URLs. | 700 // Search results don't look like URLs. |
| 554 match.transition = PageTransition::GENERATED; | 701 match.transition = PageTransition::GENERATED; |
| 555 | 702 |
| 556 // Try to add |match| to |map|. If a match for |query_string| is already in | 703 // Try to add |match| to |map|. If a match for |query_string| is already in |
| 557 // |map|, replace it if |match| is more relevant. | 704 // |map|, replace it if |match| is more relevant. |
| 558 // NOTE: Keep this ToLower() call in sync with url_database.cc. | 705 // NOTE: Keep this ToLower() call in sync with url_database.cc. |
| 559 const std::pair<MatchMap::iterator, bool> i = map->insert( | 706 const std::pair<MatchMap::iterator, bool> i = map->insert( |
| 560 std::pair<std::wstring, AutocompleteMatch>( | 707 std::pair<std::wstring, AutocompleteMatch>( |
| 561 l10n_util::ToLower(query_string), match)); | 708 l10n_util::ToLower(query_string), match)); |
| 562 // NOTE: We purposefully do a direct relevance comparison here instead of | 709 // NOTE: We purposefully do a direct relevance comparison here instead of |
| 563 // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added | 710 // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added |
| 564 // first" rather than "items alphabetically first" when the scores are equal. | 711 // first" rather than "items alphabetically first" when the scores are equal. |
| 565 // The only case this matters is when a user has results with the same score | 712 // The only case this matters is when a user has results with the same score |
| 566 // that differ only by capitalization; because the history system returns | 713 // that differ only by capitalization; because the history system returns |
| 567 // results sorted by recency, this means we'll pick the most recent such | 714 // results sorted by recency, this means we'll pick the most recent such |
| 568 // result even if the precision of our relevance score is too low to | 715 // result even if the precision of our relevance score is too low to |
| 569 // distinguish the two. | 716 // distinguish the two. |
| 570 if (!i.second && (match.relevance > i.first->second.relevance)) | 717 if (!i.second && (match.relevance > i.first->second.relevance)) |
| 571 i.first->second = match; | 718 i.first->second = match; |
| 572 } | 719 } |
| 573 | 720 |
| 574 AutocompleteMatch SearchProvider::NavigationToMatch( | 721 AutocompleteMatch SearchProvider::NavigationToMatch( |
| 575 const NavigationResult& navigation, | 722 const NavigationResult& navigation, |
| 576 int relevance) { | 723 int relevance, |
| 724 bool is_keyword) { |
| 725 const std::wstring& input_text = |
| 726 is_keyword ? keyword_input_text_ : input_.text(); |
| 577 AutocompleteMatch match(this, relevance, false, | 727 AutocompleteMatch match(this, relevance, false, |
| 578 AutocompleteMatch::NAVSUGGEST); | 728 AutocompleteMatch::NAVSUGGEST); |
| 579 match.destination_url = navigation.url; | 729 match.destination_url = navigation.url; |
| 580 match.contents = StringForURLDisplay(navigation.url, true); | 730 match.contents = StringForURLDisplay(navigation.url, true); |
| 581 // TODO(kochi): Consider moving HistoryURLProvider::TrimHttpPrefix() to some | 731 // TODO(kochi): Consider moving HistoryURLProvider::TrimHttpPrefix() to some |
| 582 // public utility function. | 732 // public utility function. |
| 583 if (!url_util::FindAndCompareScheme(WideToUTF8(input_.text()), | 733 if (!url_util::FindAndCompareScheme(WideToUTF8(input_text), "http", NULL)) |
| 584 "http", NULL)) | |
| 585 TrimHttpPrefix(&match.contents); | 734 TrimHttpPrefix(&match.contents); |
| 586 AutocompleteMatch::ClassifyMatchInString(input_.text(), match.contents, | 735 AutocompleteMatch::ClassifyMatchInString(input_text, match.contents, |
| 587 ACMatchClassification::URL, | 736 ACMatchClassification::URL, |
| 588 &match.contents_class); | 737 &match.contents_class); |
| 589 | 738 |
| 590 match.description = navigation.site_name; | 739 match.description = navigation.site_name; |
| 591 AutocompleteMatch::ClassifyMatchInString(input_.text(), navigation.site_name, | 740 AutocompleteMatch::ClassifyMatchInString(input_text, navigation.site_name, |
| 592 ACMatchClassification::NONE, | 741 ACMatchClassification::NONE, |
| 593 &match.description_class); | 742 &match.description_class); |
| 594 | 743 |
| 595 // When the user forced a query, we need to make sure all the fill_into_edit | 744 // When the user forced a query, we need to make sure all the fill_into_edit |
| 596 // values preserve that property. Otherwise, if the user starts editing a | 745 // values preserve that property. Otherwise, if the user starts editing a |
| 597 // suggestion, non-Search results will suddenly appear. | 746 // suggestion, non-Search results will suddenly appear. |
| 598 if (input_.type() == AutocompleteInput::FORCED_QUERY) | 747 if (input_.type() == AutocompleteInput::FORCED_QUERY) |
| 599 match.fill_into_edit.assign(L"?"); | 748 match.fill_into_edit.assign(L"?"); |
| 600 match.fill_into_edit.append(match.contents); | 749 match.fill_into_edit.append(match.contents); |
| 601 // TODO(pkasting): http://b/1112879 These should perhaps be | 750 // TODO(pkasting): http://b/1112879 These should perhaps be |
| (...skipping 15 matching lines...) Expand all Loading... |
| 617 const size_t after_slashes = std::min(url->length(), | 766 const size_t after_slashes = std::min(url->length(), |
| 618 static_cast<size_t>(scheme.end() + 3)); | 767 static_cast<size_t>(scheme.end() + 3)); |
| 619 while ((prefix_len < after_slashes) && ((*url)[prefix_len] == L'/')) | 768 while ((prefix_len < after_slashes) && ((*url)[prefix_len] == L'/')) |
| 620 ++prefix_len; | 769 ++prefix_len; |
| 621 if (prefix_len == url->length()) | 770 if (prefix_len == url->length()) |
| 622 url->clear(); | 771 url->clear(); |
| 623 else | 772 else |
| 624 url->erase(url->begin(), url->begin() + prefix_len); | 773 url->erase(url->begin(), url->begin() + prefix_len); |
| 625 return prefix_len; | 774 return prefix_len; |
| 626 } | 775 } |
| OLD | NEW |