Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / autocomplete / search_provider_unittest.cc
index 7df13f4..7f096da 100644 (file)
 #include "build/build_config.h"
 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
 #include "chrome/browser/autocomplete/autocomplete_controller.h"
-#include "chrome/browser/autocomplete/autocomplete_input.h"
-#include "chrome/browser/autocomplete/autocomplete_match.h"
-#include "chrome/browser/autocomplete/autocomplete_provider.h"
-#include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
+#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
 #include "chrome/browser/autocomplete/history_url_provider.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/omnibox/omnibox_field_trial.h"
-#include "chrome/browser/search/search.h"
-#include "chrome/browser/search_engines/template_url.h"
-#include "chrome/browser/search_engines/template_url_service.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/metrics/variations/variations_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/google/core/browser/google_switches.h"
+#include "components/metrics/proto/omnibox_event.pb.h"
+#include "components/omnibox/autocomplete_input.h"
+#include "components/omnibox/autocomplete_match.h"
+#include "components/omnibox/autocomplete_provider.h"
+#include "components/omnibox/autocomplete_provider_listener.h"
+#include "components/omnibox/omnibox_field_trial.h"
+#include "components/omnibox/omnibox_switches.h"
+#include "components/search_engines/search_engine_type.h"
+#include "components/search_engines/search_engines_switches.h"
+#include "components/search_engines/search_terms_data.h"
+#include "components/search_engines/template_url.h"
+#include "components/search_engines/template_url_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/sync_driver/pref_names.h"
 #include "components/variations/entropy_provider.h"
+#include "components/variations/variations_associated_data.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_request_status.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using base::ASCIIToUTF16;
+
+namespace {
+
+// Returns the first match in |matches| with |allowed_to_be_default_match|
+// set to true.
+ACMatches::const_iterator FindDefaultMatch(const ACMatches& matches) {
+  ACMatches::const_iterator it = matches.begin();
+  while ((it != matches.end()) && !it->allowed_to_be_default_match)
+    ++it;
+  return it;
+}
+
+class SuggestionDeletionHandler;
+class SearchProviderForTest : public SearchProvider {
+ public:
+  SearchProviderForTest(AutocompleteProviderListener* listener,
+                        TemplateURLService* template_url_service,
+                        Profile* profile);
+  bool is_success() { return is_success_; };
+
+ protected:
+  virtual ~SearchProviderForTest();
+
+ private:
+  virtual void RecordDeletionResult(bool success) OVERRIDE;
+  bool is_success_;
+  DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest);
+};
+
+SearchProviderForTest::SearchProviderForTest(
+    AutocompleteProviderListener* listener,
+    TemplateURLService* template_url_service,
+    Profile* profile)
+    : SearchProvider(listener, template_url_service, profile),
+      is_success_(false) {
+}
+
+SearchProviderForTest::~SearchProviderForTest() {
+}
+
+void SearchProviderForTest::RecordDeletionResult(bool success) {
+  is_success_ = success;
+}
+
+} // namespace
+
 // SearchProviderTest ---------------------------------------------------------
 
 // The following environment is configured for these tests:
@@ -56,23 +114,27 @@ class SearchProviderTest : public testing::Test,
                            public AutocompleteProviderListener {
  public:
   struct ResultInfo {
-    ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES) {
+    ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES),
+                   allowed_to_be_default_match(false) {
     }
     ResultInfo(GURL gurl,
                AutocompleteMatch::Type result_type,
-               string16 fill_into_edit)
+               bool allowed_to_be_default_match,
+               base::string16 fill_into_edit)
       : gurl(gurl),
         result_type(result_type),
+        allowed_to_be_default_match(allowed_to_be_default_match),
         fill_into_edit(fill_into_edit) {
     }
 
     const GURL gurl;
     const AutocompleteMatch::Type result_type;
-    const string16 fill_into_edit;
+    const bool allowed_to_be_default_match;
+    const base::string16 fill_into_edit;
   };
 
   struct TestData {
-    const string16 input;
+    const base::string16 input;
     const size_t num_results;
     const ResultInfo output[3];
   };
@@ -101,11 +163,11 @@ class SearchProviderTest : public testing::Test,
 
   // Adds a search for |term|, using the engine |t_url| to the history, and
   // returns the URL for that search.
-  GURL AddSearchToHistory(TemplateURL* t_url, string16 term, int visit_count);
+  GURL AddSearchToHistory(TemplateURL* t_url, base::string16 term, int visit_count);
 
   // Looks for a match in |provider_| with |contents| equal to |contents|.
   // Sets |match| to it if found.  Returns whether |match| was set.
-  bool FindMatchWithContents(const string16& contents,
+  bool FindMatchWithContents(const base::string16& contents,
                              AutocompleteMatch* match);
 
   // Looks for a match in |provider_| with destination |url|.  Sets |match| to
@@ -121,13 +183,13 @@ class SearchProviderTest : public testing::Test,
   void RunTillProviderDone();
 
   // Invokes Start on provider_, then runs all pending tasks.
-  void QueryForInput(const string16& text,
+  void QueryForInput(const base::string16& text,
                      bool prevent_inline_autocomplete,
                      bool prefer_keyword);
 
   // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is
   // non-NULL, sets it to the "what you typed" entry for |text|.
-  void QueryForInputAndSetWYTMatch(const string16& text,
+  void QueryForInputAndSetWYTMatch(const base::string16& text,
                                    AutocompleteMatch* wyt_match);
 
   // Notifies the URLFetcher for the suggest query corresponding to the default
@@ -135,16 +197,29 @@ class SearchProviderTest : public testing::Test,
   // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
   void FinishDefaultSuggestQuery();
 
+  // Runs SearchProvider on |input|, for which the suggest server replies
+  // with |json|, and expects that the resulting matches' contents equals
+  // that in |matches|.  An empty entry in |matches| means no match should
+  // be returned in that position.  Reports any errors with a message that
+  // includes |error_description|.
+  void ForcedQueryTestHelper(const std::string& input,
+                             const std::string& json,
+                             const std::string matches[3],
+                             const std::string& error_description);
+
   void ResetFieldTrialList();
 
+  // Create a field trial, with ZeroSuggest activation based on |enabled|.
+  base::FieldTrial* CreateZeroSuggestFieldTrial(bool enabled);
+
   void ClearAllResults();
 
   // See description above class for details of these fields.
   TemplateURL* default_t_url_;
-  const string16 term1_;
+  const base::string16 term1_;
   GURL term1_url_;
   TemplateURL* keyword_t_url_;
-  const string16 keyword_term_;
+  const base::string16 keyword_term_;
   GURL keyword_url_;
 
   content::TestBrowserThreadBundle thread_bundle_;
@@ -156,7 +231,7 @@ class SearchProviderTest : public testing::Test,
   TestingProfile profile_;
 
   // The provider.
-  scoped_refptr<SearchProvider> provider_;
+  scoped_refptr<SearchProviderForTest> provider_;
 
   // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
   base::RunLoop* run_loop_;
@@ -188,9 +263,9 @@ void SearchProviderTest::SetUp() {
   data.suggestions_url = "http://defaultturl2/{searchTerms}";
   data.instant_url = "http://does/not/exist?strk=1";
   data.search_terms_replacement_key = "strk";
-  default_t_url_ = new TemplateURL(&profile_, data);
+  default_t_url_ = new TemplateURL(data);
   turl_model->Add(default_t_url_);
-  turl_model->SetDefaultSearchProvider(default_t_url_);
+  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
   TemplateURLID default_provider_id = default_t_url_->id();
   ASSERT_NE(0, default_provider_id);
 
@@ -202,7 +277,7 @@ void SearchProviderTest::SetUp() {
   data.SetKeyword(ASCIIToUTF16("k"));
   data.SetURL("http://keyword/{searchTerms}");
   data.suggestions_url = "http://suggest_keyword/{searchTerms}";
-  keyword_t_url_ = new TemplateURL(&profile_, data);
+  keyword_t_url_ = new TemplateURL(data);
   turl_model->Add(keyword_t_url_);
   ASSERT_NE(0, keyword_t_url_->id());
 
@@ -214,7 +289,7 @@ void SearchProviderTest::SetUp() {
   // requests to ensure the InMemoryDatabase is the state we expect it.
   profile_.BlockUntilHistoryProcessesPendingRequests();
 
-  provider_ = new SearchProvider(this, &profile_);
+  provider_ = new SearchProviderForTest(this, turl_model, &profile_);
   provider_->kMinimumTimeBetweenSuggestQueriesMs = 0;
 }
 
@@ -230,13 +305,16 @@ void SearchProviderTest::RunTest(TestData* cases,
                                  bool prefer_keyword) {
   ACMatches matches;
   for (int i = 0; i < num_cases; ++i) {
-    AutocompleteInput input(cases[i].input, string16::npos, string16(), GURL(),
-                            AutocompleteInput::INVALID_SPEC, false,
-                            prefer_keyword, true,
-                            AutocompleteInput::ALL_MATCHES);
+    AutocompleteInput input(cases[i].input, base::string16::npos,
+                            base::string16(), GURL(),
+                            metrics::OmniboxEventProto::INVALID_SPEC, false,
+                            prefer_keyword, true, true,
+                            ChromeAutocompleteSchemeClassifier(&profile_));
     provider_->Start(input, false);
     matches = provider_->matches();
-    string16 diagnostic_details = ASCIIToUTF16("Input was: ") + cases[i].input +
+    base::string16 diagnostic_details =
+        ASCIIToUTF16("Input was: ") +
+        cases[i].input +
         ASCIIToUTF16("; prefer_keyword was: ") +
         (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false"));
     EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details;
@@ -247,11 +325,9 @@ void SearchProviderTest::RunTest(TestData* cases,
         EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) <<
             diagnostic_details;
         EXPECT_EQ(cases[i].output[j].fill_into_edit,
-                  matches[j].fill_into_edit) <<
-            diagnostic_details;
-        // All callers that use this helper function at the moment produce
-        // matches that are always allowed to be the default match.
-        EXPECT_TRUE(matches[j].allowed_to_be_default_match);
+                  matches[j].fill_into_edit) << diagnostic_details;
+        EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
+                  matches[j].allowed_to_be_default_match) << diagnostic_details;
       }
     }
   }
@@ -273,14 +349,14 @@ void SearchProviderTest::RunTillProviderDone() {
   run_loop.Run();
 }
 
-void SearchProviderTest::QueryForInput(const string16& text,
+void SearchProviderTest::QueryForInput(const base::string16& text,
                                        bool prevent_inline_autocomplete,
                                        bool prefer_keyword) {
   // Start a query.
-  AutocompleteInput input(text, string16::npos, string16(), GURL(),
-                          AutocompleteInput::INVALID_SPEC,
+  AutocompleteInput input(text, base::string16::npos, base::string16(), GURL(),
+                          metrics::OmniboxEventProto::INVALID_SPEC,
                           prevent_inline_autocomplete, prefer_keyword, true,
-                          AutocompleteInput::ALL_MATCHES);
+                          true, ChromeAutocompleteSchemeClassifier(&profile_));
   provider_->Start(input, false);
 
   // RunUntilIdle so that the task scheduled by SearchProvider to create the
@@ -289,7 +365,7 @@ void SearchProviderTest::QueryForInput(const string16& text,
 }
 
 void SearchProviderTest::QueryForInputAndSetWYTMatch(
-    const string16& text,
+    const base::string16& text,
     AutocompleteMatch* wyt_match) {
   QueryForInput(text, false, false);
   profile_.BlockUntilHistoryProcessesPendingRequests();
@@ -297,30 +373,35 @@ void SearchProviderTest::QueryForInputAndSetWYTMatch(
   if (!wyt_match)
     return;
   ASSERT_GE(provider_->matches().size(), 1u);
-  EXPECT_TRUE(FindMatchWithDestination(GURL(
-      default_t_url_->url_ref().ReplaceSearchTerms(
-          TemplateURLRef::SearchTermsArgs(text))),
+  EXPECT_TRUE(FindMatchWithDestination(
+      GURL(default_t_url_->url_ref().ReplaceSearchTerms(
+          TemplateURLRef::SearchTermsArgs(base::CollapseWhitespace(
+              text, false)),
+          TemplateURLServiceFactory::GetForProfile(
+              &profile_)->search_terms_data())),
       wyt_match));
 }
 
 GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url,
-                                            string16 term,
+                                            base::string16 term,
                                             int visit_count) {
   HistoryService* history =
       HistoryServiceFactory::GetForProfile(&profile_,
                                            Profile::EXPLICIT_ACCESS);
   GURL search(t_url->url_ref().ReplaceSearchTerms(
-      TemplateURLRef::SearchTermsArgs(term)));
+      TemplateURLRef::SearchTermsArgs(term),
+      TemplateURLServiceFactory::GetForProfile(
+          &profile_)->search_terms_data()));
   static base::Time last_added_time;
   last_added_time = std::max(base::Time::Now(),
       last_added_time + base::TimeDelta::FromMicroseconds(1));
-  history->AddPageWithDetails(search, string16(), visit_count, visit_count,
+  history->AddPageWithDetails(search, base::string16(), visit_count, visit_count,
       last_added_time, false, history::SOURCE_BROWSED);
   history->SetKeywordSearchTermsForURL(search, t_url->id(), term);
   return search;
 }
 
-bool SearchProviderTest::FindMatchWithContents(const string16& contents,
+bool SearchProviderTest::FindMatchWithContents(const base::string16& contents,
                                                AutocompleteMatch* match) {
   for (ACMatches::const_iterator i = provider_->matches().begin();
        i != provider_->matches().end(); ++i) {
@@ -355,18 +436,58 @@ void SearchProviderTest::FinishDefaultSuggestQuery() {
   default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
 }
 
+void SearchProviderTest::ForcedQueryTestHelper(
+    const std::string& input,
+    const std::string& json,
+    const std::string expected_matches[3],
+    const std::string& error_description) {
+  QueryForInput(ASCIIToUTF16(input), false, false);
+  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
+      SearchProvider::kDefaultProviderURLFetcherID);
+  ASSERT_TRUE(fetcher);
+  fetcher->set_response_code(200);
+  fetcher->SetResponseString(json);
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  RunTillProviderDone();
+
+  const ACMatches& matches = provider_->matches();
+  ASSERT_LE(matches.size(), 3u);
+  size_t i = 0;
+  // Ensure that the returned matches equal the expectations.
+  for (; i < matches.size(); ++i) {
+    EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) <<
+        error_description;
+  }
+  // Ensure that no expected matches are missing.
+  for (; i < 3u; ++i) {
+    EXPECT_EQ(std::string(), expected_matches[i]) <<
+        "Case #" << i << ": " << error_description;
+  }
+}
+
 void SearchProviderTest::ResetFieldTrialList() {
   // Destroy the existing FieldTrialList before creating a new one to avoid
   // a DCHECK.
   field_trial_list_.reset();
   field_trial_list_.reset(new base::FieldTrialList(
       new metrics::SHA1EntropyProvider("foo")));
-  chrome_variations::testing::ClearAllVariationParams();
+  variations::testing::ClearAllVariationParams();
   base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
       "AutocompleteDynamicTrial_0", "DefaultGroup");
   trial->group();
 }
 
+base::FieldTrial* SearchProviderTest::CreateZeroSuggestFieldTrial(
+    bool enabled) {
+  std::map<std::string, std::string> params;
+  params[std::string(OmniboxFieldTrial::kZeroSuggestRule)] = enabled ?
+      "true" : "false";
+  variations::AssociateVariationParams(
+      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params);
+  return base::FieldTrialList::CreateFieldTrial(
+      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
+}
+
 void SearchProviderTest::ClearAllResults() {
   provider_->ClearAllResults();
 }
@@ -376,7 +497,7 @@ void SearchProviderTest::ClearAllResults() {
 // Make sure we query history for the default provider and a URLFetcher is
 // created for the default provider suggest results.
 TEST_F(SearchProviderTest, QueryDefaultProvider) {
-  string16 term = term1_.substr(0, term1_.length() - 1);
+  base::string16 term = term1_.substr(0, term1_.length() - 1);
   QueryForInput(term, false, false);
 
   // Make sure the default providers suggest service was queried.
@@ -386,7 +507,9 @@ TEST_F(SearchProviderTest, QueryDefaultProvider) {
 
   // And the URL matches what we expected.
   GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
-      TemplateURLRef::SearchTermsArgs(term)));
+      TemplateURLRef::SearchTermsArgs(term),
+      TemplateURLServiceFactory::GetForProfile(
+          &profile_)->search_terms_data()));
   ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url);
 
   // Tell the SearchProvider the suggest query is done.
@@ -407,7 +530,10 @@ TEST_F(SearchProviderTest, QueryDefaultProvider) {
   AutocompleteMatch wyt_match;
   EXPECT_TRUE(FindMatchWithDestination(
       GURL(default_t_url_->url_ref().ReplaceSearchTerms(
-          TemplateURLRef::SearchTermsArgs(term))), &wyt_match));
+          TemplateURLRef::SearchTermsArgs(term),
+          TemplateURLServiceFactory::GetForProfile(
+              &profile_)->search_terms_data())),
+      &wyt_match));
   EXPECT_TRUE(wyt_match.description.empty());
 
   // The match for term1 should be more relevant than the what you typed match.
@@ -419,7 +545,7 @@ TEST_F(SearchProviderTest, QueryDefaultProvider) {
 }
 
 TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
-  string16 term = term1_.substr(0, term1_.length() - 1);
+  base::string16 term = term1_.substr(0, term1_.length() - 1);
   QueryForInput(term, true, false);
 
   ASSERT_FALSE(provider_->matches().empty());
@@ -431,7 +557,7 @@ TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
 // Issues a query that matches the registered keyword and makes sure history
 // is queried as well as URLFetchers getting created.
 TEST_F(SearchProviderTest, QueryKeywordProvider) {
-  string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
+  base::string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
   QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term,
                 false,
                 false);
@@ -453,7 +579,9 @@ TEST_F(SearchProviderTest, QueryKeywordProvider) {
 
   // And the URL matches what we expected.
   GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms(
-      TemplateURLRef::SearchTermsArgs(term)));
+      TemplateURLRef::SearchTermsArgs(term),
+      TemplateURLServiceFactory::GetForProfile(
+          &profile_)->search_terms_data()));
   ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url);
 
   // Tell the SearchProvider the keyword suggest query is done.
