Part 8 of search provider refactoring.

Moves OnURLFetchComplete into shared base class. Introduces several
new methods and moves UpdateMatch() function within search_provider (no
functionality changes).

Changes zero_suggest_provider to use suggest_results_pending_ counter
instead of have_pending_requests_ boolean. In zero suggest provider the
variable just takes on values of 0 or 1.

BUG=338955
TBR=mpearson

Review URL: https://codereview.chromium.org/200493008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@257938 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc
index debfdb48..1b77f340 100644
--- a/chrome/browser/autocomplete/search_provider.cc
+++ b/chrome/browser/autocomplete/search_provider.cc
@@ -10,7 +10,6 @@
 #include "base/callback.h"
 #include "base/i18n/break_iterator.h"
 #include "base/i18n/case_conversion.h"
-#include "base/i18n/icu_string_conversions.h"
 #include "base/json/json_string_value_serializer.h"
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram.h"
@@ -43,7 +42,6 @@
 #include "net/base/load_flags.h"
 #include "net/base/net_util.h"
 #include "net/http/http_request_headers.h"
-#include "net/http/http_response_headers.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_request_status.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -130,8 +128,7 @@
 SearchProvider::SearchProvider(AutocompleteProviderListener* listener,
                                Profile* profile)
     : BaseSearchProvider(listener, profile, AutocompleteProvider::TYPE_SEARCH),
-      providers_(TemplateURLServiceFactory::GetForProfile(profile)),
-      suggest_results_pending_(0) {
+      providers_(TemplateURLServiceFactory::GetForProfile(profile)) {
 }
 
 // static
@@ -310,72 +307,6 @@
   UpdateMatches();
 }
 
-void SearchProvider::OnURLFetchComplete(const net::URLFetcher* source) {
-  DCHECK(!done_);
-  suggest_results_pending_--;
-  LogOmniboxSuggestRequest(REPLY_RECEIVED);
-  DCHECK_GE(suggest_results_pending_, 0);  // Should never go negative.
-
-  const bool is_keyword = (source == keyword_fetcher_.get());
-  // Ensure the request succeeded and that the provider used is still available.
-  // A verbatim match cannot be generated without this provider, causing errors.
-  const bool request_succeeded =
-      source->GetStatus().is_success() && (source->GetResponseCode() == 200) &&
-      (is_keyword ?
-          providers_.GetKeywordProviderURL() :
-          providers_.GetDefaultProviderURL());
-
-  // Record response time for suggest requests sent to Google.  We care
-  // only about the common case: the Google default provider used in
-  // non-keyword mode.
-  const TemplateURL* default_url = providers_.GetDefaultProviderURL();
-  if (!is_keyword && default_url &&
-      (TemplateURLPrepopulateData::GetEngineType(*default_url) ==
-       SEARCH_ENGINE_GOOGLE)) {
-    const base::TimeDelta elapsed_time =
-        base::TimeTicks::Now() - time_suggest_request_sent_;
-    if (request_succeeded) {
-      UMA_HISTOGRAM_TIMES("Omnibox.SuggestRequest.Success.GoogleResponseTime",
-                          elapsed_time);
-    } else {
-      UMA_HISTOGRAM_TIMES("Omnibox.SuggestRequest.Failure.GoogleResponseTime",
-                          elapsed_time);
-    }
-  }
-
-  bool results_updated = false;
-  if (request_succeeded) {
-    const net::HttpResponseHeaders* const response_headers =
-        source->GetResponseHeaders();
-    std::string json_data;
-    source->GetResponseAsString(&json_data);
-
-    // JSON is supposed to be UTF-8, but some suggest service providers send
-    // JSON files in non-UTF-8 encodings.  The actual encoding is usually
-    // specified in the Content-Type header field.
-    if (response_headers) {
-      std::string charset;
-      if (response_headers->GetCharset(&charset)) {
-        base::string16 data_16;
-        // TODO(jungshik): Switch to CodePageToUTF8 after it's added.
-        if (base::CodepageToUTF16(json_data, charset.c_str(),
-                                  base::OnStringConversionError::FAIL,
-                                  &data_16))
-          json_data = base::UTF16ToUTF8(data_16);
-      }
-    }
-
-    scoped_ptr<base::Value> data(DeserializeJsonData(json_data));
-    results_updated = data.get() && ParseSuggestResults(
-        *data.get(), is_keyword,
-        is_keyword ? &keyword_results_ : &default_results_);
-  }
-
-  UpdateMatches();
-  if (done_ || results_updated)
-    listener_->OnProviderUpdate(results_updated);
-}
-
 void SearchProvider::SortResults(bool is_keyword,
                                  const base::ListValue* relevances,
                                  Results* results) {
@@ -404,16 +335,19 @@
                    comparator);
 }
 
