Allow one to call TemplateURLModel::GenerateSearchURL when not on the UI thread.

BUG=38475
TEST=unit_test --gtest_filter=Temp*

Review URL: http://codereview.chromium.org/3240005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57918 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/search_engines/search_host_to_urls_map_unittest.cc b/chrome/browser/search_engines/search_host_to_urls_map_unittest.cc
index 52829d46..7eb6daf 100644
--- a/chrome/browser/search_engines/search_host_to_urls_map_unittest.cc
+++ b/chrome/browser/search_engines/search_host_to_urls_map_unittest.cc
@@ -16,14 +16,12 @@
 
   virtual void SetUp();
   virtual void TearDown() {
-    delete TemplateURLRef::google_base_url_;
-    TemplateURLRef::google_base_url_ = NULL;
+    TemplateURLRef::SetGoogleBaseURL(NULL);
   }
 
  protected:
   void SetGoogleBaseURL(const std::string& base_url) const {
-    delete TemplateURLRef::google_base_url_;
-    TemplateURLRef::google_base_url_ = new std::string(base_url);
+    TemplateURLRef::SetGoogleBaseURL(new std::string(base_url));
   }
 
   void VerifyDefault(const std::string& origin) {
diff --git a/chrome/browser/search_engines/search_terms_data.cc b/chrome/browser/search_engines/search_terms_data.cc
new file mode 100644
index 0000000..831a8785
--- /dev/null
+++ b/chrome/browser/search_engines/search_terms_data.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/search_engines/search_terms_data.h"
+
+#include "base/logging.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/google_url_tracker.h"
+#include "googleurl/src/gurl.h"
+
+#if defined(OS_WIN)
+#include "chrome/browser/rlz/rlz.h"
+#endif
+
+SearchTermsData::SearchTermsData() {
+}
+
+SearchTermsData::~SearchTermsData() {
+}
+
+std::string SearchTermsData::GoogleBaseSuggestURLValue() const {
+  // The suggest base URL we want at the end is something like
+  // "http://clients1.google.TLD/complete/".  The key bit we want from the
+  // original Google base URL is the TLD.
+
+  // Start with the Google base URL.
+  const GURL base_url(GoogleBaseURLValue());
+  DCHECK(base_url.is_valid());
+
+  // Change "www." to "clients1." in the hostname.  If no "www." was found, just
+  // prepend "clients1.".
+  const std::string base_host(base_url.host());
+  GURL::Replacements repl;
+  const std::string suggest_host("clients1." +
+      (base_host.compare(0, 4, "www.") ? base_host : base_host.substr(4)));
+  repl.SetHostStr(suggest_host);
+
+  // Replace any existing path with "/complete/".
+  static const std::string suggest_path("/complete/");
+  repl.SetPathStr(suggest_path);
+
+  // Clear the query and ref.
+  repl.ClearQuery();
+  repl.ClearRef();
+  return base_url.ReplaceComponents(repl).spec();
+}
+
+// static
+std::string* UIThreadSearchTermsData::google_base_url_ = NULL;
+
+UIThreadSearchTermsData::UIThreadSearchTermsData() {
+  // GoogleURLTracker::GoogleURL() DCHECKs this also, but adding it here helps
+  // us catch bad behavior at a more common place in this code.
+  DCHECK(!ChromeThread::IsWellKnownThread(ChromeThread::UI) ||
+         ChromeThread::CurrentlyOn(ChromeThread::UI));
+}
+
+std::string UIThreadSearchTermsData::GoogleBaseURLValue() const {
+  DCHECK(!ChromeThread::IsWellKnownThread(ChromeThread::UI) ||
+         ChromeThread::CurrentlyOn(ChromeThread::UI));
+  return google_base_url_ ?
+    (*google_base_url_) : GoogleURLTracker::GoogleURL().spec();
+}
+
+std::string UIThreadSearchTermsData::GetApplicationLocale() const {
+  DCHECK(!ChromeThread::IsWellKnownThread(ChromeThread::UI) ||
+         ChromeThread::CurrentlyOn(ChromeThread::UI));
+  return g_browser_process->GetApplicationLocale();
+}
+
+#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
+std::wstring UIThreadSearchTermsData::GetRlzParameterValue() const {
+  DCHECK(!ChromeThread::IsWellKnownThread(ChromeThread::UI) ||
+         ChromeThread::CurrentlyOn(ChromeThread::UI));
+  std::wstring rlz_string;
+  RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz_string);
+  return rlz_string;
+}
+#endif
+
+// static
+void UIThreadSearchTermsData::SetGoogleBaseURL(std::string* google_base_url) {
+  delete google_base_url_;
+  google_base_url_ = google_base_url;
+}
diff --git a/chrome/browser/search_engines/search_terms_data.h b/chrome/browser/search_engines/search_terms_data.h
new file mode 100644
index 0000000..41807c3
--- /dev/null
+++ b/chrome/browser/search_engines/search_terms_data.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SEARCH_ENGINES_SEARCH_TERMS_DATA_H_
+#define CHROME_BROWSER_SEARCH_ENGINES_SEARCH_TERMS_DATA_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+
+// All data needed by TemplateURLRef::ReplaceSearchTerms which typically may
+// only be accessed on the UI thread.
+class SearchTermsData {
+ public:
+  SearchTermsData();
+  virtual ~SearchTermsData();
+
+  // Returns the value for the GOOGLE_BASE_SUGGEST_URL term.
+  std::string GoogleBaseSuggestURLValue() const;
+
+  // Returns the value to use for replacements of type GOOGLE_BASE_URL.
+  virtual std::string GoogleBaseURLValue() const = 0;
+
+  // Returns the locale used by the application.
+  virtual std::string GetApplicationLocale() const = 0;
+
+#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
+  // Returns the value for the Chrome Omnibox rlz.
+  virtual std::wstring GetRlzParameterValue() const = 0;
+#endif
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SearchTermsData);
+};
+
+// Implementation of SearchTermsData that is only usable on the UI thread.
+class UIThreadSearchTermsData : public SearchTermsData {
+ public:
+  UIThreadSearchTermsData();
+
+  // Implementation of SearchTermsData.
+  virtual std::string GoogleBaseURLValue() const;
+  virtual std::string GetApplicationLocale() const;
+#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
+  virtual std::wstring GetRlzParameterValue() const;
+#endif
+
+  // Used by tests to set the value for the Google base url. This takes
+  // ownership of the given std::string.
+  static void SetGoogleBaseURL(std::string* google_base_url);
+
+ private:
+  static std::string* google_base_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(UIThreadSearchTermsData);
+};
+
+#endif  // CHROME_BROWSER_SEARCH_ENGINES_SEARCH_TERMS_DATA_H_
diff --git a/chrome/browser/search_engines/template_url.cc b/chrome/browser/search_engines/template_url.cc
index 98d80fb..78a81636 100644
--- a/chrome/browser/search_engines/template_url.cc
+++ b/chrome/browser/search_engines/template_url.cc
@@ -10,18 +10,12 @@
 #include "base/logging.h"
 #include "base/string_number_conversions.h"
 #include "base/utf_string_conversions.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_thread.h"
