Split keyword related parts of WebDataService as KeywordWebDataService

The new class KeywordWebDataService will be moved to components/search_engines with KeywordTable later.

Move keyword related parts from web_data_service.{cc,h} to keyword_web_data_service.{cc,h}.
Add a new ProfileErrorType enum.

BUG=381572
TEST=build
[email protected] for the enum addition in histograms.xml corresponding to the one in chrome/browser/ui/profile_error_dialog.h

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@280969 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/search_engines/template_url_service.cc b/chrome/browser/search_engines/template_url_service.cc
index 91936db..47967e9 100644
--- a/chrome/browser/search_engines/template_url_service.cc
+++ b/chrome/browser/search_engines/template_url_service.cc
@@ -31,7 +31,7 @@
 #include "chrome/browser/search_engines/template_url_service_observer.h"
 #include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
 #include "chrome/browser/search_engines/util.h"
-#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/browser/webdata/web_data_service_factory.h"
 #include "components/rappor/rappor_service.h"
 #include "components/search_engines/search_engines_pref_names.h"
 #include "components/search_engines/template_url.h"
@@ -220,7 +220,6 @@
       loaded_(false),
       load_failed_(false),
       load_handle_(0),
-      service_(NULL),
       default_search_provider_(NULL),
       next_id_(kInvalidTemplateURLID + 1),
       time_provider_(&base::Time::Now),
@@ -235,8 +234,8 @@
 }
 
 TemplateURLService::~TemplateURLService() {
-  // |service_| should be deleted during Shutdown().
-  DCHECK(!service_);
+  // |web_data_service_| should be deleted during Shutdown().
+  DCHECK(!web_data_service_);
   STLDeleteElements(&template_urls_);
 }
 