@@ -473,7 +601,7 @@ TEST_F(SearchProviderTest, QueryKeywordProvider) {
   EXPECT_FALSE(match.keyword.empty());
 
   // The fill into edit should contain the keyword.
-  EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_,
+  EXPECT_EQ(keyword_t_url_->keyword() + base::char16(' ') + keyword_term_,
             match.fill_into_edit);
 }
 
@@ -537,7 +665,7 @@ TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
 
   // Add the term as a url.
   HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)->
-      AddPageWithDetails(GURL("http://docs.google.com"), string16(), 1, 1,
+      AddPageWithDetails(GURL("http://docs.google.com"), base::string16(), 1, 1,
                          base::Time::Now(), false, history::SOURCE_BROWSED);
   profile_.BlockUntilHistoryProcessesPendingRequests();
 
@@ -556,6 +684,54 @@ TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
   EXPECT_TRUE(term_match.allowed_to_be_default_match);
 }
 
+TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) {
+  const std::string kEmptyMatch;
+  struct {
+    const std::string json;
+    const std::string matches_in_default_mode[3];
+    const std::string matches_in_forced_query_mode[3];
+  } cases[] = {
+    // Without suggested relevance scores.
+    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
+       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]",
+      { "a", "a1.com", "a2" },
+      { "a", "a2", kEmptyMatch } },
+
+    // With suggested relevance scores in a situation where navsuggest would
+    // go second.
+    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
+       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
+        "\"google:suggestrelevance\":[1250, 1200]}]",
+      { "a", "a1.com", "a2" },
+      { "a", "a2", kEmptyMatch } },
+
+    // With suggested relevance scores in a situation where navsuggest
+    // would go first.
+    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
+       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
+        "\"google:suggestrelevance\":[1350, 1250]}]",
+      { "a1.com", "a", "a2" },
+      { "a", "a2", kEmptyMatch } },
+
+    // With suggested relevance scores in a situation where navsuggest
+    // would go first only because verbatim has been demoted.
+    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
+       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
+        "\"google:suggestrelevance\":[1450, 1400],"
+        "\"google:verbatimrelevance\":1350}]",
+      { "a1.com", "a2", "a" },
+      { "a2", "a", kEmptyMatch } },
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+    ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode,
+                           "regular input with json=" + cases[i].json);
+    ForcedQueryTestHelper("?a", cases[i].json,
+                          cases[i].matches_in_forced_query_mode,
+                          "forced query input with json=" + cases[i].json);
+  }
+}
+
 // A multiword search with one visit should not autocomplete until multiple
 // words are typed.
 TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
@@ -599,10 +775,14 @@ TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
   EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
 }
 
-// Autocompletion should work at a word boundary after a space.
+// Autocompletion should work at a word boundary after a space, and should
+// offer a suggestion for the trimmed search query.
 TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
-  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
-                                   2));
+  AddSearchToHistory(default_t_url_, ASCIIToUTF16("two  searches "), 2);
+  GURL suggested_url(default_t_url_->url_ref().ReplaceSearchTerms(
+      TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("two searches")),
+      TemplateURLServiceFactory::GetForProfile(
+          &profile_)->search_terms_data()));
   profile_.BlockUntilHistoryProcessesPendingRequests();
 
   AutocompleteMatch wyt_match;
@@ -610,9 +790,11 @@ TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
                                                       &wyt_match));
   ASSERT_EQ(2u, provider_->matches().size());
   AutocompleteMatch term_match;
-  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
+  EXPECT_TRUE(FindMatchWithDestination(suggested_url, &term_match));
   EXPECT_GT(term_match.relevance, wyt_match.relevance);
   EXPECT_TRUE(term_match.allowed_to_be_default_match);
+  EXPECT_EQ(ASCIIToUTF16("searches"), term_match.inline_autocompletion);
+  EXPECT_EQ(ASCIIToUTF16("two searches"), term_match.fill_into_edit);
   EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
 }
 
@@ -640,35 +822,58 @@ TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) {
 }
 
 // An autocompleted multiword search should not be replaced by a different
-// autocompletion while the user is still typing a valid prefix.
+// autocompletion while the user is still typing a valid prefix unless the
+// user has typed the prefix as a query before.
 TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) {
   GURL term_url_a(AddSearchToHistory(default_t_url_,
-                                     ASCIIToUTF16("four searches aaa"), 2));
+                                     ASCIIToUTF16("four searches aaa"), 3));
   GURL term_url_b(AddSearchToHistory(default_t_url_,
                                      ASCIIToUTF16("four searches bbb"), 1));
+  GURL term_url_c(AddSearchToHistory(default_t_url_,
+                                     ASCIIToUTF16("four searches"), 1));
   profile_.BlockUntilHistoryProcessesPendingRequests();
 
   AutocompleteMatch wyt_match;
   ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"),
                                                       &wyt_match));
-  ASSERT_EQ(3u, provider_->matches().size());
+  ASSERT_EQ(4u, provider_->matches().size());
   AutocompleteMatch term_match_a;
   EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
   AutocompleteMatch term_match_b;
   EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
+  AutocompleteMatch term_match_c;
+  EXPECT_TRUE(FindMatchWithDestination(term_url_c, &term_match_c));
   EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
+  // We don't care about the relative order of b and c.
   EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
+  EXPECT_GT(wyt_match.relevance, term_match_c.relevance);
   EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
   EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
+  EXPECT_TRUE(term_match_c.allowed_to_be_default_match);
   EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
 
   ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"),
                                                       &wyt_match));
-  ASSERT_EQ(3u, provider_->matches().size());
+  ASSERT_EQ(4u, provider_->matches().size());
   EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
   EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
+  EXPECT_TRUE(FindMatchWithDestination(term_url_c, &term_match_c));
   EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
   EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
+  EXPECT_GT(wyt_match.relevance, term_match_c.relevance);
+  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
+  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
+  EXPECT_TRUE(term_match_c.allowed_to_be_default_match);
+  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
+
+  // For the exact previously-issued query, the what-you-typed match should win.
+  ASSERT_NO_FATAL_FAILURE(
+      QueryForInputAndSetWYTMatch(ASCIIToUTF16("four searches"), &wyt_match));
+  ASSERT_EQ(3u, provider_->matches().size());
+  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
+  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
+  EXPECT_GT(wyt_match.relevance, term_match_a.relevance);
+  EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
   EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
   EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
   EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
@@ -718,12 +923,13 @@ TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
   AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1);
   profile_.BlockUntilHistoryProcessesPendingRequests();
 
-  AutocompleteController controller(&profile_, NULL,
-      AutocompleteProvider::TYPE_SEARCH);
+  AutocompleteController controller(&profile_,
+      TemplateURLServiceFactory::GetForProfile(&profile_),
+      NULL, AutocompleteProvider::TYPE_SEARCH);
   controller.Start(AutocompleteInput(
-      ASCIIToUTF16("k t"), string16::npos, string16(), GURL(),
-      AutocompleteInput::INVALID_SPEC, false, false, true,
-      AutocompleteInput::ALL_MATCHES));
+      ASCIIToUTF16("k t"), base::string16::npos, base::string16(), GURL(),
+      metrics::OmniboxEventProto::INVALID_SPEC, false, false, true, true,
+      ChromeAutocompleteSchemeClassifier(&profile_)));
   const AutocompleteResult& result = controller.result();
 
   // There should be three matches, one for the keyword history, one for
@@ -739,7 +945,7 @@ TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
   EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance);
   EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match);
   EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match);
-  EXPECT_TRUE(result.match_at(2).allowed_to_be_default_match);
+  EXPECT_FALSE(result.match_at(2).allowed_to_be_default_match);
 
   // The two keyword results should come with the keyword we expect.
   EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword);
@@ -764,58 +970,78 @@ TEST_F(SearchProviderTest, KeywordVerbatim) {
     { ASCIIToUTF16("k foo"), 2,
       { ResultInfo(GURL("http://keyword/foo"),
                    AutocompleteMatchType::SEARCH_OTHER_ENGINE,
+                   true,
                    ASCIIToUTF16("k foo")),
         ResultInfo(GURL("http://defaultturl/k%20foo"),
                    AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
+                   false,
                    ASCIIToUTF16("k foo") ) } },
 
     // Make sure extra whitespace after the keyword doesn't change the
-    // keyword verbatim query.
+    // keyword verbatim query.  Also verify that interior consecutive
+    // whitespace gets trimmed.
     { ASCIIToUTF16("k   foo"), 2,
       { ResultInfo(GURL("http://keyword/foo"),
                    AutocompleteMatchType::SEARCH_OTHER_ENGINE,
+                   true,
                    ASCIIToUTF16("k foo")),
-        ResultInfo(GURL("http://defaultturl/k%20%20%20foo"),
+        ResultInfo(GURL("http://defaultturl/k%20foo"),
                    AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
-                   ASCIIToUTF16("k   foo")) } },
+                   false,
+                   ASCIIToUTF16("k foo")) } },
     // Leading whitespace should be stripped before SearchProvider gets the
     // input; hence there are no tests here about how it handles those inputs.
 
-    // But whitespace elsewhere in the query string should matter to both
-    // matches.
+    // Verify that interior consecutive whitespace gets trimmed in either case.
     { ASCIIToUTF16("k  foo  bar"), 2,
-      { ResultInfo(GURL("http://keyword/foo%20%20bar"),
+      { ResultInfo(GURL("http://keyword/foo%20bar"),
                    AutocompleteMatchType::SEARCH_OTHER_ENGINE,
-                   ASCIIToUTF16("k foo  bar")),
-        ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"),
+                   true,
+                   ASCIIToUTF16("k foo bar")),
+        ResultInfo(GURL("http://defaultturl/k%20foo%20bar"),
                    AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
-                   ASCIIToUTF16("k  foo  bar")) } },
-    // Note in the above test case we don't test trailing whitespace because
-    // SearchProvider still doesn't handle this well.  See related bugs:
-    // 102690, 99239, 164635.
+                   false,
+                   ASCIIToUTF16("k foo bar")) } },
+
+    // Verify that trailing whitespace gets trimmed.
+    { ASCIIToUTF16("k foo bar  "), 2,
+      { ResultInfo(GURL("http://keyword/foo%20bar"),
+                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
+                   true,
+                   ASCIIToUTF16("k foo bar")),
+        ResultInfo(GURL("http://defaultturl/k%20foo%20bar"),
+                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
+                   false,
+                   ASCIIToUTF16("k foo bar")) } },
 
     // Keywords can be prefixed by certain things that should get ignored
     // when constructing the keyword match.
     { ASCIIToUTF16("www.k foo"), 2,
       { ResultInfo(GURL("http://keyword/foo"),
                    AutocompleteMatchType::SEARCH_OTHER_ENGINE,
+                   true,
                    ASCIIToUTF16("k foo")),
         ResultInfo(GURL("http://defaultturl/www.k%20foo"),
                    AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
+                   false,
                    ASCIIToUTF16("www.k foo")) } },
     { ASCIIToUTF16("http://k foo"), 2,
       { ResultInfo(GURL("http://keyword/foo"),
                    AutocompleteMatchType::SEARCH_OTHER_ENGINE,
+                   true,
                    ASCIIToUTF16("k foo")),
         ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"),
                    AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
+                   false,
                    ASCIIToUTF16("http://k foo")) } },
     { ASCIIToUTF16("http://www.k foo"), 2,
       { ResultInfo(GURL("http://keyword/foo"),
                    AutocompleteMatchType::SEARCH_OTHER_ENGINE,
+                   true,
                    ASCIIToUTF16("k foo")),
         ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"),
                    AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
+                   false,
                    ASCIIToUTF16("http://www.k foo")) } },
 
     // A keyword with no remaining input shouldn't get a keyword
@@ -823,11 +1049,14 @@ TEST_F(SearchProviderTest, KeywordVerbatim) {
     { ASCIIToUTF16("k"), 1,
       { ResultInfo(GURL("http://defaultturl/k"),
                    AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
+                   true,
                    ASCIIToUTF16("k")) } },
+    // Ditto.  Trailing whitespace shouldn't make a difference.
     { ASCIIToUTF16("k "), 1,
-      { ResultInfo(GURL("http://defaultturl/k%20"),
+      { ResultInfo(GURL("http://defaultturl/k"),
                    AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
-                   ASCIIToUTF16("k ")) } }
+                   true,
+                   ASCIIToUTF16("k")) } }
 
     // The fact that verbatim queries to keyword are handled by KeywordProvider
     // not SearchProvider is tested in
@@ -851,9 +1080,9 @@ TEST_F(SearchProviderTest, CommandLineOverrides) {
   data.short_name = ASCIIToUTF16("default");
   data.SetKeyword(data.short_name);
   data.SetURL("{google:baseURL}{searchTerms}");
-  default_t_url_ = new TemplateURL(&profile_, data);
+  default_t_url_ = new TemplateURL(data);
   turl_model->Add(default_t_url_);
-  turl_model->SetDefaultSearchProvider(default_t_url_);
+  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
 
   CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL,
                                                       "http://www.bar.com/");