-#include "chrome/browser/google_url_tracker.h"
+#include "chrome/browser/search_engines/search_terms_data.h"
 #include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/common/url_constants.h"
 #include "gfx/favicon_size.h"
 #include "net/base/escape.h"
 
-#if defined(OS_WIN)
-#include "chrome/browser/rlz/rlz.h"
-#endif
-
 // The TemplateURLRef has any number of terms that need to be replaced. Each of
 // the terms is enclosed in braces. If the character preceeding the final
 // brace is a ?, it indicates the term is optional and can be replaced with
@@ -72,9 +66,6 @@
 // Used if the parameter kOutputEncodingParameter is required.
 static const char kOutputEncodingType[] = "UTF-8";
 
-// static
-std::string* TemplateURLRef::google_base_url_ = NULL;
-
 TemplateURLRef::TemplateURLRef() {
   Set(std::string(), 0, 0);
 }
@@ -221,13 +212,15 @@
 }
 
 void TemplateURLRef::ParseHostAndSearchTermKey() const {
+  UIThreadSearchTermsData search_terms_data;
+
   std::string url_string = url_;
   ReplaceSubstringsAfterOffset(&url_string, 0,
                                kGoogleBaseURLParameterFull,
-                               GoogleBaseURLValue());
+                               search_terms_data.GoogleBaseURLValue());
   ReplaceSubstringsAfterOffset(&url_string, 0,
                                kGoogleBaseSuggestURLParameterFull,
-                               GoogleBaseSuggestURLValue());
+                               search_terms_data.GoogleBaseSuggestURLValue());
 
   GURL url(url_string);
   if (!url.is_valid())