@@ -538,7 +537,7 @@
 }
 
 bool TemplateURLService::Add(TemplateURL* template_url) {
-  WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+  KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
   if (!AddNoNotify(template_url, true))
     return false;
   NotifyObservers();
@@ -570,7 +569,7 @@
   DCHECK(!FindTemplateURLForExtension(info->extension_id, info->type));
   template_url->extension_info_.swap(info);
 
-  WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+  KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
   if (AddNoNotify(template_url, true)) {
     if (template_url->extension_info_->wants_to_be_default_engine)
       UpdateExtensionDefaultSearchEngine();
@@ -594,7 +593,7 @@
   // UpdateExtensionDefaultSearchEngine will cause it to be reset.
   if (default_search_provider_ == url)
     default_search_provider_ = NULL;
-  WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+  KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
   RemoveNoNotify(url);
   UpdateExtensionDefaultSearchEngine();
   NotifyObservers();
@@ -615,7 +614,7 @@
     base::Time created_before) {
   GURL o(origin.GetOrigin());
   bool should_notify = false;
-  WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+  KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
   for (size_t i = 0; i < template_urls_.size();) {
     if (template_urls_[i]->date_created() >= created_after &&
         (created_before.is_null() ||
@@ -670,8 +669,8 @@
     return;
   ++url->data_.usage_count;
 
-  if (service_)
-    service_->UpdateKeyword(url->data());
+  if (web_data_service_)
+    web_data_service_->UpdateKeyword(url->data());
 }
 
 void TemplateURLService::ResetTemplateURL(TemplateURL* url,
@@ -753,7 +752,7 @@
   ActionsFromPrepopulateData actions(CreateActionsFromCurrentPrepopulateData(
       &prepopulated_urls, template_urls_, default_search_provider_));
 
-  WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+  KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
 
   // Remove items.
   for (std::vector<TemplateURL*>::iterator i = actions.removed_engines.begin();
@@ -808,11 +807,13 @@
   if (loaded_ || load_handle_)
     return;
 
-  if (!service_)
-    service_ = WebDataService::FromBrowserContext(profile_);
+  if (!web_data_service_) {
+    web_data_service_ = WebDataServiceFactory::GetKeywordWebDataForProfile(
+        profile_, Profile::EXPLICIT_ACCESS);
+  }
 
-  if (service_)
-    load_handle_ = service_->GetKeywords(this);
+  if (web_data_service_)
+    load_handle_ = web_data_service_->GetKeywords(this);
   else
     ChangeToLoadedState();
 }
@@ -826,7 +827,7 @@
 }
 
 void TemplateURLService::OnWebDataServiceRequestDone(
-    WebDataService::Handle h,
+    KeywordWebDataService::Handle h,
     const WDTypedResult* result) {
   // Reset the load_handle so that we don't try and cancel the load in
   // the destructor.
@@ -836,7 +837,7 @@
     // Results are null if the database went away or (most likely) wasn't
     // loaded.
     load_failed_ = true;
-    service_ = NULL;
+    web_data_service_ = NULL;
     ChangeToLoadedState();
     return;
   }
@@ -845,7 +846,7 @@
   int new_resource_keyword_version = 0;
   GetSearchProvidersUsingKeywordResult(
       *result,
-      service_.get(),
+      web_data_service_.get(),
       prefs_,
       &template_urls,
       (default_search_provider_source_ == DefaultSearchManager::FROM_USER) ?
@@ -854,7 +855,7 @@
       &new_resource_keyword_version,
       &pre_sync_deletes_);
 
-  WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+  KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
 
   PatchMissingSyncGUIDs(&template_urls);
   SetTemplateURLs(&template_urls);
@@ -870,7 +871,7 @@
   visits_to_add_.clear();
 
   if (new_resource_keyword_version)
-    service_->SetBuiltinKeywordVersion(new_resource_keyword_version);
+    web_data_service_->SetBuiltinKeywordVersion(new_resource_keyword_version);
 
   if (default_search_provider_) {
     UMA_HISTOGRAM_ENUMERATION(
@@ -919,13 +920,13 @@
 
 void TemplateURLService::Shutdown() {
   // This check has to be done at Shutdown() instead of in the dtor to ensure
-  // that no clients of WebDataService are holding ptrs to it after the first
-  // phase of the KeyedService Shutdown() process.
+  // that no clients of KeywordWebDataService are holding ptrs to it after the
+  // first phase of the KeyedService Shutdown() process.
   if (load_handle_) {
-    DCHECK(service_.get());
-    service_->CancelRequest(load_handle_);
+    DCHECK(web_data_service_.get());
+    web_data_service_->CancelRequest(load_handle_);
   }
-  service_ = NULL;
+  web_data_service_ = NULL;
 }
 
 syncer::SyncDataList TemplateURLService::GetAllSyncData(
@@ -967,7 +968,7 @@
   base::AutoReset<DefaultSearchChangeOrigin> change_origin(&dsp_change_origin_,
       DSP_CHANGE_SYNC_UNINTENTIONAL);
 
-  WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+  KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
 
   syncer::SyncChangeList new_changes;
   syncer::SyncError error;
@@ -1126,7 +1127,7 @@
       GetAllSyncData(syncer::SEARCH_ENGINES));
   SyncDataMap sync_data_map = CreateGUIDToSyncDataMap(initial_sync_data);
 
-  WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+  KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
 
   merge_result.set_num_items_before_association(local_data_map.size());
   for (SyncDataMap::const_iterator iter = sync_data_map.begin();
@@ -1459,7 +1460,7 @@
     ChangeToLoadedState();
 
     // Add specific initializers, if any.
-    WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+    KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
     for (int i(0); i < num_initializers; ++i) {
       DCHECK(initializers[i].keyword);
       DCHECK(initializers[i].url);
@@ -1698,8 +1699,8 @@
   if (!existing_turl->sync_guid().empty())
     guid_to_template_map_[existing_turl->sync_guid()] = existing_turl;
 
-  if (service_)
-    service_->UpdateKeyword(existing_turl->data());
+  if (web_data_service_)
+    web_data_service_->UpdateKeyword(existing_turl->data());
 
   // Inform sync of the update.
   ProcessTemplateURLChange(
@@ -1802,7 +1803,7 @@
 }
 
 void TemplateURLService::GoogleBaseURLChanged() {
-  WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+  KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
   bool something_changed = false;
   for (TemplateURLVector::iterator i(template_urls_.begin());
        i != template_urls_.end(); ++i) {
@@ -1899,7 +1900,7 @@
   // a change.
   TemplateURL* previous_default_search_engine = default_search_provider_;
 
-  WebDataService::KeywordBatchModeScoper keyword_scoper(service_.get());
+  KeywordWebDataService::BatchModeScoper scoper(web_data_service_.get());
   if (default_search_provider_source_ == DefaultSearchManager::FROM_POLICY ||
       source == DefaultSearchManager::FROM_POLICY) {
     // We do this both to remove any no-longer-applicable policy-defined DSE as
@@ -2029,8 +2030,8 @@
   if (newly_adding &&
       (template_url->GetType() !=
           TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION)) {
-    if (service_)
-      service_->AddKeyword(template_url->data());
+    if (web_data_service_)
+      web_data_service_->AddKeyword(template_url->data());
 
     // Inform sync of the addition. Note that this will assign a GUID to
     // template_url and add it to the guid_to_template_map_.
@@ -2056,8 +2057,8 @@
   template_urls_.erase(i);
 
   if (template_url->GetType() != TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) {
-    if (service_)
-      service_->RemoveKeyword(template_url->id());
+    if (web_data_service_)
+      web_data_service_->RemoveKeyword(template_url->id());
 
     // Inform sync of the deletion.
     ProcessTemplateURLChange(FROM_HERE,
@@ -2139,8 +2140,8 @@
 
       RemoveFromMaps(template_url);
       i = template_urls->erase(i);
-      if (service_)
-        service_->RemoveKeyword(template_url->id());
+      if (web_data_service_)
+        web_data_service_->RemoveKeyword(template_url->id());
       delete template_url;
     } else {
       ++i;
@@ -2332,8 +2333,8 @@
         (template_url->GetType() !=
             TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION)) {
       template_url->data_.sync_guid = base::GenerateGUID();
-      if (service_)
-        service_->UpdateKeyword(template_url->data());
+      if (web_data_service_)
+        web_data_service_->UpdateKeyword(template_url->data());
     }
   }
 }
diff --git a/chrome/browser/search_engines/template_url_service.h b/chrome/browser/search_engines/template_url_service.h
index 663ebfc..a301338 100644
--- a/chrome/browser/search_engines/template_url_service.h
+++ b/chrome/browser/search_engines/template_url_service.h
@@ -16,11 +16,13 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
 #include "base/prefs/pref_change_registrar.h"
-#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/browser/webdata/keyword_web_data_service.h"
 #include "components/google/core/browser/google_url_tracker.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/search_engines/default_search_manager.h"
+#include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_id.h"
+#include "components/webdata/common/web_data_service_consumer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "sync/api/sync_change.h"
@@ -52,10 +54,10 @@
 // KeywordAutocomplete.
 //
 // TemplateURLService stores a vector of TemplateURLs. The TemplateURLs are
-// persisted to the database maintained by WebDataService. *ALL* mutations
-// to the TemplateURLs must funnel through TemplateURLService. This allows
-// TemplateURLService to notify listeners of changes as well as keep the
-// database in sync.
+// persisted to the database maintained by KeywordWebDataService.
+// *ALL* mutations to the TemplateURLs must funnel through TemplateURLService.
+// This allows TemplateURLService to notify listeners of changes as well as keep
+// the database in sync.
 //
 // There is a TemplateURLService per Profile.
 //
@@ -66,8 +68,8 @@
 // the Load method.
 //
 // TemplateURLService takes ownership of any TemplateURL passed to it. If there
-// is a WebDataService, deletion is handled by WebDataService, otherwise
-// TemplateURLService handles deletion.
+// is a KeywordWebDataService, deletion is handled by KeywordWebDataService,
+// otherwise TemplateURLService handles deletion.
 
 class TemplateURLService : public WebDataServiceConsumer,
                            public KeyedService,
@@ -287,7 +289,7 @@
   // This is invoked from WebDataService, and should not be directly
   // invoked.
   virtual void OnWebDataServiceRequestDone(
-      WebDataService::Handle h,
+      KeywordWebDataService::Handle h,
       const WDTypedResult* result) OVERRIDE;
 
   // Returns the locale-direction-adjusted short name for the given keyword.
@@ -690,10 +692,10 @@
   bool load_failed_;
 
   // If non-zero, we're waiting on a load.
-  WebDataService::Handle load_handle_;
+  KeywordWebDataService::Handle load_handle_;
 
   // Service used to store entries.
-  scoped_refptr<WebDataService> service_;
+  scoped_refptr<KeywordWebDataService> web_data_service_;
 
   // All visits that occurred before we finished loading. Once loaded
   // UpdateKeywordSearchTermsForURL is invoked for each element of the vector.
@@ -734,7 +736,8 @@
   scoped_ptr<syncer::SyncErrorFactory> sync_error_factory_;
 
   // A set of sync GUIDs denoting TemplateURLs that have been removed from this
-  // model or the underlying WebDataService prior to MergeDataAndStartSyncing.
+  // model or the underlying KeywordWebDataService prior to
+  // MergeDataAndStartSyncing.
   // This set is used to determine what entries from the server we want to
   // ignore locally and return a delete command for.
   std::set<std::string> pre_sync_deletes_;
diff --git a/chrome/browser/search_engines/template_url_service_test_util.cc b/chrome/browser/search_engines/template_url_service_test_util.cc
index 11a6efdac..2bdaf8b 100644
--- a/chrome/browser/search_engines/template_url_service_test_util.cc
+++ b/chrome/browser/search_engines/template_url_service_test_util.cc
@@ -97,7 +97,9 @@
   // Initialize the web data service so that the database gets updated with
   // any changes made.
 
-  model()->service_ = WebDataService::FromBrowserContext(profile());
+  model()->web_data_service_ =
+      WebDataServiceFactory::GetKeywordWebDataForProfile(
+          profile(), Profile::EXPLICIT_ACCESS);
   base::RunLoop().RunUntilIdle();
 }
 
diff --git a/chrome/browser/search_engines/template_url_service_test_util.h b/chrome/browser/search_engines/template_url_service_test_util.h
index 2273fd0..7ec62be 100644
--- a/chrome/browser/search_engines/template_url_service_test_util.h
+++ b/chrome/browser/search_engines/template_url_service_test_util.h
@@ -21,7 +21,6 @@
 class TestingProfile;
 class TestingTemplateURLService;
 class TestingProfile;
-class WebDataService;
 
 // TemplateURLServiceTestUtilBase contains basic API to ease testing of
 // TemplateURLService. User should take care of the infrastructure separately.
diff --git a/chrome/browser/search_engines/template_url_service_unittest.cc b/chrome/browser/search_engines/template_url_service_unittest.cc
index 8e07c23..0974fae 100644
--- a/chrome/browser/search_engines/template_url_service_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_unittest.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/search_engines/search_host_to_urls_map.h"
 #include "chrome/browser/search_engines/template_url_service.h"
 #include "chrome/browser/search_engines/template_url_service_test_util.h"
+#include "chrome/browser/webdata/keyword_web_data_service.h"
 #include "chrome/browser/webdata/web_data_service_factory.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/search_engines/search_terms_data.h"
@@ -1181,9 +1182,8 @@
   test_util_.VerifyLoad();
 
   test_util_.ClearModel();
-  scoped_refptr<WebDataService> web_service =
-      WebDataService::FromBrowserContext(test_util_.profile());
-  web_service->ShutdownDatabase();
+  WebDataServiceFactory::GetKeywordWebDataForProfile(
+      test_util_.profile(), Profile::EXPLICIT_ACCESS)->ShutdownDatabase();
 
   test_util_.ResetModel(false);
   model()->Load();
diff --git a/chrome/browser/search_engines/util.cc b/chrome/browser/search_engines/util.cc
index 2117e7e..9e6c744 100644
--- a/chrome/browser/search_engines/util.cc
+++ b/chrome/browser/search_engines/util.cc
@@ -45,7 +45,7 @@
 }
 
 void RemoveDuplicatePrepopulateIDs(
-    WebDataService* service,
+    KeywordWebDataService* service,
     const ScopedVector<TemplateURLData>& prepopulated_urls,
     TemplateURL* default_search_provider,
     TemplateURLService::TemplateURLVector* template_urls,
@@ -186,7 +186,7 @@
 // from the DB will be added to it.  Note that this function will take
 // ownership of |prepopulated_urls| and will clear the vector.
 void MergeEnginesFromPrepopulateData(
-    WebDataService* service,
+    KeywordWebDataService* service,
     ScopedVector<TemplateURLData>* prepopulated_urls,
     size_t default_search_index,
     TemplateURLService::TemplateURLVector* template_urls,
@@ -307,7 +307,7 @@
 
 void GetSearchProvidersUsingKeywordResult(
     const WDTypedResult& result,
-    WebDataService* service,
+    KeywordWebDataService* service,
     PrefService* prefs,
     TemplateURLService::TemplateURLVector* template_urls,
     TemplateURL* default_search_provider,
@@ -347,7 +347,7 @@
 }
 
 void GetSearchProvidersUsingLoadedEngines(
-    WebDataService* service,
+    KeywordWebDataService* service,
     PrefService* prefs,
     TemplateURLService::TemplateURLVector* template_urls,
     TemplateURL* default_search_provider,
diff --git a/chrome/browser/search_engines/util.h b/chrome/browser/search_engines/util.h
index 651f105..2d4c395 100644
--- a/chrome/browser/search_engines/util.h
+++ b/chrome/browser/search_engines/util.h
@@ -14,10 +14,10 @@
 #include "base/strings/string16.h"
 #include "chrome/browser/search_engines/template_url_service.h"
 
+class KeywordWebDataService;
 class PrefService;
 class TemplateURL;
 class WDTypedResult;
-class WebDataService;
 
 // Returns the short name of the default search engine, or the empty string if
 // none is set.
@@ -77,21 +77,21 @@
     const TemplateURLService::TemplateURLVector& existing_urls,
     const TemplateURL* default_search_provider);
 
-// Processes the results of WebDataService::GetKeywords, combining it with
-// prepopulated search providers to result in:
+// Processes the results of KeywordWebDataService::GetKeywords, combining it
+// with prepopulated search providers to result in:
 //  * a set of template_urls (search providers). The caller owns the
 //    TemplateURL* returned in template_urls.
 //  * whether there is a new resource keyword version (and the value).
 //    |*new_resource_keyword_version| is set to 0 if no new value. Otherwise,
 //    it is the new value.
-// Only pass in a non-NULL value for service if the WebDataService should be
-// updated. If |removed_keyword_guids| is not NULL, any TemplateURLs removed
-// from the keyword table in the WebDataService will have their Sync GUIDs
-// added to it. |default_search_provider| will be used to prevent removing the
-// current user-selected DSE, regardless of changes in prepopulate data.
+// Only pass in a non-NULL value for service if the KeywordWebDataService should
+// be updated. If |removed_keyword_guids| is not NULL, any TemplateURLs removed
+// from the keyword table in the KeywordWebDataService will have their Sync
+// GUIDs added to it. |default_search_provider| will be used to prevent removing
+// the current user-selected DSE, regardless of changes in prepopulate data.
 void GetSearchProvidersUsingKeywordResult(
     const WDTypedResult& result,
-    WebDataService* service,
+    KeywordWebDataService* service,
     PrefService* prefs,
     TemplateURLService::TemplateURLVector* template_urls,
     TemplateURL* default_search_provider,
@@ -107,7 +107,7 @@
 // that has been merged into the current keyword data.  On exit, this will be
 // set as in GetSearchProvidersUsingKeywordResult().
 void GetSearchProvidersUsingLoadedEngines(
-    WebDataService* service,
+    KeywordWebDataService* service,
     PrefService* prefs,
     TemplateURLService::TemplateURLVector* template_urls,
     TemplateURL* default_search_provider,
@@ -126,7 +126,7 @@
 // helper used by GetSearchProvidersUsingKeywordResult(), but is declared here
 // so it's accessible by unittests.
 void RemoveDuplicatePrepopulateIDs(
-    WebDataService* service,
+    KeywordWebDataService* service,
     const ScopedVector<TemplateURLData>& prepopulated_urls,
     TemplateURL* default_search_provider,
     TemplateURLService::TemplateURLVector* template_urls,
diff --git a/chrome/browser/ui/profile_error_dialog.h b/chrome/browser/ui/profile_error_dialog.h
index bb483af7..eec7ba9 100644
--- a/chrome/browser/ui/profile_error_dialog.h
+++ b/chrome/browser/ui/profile_error_dialog.h
@@ -17,6 +17,7 @@
   PROFILE_ERROR_DB_AUTOFILL_WEB_DATA,
   PROFILE_ERROR_DB_TOKEN_WEB_DATA,
   PROFILE_ERROR_DB_WEB_DATA,
+  PROFILE_ERROR_DB_KEYWORD_WEB_DATA,
   PROFILE_ERROR_END
 };
 
diff --git a/chrome/browser/webdata/OWNERS b/chrome/browser/webdata/OWNERS
index edd7941..78e6cc8 100644
--- a/chrome/browser/webdata/OWNERS
+++ b/chrome/browser/webdata/OWNERS
@@ -10,3 +10,4 @@
 
 # Keywords
 per-file keyword_table*[email protected]
+per-file keyword_web_data_service*[email protected]
\ No newline at end of file
diff --git a/chrome/browser/webdata/keyword_web_data_service.cc b/chrome/browser/webdata/keyword_web_data_service.cc
new file mode 100644
index 0000000..44aaa1b
--- /dev/null
+++ b/chrome/browser/webdata/keyword_web_data_service.cc
@@ -0,0 +1,149 @@
+// Copyright 2014 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/webdata/keyword_web_data_service.h"
+
+#include "base/bind.h"
+#include "chrome/browser/webdata/keyword_table.h"
+#include "components/search_engines/template_url_data.h"
+#include "components/webdata/common/web_data_results.h"
+#include "components/webdata/common/web_database_service.h"
+
+using base::Bind;
+
+WDKeywordsResult::WDKeywordsResult()
+  : default_search_provider_id(0),
+    builtin_keyword_version(0) {
+}
+
+WDKeywordsResult::~WDKeywordsResult() {}
+
+KeywordWebDataService::BatchModeScoper::BatchModeScoper(
+    KeywordWebDataService* service)
+    : service_(service) {
+  if (service_)
+    service_->AdjustBatchModeLevel(true);
+}
+
+KeywordWebDataService::BatchModeScoper::~BatchModeScoper() {
+  if (service_)
+    service_->AdjustBatchModeLevel(false);
+}
+
+KeywordWebDataService::KeywordWebDataService(
+    scoped_refptr<WebDatabaseService> wdbs,
+    scoped_refptr<base::MessageLoopProxy> ui_thread,
+    const ProfileErrorCallback& callback)
+    : WebDataServiceBase(wdbs, callback, ui_thread),
+      batch_mode_level_(0) {
+}
+
+void KeywordWebDataService::AddKeyword(const TemplateURLData& data) {
+  if (batch_mode_level_) {
+    queued_keyword_operations_.push_back(
+        KeywordTable::Operation(KeywordTable::ADD, data));
+  } else {
+    AdjustBatchModeLevel(true);
+    AddKeyword(data);
+    AdjustBatchModeLevel(false);
+  }
+}
+
+void KeywordWebDataService::RemoveKeyword(TemplateURLID id) {
+  if (batch_mode_level_) {
+    TemplateURLData data;
+    data.id = id;
+    queued_keyword_operations_.push_back(
+        KeywordTable::Operation(KeywordTable::REMOVE, data));
+  } else {
+    AdjustBatchModeLevel(true);
+    RemoveKeyword(id);
+    AdjustBatchModeLevel(false);
+  }
+}
+
+void KeywordWebDataService::UpdateKeyword(const TemplateURLData& data) {
+  if (batch_mode_level_) {
+    queued_keyword_operations_.push_back(
+        KeywordTable::Operation(KeywordTable::UPDATE, data));
+  } else {
+    AdjustBatchModeLevel(true);
+    UpdateKeyword(data);
+    AdjustBatchModeLevel(false);
+  }
+}
+
+WebDataServiceBase::Handle KeywordWebDataService::GetKeywords(
+    WebDataServiceConsumer* consumer) {
+  return wdbs_->ScheduleDBTaskWithResult(
+      FROM_HERE, Bind(&KeywordWebDataService::GetKeywordsImpl, this), consumer);
+}
+
+void KeywordWebDataService::SetDefaultSearchProviderID(TemplateURLID id) {
+  wdbs_->ScheduleDBTask(
+      FROM_HERE,
+      Bind(&KeywordWebDataService::SetDefaultSearchProviderIDImpl, this, id));
+}
+
+void KeywordWebDataService::SetBuiltinKeywordVersion(int version) {
+  wdbs_->ScheduleDBTask(
+      FROM_HERE,
+      Bind(&KeywordWebDataService::SetBuiltinKeywordVersionImpl,
+           this, version));
+}
+
+KeywordWebDataService::~KeywordWebDataService() {
+  DCHECK(!batch_mode_level_);
+}
+
+void KeywordWebDataService::AdjustBatchModeLevel(bool entering_batch_mode) {
+  if (entering_batch_mode) {
+    ++batch_mode_level_;
+  } else {
+    DCHECK(batch_mode_level_);
+    --batch_mode_level_;
+    if (!batch_mode_level_ && !queued_keyword_operations_.empty()) {
+      wdbs_->ScheduleDBTask(
+          FROM_HERE,
+          Bind(&KeywordWebDataService::PerformKeywordOperationsImpl, this,
+               queued_keyword_operations_));
+      queued_keyword_operations_.clear();
+    }
+  }
+}
+
+WebDatabase::State KeywordWebDataService::PerformKeywordOperationsImpl(
+    const KeywordTable::Operations& operations,
+    WebDatabase* db) {
+  return KeywordTable::FromWebDatabase(db)->PerformOperations(operations) ?
+      WebDatabase::COMMIT_NEEDED : WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+scoped_ptr<WDTypedResult> KeywordWebDataService::GetKeywordsImpl(
+    WebDatabase* db) {
+  scoped_ptr<WDTypedResult> result_ptr;
+  WDKeywordsResult result;
+  if (KeywordTable::FromWebDatabase(db)->GetKeywords(&result.keywords)) {
+    result.default_search_provider_id =
+        KeywordTable::FromWebDatabase(db)->GetDefaultSearchProviderID();
+    result.builtin_keyword_version =
+        KeywordTable::FromWebDatabase(db)->GetBuiltinKeywordVersion();
+    result_ptr.reset(new WDResult<WDKeywordsResult>(KEYWORDS_RESULT, result));
+  }
+  return result_ptr.Pass();
+}
+
+WebDatabase::State KeywordWebDataService::SetDefaultSearchProviderIDImpl(
+    TemplateURLID id,
+    WebDatabase* db) {
+  return KeywordTable::FromWebDatabase(db)->SetDefaultSearchProviderID(id) ?
+      WebDatabase::COMMIT_NEEDED : WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+WebDatabase::State KeywordWebDataService::SetBuiltinKeywordVersionImpl(
+    int version,
+    WebDatabase* db) {
+  return KeywordTable::FromWebDatabase(db)->SetBuiltinKeywordVersion(version) ?
+      WebDatabase::COMMIT_NEEDED : WebDatabase::COMMIT_NOT_NEEDED;
+}
diff --git a/chrome/browser/webdata/keyword_web_data_service.h b/chrome/browser/webdata/keyword_web_data_service.h
new file mode 100644
index 0000000..3659b09
--- /dev/null
+++ b/chrome/browser/webdata/keyword_web_data_service.h
@@ -0,0 +1,110 @@
+// Copyright 2014 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_WEBDATA_KEYWORD_WEB_DATA_SERVICE_H__
+#define CHROME_BROWSER_WEBDATA_KEYWORD_WEB_DATA_SERVICE_H__
+
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/webdata/keyword_table.h"
+#include "components/search_engines/template_url_id.h"
+#include "components/webdata/common/web_data_service_base.h"
+#include "components/webdata/common/web_database.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+class WDTypedResult;
+class WebDatabaseService;
+struct TemplateURLData;
+
+struct WDKeywordsResult {
+  WDKeywordsResult();
+  ~WDKeywordsResult();
+
+  KeywordTable::Keywords keywords;
+  // Identifies the ID of the TemplateURL that is the default search. A value of
+  // 0 indicates there is no default search provider.
+  int64 default_search_provider_id;
+  // Version of the built-in keywords. A value of 0 indicates a first run.
+  int builtin_keyword_version;
+};
+
+class WebDataServiceConsumer;
+
+class KeywordWebDataService : public WebDataServiceBase {
+ public:
+  // Instantiate this to turn on batch mode on the provided |service|
+  // until the scoper is destroyed.  When batch mode is on, calls to any of the
+  // three keyword table modification functions below will result in locally
+  // queueing the operation; on setting this back to false, all the
+  // modifications will be performed at once.  This is a performance
+  // optimization; see comments on KeywordTable::PerformOperations().
+  //
+  // If multiple scopers are in-scope simultaneously, batch mode will only be
+  // exited when all are destroyed.  If |service| is NULL, the object will do
+  // nothing.
+  class BatchModeScoper {
+   public:
+    explicit BatchModeScoper(KeywordWebDataService* service);
+    ~BatchModeScoper();
+
+   private:
+    KeywordWebDataService* service_;
+
+    DISALLOW_COPY_AND_ASSIGN(BatchModeScoper);
+  };
+
+  KeywordWebDataService(scoped_refptr<WebDatabaseService> wdbs,
+                        scoped_refptr<base::MessageLoopProxy> ui_thread,
+                        const ProfileErrorCallback& callback);
+
+  // As the database processes requests at a later date, all deletion is
+  // done on the background thread.
+  //
+  // Many of the keyword related methods do not return a handle. This is because
+  // the caller (TemplateURLService) does not need to know when the request is
+  // done.
+
+  void AddKeyword(const TemplateURLData& data);
+  void RemoveKeyword(TemplateURLID id);
+  void UpdateKeyword(const TemplateURLData& data);
+
+  // Fetches the keywords.
+  // On success, consumer is notified with WDResult<KeywordTable::Keywords>.
+  Handle GetKeywords(WebDataServiceConsumer* consumer);
+
+  // Sets the ID of the default search provider.
+  void SetDefaultSearchProviderID(TemplateURLID id);
+
+  // Sets the version of the builtin keywords.
+  void SetBuiltinKeywordVersion(int version);
+
+ protected:
+  virtual ~KeywordWebDataService();
+
+ private:
+  // Called by the BatchModeScoper (see comments there).
+  void AdjustBatchModeLevel(bool entering_batch_mode);
+
+  //////////////////////////////////////////////////////////////////////////////
+  //
+  // The following methods are only invoked on the DB thread.
+  //
+  //////////////////////////////////////////////////////////////////////////////
+  WebDatabase::State PerformKeywordOperationsImpl(
+      const KeywordTable::Operations& operations,
+      WebDatabase* db);
+  scoped_ptr<WDTypedResult> GetKeywordsImpl(WebDatabase* db);
+  WebDatabase::State SetDefaultSearchProviderIDImpl(TemplateURLID id,
+                                                    WebDatabase* db);
+  WebDatabase::State SetBuiltinKeywordVersionImpl(int version, WebDatabase* db);
+
+  size_t batch_mode_level_;
+  KeywordTable::Operations queued_keyword_operations_;
+
+  DISALLOW_COPY_AND_ASSIGN(KeywordWebDataService);
+};
+
+#endif  // CHROME_BROWSER_WEBDATA_KEYWORD_WEB_DATA_SERVICE_H__
diff --git a/chrome/browser/webdata/web_data_service.cc b/chrome/browser/webdata/web_data_service.cc
index 4ed9c9b..b981782a 100644
--- a/chrome/browser/webdata/web_data_service.cc
+++ b/chrome/browser/webdata/web_data_service.cc
@@ -5,9 +5,6 @@
 #include "chrome/browser/webdata/web_data_service.h"
 
 #include "base/bind.h"
-#include "base/stl_util.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/webdata/keyword_table.h"
 #include "chrome/browser/webdata/logins_table.h"
 #include "chrome/browser/webdata/web_apps_table.h"
 #include "chrome/browser/webdata/web_intents_table.h"
@@ -15,9 +12,6 @@
 #include "components/signin/core/browser/webdata/token_service_table.h"
 #include "components/webdata/common/web_database_service.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_source.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -27,97 +21,17 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 using base::Bind;
-using base::Time;
 using content::BrowserThread;
 
 WDAppImagesResult::WDAppImagesResult() : has_all_images(false) {}
 
 WDAppImagesResult::~WDAppImagesResult() {}
 
-WDKeywordsResult::WDKeywordsResult()
-  : default_search_provider_id(0),
-    builtin_keyword_version(0) {
-}
-
-WDKeywordsResult::~WDKeywordsResult() {}
-
-WebDataService::KeywordBatchModeScoper::KeywordBatchModeScoper(
-    WebDataService* service)
-    : service_(service) {
-  if (service_)
-    service_->AdjustKeywordBatchModeLevel(true);
-}
-
-WebDataService::KeywordBatchModeScoper::~KeywordBatchModeScoper() {
-  if (service_)
-    service_->AdjustKeywordBatchModeLevel(false);
-}
-
 WebDataService::WebDataService(scoped_refptr<WebDatabaseService> wdbs,
                                const ProfileErrorCallback& callback)
     : WebDataServiceBase(
           wdbs, callback,
-          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)),
-      keyword_batch_mode_level_(0) {
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Keywords.
-//
-//////////////////////////////////////////////////////////////////////////////
-
-void WebDataService::AddKeyword(const TemplateURLData& data) {
-  if (keyword_batch_mode_level_) {
-    queued_keyword_operations_.push_back(
-        KeywordTable::Operation(KeywordTable::ADD, data));
-  } else {
-    AdjustKeywordBatchModeLevel(true);
-    AddKeyword(data);
-    AdjustKeywordBatchModeLevel(false);
-  }
-}
-
-void WebDataService::RemoveKeyword(TemplateURLID id) {
-  if (keyword_batch_mode_level_) {
-    TemplateURLData data;
-    data.id = id;
-    queued_keyword_operations_.push_back(
-        KeywordTable::Operation(KeywordTable::REMOVE, data));
-  } else {
-    AdjustKeywordBatchModeLevel(true);
-    RemoveKeyword(id);
-    AdjustKeywordBatchModeLevel(false);
-  }
-}
-
-void WebDataService::UpdateKeyword(const TemplateURLData& data) {
-  if (keyword_batch_mode_level_) {
-    queued_keyword_operations_.push_back(
-        KeywordTable::Operation(KeywordTable::UPDATE, data));
-  } else {
-    AdjustKeywordBatchModeLevel(true);
-    UpdateKeyword(data);
-    AdjustKeywordBatchModeLevel(false);
-  }
-}
-
-WebDataServiceBase::Handle WebDataService::GetKeywords(
-    WebDataServiceConsumer* consumer) {
-  return wdbs_->ScheduleDBTaskWithResult(
-      FROM_HERE, Bind(&WebDataService::GetKeywordsImpl, this), consumer);
-}
-
-void WebDataService::SetDefaultSearchProviderID(TemplateURLID id) {
-  wdbs_->ScheduleDBTask(
-      FROM_HERE,
-      Bind(&WebDataService::SetDefaultSearchProviderIDImpl, this, id));
-}
-
-void WebDataService::SetBuiltinKeywordVersion(int version) {
-  wdbs_->ScheduleDBTask(
-      FROM_HERE,
-      Bind(&WebDataService::SetBuiltinKeywordVersionImpl, this, version));
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)) {
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -155,68 +69,10 @@
 WebDataService::WebDataService()
     : WebDataServiceBase(
           NULL, ProfileErrorCallback(),
-          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)),
-      keyword_batch_mode_level_(0) {
+          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)) {
 }
 
 WebDataService::~WebDataService() {
-  DCHECK(!keyword_batch_mode_level_);
-}
-
-void WebDataService::AdjustKeywordBatchModeLevel(bool entering_batch_mode) {
-  if (entering_batch_mode) {
-    ++keyword_batch_mode_level_;
-  } else {
-    DCHECK(keyword_batch_mode_level_);
-    --keyword_batch_mode_level_;
-    if (!keyword_batch_mode_level_ && !queued_keyword_operations_.empty()) {
-      wdbs_->ScheduleDBTask(
-          FROM_HERE,
-          Bind(&WebDataService::PerformKeywordOperationsImpl, this,
-               queued_keyword_operations_));
-      queued_keyword_operations_.clear();
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// Keywords implementation.
-//
-////////////////////////////////////////////////////////////////////////////////
-
-WebDatabase::State WebDataService::PerformKeywordOperationsImpl(
-    const KeywordTable::Operations& operations,
-    WebDatabase* db) {
-  return KeywordTable::FromWebDatabase(db)->PerformOperations(operations) ?
-      WebDatabase::COMMIT_NEEDED : WebDatabase::COMMIT_NOT_NEEDED;
-}
-
-scoped_ptr<WDTypedResult> WebDataService::GetKeywordsImpl(WebDatabase* db) {
-  scoped_ptr<WDTypedResult> result_ptr;
-  WDKeywordsResult result;
-  if (KeywordTable::FromWebDatabase(db)->GetKeywords(&result.keywords)) {
-    result.default_search_provider_id =
-        KeywordTable::FromWebDatabase(db)->GetDefaultSearchProviderID();
-    result.builtin_keyword_version =
-        KeywordTable::FromWebDatabase(db)->GetBuiltinKeywordVersion();
-    result_ptr.reset(new WDResult<WDKeywordsResult>(KEYWORDS_RESULT, result));
-  }
-  return result_ptr.Pass();
-}
-
-WebDatabase::State WebDataService::SetDefaultSearchProviderIDImpl(
-    TemplateURLID id,
-    WebDatabase* db) {
-  return KeywordTable::FromWebDatabase(db)->SetDefaultSearchProviderID(id) ?
-      WebDatabase::COMMIT_NEEDED : WebDatabase::COMMIT_NOT_NEEDED;
-}
-
-WebDatabase::State WebDataService::SetBuiltinKeywordVersionImpl(
-    int version,
-    WebDatabase* db) {
-  return KeywordTable::FromWebDatabase(db)->SetBuiltinKeywordVersion(version) ?
-      WebDatabase::COMMIT_NEEDED : WebDatabase::COMMIT_NOT_NEEDED;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/webdata/web_data_service.h b/chrome/browser/webdata/web_data_service.h
index d63e810b..3a84ed5 100644
--- a/chrome/browser/webdata/web_data_service.h
+++ b/chrome/browser/webdata/web_data_service.h
@@ -9,18 +9,10 @@
 #ifndef CHROME_BROWSER_WEBDATA_WEB_DATA_SERVICE_H__
 #define CHROME_BROWSER_WEBDATA_WEB_DATA_SERVICE_H__
 
-#include <map>
-#include <string>
 #include <vector>
 
-#include "base/callback_forward.h"
-#include "base/files/file_path.h"
-#include "base/location.h"
 #include "base/memory/ref_counted.h"
 #include "base/sequenced_task_runner_helpers.h"
-#include "chrome/browser/webdata/keyword_table.h"
-#include "components/search_engines/template_url.h"
-#include "components/search_engines/template_url_id.h"
 #include "components/webdata/common/web_data_results.h"
 #include "components/webdata/common/web_data_service_base.h"
 #include "components/webdata/common/web_data_service_consumer.h"
@@ -63,8 +55,6 @@
 //
 ////////////////////////////////////////////////////////////////////////////////
 
-typedef base::Callback<scoped_ptr<WDTypedResult>(void)> ResultTask;
-
 // Result from GetWebAppImages.
 struct WDAppImagesResult {
   WDAppImagesResult();
@@ -77,43 +67,10 @@
   std::vector<SkBitmap> images;
 };
 
-struct WDKeywordsResult {
-  WDKeywordsResult();
-  ~WDKeywordsResult();
-
-  KeywordTable::Keywords keywords;
-  // Identifies the ID of the TemplateURL that is the default search. A value of
-  // 0 indicates there is no default search provider.
-  int64 default_search_provider_id;
-  // Version of the built-in keywords. A value of 0 indicates a first run.
-  int builtin_keyword_version;
-};
-
 class WebDataServiceConsumer;
 
 class WebDataService : public WebDataServiceBase {
  public:
-  // Instantiate this to turn on keyword batch mode on the provided |service|
-  // until the scoper is destroyed.  When batch mode is on, calls to any of the
-  // three keyword table modification functions below will result in locally
-  // queueing the operation; on setting this back to false, all the
-  // modifications will be performed at once.  This is a performance
-  // optimization; see comments on KeywordTable::PerformOperations().
-  //
-  // If multiple scopers are in-scope simultaneously, batch mode will only be
-  // exited when all are destroyed.  If |service| is NULL, the object will do
-  // nothing.
-  class KeywordBatchModeScoper {
-   public:
-    explicit KeywordBatchModeScoper(WebDataService* service);
-    ~KeywordBatchModeScoper();
-
-   private:
-    WebDataService* service_;
-
-    DISALLOW_COPY_AND_ASSIGN(KeywordBatchModeScoper);
-  };
-
   // Retrieve a WebDataService for the given context.
   static scoped_refptr<WebDataService> FromBrowserContext(
       content::BrowserContext* context);
@@ -123,33 +80,6 @@
 
   //////////////////////////////////////////////////////////////////////////////
   //
-  // Keywords
-  //
-  //////////////////////////////////////////////////////////////////////////////
-
-  // As the database processes requests at a later date, all deletion is
-  // done on the background thread.
-  //
-  // Many of the keyword related methods do not return a handle. This is because
-  // the caller (TemplateURLService) does not need to know when the request is
-  // done.
-
-  void AddKeyword(const TemplateURLData& data);
-  void RemoveKeyword(TemplateURLID id);
-  void UpdateKeyword(const TemplateURLData& data);
-
-  // Fetches the keywords.
-  // On success, consumer is notified with WDResult<KeywordTable::Keywords>.
-  Handle GetKeywords(WebDataServiceConsumer* consumer);
-
-  // Sets the ID of the default search provider.
-  void SetDefaultSearchProviderID(TemplateURLID id);
-
-  // Sets the version of the builtin keywords.
-  void SetBuiltinKeywordVersion(int version);
-
-  //////////////////////////////////////////////////////////////////////////////
-  //
   // Web Apps
   //
   //////////////////////////////////////////////////////////////////////////////
@@ -197,9 +127,6 @@
   virtual ~WebDataService();
 
  private:
-  // Called by the KeywordBatchModeScoper (see comments there).
-  void AdjustKeywordBatchModeLevel(bool entering_batch_mode);
-
   //////////////////////////////////////////////////////////////////////////////
   //
   // The following methods are only invoked on the DB thread.
@@ -208,19 +135,6 @@
 
   //////////////////////////////////////////////////////////////////////////////
   //
-  // Keywords.
-  //
-  //////////////////////////////////////////////////////////////////////////////
-  WebDatabase::State PerformKeywordOperationsImpl(
-      const KeywordTable::Operations& operations,
-      WebDatabase* db);
-  scoped_ptr<WDTypedResult> GetKeywordsImpl(WebDatabase* db);
-  WebDatabase::State SetDefaultSearchProviderIDImpl(TemplateURLID id,
-                                                    WebDatabase* db);
-  WebDatabase::State SetBuiltinKeywordVersionImpl(int version, WebDatabase* db);
-
-  //////////////////////////////////////////////////////////////////////////////
-  //
   // Web Apps.
   //
   //////////////////////////////////////////////////////////////////////////////
@@ -273,9 +187,6 @@
       const IE7PasswordInfo& info, WebDatabase* db);
 #endif  // defined(OS_WIN)
 
-  size_t keyword_batch_mode_level_;
-  KeywordTable::Operations queued_keyword_operations_;
-
   DISALLOW_COPY_AND_ASSIGN(WebDataService);
 };
 
diff --git a/chrome/browser/webdata/web_data_service_factory.cc b/chrome/browser/webdata/web_data_service_factory.cc
index e49c6b2..b5d5dc3 100644
--- a/chrome/browser/webdata/web_data_service_factory.cc
+++ b/chrome/browser/webdata/web_data_service_factory.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/ui/profile_error_dialog.h"
 #include "chrome/browser/webdata/autocomplete_syncable_service.h"
 #include "chrome/browser/webdata/keyword_table.h"
+#include "chrome/browser/webdata/keyword_web_data_service.h"
 #include "chrome/browser/webdata/logins_table.h"
 #include "chrome/browser/webdata/web_apps_table.h"
 #include "chrome/browser/webdata/web_data_service.h"
@@ -105,6 +106,11 @@
           &ProfileErrorCallback, PROFILE_ERROR_DB_AUTOFILL_WEB_DATA));
   autofill_web_data_->Init();
 
+  keyword_web_data_ = new KeywordWebDataService(
+      web_database_, ui_thread, base::Bind(
+          &ProfileErrorCallback, PROFILE_ERROR_DB_KEYWORD_WEB_DATA));
+  keyword_web_data_->Init();
+
   token_web_data_ = new TokenWebData(
       web_database_, ui_thread, db_thread, base::Bind(
          &ProfileErrorCallback, PROFILE_ERROR_DB_TOKEN_WEB_DATA));
@@ -127,6 +133,7 @@
 
 void WebDataServiceWrapper::Shutdown() {
   autofill_web_data_->ShutdownOnUIThread();
+  keyword_web_data_->ShutdownOnUIThread();
   token_web_data_->ShutdownOnUIThread();
   web_data_->ShutdownOnUIThread();
   web_database_->ShutdownDatabase();
@@ -137,6 +144,11 @@
   return autofill_web_data_.get();
 }
 
+scoped_refptr<KeywordWebDataService>
+WebDataServiceWrapper::GetKeywordWebData() {
+  return keyword_web_data_.get();
+}
+
 scoped_refptr<WebDataService> WebDataServiceWrapper::GetWebData() {
   return web_data_.get();
 }
@@ -207,6 +219,18 @@
 }
 
 // static
+scoped_refptr<KeywordWebDataService>
+WebDataServiceFactory::GetKeywordWebDataForProfile(
+    Profile* profile,
+    Profile::ServiceAccessType access_type) {
+  WebDataServiceWrapper* wrapper =
+      WebDataServiceFactory::GetForProfile(profile, access_type);
+  // |wrapper| can be NULL in Incognito mode.
+  return wrapper ?
+      wrapper->GetKeywordWebData() : scoped_refptr<KeywordWebDataService>(NULL);
+}
+
+// static
 scoped_refptr<TokenWebData>
 WebDataServiceFactory::GetTokenWebDataForProfile(
     Profile* profile,
diff --git a/chrome/browser/webdata/web_data_service_factory.h b/chrome/browser/webdata/web_data_service_factory.h
index 3eded2d..ae3a3858 100644
--- a/chrome/browser/webdata/web_data_service_factory.h
+++ b/chrome/browser/webdata/web_data_service_factory.h
@@ -13,6 +13,7 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/webdata/common/web_database_service.h"
 
+class KeywordWebDataService;
 class TokenWebData;
 class WebDataService;
 
@@ -35,6 +36,8 @@
 
   virtual scoped_refptr<autofill::AutofillWebDataService> GetAutofillWebData();
 
+  virtual scoped_refptr<KeywordWebDataService> GetKeywordWebData();
+
   virtual scoped_refptr<WebDataService> GetWebData();
 
   virtual scoped_refptr<TokenWebData> GetTokenWebData();
@@ -43,6 +46,7 @@
   scoped_refptr<WebDatabaseService> web_database_;
 
   scoped_refptr<autofill::AutofillWebDataService> autofill_web_data_;
+  scoped_refptr<KeywordWebDataService> keyword_web_data_;
   scoped_refptr<TokenWebData> token_web_data_;
   scoped_refptr<WebDataService> web_data_;
 
@@ -67,6 +71,11 @@
       GetAutofillWebDataForProfile(Profile* profile,
                                    Profile::ServiceAccessType access_type);
 
+  // Returns the KeywordWebDataService associated with the |profile|.
+  static scoped_refptr<KeywordWebDataService>
+      GetKeywordWebDataForProfile(Profile* profile,
+                                  Profile::ServiceAccessType access_type);
+
   // Returns the TokenWebData associated with the |profile|.
   static scoped_refptr<TokenWebData> GetTokenWebDataForProfile(Profile* profile,
       Profile::ServiceAccessType access_type);
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 6787860..6aafbf6 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1652,6 +1652,8 @@
       'browser/webdata/autocomplete_syncable_service.h',
       'browser/webdata/keyword_table.cc',
       'browser/webdata/keyword_table.h',
+      'browser/webdata/keyword_web_data_service.cc',
+      'browser/webdata/keyword_web_data_service.h',
       'browser/webdata/logins_table.cc',
       'browser/webdata/logins_table.h',
       'browser/webdata/logins_table_win.cc',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 981fcb7a..cee3bd4 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -44555,6 +44555,7 @@
   <int value="2" label="Webdata autofill DB error"/>
   <int value="3" label="Webdata token DB error"/>
   <int value="4" label="Webdata DB error"/>
+  <int value="5" label="Webdata keyword DB error"/>
 </enum>
 
 <enum name="ProfileGaiaPhotoOptions" type="int">