@@ -864,9 +1093,11 @@ TEST_F(SearchProviderTest, CommandLineOverrides) {
     { ASCIIToUTF16("k a"), 2,
       { ResultInfo(GURL("http://keyword/a"),
                    AutocompleteMatchType::SEARCH_OTHER_ENGINE,
+                   true,
                    ASCIIToUTF16("k a")),
         ResultInfo(GURL("http://www.bar.com/k%20a?a=b"),
                    AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
+                   false,
                    ASCIIToUTF16("k a")) } },
   };
 
@@ -937,6 +1168,70 @@ TEST_F(SearchProviderTest, SuggestRelevance) {
   EXPECT_TRUE(match_a3.allowed_to_be_default_match);
 }
 
+// Verifies that the default provider abandons suggested relevance scores
+// when in keyword mode.  This should happen regardless of whether the
+// keyword provider returns suggested relevance scores.
+TEST_F(SearchProviderTest, DefaultProviderNoSuggestRelevanceInKeywordMode) {
+  struct {
+    const std::string default_provider_json;
+    const std::string keyword_provider_json;
+    const std::string matches[5];
+  } cases[] = {
+    // First, try an input where the keyword provider does not deliver
+    // suggested relevance scores.
+    { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
+      "{\"google:verbatimrelevance\":9700,"
+      "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
+      "\"google:suggestrelevance\":[9900, 9800]}]",
+      "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]}]",
+      { "a", "akeyword-query", "k a", "adefault.com", "k adefault-query" } },
+
+    // Now try with keyword provider suggested relevance scores.
+    { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
+      "{\"google:verbatimrelevance\":9700,"
+      "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
+      "\"google:suggestrelevance\":[9900, 9800]}]",
+      "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"],"
+      "\"google:verbatimrelevance\":9500,"
+      "\"google:suggestrelevance\":[9600]}]",
+      { "akeyword-query", "a", "k a", "adefault.com", "k adefault-query" } }
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
+    QueryForInput(ASCIIToUTF16("k a"), false, true);
+    net::TestURLFetcher* default_fetcher =
+        test_factory_.GetFetcherByID(
+            SearchProvider::kDefaultProviderURLFetcherID);
+    ASSERT_TRUE(default_fetcher);
+    default_fetcher->set_response_code(200);
+    default_fetcher->SetResponseString(cases[i].default_provider_json);
+    default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
+    net::TestURLFetcher* keyword_fetcher =
+        test_factory_.GetFetcherByID(
+            SearchProvider::kKeywordProviderURLFetcherID);
+    ASSERT_TRUE(keyword_fetcher);
+    keyword_fetcher->set_response_code(200);
+    keyword_fetcher->SetResponseString(cases[i].keyword_provider_json);
+    keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
+    RunTillProviderDone();
+
+    const std::string description = "for input with default_provider_json=" +
+        cases[i].default_provider_json + " and keyword_provider_json=" +
+        cases[i].keyword_provider_json;
+    const ACMatches& matches = provider_->matches();
+    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
+    size_t j = 0;
+    // Ensure that the returned matches equal the expectations.
+    for (; j < matches.size(); ++j) {
+      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents) <<
+          description;
+    }
+    // Ensure that no expected matches are missing.
+    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
+      EXPECT_EQ(std::string(), cases[i].matches[j]) << description;
+  }
+}
+
 // Verifies that suggest results with relevance scores are added
 // properly when using the default fetcher.  When adding a new test
 // case to this test, please consider adding it to the tests in
@@ -949,138 +1244,157 @@ TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
   const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
   struct {
     const std::string json;
-    const DefaultFetcherMatch matches[4];
+    const DefaultFetcherMatch matches[6];
     const std::string inline_autocompletion;
   } cases[] = {
     // Ensure that suggestrelevance scores reorder matches.
     { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
-      { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch },
+      { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
         "\"google:suggestrelevance\":[1, 2]}]",
-      { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch },
+      { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch,
+        kEmptyMatch, kEmptyMatch },
       std::string() },
 
     // Without suggested relevance scores, we should only allow one
     // navsuggest result to be be displayed.
     { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
-      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
+      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch, kEmptyMatch },
       std::string() },
 
     // Ensure that verbatimrelevance scores reorder or suppress verbatim.
     // Negative values will have no effect; the calculated value will be used.
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
                              "\"google:suggestrelevance\":[9998]}]",
-      { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch },
+      { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       std::string() },
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
                              "\"google:suggestrelevance\":[9999]}]",
-      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
+      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       "1" },
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
                              "\"google:suggestrelevance\":[9999]}]",
-      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
+      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       "1" },
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
                              "\"google:suggestrelevance\":[9999]}]",
-      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
+      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       "1" },
     { "[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:verbatimrelevance\":9999,"
         "\"google:suggestrelevance\":[9998]}]",
-      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
+      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:verbatimrelevance\":9998,"
         "\"google:suggestrelevance\":[9999]}]",
-      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
+      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       ".com" },
     { "[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:verbatimrelevance\":0,"
         "\"google:suggestrelevance\":[9999]}]",
-      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
+      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       ".com" },
     { "[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:verbatimrelevance\":-1,"
         "\"google:suggestrelevance\":[9999]}]",
-      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
+      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       ".com" },
 
     // Ensure that both types of relevance scores reorder matches together.
     { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
                                      "\"google:verbatimrelevance\":9998}]",
-      { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch },
+      { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       "1" },
 
-    // Ensure that only inlinable matches may be ranked as the highest result.
-    // Ignore all suggested relevance scores if this constraint is violated.
+    // Allow non-inlineable matches to be the highest-scoring match but,
+    // if the result set lacks a single inlineable result, abandon suggested
+    // relevance scores entirely.
     { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
-      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
+      { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       std::string() },
     { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
                             "\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
+      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://b.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:suggestrelevance\":[9999]}]",
-      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
+      { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://b.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:suggestrelevance\":[9999],"
         "\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"https://a/\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[9999]}]",
-      { { "https://a", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
+      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch, kEmptyMatch },
       std::string() },
 
-    // Ensure that the top result is ranked as highly as calculated verbatim.
-    // Ignore the suggested verbatim relevance if this constraint is violated.
+    // Allow low-scoring matches.
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
-      std::string() },
+      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
+      "1" },
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
-      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
-      std::string() },
+      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
+      "1" },
     { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
                              "\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
-      std::string() },
+      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
+      "1" },
     { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
                                      "\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch },
-      std::string() },
+      { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
+      "2" },
     { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
       "\"google:verbatimrelevance\":2}]",
-      { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch },
-      std::string() },
+      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
+      "2" },
     { "[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:suggestrelevance\":[1],"
         "\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
-      std::string() },
+      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
+      ".com" },
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
         "\"google:suggestrelevance\":[1, 2],"
         "\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "a2.com", true }, { "a1.com", true }, kEmptyMatch },
-      std::string() },
+      { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch, kEmptyMatch },
+      "2.com" },
 
     // Ensure that all suggestions are considered, regardless of order.
     { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
        "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
-      { { "a", true }, { "h", false }, { "g", false }, { "f", false } },
+      { { "a", true }, { "h", false }, { "g", false }, { "f", false },
+        { "e", false }, { "d", false } },
       std::string() },
     { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
               "\"http://e.com\", \"http://f.com\", \"http://g.com\","
@@ -1091,45 +1405,53 @@ TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
                                 "\"NAVIGATION\"],"
         "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
       { { "a", true }, { "h.com", false }, { "g.com", false },
-        { "f.com", false } },
+        { "f.com", false }, { "e.com", false }, { "d.com", false } },
       std::string() },
 
     // Ensure that incorrectly sized suggestion relevance lists are ignored.
     { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
-      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch },
+      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       std::string() },
     { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
-      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
+      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
         "\"google:suggestrelevance\":[1]}]",
-      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
+      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://a1.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
        "\"google:suggestrelevance\":[9999, 1]}]",
-      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
+      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch, kEmptyMatch },
       std::string() },
 
     // Ensure that all 'verbatim' results are merged with their maximum score.
     { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
        "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
-      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
+      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       "2" },
     { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
        "{\"google:suggestrelevance\":[9998, 9997, 9999],"
         "\"google:verbatimrelevance\":0}]",
-      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
+      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       "2" },
 
     // Ensure that verbatim is always generated without other suggestions.
     // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
     { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
-      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
+      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       std::string() },
     { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
-      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
+      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
+        kEmptyMatch },
       std::string() },
   };
 
@@ -1146,12 +1468,15 @@ TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
 
     const std::string description = "for input with json=" + cases[i].json;
     const ACMatches& matches = provider_->matches();
-    // The top match must inline and score as highly as calculated verbatim.
     ASSERT_FALSE(matches.empty());
+    // Find the first match that's allowed to be the default match and check
+    // its inline_autocompletion.
+    ACMatches::const_iterator it = FindDefaultMatch(matches);
+    ASSERT_NE(matches.end(), it);
     EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
-              matches[0].inline_autocompletion) << description;
-    EXPECT_GE(matches[0].relevance, 1300) << description;
+              it->inline_autocompletion) << description;
 
+    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
     size_t j = 0;
     // Ensure that the returned matches equal the expectations.
     for (; j < matches.size(); ++j) {
@@ -1167,431 +1492,193 @@ TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
   }
 }
 