@@ -256,17 +249,30 @@
   }
 }
 
+// static
+void TemplateURLRef::SetGoogleBaseURL(std::string* google_base_url) {
+  UIThreadSearchTermsData::SetGoogleBaseURL(google_base_url);
+}
+
 std::string TemplateURLRef::ReplaceSearchTerms(
     const TemplateURL& host,
     const std::wstring& terms,
     int accepted_suggestion,
     const std::wstring& original_query_for_suggestion) const {
-  // GoogleBaseURLValue() enforces this, but this assert helps us catch bad
-  // behavior more frequently (instead of only when there is a GoogleBaseURL
-  // component in the passed in host).
-  DCHECK(!ChromeThread::IsWellKnownThread(ChromeThread::UI) ||
-         ChromeThread::CurrentlyOn(ChromeThread::UI));
+  UIThreadSearchTermsData search_terms_data;
+  return ReplaceSearchTermsUsingTermsData(host,
+                                          terms,
+                                          accepted_suggestion,
+                                          original_query_for_suggestion,
+                                          search_terms_data);
+}
 
+std::string TemplateURLRef::ReplaceSearchTermsUsingTermsData(
+    const TemplateURL& host,
+    const std::wstring& terms,
+    int accepted_suggestion,
+    const std::wstring& original_query_for_suggestion,
+    const SearchTermsData& search_terms_data) const {
   ParseIfNecessary();
   if (!valid_)
     return std::string();
@@ -342,11 +348,11 @@
         break;
 
       case GOOGLE_BASE_URL:
-        url.insert(i->index, GoogleBaseURLValue());
+        url.insert(i->index, search_terms_data.GoogleBaseURLValue());
         break;
 
       case GOOGLE_BASE_SUGGEST_URL:
-        url.insert(i->index, GoogleBaseSuggestURLValue());
+        url.insert(i->index, search_terms_data.GoogleBaseSuggestURLValue());
         break;
 
       case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION:
@@ -361,8 +367,7 @@
         // empty string.  (If we don't handle this case, we hit a
         // NOTREACHED below.)
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
-        std::wstring rlz_string;
-        RLZTracker::GetAccessPointRlz(rlz_lib::CHROME_OMNIBOX, &rlz_string);
+        std::wstring rlz_string = search_terms_data.GetRlzParameterValue();
         if (!rlz_string.empty()) {
           rlz_string = L"rlz=" + rlz_string + L"&";
           url.insert(i->index, WideToUTF8(rlz_string));
@@ -382,7 +387,7 @@
       }
 
       case LANGUAGE:
-        url.insert(i->index, g_browser_process->GetApplicationLocale());
+        url.insert(i->index, search_terms_data.GetApplicationLocale());
         break;
 
       case SEARCH_TERMS:
@@ -499,53 +504,6 @@
   replacements_.clear();
 }
 
