OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 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 "components/omnibox/search_provider.h" | 5 #include "components/omnibox/search_provider.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 | 9 |
10 #include "base/base64.h" | 10 #include "base/base64.h" |
11 #include "base/callback.h" | 11 #include "base/callback.h" |
12 #include "base/i18n/break_iterator.h" | 12 #include "base/i18n/break_iterator.h" |
13 #include "base/i18n/case_conversion.h" | 13 #include "base/i18n/case_conversion.h" |
14 #include "base/json/json_string_value_serializer.h" | 14 #include "base/json/json_string_value_serializer.h" |
15 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
16 #include "base/metrics/user_metrics.h" | 16 #include "base/metrics/user_metrics.h" |
17 #include "base/rand_util.h" | 17 #include "base/rand_util.h" |
18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
19 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
20 #include "components/history/core/browser/in_memory_database.h" | 20 #include "components/history/core/browser/in_memory_database.h" |
21 #include "components/history/core/browser/keyword_search_term.h" | 21 #include "components/history/core/browser/keyword_search_term.h" |
22 #include "components/metrics/proto/omnibox_input_type.pb.h" | 22 #include "components/metrics/proto/omnibox_input_type.pb.h" |
23 #include "components/omnibox/autocomplete_provider_delegate.h" | 23 #include "components/omnibox/autocomplete_provider_client.h" |
24 #include "components/omnibox/autocomplete_provider_listener.h" | 24 #include "components/omnibox/autocomplete_provider_listener.h" |
25 #include "components/omnibox/autocomplete_result.h" | 25 #include "components/omnibox/autocomplete_result.h" |
26 #include "components/omnibox/keyword_provider.h" | 26 #include "components/omnibox/keyword_provider.h" |
27 #include "components/omnibox/omnibox_field_trial.h" | 27 #include "components/omnibox/omnibox_field_trial.h" |
28 #include "components/omnibox/url_prefix.h" | 28 #include "components/omnibox/url_prefix.h" |
29 #include "components/search/search.h" | 29 #include "components/search/search.h" |
30 #include "components/search_engines/template_url_prepopulate_data.h" | 30 #include "components/search_engines/template_url_prepopulate_data.h" |
31 #include "components/search_engines/template_url_service.h" | 31 #include "components/search_engines/template_url_service.h" |
32 #include "components/variations/variations_http_header_provider.h" | 32 #include "components/variations/variations_http_header_provider.h" |
33 #include "grit/components_strings.h" | 33 #include "grit/components_strings.h" |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 | 115 |
116 | 116 |
117 // SearchProvider ------------------------------------------------------------- | 117 // SearchProvider ------------------------------------------------------------- |
118 | 118 |
119 // static | 119 // static |
120 int SearchProvider::kMinimumTimeBetweenSuggestQueriesMs = 100; | 120 int SearchProvider::kMinimumTimeBetweenSuggestQueriesMs = 100; |
121 | 121 |
122 SearchProvider::SearchProvider( | 122 SearchProvider::SearchProvider( |
123 AutocompleteProviderListener* listener, | 123 AutocompleteProviderListener* listener, |
124 TemplateURLService* template_url_service, | 124 TemplateURLService* template_url_service, |
125 scoped_ptr<AutocompleteProviderDelegate> delegate) | 125 scoped_ptr<AutocompleteProviderClient> client) |
126 : BaseSearchProvider(template_url_service, delegate.Pass(), | 126 : BaseSearchProvider(template_url_service, client.Pass(), |
127 AutocompleteProvider::TYPE_SEARCH), | 127 AutocompleteProvider::TYPE_SEARCH), |
128 listener_(listener), | 128 listener_(listener), |
129 suggest_results_pending_(0), | 129 suggest_results_pending_(0), |
130 providers_(template_url_service), | 130 providers_(template_url_service), |
131 answers_cache_(1) { | 131 answers_cache_(1) { |
132 } | 132 } |
133 | 133 |
134 // static | 134 // static |
135 std::string SearchProvider::GetSuggestMetadata(const AutocompleteMatch& match) { | 135 std::string SearchProvider::GetSuggestMetadata(const AutocompleteMatch& match) { |
136 return match.GetAdditionalInfo(kSuggestMetadataKey); | 136 return match.GetAdditionalInfo(kSuggestMetadataKey); |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
325 } | 325 } |
326 | 326 |
327 void SearchProvider::UpdateMatchContentsClass( | 327 void SearchProvider::UpdateMatchContentsClass( |
328 const base::string16& input_text, | 328 const base::string16& input_text, |
329 SearchSuggestionParser::Results* results) { | 329 SearchSuggestionParser::Results* results) { |
330 for (SearchSuggestionParser::SuggestResults::iterator sug_it = | 330 for (SearchSuggestionParser::SuggestResults::iterator sug_it = |
331 results->suggest_results.begin(); | 331 results->suggest_results.begin(); |
332 sug_it != results->suggest_results.end(); ++sug_it) { | 332 sug_it != results->suggest_results.end(); ++sug_it) { |
333 sug_it->ClassifyMatchContents(false, input_text); | 333 sug_it->ClassifyMatchContents(false, input_text); |
334 } | 334 } |
335 const std::string languages(delegate_->AcceptLanguages()); | 335 const std::string languages(client_->AcceptLanguages()); |
336 for (SearchSuggestionParser::NavigationResults::iterator nav_it = | 336 for (SearchSuggestionParser::NavigationResults::iterator nav_it = |
337 results->navigation_results.begin(); | 337 results->navigation_results.begin(); |
338 nav_it != results->navigation_results.end(); ++nav_it) { | 338 nav_it != results->navigation_results.end(); ++nav_it) { |
339 nav_it->CalculateAndClassifyMatchContents(false, input_text, languages); | 339 nav_it->CalculateAndClassifyMatchContents(false, input_text, languages); |
340 } | 340 } |
341 } | 341 } |
342 | 342 |
343 void SearchProvider::SortResults(bool is_keyword, | 343 void SearchProvider::SortResults(bool is_keyword, |
344 SearchSuggestionParser::Results* results) { | 344 SearchSuggestionParser::Results* results) { |
345 // Ignore suggested scores for non-keyword matches in keyword mode; if the | 345 // Ignore suggested scores for non-keyword matches in keyword mode; if the |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
466 if (minimal_changes) | 466 if (minimal_changes) |
467 return; | 467 return; |
468 | 468 |
469 keyword_history_results_.clear(); | 469 keyword_history_results_.clear(); |
470 default_history_results_.clear(); | 470 default_history_results_.clear(); |
471 | 471 |
472 if (OmniboxFieldTrial::SearchHistoryDisable( | 472 if (OmniboxFieldTrial::SearchHistoryDisable( |
473 input_.current_page_classification())) | 473 input_.current_page_classification())) |
474 return; | 474 return; |
475 | 475 |
476 history::URLDatabase* url_db = delegate_->InMemoryDatabase(); | 476 history::URLDatabase* url_db = client_->InMemoryDatabase(); |
477 if (!url_db) | 477 if (!url_db) |
478 return; | 478 return; |
479 | 479 |
480 // Request history for both the keyword and default provider. We grab many | 480 // Request history for both the keyword and default provider. We grab many |
481 // more matches than we'll ultimately clamp to so that if there are several | 481 // more matches than we'll ultimately clamp to so that if there are several |
482 // recent multi-word matches who scores are lowered (see | 482 // recent multi-word matches who scores are lowered (see |
483 // AddHistoryResultsToMap()), they won't crowd out older, higher-scoring | 483 // AddHistoryResultsToMap()), they won't crowd out older, higher-scoring |
484 // matches. Note that this doesn't fix the problem entirely, but merely | 484 // matches. Note that this doesn't fix the problem entirely, but merely |
485 // limits it to cases with a very large number of such multi-word matches; for | 485 // limits it to cases with a very large number of such multi-word matches; for |
486 // now, this seems OK compared with the complexity of a real fix, which would | 486 // now, this seems OK compared with the complexity of a real fix, which would |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
547 return; | 547 return; |
548 } | 548 } |
549 timer_.Start(FROM_HERE, next_suggest_time - now, this, &SearchProvider::Run); | 549 timer_.Start(FROM_HERE, next_suggest_time - now, this, &SearchProvider::Run); |
550 } | 550 } |
551 | 551 |
552 bool SearchProvider::IsQuerySuitableForSuggest() const { | 552 bool SearchProvider::IsQuerySuitableForSuggest() const { |
553 // Don't run Suggest in incognito mode, if the engine doesn't support it, or | 553 // Don't run Suggest in incognito mode, if the engine doesn't support it, or |
554 // if the user has disabled it. | 554 // if the user has disabled it. |
555 const TemplateURL* default_url = providers_.GetDefaultProviderURL(); | 555 const TemplateURL* default_url = providers_.GetDefaultProviderURL(); |
556 const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); | 556 const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
557 if (delegate_->IsOffTheRecord() || | 557 if (client_->IsOffTheRecord() || |
558 ((!default_url || default_url->suggestions_url().empty()) && | 558 ((!default_url || default_url->suggestions_url().empty()) && |
559 (!keyword_url || keyword_url->suggestions_url().empty())) || | 559 (!keyword_url || keyword_url->suggestions_url().empty())) || |
560 !delegate_->SearchSuggestEnabled()) | 560 !client_->SearchSuggestEnabled()) |
561 return false; | 561 return false; |
562 | 562 |
563 // If the input type might be a URL, we take extra care so that private data | 563 // If the input type might be a URL, we take extra care so that private data |
564 // isn't sent to the server. | 564 // isn't sent to the server. |
565 | 565 |
566 // FORCED_QUERY means the user is explicitly asking us to search for this, so | 566 // FORCED_QUERY means the user is explicitly asking us to search for this, so |
567 // we assume it isn't a URL and/or there isn't private data. | 567 // we assume it isn't a URL and/or there isn't private data. |
568 if (input_.type() == metrics::OmniboxInputType::FORCED_QUERY) | 568 if (input_.type() == metrics::OmniboxInputType::FORCED_QUERY) |
569 return true; | 569 return true; |
570 | 570 |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
671 } | 671 } |
672 GURL suggest_url(template_url->suggestions_url_ref().ReplaceSearchTerms( | 672 GURL suggest_url(template_url->suggestions_url_ref().ReplaceSearchTerms( |
673 search_term_args, | 673 search_term_args, |
674 providers_.template_url_service()->search_terms_data())); | 674 providers_.template_url_service()->search_terms_data())); |
675 if (!suggest_url.is_valid()) | 675 if (!suggest_url.is_valid()) |
676 return NULL; | 676 return NULL; |
677 // Send the current page URL if user setting and URL requirements are met and | 677 // Send the current page URL if user setting and URL requirements are met and |
678 // the user is in the field trial. | 678 // the user is in the field trial. |
679 if (CanSendURL(current_page_url_, suggest_url, template_url, | 679 if (CanSendURL(current_page_url_, suggest_url, template_url, |
680 input.current_page_classification(), | 680 input.current_page_classification(), |
681 template_url_service_->search_terms_data(), delegate_.get()) && | 681 template_url_service_->search_terms_data(), client_.get()) && |
682 OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial()) { | 682 OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial()) { |
683 search_term_args.current_page_url = current_page_url_.spec(); | 683 search_term_args.current_page_url = current_page_url_.spec(); |
684 // Create the suggest URL again with the current page URL. | 684 // Create the suggest URL again with the current page URL. |
685 suggest_url = GURL(template_url->suggestions_url_ref().ReplaceSearchTerms( | 685 suggest_url = GURL(template_url->suggestions_url_ref().ReplaceSearchTerms( |
686 search_term_args, | 686 search_term_args, |
687 providers_.template_url_service()->search_terms_data())); | 687 providers_.template_url_service()->search_terms_data())); |
688 } | 688 } |
689 | 689 |
690 suggest_results_pending_++; | 690 suggest_results_pending_++; |
691 LogOmniboxSuggestRequest(REQUEST_SENT); | 691 LogOmniboxSuggestRequest(REQUEST_SENT); |
692 | 692 |
693 net::URLFetcher* fetcher = | 693 net::URLFetcher* fetcher = |
694 net::URLFetcher::Create(id, suggest_url, net::URLFetcher::GET, this); | 694 net::URLFetcher::Create(id, suggest_url, net::URLFetcher::GET, this); |
695 fetcher->SetRequestContext(delegate_->RequestContext()); | 695 fetcher->SetRequestContext(client_->RequestContext()); |
696 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES); | 696 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES); |
697 // Add Chrome experiment state to the request headers. | 697 // Add Chrome experiment state to the request headers. |
698 net::HttpRequestHeaders headers; | 698 net::HttpRequestHeaders headers; |
699 variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( | 699 variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( |
700 fetcher->GetOriginalURL(), delegate_->IsOffTheRecord(), false, &headers); | 700 fetcher->GetOriginalURL(), client_->IsOffTheRecord(), false, &headers); |
701 fetcher->SetExtraRequestHeaders(headers.ToString()); | 701 fetcher->SetExtraRequestHeaders(headers.ToString()); |
702 fetcher->Start(); | 702 fetcher->Start(); |
703 return fetcher; | 703 return fetcher; |
704 } | 704 } |
705 | 705 |
706 void SearchProvider::ConvertResultsToAutocompleteMatches() { | 706 void SearchProvider::ConvertResultsToAutocompleteMatches() { |
707 // Convert all the results to matches and add them to a map, so we can keep | 707 // Convert all the results to matches and add them to a map, so we can keep |
708 // the most relevant match for each result. | 708 // the most relevant match for each result. |
709 base::TimeTicks start_time(base::TimeTicks::Now()); | 709 base::TimeTicks start_time(base::TimeTicks::Now()); |
710 MatchMap map; | 710 MatchMap map; |
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
977 // (which will corrupt state and likely crash), since the classifier | 977 // (which will corrupt state and likely crash), since the classifier |
978 // always disables inline autocomplete. | 978 // always disables inline autocomplete. |
979 // * When the user has typed the whole string before as a query, then it's | 979 // * When the user has typed the whole string before as a query, then it's |
980 // likely the user has no expectation that term should be interpreted as | 980 // likely the user has no expectation that term should be interpreted as |
981 // as a URL, so we need not do anything special to preserve user | 981 // as a URL, so we need not do anything special to preserve user |
982 // expectation. | 982 // expectation. |
983 int last_relevance = 0; | 983 int last_relevance = 0; |
984 if (!base_prevent_inline_autocomplete && !found_what_you_typed_match && | 984 if (!base_prevent_inline_autocomplete && !found_what_you_typed_match && |
985 scored_results.front().relevance() >= 1200) { | 985 scored_results.front().relevance() >= 1200) { |
986 AutocompleteMatch match; | 986 AutocompleteMatch match; |
987 delegate_->Classify(scored_results.front().suggestion(), false, false, | 987 client_->Classify(scored_results.front().suggestion(), false, false, |
988 input_.current_page_classification(), &match, NULL); | 988 input_.current_page_classification(), &match, NULL); |
989 // Demote this match that would normally be interpreted as a URL to have | 989 // Demote this match that would normally be interpreted as a URL to have |
990 // the highest score a previously-issued search query could have when | 990 // the highest score a previously-issued search query could have when |
991 // scoring with the non-aggressive method. A consequence of demoting | 991 // scoring with the non-aggressive method. A consequence of demoting |
992 // by revising |last_relevance| is that this match and all following | 992 // by revising |last_relevance| is that this match and all following |
993 // matches get demoted; the relative order of matches is preserved. | 993 // matches get demoted; the relative order of matches is preserved. |
994 // One could imagine demoting only those matches that might cause | 994 // One could imagine demoting only those matches that might cause |
995 // confusion (which, by the way, might change the relative order of | 995 // confusion (which, by the way, might change the relative order of |
996 // matches. We have decided to go with the simple demote-all approach | 996 // matches. We have decided to go with the simple demote-all approach |
997 // because selective demotion requires multiple Classify() calls and | 997 // because selective demotion requires multiple Classify() calls and |
998 // such calls can be expensive (as expensive as running the whole | 998 // such calls can be expensive (as expensive as running the whole |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1148 // scheme. | 1148 // scheme. |
1149 const URLPrefix* prefix = | 1149 const URLPrefix* prefix = |
1150 URLPrefix::BestURLPrefix(navigation.formatted_url(), input); | 1150 URLPrefix::BestURLPrefix(navigation.formatted_url(), input); |
1151 size_t match_start = (prefix == NULL) ? | 1151 size_t match_start = (prefix == NULL) ? |
1152 navigation.formatted_url().find(input) : prefix->prefix.length(); | 1152 navigation.formatted_url().find(input) : prefix->prefix.length(); |
1153 bool trim_http = !AutocompleteInput::HasHTTPScheme(input) && | 1153 bool trim_http = !AutocompleteInput::HasHTTPScheme(input) && |
1154 (!prefix || (match_start != 0)); | 1154 (!prefix || (match_start != 0)); |
1155 const net::FormatUrlTypes format_types = | 1155 const net::FormatUrlTypes format_types = |
1156 net::kFormatUrlOmitAll & ~(trim_http ? 0 : net::kFormatUrlOmitHTTP); | 1156 net::kFormatUrlOmitAll & ~(trim_http ? 0 : net::kFormatUrlOmitHTTP); |
1157 | 1157 |
1158 const std::string languages(delegate_->AcceptLanguages()); | 1158 const std::string languages(client_->AcceptLanguages()); |
1159 size_t inline_autocomplete_offset = (prefix == NULL) ? | 1159 size_t inline_autocomplete_offset = (prefix == NULL) ? |
1160 base::string16::npos : (match_start + input.length()); | 1160 base::string16::npos : (match_start + input.length()); |
1161 match.fill_into_edit += | 1161 match.fill_into_edit += |
1162 AutocompleteInput::FormattedStringWithEquivalentMeaning( | 1162 AutocompleteInput::FormattedStringWithEquivalentMeaning( |
1163 navigation.url(), | 1163 navigation.url(), |
1164 net::FormatUrl(navigation.url(), languages, format_types, | 1164 net::FormatUrl(navigation.url(), languages, format_types, |
1165 net::UnescapeRule::SPACES, NULL, NULL, | 1165 net::UnescapeRule::SPACES, NULL, NULL, |
1166 &inline_autocomplete_offset), | 1166 &inline_autocomplete_offset), |
1167 delegate_->SchemeClassifier()); | 1167 client_->SchemeClassifier()); |
1168 // Preserve the forced query '?' prefix in |match.fill_into_edit|. | 1168 // Preserve the forced query '?' prefix in |match.fill_into_edit|. |
1169 // Otherwise, user edits to a suggestion would show non-Search results. | 1169 // Otherwise, user edits to a suggestion would show non-Search results. |
1170 if (input_.type() == metrics::OmniboxInputType::FORCED_QUERY) { | 1170 if (input_.type() == metrics::OmniboxInputType::FORCED_QUERY) { |
1171 match.fill_into_edit.insert(0, base::ASCIIToUTF16("?")); | 1171 match.fill_into_edit.insert(0, base::ASCIIToUTF16("?")); |
1172 if (inline_autocomplete_offset != base::string16::npos) | 1172 if (inline_autocomplete_offset != base::string16::npos) |
1173 ++inline_autocomplete_offset; | 1173 ++inline_autocomplete_offset; |
1174 } | 1174 } |
1175 if (inline_autocomplete_offset != base::string16::npos) { | 1175 if (inline_autocomplete_offset != base::string16::npos) { |
1176 DCHECK(inline_autocomplete_offset <= match.fill_into_edit.length()); | 1176 DCHECK(inline_autocomplete_offset <= match.fill_into_edit.length()); |
1177 match.inline_autocompletion = | 1177 match.inline_autocompletion = |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1247 match->fill_into_edit.empty()) | 1247 match->fill_into_edit.empty()) |
1248 return; | 1248 return; |
1249 | 1249 |
1250 // Valid answer encountered, cache it for further queries. | 1250 // Valid answer encountered, cache it for further queries. |
1251 answers_cache_.UpdateRecentAnswers(match->fill_into_edit, match->answer_type); | 1251 answers_cache_.UpdateRecentAnswers(match->fill_into_edit, match->answer_type); |
1252 } | 1252 } |
1253 | 1253 |
1254 void SearchProvider::DoAnswersQuery(const AutocompleteInput& input) { | 1254 void SearchProvider::DoAnswersQuery(const AutocompleteInput& input) { |
1255 prefetch_data_ = answers_cache_.GetTopAnswerEntry(input.text()); | 1255 prefetch_data_ = answers_cache_.GetTopAnswerEntry(input.text()); |
1256 } | 1256 } |
OLD | NEW |