-// This test is like DefaultFetcherSuggestRelevance above except it enables
-// the field trial that causes the omnibox to be willing to reorder matches
-// to guarantee the top result is a legal default match.  This field trial
-// causes SearchProvider to allow some constraints to be violated that it
-// wouldn't normally because the omnibox will fix the problems later.
-TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevanceWithReorder) {
-  struct DefaultFetcherMatch {
+// Verifies that suggest results with relevance scores are added
+// properly when using the keyword fetcher.  This is similar to the
+// test DefaultFetcherSuggestRelevance above but this uses inputs that
+// trigger keyword suggestions (i.e., "k a" rather than "a") and has
+// different expectations (because now the results are a mix of
+// keyword suggestions and default provider suggestions).  When a new
+// test is added to this TEST_F, please consider if it would be
+// appropriate to add to DefaultFetcherSuggestRelevance as well.
+TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
+  struct KeywordFetcherMatch {
     std::string contents;
+    bool from_keyword;
     bool allowed_to_be_default_match;
   };
-  const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
+  const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
   struct {
     const std::string json;
-    const DefaultFetcherMatch matches[4];
+    const KeywordFetcherMatch matches[6];
     const std::string inline_autocompletion;
   } cases[] = {
-    // Ensure that suggestrelevance scores reorder matches.
+    // Ensure that suggest relevance scores reorder matches and that
+    // the keyword verbatim (lacking a suggested verbatim score) beats
+    // the default provider verbatim.
     { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
-      { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch },
+      { { "a",   true,  true },
+        { "k a", false, false },
+        { "c",   true,  false },
+        { "b",   true,  false },
+        kEmptyMatch, kEmptyMatch },
       std::string() },
-    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[1, 2]}]",
-      { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch },
+    // Again, check that relevance scores reorder matches, just this
+    // time with navigation matches.  This also checks that with
+    // suggested relevance scores we allow multiple navsuggest results.
+    // Note that navsuggest results that come from a keyword provider
+    // are marked as not a keyword result.  (They don't go to a
+    // keyword search engine.)
+    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
+       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
+       "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
+      { { "a",     true,  true },
+        { "d",     true,  false },
+        { "c.com", false, false },
+        { "b.com", false, false },
+        { "k a",   false, false },
+        kEmptyMatch },
       std::string() },
 
     // Without suggested relevance scores, we should only allow one
     // navsuggest result to be be displayed.
     { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
-      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
+      { { "a",     true,  true },
+        { "b.com", false, false },
+        { "k a",   false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
 
     // Ensure that verbatimrelevance scores reorder or suppress verbatim.
     // Negative values will have no effect; the calculated value will be used.
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
                              "\"google:suggestrelevance\":[9998]}]",
-      { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch },
+      { { "a",   true,  true },
+        { "a1",  true,  true },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
                              "\"google:suggestrelevance\":[9999]}]",
-      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
+      { { "a1",  true,  true },
+        { "a",   true,  true },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       "1" },
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
                              "\"google:suggestrelevance\":[9999]}]",
-      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
+      { { "a1",  true,  true },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
       "1" },
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
                              "\"google:suggestrelevance\":[9999]}]",
-      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
+      { { "a1",  true,  true },
+        { "a",   true,  true },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       "1" },
     { "[\"a\",[\"http://a.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:verbatimrelevance\":9999,"
         "\"google:suggestrelevance\":[9998]}]",
-      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
+      { { "a",     true,  true },
+        { "a.com", false, false },
+        { "k a",   false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
-    { "[\"a\",[\"http://a.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:verbatimrelevance\":9998,"
-        "\"google:suggestrelevance\":[9999]}]",
-      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
-      ".com" },
-    { "[\"a\",[\"http://a.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:verbatimrelevance\":0,"
-        "\"google:suggestrelevance\":[9999]}]",
-      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
-      ".com" },
-    { "[\"a\",[\"http://a.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:verbatimrelevance\":-1,"
-        "\"google:suggestrelevance\":[9999]}]",
-      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
-      ".com" },
 
     // Ensure that both types of relevance scores reorder matches together.
     { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
                                      "\"google:verbatimrelevance\":9998}]",
-      { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch },
+      { { "a1",  true,  true },
+        { "a",   true,  true },
+        { "a2",  true,  true },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch },
       "1" },
 
-    // Allow non-inlineable matches to be the highest-scoring match but,
-    // if the result set lacks a single inlineable result, abandon suggested
-    // relevance scores entirely.
+    // Check that non-inlinable matches may be ranked as the highest result
+    // if there is at least one inlineable match.
     { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
-      { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
-                            "\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
+      { { "b",   true,  false },
+        { "a",   true,  true },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://b.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:suggestrelevance\":[9999]}]",
-      { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch },
+      { { "b.com", false, false },
+        { "a",     true,  true },
+        { "k a",   false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
+      std::string() },
+    // On the other hand, if there is no inlineable match, restore
+    // the keyword verbatim score.
+    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
+                            "\"google:verbatimrelevance\":0}]",
+      { { "b",   true,  false },
+        { "a",   true,  true },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://b.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:suggestrelevance\":[9999],"
         "\"google:verbatimrelevance\":0}]",
-      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
+      { { "b.com", false, false },
+        { "a",     true,  true },
+        { "k a",   false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
 
-    // Allow low-scoring matches.
+    // The top result does not have to score as highly as calculated
+    // verbatim.  i.e., there are no minimum score restrictions in
+    // this provider.
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
-      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
+      { { "a1",  true,  true },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
       "1" },
     { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
-      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
+      { { "a1",  true,  true },
+        { "k a", false, false },
+        { "a",   true,  true },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       "1" },
     { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
                              "\"google:verbatimrelevance\":0}]",
-      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
+      { { "k a", false, false },
+        { "a1",   true, true },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
       "1" },
     { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
                                      "\"google:verbatimrelevance\":0}]",
-      { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
+      {
+        { "k a", false, false },
+        { "a2",  true,  true },
+        { "a1",  true,  true },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       "2" },
     { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
       "\"google:verbatimrelevance\":2}]",
-      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
+      { { "k a", false, false },
+        { "a2",  true,  true },
+        { "a",   true,  true },
+        { "a1",  true,  true },
+        kEmptyMatch, kEmptyMatch },
       "2" },
-    { "[\"a\",[\"http://a.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[1],"
-        "\"google:verbatimrelevance\":0}]",
-      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
-      ".com" },
-    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[1, 2],"
-        "\"google:verbatimrelevance\":0}]",
-      { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
-      "2.com" },
-
-    // Ensure that all suggestions are considered, regardless of order.
-    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
-       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
-      { { "a", true }, { "h", false }, { "g", false }, { "f", false } },
-      std::string() },
-    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
-              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
-              "\"http://h.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
-                                "\"NAVIGATION\", \"NAVIGATION\","
-                                "\"NAVIGATION\", \"NAVIGATION\","
-                                "\"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
-      { { "a", true }, { "h.com", false }, { "g.com", false },
-        { "f.com", false } },
-      std::string() },
-
-    // Ensure that incorrectly sized suggestion relevance lists are ignored.
-    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
-      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
-      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[1]}]",
-      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"http://a1.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\"],"
-       "\"google:suggestrelevance\":[9999, 1]}]",
-      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
-      std::string() },
-
-    // Ensure that all 'verbatim' results are merged with their maximum score.
-    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
-       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
-      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
-      "2" },
-    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
-       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
-        "\"google:verbatimrelevance\":0}]",
-      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
-      "2" },
-
-    // Ensure that verbatim is always generated without other suggestions.
-    // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
-    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
-      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
-      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
-      std::string() },
-  };
-
-  std::map<std::string, std::string> params;
-  params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
-         ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled;
-  ASSERT_TRUE(chrome_variations::AssociateVariationParams(
-      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
-  base::FieldTrialList::CreateFieldTrial(
-      OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
-    QueryForInput(ASCIIToUTF16("a"), false, false);
-    net::TestURLFetcher* fetcher =
-        test_factory_.GetFetcherByID(
-            SearchProvider::kDefaultProviderURLFetcherID);
-    ASSERT_TRUE(fetcher);
-    fetcher->set_response_code(200);
-    fetcher->SetResponseString(cases[i].json);
-    fetcher->delegate()->OnURLFetchComplete(fetcher);
-    RunTillProviderDone();
-
-    const std::string description = "for input with json=" + cases[i].json;
-    const ACMatches& matches = provider_->matches();
-    // The top match must inline and score as highly as calculated verbatim.
-    ASSERT_FALSE(matches.empty());
-    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
-              matches[0].inline_autocompletion) << description;
-
-    size_t j = 0;
-    // Ensure that the returned matches equal the expectations.
-    for (; j < matches.size(); ++j) {
-      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
-                matches[j].contents) << description;
-      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
-                matches[j].allowed_to_be_default_match) << description;
-    }
-    // Ensure that no expected matches are missing.
-    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
-      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
-          "Case # " << i << " " << description;
-  }
-}
-
-// Verifies that suggest results with relevance scores are added
-// properly when using the keyword fetcher.  This is similar to the
-// test DefaultFetcherSuggestRelevance above but this uses inputs that
-// trigger keyword suggestions (i.e., "k a" rather than "a") and has
-// different expectations (because now the results are a mix of
-// keyword suggestions and default provider suggestions).  When a new
-// test is added to this TEST_F, please consider if it would be
-// appropriate to add to DefaultFetcherSuggestRelevance as well.
-TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
-  struct KeywordFetcherMatch {
-    std::string contents;
-    bool from_keyword;
-    bool allowed_to_be_default_match;
-  };
-  const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
-  struct {
-    const std::string json;
-    const KeywordFetcherMatch matches[5];
-    const std::string inline_autocompletion;
-  } cases[] = {
-    // Ensure that suggest relevance scores reorder matches and that
-    // the keyword verbatim (lacking a suggested verbatim score) beats
-    // the default provider verbatim.
-    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
-      { { "a",   true,  true },
-        { "k a", false, true },
-        { "c",   true,  false },
-        { "b",   true,  false },
-        kEmptyMatch },
-      std::string() },
-    // Again, check that relevance scores reorder matches, just this
-    // time with navigation matches.  This also checks that with
-    // suggested relevance scores we allow multiple navsuggest results.
-    // It's odd that navsuggest results that come from a keyword
-    // provider are marked as not a keyword result.  I think this
-    // comes from them not going to a keyword search engine).
-    // TODO(mpearson): Investigate the implications (if any) of
-    // tagging these results appropriately.  If so, do it because it
-    // makes more sense.
-    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
-       "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
-      { { "a",     true,  true },
-        { "d",     true,  false },
-        { "c.com", false, false },
-        { "b.com", false, false },
-        { "k a",   false, true }, },
-      std::string() },
-
-    // Without suggested relevance scores, we should only allow one
-    // navsuggest result to be be displayed.
-    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
-      { { "a",     true,  true },
-        { "b.com", false, false },
-        { "k a",   false, true },
-        kEmptyMatch, kEmptyMatch },
-      std::string() },
-
-    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
-    // Negative values will have no effect; the calculated value will be used.
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
-                             "\"google:suggestrelevance\":[9998]}]",
-      { { "a",   true,  true },
-        { "a1",  true,  true },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
-                             "\"google:suggestrelevance\":[9999]}]",
-      { { "a1",  true,  true },
-        { "a",   true,  true },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch },
-      "1" },
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
-                             "\"google:suggestrelevance\":[9999]}]",
-      { { "a1",  true,  true },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch, kEmptyMatch },
-      "1" },
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
-                             "\"google:suggestrelevance\":[9999]}]",
-      { { "a1",  true,  true },
-        { "a",   true,  true },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch },
-      "1" },
-    { "[\"a\",[\"http://a.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:verbatimrelevance\":9999,"
-        "\"google:suggestrelevance\":[9998]}]",
-      { { "a",     true,  true },
-        { "a.com", false, true },
-        { "k a",   false, true },
-        kEmptyMatch, kEmptyMatch },
-      std::string() },
-
-    // Ensure that both types of relevance scores reorder matches together.
-    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
-                                     "\"google:verbatimrelevance\":9998}]",
-      { { "a1",  true,  true },
-        { "a",   true,  true },
-        { "a2",  true,  true },
-        { "k a", false, true },
-        kEmptyMatch },
-      "1" },
-
-    // Ensure that only inlinable matches may be ranked as the highest result.
-    // Ignore all suggested relevance scores if this constraint is violated.
-    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
-      { { "a",   true,  true },
-        { "b",   true,  false },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
-                            "\"google:verbatimrelevance\":0}]",
-      { { "a",   true,  true },
-        { "b",   true,  false },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"http://b.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[9999]}]",
-      { { "a",     true,  true },
-        { "b.com", false, false },
-        { "k a",   false, true },
-        kEmptyMatch, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"http://b.com\"],[],[],"
-       "{\"google:suggesttype\":[\"NAVIGATION\"],"
-        "\"google:suggestrelevance\":[9999],"
-        "\"google:verbatimrelevance\":0}]",
-      { { "a",     true,  true },
-        { "b.com", false, false },
-        { "k a",   false, true },
-        kEmptyMatch, kEmptyMatch },
-      std::string() },
-
-    // Ensure that the top result is ranked as highly as calculated verbatim.
-    // Ignore the suggested verbatim relevance if this constraint is violated.
-    // Note that keyword suggestions by default (not in suggested relevance
-    // mode) score more highly than the default verbatim.
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
-      { { "a",   true,  true },
-        { "a1",  true,  true },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
-      { { "a",   true,  true },
-        { "a1",  true,  true },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch},
-      std::string() },
-    // Continuing the same category of tests, but make sure we keep the
-    // suggested relevance scores even as we discard the verbatim relevance
-    // scores.
-    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
-                             "\"google:verbatimrelevance\":0}]",
-      { { "a",   true,  true },
-        { "k a", false, true },
-        { "a1",   true, true },
-        kEmptyMatch, kEmptyMatch},
-      std::string() },
-    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
-                                     "\"google:verbatimrelevance\":0}]",
-      { { "a",   true,  true },
-        { "k a", false, true },
-        { "a2",  true,  true },
-        { "a1",  true,  true },
-        kEmptyMatch },
-      std::string() },
-    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
-      "\"google:verbatimrelevance\":2}]",
-      { { "a",   true,  true },
-        { "k a", false, true },
-        { "a2",  true,  true },
-        { "a1",  true,  true },
-        kEmptyMatch },
-      std::string() },
 
     // Ensure that all suggestions are considered, regardless of order.
     { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
        "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
       { { "a",   true,  true },
-        { "k a", false, true },
+        { "k a", false, false },
         { "h",   true,  false },
         { "g",   true,  false },
-        { "f",   true,  false } },
+        { "f",   true,  false },
+        { "e",   true,  false } },
       std::string() },
     { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
               "\"http://e.com\", \"http://f.com\", \"http://g.com\","
@@ -1602,10 +1689,11 @@ TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
                                 "\"NAVIGATION\"],"
         "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
       { { "a",     true,  true },
-        { "k a",   false, true },
+        { "k a",   false, false },
         { "h.com", false, false },
         { "g.com", false, false },
-        { "f.com", false, false } },
+        { "f.com", false, false },
+        { "e.com", false, false } },
       std::string() },
 
     // Ensure that incorrectly sized suggestion relevance lists are ignored.
@@ -1615,32 +1703,32 @@ TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
       { { "a",   true,  true },
         { "a1",  true,  true },
         { "a2",  true,  true },
-        { "k a", false, true },
-        kEmptyMatch },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
       { { "a",   true,  true },
         { "a1",  true,  true },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch},
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
-    // In this case, ignored the suggested relevance scores means we keep
+    // In this case, ignoring the suggested relevance scores means we keep
     // only one navsuggest result.
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
         "\"google:suggestrelevance\":[1]}]",
       { { "a",      true,  true },
-        { "a1.com", false, true },
-        { "k a",    false, true },
-        kEmptyMatch, kEmptyMatch},
+        { "a1.com", false, false },
+        { "k a",    false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://a1.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
        "\"google:suggestrelevance\":[9999, 1]}]",
       { { "a",      true,  true },
-        { "a1.com", false, true },
-        { "k a",    false, true },
-        kEmptyMatch, kEmptyMatch},
+        { "a1.com", false, false },
+        { "k a",    false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
 
     // Ensure that all 'verbatim' results are merged with their maximum score.
@@ -1649,8 +1737,8 @@ TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
       { { "a2",  true,  true },
         { "a",   true,  true },
         { "a1",  true,  true },
-        { "k a", false, true },
-        kEmptyMatch },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch },
       "2" },
     { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
        "{\"google:suggestrelevance\":[9998, 9997, 9999],"
@@ -1658,57 +1746,54 @@ TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
       { { "a2",  true,  true },
         { "a",   true,  true },
         { "a1",  true,  true },
-        { "k a", false, true },
-        kEmptyMatch },
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch },
       "2" },
 
     // Ensure that verbatim is always generated without other suggestions.
     // TODO(mpearson): Ensure the value of verbatimrelevance is respected
     // (except when suggested relevances are ignored).
     { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
-      { { "a",   true,  true },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch, kEmptyMatch},
+      { { "k a", false, false },
+        { "a",   true,  true },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
       { { "a",   true,  true },
-        { "k a", false, true },
-        kEmptyMatch, kEmptyMatch, kEmptyMatch},
+        { "k a", false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
 
-    // Check that navsuggestions will be demoted below queries.
-    // (Navsuggestions are not allowed to appear first.)  In the process,
-    // make sure the navsuggestions still remain in the same order.
-    // First, check the situation where navsuggest scores more than verbatim
-    // and there are no query suggestions.
+    // In reorder mode, navsuggestions will not need to be demoted (because
+    // they are marked as not allowed to be default match and will be
+    // reordered as necessary).
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
         "\"google:verbatimrelevance\":9990,"
         "\"google:suggestrelevance\":[9998, 9999]}]",
-      { { "a",      true,  true },
-        { "a2.com", false, true },
-        { "a1.com", false, true },
-        { "k a",    false, true },
-        kEmptyMatch },
+      { { "a2.com", false, false },
+        { "a1.com", false, false },
+        { "a",      true,  true },
+        { "k a",    false, false },
+        kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
         "\"google:verbatimrelevance\":9990,"
         "\"google:suggestrelevance\":[9999, 9998]}]",
-      { { "a",      true,  true },
-        { "a1.com", false, true },
-        { "a2.com", false, true },
-        { "k a",    false, true },
-        kEmptyMatch },
+      { { "a1.com", false, false },
+        { "a2.com", false, false },
+        { "a",      true,  true },
+        { "k a",    false, false },
+        kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[\"https://a/\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\"],"
         "\"google:suggestrelevance\":[9999]}]",
-      { { "a",         true, true },
-        { "https://a", false, true },
-        { "k a",       false, true },
-        kEmptyMatch,
-        kEmptyMatch },
+      { { "https://a", false, false },
+        { "a",         true,  true },
+        { "k a",       false, false },
+        kEmptyMatch, kEmptyMatch, kEmptyMatch },
       std::string() },
     // Check when navsuggest scores more than verbatim and there is query
     // suggestion but it scores lower.
@@ -1716,21 +1801,23 @@ TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
         "\"google:verbatimrelevance\":9990,"
         "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
-      { { "a",      true,  true },
-        { "a2.com", false, true },
-        { "a1.com", false, true },
+      { { "a2.com", false, false },
+        { "a1.com", false, false },
+        { "a",      true,  true },
         { "a3",     true,  true },
-        { "k a",    false, true } },
+        { "k a",    false, false },
+        kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
         "\"google:verbatimrelevance\":9990,"
         "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
-      { { "a",      true,  true },
-        { "a1.com", false, true },
-        { "a2.com", false, true },
+      { { "a1.com", false, false },
+        { "a2.com", false, false },
+        { "a",      true,  true },
         { "a3",     true,  true },
-        { "k a",    false, true } },
+        { "k a",    false, false },
+        kEmptyMatch },
       std::string() },
     // Check when navsuggest scores more than a query suggestion.  There is
     // a verbatim but it scores lower.
@@ -1738,66 +1825,66 @@ TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
         "\"google:verbatimrelevance\":9990,"
         "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
-      { { "a3",     true,  true },
-        { "a2.com", false, true },
-        { "a1.com", false, true },
+      { { "a2.com", false, false },
+        { "a1.com", false, false },
+        { "a3",     true,  true },
         { "a",      true,  true },
-        { "k a",    false, true } },
+        { "k a",    false, false },
+        kEmptyMatch },
       "3" },
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
         "\"google:verbatimrelevance\":9990,"
         "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
-      { { "a3",     true,  true },
-        { "a1.com", false, true },
-        { "a2.com", false, true },
+      { { "a1.com", false, false },
+        { "a2.com", false, false },
+        { "a3",     true,  true },
         { "a",      true,  true },
-        { "k a",    false, true } },
+        { "k a",    false, false },
+        kEmptyMatch },
       "3" },
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
         "\"google:verbatimrelevance\":0,"
         "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
-      { { "a3",     true,  true },
-        { "a2.com", false, true },
-        { "a1.com", false, true },
-        { "k a",    false, true },
-        kEmptyMatch },
+      { { "a2.com", false, false },
+        { "a1.com", false, false },
+        { "a3",     true,  true },
+        { "k a",    false, false },
+        kEmptyMatch, kEmptyMatch },
       "3" },
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
         "\"google:verbatimrelevance\":0,"
         "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
-      { { "a3",     true,  true },
-        { "a1.com", false, true },
-        { "a2.com", false, true },
-        { "k a",    false, true },
-        kEmptyMatch },
+      { { "a1.com", false, false },
+        { "a2.com", false, false },
+        { "a3",     true,  true },
+        { "k a",    false, false },
+        kEmptyMatch, kEmptyMatch },
       "3" },
     // Check when there is neither verbatim nor a query suggestion that,
     // because we can't demote navsuggestions below a query suggestion,
-    // we abandon suggested relevance scores entirely.  One consequence is
-    // that this means we restore the keyword verbatim match.  Note
-    // that in this case of abandoning suggested relevance scores, we still
-    // keep the navsuggestions in the same order, but we revert to only allowing
-    // one navigation to appear because the scores are completely local.
+    // we restore the keyword verbatim score.
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
         "\"google:verbatimrelevance\":0,"
         "\"google:suggestrelevance\":[9998, 9999]}]",
-      { { "a",      true,  true },
-        { "a2.com", false, true },
-        { "k a",    false, true },
-        kEmptyMatch, kEmptyMatch},
+      { { "a2.com", false, false },
+        { "a1.com", false, false },
+        { "a",      true,  true },
+        { "k a",    false, false },
+        kEmptyMatch, kEmptyMatch },
       std::string() },
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
         "\"google:verbatimrelevance\":0,"
         "\"google:suggestrelevance\":[9999, 9998]}]",