-// Returns the value to use for replacements of type GOOGLE_BASE_URL.
-// static
-std::string TemplateURLRef::GoogleBaseURLValue() {
-  // Normally GoogleURLTracker::GoogleURL() enforces this, but this
-  // assert helps us catch bad behavior at unit tests time.
-  DCHECK(!ChromeThread::IsWellKnownThread(ChromeThread::UI) ||
-         ChromeThread::CurrentlyOn(ChromeThread::UI));
-
-  return google_base_url_ ?
-    (*google_base_url_) : GoogleURLTracker::GoogleURL().spec();
-}
-
-// Returns the value to use for replacements of type GOOGLE_BASE_SUGGEST_URL.
-// static
-std::string TemplateURLRef::GoogleBaseSuggestURLValue() {
-  // Normally GoogleURLTracker::GoogleURL() enforces this, but this
-  // assert helps us catch bad behavior at unit tests time.
-  DCHECK(!ChromeThread::IsWellKnownThread(ChromeThread::UI) ||
-         ChromeThread::CurrentlyOn(ChromeThread::UI));
-
-  // The suggest base URL we want at the end is something like
-  // "http://clients1.google.TLD/complete/".  The key bit we want from the
-  // original Google base URL is the TLD.
-
-  // Start with the Google base URL.
-  const GURL base_url(google_base_url_ ?
-      GURL(*google_base_url_) : GoogleURLTracker::GoogleURL());
-  DCHECK(base_url.is_valid());
-
-  // Change "www." to "clients1." in the hostname.  If no "www." was found, just
-  // prepend "clients1.".
-  const std::string base_host(base_url.host());
-  GURL::Replacements repl;
-  const std::string suggest_host("clients1." +
-      (base_host.compare(0, 4, "www.") ? base_host : base_host.substr(4)));
-  repl.SetHostStr(suggest_host);
-
-  // Replace any existing path with "/complete/".
-  static const std::string suggest_path("/complete/");
-  repl.SetPathStr(suggest_path);
-
-  // Clear the query and ref.
-  repl.ClearQuery();
-  repl.ClearRef();
-  return base_url.ReplaceComponents(repl).spec();
-}
-
 // TemplateURL ----------------------------------------------------------------
 
 // static
diff --git a/chrome/browser/search_engines/template_url.h b/chrome/browser/search_engines/template_url.h
index fbcdd8b..7c061c4 100644
--- a/chrome/browser/search_engines/template_url.h
+++ b/chrome/browser/search_engines/template_url.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
 #include "googleurl/src/gurl.h"
 
+class SearchTermsData;
 class TemplateURL;
 class WebDataService;
 struct WDKeywordsResult;
@@ -69,6 +70,16 @@
       int accepted_suggestion,
       const std::wstring& original_query_for_suggestion) const;
 
+  // Just like ReplaceSearchTerms except that it takes SearchTermsData to supply
+  // the data for some search terms. Most of the time ReplaceSearchTerms should
+  // be called.
+  std::string ReplaceSearchTermsUsingTermsData(
+      const TemplateURL& host,
+      const std::wstring& terms,
+      int accepted_suggestion,
+      const std::wstring& original_query_for_suggestion,
+      const SearchTermsData& search_terms_data) const;
+
   // Returns the raw URL. None of the parameters will have been replaced.
   const std::string& url() const { return url_; }
 
@@ -183,11 +194,9 @@
   // Extracts the query key and host from the url.
   void ParseHostAndSearchTermKey() const;
 
-  // Returns the value for the GOOGLE_BASE_URL term.
-  static std::string GoogleBaseURLValue();
-
-  // Returns the value for the GOOGLE_BASE_SUGGEST_URL term.
-  static std::string GoogleBaseSuggestURLValue();
+  // Used by tests to set the value for the Google base url. This takes
+  // ownership of the given std::string.
+  static void SetGoogleBaseURL(std::string* google_base_url);
 
   // The raw URL. Where as this contains all the terms (such as {searchTerms}),
   // parsed_url_ has them all stripped out.
@@ -221,10 +230,6 @@
   mutable std::string host_;
   mutable std::string path_;
   mutable std::string search_term_key_;
-
-  // For testing. If non-null this is the replacement value for GOOGLE_BASE_URL
-  // terms.
-  static std::string* google_base_url_;
 };
 
 // Describes the relevant portions of a single OSD document.
diff --git a/chrome/browser/search_engines/template_url_model.cc b/chrome/browser/search_engines/template_url_model.cc
index fca7941..47c782d 100644
--- a/chrome/browser/search_engines/template_url_model.cc
+++ b/chrome/browser/search_engines/template_url_model.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/profile.h"
 #include "chrome/browser/rlz/rlz.h"
 #include "chrome/browser/search_engines/search_host_to_urls_map.h"
+#include "chrome/browser/search_engines/search_terms_data.h"
 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
 #include "chrome/browser/search_engines/util.h"
 #include "chrome/common/chrome_switches.h"
