OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 | 9 |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
56 cached_keyword_provider_ = *keyword_provider; | 56 cached_keyword_provider_ = *keyword_provider; |
57 } | 57 } |
58 | 58 |
59 SearchProvider::SearchProvider(ACProviderListener* listener, Profile* profile) | 59 SearchProvider::SearchProvider(ACProviderListener* listener, Profile* profile) |
60 : AutocompleteProvider(listener, profile, "Search"), | 60 : AutocompleteProvider(listener, profile, "Search"), |
61 suggest_results_pending_(0), | 61 suggest_results_pending_(0), |
62 have_suggest_results_(false), | 62 have_suggest_results_(false), |
63 instant_finalized_(false) { | 63 instant_finalized_(false) { |
64 } | 64 } |
65 | 65 |
66 void SearchProvider::FinalizeInstantQuery(const string16& input_text, | 66 void SearchProvider::FinalizeInstantQuery(const std::wstring& input_text, |
67 const string16& suggest_text) { | 67 const std::wstring& suggest_text) { |
68 if (done_ || instant_finalized_) | 68 if (done_ || instant_finalized_) |
69 return; | 69 return; |
70 | 70 |
71 instant_finalized_ = true; | 71 instant_finalized_ = true; |
72 UpdateDone(); | 72 UpdateDone(); |
73 | 73 |
74 if (input_text.empty()) { | 74 if (input_text.empty()) { |
75 // We only need to update the listener if we're actually done. | 75 // We only need to update the listener if we're actually done. |
76 if (done_) | 76 if (done_) |
77 listener_->OnProviderUpdate(false); | 77 listener_->OnProviderUpdate(false); |
78 return; | 78 return; |
79 } | 79 } |
80 | 80 |
81 default_provider_suggest_text_ = suggest_text; | 81 default_provider_suggest_text_ = suggest_text; |
82 | 82 |
83 string16 adjusted_input_text(input_text); | 83 std::wstring adjusted_input_text(input_text); |
84 AutocompleteInput::RemoveForcedQueryStringIfNecessary(input_.type(), | 84 AutocompleteInput::RemoveForcedQueryStringIfNecessary(input_.type(), |
85 &adjusted_input_text); | 85 &adjusted_input_text); |
86 | 86 |
87 const string16 text = adjusted_input_text + suggest_text; | 87 const std::wstring text = adjusted_input_text + suggest_text; |
88 // Remove any matches that are identical to |text|. We don't use the | 88 // Remove any matches that are identical to |text|. We don't use the |
89 // destination_url for comparison as it varies depending upon the index passed | 89 // destination_url for comparison as it varies depending upon the index passed |
90 // to TemplateURL::ReplaceSearchTerms. | 90 // to TemplateURL::ReplaceSearchTerms. |
91 for (ACMatches::iterator i = matches_.begin(); i != matches_.end();) { | 91 for (ACMatches::iterator i = matches_.begin(); i != matches_.end();) { |
92 // Reset the description/description_class of all searches. We'll set the | 92 // Reset the description/description_class of all searches. We'll set the |
93 // description of the new first match in the call to | 93 // description of the new first match in the call to |
94 // UpdateFirstSearchMatchDescription() below. | 94 // UpdateFirstSearchMatchDescription() below. |
95 if ((i->type == AutocompleteMatch::SEARCH_HISTORY) || | 95 if ((i->type == AutocompleteMatch::SEARCH_HISTORY) || |
96 (i->type == AutocompleteMatch::SEARCH_SUGGEST) || | 96 (i->type == AutocompleteMatch::SEARCH_SUGGEST) || |
97 (i->type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED)) { | 97 (i->type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED)) { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 } | 173 } |
174 | 174 |
175 providers_.Set(default_provider, keyword_provider); | 175 providers_.Set(default_provider, keyword_provider); |
176 | 176 |
177 if (input.text().empty()) { | 177 if (input.text().empty()) { |
178 // User typed "?" alone. Give them a placeholder result indicating what | 178 // User typed "?" alone. Give them a placeholder result indicating what |
179 // this syntax does. | 179 // this syntax does. |
180 if (default_provider) { | 180 if (default_provider) { |
181 AutocompleteMatch match; | 181 AutocompleteMatch match; |
182 match.provider = this; | 182 match.provider = this; |
183 match.contents.assign(l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE)); | 183 match.contents.assign(UTF16ToWideHack( |
| 184 l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE))); |
184 match.contents_class.push_back( | 185 match.contents_class.push_back( |
185 ACMatchClassification(0, ACMatchClassification::NONE)); | 186 ACMatchClassification(0, ACMatchClassification::NONE)); |
186 matches_.push_back(match); | 187 matches_.push_back(match); |
187 UpdateFirstSearchMatchDescription(); | 188 UpdateFirstSearchMatchDescription(); |
188 } | 189 } |
189 Stop(); | 190 Stop(); |
190 return; | 191 return; |
191 } | 192 } |
192 | 193 |
193 input_ = input; | 194 input_ = input; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 DCHECK_GE(suggest_results_pending_, 0); // Should never go negative. | 237 DCHECK_GE(suggest_results_pending_, 0); // Should never go negative. |
237 const net::HttpResponseHeaders* const response_headers = | 238 const net::HttpResponseHeaders* const response_headers = |
238 source->response_headers(); | 239 source->response_headers(); |
239 std::string json_data(data); | 240 std::string json_data(data); |
240 // JSON is supposed to be UTF-8, but some suggest service providers send JSON | 241 // JSON is supposed to be UTF-8, but some suggest service providers send JSON |
241 // files in non-UTF-8 encodings. The actual encoding is usually specified in | 242 // files in non-UTF-8 encodings. The actual encoding is usually specified in |
242 // the Content-Type header field. | 243 // the Content-Type header field. |
243 if (response_headers) { | 244 if (response_headers) { |
244 std::string charset; | 245 std::string charset; |
245 if (response_headers->GetCharset(&charset)) { | 246 if (response_headers->GetCharset(&charset)) { |
246 string16 data_16; | 247 std::wstring wide_data; |
247 // TODO(jungshik): Switch to CodePageToUTF8 after it's added. | 248 // TODO(jungshik): Switch to CodePageToUTF8 after it's added. |
248 if (base::CodepageToUTF16(data, charset.c_str(), | 249 if (base::CodepageToWide(data, charset.c_str(), |
249 base::OnStringConversionError::FAIL, | 250 base::OnStringConversionError::FAIL, |
250 &data_16)) | 251 &wide_data)) |
251 json_data = UTF16ToUTF8(data_16); | 252 json_data = WideToUTF8(wide_data); |
252 } | 253 } |
253 } | 254 } |
254 | 255 |
255 bool is_keyword_results = (source == keyword_fetcher_.get()); | 256 bool is_keyword_results = (source == keyword_fetcher_.get()); |
256 SuggestResults* suggest_results = is_keyword_results ? | 257 SuggestResults* suggest_results = is_keyword_results ? |
257 &keyword_suggest_results_ : &default_suggest_results_; | 258 &keyword_suggest_results_ : &default_suggest_results_; |
258 | 259 |
259 if (status.is_success() && response_code == 200) { | 260 if (status.is_success() && response_code == 200) { |
260 JSONStringValueSerializer deserializer(json_data); | 261 JSONStringValueSerializer deserializer(json_data); |
261 deserializer.set_allow_trailing_comma(true); | 262 deserializer.set_allow_trailing_comma(true); |
262 scoped_ptr<Value> root_val(deserializer.Deserialize(NULL, NULL)); | 263 scoped_ptr<Value> root_val(deserializer.Deserialize(NULL, NULL)); |
263 const string16& input_text = | 264 const std::wstring& input_text = |
264 is_keyword_results ? keyword_input_text_ : input_.text(); | 265 is_keyword_results ? keyword_input_text_ : input_.text(); |
265 have_suggest_results_ = | 266 have_suggest_results_ = |
266 root_val.get() && | 267 root_val.get() && |
267 ParseSuggestResults(root_val.get(), is_keyword_results, input_text, | 268 ParseSuggestResults(root_val.get(), is_keyword_results, input_text, |
268 suggest_results); | 269 suggest_results); |
269 } | 270 } |
270 | 271 |
271 ConvertResultsToAutocompleteMatches(); | 272 ConvertResultsToAutocompleteMatches(); |
272 listener_->OnProviderUpdate(!suggest_results->empty()); | 273 listener_->OnProviderUpdate(!suggest_results->empty()); |
273 } | 274 } |
(...skipping 14 matching lines...) Expand all Loading... |
288 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | 289 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); |
289 history::URLDatabase* url_db = history_service ? | 290 history::URLDatabase* url_db = history_service ? |
290 history_service->InMemoryDatabase() : NULL; | 291 history_service->InMemoryDatabase() : NULL; |
291 if (!url_db) | 292 if (!url_db) |
292 return; | 293 return; |
293 | 294 |
294 // Request history for both the keyword and default provider. | 295 // Request history for both the keyword and default provider. |
295 if (providers_.valid_keyword_provider()) { | 296 if (providers_.valid_keyword_provider()) { |
296 url_db->GetMostRecentKeywordSearchTerms( | 297 url_db->GetMostRecentKeywordSearchTerms( |
297 providers_.keyword_provider().id(), | 298 providers_.keyword_provider().id(), |
298 keyword_input_text_, | 299 WideToUTF16(keyword_input_text_), |
299 static_cast<int>(kMaxMatches), | 300 static_cast<int>(kMaxMatches), |
300 &keyword_history_results_); | 301 &keyword_history_results_); |
301 } | 302 } |
302 if (providers_.valid_default_provider()) { | 303 if (providers_.valid_default_provider()) { |
303 url_db->GetMostRecentKeywordSearchTerms( | 304 url_db->GetMostRecentKeywordSearchTerms( |
304 providers_.default_provider().id(), | 305 providers_.default_provider().id(), |
305 input_.text(), | 306 WideToUTF16(input_.text()), |
306 static_cast<int>(kMaxMatches), | 307 static_cast<int>(kMaxMatches), |
307 &default_history_results_); | 308 &default_history_results_); |
308 } | 309 } |
309 } | 310 } |
310 | 311 |
311 void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) { | 312 void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) { |
312 // Don't send any queries to the server until some time has elapsed after | 313 // Don't send any queries to the server until some time has elapsed after |
313 // the last keypress, to avoid flooding the server with requests we are | 314 // the last keypress, to avoid flooding the server with requests we are |
314 // likely to end up throwing away anyway. | 315 // likely to end up throwing away anyway. |
315 static const int kQueryDelayMs = 200; | 316 static const int kQueryDelayMs = 200; |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
365 // Next we check the scheme. If this is UNKNOWN/REQUESTED_URL/URL with a | 366 // Next we check the scheme. If this is UNKNOWN/REQUESTED_URL/URL with a |
366 // scheme that isn't http/https/ftp, we shouldn't send it. Sending things | 367 // scheme that isn't http/https/ftp, we shouldn't send it. Sending things |
367 // like file: and data: is both a waste of time and a disclosure of | 368 // like file: and data: is both a waste of time and a disclosure of |
368 // potentially private, local data. Other "schemes" may actually be | 369 // potentially private, local data. Other "schemes" may actually be |
369 // usernames, and we don't want to send passwords. If the scheme is OK, we | 370 // usernames, and we don't want to send passwords. If the scheme is OK, we |
370 // still need to check other cases below. If this is QUERY, then the presence | 371 // still need to check other cases below. If this is QUERY, then the presence |
371 // of these schemes means the user explicitly typed one, and thus this is | 372 // of these schemes means the user explicitly typed one, and thus this is |
372 // probably a URL that's being entered and happens to currently be invalid -- | 373 // probably a URL that's being entered and happens to currently be invalid -- |
373 // in which case we again want to run our checks below. Other QUERY cases are | 374 // in which case we again want to run our checks below. Other QUERY cases are |
374 // less likely to be URLs and thus we assume we're OK. | 375 // less likely to be URLs and thus we assume we're OK. |
375 if (!LowerCaseEqualsASCII(input_.scheme(), chrome::kHttpScheme) && | 376 if ((input_.scheme() != L"http") && (input_.scheme() != L"https") && |
376 !LowerCaseEqualsASCII(input_.scheme(), chrome::kHttpsScheme) && | 377 (input_.scheme() != L"ftp")) |
377 !LowerCaseEqualsASCII(input_.scheme(), chrome::kFtpScheme)) | |
378 return (input_.type() == AutocompleteInput::QUERY); | 378 return (input_.type() == AutocompleteInput::QUERY); |
379 | 379 |
380 // Don't send URLs with usernames, queries or refs. Some of these are | 380 // Don't send URLs with usernames, queries or refs. Some of these are |
381 // private, and the Suggest server is unlikely to have any useful results | 381 // private, and the Suggest server is unlikely to have any useful results |
382 // for any of them. Also don't send URLs with ports, as we may initially | 382 // for any of them. Also don't send URLs with ports, as we may initially |
383 // think that a username + password is a host + port (and we don't want to | 383 // think that a username + password is a host + port (and we don't want to |
384 // send usernames/passwords), and even if the port really is a port, the | 384 // send usernames/passwords), and even if the port really is a port, the |
385 // server is once again unlikely to have and useful results. | 385 // server is once again unlikely to have and useful results. |
386 const url_parse::Parsed& parts = input_.parts(); | 386 const url_parse::Parsed& parts = input_.parts(); |
387 if (parts.username.is_nonempty() || parts.port.is_nonempty() || | 387 if (parts.username.is_nonempty() || parts.port.is_nonempty() || |
388 parts.query.is_nonempty() || parts.ref.is_nonempty()) | 388 parts.query.is_nonempty() || parts.ref.is_nonempty()) |
389 return false; | 389 return false; |
390 | 390 |
391 // Don't send anything for https except the hostname. Hostnames are OK | 391 // Don't send anything for https except the hostname. Hostnames are OK |
392 // because they are visible when the TCP connection is established, but the | 392 // because they are visible when the TCP connection is established, but the |
393 // specific path may reveal private information. | 393 // specific path may reveal private information. |
394 if (LowerCaseEqualsASCII(input_.scheme(), chrome::kHttpsScheme) && | 394 if ((input_.scheme() == L"https") && parts.path.is_nonempty()) |
395 parts.path.is_nonempty()) | |
396 return false; | 395 return false; |
397 | 396 |
398 return true; | 397 return true; |
399 } | 398 } |
400 | 399 |
401 void SearchProvider::StopSuggest() { | 400 void SearchProvider::StopSuggest() { |
402 suggest_results_pending_ = 0; | 401 suggest_results_pending_ = 0; |
403 timer_.Stop(); | 402 timer_.Stop(); |
404 // Stop any in-progress URL fetches. | 403 // Stop any in-progress URL fetches. |
405 keyword_fetcher_.reset(); | 404 keyword_fetcher_.reset(); |
406 default_fetcher_.reset(); | 405 default_fetcher_.reset(); |
407 keyword_suggest_results_.clear(); | 406 keyword_suggest_results_.clear(); |
408 default_suggest_results_.clear(); | 407 default_suggest_results_.clear(); |
409 keyword_navigation_results_.clear(); | 408 keyword_navigation_results_.clear(); |
410 default_navigation_results_.clear(); | 409 default_navigation_results_.clear(); |
411 have_suggest_results_ = false; | 410 have_suggest_results_ = false; |
412 } | 411 } |
413 | 412 |
414 URLFetcher* SearchProvider::CreateSuggestFetcher(int id, | 413 URLFetcher* SearchProvider::CreateSuggestFetcher(int id, |
415 const TemplateURL& provider, | 414 const TemplateURL& provider, |
416 const string16& text) { | 415 const std::wstring& text) { |
417 const TemplateURLRef* const suggestions_url = provider.suggestions_url(); | 416 const TemplateURLRef* const suggestions_url = provider.suggestions_url(); |
418 DCHECK(suggestions_url->SupportsReplacement()); | 417 DCHECK(suggestions_url->SupportsReplacement()); |
419 URLFetcher* fetcher = URLFetcher::Create(id, | 418 URLFetcher* fetcher = URLFetcher::Create(id, |
420 GURL(suggestions_url->ReplaceSearchTerms( | 419 GURL(suggestions_url->ReplaceSearchTerms( |
421 provider, text, | 420 provider, WideToUTF16Hack(text), |
422 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16())), | 421 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16())), |
423 URLFetcher::GET, this); | 422 URLFetcher::GET, this); |
424 fetcher->set_request_context(profile_->GetRequestContext()); | 423 fetcher->set_request_context(profile_->GetRequestContext()); |
425 fetcher->Start(); | 424 fetcher->Start(); |
426 return fetcher; | 425 return fetcher; |
427 } | 426 } |
428 | 427 |
429 bool SearchProvider::ParseSuggestResults(Value* root_val, | 428 bool SearchProvider::ParseSuggestResults(Value* root_val, |
430 bool is_keyword, | 429 bool is_keyword, |
431 const string16& input_text, | 430 const std::wstring& input_text, |
432 SuggestResults* suggest_results) { | 431 SuggestResults* suggest_results) { |
433 if (!root_val->IsType(Value::TYPE_LIST)) | 432 if (!root_val->IsType(Value::TYPE_LIST)) |
434 return false; | 433 return false; |
435 ListValue* root_list = static_cast<ListValue*>(root_val); | 434 ListValue* root_list = static_cast<ListValue*>(root_val); |
436 | 435 |
437 Value* query_val; | 436 Value* query_val; |
438 string16 query_str; | 437 string16 query_str; |
439 Value* result_val; | 438 Value* result_val; |
440 if ((root_list->GetSize() < 2) || !root_list->Get(0, &query_val) || | 439 if ((root_list->GetSize() < 2) || !root_list->Get(0, &query_val) || |
441 !query_val->GetAsString(&query_str) || | 440 !query_val->GetAsString(&query_str) || |
442 (query_str != input_text) || | 441 (query_str != WideToUTF16Hack(input_text)) || |
443 !root_list->Get(1, &result_val) || !result_val->IsType(Value::TYPE_LIST)) | 442 !root_list->Get(1, &result_val) || !result_val->IsType(Value::TYPE_LIST)) |
444 return false; | 443 return false; |
445 | 444 |
446 ListValue* description_list = NULL; | 445 ListValue* description_list = NULL; |
447 if (root_list->GetSize() > 2) { | 446 if (root_list->GetSize() > 2) { |
448 // 3rd element: Description list. | 447 // 3rd element: Description list. |
449 Value* description_val; | 448 Value* description_val; |
450 if (root_list->Get(2, &description_val) && | 449 if (root_list->Get(2, &description_val) && |
451 description_val->IsType(Value::TYPE_LIST)) | 450 description_val->IsType(Value::TYPE_LIST)) |
452 description_list = static_cast<ListValue*>(description_val); | 451 description_list = static_cast<ListValue*>(description_val); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
497 is_keyword ? keyword_navigation_results_ : | 496 is_keyword ? keyword_navigation_results_ : |
498 default_navigation_results_; | 497 default_navigation_results_; |
499 if ((navigation_results.size() < kMaxMatches) && | 498 if ((navigation_results.size() < kMaxMatches) && |
500 description_list && description_list->Get(i, &site_val) && | 499 description_list && description_list->Get(i, &site_val) && |
501 site_val->IsType(Value::TYPE_STRING) && | 500 site_val->IsType(Value::TYPE_STRING) && |
502 site_val->GetAsString(&site_name)) { | 501 site_val->GetAsString(&site_name)) { |
503 // We can't blindly trust the URL coming from the server to be valid. | 502 // We can't blindly trust the URL coming from the server to be valid. |
504 GURL result_url(URLFixerUpper::FixupURL(UTF16ToUTF8(suggestion_str), | 503 GURL result_url(URLFixerUpper::FixupURL(UTF16ToUTF8(suggestion_str), |
505 std::string())); | 504 std::string())); |
506 if (result_url.is_valid()) { | 505 if (result_url.is_valid()) { |
507 navigation_results.push_back(NavigationResult(result_url, site_name)); | 506 navigation_results.push_back(NavigationResult(result_url, |
| 507 UTF16ToWideHack(site_name))); |
508 } | 508 } |
509 } | 509 } |
510 } else { | 510 } else { |
511 // TODO(kochi): Currently we treat a calculator result as a query, but it | 511 // TODO(kochi): Currently we treat a calculator result as a query, but it |
512 // is better to have better presentation for caluculator results. | 512 // is better to have better presentation for caluculator results. |
513 if (suggest_results->size() < kMaxMatches) | 513 if (suggest_results->size() < kMaxMatches) |
514 suggest_results->push_back(suggestion_str); | 514 suggest_results->push_back(UTF16ToWideHack(suggestion_str)); |
515 } | 515 } |
516 } | 516 } |
517 | 517 |
518 return true; | 518 return true; |
519 } | 519 } |
520 | 520 |
521 void SearchProvider::ConvertResultsToAutocompleteMatches() { | 521 void SearchProvider::ConvertResultsToAutocompleteMatches() { |
522 // Convert all the results to matches and add them to a map, so we can keep | 522 // Convert all the results to matches and add them to a map, so we can keep |
523 // the most relevant match for each result. | 523 // the most relevant match for each result. |
524 MatchMap map; | 524 MatchMap map; |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
605 // match having a relevance greater than the previous, but they might be | 605 // match having a relevance greater than the previous, but they might be |
606 // equal. If we didn't force the relevance to decrease and we ended up in a | 606 // equal. If we didn't force the relevance to decrease and we ended up in a |
607 // situation where the relevance was equal, then which was shown first would | 607 // situation where the relevance was equal, then which was shown first would |
608 // be random. | 608 // be random. |
609 // This uses >= to handle the case where 3 or more results have the same | 609 // This uses >= to handle the case where 3 or more results have the same |
610 // relevance. | 610 // relevance. |
611 int relevance = CalculateRelevanceForHistory(i->time, is_keyword); | 611 int relevance = CalculateRelevanceForHistory(i->time, is_keyword); |
612 if (i != results.begin() && relevance >= last_relevance) | 612 if (i != results.begin() && relevance >= last_relevance) |
613 relevance = last_relevance - 1; | 613 relevance = last_relevance - 1; |
614 last_relevance = relevance; | 614 last_relevance = relevance; |
615 AddMatchToMap(i->term, | 615 AddMatchToMap(UTF16ToWide(i->term), |
616 is_keyword ? keyword_input_text_ : input_.text(), | 616 is_keyword ? keyword_input_text_ : input_.text(), |
617 relevance, | 617 relevance, |
618 AutocompleteMatch::SEARCH_HISTORY, did_not_accept_suggestion, | 618 AutocompleteMatch::SEARCH_HISTORY, did_not_accept_suggestion, |
619 is_keyword, input_.initial_prevent_inline_autocomplete(), | 619 is_keyword, input_.initial_prevent_inline_autocomplete(), |
620 map); | 620 map); |
621 } | 621 } |
622 } | 622 } |
623 | 623 |
624 void SearchProvider::AddSuggestResultsToMap( | 624 void SearchProvider::AddSuggestResultsToMap( |
625 const SuggestResults& suggest_results, | 625 const SuggestResults& suggest_results, |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
710 int SearchProvider::CalculateRelevanceForNavigation(size_t num_results, | 710 int SearchProvider::CalculateRelevanceForNavigation(size_t num_results, |
711 size_t result_number, | 711 size_t result_number, |
712 bool is_keyword) const { | 712 bool is_keyword) const { |
713 DCHECK(result_number < num_results); | 713 DCHECK(result_number < num_results); |
714 // TODO(kochi): http://b/784900 Use relevance score from the NavSuggest | 714 // TODO(kochi): http://b/784900 Use relevance score from the NavSuggest |
715 // server if possible. | 715 // server if possible. |
716 return (providers_.is_primary_provider(is_keyword) ? 800 : 150) + | 716 return (providers_.is_primary_provider(is_keyword) ? 800 : 150) + |
717 static_cast<int>(num_results - 1 - result_number); | 717 static_cast<int>(num_results - 1 - result_number); |
718 } | 718 } |
719 | 719 |
720 void SearchProvider::AddMatchToMap(const string16& query_string, | 720 void SearchProvider::AddMatchToMap(const std::wstring& query_string, |
721 const string16& input_text, | 721 const std::wstring& input_text, |
722 int relevance, | 722 int relevance, |
723 AutocompleteMatch::Type type, | 723 AutocompleteMatch::Type type, |
724 int accepted_suggestion, | 724 int accepted_suggestion, |
725 bool is_keyword, | 725 bool is_keyword, |
726 bool prevent_inline_autocomplete, | 726 bool prevent_inline_autocomplete, |
727 MatchMap* map) { | 727 MatchMap* map) { |
728 AutocompleteMatch match(this, relevance, false, type); | 728 AutocompleteMatch match(this, relevance, false, type); |
729 std::vector<size_t> content_param_offsets; | 729 std::vector<size_t> content_param_offsets; |
730 const TemplateURL& provider = is_keyword ? providers_.keyword_provider() : | 730 const TemplateURL& provider = is_keyword ? providers_.keyword_provider() : |
731 providers_.default_provider(); | 731 providers_.default_provider(); |
732 match.contents.assign(query_string); | 732 match.contents.assign(query_string); |
733 // We do intra-string highlighting for suggestions - the suggested segment | 733 // We do intra-string highlighting for suggestions - the suggested segment |
734 // will be highlighted, e.g. for input_text = "you" the suggestion may be | 734 // will be highlighted, e.g. for input_text = "you" the suggestion may be |
735 // "youtube", so we'll bold the "tube" section: you*tube*. | 735 // "youtube", so we'll bold the "tube" section: you*tube*. |
736 if (input_text != query_string) { | 736 if (input_text != query_string) { |
737 size_t input_position = match.contents.find(input_text); | 737 size_t input_position = match.contents.find(input_text); |
738 if (input_position == string16::npos) { | 738 if (input_position == std::wstring::npos) { |
739 // The input text is not a substring of the query string, e.g. input | 739 // The input text is not a substring of the query string, e.g. input |
740 // text is "slasdot" and the query string is "slashdot", so we bold the | 740 // text is "slasdot" and the query string is "slashdot", so we bold the |
741 // whole thing. | 741 // whole thing. |
742 match.contents_class.push_back( | 742 match.contents_class.push_back( |
743 ACMatchClassification(0, ACMatchClassification::MATCH)); | 743 ACMatchClassification(0, ACMatchClassification::MATCH)); |
744 } else { | 744 } else { |
745 // TODO(beng): ACMatchClassification::MATCH now seems to just mean | 745 // TODO(beng): ACMatchClassification::MATCH now seems to just mean |
746 // "bold" this. Consider modifying the terminology. | 746 // "bold" this. Consider modifying the terminology. |
747 // We don't iterate over the string here annotating all matches because | 747 // We don't iterate over the string here annotating all matches because |
748 // it looks odd to have every occurrence of a substring that may be as | 748 // it looks odd to have every occurrence of a substring that may be as |
(...skipping 18 matching lines...) Expand all Loading... |
767 // completion. | 767 // completion. |
768 match.contents_class.push_back( | 768 match.contents_class.push_back( |
769 ACMatchClassification(0, ACMatchClassification::NONE)); | 769 ACMatchClassification(0, ACMatchClassification::NONE)); |
770 } | 770 } |
771 | 771 |
772 // When the user forced a query, we need to make sure all the fill_into_edit | 772 // When the user forced a query, we need to make sure all the fill_into_edit |
773 // values preserve that property. Otherwise, if the user starts editing a | 773 // values preserve that property. Otherwise, if the user starts editing a |
774 // suggestion, non-Search results will suddenly appear. | 774 // suggestion, non-Search results will suddenly appear. |
775 size_t search_start = 0; | 775 size_t search_start = 0; |
776 if (input_.type() == AutocompleteInput::FORCED_QUERY) { | 776 if (input_.type() == AutocompleteInput::FORCED_QUERY) { |
777 match.fill_into_edit.assign(ASCIIToUTF16("?")); | 777 match.fill_into_edit.assign(L"?"); |
778 ++search_start; | 778 ++search_start; |
779 } | 779 } |
780 if (is_keyword) { | 780 if (is_keyword) { |
781 match.fill_into_edit.append( | 781 match.fill_into_edit.append(UTF16ToWideHack( |
782 providers_.keyword_provider().keyword() + char16(' ')); | 782 providers_.keyword_provider().keyword() + char16(' '))); |
783 match.template_url = &providers_.keyword_provider(); | 783 match.template_url = &providers_.keyword_provider(); |
784 } | 784 } |
785 match.fill_into_edit.append(query_string); | 785 match.fill_into_edit.append(query_string); |
786 // Not all suggestions start with the original input. | 786 // Not all suggestions start with the original input. |
787 if (!prevent_inline_autocomplete && | 787 if (!prevent_inline_autocomplete && |
788 !match.fill_into_edit.compare(search_start, input_text.length(), | 788 !match.fill_into_edit.compare(search_start, input_text.length(), |
789 input_text)) | 789 input_text)) |
790 match.inline_autocomplete_offset = search_start + input_text.length(); | 790 match.inline_autocomplete_offset = search_start + input_text.length(); |
791 | 791 |
792 const TemplateURLRef* const search_url = provider.url(); | 792 const TemplateURLRef* const search_url = provider.url(); |
793 DCHECK(search_url->SupportsReplacement()); | 793 DCHECK(search_url->SupportsReplacement()); |
794 match.destination_url = | 794 match.destination_url = |
795 GURL(search_url->ReplaceSearchTerms(provider, | 795 GURL(search_url->ReplaceSearchTerms(provider, |
796 query_string, | 796 WideToUTF16Hack(query_string), |
797 accepted_suggestion, | 797 accepted_suggestion, |
798 input_text)); | 798 WideToUTF16Hack(input_text))); |
799 | 799 |
800 // Search results don't look like URLs. | 800 // Search results don't look like URLs. |
801 match.transition = | 801 match.transition = |
802 is_keyword ? PageTransition::KEYWORD : PageTransition::GENERATED; | 802 is_keyword ? PageTransition::KEYWORD : PageTransition::GENERATED; |
803 | 803 |
804 // Try to add |match| to |map|. If a match for |query_string| is already in | 804 // Try to add |match| to |map|. If a match for |query_string| is already in |
805 // |map|, replace it if |match| is more relevant. | 805 // |map|, replace it if |match| is more relevant. |
806 // NOTE: Keep this ToLower() call in sync with url_database.cc. | 806 // NOTE: Keep this ToLower() call in sync with url_database.cc. |
807 const std::pair<MatchMap::iterator, bool> i = map->insert( | 807 const std::pair<MatchMap::iterator, bool> i = map->insert( |
808 std::pair<string16, AutocompleteMatch>( | 808 std::pair<std::wstring, AutocompleteMatch>( |
809 l10n_util::ToLower(query_string), match)); | 809 UTF16ToWide(l10n_util::ToLower(WideToUTF16(query_string))), match)); |
810 // NOTE: We purposefully do a direct relevance comparison here instead of | 810 // NOTE: We purposefully do a direct relevance comparison here instead of |
811 // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added | 811 // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added |
812 // first" rather than "items alphabetically first" when the scores are equal. | 812 // first" rather than "items alphabetically first" when the scores are equal. |
813 // The only case this matters is when a user has results with the same score | 813 // The only case this matters is when a user has results with the same score |
814 // that differ only by capitalization; because the history system returns | 814 // that differ only by capitalization; because the history system returns |
815 // results sorted by recency, this means we'll pick the most recent such | 815 // results sorted by recency, this means we'll pick the most recent such |
816 // result even if the precision of our relevance score is too low to | 816 // result even if the precision of our relevance score is too low to |
817 // distinguish the two. | 817 // distinguish the two. |
818 if (!i.second && (match.relevance > i.first->second.relevance)) | 818 if (!i.second && (match.relevance > i.first->second.relevance)) |
819 i.first->second = match; | 819 i.first->second = match; |
820 } | 820 } |
821 | 821 |
822 AutocompleteMatch SearchProvider::NavigationToMatch( | 822 AutocompleteMatch SearchProvider::NavigationToMatch( |
823 const NavigationResult& navigation, | 823 const NavigationResult& navigation, |
824 int relevance, | 824 int relevance, |
825 bool is_keyword) { | 825 bool is_keyword) { |
826 const string16& input_text = | 826 const std::wstring& input_text = |
827 is_keyword ? keyword_input_text_ : input_.text(); | 827 is_keyword ? keyword_input_text_ : input_.text(); |
828 AutocompleteMatch match(this, relevance, false, | 828 AutocompleteMatch match(this, relevance, false, |
829 AutocompleteMatch::NAVSUGGEST); | 829 AutocompleteMatch::NAVSUGGEST); |
830 match.destination_url = navigation.url; | 830 match.destination_url = navigation.url; |
831 match.contents = | 831 match.contents = |
832 StringForURLDisplay(navigation.url, true, !HasHTTPScheme(input_text)); | 832 StringForURLDisplay(navigation.url, true, !HasHTTPScheme(input_text)); |
833 AutocompleteMatch::ClassifyMatchInString(input_text, match.contents, | 833 AutocompleteMatch::ClassifyMatchInString(input_text, match.contents, |
834 ACMatchClassification::URL, | 834 ACMatchClassification::URL, |
835 &match.contents_class); | 835 &match.contents_class); |
836 | 836 |
837 match.description = navigation.site_name; | 837 match.description = navigation.site_name; |
838 AutocompleteMatch::ClassifyMatchInString(input_text, navigation.site_name, | 838 AutocompleteMatch::ClassifyMatchInString(input_text, navigation.site_name, |
839 ACMatchClassification::NONE, | 839 ACMatchClassification::NONE, |
840 &match.description_class); | 840 &match.description_class); |
841 | 841 |
842 // When the user forced a query, we need to make sure all the fill_into_edit | 842 // When the user forced a query, we need to make sure all the fill_into_edit |
843 // values preserve that property. Otherwise, if the user starts editing a | 843 // values preserve that property. Otherwise, if the user starts editing a |
844 // suggestion, non-Search results will suddenly appear. | 844 // suggestion, non-Search results will suddenly appear. |
845 if (input_.type() == AutocompleteInput::FORCED_QUERY) | 845 if (input_.type() == AutocompleteInput::FORCED_QUERY) |
846 match.fill_into_edit.assign(ASCIIToUTF16("?")); | 846 match.fill_into_edit.assign(L"?"); |
847 match.fill_into_edit.append( | 847 match.fill_into_edit.append( |
848 AutocompleteInput::FormattedStringWithEquivalentMeaning(navigation.url, | 848 AutocompleteInput::FormattedStringWithEquivalentMeaning(navigation.url, |
849 match.contents)); | 849 match.contents)); |
850 // TODO(pkasting): http://b/1112879 These should perhaps be | 850 // TODO(pkasting): http://b/1112879 These should perhaps be |
851 // inline-autocompletable? | 851 // inline-autocompletable? |
852 | 852 |
853 return match; | 853 return match; |
854 } | 854 } |
855 | 855 |
856 void SearchProvider::UpdateDone() { | 856 void SearchProvider::UpdateDone() { |
857 // We're done when there are no more suggest queries pending (this is set to 1 | 857 // We're done when there are no more suggest queries pending (this is set to 1 |
858 // when the timer is started) and we're not waiting on instant. | 858 // when the timer is started) and we're not waiting on instant. |
859 done_ = ((suggest_results_pending_ == 0) && | 859 done_ = ((suggest_results_pending_ == 0) && |
860 (instant_finalized_ || !InstantController::IsEnabled(profile_))); | 860 (instant_finalized_ || !InstantController::IsEnabled(profile_))); |
861 } | 861 } |
862 | 862 |
863 void SearchProvider::UpdateFirstSearchMatchDescription() { | 863 void SearchProvider::UpdateFirstSearchMatchDescription() { |
864 if (!providers_.valid_default_provider() || matches_.empty()) | 864 if (!providers_.valid_default_provider() || matches_.empty()) |
865 return; | 865 return; |
866 | 866 |
867 for (ACMatches::iterator i = matches_.begin(); i != matches_.end(); ++i) { | 867 for (ACMatches::iterator i = matches_.begin(); i != matches_.end(); ++i) { |
868 AutocompleteMatch& match = *i; | 868 AutocompleteMatch& match = *i; |
869 switch (match.type) { | 869 switch (match.type) { |
870 case AutocompleteMatch::SEARCH_WHAT_YOU_TYPED: | 870 case AutocompleteMatch::SEARCH_WHAT_YOU_TYPED: |
871 case AutocompleteMatch::SEARCH_HISTORY: | 871 case AutocompleteMatch::SEARCH_HISTORY: |
872 case AutocompleteMatch::SEARCH_SUGGEST: | 872 case AutocompleteMatch::SEARCH_SUGGEST: |
873 match.description.assign(l10n_util::GetStringFUTF16( | 873 match.description.assign( |
874 IDS_AUTOCOMPLETE_SEARCH_DESCRIPTION, | 874 UTF16ToWideHack(l10n_util::GetStringFUTF16( |
875 providers_.default_provider(). | 875 IDS_AUTOCOMPLETE_SEARCH_DESCRIPTION, |
876 AdjustedShortNameForLocaleDirection())); | 876 providers_.default_provider(). |
| 877 AdjustedShortNameForLocaleDirection()))); |
877 match.description_class.push_back( | 878 match.description_class.push_back( |
878 ACMatchClassification(0, ACMatchClassification::DIM)); | 879 ACMatchClassification(0, ACMatchClassification::DIM)); |
879 // Only the first search match gets a description. | 880 // Only the first search match gets a description. |
880 return; | 881 return; |
881 | 882 |
882 default: | 883 default: |
883 break; | 884 break; |
884 } | 885 } |
885 } | 886 } |
886 } | 887 } |
OLD | NEW |