-const TemplateURL* SearchProvider::GetTemplateURL(
-    const SuggestResult& result) const {
-  return result.from_keyword_provider() ? providers_.GetKeywordProviderURL()
-                                        : providers_.GetDefaultProviderURL();
+const TemplateURL* SearchProvider::GetTemplateURL(bool is_keyword) const {
+  return is_keyword ? providers_.GetKeywordProviderURL()
+                    : providers_.GetDefaultProviderURL();
 }
 
 const AutocompleteInput SearchProvider::GetInput(bool is_keyword) const {
   return is_keyword ? keyword_input_ : input_;
 }
 
+BaseSearchProvider::Results* SearchProvider::GetResultsToFill(bool is_keyword) {
+  return is_keyword ? &keyword_results_ : &default_results_;
+}
+
 bool SearchProvider::ShouldAppendExtraParams(
     const SuggestResult& result) const {
   return !result.from_keyword_provider() ||
@@ -451,6 +385,137 @@
   }
 }
 
+void SearchProvider::LogFetchComplete(bool success, bool is_keyword) {
+  LogOmniboxSuggestRequest(REPLY_RECEIVED);
+  // Record response time for suggest requests sent to Google.  We care
+  // only about the common case: the Google default provider used in
+  // non-keyword mode.
+  const TemplateURL* default_url = providers_.GetDefaultProviderURL();
+  if (!is_keyword && default_url &&
+      (TemplateURLPrepopulateData::GetEngineType(*default_url) ==
+       SEARCH_ENGINE_GOOGLE)) {
+    const base::TimeDelta elapsed_time =
+        base::TimeTicks::Now() - time_suggest_request_sent_;
+    if (success) {
+      UMA_HISTOGRAM_TIMES("Omnibox.SuggestRequest.Success.GoogleResponseTime",
+                          elapsed_time);
+    } else {
+      UMA_HISTOGRAM_TIMES("Omnibox.SuggestRequest.Failure.GoogleResponseTime",
+                          elapsed_time);
+    }
+  }
+}
+
+bool SearchProvider::IsKeywordFetcher(const net::URLFetcher* fetcher) const {
+  return fetcher == keyword_fetcher_.get();
+}
+
+void SearchProvider::UpdateMatches() {
+  base::TimeTicks update_matches_start_time(base::TimeTicks::Now());
+  ConvertResultsToAutocompleteMatches();
+
+  // Check constraints that may be violated by suggested relevances.
+  if (!matches_.empty() &&
+      (default_results_.HasServerProvidedScores() ||
+       keyword_results_.HasServerProvidedScores())) {
+    // These blocks attempt to repair undesirable behavior by suggested
+    // relevances with minimal impact, preserving other suggested relevances.
+
+    // True if the omnibox will reorder matches as necessary to make the first
+    // one something that is allowed to be the default match.
+    const bool omnibox_will_reorder_for_legal_default_match =
+        OmniboxFieldTrial::ReorderForLegalDefaultMatch(
+            input_.current_page_classification());
+    if (IsTopMatchNavigationInKeywordMode(
+        omnibox_will_reorder_for_legal_default_match)) {
+      // Correct the suggested relevance scores if the top match is a
+      // navigation in keyword mode, since inlining a navigation match
+      // would break the user out of keyword mode.  This will only be
+      // triggered in regular (non-reorder) mode; in reorder mode,
+      // navigation matches are marked as not allowed to be the default
+      // match and hence IsTopMatchNavigation() will always return false.
+      DCHECK(!omnibox_will_reorder_for_legal_default_match);
+      DemoteKeywordNavigationMatchesPastTopQuery();
+      ConvertResultsToAutocompleteMatches();
+      DCHECK(!IsTopMatchNavigationInKeywordMode(
+          omnibox_will_reorder_for_legal_default_match));
+    }
+    if (!HasKeywordDefaultMatchInKeywordMode()) {
+      // In keyword mode, disregard the keyword verbatim suggested relevance
+      // if necessary so there at least one keyword match that's allowed to
+      // be the default match.
+      keyword_results_.verbatim_relevance = -1;
+      ConvertResultsToAutocompleteMatches();
+    }
+    if (IsTopMatchScoreTooLow(omnibox_will_reorder_for_legal_default_match)) {
+      // Disregard the suggested verbatim relevance if the top score is below
+      // the usual verbatim value. For example, a BarProvider may rely on
+      // SearchProvider's verbatim or inlineable matches for input "foo" (all
+      // allowed to be default match) to always outrank its own lowly-ranked
+      // "bar" matches that shouldn't be the default match.
+      default_results_.verbatim_relevance = -1;
+      keyword_results_.verbatim_relevance = -1;
+      ConvertResultsToAutocompleteMatches();
+    }
+    if (IsTopMatchSearchWithURLInput(
+        omnibox_will_reorder_for_legal_default_match)) {
+      // Disregard the suggested search and verbatim relevances if the input
+      // type is URL and the top match is a highly-ranked search suggestion.
+      // For example, prevent a search for "foo.com" from outranking another
+      // provider's navigation for "foo.com" or "foo.com/url_from_history".
+      ApplyCalculatedSuggestRelevance(&keyword_results_.suggest_results);
+      ApplyCalculatedSuggestRelevance(&default_results_.suggest_results);
+      default_results_.verbatim_relevance = -1;
+      keyword_results_.verbatim_relevance = -1;
+      ConvertResultsToAutocompleteMatches();
+    }
+    if (!HasValidDefaultMatch(omnibox_will_reorder_for_legal_default_match)) {
+      // If the omnibox is not going to reorder results to put a legal default
+      // match at the top, then this provider needs to guarantee that its top
+      // scoring result is a legal default match (i.e., it's either a verbatim
+      // match or inlinable).  For example, input "foo" should not invoke a
+      // search for "bar", which would happen if the "bar" search match
+      // outranked all other matches.  On the other hand, if the omnibox will
+      // reorder matches as necessary to put a legal default match at the top,
+      // all we need to guarantee is that SearchProvider returns a legal
+      // default match.  (The omnibox always needs at least one legal default
+      // match, and it relies on SearchProvider to always return one.)
+      ApplyCalculatedRelevance();
+      ConvertResultsToAutocompleteMatches();
+    }
+    DCHECK(!IsTopMatchNavigationInKeywordMode(
+        omnibox_will_reorder_for_legal_default_match));
+    DCHECK(HasKeywordDefaultMatchInKeywordMode());
+    DCHECK(!IsTopMatchScoreTooLow(
+        omnibox_will_reorder_for_legal_default_match));
+    DCHECK(!IsTopMatchSearchWithURLInput(
+        omnibox_will_reorder_for_legal_default_match));
+    DCHECK(HasValidDefaultMatch(omnibox_will_reorder_for_legal_default_match));
+  }
+  UMA_HISTOGRAM_CUSTOM_COUNTS(
+      "Omnibox.SearchProviderMatches", matches_.size(), 1, 6, 7);
+
+  const TemplateURL* keyword_url = providers_.GetKeywordProviderURL();
+  if ((keyword_url != NULL) && HasKeywordDefaultMatchInKeywordMode()) {
+    // If there is a keyword match that is allowed to be the default match,
+    // then prohibit default provider matches from being the default match lest
+    // such matches cause the user to break out of keyword mode.
+    for (ACMatches::iterator it = matches_.begin(); it != matches_.end();
+         ++it) {
+      if (it->keyword != keyword_url->keyword())
+        it->allowed_to_be_default_match = false;
+    }
+  }
+
+  base::TimeTicks update_starred_start_time(base::TimeTicks::Now());
+  UpdateStarredStateOfMatches();
+  UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.UpdateStarredTime",
+                      base::TimeTicks::Now() - update_starred_start_time);
+  UpdateDone();
+  UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.UpdateMatchesTime",
+                      base::TimeTicks::Now() - update_matches_start_time);
+}
+
 void SearchProvider::Run() {
   // Start a new request with the current input.
   suggest_results_pending_ = 0;
@@ -928,111 +993,6 @@
   return false;
 }
 