@@ -148,6 +149,15 @@
 // static
 GURL TemplateURLModel::GenerateSearchURL(const TemplateURL* t_url) {
   DCHECK(t_url);
+  UIThreadSearchTermsData search_terms_data;
+  return GenerateSearchURLWithTermsData(t_url, search_terms_data);
+}
+
+// static
+GURL TemplateURLModel::GenerateSearchURLWithTermsData(
+    const TemplateURL* t_url,
+    const SearchTermsData& search_terms_data) {
+  DCHECK(t_url);
   const TemplateURLRef* search_ref = t_url->url();
   // Extension keywords don't have host-based search URLs.
   if (!search_ref || !search_ref->IsValid() || t_url->IsExtensionKeyword())
diff --git a/chrome/browser/search_engines/template_url_model.h b/chrome/browser/search_engines/template_url_model.h
index 8e18279b..d940cbd 100644
--- a/chrome/browser/search_engines/template_url_model.h
+++ b/chrome/browser/search_engines/template_url_model.h
@@ -20,6 +20,7 @@
 class PrefService;
 class Profile;
 class SearchHostToURLsMap;
+class SearchTermsData;
 
 namespace history {
 struct URLVisitedDetails;
@@ -90,6 +91,13 @@
   // url().
   static GURL GenerateSearchURL(const TemplateURL* t_url);
 
+  // Just like GenerateSearchURL except that it takes SearchTermsData to supply
+  // the data for some search terms. Most of the time GenerateSearchURL should
+  // be called.
+  static GURL GenerateSearchURLWithTermsData(
+      const TemplateURL* t_url,
+      const SearchTermsData& search_terms_data);
+
   // Returns true if there is no TemplateURL that conflicts with the
   // keyword/url pair, or there is one but it can be replaced. If there is an
   // existing keyword that can be replaced and template_url_to_replace is
diff --git a/chrome/browser/search_engines/template_url_model_unittest.cc b/chrome/browser/search_engines/template_url_model_unittest.cc
index 4d1487f..95a18ad5 100644
--- a/chrome/browser/search_engines/template_url_model_unittest.cc
+++ b/chrome/browser/search_engines/template_url_model_unittest.cc
@@ -147,8 +147,7 @@
 
   virtual void TearDown() {
     profile_->TearDown();
-    delete TemplateURLRef::google_base_url_;
-    TemplateURLRef::google_base_url_ = NULL;
+    TemplateURLRef::SetGoogleBaseURL(NULL);
 
     // Flush the message loop to make Purify happy.
     message_loop_.RunAllPending();
@@ -236,8 +235,7 @@
   }
 
   void SetGoogleBaseURL(const std::string& base_url) const {
-    delete TemplateURLRef::google_base_url_;
-    TemplateURLRef::google_base_url_ = new std::string(base_url);
+    TemplateURLRef::SetGoogleBaseURL(new std::string(base_url));
   }
 
   // Creates a TemplateURL with the same prepopluated id as a real prepopulated
@@ -251,7 +249,7 @@
   // Since the input is the offset from the default, when one passes in
   // 0, it tests the default. Passing in a number > 0 will verify what
   // happens when a preloaded url that is not the default gets updated.
-  void TestLoadUpdatingPreloadedUrl(size_t index_offset_from_default);
+  void TestLoadUpdatingPreloadedURL(size_t index_offset_from_default);
 
   MessageLoopForUI message_loop_;
   // Needed to make the DeleteOnUIThread trait of WebDataService work
@@ -283,7 +281,7 @@
   return t_url;
 }
 
-void TemplateURLModelTest::TestLoadUpdatingPreloadedUrl(
+void TemplateURLModelTest::TestLoadUpdatingPreloadedURL(
     size_t index_offset_from_default) {
   std::wstring prepopulated_url;
   TemplateURL* t_url = CreateReplaceablePreloadedTemplateURL(
@@ -940,14 +938,14 @@
 
 // Make sure that the load routine updates the url of a preexisting
 // default search engine provider and that the result is saved correctly.
-TEST_F(TemplateURLModelTest, LoadUpdatesDefaultSearchUrl) {
-  TestLoadUpdatingPreloadedUrl(0);
+TEST_F(TemplateURLModelTest, LoadUpdatesDefaultSearchURL) {
+  TestLoadUpdatingPreloadedURL(0);
 }
 
 // Make sure that the load routine updates the url of a preexisting
 // non-default search engine provider and that the result is saved correctly.
-TEST_F(TemplateURLModelTest, LoadUpdatesSearchUrl) {
-  TestLoadUpdatingPreloadedUrl(1);
+TEST_F(TemplateURLModelTest, LoadUpdatesSearchURL) {
+  TestLoadUpdatingPreloadedURL(1);
 }
 
 // Make sure that the load does update of auto-keywords correctly.
diff --git a/chrome/browser/search_engines/template_url_unittest.cc b/chrome/browser/search_engines/template_url_unittest.cc
index 05087b8..9e48cd37 100644
--- a/chrome/browser/search_engines/template_url_unittest.cc
+++ b/chrome/browser/search_engines/template_url_unittest.cc
@@ -7,22 +7,49 @@
 #include "base/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/rlz/rlz.h"
+#include "chrome/browser/search_engines/search_terms_data.h"
 #include "chrome/browser/search_engines/template_url.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+// Simple implementation of SearchTermsData.
+class TestSearchTermsData : public SearchTermsData {
+ public:
+  explicit TestSearchTermsData(const char* google_base_url)
+      : google_base_url_(google_base_url)  {
+  }
+
+  virtual std::string GoogleBaseURLValue() const {
+    return google_base_url_;
+  }
+
+  virtual std::string GetApplicationLocale() const {
+    return "yy";
+  }
+
+#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
+  // Returns the value for the Chrome Omnibox rlz.
+  virtual std::wstring GetRlzParameterValue() const {
+    return "";
+  }
+#endif
+
+ private:
+  std::string google_base_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSearchTermsData);
+};
+
 class TemplateURLTest : public testing::Test {
  public:
   virtual void TearDown() {
-    delete TemplateURLRef::google_base_url_;
-    TemplateURLRef::google_base_url_ = NULL;
+    TemplateURLRef::SetGoogleBaseURL(NULL);
   }
 
   void CheckSuggestBaseURL(const char* base_url,
                            const char* base_suggest_url) const {
-    delete TemplateURLRef::google_base_url_;
-    TemplateURLRef::google_base_url_ = new std::string(base_url);
+    TestSearchTermsData search_terms_data(base_url);
     EXPECT_STREQ(base_suggest_url,
-                 TemplateURLRef::GoogleBaseSuggestURLValue().c_str());
+                 search_terms_data.GoogleBaseSuggestURLValue().c_str());
   }
 };
 
@@ -147,6 +174,35 @@
   ASSERT_EQ("http://fooxxutf-8yutf-8a/", result.spec());
 }
 
+TEST_F(TemplateURLTest, URLRefTestSearchTermsUsingTermsData) {
+  struct SearchTermsCase {
+    const char* url;
+    const wchar_t* terms;
+    const char* output;
+  } search_term_cases[] = {
+    { "{google:baseURL}{language}{searchTerms}", L"",
+      "http://example.com/e/yy" },
+    { "{google:baseSuggestURL}{searchTerms}", L"",
+      "http://clients1.example.com/complete/" }
+  };
+
+  TestSearchTermsData search_terms_data("http://example.com/e/");
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(search_term_cases); ++i) {
+    const SearchTermsCase& value = search_term_cases[i];
+    TemplateURL t_url;
+    TemplateURLRef ref(value.url, 0, 0);
+    ASSERT_TRUE(ref.IsValid());
+
+    ASSERT_TRUE(ref.SupportsReplacement());
+    GURL result = GURL(ref.ReplaceSearchTermsUsingTermsData(
+        t_url, value.terms,
+        TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring(),
+        search_terms_data));
+    ASSERT_TRUE(result.is_valid());
+    ASSERT_EQ(value.output, result.spec());
+  }
+}
+
 TEST_F(TemplateURLTest, URLRefTermToWide) {
   struct ToWideCase {
     const char* encoded_search_term;
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 9d8dcab..402a5f78 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2415,6 +2415,8 @@
         'browser/search_engines/search_provider_install_data.h',
         'browser/search_engines/search_provider_install_state_dispatcher_host.cc',
         'browser/search_engines/search_provider_install_state_dispatcher_host.h',
+        'browser/search_engines/search_terms_data.cc',
+        'browser/search_engines/search_terms_data.h',
         'browser/search_engines/template_url.cc',
         'browser/search_engines/template_url.h',
         'browser/search_engines/template_url_fetcher.cc',