| 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" |
| 11 #include "base/i18n/break_iterator.h" |
| 11 #include "base/i18n/case_conversion.h" | 12 #include "base/i18n/case_conversion.h" |
| 12 #include "base/i18n/icu_string_conversions.h" | 13 #include "base/i18n/icu_string_conversions.h" |
| 13 #include "base/message_loop.h" | 14 #include "base/message_loop.h" |
| 14 #include "base/string16.h" | 15 #include "base/string16.h" |
| 15 #include "base/utf_string_conversions.h" | 16 #include "base/utf_string_conversions.h" |
| 16 #include "chrome/browser/autocomplete/autocomplete_classifier.h" | 17 #include "chrome/browser/autocomplete/autocomplete_classifier.h" |
| 17 #include "chrome/browser/autocomplete/autocomplete_match.h" | 18 #include "chrome/browser/autocomplete/autocomplete_match.h" |
| 18 #include "chrome/browser/autocomplete/keyword_provider.h" | 19 #include "chrome/browser/autocomplete/keyword_provider.h" |
| 19 #include "chrome/browser/history/history.h" | 20 #include "chrome/browser/history/history.h" |
| 20 #include "chrome/browser/instant/instant_controller.h" | 21 #include "chrome/browser/instant/instant_controller.h" |
| 21 #include "chrome/browser/net/url_fixer_upper.h" | 22 #include "chrome/browser/net/url_fixer_upper.h" |
| 22 #include "chrome/browser/prefs/pref_service.h" | 23 #include "chrome/browser/prefs/pref_service.h" |
| 23 #include "chrome/browser/profiles/profile.h" | 24 #include "chrome/browser/profiles/profile.h" |
| 24 #include "chrome/browser/history/in_memory_database.h" | 25 #include "chrome/browser/history/in_memory_database.h" |
| 25 #include "chrome/browser/search_engines/template_url_service.h" | 26 #include "chrome/browser/search_engines/template_url_service.h" |
| 26 #include "chrome/browser/search_engines/template_url_service_factory.h" | 27 #include "chrome/browser/search_engines/template_url_service_factory.h" |
| 27 #include "chrome/common/pref_names.h" | 28 #include "chrome/common/pref_names.h" |
| 28 #include "chrome/common/url_constants.h" | 29 #include "chrome/common/url_constants.h" |
| 29 #include "content/common/json_value_serializer.h" | 30 #include "content/common/json_value_serializer.h" |
| 30 #include "googleurl/src/url_util.h" | 31 #include "googleurl/src/url_util.h" |
| 31 #include "grit/generated_resources.h" | 32 #include "grit/generated_resources.h" |
| 32 #include "net/base/escape.h" | 33 #include "net/base/escape.h" |
| 33 #include "net/http/http_response_headers.h" | 34 #include "net/http/http_response_headers.h" |
| 34 #include "net/url_request/url_request_status.h" | 35 #include "net/url_request/url_request_status.h" |
| 35 #include "ui/base/l10n/l10n_util.h" | 36 #include "ui/base/l10n/l10n_util.h" |
| 36 | 37 |
| 37 using base::Time; | 38 using base::Time; |
| 38 using base::TimeDelta; | 39 using base::TimeDelta; |
| 39 | 40 |
| 41 namespace { |
| 42 |
| 43 bool HasMultipleWords(const string16& text) { |
| 44 base::i18n::BreakIterator i(text, base::i18n::BreakIterator::BREAK_WORD); |
| 45 bool found_word = false; |
| 46 if (i.Init()) { |
| 47 while (i.Advance()) { |
| 48 if (i.IsWord()) { |
| 49 if (found_word) |
| 50 return true; |
| 51 found_word = true; |
| 52 } |
| 53 } |
| 54 } |
| 55 return false; |
| 56 } |
| 57 |
| 58 }; |
| 59 |
| 40 // static | 60 // static |
| 41 const int SearchProvider::kDefaultProviderURLFetcherID = 1; | 61 const int SearchProvider::kDefaultProviderURLFetcherID = 1; |
| 42 // static | 62 // static |
| 43 const int SearchProvider::kKeywordProviderURLFetcherID = 2; | 63 const int SearchProvider::kKeywordProviderURLFetcherID = 2; |
| 44 | 64 |
| 45 // static | 65 // static |
| 46 bool SearchProvider::query_suggest_immediately_ = false; | 66 bool SearchProvider::query_suggest_immediately_ = false; |
| 47 | 67 |
| 48 void SearchProvider::Providers::Set(const TemplateURL* default_provider, | 68 void SearchProvider::Providers::Set(const TemplateURL* default_provider, |
| 49 const TemplateURL* keyword_provider) { | 69 const TemplateURL* keyword_provider) { |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 180 return; | 200 return; |
| 181 } | 201 } |
| 182 | 202 |
| 183 input_ = input; | 203 input_ = input; |
| 184 | 204 |
| 185 DoHistoryQuery(minimal_changes); | 205 DoHistoryQuery(minimal_changes); |
| 186 StartOrStopSuggestQuery(minimal_changes); | 206 StartOrStopSuggestQuery(minimal_changes); |
| 187 ConvertResultsToAutocompleteMatches(); | 207 ConvertResultsToAutocompleteMatches(); |
| 188 } | 208 } |
| 189 | 209 |
| 210 class SearchProvider::CompareScoredTerms { |
| 211 public: |
| 212 bool operator()(const ScoredTerm& a, const ScoredTerm& b) { |
| 213 // Sort in descending relevance order. |
| 214 return a.second > b.second; |
| 215 } |
| 216 }; |
| 217 |
| 190 void SearchProvider::Run() { | 218 void SearchProvider::Run() { |
| 191 // Start a new request with the current input. | 219 // Start a new request with the current input. |
| 192 DCHECK(!done_); | 220 DCHECK(!done_); |
| 193 suggest_results_pending_ = 0; | 221 suggest_results_pending_ = 0; |
| 194 if (providers_.valid_suggest_for_keyword_provider()) { | 222 if (providers_.valid_suggest_for_keyword_provider()) { |
| 195 suggest_results_pending_++; | 223 suggest_results_pending_++; |
| 196 keyword_fetcher_.reset( | 224 keyword_fetcher_.reset( |
| 197 CreateSuggestFetcher(kKeywordProviderURLFetcherID, | 225 CreateSuggestFetcher(kKeywordProviderURLFetcherID, |
| 198 providers_.keyword_provider(), | 226 providers_.keyword_provider(), |
| 199 keyword_input_text_)); | 227 keyword_input_text_)); |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 274 keyword_history_results_.clear(); | 302 keyword_history_results_.clear(); |
| 275 default_history_results_.clear(); | 303 default_history_results_.clear(); |
| 276 | 304 |
| 277 HistoryService* const history_service = | 305 HistoryService* const history_service = |
| 278 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); | 306 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); |
| 279 history::URLDatabase* url_db = history_service ? | 307 history::URLDatabase* url_db = history_service ? |
| 280 history_service->InMemoryDatabase() : NULL; | 308 history_service->InMemoryDatabase() : NULL; |
| 281 if (!url_db) | 309 if (!url_db) |
| 282 return; | 310 return; |
| 283 | 311 |
| 284 // Request history for both the keyword and default provider. | 312 // Request history for both the keyword and default provider. We grab many |
| 313 // more matches than we'll ultimately clamp to so that if there are several |
| 314 // recent multi-word matches who scores are lowered (see |
| 315 // AddHistoryResultsToMap()), they won't crowd out older, higher-scoring |
| 316 // matches. Note that this doesn't fix the problem entirely, but merely |
| 317 // limits it to cases with a very large number of such multi-word matches; for |
| 318 // now, this seems OK compared with the complexity of a real fix, which would |
| 319 // require multiple searches and tracking of "single- vs. multi-word" in the |
| 320 // database. |
| 321 int num_matches = kMaxMatches * 5; |
| 285 if (providers_.valid_keyword_provider()) { | 322 if (providers_.valid_keyword_provider()) { |
| 286 url_db->GetMostRecentKeywordSearchTerms( | 323 url_db->GetMostRecentKeywordSearchTerms(providers_.keyword_provider().id(), |
| 287 providers_.keyword_provider().id(), | 324 keyword_input_text_, num_matches, &keyword_history_results_); |
| 288 keyword_input_text_, | |
| 289 static_cast<int>(kMaxMatches), | |
| 290 &keyword_history_results_); | |
| 291 } | 325 } |
| 292 if (providers_.valid_default_provider()) { | 326 if (providers_.valid_default_provider()) { |
| 293 url_db->GetMostRecentKeywordSearchTerms( | 327 url_db->GetMostRecentKeywordSearchTerms(providers_.default_provider().id(), |
| 294 providers_.default_provider().id(), | 328 input_.text(), num_matches, &default_history_results_); |
| 295 input_.text(), | |
| 296 static_cast<int>(kMaxMatches), | |
| 297 &default_history_results_); | |
| 298 } | 329 } |
| 299 } | 330 } |
| 300 | 331 |
| 301 void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) { | 332 void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) { |
| 302 // Don't send any queries to the server until some time has elapsed after | 333 // Don't send any queries to the server until some time has elapsed after |
| 303 // the last keypress, to avoid flooding the server with requests we are | 334 // the last keypress, to avoid flooding the server with requests we are |
| 304 // likely to end up throwing away anyway. | 335 // likely to end up throwing away anyway. |
| 305 static const int kQueryDelayMs = 200; | 336 static const int kQueryDelayMs = 200; |
| 306 | 337 |
| 307 if (!IsQuerySuitableForSuggest()) { | 338 if (!IsQuerySuitableForSuggest()) { |
| (...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 580 matches_.push_back(NavigationToMatch(navigation_results.front(), | 611 matches_.push_back(NavigationToMatch(navigation_results.front(), |
| 581 CalculateRelevanceForNavigation(num_results, 0, is_keyword), | 612 CalculateRelevanceForNavigation(num_results, 0, is_keyword), |
| 582 is_keyword)); | 613 is_keyword)); |
| 583 } | 614 } |
| 584 } | 615 } |
| 585 | 616 |
| 586 void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, | 617 void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, |
| 587 bool is_keyword, | 618 bool is_keyword, |
| 588 int did_not_accept_suggestion, | 619 int did_not_accept_suggestion, |
| 589 MatchMap* map) { | 620 MatchMap* map) { |
| 590 int last_relevance = 0; | 621 if (results.empty()) |
| 622 return; |
| 623 |
| 624 bool base_prevent_inline_autocomplete = |
| 625 (input_.type() == AutocompleteInput::URL) || |
| 626 input_.prevent_inline_autocomplete(); |
| 627 const string16& input_text( |
| 628 is_keyword ? keyword_input_text_ : input_.text()); |
| 629 bool input_multiple_words = HasMultipleWords(input_text); |
| 630 |
| 631 ScoredTerms scored_terms; |
| 632 if (!base_prevent_inline_autocomplete && input_multiple_words) { |
| 633 // ScoreHistoryTerms() allows autocompletion of multi-word, 1-visit queries |
| 634 // if the input also has multiple words. But if we were already |
| 635 // autocompleting a multi-word, multi-visit query, and the current input is |
| 636 // still a prefix of it, then changing the autocompletion suddenly feels |
| 637 // wrong. To detect this case, first score as if only one word has been |
| 638 // typed, then check for a best result that is an autocompleted, multi-word |
| 639 // query. If we find one, then just keep that score set. |
| 640 scored_terms = ScoreHistoryTerms(results, base_prevent_inline_autocomplete, |
| 641 false, input_text, is_keyword); |
| 642 if ((scored_terms[0].second < AutocompleteResult::kLowestDefaultScore) || |
| 643 !HasMultipleWords(scored_terms[0].first)) |
| 644 scored_terms.clear(); // Didn't detect the case above, score normally. |
| 645 } |
| 646 if (scored_terms.empty()) |
| 647 scored_terms = ScoreHistoryTerms(results, base_prevent_inline_autocomplete, |
| 648 input_multiple_words, input_text, |
| 649 is_keyword); |
| 650 for (ScoredTerms::const_iterator i(scored_terms.begin()); |
| 651 i != scored_terms.end(); ++i) { |
| 652 AddMatchToMap(i->first, input_text, i->second, |
| 653 AutocompleteMatch::SEARCH_HISTORY, did_not_accept_suggestion, |
| 654 is_keyword, input_.prevent_inline_autocomplete(), map); |
| 655 } |
| 656 } |
| 657 |
| 658 SearchProvider::ScoredTerms SearchProvider::ScoreHistoryTerms( |
| 659 const HistoryResults& results, |
| 660 bool base_prevent_inline_autocomplete, |
| 661 bool input_multiple_words, |
| 662 const string16& input_text, |
| 663 bool is_keyword) { |
| 591 AutocompleteClassifier* classifier = profile_->GetAutocompleteClassifier(); | 664 AutocompleteClassifier* classifier = profile_->GetAutocompleteClassifier(); |
| 665 ScoredTerms scored_terms; |
| 592 for (HistoryResults::const_iterator i(results.begin()); i != results.end(); | 666 for (HistoryResults::const_iterator i(results.begin()); i != results.end(); |
| 593 ++i) { | 667 ++i) { |
| 594 // History returns results sorted for us. We force the relevance to decrease | 668 // Don't autocomplete multi-word queries that have only been seen once |
| 595 // so that the sort from history is honored. We should never end up with a | 669 // unless the user has typed more than one word. |
| 596 // match having a relevance greater than the previous, but they might be | 670 bool prevent_inline_autocomplete = base_prevent_inline_autocomplete || |
| 597 // equal. If we didn't force the relevance to decrease and we ended up in a | 671 (!input_multiple_words && (i->visits < 2) && HasMultipleWords(i->term)); |
| 598 // situation where the relevance was equal, then which was shown first would | 672 |
| 599 // be random. | |
| 600 // This uses >= to handle the case where 3 or more results have the same | |
| 601 // relevance. | |
| 602 bool term_looks_like_url = false; | |
| 603 // Don't autocomplete search terms that would normally be treated as URLs | 673 // Don't autocomplete search terms that would normally be treated as URLs |
| 604 // when typed. For example, if the user searched for google.com and types | 674 // when typed. For example, if the user searched for "google.com" and types |
| 605 // goog, don't autocomplete to the search term google.com. Otherwise, the | 675 // "goog", don't autocomplete to the search term "google.com". Otherwise, |
| 606 // input will look like a URL but act like a search, which is confusing. | 676 // the input will look like a URL but act like a search, which is confusing. |
| 607 // NOTE: We don't check this in the following cases: | 677 // NOTE: We don't check this in the following cases: |
| 608 // * When inline autocomplete is disabled, we won't be inline | 678 // * When inline autocomplete is disabled, we won't be inline |
| 609 // autocompleting this term, so we don't need to worry about confusion as | 679 // autocompleting this term, so we don't need to worry about confusion as |
| 610 // much. This also prevents calling Classify() again from inside the | 680 // much. This also prevents calling Classify() again from inside the |
| 611 // classifier (which will corrupt state and likely crash), since the | 681 // classifier (which will corrupt state and likely crash), since the |
| 612 // classifier always disabled inline autocomplete. | 682 // classifier always disables inline autocomplete. |
| 613 // * When the user has typed the whole term, the "what you typed" history | 683 // * When the user has typed the whole term, the "what you typed" history |
| 614 // match will outrank us for URL-like inputs anyway, so we need not do | 684 // match will outrank us for URL-like inputs anyway, so we need not do |
| 615 // anything special. | 685 // anything special. |
| 616 if (!input_.prevent_inline_autocomplete() && classifier && | 686 if (!prevent_inline_autocomplete && classifier && (i->term != input_text)) { |
| 617 i->term != input_.text()) { | |
| 618 AutocompleteMatch match; | 687 AutocompleteMatch match; |
| 619 classifier->Classify(i->term, string16(), false, false, &match, NULL); | 688 classifier->Classify(i->term, string16(), false, false, &match, NULL); |
| 620 term_looks_like_url = match.transition == PageTransition::TYPED; | 689 prevent_inline_autocomplete = match.transition == PageTransition::TYPED; |
| 621 } | 690 } |
| 622 int relevance = CalculateRelevanceForHistory(i->time, term_looks_like_url, | 691 |
| 623 is_keyword); | 692 int relevance = CalculateRelevanceForHistory(i->time, is_keyword, |
| 624 if (i != results.begin() && relevance >= last_relevance) | 693 prevent_inline_autocomplete); |
| 625 relevance = last_relevance - 1; | 694 scored_terms.push_back(std::make_pair(i->term, relevance)); |
| 626 last_relevance = relevance; | |
| 627 AddMatchToMap(i->term, | |
| 628 is_keyword ? keyword_input_text_ : input_.text(), | |
| 629 relevance, | |
| 630 AutocompleteMatch::SEARCH_HISTORY, did_not_accept_suggestion, | |
| 631 is_keyword, input_.prevent_inline_autocomplete(), | |
| 632 map); | |
| 633 } | 695 } |
| 696 |
| 697 // History returns results sorted for us. However, we may have docked some |
| 698 // results' scores, so things are no longer in order. Do a stable sort to get |
| 699 // things back in order without otherwise disturbing results with equal |
| 700 // scores, then force the scores to be unique, so that the order in which |
| 701 // they're shown is deterministic. |
| 702 std::stable_sort(scored_terms.begin(), scored_terms.end(), |
| 703 CompareScoredTerms()); |
| 704 int last_relevance = 0; |
| 705 for (ScoredTerms::iterator i(scored_terms.begin()); i != scored_terms.end(); |
| 706 ++i) { |
| 707 if ((i != scored_terms.begin()) && (i->second >= last_relevance)) |
| 708 i->second = last_relevance - 1; |
| 709 last_relevance = i->second; |
| 710 } |
| 711 |
| 712 return scored_terms; |
| 634 } | 713 } |
| 635 | 714 |
| 636 void SearchProvider::AddSuggestResultsToMap( | 715 void SearchProvider::AddSuggestResultsToMap( |
| 637 const SuggestResults& suggest_results, | 716 const SuggestResults& suggest_results, |
| 638 bool is_keyword, | 717 bool is_keyword, |
| 639 int did_not_accept_suggestion, | 718 int did_not_accept_suggestion, |
| 640 MatchMap* map) { | 719 MatchMap* map) { |
| 641 for (size_t i = 0; i < suggest_results.size(); ++i) { | 720 for (size_t i = 0; i < suggest_results.size(); ++i) { |
| 642 AddMatchToMap(suggest_results[i], | 721 AddMatchToMap(suggest_results[i], |
| 643 is_keyword ? keyword_input_text_ : input_.text(), | 722 is_keyword ? keyword_input_text_ : input_.text(), |
| (...skipping 20 matching lines...) Expand all Loading... |
| 664 | 743 |
| 665 case AutocompleteInput::URL: | 744 case AutocompleteInput::URL: |
| 666 return 850; | 745 return 850; |
| 667 | 746 |
| 668 default: | 747 default: |
| 669 NOTREACHED(); | 748 NOTREACHED(); |
| 670 return 0; | 749 return 0; |
| 671 } | 750 } |
| 672 } | 751 } |
| 673 | 752 |
| 674 int SearchProvider::CalculateRelevanceForHistory(const Time& time, | 753 int SearchProvider::CalculateRelevanceForHistory( |
| 675 bool looks_like_url, | 754 const Time& time, |
| 676 bool is_keyword) const { | 755 bool is_keyword, |
| 756 bool prevent_inline_autocomplete) const { |
| 677 // The relevance of past searches falls off over time. There are two distinct | 757 // The relevance of past searches falls off over time. There are two distinct |
| 678 // equations used. If the first equation is used (searches to the primary | 758 // equations used. If the first equation is used (searches to the primary |
| 679 // provider with a type other than URL that don't autocomplete to a url) the | 759 // provider that we want to inline autocomplete), the score starts at 1399 and |
| 680 // score starts at 1399 and falls to 1300. If the second equation is used the | 760 // falls to 1300. If the second equation is used the relevance of a search 15 |
| 681 // relevance of a search 15 minutes ago is discounted about 50 points, while | 761 // minutes ago is discounted 50 points, while the relevance of a search two |
| 682 // the relevance of a search two weeks ago is discounted about 450 points. | 762 // weeks ago is discounted 450 points. |
| 683 double elapsed_time = std::max((Time::Now() - time).InSecondsF(), 0.); | 763 double elapsed_time = std::max((Time::Now() - time).InSecondsF(), 0.); |
| 684 | 764 bool is_primary_provider = providers_.is_primary_provider(is_keyword); |
| 685 if (providers_.is_primary_provider(is_keyword) && | 765 if (is_primary_provider && !prevent_inline_autocomplete) { |
| 686 input_.type() != AutocompleteInput::URL && | |
| 687 !input_.prevent_inline_autocomplete() && !looks_like_url) { | |
| 688 // Searches with the past two days get a different curve. | 766 // Searches with the past two days get a different curve. |
| 689 const double autocomplete_time= 2 * 24 * 60 * 60; | 767 const double autocomplete_time = 2 * 24 * 60 * 60; |
| 690 if (elapsed_time < autocomplete_time) { | 768 if (elapsed_time < autocomplete_time) { |
| 691 return (is_keyword ? 1599 : 1399) - static_cast<int>(99 * | 769 return (is_keyword ? 1599 : 1399) - static_cast<int>(99 * |
| 692 std::pow(elapsed_time / autocomplete_time, 2.5)); | 770 std::pow(elapsed_time / autocomplete_time, 2.5)); |
| 693 } | 771 } |
| 694 elapsed_time -= autocomplete_time; | 772 elapsed_time -= autocomplete_time; |
| 695 } | 773 } |
| 696 | 774 |
| 697 const int score_discount = | 775 const int score_discount = |
| 698 static_cast<int>(6.5 * std::pow(elapsed_time, 0.3)); | 776 static_cast<int>(6.5 * std::pow(elapsed_time, 0.3)); |
| 699 | 777 |
| 700 // Don't let scores go below 0. Negative relevance scores are meaningful in | 778 // Don't let scores go below 0. Negative relevance scores are meaningful in |
| 701 // a different way. | 779 // a different way. |
| 702 int base_score; | 780 int base_score; |
| 703 if (!providers_.is_primary_provider(is_keyword)) | 781 if (is_primary_provider) |
| 782 base_score = (input_.type() == AutocompleteInput::URL) ? 750 : 1050; |
| 783 else |
| 704 base_score = 200; | 784 base_score = 200; |
| 705 else | |
| 706 base_score = (input_.type() == AutocompleteInput::URL) ? 750 : 1050; | |
| 707 return std::max(0, base_score - score_discount); | 785 return std::max(0, base_score - score_discount); |
| 708 } | 786 } |
| 709 | 787 |
| 710 int SearchProvider::CalculateRelevanceForSuggestion(size_t num_results, | 788 int SearchProvider::CalculateRelevanceForSuggestion(size_t num_results, |
| 711 size_t result_number, | 789 size_t result_number, |
| 712 bool is_keyword) const { | 790 bool is_keyword) const { |
| 713 DCHECK(result_number < num_results); | 791 DCHECK(result_number < num_results); |
| 714 int base_score; | 792 int base_score; |
| 715 if (!providers_.is_primary_provider(is_keyword)) | 793 if (!providers_.is_primary_provider(is_keyword)) |
| 716 base_score = 100; | 794 base_score = 100; |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 868 | 946 |
| 869 return match; | 947 return match; |
| 870 } | 948 } |
| 871 | 949 |
| 872 void SearchProvider::UpdateDone() { | 950 void SearchProvider::UpdateDone() { |
| 873 // We're done when there are no more suggest queries pending (this is set to 1 | 951 // We're done when there are no more suggest queries pending (this is set to 1 |
| 874 // when the timer is started) and we're not waiting on instant. | 952 // when the timer is started) and we're not waiting on instant. |
| 875 done_ = ((suggest_results_pending_ == 0) && | 953 done_ = ((suggest_results_pending_ == 0) && |
| 876 (instant_finalized_ || !InstantController::IsEnabled(profile_))); | 954 (instant_finalized_ || !InstantController::IsEnabled(profile_))); |
| 877 } | 955 } |
| OLD | NEW |