-void SearchProvider::UpdateMatches() {
-  base::TimeTicks update_matches_start_time(base::TimeTicks::Now());
-  ConvertResultsToAutocompleteMatches();
-
-  // Check constraints that may be violated by suggested relevances.
-  if (!matches_.empty() &&
-      (default_results_.HasServerProvidedScores() ||
-       keyword_results_.HasServerProvidedScores())) {
-    // These blocks attempt to repair undesirable behavior by suggested
-    // relevances with minimal impact, preserving other suggested relevances.
-
-    // True if the omnibox will reorder matches as necessary to make the first
-    // one something that is allowed to be the default match.
-    const bool omnibox_will_reorder_for_legal_default_match =
-        OmniboxFieldTrial::ReorderForLegalDefaultMatch(
-            input_.current_page_classification());
-    if (IsTopMatchNavigationInKeywordMode(
-        omnibox_will_reorder_for_legal_default_match)) {
-      // Correct the suggested relevance scores if the top match is a
-      // navigation in keyword mode, since inlining a navigation match
-      // would break the user out of keyword mode.  This will only be
-      // triggered in regular (non-reorder) mode; in reorder mode,
-      // navigation matches are marked as not allowed to be the default
-      // match and hence IsTopMatchNavigation() will always return false.
-      DCHECK(!omnibox_will_reorder_for_legal_default_match);
-      DemoteKeywordNavigationMatchesPastTopQuery();
-      ConvertResultsToAutocompleteMatches();
-      DCHECK(!IsTopMatchNavigationInKeywordMode(
-          omnibox_will_reorder_for_legal_default_match));
-    }
-    if (!HasKeywordDefaultMatchInKeywordMode()) {
-      // In keyword mode, disregard the keyword verbatim suggested relevance
-      // if necessary so there at least one keyword match that's allowed to
-      // be the default match.
-      keyword_results_.verbatim_relevance = -1;
-      ConvertResultsToAutocompleteMatches();
-    }
-    if (IsTopMatchScoreTooLow(omnibox_will_reorder_for_legal_default_match)) {
-      // Disregard the suggested verbatim relevance if the top score is below
-      // the usual verbatim value. For example, a BarProvider may rely on
-      // SearchProvider's verbatim or inlineable matches for input "foo" (all
-      // allowed to be default match) to always outrank its own lowly-ranked
-      // "bar" matches that shouldn't be the default match.
-      default_results_.verbatim_relevance = -1;
-      keyword_results_.verbatim_relevance = -1;
-      ConvertResultsToAutocompleteMatches();
-    }
-    if (IsTopMatchSearchWithURLInput(
-        omnibox_will_reorder_for_legal_default_match)) {
-      // Disregard the suggested search and verbatim relevances if the input
-      // type is URL and the top match is a highly-ranked search suggestion.
-      // For example, prevent a search for "foo.com" from outranking another
-      // provider's navigation for "foo.com" or "foo.com/url_from_history".
-      ApplyCalculatedSuggestRelevance(&keyword_results_.suggest_results);
-      ApplyCalculatedSuggestRelevance(&default_results_.suggest_results);
-      default_results_.verbatim_relevance = -1;
-      keyword_results_.verbatim_relevance = -1;
-      ConvertResultsToAutocompleteMatches();
-    }
-    if (!HasValidDefaultMatch(omnibox_will_reorder_for_legal_default_match)) {
-      // If the omnibox is not going to reorder results to put a legal default
-      // match at the top, then this provider needs to guarantee that its top
-      // scoring result is a legal default match (i.e., it's either a verbatim
-      // match or inlinable).  For example, input "foo" should not invoke a
-      // search for "bar", which would happen if the "bar" search match
-      // outranked all other matches.  On the other hand, if the omnibox will
-      // reorder matches as necessary to put a legal default match at the top,
-      // all we need to guarantee is that SearchProvider returns a legal
-      // default match.  (The omnibox always needs at least one legal default
-      // match, and it relies on SearchProvider to always return one.)
-      ApplyCalculatedRelevance();
-      ConvertResultsToAutocompleteMatches();
-    }
-    DCHECK(!IsTopMatchNavigationInKeywordMode(
-        omnibox_will_reorder_for_legal_default_match));
-    DCHECK(HasKeywordDefaultMatchInKeywordMode());
-    DCHECK(!IsTopMatchScoreTooLow(
-        omnibox_will_reorder_for_legal_default_match));
-    DCHECK(!IsTopMatchSearchWithURLInput(
-        omnibox_will_reorder_for_legal_default_match));
-    DCHECK(HasValidDefaultMatch(omnibox_will_reorder_for_legal_default_match));
-  }
-  UMA_HISTOGRAM_CUSTOM_COUNTS(
-      "Omnibox.SearchProviderMatches", matches_.size(), 1, 6, 7);
-
-  const TemplateURL* keyword_url = providers_.GetKeywordProviderURL();
-  if ((keyword_url != NULL) && HasKeywordDefaultMatchInKeywordMode()) {
-    // If there is a keyword match that is allowed to be the default match,
-    // then prohibit default provider matches from being the default match lest
-    // such matches cause the user to break out of keyword mode.
-    for (ACMatches::iterator it = matches_.begin(); it != matches_.end();
-         ++it) {
-      if (it->keyword != keyword_url->keyword())
-        it->allowed_to_be_default_match = false;
-    }
-  }
-
-  base::TimeTicks update_starred_start_time(base::TimeTicks::Now());
-  UpdateStarredStateOfMatches();
-  UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.UpdateStarredTime",
-                      base::TimeTicks::Now() - update_starred_start_time);
-  UpdateDone();
-  UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.UpdateMatchesTime",
-                      base::TimeTicks::Now() - update_matches_start_time);
-}
 
 void SearchProvider::AddNavigationResultsToMatches(
     const NavigationResults& navigation_results,