-      { { "a",      true,  true },
-        { "a1.com", false, true },
-        { "k a",    false, true },
-        kEmptyMatch, kEmptyMatch},
+      { { "a1.com", false, false },
+        { "a2.com", false, false },
+        { "a",      true,  true },
+        { "k a",    false, false },
+        kEmptyMatch, kEmptyMatch },
       std::string() },
     // More checks that everything works when it's not necessary to demote.
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
@@ -1805,20 +1892,22 @@ TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
         "\"google:verbatimrelevance\":9990,"
         "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
       { { "a3",     true,  true },
-        { "a2.com", false, true },
-        { "a1.com", false, true },
+        { "a2.com", false, false },
+        { "a1.com", false, false },
         { "a",      true,  true },
-        { "k a",    false, true } },
+        { "k a",    false, false },
+        kEmptyMatch },
       "3" },
     { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
        "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
         "\"google:verbatimrelevance\":9990,"
         "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
       { { "a3",     true,  true },
-        { "a1.com", false, true },
-        { "a2.com", false, true },
+        { "a1.com", false, false },
+        { "a2.com", false, false },
         { "a",      true,  true },
-        { "k a",    false, true } },
+        { "k a",    false, false },
+        kEmptyMatch },
       "3" },
   };
 
@@ -1847,12 +1936,15 @@ TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
 
     const std::string description = "for input with json=" + cases[i].json;
     const ACMatches& matches = provider_->matches();
-    // The top match must inline and score as highly as calculated verbatim.
     ASSERT_FALSE(matches.empty());
+    // Find the first match that's allowed to be the default match and check
+    // its inline_autocompletion.
+    ACMatches::const_iterator it = FindDefaultMatch(matches);
+    ASSERT_NE(matches.end(), it);
     EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
-              matches[0].inline_autocompletion) << description;
-    EXPECT_GE(matches[0].relevance, 1300) << description;
+              it->inline_autocompletion) << description;
 
+    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
     size_t j = 0;
     // Ensure that the returned matches equal the expectations.
     for (; j < matches.size(); ++j) {
@@ -1871,20 +1963,16 @@ TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
 }
 
 TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
-  // Enable Instant Extended in order to allow an increased number of
-  // suggestions.
-  chrome::EnableInstantExtendedAPIForTesting();
-
   // We hardcode the string "term1" below, so ensure that the search term that
   // got added to history already is that string.
   ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
-  string16 term = term1_.substr(0, term1_.length() - 1);
+  base::string16 term = term1_.substr(0, term1_.length() - 1);
 
   AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
   profile_.BlockUntilHistoryProcessesPendingRequests();
 
   struct {
-    const string16 input;
+    const base::string16 input;
     const std::string json;
     const std::string matches[6];
   } cases[] = {
@@ -1931,21 +2019,7 @@ TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
        "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
         "\"google:verbatimrelevance\":1450,"
         "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
-      { "term", "a1", "a2", "term2", "a3", "a4" } },
-    // When the input looks like a URL, we disallow having a query as the
-    // highest-ranking result.  If the query was provided by a suggestion, we
-    // reset the suggest scores to enforce this (see
-    // SearchProvider::UpdateMatches()).  Even if we reset the suggest scores,
-    // however, we should still allow navsuggestions to be treated as
-    // server-provided.
-    { ASCIIToUTF16("a.com"),
-      "[\"a.com\",[\"a1\", \"a2\", \"a.com/1\", \"a.com/2\"],[],[],"
-       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"NAVIGATION\","
-                                "\"NAVIGATION\"],"
-        // A verbatim query for URL-like input scores 850, so the navigation
-        // scores here should bracket it.
-        "\"google:suggestrelevance\":[9999, 9998, 900, 800]}]",
-      { "a.com/1", "a.com", "a.com/2", "a1", kNotApplicable, kNotApplicable } },
+      { "term", "a1", "a2", "term2", "a3", "a4" } }
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
@@ -1963,7 +2037,7 @@ TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
     const ACMatches& matches = provider_->matches();
 
     // Ensure no extra matches are present.
-    ASSERT_LE(matches.size(), 6U);
+    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
 
     size_t j = 0;
     // Ensure that the returned matches equal the expectations.
@@ -1991,7 +2065,21 @@ TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
     const std::string json;
     const DefaultFetcherUrlInputMatch output[4];
   } cases[] = {
-    // Ensure topmost NAVIGATION matches are allowed for URL input.
+    // Ensure NAVIGATION matches are allowed to be listed first for URL
+    // input regardless of whether the match is inlineable.  Note that
+    // non-inlineable matches should not be allowed to be the default match.
+    { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[],"
+                "{\"google:suggesttype\":[\"NAVIGATION\"],"
+                 "\"google:suggestrelevance\":[9999]}]",
+      { { "b.com",   AutocompleteMatchType::NAVSUGGEST,            false },
+        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
+        kEmptyMatch, kEmptyMatch } },
+    { "a.com", "[\"a.com\",[\"https://b.com\"],[],[],"
+                "{\"google:suggesttype\":[\"NAVIGATION\"],"
+                 "\"google:suggestrelevance\":[9999]}]",
+      { { "https://b.com", AutocompleteMatchType::NAVSUGGEST,           false },
+        { "a.com",         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
+        kEmptyMatch, kEmptyMatch } },
     { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
                 "{\"google:suggesttype\":[\"NAVIGATION\"],"
                  "\"google:suggestrelevance\":[9999]}]",
@@ -2005,52 +2093,50 @@ TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
         { "a.com",         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
         kEmptyMatch, kEmptyMatch } },
 
-    // Ensure topmost SUGGEST matches are not allowed for URL input.
-    // SearchProvider disregards search and verbatim suggested relevances.
+    // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL
+    // input.  SearchProvider disregards search and verbatim suggested
+    // relevances.
     { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
                 "{\"google:suggestrelevance\":[9999]}]",
       { { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
         { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
         kEmptyMatch, kEmptyMatch } },
-    { "a.com", "[\"a.com\",[\"a.com/a\"],[],[],"
+    { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
                 "{\"google:suggestrelevance\":[9999]}]",
-      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
-        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
+      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,   true },
+        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
         kEmptyMatch, kEmptyMatch } },
 
     // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
-    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
+    { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
                 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
                  "\"google:suggestrelevance\":[9999, 9998]}]",
-      { { "a.com/b", AutocompleteMatchType::NAVSUGGEST,            true },
-        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
-        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
+      { { "a.com/b",    AutocompleteMatchType::NAVSUGGEST,            true },
+        { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
+        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
         kEmptyMatch } },
-    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
+    { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
                 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
                  "\"google:suggestrelevance\":[9998, 9997],"
                  "\"google:verbatimrelevance\":9999}]",
-      { { "a.com/b", AutocompleteMatchType::NAVSUGGEST,            true },
-        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
-        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
+      { { "a.com/b",    AutocompleteMatchType::NAVSUGGEST,            true },
+        { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
+        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
         kEmptyMatch } },
 
-    // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches.
-    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
-                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
-      "\"google:suggestrelevance\":[9999, 9998]}]",
-      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
-        { "abc.com", AutocompleteMatchType::NAVSUGGEST,            false },
-        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
-        kEmptyMatch } },
-    { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
-                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
-                 "\"google:suggestrelevance\":[9998, 9997],"
-                 "\"google:verbatimrelevance\":9999}]",
-      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
-        { "abc.com", AutocompleteMatchType::NAVSUGGEST,            false },
-        { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST,        true },
-        kEmptyMatch } },
+    // Ensure topmost non-inlineable SUGGEST matches are allowed for URL
+    // input assuming the top inlineable match is not a query (i.e., is a
+    // NAVSUGGEST).
+    { "a.com", "[\"a.com\",[\"info\"],[],[],"
+                "{\"google:suggestrelevance\":[9999]}]",
+      { { "info",  AutocompleteMatchType::SEARCH_SUGGEST,        false },
+        { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
+        kEmptyMatch, kEmptyMatch } },
+    { "a.com", "[\"a.com\",[\"info\"],[],[],"
+                "{\"google:suggestrelevance\":[9999]}]",
+      { { "info",  AutocompleteMatchType::SEARCH_SUGGEST,        false },
+        { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
+        kEmptyMatch, kEmptyMatch } },
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
@@ -2066,6 +2152,7 @@ TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
 
     size_t j = 0;
     const ACMatches& matches = provider_->matches();
+    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output));
     // Ensure that the returned matches equal the expectations.
     for (; j < matches.size(); ++j) {
       EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
@@ -2135,163 +2222,198 @@ TEST_F(SearchProviderTest, NavigationInline) {
     // Some cases do not trim "http://" to match from the start of the scheme.
     const std::string fill_into_edit;
     const std::string inline_autocompletion;
-    const bool allowed_to_be_default_match;
+    const bool allowed_to_be_default_match_in_regular_mode;
+    const bool allowed_to_be_default_match_in_prevent_inline_mode;
   } cases[] = {
     // Do not inline matches that do not contain the input; trim http as needed.
-    { "x",                    "http://www.abc.com",
-                                     "www.abc.com",  std::string(), false },
-    { "https:",               "http://www.abc.com",
-                                     "www.abc.com",  std::string(), false },
-    { "abc.com/",             "http://www.abc.com",
-                                     "www.abc.com",  std::string(), false },
+    { "x",                 "http://www.abc.com",
+                                  "www.abc.com",  std::string(), false, false },
+    { "https:",            "http://www.abc.com",
+                                  "www.abc.com",  std::string(), false, false },
     { "http://www.abc.com/a", "http://www.abc.com",
-                              "http://www.abc.com",  std::string(), false },
-    { "http://www.abc.com",   "https://www.abc.com",
-                              "https://www.abc.com", std::string(), false },
-    { "http://abc.com",       "ftp://abc.com",
-                              "ftp://abc.com",       std::string(), false },
-    { "https://www.abc.com",  "http://www.abc.com",
-                                     "www.abc.com",  std::string(), false },
-    { "ftp://abc.com",        "http://abc.com",
-                                     "abc.com",      std::string(), false },
+                              "http://www.abc.com",  std::string(), false,
+                                                                    false },
 
     // Do not inline matches with invalid input prefixes; trim http as needed.
-    { "ttp",                  "http://www.abc.com",
-                                     "www.abc.com", std::string(), false },
-    { "://w",                 "http://www.abc.com",
-                                     "www.abc.com", std::string(), false },
-    { "ww.",                  "http://www.abc.com",
-                                     "www.abc.com", std::string(), false },
-    { ".ab",                  "http://www.abc.com",
-                                     "www.abc.com", std::string(), false },
-    { "bc",                   "http://www.abc.com",
-                                     "www.abc.com", std::string(), false },
-    { ".com",                 "http://www.abc.com",
-                                     "www.abc.com", std::string(), false },
+    { "ttp",              "http://www.abc.com",
+                                 "www.abc.com", std::string(), false, false },
+    { "://w",             "http://www.abc.com",
+                                 "www.abc.com", std::string(), false, false },
+    { "ww.",              "http://www.abc.com",
+                                 "www.abc.com", std::string(), false, false },
+    { ".ab",              "http://www.abc.com",
+                                 "www.abc.com", std::string(), false, false },
+    { "bc",               "http://www.abc.com",
+                                 "www.abc.com", std::string(), false, false },
+    { ".com",             "http://www.abc.com",
+                                 "www.abc.com", std::string(), false, false },
 
     // Do not inline matches that omit input domain labels; trim http as needed.
-    { "www.a",                "http://a.com",
-                                     "a.com",       std::string(), false },
-    { "http://www.a",         "http://a.com",
-                              "http://a.com",       std::string(), false },
-    { "www.a",                "ftp://a.com",
-                              "ftp://a.com",        std::string(), false },
-    { "ftp://www.a",          "ftp://a.com",
-                              "ftp://a.com",        std::string(), false },
+    { "www.a",            "http://a.com",
+                                 "a.com",       std::string(), false, false },
+    { "http://www.a",     "http://a.com",
+                          "http://a.com",       std::string(), false, false },
+    { "www.a",            "ftp://a.com",
+                          "ftp://a.com",        std::string(), false, false },
+    { "ftp://www.a",      "ftp://a.com",
+                          "ftp://a.com",        std::string(), false, false },
 
     // Input matching but with nothing to inline will not yield an offset, but
     // will be allowed to be default.
-    { "abc.com",              "http://www.abc.com",
-                                     "www.abc.com", std::string(), true },
-    { "http://www.abc.com",   "http://www.abc.com",
-                              "http://www.abc.com", std::string(), true },
+    { "abc.com",             "http://www.abc.com",
+                                    "www.abc.com", std::string(), true, true },
+    { "abc.com/",            "http://www.abc.com",
+                                    "www.abc.com", std::string(), true, true },
+    { "http://www.abc.com",  "http://www.abc.com",
+                             "http://www.abc.com", std::string(), true, true },
+    { "http://www.abc.com/", "http://www.abc.com",
+                             "http://www.abc.com", std::string(), true, true },
+
+    // Inputs with trailing whitespace should inline when possible.
+    { "abc.com ",      "http://www.abc.com",
+                              "www.abc.com",      std::string(), true,  true },
+    { "abc.com/ ",     "http://www.abc.com",
+                              "www.abc.com",      std::string(), true,  true },
+    { "abc.com ",      "http://www.abc.com/bar",
+                              "www.abc.com/bar",  "/bar",        false, false },
+
+    // A suggestion that's equivalent to what the input gets fixed up to
+    // should be inlined.
+    { "abc.com:",      "http://abc.com/",
+                              "abc.com",      "", true, true },
+    { "abc.com:",      "http://www.abc.com",
+                              "www.abc.com",  "", true, true },
 
     // Inline matches when the input is a leading substring of the scheme.
-    { "h",                    "http://www.abc.com",
-                              "http://www.abc.com", "ttp://www.abc.com", true },
-    { "http",                 "http://www.abc.com",
-                              "http://www.abc.com", "://www.abc.com",    true },
+    { "h",             "http://www.abc.com",
+                       "http://www.abc.com", "ttp://www.abc.com", true, false },
+    { "http",          "http://www.abc.com",
+                       "http://www.abc.com", "://www.abc.com",    true, false },
 
     // Inline matches when the input is a leading substring of the full URL.
-    { "http:",                "http://www.abc.com",
-                              "http://www.abc.com", "//www.abc.com", true },
-    { "http://w",             "http://www.abc.com",
-                              "http://www.abc.com", "ww.abc.com",    true },
-    { "http://www.",          "http://www.abc.com",
-                              "http://www.abc.com", "abc.com",       true },
-    { "http://www.ab",        "http://www.abc.com",
-                              "http://www.abc.com", "c.com",         true },
+    { "http:",             "http://www.abc.com",
+                           "http://www.abc.com", "//www.abc.com", true, false },
+    { "http://w",          "http://www.abc.com",
+                           "http://www.abc.com", "ww.abc.com",    true, false },
+    { "http://www.",       "http://www.abc.com",
+                           "http://www.abc.com", "abc.com",       true, false },
+    { "http://www.ab",     "http://www.abc.com",
+                           "http://www.abc.com", "c.com",         true, false },
     { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
                               "http://www.abc.com/path/file.htm?q=x#foo",
                                                   "ath/file.htm?q=x#foo",
-                              true },
+                                                                  true, false },
     { "http://abc.com/p",     "http://abc.com/path/file.htm?q=x#foo",
                               "http://abc.com/path/file.htm?q=x#foo",
-                                              "ath/file.htm?q=x#foo", true},
+                                              "ath/file.htm?q=x#foo",
+                                                                  true, false},
 
     // Inline matches with valid URLPrefixes; only trim "http://".
     { "w",               "http://www.abc.com",
-                                "www.abc.com", "ww.abc.com", true },
+                                "www.abc.com", "ww.abc.com", true, false },
     { "www.a",           "http://www.abc.com",
-                                "www.abc.com", "bc.com",     true },
+                                "www.abc.com", "bc.com",     true, false },
     { "abc",             "http://www.abc.com",
-                                "www.abc.com", ".com",       true },
+                                "www.abc.com", ".com",       true, false },
     { "abc.c",           "http://www.abc.com",
-                                "www.abc.com", "om",         true },
+                                "www.abc.com", "om",         true, false },
     { "abc.com/p",       "http://www.abc.com/path/file.htm?q=x#foo",
                                 "www.abc.com/path/file.htm?q=x#foo",
-                                             "ath/file.htm?q=x#foo", true },
+                                             "ath/file.htm?q=x#foo",
+                                                             true, false },
     { "abc.com/p",       "http://abc.com/path/file.htm?q=x#foo",
                                 "abc.com/path/file.htm?q=x#foo",
-                                         "ath/file.htm?q=x#foo", true },
+                                         "ath/file.htm?q=x#foo",
+                                                             true, false },
 
     // Inline matches using the maximal URLPrefix components.
     { "h",               "http://help.com",
-                                "help.com", "elp.com",     true },
+                                "help.com", "elp.com",     true, false },
     { "http",            "http://http.com",
-                                "http.com", ".com",        true },
+                                "http.com", ".com",        true, false },
     { "h",               "http://www.help.com",
-                                "www.help.com", "elp.com", true },
+                                "www.help.com", "elp.com", true, false },
     { "http",            "http://www.http.com",
-                                "www.http.com", ".com",    true },
+                                "www.http.com", ".com",    true, false },
     { "w",               "http://www.www.com",
-                                "www.www.com",  "ww.com",  true },
+                                "www.www.com",  "ww.com",  true, false },
 
     // Test similar behavior for the ftp and https schemes.
-    { "ftp://www.ab",    "ftp://www.abc.com/path/file.htm?q=x#foo",
-                         "ftp://www.abc.com/path/file.htm?q=x#foo",
-                                     "c.com/path/file.htm?q=x#foo", true },
-    { "www.ab",          "ftp://www.abc.com/path/file.htm?q=x#foo",
-                         "ftp://www.abc.com/path/file.htm?q=x#foo",
-                                     "c.com/path/file.htm?q=x#foo", true },
-    { "ab",              "ftp://www.abc.com/path/file.htm?q=x#foo",
-                         "ftp://www.abc.com/path/file.htm?q=x#foo",
-                                     "c.com/path/file.htm?q=x#foo", true },
-    { "ab",              "ftp://abc.com/path/file.htm?q=x#foo",
-                         "ftp://abc.com/path/file.htm?q=x#foo",
-                                 "c.com/path/file.htm?q=x#foo",     true },
+    { "ftp://www.ab",  "ftp://www.abc.com/path/file.htm?q=x#foo",
+                       "ftp://www.abc.com/path/file.htm?q=x#foo",
+                                  "c.com/path/file.htm?q=x#foo",  true, false },
+    { "www.ab",        "ftp://www.abc.com/path/file.htm?q=x#foo",
+                       "ftp://www.abc.com/path/file.htm?q=x#foo",
+                                   "c.com/path/file.htm?q=x#foo", true, false },
+    { "ab",            "ftp://www.abc.com/path/file.htm?q=x#foo",
+                       "ftp://www.abc.com/path/file.htm?q=x#foo",
+                                   "c.com/path/file.htm?q=x#foo", true, false },
+    { "ab",            "ftp://abc.com/path/file.htm?q=x#foo",
+                       "ftp://abc.com/path/file.htm?q=x#foo",
+                               "c.com/path/file.htm?q=x#foo",     true, false },
     { "https://www.ab",  "https://www.abc.com/path/file.htm?q=x#foo",
                          "https://www.abc.com/path/file.htm?q=x#foo",
-                                       "c.com/path/file.htm?q=x#foo", true },
-    { "www.ab",          "https://www.abc.com/path/file.htm?q=x#foo",
-                         "https://www.abc.com/path/file.htm?q=x#foo",
-                                       "c.com/path/file.htm?q=x#foo", true },
-    { "ab",              "https://www.abc.com/path/file.htm?q=x#foo",
-                         "https://www.abc.com/path/file.htm?q=x#foo",
-                                       "c.com/path/file.htm?q=x#foo", true },
-    { "ab",              "https://abc.com/path/file.htm?q=x#foo",
-                         "https://abc.com/path/file.htm?q=x#foo",
-                                   "c.com/path/file.htm?q=x#foo", true },
+                                       "c.com/path/file.htm?q=x#foo",
+                                                                  true, false },
+    { "www.ab",      "https://www.abc.com/path/file.htm?q=x#foo",
+                     "https://www.abc.com/path/file.htm?q=x#foo",
+                                   "c.com/path/file.htm?q=x#foo", true, false },
+    { "ab",          "https://www.abc.com/path/file.htm?q=x#foo",
+                     "https://www.abc.com/path/file.htm?q=x#foo",
+                                   "c.com/path/file.htm?q=x#foo", true, false },
+    { "ab",          "https://abc.com/path/file.htm?q=x#foo",
+                     "https://abc.com/path/file.htm?q=x#foo",
+                               "c.com/path/file.htm?q=x#foo",     true, false },
 
     // Forced query input should inline and retain the "?" prefix.
     { "?http://www.ab",  "http://www.abc.com",
-                        "?http://www.abc.com", "c.com", true },
+                        "?http://www.abc.com", "c.com", true, false },
     { "?www.ab",         "http://www.abc.com",
-                               "?www.abc.com", "c.com", true },
+                               "?www.abc.com", "c.com", true, false },
     { "?ab",             "http://www.abc.com",
-                               "?www.abc.com", "c.com", true },
+                               "?www.abc.com", "c.com", true, false },
+    { "?abc.com",        "http://www.abc.com",
+                               "?www.abc.com", "",      true, true },
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
+    // First test regular mode.
     QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
     AutocompleteMatch match(
-        provider_->NavigationToMatch(SearchProvider::NavigationResult(
-            *provider_.get(), GURL(cases[i].url), string16(), false, 0,
-            false)));
+        provider_->NavigationToMatch(SearchSuggestionParser::NavigationResult(
+            ChromeAutocompleteSchemeClassifier(&profile_), GURL(cases[i].url),
+            AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
+            false, 0, false, ASCIIToUTF16(cases[i].input), std::string())));
     EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
               match.inline_autocompletion);
     EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
-    EXPECT_EQ(cases[i].allowed_to_be_default_match,
+    EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode,
               match.allowed_to_be_default_match);
+
+    // Then test prevent-inline-autocomplete mode.
+    QueryForInput(ASCIIToUTF16(cases[i].input), true, false);
+    AutocompleteMatch match_prevent_inline(
+        provider_->NavigationToMatch(SearchSuggestionParser::NavigationResult(
+            ChromeAutocompleteSchemeClassifier(&profile_), GURL(cases[i].url),
+            AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
+            false, 0, false, ASCIIToUTF16(cases[i].input), std::string())));
+    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
+              match_prevent_inline.inline_autocompletion);
+    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit),
+              match_prevent_inline.fill_into_edit);
+    EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode,
+              match_prevent_inline.allowed_to_be_default_match);
   }
 }
 
 // Verifies that "http://" is not trimmed for input that is a leading substring.
 TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
-  const string16 input(ASCIIToUTF16("ht"));
-  const string16 url(ASCIIToUTF16("http://a.com"));
-  const SearchProvider::NavigationResult result(
-      *provider_.get(), GURL(url), string16(), false, 0, false);
+  const base::string16 input(ASCIIToUTF16("ht"));
+  const base::string16 url(ASCIIToUTF16("http://a.com"));
+  const SearchSuggestionParser::NavigationResult result(
+      ChromeAutocompleteSchemeClassifier(&profile_), GURL(url),
+      AutocompleteMatchType::NAVSUGGEST,
+      base::string16(), std::string(), false, 0, false, input, std::string());
 
   // Check the offset and strings when inline autocompletion is allowed.
   QueryForInput(input, false, false);
@@ -2301,11 +2423,10 @@ TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
   EXPECT_TRUE(match_inline.allowed_to_be_default_match);
   EXPECT_EQ(url, match_inline.contents);
 
-  // Check the same offset and strings when inline autocompletion is prevented.
+  // Check the same strings when inline autocompletion is prevented.
   QueryForInput(input, true, false);
   AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
   EXPECT_EQ(url, match_prevent.fill_into_edit);
-  EXPECT_TRUE(match_prevent.inline_autocompletion.empty());
   EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
   EXPECT_EQ(url, match_prevent.contents);
 }
@@ -2314,9 +2435,11 @@ TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
 TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
   QueryForInput(ASCIIToUTF16("w"), false, false);
   AutocompleteMatch match(
-      provider_->NavigationToMatch(SearchProvider::NavigationResult(
-          *provider_.get(), GURL("http://www.wow.com"), string16(), false, 0,
-          false)));
+      provider_->NavigationToMatch(SearchSuggestionParser::NavigationResult(
+          ChromeAutocompleteSchemeClassifier(&profile_),
+          GURL("http://www.wow.com"),
+          AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
+          false, 0, false, ASCIIToUTF16("w"), std::string())));
   EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
   EXPECT_TRUE(match.allowed_to_be_default_match);
   EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
@@ -2336,213 +2459,19 @@ TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
             match.contents_class[2].style);
 }
 
-TEST_F(SearchProviderTest, RemoveStaleResultsTest) {
-  // TODO(mpearson): Consider expanding this test to explicitly cover
-  // testing staleness for keyword results.
-  struct {
-    const std::string omnibox_input;
-    const int verbatim_relevance;
-    // These cached suggestions should already be sorted.
-    // The particular number 5 as the length of the array is
-    // unimportant; it's merely enough cached results to fully test
-    // the functioning of RemoveAllStaleResults().
-    struct {
-      const std::string suggestion;
-      const bool is_navigation_result;
-      const int relevance;
-      // |expect_match| is true if this result should survive
-      // RemoveAllStaleResults() filtering against |omnibox_input| below.
-      const bool expect_match;
-    } results[5];
-  } cases[] = {
-    // Simple case: multiple query suggestions and no navsuggestions.
-    // All query suggestions score less than search-what-you-typed and
-    // thus none should be filtered because none will appear first.
-    { "x", 1300,
-      { { "food",         false, 1299, true  },
-        { "foobar",       false, 1298, true  },
-        { "crazy",        false, 1297, true  },
-        { "friend",       false, 1296, true  },
-        { kNotApplicable, false, 0,    false } } },
-
-    // Similarly simple cases, but the query suggestion appears first.
-    { "f", 1200,
-      { { "food",         false, 1299, true  },
-        { "foobar",       false, 1298, true  },
-        { "crazy",        false, 1297, true  },
-        { "friend",       false, 1296, true  },
-        { kNotApplicable, false, 0,    false } } },
-    { "c", 1200,
-      { { "food",         false, 1299, false },
-        { "foobar",       false, 1298, false },
-        { "crazy",        false, 1297, true  },
-        { "friend",       false, 1296, true  },
-        { kNotApplicable, false, 0,    false } } },
-    { "x", 1200,
-      { { "food",         false, 1299, false },
-        { "foobar",       false, 1298, false },
-        { "crazy",        false, 1297, false },
-        { "friend",       false, 1296, false },
-        { kNotApplicable, false, 0,    false } } },
-
-    // The same sort of cases, just using a mix of queries and navsuggestions.
-    { "x", 1300,
-      { { "http://food.com/",   true,  1299, true },
-        { "foobar",             false, 1298, true },
-        { "http://crazy.com/",  true,  1297, true },
-        { "friend",             false, 1296, true },
-        { "http://friend.com/", true,  1295, true } } },
-    { "f", 1200,
-      { { "http://food.com/",   true,  1299, true },
-        { "foobar",             false, 1298, true },
-        { "http://crazy.com/",  true,  1297, true },
-        { "friend",             false, 1296, true },
-        { "http://friend.com/", true,  1295, true } } },
-    { "c", 1200,
-      { { "http://food.com/",   true,  1299, false },
-        { "foobar",             false, 1298, false },
-        { "http://crazy.com/",  true,  1297, true  },
-        { "friend",             false, 1296, true  },
-        { "http://friend.com/", true,  1295, true  } } },
-    { "x", 1200,
-      { { "http://food.com/",   true,  1299, false },
-        { "foobar",             false, 1298, false },
-        { "http://crazy.com/",  true,  1297, false },
-        { "friend",             false, 1296, false },
-        { "http://friend.com/", true,  1295, false } } },
-
-    // Run the three tests immediately above again, just with verbatim
-    // suppressed.  Note that in the last case, all results are filtered.
-    // Because verbatim is also suppressed, SearchProvider will realize
-    // in UpdateMatches() that it needs to restore verbatim to fulfill
-    // its constraints.  This restoration does not happen in
-    // RemoveAllStaleResults() and hence is not tested here.  This restoration
-    // is tested in the DefaultFetcherSuggestRelevance test.
-    { "f", 0,
-      { { "http://food.com/",   true,  1299, true },
-        { "foobar",             false, 1298, true },
-        { "http://crazy.com/",  true,  1297, true },
-        { "friend",             false, 1296, true },
-        { "http://friend.com/", true,  1295, true } } },
-    { "c", 0,
-      { { "http://food.com/",   true,  1299, false },
-        { "foobar",             false, 1298, false },
-        { "http://crazy.com/",  true,  1297, true  },
-        { "friend",             false, 1296, true  },
-        { "http://friend.com/", true,  1295, true  } } },
-    { "x", 0,
-      { { "http://food.com/",   true,  1299, false },
-        { "foobar",             false, 1298, false },
-        { "http://crazy.com/",  true,  1297, false },
-        { "friend",             false, 1296, false },
-        { "http://friend.com/", true,  1295, false } } },
-
-    // The same sort of tests again, just with verbatim with a score
-    // that would place it in between other suggestions.
-    { "f", 1290,
-      { { "http://food.com/",   true,  1299, true },
-        { "foobar",             false, 1288, true },
-        { "http://crazy.com/",  true,  1277, true },
-        { "friend",             false, 1266, true },
-        { "http://friend.com/", true,  1255, true } } },
-    { "c", 1290,
-      { { "http://food.com/",   true,  1299, false },
-        { "foobar",             false, 1288, true  },
-        { "http://crazy.com/",  true,  1277, true  },
-        { "friend",             false, 1266, true  },
-        { "http://friend.com/", true,  1255, true  } } },
-    { "c", 1270,
-      { { "http://food.com/",   true,  1299, false },
-        { "foobar",             false, 1288, false },
-        { "http://crazy.com/",  true,  1277, true  },
-        { "friend",             false, 1266, true  },
-        { "http://friend.com/", true,  1255, true  } } },
-    { "x", 1280,
-      { { "http://food.com/",   true,  1299, false },
-        { "foobar",             false, 1288, false },
-        { "http://crazy.com/",  true,  1277, true  },
-        { "friend",             false, 1266, true  },
-        { "http://friend.com/", true,  1255, true  } } },
-  };
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
-    // Initialize cached results for this test case.
-    provider_->default_results_.verbatim_relevance =
-        cases[i].verbatim_relevance;
-    provider_->default_results_.navigation_results.clear();
-    provider_->default_results_.suggest_results.clear();
-    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
-      const std::string& suggestion = cases[i].results[j].suggestion;
-      if (suggestion == kNotApplicable)
-        break;
-      if (cases[i].results[j].is_navigation_result) {
-        provider_->default_results_.navigation_results.push_back(
-            SearchProvider::NavigationResult(
-                *provider_.get(), GURL(suggestion), string16(), false,
-                cases[i].results[j].relevance, false));
-      } else {
-        provider_->default_results_.suggest_results.push_back(
-            SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), string16(),
-                                          string16(), std::string(), false,
-                                          cases[i].results[j].relevance,
-                                          false, false));
-      }
-    }
-
-    provider_->input_ = AutocompleteInput(
-        ASCIIToUTF16(cases[i].omnibox_input), string16::npos, string16(),
-        GURL(), AutocompleteInput::INVALID_SPEC, false, false, true,
-        AutocompleteInput::ALL_MATCHES);
-    provider_->RemoveAllStaleResults();
-
-    // Check cached results.
-    SearchProvider::SuggestResults::const_iterator sug_it =
-        provider_->default_results_.suggest_results.begin();
-    const SearchProvider::SuggestResults::const_iterator sug_end =
-        provider_->default_results_.suggest_results.end();
-    SearchProvider::NavigationResults::const_iterator nav_it =
-        provider_->default_results_.navigation_results.begin();
-    const SearchProvider::NavigationResults::const_iterator nav_end =
-        provider_->default_results_.navigation_results.end();
-    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
-      const std::string& suggestion = cases[i].results[j].suggestion;
-      if (suggestion == kNotApplicable)
-        continue;
-      if (!cases[i].results[j].expect_match)
-        continue;
-      if (cases[i].results[j].is_navigation_result) {
-        ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion;
-        EXPECT_EQ(suggestion, nav_it->url().spec());
-        ++nav_it;
-      } else {
-        ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion;
-        EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion());
-        ++sug_it;
-      }
-    }
-    EXPECT_EQ(sug_end, sug_it);
-    EXPECT_EQ(nav_end, nav_it);
-  }
-}
-
 #if !defined(OS_WIN)
 // Verify entity suggestion parsing.
 TEST_F(SearchProviderTest, ParseEntitySuggestion) {
   struct Match {
     std::string contents;
+    std::string description;
     std::string query_params;
     std::string fill_into_edit;
     AutocompleteMatchType::Type type;
-    size_t classification_offsets[3];
-    int classification_styles[3];
   };
-  const size_t invalid_offset = 10;
-  const int invalid_style = -1;
   const Match kEmptyMatch = {
-    kNotApplicable, kNotApplicable, kNotApplicable,
-    AutocompleteMatchType::NUM_TYPES,
-    { invalid_offset, invalid_offset, invalid_offset },
-    { invalid_style, invalid_style, invalid_style } };
+    kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable,
+    AutocompleteMatchType::NUM_TYPES};
 
   struct {
     const std::string input_text;
@@ -2551,21 +2480,14 @@ TEST_F(SearchProviderTest, ParseEntitySuggestion) {
   } cases[] = {
     // A query and an entity suggestion with different search terms.
     { "x",
-      "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
+      "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[],"
       " {\"google:suggestdetail\":[{},"
-      "   {\"a\":\"A\",\"dq\":\"yy\",\"q\":\"p=v\"}],"
+      "   {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
       "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
-      { { "x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
-          { 0, invalid_offset, invalid_offset },
-          { ACMatchClassification::NONE, invalid_style, invalid_style } },
-        { "xy", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST,
-          { 0, 1, invalid_offset },
-          { ACMatchClassification::NONE, ACMatchClassification::MATCH,
-            invalid_style } },
-        { "xy - A", "p=v", "yy", AutocompleteMatchType::SEARCH_SUGGEST,
-          { 0, 1, 2 },
-          { ACMatchClassification::NONE, ACMatchClassification::MATCH,
-            ACMatchClassification::DIM } },
+      { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
+        { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
+        { "xy", "A", "p=v", "yy",
+          AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
         kEmptyMatch,
         kEmptyMatch
       },
@@ -2574,19 +2496,12 @@ TEST_F(SearchProviderTest, ParseEntitySuggestion) {
     { "x",
       "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
       " {\"google:suggestdetail\":[{},"
-      "   {\"a\":\"A\",\"dq\":\"xy\",\"q\":\"p=v\"}],"
+      "   {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
       "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
-      { { "x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
-          { 0, invalid_offset, invalid_offset },
-          { ACMatchClassification::NONE, invalid_style, invalid_style } },
-        { "xy", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST,
-          { 0, 1, invalid_offset },
-          { ACMatchClassification::NONE, ACMatchClassification::MATCH,
-            invalid_style } },
-        { "xy - A", "p=v", "xy", AutocompleteMatchType::SEARCH_SUGGEST,
-          { 0, 1, 2 },
-          { ACMatchClassification::NONE, ACMatchClassification::MATCH,
-            ACMatchClassification::DIM } },
+      { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
+        { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
+        { "xy", "A", "p=v", "xy",
+          AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
         kEmptyMatch,
         kEmptyMatch
       },
@@ -2611,37 +2526,27 @@ TEST_F(SearchProviderTest, ParseEntitySuggestion) {
 
     SCOPED_TRACE("for input with json = " + cases[i].response_json);
 
+    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
     size_t j = 0;
     // Ensure that the returned matches equal the expectations.
     for (; j < matches.size(); ++j) {
       const Match& match = cases[i].matches[j];
       SCOPED_TRACE(" and match index: " + base::IntToString(j));
       EXPECT_EQ(match.contents,
-                UTF16ToUTF8(matches[j].contents));
+                base::UTF16ToUTF8(matches[j].contents));
+      EXPECT_EQ(match.description,
+                base::UTF16ToUTF8(matches[j].description));
       EXPECT_EQ(match.query_params,
                 matches[j].search_terms_args->suggest_query_params);
       EXPECT_EQ(match.fill_into_edit,
-                UTF16ToUTF8(matches[j].fill_into_edit));
+                base::UTF16ToUTF8(matches[j].fill_into_edit));
       EXPECT_EQ(match.type, matches[j].type);
-
-      size_t k = 0;
-      for (; k < matches[j].contents_class.size(); k++) {
-        SCOPED_TRACE(" and contents class: " + base::IntToString(k));
-        EXPECT_EQ(match.classification_offsets[k],
-            matches[j].contents_class[k].offset);
-        EXPECT_EQ(match.classification_styles[k],
-            matches[j].contents_class[k].style);
-      }
-      for (; k < ARRAYSIZE_UNSAFE(match.classification_offsets); k++) {
-        SCOPED_TRACE(" and contents class: " + base::IntToString(k));
-        EXPECT_EQ(match.classification_offsets[k], invalid_offset);
-        EXPECT_EQ(match.classification_styles[k], invalid_style);
-      }
     }
     // Ensure that no expected matches are missing.
     for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
       SCOPED_TRACE(" and match index: " + base::IntToString(j));
       EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
+      EXPECT_EQ(cases[i].matches[j].description, kNotApplicable);
       EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
       EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
       EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
@@ -2650,95 +2555,6 @@ TEST_F(SearchProviderTest, ParseEntitySuggestion) {
 }
 #endif  // !defined(OS_WIN)
 
-TEST_F(SearchProviderTest, SearchHistorySuppressesEntitySuggestion) {
-  struct Match {
-    std::string contents;
-    std::string query_params;
-    std::string fill_into_edit;
-    AutocompleteMatchType::Type type;
-  };
-  const Match kEmptyMatch = { kNotApplicable, kNotApplicable, kNotApplicable,
-                              AutocompleteMatchType::NUM_TYPES};
-
-  struct {
-    const std::string input_text;
-    const std::string history_search_term;
-    const std::string response_json;
-    const Match matches[5];
-  } cases[] = {
-    // Search history suppresses both query and entity suggestions.
-    { "x", "xy",
-      "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
-      " {\"google:suggestdetail\":[{},"
-      "   {\"a\":\"A\",\"dq\":\"xy\",\"q\":\"p=v\"}],"
-      "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
-      {
-        {"xy", "", "xy", AutocompleteMatchType::SEARCH_HISTORY},
-        {"x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED},
-        {"xy - A", "p=v", "xy", AutocompleteMatchType::SEARCH_SUGGEST},
-        kEmptyMatch,
-        kEmptyMatch,
-      },
-    },
-    // Search history suppresses only query suggestion.
-    { "x", "xyy",
-      "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
-      " {\"google:suggestdetail\":[{},"
-      "   {\"a\":\"A\",\"dq\":\"xyy\",\"q\":\"p=v\"}],"
-      "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
-      {
-        {"xyy", "", "xyy", AutocompleteMatchType::SEARCH_HISTORY},
-        {"xy", "", "xy", AutocompleteMatchType::SEARCH_HISTORY},
-        {"x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED},
-        {"xy - A", "p=v", "xyy", AutocompleteMatchType::SEARCH_SUGGEST},
-        kEmptyMatch,
-      },
-    }
-  };
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
-    GURL term_url(AddSearchToHistory(
-        default_t_url_, ASCIIToUTF16(cases[i].history_search_term), 10));
-    profile_.BlockUntilHistoryProcessesPendingRequests();
-    QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
-
-    // Set up a default fetcher with provided results.
-    net::TestURLFetcher* fetcher =
-        test_factory_.GetFetcherByID(
-            SearchProvider::kDefaultProviderURLFetcherID);
-    ASSERT_TRUE(fetcher);
-    fetcher->set_response_code(200);
-    fetcher->SetResponseString(cases[i].response_json);
-    fetcher->delegate()->OnURLFetchComplete(fetcher);
-
-    RunTillProviderDone();
-
-    const ACMatches& matches = provider_->matches();
-    ASSERT_FALSE(matches.empty());
-    SCOPED_TRACE("for case: " + base::IntToString(i));
-
-    size_t j = 0;
-    // Ensure that the returned matches equal the expectations.
-    for (; j < matches.size(); ++j) {
-      SCOPED_TRACE(" and match index: " + base::IntToString(j));
-      EXPECT_EQ(cases[i].matches[j].contents,
-                UTF16ToUTF8(matches[j].contents));
-      EXPECT_EQ(cases[i].matches[j].query_params,
-                matches[j].search_terms_args->suggest_query_params);
-      EXPECT_EQ(cases[i].matches[j].fill_into_edit,
-                UTF16ToUTF8(matches[j].fill_into_edit));
-      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
-    }
-    // Ensure that no expected matches are missing.
-    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
-      SCOPED_TRACE(" and match index: " + base::IntToString(j));
-      EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
-      EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
-      EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
-      EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
-    }
-  }
-}
 
 // A basic test that verifies the prefetch metadata parsing logic.
 TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
@@ -2861,10 +2677,12 @@ TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
     ASSERT_FALSE(matches.empty());
     EXPECT_GE(matches[0].relevance, 1300);
 
+    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
     // Ensure that the returned matches equal the expectations.
     for (size_t j = 0; j < matches.size(); ++j) {
       SCOPED_TRACE(description);
-      EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
+      EXPECT_EQ(cases[i].matches[j].contents,
+                base::UTF16ToUTF8(matches[j].contents));
       EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
                 SearchProvider::ShouldPrefetch(matches[j]));
       EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
@@ -2874,15 +2692,42 @@ TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
   }
 }
 
+TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) {
+  ClearAllResults();
+
+  std::string input_str("abc");
+  QueryForInput(ASCIIToUTF16(input_str), false, false);
+
+  // Set up a default fetcher with provided results.
+  net::TestURLFetcher* fetcher =
+      test_factory_.GetFetcherByID(
+          SearchProvider::kDefaultProviderURLFetcherID);
+  ASSERT_TRUE(fetcher);
+  fetcher->set_response_code(200);
+  fetcher->SetResponseString("this is a bad non-json response");
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+
+  RunTillProviderDone();
+
+  const ACMatches& matches = provider_->matches();
+
+  // Should have exactly one "search what you typed" match
+  ASSERT_TRUE(matches.size() == 1);
+  EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents));
+  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
+            matches[0].type);
+}
+
 // A basic test that verifies that the XSSI guarded JSON response is parsed
 // correctly.
-TEST_F(SearchProviderTest, XSSIGuardedJSONParsing) {
+TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) {
   struct Match {
     std::string contents;
     AutocompleteMatchType::Type type;
   };
-  const Match kEmptyMatch = { kNotApplicable,
-                              AutocompleteMatchType::NUM_TYPES};
+  const Match kEmptyMatch = {
+      kNotApplicable, AutocompleteMatchType::NUM_TYPES
+  };
 
   struct {
     const std::string input_text;
@@ -2945,11 +2790,13 @@ TEST_F(SearchProviderTest, XSSIGuardedJSONParsing) {
     EXPECT_GE(matches[0].relevance, 1300);
 
     SCOPED_TRACE("for case: " + base::IntToString(i));
+    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
     size_t j = 0;
     // Ensure that the returned matches equal the expectations.
     for (; j < matches.size(); ++j) {
       SCOPED_TRACE("and match: " + base::IntToString(j));
-      EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
+      EXPECT_EQ(cases[i].matches[j].contents,
+                base::UTF16ToUTF8(matches[j].contents));
       EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
     }
     for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
@@ -2960,10 +2807,114 @@ TEST_F(SearchProviderTest, XSSIGuardedJSONParsing) {
   }
 }
 
+// Test that deletion url gets set on an AutocompleteMatch when available for a
+// personalized query or a personalized URL.
+TEST_F(SearchProviderTest, ParseDeletionUrl) {
+   struct Match {
+     std::string contents;
+     std::string deletion_url;
+     AutocompleteMatchType::Type type;
+   };
+
+   const Match kEmptyMatch = {
+       kNotApplicable, "", AutocompleteMatchType::NUM_TYPES
+   };
+
+   const char* url[] = {
+    "http://defaultturl/complete/deleteitems"
+       "?delq=ab&client=chrome&deltok=xsrf124",
+    "http://defaultturl/complete/deleteitems"
+       "?delq=www.amazon.com&client=chrome&deltok=xsrf123",
+     };
+
+   struct {
+       const std::string input_text;
+       const std::string response_json;
+       const Match matches[5];
+     } cases[] = {
+       // A deletion URL on a personalized query should be reflected in the
+       // resulting AutocompleteMatch.
+       { "a",
+         "[\"a\",[\"ab\", \"ac\",\"www.amazon.com\"],[],[],"
+         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
+          "\"PERSONALIZED_NAVIGATION\"],"
+         "\"google:suggestrelevance\":[3, 2, 1],"
+         "\"google:suggestdetail\":[{\"du\":"
+         "\"/complete/deleteitems?delq=ab&client=chrome"
+          "&deltok=xsrf124\"}, {}, {\"du\":"
+         "\"/complete/deleteitems?delq=www.amazon.com&"
+         "client=chrome&deltok=xsrf123\"}]}]",
+         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
+           { "ab", url[0], AutocompleteMatchType::SEARCH_SUGGEST },
+           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
+           { "www.amazon.com", url[1],
+              AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
+           kEmptyMatch,
+         },
+       },
+       // Personalized queries or a personalized URL without deletion URLs
+       // shouldn't cause errors.
+       { "a",
+         "[\"a\",[\"ab\", \"ac\"],[],[],"
+         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
+         "\"PERSONALIZED_NAVIGATION\"],"
+         "\"google:suggestrelevance\":[1, 2],"
+         "\"google:suggestdetail\":[{}, {}]}]",
+         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
+           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
+           { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
+           { "www.amazon.com", "",
+              AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
+           kEmptyMatch,
+         },
+       },
+       // Personalized queries or a personalized URL without
+       // google:suggestdetail shouldn't cause errors.
+       { "a",
+         "[\"a\",[\"ab\", \"ac\"],[],[],"
+         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
+         "\"PERSONALIZED_NAVIGATION\"],"
+         "\"google:suggestrelevance\":[1, 2]}]",
+         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
+           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
+           { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
+           { "www.amazon.com", "",
+              AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
+           kEmptyMatch,
+         },
+       },
+     };
+
+     for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
+       QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
+
+       net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
+           SearchProvider::kDefaultProviderURLFetcherID);
+       ASSERT_TRUE(fetcher);
+       fetcher->set_response_code(200);
+       fetcher->SetResponseString(cases[i].response_json);
+       fetcher->delegate()->OnURLFetchComplete(fetcher);
+
+       RunTillProviderDone();
+
+       const ACMatches& matches = provider_->matches();
+       ASSERT_FALSE(matches.empty());
+
+       SCOPED_TRACE("for input with json = " + cases[i].response_json);
+
+       for (size_t j = 0; j < matches.size(); ++j) {
+         const Match& match = cases[i].matches[j];
+         SCOPED_TRACE(" and match index: " + base::IntToString(j));
+         EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents));
+         EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo(
+             "deletion_url"));
+       }
+     }
+}
 
 TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
   profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false);
-  string16 term = term1_.substr(0, term1_.length() - 1);
+  base::string16 term = term1_.substr(0, term1_.length() - 1);
   QueryForInput(term, true, false);
   ASSERT_FALSE(provider_->matches().empty());
   EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
@@ -2980,3 +2931,347 @@ TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
   ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
   EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
 }
+
+TEST_F(SearchProviderTest, CanSendURL) {
+  TemplateURLData template_url_data;
+  template_url_data.short_name = ASCIIToUTF16("t");
+  template_url_data.SetURL("http://www.google.com/{searchTerms}");
+  template_url_data.suggestions_url = "http://www.google.com/{searchTerms}";
+  template_url_data.instant_url = "http://does/not/exist?strk=1";
+  template_url_data.search_terms_replacement_key = "strk";
+  template_url_data.id = SEARCH_ENGINE_GOOGLE;
+  TemplateURL google_template_url(template_url_data);
+
+  // Create field trial.
+  CreateZeroSuggestFieldTrial(true);
+
+  // Not signed in.
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+  SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_);
+  signin->SetAuthenticatedUsername("test");
+
+  // All conditions should be met.
+  EXPECT_TRUE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+
+  // Not in field trial.
+  ResetFieldTrialList();
+  CreateZeroSuggestFieldTrial(false);
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+  ResetFieldTrialList();
+  CreateZeroSuggestFieldTrial(true);
+
+  // Invalid page URL.
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("badpageurl"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+
+  // Invalid page classification.
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
+      SearchTermsData(), &profile_));
+
+  // Invalid page classification.
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
+      SearchTermsData(), &profile_));
+
+  // HTTPS page URL on same domain as provider.
+  EXPECT_TRUE(SearchProvider::CanSendURL(
+      GURL("https://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"),
+      &google_template_url, metrics::OmniboxEventProto::OTHER,
+      SearchTermsData(), &profile_));
+
+  // Non-HTTP[S] page URL on same domain as provider.
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("ftp://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+
+  // Non-HTTP page URL on different domain.
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("https://www.notgoogle.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+
+  // Non-HTTPS provider.
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("http://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+
+  // Suggest disabled.
+  profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false);
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+  profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true);
+
+  // Incognito.
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(),
+      profile_.GetOffTheRecordProfile()));
+
+  // Tab sync not enabled.
+  profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncKeepEverythingSynced,
+                                  false);
+  profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, false);
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+  profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, true);
+
+  // Tab sync is encrypted.
+  ProfileSyncService* service =
+      ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_);
+  syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes();
+  encrypted_types.Put(syncer::SESSIONS);
+  service->OnEncryptedTypesChanged(encrypted_types, false);
+  EXPECT_FALSE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+  encrypted_types.Remove(syncer::SESSIONS);
+  service->OnEncryptedTypesChanged(encrypted_types, false);
+
+  // Check that there were no side effects from previous tests.
+  EXPECT_TRUE(SearchProvider::CanSendURL(
+      GURL("http://www.google.com/search"),
+      GURL("https://www.google.com/complete/search"), &google_template_url,
+      metrics::OmniboxEventProto::OTHER, SearchTermsData(), &profile_));
+}
+
+TEST_F(SearchProviderTest, TestDeleteMatch) {
+  AutocompleteMatch match(provider_, 0, true,
+                          AutocompleteMatchType::SEARCH_SUGGEST);
+  match.RecordAdditionalInfo(
+      SearchProvider::kDeletionUrlKey,
+      "https://www.google.com/complete/deleteitem?q=foo");
+
+  // Test a successful deletion request.
+  provider_->matches_.push_back(match);
+  provider_->DeleteMatch(match);
+  EXPECT_FALSE(provider_->deletion_handlers_.empty());
+  EXPECT_TRUE(provider_->matches_.empty());
+  // Set up a default fetcher with provided results.
+  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
+      SearchProvider::kDeletionURLFetcherID);
+  ASSERT_TRUE(fetcher);
+  fetcher->set_response_code(200);
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_TRUE(provider_->deletion_handlers_.empty());
+  EXPECT_TRUE(provider_->is_success());
+
+  // Test a failing deletion request.
+  provider_->matches_.push_back(match);
+  provider_->DeleteMatch(match);
+  EXPECT_FALSE(provider_->deletion_handlers_.empty());
+  // Set up a default fetcher with provided results.
+  fetcher = test_factory_.GetFetcherByID(
+      SearchProvider::kDeletionURLFetcherID);
+  ASSERT_TRUE(fetcher);
+  fetcher->set_response_code(500);
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_TRUE(provider_->deletion_handlers_.empty());
+  EXPECT_FALSE(provider_->is_success());
+}
+
+TEST_F(SearchProviderTest, TestDeleteHistoryQueryMatch) {
+  GURL term_url(
+      AddSearchToHistory(default_t_url_, ASCIIToUTF16("flash games"), 1));
+  profile_.BlockUntilHistoryProcessesPendingRequests();
+
+  AutocompleteMatch games;
+  QueryForInput(ASCIIToUTF16("fla"), false, false);
+  profile_.BlockUntilHistoryProcessesPendingRequests();
+  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
+  ASSERT_TRUE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
+
+  size_t matches_before = provider_->matches().size();
+  provider_->DeleteMatch(games);
+  EXPECT_EQ(matches_before - 1, provider_->matches().size());
+
+  // Process history deletions.
+  profile_.BlockUntilHistoryProcessesPendingRequests();
+
+  // Check that the match is gone.
+  QueryForInput(ASCIIToUTF16("fla"), false, false);
+  profile_.BlockUntilHistoryProcessesPendingRequests();
+  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
+  EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
+}
+
+// Verifies that duplicates are preserved in AddMatchToMap().
+TEST_F(SearchProviderTest, CheckDuplicateMatchesSaved) {
+  AddSearchToHistory(default_t_url_, ASCIIToUTF16("a"), 1);
+  AddSearchToHistory(default_t_url_, ASCIIToUTF16("alpha"), 1);
+  AddSearchToHistory(default_t_url_, ASCIIToUTF16("avid"), 1);
+
+  profile_.BlockUntilHistoryProcessesPendingRequests();
+  QueryForInput(ASCIIToUTF16("a"), false, false);
+
+  // Make sure the default provider's suggest service was queried.
+  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
+      SearchProvider::kDefaultProviderURLFetcherID);
+  ASSERT_TRUE(fetcher);
+
+  // Tell the SearchProvider the suggest query is done.
+  fetcher->set_response_code(200);
+  fetcher->SetResponseString(
+      "[\"a\",[\"a\", \"alpha\", \"avid\", \"apricot\"],[],[],"
+      "{\"google:suggestrelevance\":[1450, 1200, 1150, 1100],"
+      "\"google:verbatimrelevance\":1350}]");
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  fetcher = NULL;
+
+  // Run till the history results complete.
+  RunTillProviderDone();
+
+  AutocompleteMatch verbatim, match_alpha, match_apricot, match_avid;
+  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
+  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("alpha"), &match_alpha));
+  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("apricot"), &match_apricot));
+  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("avid"), &match_avid));
+
+  // Verbatim match duplicates are added such that each one has a higher
+  // relevance than the previous one.
+  EXPECT_EQ(2U, verbatim.duplicate_matches.size());
+
+  // Other match duplicates are added in descending relevance order.
+  EXPECT_EQ(1U, match_alpha.duplicate_matches.size());
+  EXPECT_EQ(1U, match_avid.duplicate_matches.size());
+
+  EXPECT_EQ(0U, match_apricot.duplicate_matches.size());
+}
+
+TEST_F(SearchProviderTest, SuggestQueryUsesToken) {
+  CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kEnableAnswersInSuggest);
+
+  TemplateURLService* turl_model =
+      TemplateURLServiceFactory::GetForProfile(&profile_);
+
+  TemplateURLData data;
+  data.short_name = ASCIIToUTF16("default");
+  data.SetKeyword(data.short_name);
+  data.SetURL("http://example/{searchTerms}{google:sessionToken}");
+  data.suggestions_url =
+      "http://suggest/?q={searchTerms}&{google:sessionToken}";
+  default_t_url_ = new TemplateURL(data);
+  turl_model->Add(default_t_url_);
+  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
+
+  base::string16 term = term1_.substr(0, term1_.length() - 1);
+  QueryForInput(term, false, false);
+
+  // Make sure the default provider's suggest service was queried.
+  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
+      SearchProvider::kDefaultProviderURLFetcherID);
+  ASSERT_TRUE(fetcher);
+
+  // And the URL matches what we expected.
+  TemplateURLRef::SearchTermsArgs search_terms_args(term);
+  search_terms_args.session_token = provider_->current_token_;
+  GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
+      search_terms_args, turl_model->search_terms_data()));
+  EXPECT_EQ(fetcher->GetOriginalURL().spec(), expected_url.spec());
+
+  // Complete running the fetcher to clean up.
+  fetcher->set_response_code(200);
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  RunTillProviderDone();
+}
+
+TEST_F(SearchProviderTest, SessionToken) {
+  // Subsequent calls always get the same token.
+  std::string token = provider_->GetSessionToken();
+  std::string token2 = provider_->GetSessionToken();
+  EXPECT_EQ(token, token2);
+  EXPECT_FALSE(token.empty());
+
+  // Calls do not regenerate a token.
+  provider_->current_token_ = "PRE-EXISTING TOKEN";
+  token = provider_->GetSessionToken();
+  EXPECT_EQ(token, "PRE-EXISTING TOKEN");
+
+  // ... unless the token has expired.
+  provider_->current_token_.clear();
+  const base::TimeDelta kSmallDelta = base::TimeDelta::FromMilliseconds(1);
+  provider_->token_expiration_time_ = base::TimeTicks::Now() - kSmallDelta;
+  token = provider_->GetSessionToken();
+  EXPECT_FALSE(token.empty());
+  EXPECT_EQ(token, provider_->current_token_);
+
+  // The expiration time is always updated.
+  provider_->GetSessionToken();
+  base::TimeTicks expiration_time_1 = provider_->token_expiration_time_;
+  base::PlatformThread::Sleep(kSmallDelta);
+  provider_->GetSessionToken();
+  base::TimeTicks expiration_time_2 = provider_->token_expiration_time_;
+  EXPECT_GT(expiration_time_2, expiration_time_1);
+  EXPECT_GE(expiration_time_2, expiration_time_1 + kSmallDelta);
+}
+
+TEST_F(SearchProviderTest, AnswersCache) {
+  // Initial condition: empty cache.
+  ASSERT_TRUE(provider_->last_answer_seen_.full_query_text.empty());
+
+  AutocompleteResult result;
+  ACMatches matches;
+  AutocompleteMatch match1;
+  match1.answer_contents = base::ASCIIToUTF16("m1");
+  match1.answer_type = base::ASCIIToUTF16("2334");
+  match1.fill_into_edit = base::ASCIIToUTF16("weather los angeles");
+
+  AutocompleteMatch non_answer_match1;
+  non_answer_match1.fill_into_edit = base::ASCIIToUTF16("weather laguna beach");
+
+  // Test that an answer in the first slot populates the cache.
+  matches.push_back(match1);
+  matches.push_back(non_answer_match1);
+  result.AppendMatches(matches);
+  provider_->RegisterDisplayedAnswers(result);
+  EXPECT_EQ(base::ASCIIToUTF16("weather los angeles"),
+            provider_->last_answer_seen_.full_query_text);
+  EXPECT_EQ(base::ASCIIToUTF16("2334"),
+            provider_->last_answer_seen_.query_type);
+
+  // Test that DoAnswersQuery retrieves data from cache.
+  AutocompleteInput input(base::ASCIIToUTF16("weather l"),
+                          base::string16::npos, base::string16(), GURL(),
+                          metrics::OmniboxEventProto::INVALID_SPEC, false,
+                          false, true, true,
+                          ChromeAutocompleteSchemeClassifier(&profile_));
+  provider_->DoAnswersQuery(input);
+  EXPECT_EQ(base::ASCIIToUTF16("weather los angeles"),
+            provider_->prefetch_data_.full_query_text);
+  EXPECT_EQ(base::ASCIIToUTF16("2334"), provider_->prefetch_data_.query_type);
+
+  // Mismatching input will return empty prefetch data.
+  AutocompleteInput input2(base::ASCIIToUTF16("weather n"),
+                           base::string16::npos, base::string16(), GURL(),
+                           metrics::OmniboxEventProto::INVALID_SPEC, false,
+                           false, true, true,
+                           ChromeAutocompleteSchemeClassifier(&profile_));
+  provider_->DoAnswersQuery(input2);
+  EXPECT_TRUE(provider_->prefetch_data_.full_query_text.empty());
+  EXPECT_TRUE(provider_->prefetch_data_.query_type.empty());
+}