Linux: refactor GNOME Keyring and KWallet integration to allow migration from the default store, and add unit tests. Still disabled.
BUG=12351, 25404
TEST=unit tests work
Review URL: http://codereview.chromium.org/2806002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50034 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/password_manager/password_store_gnome.cc b/chrome/browser/password_manager/native_backend_gnome_x.cc
similarity index 76%
rename from chrome/browser/password_manager/password_store_gnome.cc
rename to chrome/browser/password_manager/native_backend_gnome_x.cc
index 7495815..29febc0 100644
--- a/chrome/browser/password_manager/password_store_gnome.cc
+++ b/chrome/browser/password_manager/native_backend_gnome_x.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/password_manager/password_store_gnome.h"
+#include "chrome/browser/password_manager/native_backend_gnome_x.h"
 
 #if defined(DLOPEN_GNOME_KEYRING)
 #include <dlfcn.h>
@@ -13,13 +13,12 @@
 
 #include "base/logging.h"
 #include "base/string_util.h"
-#include "base/task.h"
 #include "base/time.h"
 #include "base/utf_string_conversions.h"
+#include "chrome/browser/chrome_thread.h"
 
 using std::map;
 using std::string;
-using std::vector;
 using webkit_glue::PasswordForm;
 
 /* Many of the gnome_keyring_* functions use variable arguments, which makes
@@ -136,7 +135,7 @@
 #define GNOME_KEYRING_APPLICATION_CHROME "chrome"
 
 // Schema is analagous to the fields in PasswordForm.
-const GnomeKeyringPasswordSchema PasswordStoreGnome::kGnomeSchema = {
+const GnomeKeyringPasswordSchema NativeBackendGnome::kGnomeSchema = {
   GNOME_KEYRING_ITEM_GENERIC_SECRET, {
     { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
     { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
@@ -156,26 +155,47 @@
   }
 };
 
-PasswordStoreGnome::PasswordStoreGnome(LoginDatabase* login_db,
-                                       Profile* profile,
-                                       WebDataService* web_data_service) {
+NativeBackendGnome::NativeBackendGnome() {
 }
 
-PasswordStoreGnome::~PasswordStoreGnome() {
+NativeBackendGnome::~NativeBackendGnome() {
 }
 
-bool PasswordStoreGnome::Init() {
-  return PasswordStore::Init() &&
-         LoadGnomeKeyring() &&
-         gnome_keyring_is_available();
+bool NativeBackendGnome::Init() {
+  return LoadGnomeKeyring() && gnome_keyring_is_available();
 }
 
-void PasswordStoreGnome::AddLoginImpl(const PasswordForm& form) {
+bool NativeBackendGnome::AddLogin(const PasswordForm& form) {
   DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
-  AddLoginHelper(form, base::Time::Now());
+  GnomeKeyringResult result = gnome_keyring_store_password_sync(
+      &kGnomeSchema,
+      NULL,  // Default keyring.
+      form.origin.spec().c_str(),  // Display name.
+      UTF16ToUTF8(form.password_value).c_str(),
+      "origin_url", form.origin.spec().c_str(),
+      "action_url", form.action.spec().c_str(),
+      "username_element", UTF16ToUTF8(form.username_element).c_str(),
+      "username_value", UTF16ToUTF8(form.username_value).c_str(),
+      "password_element", UTF16ToUTF8(form.password_element).c_str(),
+      "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
+      "signon_realm", form.signon_realm.c_str(),
+      "ssl_valid", form.ssl_valid,
+      "preferred", form.preferred,
+      "date_created", Int64ToString(form.date_created.ToTimeT()).c_str(),
+      "blacklisted_by_user", form.blacklisted_by_user,
+      "scheme", form.scheme,
+      "application", GNOME_KEYRING_APPLICATION_CHROME,
+      NULL);
+
+  if (result != GNOME_KEYRING_RESULT_OK) {
+    LOG(ERROR) << "Keyring save failed: "
+               << gnome_keyring_result_to_message(result);
+    return false;
+  }
+  return true;
 }
 
-void PasswordStoreGnome::UpdateLoginImpl(const PasswordForm& form) {
+bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) {
   // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
   // origin_url, username_element, username_value, password_element, and
   // signon_realm. We then compare the result to the updated form. If they
@@ -201,31 +221,35 @@
       "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
       GNOME_KEYRING_APPLICATION_CHROME,
       NULL);
-  vector<PasswordForm*> forms;
-  if (result == GNOME_KEYRING_RESULT_OK) {
-    FillFormVector(found, &forms);
-    for (size_t i = 0; i < forms.size(); ++i) {
-      if (forms[i]->action != form.action ||
-          forms[i]->password_value != form.password_value ||
-          forms[i]->ssl_valid != form.ssl_valid ||
-          forms[i]->preferred != form.preferred) {
-        PasswordForm updated = *forms[i];
-        updated.action = form.action;
-        updated.password_value = form.password_value;
-        updated.ssl_valid = form.ssl_valid;
-        updated.preferred = form.preferred;
-        if (AddLoginHelper(updated, updated.date_created))
-          RemoveLoginImpl(*forms[i]);
-      }
-      delete forms[i];
-    }
-  } else {
+  if (result != GNOME_KEYRING_RESULT_OK) {
     LOG(ERROR) << "Keyring find failed: "
                << gnome_keyring_result_to_message(result);
+    return false;
   }
+  bool ok = true;
+  PasswordFormList forms;
+  ConvertFormList(found, &forms);
+  for (size_t i = 0; i < forms.size(); ++i) {
+    if (forms[i]->action != form.action ||
+        forms[i]->password_value != form.password_value ||
+        forms[i]->ssl_valid != form.ssl_valid ||
+        forms[i]->preferred != form.preferred) {
+      PasswordForm updated = *forms[i];
+      updated.action = form.action;
+      updated.password_value = form.password_value;
+      updated.ssl_valid = form.ssl_valid;
+      updated.preferred = form.preferred;
+      if (AddLogin(updated))
+        RemoveLogin(*forms[i]);
+      else
+        ok = false;
+    }
+    delete forms[i];
+  }
+  return ok;
 }
 
-void PasswordStoreGnome::RemoveLoginImpl(const PasswordForm& form) {
+bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
   DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
   // We find forms using the same fields as LoginDatabase::RemoveLogin().
   GnomeKeyringResult result = gnome_keyring_delete_password_sync(
@@ -241,43 +265,35 @@
   if (result != GNOME_KEYRING_RESULT_OK) {
     LOG(ERROR) << "Keyring delete failed: "
                << gnome_keyring_result_to_message(result);
+    return false;
   }
+  return true;
 }
 
-void PasswordStoreGnome::RemoveLoginsCreatedBetweenImpl(
+bool NativeBackendGnome::RemoveLoginsCreatedBetween(
     const base::Time& delete_begin,
     const base::Time& delete_end) {
   DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
-  GList* found = NULL;
-  // Search GNOME keyring for all passwords, then delete the ones in the range.
-  // We need to search for something, otherwise we get no results - so we search
-  // for the fixed application string.
-  GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
-      GNOME_KEYRING_ITEM_GENERIC_SECRET,
-      &found,
-      "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
-      GNOME_KEYRING_APPLICATION_CHROME,
-      NULL);
-  if (result == GNOME_KEYRING_RESULT_OK) {
-    // We could walk the list and delete items as we find them, but it is much
-    // easier to build the vector and use RemoveLoginImpl() to delete them.
-    vector<PasswordForm*> forms;
-    FillFormVector(found, &forms);
-    for (size_t i = 0; i < forms.size(); ++i) {
-      if (delete_begin <= forms[i]->date_created &&
-          (delete_end.is_null() || forms[i]->date_created < delete_end)) {
-        RemoveLoginImpl(*forms[i]);
-      }
-      delete forms[i];
+  bool ok = true;
+  // We could walk the list and delete items as we find them, but it is much
+  // easier to build the list and use RemoveLogin() to delete them.
+  PasswordFormList forms;
+  if (!GetAllLogins(&forms))
+    return false;
+
+  for (size_t i = 0; i < forms.size(); ++i) {
+    if (delete_begin <= forms[i]->date_created &&
+        (delete_end.is_null() || forms[i]->date_created < delete_end)) {
+      if (!RemoveLogin(*forms[i]))
+        ok = false;
     }
-  } else if (result != GNOME_KEYRING_RESULT_NO_MATCH) {
-    LOG(ERROR) << "Keyring find failed: "
-               << gnome_keyring_result_to_message(result);
+    delete forms[i];
   }
+  return ok;
 }
 
-void PasswordStoreGnome::GetLoginsImpl(GetLoginsRequest* request,
-                                       const PasswordForm& form) {
+bool NativeBackendGnome::GetLogins(const PasswordForm& form,
+                                   PasswordFormList* forms) {
   DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
   GList* found = NULL;
   // Search gnome keyring for matching passwords.
@@ -289,73 +305,50 @@
       "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
       GNOME_KEYRING_APPLICATION_CHROME,
       NULL);
-  vector<PasswordForm*> forms;
-  if (result == GNOME_KEYRING_RESULT_OK) {
-    FillFormVector(found, &forms);
-  } else if (result != GNOME_KEYRING_RESULT_NO_MATCH) {
-    LOG(ERROR) << "Keyring find failed: "
-               << gnome_keyring_result_to_message(result);
-  }
-  NotifyConsumer(request, forms);
-}
-
-void PasswordStoreGnome::GetAutofillableLoginsImpl(
-    GetLoginsRequest* request) {
-  std::vector<PasswordForm*> forms;
-  FillAutofillableLogins(&forms);
-  NotifyConsumer(request, forms);
-}
-
-void PasswordStoreGnome::GetBlacklistLoginsImpl(
-    GetLoginsRequest* request) {
-  std::vector<PasswordForm*> forms;
-  FillBlacklistLogins(&forms);
-  NotifyConsumer(request, forms);
-}
-
-bool PasswordStoreGnome::FillAutofillableLogins(
-    std::vector<PasswordForm*>* forms) {
-  return FillSomeLogins(true, forms);
-}
-
-bool PasswordStoreGnome::FillBlacklistLogins(
-    std::vector<PasswordForm*>* forms) {
-  return FillSomeLogins(false, forms);
-}
-
-bool PasswordStoreGnome::AddLoginHelper(const PasswordForm& form,
-                                        const base::Time& date_created) {
-  GnomeKeyringResult result = gnome_keyring_store_password_sync(
-      &kGnomeSchema,
-      NULL,  // Default keyring.
-      form.origin.spec().c_str(),  // Display name.
-      UTF16ToUTF8(form.password_value).c_str(),
-      "origin_url", form.origin.spec().c_str(),
-      "action_url", form.action.spec().c_str(),
-      "username_element", UTF16ToUTF8(form.username_element).c_str(),
-      "username_value", UTF16ToUTF8(form.username_value).c_str(),
-      "password_element", UTF16ToUTF8(form.password_element).c_str(),
-      "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
-      "signon_realm", form.signon_realm.c_str(),
-      "ssl_valid", form.ssl_valid,
-      "preferred", form.preferred,
-      "date_created", Int64ToString(date_created.ToTimeT()).c_str(),
-      "blacklisted_by_user", form.blacklisted_by_user,
-      "scheme", form.scheme,
-      "application", GNOME_KEYRING_APPLICATION_CHROME,
-      NULL);
-
+  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
+    return true;
   if (result != GNOME_KEYRING_RESULT_OK) {
-    LOG(ERROR) << "Keyring save failed: "
+    LOG(ERROR) << "Keyring find failed: "
                << gnome_keyring_result_to_message(result);
     return false;
   }
+  ConvertFormList(found, forms);
   return true;
 }
 
-bool PasswordStoreGnome::FillSomeLogins(
-    bool autofillable,
-    std::vector<PasswordForm*>* forms) {
+bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin,
+                                                 const base::Time& get_end,
+                                                 PasswordFormList* forms) {
+  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
+  // We could walk the list and add items as we find them, but it is much
+  // easier to build the list and then filter the results.
+  PasswordFormList all_forms;
+  if (!GetAllLogins(&all_forms))
+    return false;
+
+  forms->reserve(forms->size() + all_forms.size());
+  for (size_t i = 0; i < all_forms.size(); ++i) {
+    if (get_begin <= all_forms[i]->date_created &&
+        (get_end.is_null() || all_forms[i]->date_created < get_end)) {
+      forms->push_back(all_forms[i]);
+    } else {
+      delete all_forms[i];
+    }
+  }
+
+  return true;
+}
+
+bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
+  return GetLoginsList(forms, true);
+}
+
+bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
+  return GetLoginsList(forms, false);
+}
+
+bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms,
+                                       bool autofillable) {
   DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
   GList* found = NULL;
   uint32_t blacklisted_by_user = !autofillable;
@@ -368,18 +361,40 @@
       "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
       GNOME_KEYRING_APPLICATION_CHROME,
       NULL);
-  if (result == GNOME_KEYRING_RESULT_OK) {
-    FillFormVector(found, forms);
-  } else if (result != GNOME_KEYRING_RESULT_NO_MATCH) {
+  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
+    return true;
+  if (result != GNOME_KEYRING_RESULT_OK) {
     LOG(ERROR) << "Keyring find failed: "
                << gnome_keyring_result_to_message(result);
     return false;
   }
+  ConvertFormList(found, forms);
   return true;
 }
 
-void PasswordStoreGnome::FillFormVector(GList* found,
-    std::vector<PasswordForm*>* forms) {
+bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
+  GList* found = NULL;
+  // We need to search for something, otherwise we get no results - so we search
+  // for the fixed application string.
+  GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
+      GNOME_KEYRING_ITEM_GENERIC_SECRET,
+      &found,
+      "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
+      GNOME_KEYRING_APPLICATION_CHROME,
+      NULL);
+  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
+    return true;
+  if (result != GNOME_KEYRING_RESULT_OK) {
+    LOG(ERROR) << "Keyring find failed: "
+               << gnome_keyring_result_to_message(result);
+    return false;
+  }
+  ConvertFormList(found, forms);
+  return true;
+}
+
+void NativeBackendGnome::ConvertFormList(GList* found,
+                                         PasswordFormList* forms) {
   GList* element = g_list_first(found);
   while (element != NULL) {
     GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
diff --git a/chrome/browser/password_manager/native_backend_gnome_x.h b/chrome/browser/password_manager/native_backend_gnome_x.h
new file mode 100644
index 0000000..aa554bed
--- /dev/null
+++ b/chrome/browser/password_manager/native_backend_gnome_x.h
@@ -0,0 +1,62 @@
+// 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_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_
+
+extern "C" {
+#include <gnome-keyring.h>
+}
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+#include "chrome/browser/password_manager/password_store_x.h"
+
+namespace webkit_glue {
+struct PasswordForm;
+}
+
+// NativeBackend implementation using GNOME Keyring.
+class NativeBackendGnome : public PasswordStoreX::NativeBackend {
+ public:
+  NativeBackendGnome();
+
+  virtual ~NativeBackendGnome();
+
+  virtual bool Init();
+
+  // Implements NativeBackend interface.
+  virtual bool AddLogin(const webkit_glue::PasswordForm& form);
+  virtual bool UpdateLogin(const webkit_glue::PasswordForm& form);
+  virtual bool RemoveLogin(const webkit_glue::PasswordForm& form);
+  virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
+                                          const base::Time& delete_end);
+  virtual bool GetLogins(const webkit_glue::PasswordForm& form,
+                         PasswordFormList* forms);
+  virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
+                                       const base::Time& get_end,
+                                       PasswordFormList* forms);
+  virtual bool GetAutofillableLogins(PasswordFormList* forms);
+  virtual bool GetBlacklistLogins(PasswordFormList* forms);
+
+ private:
+  // Reads PasswordForms from the keyring with the given autofillability state.
+  bool GetLoginsList(PasswordFormList* forms, bool autofillable);
+
+  // Helper for GetLoginsCreatedBetween().
+  bool GetAllLogins(PasswordFormList* forms);
+
+  // Parse all the results from the given GList into a PasswordFormList, and
+  // free the GList. PasswordForms are allocated on the heap, and should be
+  // deleted by the consumer.
+  void ConvertFormList(GList* found, PasswordFormList* forms);
+
+  static const GnomeKeyringPasswordSchema kGnomeSchema;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeBackendGnome);
+};
+
+#endif  // CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_GNOME_X_H_
diff --git a/chrome/browser/password_manager/password_store_kwallet.cc b/chrome/browser/password_manager/native_backend_kwallet_x.cc
similarity index 78%
rename from chrome/browser/password_manager/password_store_kwallet.cc
rename to chrome/browser/password_manager/native_backend_kwallet_x.cc
index 26f0ab95..501ce1f 100644
--- a/chrome/browser/password_manager/password_store_kwallet.cc
+++ b/chrome/browser/password_manager/native_backend_kwallet_x.cc
@@ -2,16 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/password_manager/password_store_kwallet.h"
+#include "chrome/browser/password_manager/native_backend_kwallet_x.h"
 
 #include <sstream>
 
 #include "base/logging.h"
-#include "base/md5.h"
 #include "base/pickle.h"
 #include "base/stl_util-inl.h"
 #include "base/string_util.h"
-#include "base/task.h"
+#include "chrome/browser/chrome_thread.h"
 
 using std::string;
 using std::vector;
@@ -19,31 +18,28 @@
 
 // We could localize these strings, but then changing your locale would cause
 // you to lose access to all your stored passwords. Maybe best not to do that.
-const char* PasswordStoreKWallet::kAppId = "Chrome";
-const char* PasswordStoreKWallet::kKWalletFolder = "Chrome Form Data";
+const char* NativeBackendKWallet::kAppId = "Chrome";
+const char* NativeBackendKWallet::kKWalletFolder = "Chrome Form Data";
 
-const char* PasswordStoreKWallet::kKWalletServiceName = "org.kde.kwalletd";
-const char* PasswordStoreKWallet::kKWalletPath = "/modules/kwalletd";
-const char* PasswordStoreKWallet::kKWalletInterface = "org.kde.KWallet";
-const char* PasswordStoreKWallet::kKLauncherServiceName = "org.kde.klauncher";
-const char* PasswordStoreKWallet::kKLauncherPath = "/KLauncher";
-const char* PasswordStoreKWallet::kKLauncherInterface = "org.kde.KLauncher";
+const char* NativeBackendKWallet::kKWalletServiceName = "org.kde.kwalletd";
+const char* NativeBackendKWallet::kKWalletPath = "/modules/kwalletd";
+const char* NativeBackendKWallet::kKWalletInterface = "org.kde.KWallet";
+const char* NativeBackendKWallet::kKLauncherServiceName = "org.kde.klauncher";
+const char* NativeBackendKWallet::kKLauncherPath = "/KLauncher";
+const char* NativeBackendKWallet::kKLauncherInterface = "org.kde.KLauncher";
 
-PasswordStoreKWallet::PasswordStoreKWallet(LoginDatabase* login_db,
-                                           Profile* profile,
-                                           WebDataService* web_data_service)
+NativeBackendKWallet::NativeBackendKWallet()
     : error_(NULL),
       connection_(NULL),
       proxy_(NULL) {
 }
 
-PasswordStoreKWallet::~PasswordStoreKWallet() {
-  if (proxy_) {
+NativeBackendKWallet::~NativeBackendKWallet() {
+  if (proxy_)
     g_object_unref(proxy_);
-  }
 }
 
-bool PasswordStoreKWallet::Init() {
+bool NativeBackendKWallet::Init() {
   // Initialize threading in dbus-glib - it should be fine for
   // dbus_g_thread_init to be called multiple times.
   if (!g_thread_supported())
@@ -64,7 +60,7 @@
   return true;
 }
 
-bool PasswordStoreKWallet::StartKWalletd() {
+bool NativeBackendKWallet::StartKWalletd() {
   // Sadly kwalletd doesn't use DBUS activation, so we have to make a call to
   // klauncher to start it.
   DBusGProxy* klauncher_proxy =
@@ -100,7 +96,7 @@
   return true;
 }
 
-bool PasswordStoreKWallet::InitWallet() {
+bool NativeBackendKWallet::InitWallet() {
   // Make a proxy to KWallet.
   proxy_ = dbus_g_proxy_new_for_name(connection_, kKWalletServiceName,
                                      kKWalletPath, kKWalletInterface);
@@ -129,26 +125,25 @@
   return true;
 }
 
-void PasswordStoreKWallet::AddLoginImpl(const PasswordForm& form) {
-  AutoLock l(kwallet_lock_);
+bool NativeBackendKWallet::AddLogin(const PasswordForm& form) {
   int wallet_handle = WalletHandle();
   if (wallet_handle == kInvalidKWalletHandle)
-    return;
+    return false;
 
   PasswordFormList forms;
   GetLoginsList(&forms, form.signon_realm, wallet_handle);
 
   forms.push_back(new PasswordForm(form));
-  SetLoginsList(forms, form.signon_realm, wallet_handle);
+  bool ok = SetLoginsList(forms, form.signon_realm, wallet_handle);
 
   STLDeleteElements(&forms);
+  return ok;
 }
 
-void PasswordStoreKWallet::UpdateLoginImpl(const PasswordForm& form) {
-  AutoLock l(kwallet_lock_);
+bool NativeBackendKWallet::UpdateLogin(const PasswordForm& form) {
   int wallet_handle = WalletHandle();
   if (wallet_handle == kInvalidKWalletHandle)
-    return;
+    return false;
 
   PasswordFormList forms;
   GetLoginsList(&forms, form.signon_realm, wallet_handle);
@@ -158,16 +153,16 @@
       *forms[i] = form;
   }
 
-  SetLoginsList(forms, form.signon_realm, wallet_handle);
+  bool ok = SetLoginsList(forms, form.signon_realm, wallet_handle);
 
   STLDeleteElements(&forms);
+  return ok;
 }
 
-void PasswordStoreKWallet::RemoveLoginImpl(const PasswordForm& form) {
-  AutoLock l(kwallet_lock_);
+bool NativeBackendKWallet::RemoveLogin(const PasswordForm& form) {
   int wallet_handle = WalletHandle();
   if (wallet_handle == kInvalidKWalletHandle)
-    return;
+    return false;
 
   PasswordFormList all_forms;
   GetLoginsList(&all_forms, form.signon_realm, wallet_handle);
@@ -181,18 +176,19 @@
       kept_forms.push_back(all_forms[i]);
   }
 
-  // Update the entry in the wallet.
-  SetLoginsList(kept_forms, form.signon_realm, wallet_handle);
+  // Update the entry in the wallet, possibly deleting it.
+  bool ok = SetLoginsList(kept_forms, form.signon_realm, wallet_handle);
+
   STLDeleteElements(&kept_forms);
+  return ok;
 }
 
-void PasswordStoreKWallet::RemoveLoginsCreatedBetweenImpl(
+bool NativeBackendKWallet::RemoveLoginsCreatedBetween(
     const base::Time& delete_begin,
     const base::Time& delete_end) {
-  AutoLock l(kwallet_lock_);
   int wallet_handle = WalletHandle();
   if (wallet_handle == kInvalidKWalletHandle)
-    return;
+    return false;
 
   // We could probably also use readEntryList here.
   char** realm_list = NULL;
@@ -204,8 +200,9 @@
                     G_TYPE_STRV,    &realm_list,
                     G_TYPE_INVALID);
   if (CheckError())
-    return;
+    return false;
 
+  bool ok = true;
   for (char** realm = realm_list; *realm; ++realm) {
     GArray* byte_array = NULL;
     dbus_g_proxy_call(proxy_, "readEntry", &error_,
@@ -237,49 +234,46 @@
       }
     }
 
-    SetLoginsList(kept_forms, signon_realm, wallet_handle);
+    if (!SetLoginsList(kept_forms, signon_realm, wallet_handle))
+      ok = false;
     STLDeleteElements(&kept_forms);
   }
   g_strfreev(realm_list);
+  return ok;
 }
 
-void PasswordStoreKWallet::GetLoginsImpl(GetLoginsRequest* request,
-                                         const PasswordForm& form) {
-  PasswordFormList forms;
-
-  AutoLock l(kwallet_lock_);
+bool NativeBackendKWallet::GetLogins(const PasswordForm& form,
+                                     PasswordFormList* forms) {
   int wallet_handle = WalletHandle();
-  if (wallet_handle != kInvalidKWalletHandle)
-    GetLoginsList(&forms, form.signon_realm, wallet_handle);
-
-  NotifyConsumer(request, forms);
+  if (wallet_handle == kInvalidKWalletHandle)
+    return false;
+  return GetLoginsList(forms, form.signon_realm, wallet_handle);
 }
 
-void PasswordStoreKWallet::GetAutofillableLoginsImpl(
-    GetLoginsRequest* request) {
-  std::vector<PasswordForm*> forms;
-  FillAutofillableLogins(&forms);
-  NotifyConsumer(request, forms);
+bool NativeBackendKWallet::GetLoginsCreatedBetween(const base::Time& get_begin,
+                                                   const base::Time& get_end,
+                                                   PasswordFormList* forms) {
+  int wallet_handle = WalletHandle();
+  if (wallet_handle == kInvalidKWalletHandle)
+    return false;
+  return GetLoginsList(forms, get_begin, get_end, wallet_handle);
 }
 
-void PasswordStoreKWallet::GetBlacklistLoginsImpl(
-    GetLoginsRequest* request) {
-  std::vector<PasswordForm*> forms;
-  FillBlacklistLogins(&forms);
-  NotifyConsumer(request, forms);
+bool NativeBackendKWallet::GetAutofillableLogins(PasswordFormList* forms) {
+  int wallet_handle = WalletHandle();
+  if (wallet_handle == kInvalidKWalletHandle)
+    return false;
+  return GetLoginsList(forms, true, wallet_handle);
 }
 
-bool PasswordStoreKWallet::FillAutofillableLogins(
-    std::vector<PasswordForm*>* forms) {
-  return FillSomeLogins(true, forms);
+bool NativeBackendKWallet::GetBlacklistLogins(PasswordFormList* forms) {
+  int wallet_handle = WalletHandle();
+  if (wallet_handle == kInvalidKWalletHandle)
+    return false;
+  return GetLoginsList(forms, false, wallet_handle);
 }
 
-bool PasswordStoreKWallet::FillBlacklistLogins(
-    std::vector<PasswordForm*>* forms) {
-  return FillSomeLogins(false, forms);
-}
-
-void PasswordStoreKWallet::GetLoginsList(PasswordFormList* forms,
+bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
                                          const string& signon_realm,
                                          int wallet_handle) {
   // Is there an entry in the wallet?
@@ -294,7 +288,7 @@
                     G_TYPE_INVALID);
 
   if (CheckError() || !has_entry)
-    return;
+    return false;
 
   GArray* byte_array = NULL;
   dbus_g_proxy_call(proxy_, "readEntry", &error_,
@@ -307,14 +301,93 @@
                     G_TYPE_INVALID);
 
   if (CheckError() || !byte_array || !byte_array->len)
-    return;
+    return false;
 
   Pickle pickle(byte_array->data, byte_array->len);
   DeserializeValue(signon_realm, pickle, forms);
   g_array_free(byte_array, true);
+
+  return true;
 }
 
-void PasswordStoreKWallet::SetLoginsList(const PasswordFormList& forms,
+bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
+                                         bool autofillable,
+                                         int wallet_handle) {
+  PasswordFormList all_forms;
+  if (!GetAllLogins(&all_forms, wallet_handle))
+    return false;
+
+  // We have to read all the entries, and then filter them here.
+  forms->reserve(forms->size() + all_forms.size());
+  for (size_t i = 0; i < all_forms.size(); ++i) {
+    if (all_forms[i]->blacklisted_by_user == !autofillable)
+      forms->push_back(all_forms[i]);
+    else
+      delete all_forms[i];
+  }
+
+  return true;
+}
+
+bool NativeBackendKWallet::GetLoginsList(PasswordFormList* forms,
+                                         const base::Time& begin,
+                                         const base::Time& end,
+                                         int wallet_handle) {
+  PasswordFormList all_forms;
+  if (!GetAllLogins(&all_forms, wallet_handle))
+    return false;
+
+  // We have to read all the entries, and then filter them here.
+  forms->reserve(forms->size() + all_forms.size());
+  for (size_t i = 0; i < all_forms.size(); ++i) {
+    if (begin <= all_forms[i]->date_created &&
+        (end.is_null() || all_forms[i]->date_created < end)) {
+      forms->push_back(all_forms[i]);
+    } else {
+      delete all_forms[i];
+    }
+  }
+
+  return true;
+}
+
+bool NativeBackendKWallet::GetAllLogins(PasswordFormList* forms,
+                                        int wallet_handle) {
+  // We could probably also use readEntryList here.
+  char** realm_list = NULL;
+  dbus_g_proxy_call(proxy_, "entryList", &error_,
+                    G_TYPE_INT,     wallet_handle,             // handle
+                    G_TYPE_STRING,  kKWalletFolder,            // folder
+                    G_TYPE_STRING,  kAppId,                    // appid
+                    G_TYPE_INVALID,
+                    G_TYPE_STRV,    &realm_list,
+                    G_TYPE_INVALID);
+  if (CheckError())
+    return false;
+
+  for (char** realm = realm_list; *realm; ++realm) {
+    GArray* byte_array = NULL;
+    dbus_g_proxy_call(proxy_, "readEntry", &error_,
+                      G_TYPE_INT,     wallet_handle,           // handle
+                      G_TYPE_STRING,  kKWalletFolder,          // folder
+                      G_TYPE_STRING,  *realm,                  // key
+                      G_TYPE_STRING,  kAppId,                  // appid
+                      G_TYPE_INVALID,
+                      DBUS_TYPE_G_UCHAR_ARRAY, &byte_array,
+                      G_TYPE_INVALID);
+
+    if (CheckError() || !byte_array || !byte_array->len)
+      continue;
+
+    Pickle pickle(byte_array->data, byte_array->len);
+    DeserializeValue(*realm, pickle, forms);
+    g_array_free(byte_array, true);
+  }
+  g_strfreev(realm_list);
+  return true;
+}
+
+bool NativeBackendKWallet::SetLoginsList(const PasswordFormList& forms,
                                          const string& signon_realm,
                                          int wallet_handle) {
   if (forms.empty()) {
@@ -331,7 +404,7 @@
     CheckError();
     if (ret != 0)
       LOG(ERROR) << "Bad return code " << ret << " from kwallet removeEntry";
-    return;
+    return ret == 0;
   }
 
   Pickle value;
@@ -358,61 +431,10 @@
   CheckError();
   if (ret != 0)
     LOG(ERROR) << "Bad return code " << ret << " from kwallet writeEntry";
+  return ret == 0;
 }
 
-bool PasswordStoreKWallet::FillSomeLogins(bool autofillable,
-                                          PasswordFormList* forms) {
-  AutoLock l(kwallet_lock_);
-  int wallet_handle = WalletHandle();
-  if (wallet_handle == kInvalidKWalletHandle)
-    return false;
-
-  // We could probably also use readEntryList here.
-  char** realm_list = NULL;
-  dbus_g_proxy_call(proxy_, "entryList", &error_,
-                    G_TYPE_INT,     wallet_handle,             // handle
-                    G_TYPE_STRING,  kKWalletFolder,            // folder
-                    G_TYPE_STRING,  kAppId,                    // appid
-                    G_TYPE_INVALID,
-                    G_TYPE_STRV,    &realm_list,
-                    G_TYPE_INVALID);
-  if (CheckError())
-    return false;
-
-  PasswordFormList all_forms;
-  for (char** realm = realm_list; *realm; ++realm) {
-    GArray* byte_array = NULL;
-    dbus_g_proxy_call(proxy_, "readEntry", &error_,
-                      G_TYPE_INT,     wallet_handle,           // handle
-                      G_TYPE_STRING,  kKWalletFolder,          // folder
-                      G_TYPE_STRING,  *realm,                  // key
-                      G_TYPE_STRING,  kAppId,                  // appid
-                      G_TYPE_INVALID,
-                      DBUS_TYPE_G_UCHAR_ARRAY, &byte_array,
-                      G_TYPE_INVALID);
-
-    if (CheckError() || !byte_array || !byte_array->len)
-      continue;
-
-    Pickle pickle(byte_array->data, byte_array->len);
-    DeserializeValue(*realm, pickle, &all_forms);
-    g_array_free(byte_array, true);
-  }
-  g_strfreev(realm_list);
-
-  // We have to read all the entries, and then filter them here.
-  forms->reserve(forms->size() + all_forms.size());
-  for (size_t i = 0; i < all_forms.size(); ++i) {
-    if (all_forms[i]->blacklisted_by_user == !autofillable)
-      forms->push_back(all_forms[i]);
-    else
-      delete all_forms[i];
-  }
-
-  return true;
-}
-
-bool PasswordStoreKWallet::CompareForms(const PasswordForm& a,
+bool NativeBackendKWallet::CompareForms(const PasswordForm& a,
                                         const PasswordForm& b,
                                         bool update_check) {
   // An update check doesn't care about the submit element.
@@ -425,7 +447,7 @@
          a.username_value   == b.username_value;
 }
 
-void PasswordStoreKWallet::SerializeValue(const PasswordFormList& forms,
+void NativeBackendKWallet::SerializeValue(const PasswordFormList& forms,
                                           Pickle* pickle) {
   pickle->WriteInt(kPickleVersion);
   pickle->WriteSize(forms.size());
@@ -447,7 +469,7 @@
   }
 }
 
-void PasswordStoreKWallet::DeserializeValue(const string& signon_realm,
+void NativeBackendKWallet::DeserializeValue(const string& signon_realm,
                                             const Pickle& pickle,
                                             PasswordFormList* forms) {
   void* iter = NULL;
@@ -487,14 +509,14 @@
   }
 }
 
-void PasswordStoreKWallet::ReadGURL(const Pickle& pickle, void** iter,
+void NativeBackendKWallet::ReadGURL(const Pickle& pickle, void** iter,
                                     GURL* url) {
   string url_string;
   pickle.ReadString(iter, &url_string);
   *url = GURL(url_string);
 }
 
-bool PasswordStoreKWallet::CheckError() {
+bool NativeBackendKWallet::CheckError() {
   if (error_) {
     LOG(ERROR) << "Failed to complete KWallet call: " << error_->message;
     g_error_free(error_);
@@ -504,7 +526,8 @@
   return false;
 }
 
-int PasswordStoreKWallet::WalletHandle() {
+int NativeBackendKWallet::WalletHandle() {
+  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
   // Open the wallet.
   int handle = kInvalidKWalletHandle;
   dbus_g_proxy_call(proxy_, "open", &error_,
diff --git a/chrome/browser/password_manager/native_backend_kwallet_x.h b/chrome/browser/password_manager/native_backend_kwallet_x.h
new file mode 100644
index 0000000..ad2b367
--- /dev/null
+++ b/chrome/browser/password_manager/native_backend_kwallet_x.h
@@ -0,0 +1,137 @@
+// 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_PASSWORD_MANAGER_NATIVE_BACKEND_KWALLET_X_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_KWALLET_X_H_
+
+#include <dbus/dbus-glib.h>
+#include <glib.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+#include "chrome/browser/password_manager/password_store_x.h"
+#include "webkit/glue/password_form.h"
+
+class Pickle;
+
+// NativeBackend implementation using KWallet.
+class NativeBackendKWallet : public PasswordStoreX::NativeBackend {
+ public:
+  NativeBackendKWallet();
+
+  virtual ~NativeBackendKWallet();
+
+  virtual bool Init();
+
+  // Implements NativeBackend interface.
+  virtual bool AddLogin(const webkit_glue::PasswordForm& form);
+  virtual bool UpdateLogin(const webkit_glue::PasswordForm& form);
+  virtual bool RemoveLogin(const webkit_glue::PasswordForm& form);
+  virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
+                                          const base::Time& delete_end);
+  virtual bool GetLogins(const webkit_glue::PasswordForm& form,
+                         PasswordFormList* forms);
+  virtual bool GetLoginsCreatedBetween(const base::Time& delete_begin,
+                                       const base::Time& delete_end,
+                                       PasswordFormList* forms);
+  virtual bool GetAutofillableLogins(PasswordFormList* forms);
+  virtual bool GetBlacklistLogins(PasswordFormList* forms);
+
+ private:
+  // Initialization.
+  bool StartKWalletd();
+  bool InitWallet();
+
+  // Reads PasswordForms from the wallet that match the given signon_realm.
+  bool GetLoginsList(PasswordFormList* forms,
+                     const std::string& signon_realm,
+                     int wallet_handle);
+
+  // Reads PasswordForms from the wallet with the given autofillability state.
+  bool GetLoginsList(PasswordFormList* forms,
+                     bool autofillable,
+                     int wallet_handle);
+
+  // Reads PasswordForms from the wallet created in the given time range.
+  bool GetLoginsList(PasswordFormList* forms,
+                     const base::Time& begin,
+                     const base::Time& end,
+                     int wallet_handle);
+
+  // Helper for some of the above GetLoginsList() methods.
+  bool GetAllLogins(PasswordFormList* forms, int wallet_handle);
+
+  // Writes a list of PasswordForms to the wallet with the given signon_realm.
+  // Overwrites any existing list for this signon_realm. Removes the entry if
+  // |forms| is empty. Returns true on success.
+  bool SetLoginsList(const PasswordFormList& forms,
+                     const std::string& signon_realm,
+                     int wallet_handle);
+
+  // Checks if the last DBus call returned an error. If it did, logs the error
+  // message, frees it and returns true.
+  // This must be called after every DBus call.
+  bool CheckError();
+
+  // Opens the wallet and ensures that the "Chrome Form Data" folder exists.
+  // Returns kInvalidWalletHandle on error.
+  int WalletHandle();
+
+  // Compares two PasswordForms and returns true if they are the same.
+  // If |update_check| is false, we only check the fields that are checked by
+  // LoginDatabase::UpdateLogin() when updating logins; otherwise, we check the
+  // fields that are checked by LoginDatabase::RemoveLogin() for removing them.
+  static bool CompareForms(const webkit_glue::PasswordForm& a,
+                           const webkit_glue::PasswordForm& b,
+                           bool update_check);
+
+  // Serializes a list of PasswordForms to be stored in the wallet.
+  static void SerializeValue(const PasswordFormList& forms, Pickle* pickle);
+
+  // Deserializes a list of PasswordForms from the wallet.
+  static void DeserializeValue(const std::string& signon_realm,
+                               const Pickle& pickle,
+                               PasswordFormList* forms);
+
+  // Convenience function to read a GURL from a Pickle. Assumes the URL has
+  // been written as a std::string.
+  static void ReadGURL(const Pickle& pickle, void** iter, GURL* url);
+
+  // In case the fields in the pickle ever change, version them so we can try to
+  // read old pickles. (Note: do not eat old pickles past the expiration date.)
+  static const int kPickleVersion = 0;
+
+  // Name of the application - will appear in kwallet's dialogs.
+  static const char* kAppId;
+  // Name of the folder to store passwords in.
+  static const char* kKWalletFolder;
+
+  // DBus stuff.
+  static const char* kKWalletServiceName;
+  static const char* kKWalletPath;
+  static const char* kKWalletInterface;
+  static const char* kKLauncherServiceName;
+  static const char* kKLauncherPath;
+  static const char* kKLauncherInterface;
+
+  // Invalid handle returned by WalletHandle().
+  static const int kInvalidKWalletHandle = -1;
+
+  // Error from the last DBus call. NULL when there's no error. Freed and
+  // cleared by CheckError().
+  GError* error_;
+  // Connection to the DBus session bus.
+  DBusGConnection* connection_;
+  // Proxy to the kwallet DBus service.
+  DBusGProxy* proxy_;
+
+  // The name of the wallet we've opened. Set during Init().
+  std::string wallet_name_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeBackendKWallet);
+};
+
+#endif  // CHROME_BROWSER_PASSWORD_MANAGER_NATIVE_BACKEND_KWALLET_X_H_
diff --git a/chrome/browser/password_manager/password_store_default.cc b/chrome/browser/password_manager/password_store_default.cc
index 8d3ff96..dc58f56 100644
--- a/chrome/browser/password_manager/password_store_default.cc
+++ b/chrome/browser/password_manager/password_store_default.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/password_manager/password_store_default.h"
 
+#include <vector>
+
 #include "chrome/browser/chrome_thread.h"
 #include "chrome/browser/password_manager/password_store_change.h"
 #include "chrome/browser/pref_service.h"
@@ -145,7 +147,7 @@
       static_cast<const WDResult<PasswordForms>*>(result)->GetValue();
   for (PasswordForms::const_iterator it = forms.begin();
        it != forms.end(); ++it) {
-    AddLoginImpl(**it);
+    AddLogin(**it);
     web_data_service_->RemoveLogin(**it);
     delete *it;
   }
diff --git a/chrome/browser/password_manager/password_store_gnome.h b/chrome/browser/password_manager/password_store_gnome.h
deleted file mode 100644
index 8927ba3..0000000
--- a/chrome/browser/password_manager/password_store_gnome.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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_PASSWORD_MANAGER_PASSWORD_STORE_GNOME_H_
-#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_GNOME_H_
-
-extern "C" {
-#include <gnome-keyring.h>
-}
-
-#include <vector>
-
-#include "base/lock.h"
-#include "chrome/browser/password_manager/login_database.h"
-#include "chrome/browser/password_manager/password_store.h"
-#include "chrome/browser/profile.h"
-#include "chrome/browser/webdata/web_data_service.h"
-
-class Profile;
-class Task;
-
-// PasswordStore implementation using GNOME Keyring.
-class PasswordStoreGnome : public PasswordStore {
- public:
-  PasswordStoreGnome(LoginDatabase* login_db,
-                     Profile* profile,
-                     WebDataService* web_data_service);
-
-  virtual bool Init();
-
- private:
-  virtual ~PasswordStoreGnome();
-
-  // Implements PasswordStore interface.
-  virtual void AddLoginImpl(const webkit_glue::PasswordForm& form);
-  virtual void UpdateLoginImpl(const webkit_glue::PasswordForm& form);
-  virtual void RemoveLoginImpl(const webkit_glue::PasswordForm& form);
-  virtual void RemoveLoginsCreatedBetweenImpl(const base::Time& delete_begin,
-                                              const base::Time& delete_end);
-  virtual void GetLoginsImpl(GetLoginsRequest* request,
-                             const webkit_glue::PasswordForm& form);
-  virtual void GetAutofillableLoginsImpl(GetLoginsRequest* request);
-  virtual void GetBlacklistLoginsImpl(GetLoginsRequest* request);
-  virtual bool FillAutofillableLogins(
-      std::vector<webkit_glue::PasswordForm*>* forms);
-  virtual bool FillBlacklistLogins(
-      std::vector<webkit_glue::PasswordForm*>* forms);
-
-  // Helper for AddLoginImpl() and UpdateLoginImpl() with a success status.
-  bool AddLoginHelper(const webkit_glue::PasswordForm& form,
-                      const base::Time& date_created);
-
-  // Helper for FillAutofillableLogins() and FillBlacklistLogins().
-  bool FillSomeLogins(bool autofillable,
-                      std::vector<webkit_glue::PasswordForm*>* forms);
-
-  // Parse all the results from the given GList into a
-  // vector<PasswordForm*>, and free the GList. PasswordForms are
-  // allocated on the heap, and should be deleted by the consumer.
-  void FillFormVector(GList* found,
-                      std::vector<webkit_glue::PasswordForm*>* forms);
-
-  static const GnomeKeyringPasswordSchema kGnomeSchema;
-
-  DISALLOW_COPY_AND_ASSIGN(PasswordStoreGnome);
-};
-
-#endif  // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_GNOME_H_
diff --git a/chrome/browser/password_manager/password_store_kwallet.h b/chrome/browser/password_manager/password_store_kwallet.h
deleted file mode 100644
index f7835fe..0000000
--- a/chrome/browser/password_manager/password_store_kwallet.h
+++ /dev/null
@@ -1,137 +0,0 @@
-// 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_PASSWORD_MANAGER_PASSWORD_STORE_KWALLET_H_
-#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_KWALLET_H_
-
-#include <dbus/dbus-glib.h>
-#include <glib.h>
-
-#include <string>
-#include <vector>
-
-#include "base/lock.h"
-#include "chrome/browser/password_manager/login_database.h"
-#include "chrome/browser/password_manager/password_store.h"
-#include "chrome/browser/webdata/web_data_service.h"
-#include "webkit/glue/password_form.h"
-
-class Pickle;
-class Profile;
-class Task;
-
-class PasswordStoreKWallet : public PasswordStore {
- public:
-  PasswordStoreKWallet(LoginDatabase* login_db,
-                       Profile* profile,
-                       WebDataService* web_data_service);
-
-  bool Init();
-
- private:
-  typedef std::vector<webkit_glue::PasswordForm*> PasswordFormList;
-
-  virtual ~PasswordStoreKWallet();
-
-  // Implements PasswordStore interface.
-  virtual void AddLoginImpl(const webkit_glue::PasswordForm& form);
-  virtual void UpdateLoginImpl(const webkit_glue::PasswordForm& form);
-  virtual void RemoveLoginImpl(const webkit_glue::PasswordForm& form);
-  virtual void RemoveLoginsCreatedBetweenImpl(const base::Time& delete_begin,
-                                              const base::Time& delete_end);
-  virtual void GetLoginsImpl(GetLoginsRequest* request,
-                             const webkit_glue::PasswordForm& form);
-  virtual void GetAutofillableLoginsImpl(GetLoginsRequest* request);
-  virtual void GetBlacklistLoginsImpl(GetLoginsRequest* request);
-  virtual bool FillAutofillableLogins(
-      std::vector<webkit_glue::PasswordForm*>* forms);
-  virtual bool FillBlacklistLogins(
-      std::vector<webkit_glue::PasswordForm*>* forms);
-
-  // Initialization.
-  bool StartKWalletd();
-  bool InitWallet();
-
-  // Reads a list of PasswordForms from the wallet that match the signon_realm.
-  void GetLoginsList(PasswordFormList* forms,
-                     const std::string& signon_realm,
-                     int wallet_handle);
-
-  // Writes a list of PasswordForms to the wallet with the given signon_realm.
-  // Overwrites any existing list for this signon_realm. Removes the entry if
-  // |forms| is empty.
-  void SetLoginsList(const PasswordFormList& forms,
-                     const std::string& signon_realm,
-                     int wallet_handle);
-
-  // Helper for FillAutofillableLogins() and FillBlacklistLogins().
-  bool FillSomeLogins(bool autofillable, PasswordFormList* forms);
-
-  // Checks if the last DBus call returned an error. If it did, logs the error
-  // message, frees it and returns true.
-  // This must be called after every DBus call.
-  bool CheckError();
-
-  // Opens the wallet and ensures that the "Chrome Form Data" folder exists.
-  // Returns kInvalidWalletHandle on error.
-  int WalletHandle();
-
-  // Compares two PasswordForms and returns true if they are the same.
-  // If |update_check| is false, we only check the fields that are checked by
-  // LoginDatabase::UpdateLogin() when updating logins; otherwise, we check the
-  // fields that are checked by LoginDatabase::RemoveLogin() for removing them.
-  static bool CompareForms(const webkit_glue::PasswordForm& a,
-                           const webkit_glue::PasswordForm& b,
-                           bool update_check);
-
-  // Serializes a list of PasswordForms to be stored in the wallet.
-  static void SerializeValue(const PasswordFormList& forms, Pickle* pickle);
-
-  // Deserializes a list of PasswordForms from the wallet.
-  static void DeserializeValue(const std::string& signon_realm,
-                               const Pickle& pickle,
-                               PasswordFormList* forms);
-
-  // Convenience function to read a GURL from a Pickle. Assumes the URL has
-  // been written as a std::string.
-  static void ReadGURL(const Pickle& pickle, void** iter, GURL* url);
-
-  // In case the fields in the pickle ever change, version them so we can try to
-  // read old pickles. (Note: do not eat old pickles past the expiration date.)
-  static const int kPickleVersion = 0;
-
-  // Name of the application - will appear in kwallet's dialogs.
-  static const char* kAppId;
-  // Name of the folder to store passwords in.
-  static const char* kKWalletFolder;
-
-  // DBus stuff.
-  static const char* kKWalletServiceName;
-  static const char* kKWalletPath;
-  static const char* kKWalletInterface;
-  static const char* kKLauncherServiceName;
-  static const char* kKLauncherPath;
-  static const char* kKLauncherInterface;
-
-  // Invalid handle returned by WalletHandle().
-  static const int kInvalidKWalletHandle = -1;
-
-  // Controls all access to kwallet DBus calls.
-  Lock kwallet_lock_;
-
-  // Error from the last DBus call. NULL when there's no error. Freed and
-  // cleared by CheckError().
-  GError* error_;
-  // Connection to the DBus session bus.
-  DBusGConnection* connection_;
-  // Proxy to the kwallet DBus service.
-  DBusGProxy* proxy_;
-
-  // The name of the wallet we've opened. Set during Init().
-  std::string wallet_name_;
-
-  DISALLOW_COPY_AND_ASSIGN(PasswordStoreKWallet);
-};
-
-#endif  // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_KWALLET_H_
diff --git a/chrome/browser/password_manager/password_store_x.cc b/chrome/browser/password_manager/password_store_x.cc
new file mode 100644
index 0000000..a9f7021
--- /dev/null
+++ b/chrome/browser/password_manager/password_store_x.cc
@@ -0,0 +1,231 @@
+// 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/password_manager/password_store_x.h"
+
+#include <map>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/stl_util-inl.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/password_manager/password_store_change.h"
+#include "chrome/common/notification_service.h"
+
+using std::vector;
+using webkit_glue::PasswordForm;
+
+PasswordStoreX::PasswordStoreX(LoginDatabase* login_db,
+                                   Profile* profile,
+                                   WebDataService* web_data_service,
+                                   NativeBackend* backend)
+    : PasswordStoreDefault(login_db, profile, web_data_service),
+      backend_(backend), migration_checked_(!backend), allow_fallback_(false) {
+}
+
+PasswordStoreX::~PasswordStoreX() {
+}
+
+void PasswordStoreX::AddLoginImpl(const PasswordForm& form) {
+  CheckMigration();
+  if (use_native_backend() && backend_->AddLogin(form)) {
+    PasswordStoreChangeList changes;
+    changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
+    NotificationService::current()->Notify(
+        NotificationType::LOGINS_CHANGED,
+        NotificationService::AllSources(),
+        Details<PasswordStoreChangeList>(&changes));
+    allow_fallback_ = false;
+  } else if (allow_default_store()) {
+    PasswordStoreDefault::AddLoginImpl(form);
+  }
+}
+
+void PasswordStoreX::UpdateLoginImpl(const PasswordForm& form) {
+  CheckMigration();
+  if (use_native_backend() && backend_->UpdateLogin(form)) {
+    PasswordStoreChangeList changes;
+    changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form));
+    NotificationService::current()->Notify(
+        NotificationType::LOGINS_CHANGED,
+        NotificationService::AllSources(),
+        Details<PasswordStoreChangeList>(&changes));
+    allow_fallback_ = false;
+  } else if (allow_default_store()) {
+    PasswordStoreDefault::UpdateLoginImpl(form);
+  }
+}
+
+void PasswordStoreX::RemoveLoginImpl(const PasswordForm& form) {
+  CheckMigration();
+  if (use_native_backend() && backend_->RemoveLogin(form)) {
+    PasswordStoreChangeList changes;
+    changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
+    NotificationService::current()->Notify(
+        NotificationType::LOGINS_CHANGED,
+        NotificationService::AllSources(),
+        Details<PasswordStoreChangeList>(&changes));
+    allow_fallback_ = false;
+  } else if (allow_default_store()) {
+    PasswordStoreDefault::RemoveLoginImpl(form);
+  }
+}
+
+void PasswordStoreX::RemoveLoginsCreatedBetweenImpl(
+    const base::Time& delete_begin,
+    const base::Time& delete_end) {
+  CheckMigration();
+  vector<PasswordForm*> forms;
+  if (use_native_backend() &&
+      backend_->GetLoginsCreatedBetween(delete_begin, delete_end, &forms) &&
+      backend_->RemoveLoginsCreatedBetween(delete_begin, delete_end)) {
+    PasswordStoreChangeList changes;
+    for (vector<PasswordForm*>::const_iterator it = forms.begin();
+         it != forms.end(); ++it) {
+      changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
+                                            **it));
+    }
+    NotificationService::current()->Notify(
+        NotificationType::LOGINS_CHANGED,
+        NotificationService::AllSources(),
+        Details<PasswordStoreChangeList>(&changes));
+    allow_fallback_ = false;
+  } else if (allow_default_store()) {
+    PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(delete_begin,
+                                                         delete_end);
+  }
+  STLDeleteElements(&forms);
+}
+
+void PasswordStoreX::GetLoginsImpl(GetLoginsRequest* request,
+                                     const PasswordForm& form) {
+  CheckMigration();
+  vector<PasswordForm*> forms;
+  if (use_native_backend() && backend_->GetLogins(form, &forms)) {
+    NotifyConsumer(request, forms);
+    allow_fallback_ = false;
+  } else if (allow_default_store()) {
+    PasswordStoreDefault::GetLoginsImpl(request, form);
+  } else {
+    // The consumer will be left hanging unless we reply.
+    NotifyConsumer(request, forms);
+  }
+}
+
+void PasswordStoreX::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
+  CheckMigration();
+  vector<PasswordForm*> forms;
+  if (use_native_backend() && backend_->GetAutofillableLogins(&forms)) {
+    NotifyConsumer(request, forms);
+    allow_fallback_ = false;
+  } else if (allow_default_store()) {
+    PasswordStoreDefault::GetAutofillableLoginsImpl(request);
+  } else {
+    // The consumer will be left hanging unless we reply.
+    NotifyConsumer(request, forms);
+  }
+}
+
+void PasswordStoreX::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
+  CheckMigration();
+  vector<PasswordForm*> forms;
+  if (use_native_backend() && backend_->GetBlacklistLogins(&forms)) {
+    NotifyConsumer(request, forms);
+    allow_fallback_ = false;
+  } else if (allow_default_store()) {
+    PasswordStoreDefault::GetBlacklistLoginsImpl(request);
+  } else {
+    // The consumer will be left hanging unless we reply.
+    NotifyConsumer(request, forms);
+  }
+}
+
+bool PasswordStoreX::FillAutofillableLogins(vector<PasswordForm*>* forms) {
+  CheckMigration();
+  if (use_native_backend() && backend_->GetAutofillableLogins(forms)) {
+    allow_fallback_ = false;
+    return true;
+  }
+  if (allow_default_store())
+    return PasswordStoreDefault::FillAutofillableLogins(forms);
+  return false;
+}
+
+bool PasswordStoreX::FillBlacklistLogins(vector<PasswordForm*>* forms) {
+  CheckMigration();
+  if (use_native_backend() && backend_->GetBlacklistLogins(forms)) {
+    allow_fallback_ = false;
+    return true;
+  }
+  if (allow_default_store())
+    return PasswordStoreDefault::FillBlacklistLogins(forms);
+  return false;
+}
+
+void PasswordStoreX::CheckMigration() {
+  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
+  if (migration_checked_ || !backend_.get())
+    return;
+  migration_checked_ = true;
+  ssize_t migrated = MigrateLogins();
+  if (migrated > 0) {
+    LOG(INFO) << "Migrated " << migrated << " passwords to native store.";
+  } else if (migrated == 0) {
+    // As long as we are able to migrate some passwords, we know the native
+    // store is working. But if there is nothing to migrate, the "migration"
+    // can succeed even when the native store would fail. In this case we
+    // allow a later fallback to the default store. Once any later operation
+    // succeeds on the native store, we will no longer allow it.
+    allow_fallback_ = true;
+  } else {
+    LOG(WARNING) << "Native password store migration failed! " <<
+                 "Falling back on default (unencrypted) store.";
+    backend_.reset(NULL);
+  }
+}
+
+bool PasswordStoreX::allow_default_store() {
+  if (allow_fallback_) {
+    LOG(WARNING) << "Native password store failed! " <<
+                 "Falling back on default (unencrypted) store.";
+    backend_.reset(NULL);
+    // Don't warn again. We'll use the default store because backend_ is NULL.
+    allow_fallback_ = false;
+  }
+  return !backend_.get();
+}
+
+ssize_t PasswordStoreX::MigrateLogins() {
+  DCHECK(backend_.get());
+  vector<PasswordForm*> forms;
+  bool ok = PasswordStoreDefault::FillAutofillableLogins(&forms) &&
+      PasswordStoreDefault::FillBlacklistLogins(&forms);
+  if (ok) {
+    // We add all the passwords (and blacklist entries) to the native backend
+    // before attempting to remove any from the login database, to make sure we
+    // don't somehow end up with some of the passwords in one store and some in
+    // another. We'll always have at least one intact store this way.
+    for (size_t i = 0; i < forms.size(); ++i) {
+      if (!backend_->AddLogin(*forms[i])) {
+        ok = false;
+        break;
+      }
+    }
+    if (ok) {
+      for (size_t i = 0; i < forms.size(); ++i) {
+        // If even one of these calls to RemoveLoginImpl() succeeds, then we
+        // should prefer the native backend to the now-incomplete login
+        // database. Thus we want to return a success status even in the case
+        // where some fail. The only real problem with this is that we might
+        // leave passwords in the login database and never come back to clean
+        // them out if any of these calls do fail.
+        // TODO(mdm): Really we should just delete the login database file.
+        PasswordStoreDefault::RemoveLoginImpl(*forms[i]);
+      }
+    }
+  }
+  ssize_t result = ok ? forms.size() : -1;
+  STLDeleteElements(&forms);
+  return result;
+}
diff --git a/chrome/browser/password_manager/password_store_x.h b/chrome/browser/password_manager/password_store_x.h
new file mode 100644
index 0000000..fd3fe42
--- /dev/null
+++ b/chrome/browser/password_manager/password_store_x.h
@@ -0,0 +1,107 @@
+// 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_PASSWORD_MANAGER_PASSWORD_STORE_X_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_X_H_
+
+#include <vector>
+
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "chrome/browser/password_manager/password_store_default.h"
+
+class LoginDatabase;
+class Profile;
+class WebDataService;
+
+// PasswordStoreX is used on Linux and other non-Windows, non-Mac OS X
+// operating systems. It uses a "native backend" to actually store the password
+// data when such a backend is available, and otherwise falls back to using the
+// login database like PasswordStoreDefault. It also handles automatically
+// migrating password data to a native backend from the login database.
+//
+// There are currently native backends for GNOME Keyring and KWallet.
+class PasswordStoreX : public PasswordStoreDefault {
+ public:
+  // NativeBackends more or less implement the PaswordStore interface, but
+  // with return values rather than implicit consumer notification.
+  class NativeBackend {
+   public:
+    typedef std::vector<webkit_glue::PasswordForm*> PasswordFormList;
+
+    virtual ~NativeBackend() {}
+
+    virtual bool Init() = 0;
+
+    virtual bool AddLogin(const webkit_glue::PasswordForm& form) = 0;
+    virtual bool UpdateLogin(const webkit_glue::PasswordForm& form) = 0;
+    virtual bool RemoveLogin(const webkit_glue::PasswordForm& form) = 0;
+    virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
+                                            const base::Time& delete_end) = 0;
+    virtual bool GetLogins(const webkit_glue::PasswordForm& form,
+                           PasswordFormList* forms) = 0;
+    virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
+                                         const base::Time& get_end,
+                                         PasswordFormList* forms) = 0;
+    virtual bool GetAutofillableLogins(PasswordFormList* forms) = 0;
+    virtual bool GetBlacklistLogins(PasswordFormList* forms) = 0;
+  };
+
+  // Takes ownership of |login_db| and |backend|. |backend| may be NULL in which
+  // case this PasswordStoreX will act the same as PasswordStoreDefault.
+  PasswordStoreX(LoginDatabase* login_db,
+                   Profile* profile,
+                   WebDataService* web_data_service,
+                   NativeBackend* backend);
+
+ private:
+  friend class PasswordStoreXTest;
+
+  virtual ~PasswordStoreX();
+
+  // Implements PasswordStore interface.
+  virtual void AddLoginImpl(const webkit_glue::PasswordForm& form);
+  virtual void UpdateLoginImpl(const webkit_glue::PasswordForm& form);
+  virtual void RemoveLoginImpl(const webkit_glue::PasswordForm& form);
+  virtual void RemoveLoginsCreatedBetweenImpl(const base::Time& delete_begin,
+                                              const base::Time& delete_end);
+  virtual void GetLoginsImpl(GetLoginsRequest* request,
+                             const webkit_glue::PasswordForm& form);
+  virtual void GetAutofillableLoginsImpl(GetLoginsRequest* request);
+  virtual void GetBlacklistLoginsImpl(GetLoginsRequest* request);
+  virtual bool FillAutofillableLogins(
+      std::vector<webkit_glue::PasswordForm*>* forms);
+  virtual bool FillBlacklistLogins(
+      std::vector<webkit_glue::PasswordForm*>* forms);
+
+  // Check to see whether migration is necessary, and perform it if so.
+  void CheckMigration();
+
+  // Return true if we should try using the native backend.
+  bool use_native_backend() { return !!backend_.get(); }
+
+  // Return true if we can fall back on the default store, warning the first
+  // time we call it when falling back is necessary. See |allow_fallback_|.
+  bool allow_default_store();
+
+  // Synchronously migrates all the passwords stored in the login database to
+  // the native backend. If successful, the login database will be left with no
+  // stored passwords, and the number of passwords migrated will be returned.
+  // (This might be 0 if migration was not necessary.) Returns < 0 on failure.
+  ssize_t MigrateLogins();
+
+  // The native backend in use, or NULL if none.
+  scoped_ptr<NativeBackend> backend_;
+  // Whether we have already attempted migration to the native store.
+  bool migration_checked_;
+  // Whether we should allow falling back to the default store. If there is
+  // nothing to migrate, then the first attempt to use the native store will
+  // be the first time we try to use it and we should allow falling back. If
+  // we have migrated successfully, then we do not allow falling back.
+  bool allow_fallback_;
+
+  DISALLOW_COPY_AND_ASSIGN(PasswordStoreX);
+};
+
+#endif  // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_X_H_
diff --git a/chrome/browser/password_manager/password_store_x_unittest.cc b/chrome/browser/password_manager/password_store_x_unittest.cc
new file mode 100644
index 0000000..1b4708c
--- /dev/null
+++ b/chrome/browser/password_manager/password_store_x_unittest.cc
@@ -0,0 +1,771 @@
+// 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 "base/basictypes.h"
+#include "base/scoped_temp_dir.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/waitable_event.h"
+#include "chrome/browser/password_manager/password_form_data.h"
+#include "chrome/browser/password_manager/password_store_change.h"
+#include "chrome/browser/password_manager/password_store_x.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::WaitableEvent;
+using testing::_;
+using testing::DoAll;
+using testing::ElementsAreArray;
+using testing::Pointee;
+using testing::Property;
+using testing::WithArg;
+using webkit_glue::PasswordForm;
+
+namespace {
+
+class MockPasswordStoreConsumer : public PasswordStoreConsumer {
+ public:
+  MOCK_METHOD2(OnPasswordStoreRequestDone,
+               void(int, const std::vector<PasswordForm*>&));
+};
+
+class MockWebDataServiceConsumer : public WebDataServiceConsumer {
+ public:
+  MOCK_METHOD2(OnWebDataServiceRequestDone, void(WebDataService::Handle,
+                                                 const WDTypedResult*));
+};
+
+class SignalingTask : public Task {
+ public:
+  explicit SignalingTask(WaitableEvent* event) : event_(event) {
+  }
+  virtual void Run() {
+    event_->Signal();
+  }
+ private:
+  WaitableEvent* event_;
+};
+
+class MockNotificationObserver : public NotificationObserver {
+ public:
+  MOCK_METHOD3(Observe, void(NotificationType,
+                             const NotificationSource&,
+                             const NotificationDetails&));
+};
+
+// This class will add and remove a mock notification observer from
+// the DB thread.
+class DBThreadObserverHelper
+    : public base::RefCountedThreadSafe<DBThreadObserverHelper,
+                                        ChromeThread::DeleteOnDBThread> {
+ public:
+  DBThreadObserverHelper() : done_event_(true, false) {}
+
+  void Init() {
+    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+    ChromeThread::PostTask(
+        ChromeThread::DB,
+        FROM_HERE,
+        NewRunnableMethod(this, &DBThreadObserverHelper::AddObserverTask));
+    done_event_.Wait();
+  }
+
+  virtual ~DBThreadObserverHelper() {
+    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
+    registrar_.RemoveAll();
+  }
+
+  MockNotificationObserver& observer() {
+    return observer_;
+  }
+
+ protected:
+  friend class base::RefCountedThreadSafe<DBThreadObserverHelper>;
+
+  void AddObserverTask() {
+    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
+    registrar_.Add(&observer_,
+                   NotificationType::LOGINS_CHANGED,
+                   NotificationService::AllSources());
+    done_event_.Signal();
+  }
+
+  WaitableEvent done_event_;
+  NotificationRegistrar registrar_;
+  MockNotificationObserver observer_;
+};
+
+class FailingBackend : public PasswordStoreX::NativeBackend {
+ public:
+  virtual bool Init() { return true; }
+
+  virtual bool AddLogin(const PasswordForm& form) { return false; }
+  virtual bool UpdateLogin(const PasswordForm& form) { return false; }
+  virtual bool RemoveLogin(const PasswordForm& form) { return false; }
+
+  virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
+                                          const base::Time& delete_end) {
+    return false;
+  }
+
+  virtual bool GetLogins(const PasswordForm& form, PasswordFormList* forms) {
+    return false;
+  }
+
+  virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
+                                       const base::Time& get_end,
+                                       PasswordFormList* forms) {
+    return false;
+  }
+
+  virtual bool GetAutofillableLogins(PasswordFormList* forms) { return false; }
+  virtual bool GetBlacklistLogins(PasswordFormList* forms) { return false; }
+};
+
+class MockBackend : public PasswordStoreX::NativeBackend {
+ public:
+  virtual bool Init() { return true; }
+
+  virtual bool AddLogin(const PasswordForm& form) {
+    all_forms_.push_back(form);
+    return true;
+  }
+
+  virtual bool UpdateLogin(const PasswordForm& form) {
+    for (size_t i = 0; i < all_forms_.size(); ++i)
+      if (CompareForms(all_forms_[i], form, true))
+        all_forms_[i] = form;
+    return true;
+  }
+
+  virtual bool RemoveLogin(const PasswordForm& form) {
+    for (size_t i = 0; i < all_forms_.size(); ++i)
+      if (CompareForms(all_forms_[i], form, false))
+        erase(i--);
+    return true;
+  }
+
+  virtual bool RemoveLoginsCreatedBetween(const base::Time& delete_begin,
+                                          const base::Time& delete_end) {
+    for (size_t i = 0; i < all_forms_.size(); ++i) {
+      if (delete_begin <= all_forms_[i].date_created &&
+          (delete_end.is_null() || all_forms_[i].date_created < delete_end))
+        erase(i--);
+    }
+    return true;
+  }
+
+  virtual bool GetLogins(const PasswordForm& form, PasswordFormList* forms) {
+    for (size_t i = 0; i < all_forms_.size(); ++i)
+      if (all_forms_[i].signon_realm == form.signon_realm)
+        forms->push_back(new PasswordForm(all_forms_[i]));
+    return true;
+  }
+
+  virtual bool GetLoginsCreatedBetween(const base::Time& get_begin,
+                                       const base::Time& get_end,
+                                       PasswordFormList* forms) {
+    for (size_t i = 0; i < all_forms_.size(); ++i)
+      if (get_begin <= all_forms_[i].date_created &&
+          (get_end.is_null() || all_forms_[i].date_created < get_end))
+        forms->push_back(new PasswordForm(all_forms_[i]));
+    return true;
+  }
+
+  virtual bool GetAutofillableLogins(PasswordFormList* forms) {
+    for (size_t i = 0; i < all_forms_.size(); ++i)
+      if (!all_forms_[i].blacklisted_by_user)
+        forms->push_back(new PasswordForm(all_forms_[i]));
+    return true;
+  }
+
+  virtual bool GetBlacklistLogins(PasswordFormList* forms) {
+    for (size_t i = 0; i < all_forms_.size(); ++i)
+      if (all_forms_[i].blacklisted_by_user)
+        forms->push_back(new PasswordForm(all_forms_[i]));
+    return true;
+  }
+
+ private:
+  void erase(size_t index) {
+    if (index < all_forms_.size() - 1)
+      all_forms_[index] = all_forms_[all_forms_.size() - 1];
+    all_forms_.pop_back();
+  }
+
+  bool CompareForms(const PasswordForm& a, const PasswordForm& b, bool update) {
+    // An update check doesn't care about the submit element.
+    if (!update && a.submit_element != b.submit_element)
+      return false;
+    return a.origin           == b.origin &&
+           a.password_element == b.password_element &&
+           a.signon_realm     == b.signon_realm &&
+           a.username_element == b.username_element &&
+           a.username_value   == b.username_value;
+  }
+
+  std::vector<PasswordForm> all_forms_;
+};
+
+class MockLoginDatabaseReturn {
+ public:
+  MOCK_METHOD1(OnLoginDatabaseQueryDone,
+               void(const std::vector<PasswordForm*>&));
+};
+
+class LoginDatabaseQueryTask : public Task {
+ public:
+  LoginDatabaseQueryTask(LoginDatabase* login_db,
+                         bool autofillable,
+                         MockLoginDatabaseReturn* mock_return)
+      : login_db_(login_db), autofillable_(autofillable),
+        mock_return_(mock_return) {
+  }
+
+  virtual void Run() {
+    std::vector<PasswordForm*> forms;
+    if (autofillable_)
+      login_db_->GetAutofillableLogins(&forms);
+    else
+      login_db_->GetBlacklistLogins(&forms);
+    mock_return_->OnLoginDatabaseQueryDone(forms);
+  }
+
+ private:
+  LoginDatabase* login_db_;
+  bool autofillable_;
+  MockLoginDatabaseReturn* mock_return_;
+};
+
+const PasswordFormData g_autofillable_data[] = {
+  { PasswordForm::SCHEME_HTML,
+    "http://foo.example.com",
+    "http://foo.example.com/origin",
+    "http://foo.example.com/action",
+    L"submit_element",
+    L"username_element",
+    L"password_element",
+    L"username_value",
+    L"password_value",
+    true, false, 1 },
+  { PasswordForm::SCHEME_HTML,
+    "http://bar.example.com",
+    "http://bar.example.com/origin",
+    "http://bar.example.com/action",
+    L"submit_element",
+    L"username_element",
+    L"password_element",
+    L"username_value",
+    L"password_value",
+    true, false, 2 },
+  { PasswordForm::SCHEME_HTML,
+    "http://baz.example.com",
+    "http://baz.example.com/origin",
+    "http://baz.example.com/action",
+    L"submit_element",
+    L"username_element",
+    L"password_element",
+    L"username_value",
+    L"password_value",
+    true, false, 3 },
+};
+const PasswordFormData g_blacklisted_data[] = {
+  { PasswordForm::SCHEME_HTML,
+    "http://blacklisted.example.com",
+    "http://blacklisted.example.com/origin",
+    "http://blacklisted.example.com/action",
+    L"submit_element",
+    L"username_element",
+    L"password_element",
+    NULL,
+    NULL,
+    false, false, 1 },
+  { PasswordForm::SCHEME_HTML,
+    "http://blacklisted2.example.com",
+    "http://blacklisted2.example.com/origin",
+    "http://blacklisted2.example.com/action",
+    L"submit_element",
+    L"username_element",
+    L"password_element",
+    NULL,
+    NULL,
+    false, false, 2 },
+};
+
+}  // anonymous namespace
+
+typedef std::vector<PasswordForm*> VectorOfForms;
+
+// LoginDatabase isn't reference counted, but in these unit tests that won't be
+// a problem as it always outlives the threads we post tasks to.
+template<>
+struct RunnableMethodTraits<LoginDatabase> {
+  void RetainCallee(LoginDatabase*) {}
+  void ReleaseCallee(LoginDatabase*) {}
+};
+
+enum BackendType {
+  NO_BACKEND,
+  FAILING_BACKEND,
+  WORKING_BACKEND
+};
+
+class PasswordStoreXTest : public testing::TestWithParam<BackendType> {
+ protected:
+  PasswordStoreXTest()
+      : ui_thread_(ChromeThread::UI, &message_loop_),
+        db_thread_(ChromeThread::DB) {
+  }
+
+  virtual void SetUp() {
+    ASSERT_TRUE(db_thread_.Start());
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+    profile_.reset(new TestingProfile());
+
+    login_db_.reset(new LoginDatabase());
+    ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append(
+        FILE_PATH_LITERAL("login_test"))));
+
+    wds_ = new WebDataService();
+    ASSERT_TRUE(wds_->Init(temp_dir_.path()));
+  }
+
+  virtual void TearDown() {
+    wds_->Shutdown();
+    MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask);
+    MessageLoop::current()->Run();
+    db_thread_.Stop();
+  }
+
+  PasswordStoreX::NativeBackend* GetBackend() {
+    switch (GetParam()) {
+      case FAILING_BACKEND:
+        return new FailingBackend();
+      case WORKING_BACKEND:
+        return new MockBackend();
+      default:
+        return NULL;
+    }
+  }
+
+  MessageLoopForUI message_loop_;
+  ChromeThread ui_thread_;
+  ChromeThread db_thread_;  // PasswordStore, WDS schedule work on this thread.
+
+  scoped_ptr<LoginDatabase> login_db_;
+  scoped_ptr<TestingProfile> profile_;
+  scoped_refptr<WebDataService> wds_;
+  ScopedTempDir temp_dir_;
+};
+
+ACTION(STLDeleteElements0) {
+  STLDeleteContainerPointers(arg0.begin(), arg0.end());
+}
+
+ACTION(QuitUIMessageLoop) {
+  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+  MessageLoop::current()->Quit();
+}
+
+MATCHER(EmptyWDResult, "") {
+  return static_cast<const WDResult<std::vector<PasswordForm*> >*>(
+      arg)->GetValue().empty();
+}
+
+TEST_P(PasswordStoreXTest, WDSMigration) {
+  VectorOfForms expected_autofillable;
+  for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(g_autofillable_data); ++i) {
+    expected_autofillable.push_back(
+        CreatePasswordFormFromData(g_autofillable_data[i]));
+  }
+
+  VectorOfForms expected_blacklisted;
+  for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(g_blacklisted_data); ++i) {
+    expected_blacklisted.push_back(
+        CreatePasswordFormFromData(g_blacklisted_data[i]));
+  }
+
+  // Populate the WDS with logins that should be migrated.
+  for (VectorOfForms::iterator it = expected_autofillable.begin();
+       it != expected_autofillable.end(); ++it) {
+    wds_->AddLogin(**it);
+  }
+  for (VectorOfForms::iterator it = expected_blacklisted.begin();
+       it != expected_blacklisted.end(); ++it) {
+    wds_->AddLogin(**it);
+  }
+
+  // The WDS schedules tasks to run on the DB thread so we schedule yet another
+  // task to notify us that it's safe to carry on with the test.
+  WaitableEvent done(false, false);
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+
+  // Initializing the PasswordStore should trigger a migration.
+  scoped_refptr<PasswordStoreX> store(
+      new PasswordStoreX(login_db_.release(),
+                           profile_.get(),
+                           wds_.get(),
+                           GetBackend()));
+  store->Init();
+
+  // Check that the migration preference has not been initialized.
+  ASSERT_TRUE(NULL == profile_->GetPrefs()->FindPreference(
+      prefs::kLoginDatabaseMigrated));
+
+  // Again, the WDS schedules tasks to run on the DB thread, so schedule a task
+  // to signal us when it is safe to continue.
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+
+  // Let the WDS callbacks proceed so the logins can be migrated.
+  MessageLoop::current()->RunAllPending();
+
+  MockPasswordStoreConsumer consumer;
+
+  // Make sure we quit the MessageLoop even if the test fails.
+  ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
+      .WillByDefault(QuitUIMessageLoop());
+
+  // The autofillable forms should have been migrated from the WDS to the login
+  // database.
+  EXPECT_CALL(consumer,
+      OnPasswordStoreRequestDone(_,
+          ContainsAllPasswordForms(expected_autofillable)))
+      .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
+
+  store->GetAutofillableLogins(&consumer);
+  MessageLoop::current()->Run();
+
+  // The blacklisted forms should have been migrated from the WDS to the login
+  // database.
+  EXPECT_CALL(consumer,
+      OnPasswordStoreRequestDone(_,
+          ContainsAllPasswordForms(expected_blacklisted)))
+      .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
+
+  store->GetBlacklistLogins(&consumer);
+  MessageLoop::current()->Run();
+
+  // Check that the migration updated the migrated preference.
+  ASSERT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kLoginDatabaseMigrated));
+
+  MockWebDataServiceConsumer wds_consumer;
+
+  // No autofillable logins should be left in the WDS.
+  EXPECT_CALL(wds_consumer,
+      OnWebDataServiceRequestDone(_, EmptyWDResult()));
+
+  wds_->GetAutofillableLogins(&wds_consumer);
+
+  // Wait for the WDS methods to execute on the DB thread.
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+
+  // Handle the callback from the WDS.
+  MessageLoop::current()->RunAllPending();
+
+  // Likewise, no blacklisted logins should be left in the WDS.
+  EXPECT_CALL(wds_consumer,
+      OnWebDataServiceRequestDone(_, EmptyWDResult()));
+
+  wds_->GetBlacklistLogins(&wds_consumer);
+
+  // Wait for the WDS methods to execute on the DB thread.
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+
+  // Handle the callback from the WDS.
+  MessageLoop::current()->RunAllPending();
+
+  STLDeleteElements(&expected_autofillable);
+  STLDeleteElements(&expected_blacklisted);
+}
+
+TEST_P(PasswordStoreXTest, WDSMigrationAlreadyDone) {
+  PasswordFormData wds_data[] = {
+    { PasswordForm::SCHEME_HTML,
+      "http://bar.example.com",
+      "http://bar.example.com/origin",
+      "http://bar.example.com/action",
+      L"submit_element",
+      L"username_element",
+      L"password_element",
+      L"username_value",
+      L"password_value",
+      true, false, 1 },
+  };
+
+  VectorOfForms unexpected_autofillable;
+  for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(wds_data); ++i) {
+    unexpected_autofillable.push_back(
+        CreatePasswordFormFromData(wds_data[i]));
+  }
+
+  // Populate the WDS with logins that should be migrated.
+  for (VectorOfForms::iterator it = unexpected_autofillable.begin();
+       it != unexpected_autofillable.end(); ++it) {
+    wds_->AddLogin(**it);
+  }
+
+  // The WDS schedules tasks to run on the DB thread so we schedule yet another
+  // task to notify us that it's safe to carry on with the test.
+  WaitableEvent done(false, false);
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+
+  // Prentend that the migration has already taken place.
+  profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
+                                            true);
+
+  // Initializing the PasswordStore shouldn't trigger a migration.
+  scoped_refptr<PasswordStoreX> store(
+      new PasswordStoreX(login_db_.release(),
+                           profile_.get(),
+                           wds_.get(),
+                           GetBackend()));
+  store->Init();
+
+  MockPasswordStoreConsumer consumer;
+  // Make sure we quit the MessageLoop even if the test fails.
+  ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
+      .WillByDefault(QuitUIMessageLoop());
+
+  // No forms should be migrated.
+  VectorOfForms empty;
+  EXPECT_CALL(consumer,
+      OnPasswordStoreRequestDone(_,
+          ContainsAllPasswordForms(empty)))
+      .WillOnce(QuitUIMessageLoop());
+
+  store->GetAutofillableLogins(&consumer);
+  MessageLoop::current()->Run();
+
+  STLDeleteElements(&unexpected_autofillable);
+}
+
+TEST_P(PasswordStoreXTest, Notifications) {
+  // Pretend that the migration has already taken place.
+  profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
+                                            true);
+
+  // Initializing the PasswordStore shouldn't trigger a migration.
+  scoped_refptr<PasswordStoreX> store(
+      new PasswordStoreX(login_db_.release(),
+                           profile_.get(),
+                           wds_.get(),
+                           GetBackend()));
+  store->Init();
+
+  PasswordFormData form_data =
+  { PasswordForm::SCHEME_HTML,
+    "http://bar.example.com",
+    "http://bar.example.com/origin",
+    "http://bar.example.com/action",
+    L"submit_element",
+    L"username_element",
+    L"password_element",
+    L"username_value",
+    L"password_value",
+    true, false, 1 };
+  scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
+
+  scoped_refptr<DBThreadObserverHelper> helper = new DBThreadObserverHelper;
+  helper->Init();
+
+  const PasswordStoreChange expected_add_changes[] = {
+    PasswordStoreChange(PasswordStoreChange::ADD, *form),
+  };
+
+  EXPECT_CALL(helper->observer(),
+              Observe(NotificationType(NotificationType::LOGINS_CHANGED),
+                      NotificationService::AllSources(),
+                      Property(&Details<const PasswordStoreChangeList>::ptr,
+                               Pointee(ElementsAreArray(
+                                   expected_add_changes)))));
+
+  // Adding a login should trigger a notification.
+  store->AddLogin(*form);
+
+  // The PasswordStore schedules tasks to run on the DB thread so we schedule
+  // yet another task to notify us that it's safe to carry on with the test.
+  WaitableEvent done(false, false);
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+
+  // Change the password.
+  form->password_value = WideToUTF16(L"a different password");
+
+  const PasswordStoreChange expected_update_changes[] = {
+    PasswordStoreChange(PasswordStoreChange::UPDATE, *form),
+  };
+
+  EXPECT_CALL(helper->observer(),
+              Observe(NotificationType(NotificationType::LOGINS_CHANGED),
+                      NotificationService::AllSources(),
+                      Property(&Details<const PasswordStoreChangeList>::ptr,
+                               Pointee(ElementsAreArray(
+                                   expected_update_changes)))));
+
+  // Updating the login with the new password should trigger a notification.
+  store->UpdateLogin(*form);
+
+  // Wait for PasswordStore to send the notification.
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+
+  const PasswordStoreChange expected_delete_changes[] = {
+    PasswordStoreChange(PasswordStoreChange::REMOVE, *form),
+  };
+
+  EXPECT_CALL(helper->observer(),
+              Observe(NotificationType(NotificationType::LOGINS_CHANGED),
+                      NotificationService::AllSources(),
+                      Property(&Details<const PasswordStoreChangeList>::ptr,
+                               Pointee(ElementsAreArray(
+                                   expected_delete_changes)))));
+
+  // Deleting the login should trigger a notification.
+  store->RemoveLogin(*form);
+
+  // Wait for PasswordStore to send the notification.
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+}
+
+TEST_P(PasswordStoreXTest, NativeMigration) {
+  VectorOfForms expected_autofillable;
+  for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(g_autofillable_data); ++i) {
+    expected_autofillable.push_back(
+        CreatePasswordFormFromData(g_autofillable_data[i]));
+  }
+
+  VectorOfForms expected_blacklisted;
+  for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(g_blacklisted_data); ++i) {
+    expected_blacklisted.push_back(
+        CreatePasswordFormFromData(g_blacklisted_data[i]));
+  }
+
+  LoginDatabase* login_db = login_db_.get();
+
+  // Populate the login DB with logins that should be migrated.
+  for (VectorOfForms::iterator it = expected_autofillable.begin();
+       it != expected_autofillable.end(); ++it) {
+    ChromeThread::PostTask(ChromeThread::DB,
+                           FROM_HERE,
+                           NewRunnableMethod(login_db,
+                                             &LoginDatabase::AddLogin,
+                                             **it));
+  }
+  for (VectorOfForms::iterator it = expected_blacklisted.begin();
+       it != expected_blacklisted.end(); ++it) {
+    ChromeThread::PostTask(ChromeThread::DB,
+                           FROM_HERE,
+                           NewRunnableMethod(login_db,
+                                             &LoginDatabase::AddLogin,
+                                             **it));
+  }
+
+  // Schedule another task on the DB thread to notify us that it's safe to
+  // carry on with the test.
+  WaitableEvent done(false, false);
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+
+  // Pretend that the WDS migration has already taken place.
+  profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
+                                            true);
+
+  // Initializing the PasswordStore shouldn't trigger a native migration (yet).
+  scoped_refptr<PasswordStoreX> store(
+      new PasswordStoreX(login_db_.release(),
+                           profile_.get(),
+                           wds_.get(),
+                           GetBackend()));
+  store->Init();
+
+  MockPasswordStoreConsumer consumer;
+
+  // Make sure we quit the MessageLoop even if the test fails.
+  ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
+      .WillByDefault(QuitUIMessageLoop());
+
+  // The autofillable forms should have been migrated to the native backend.
+  EXPECT_CALL(consumer,
+      OnPasswordStoreRequestDone(_,
+          ContainsAllPasswordForms(expected_autofillable)))
+      .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
+
+  store->GetAutofillableLogins(&consumer);
+  MessageLoop::current()->Run();
+
+  // The blacklisted forms should have been migrated to the native backend.
+  EXPECT_CALL(consumer,
+      OnPasswordStoreRequestDone(_,
+          ContainsAllPasswordForms(expected_blacklisted)))
+      .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
+
+  store->GetBlacklistLogins(&consumer);
+  MessageLoop::current()->Run();
+
+  VectorOfForms empty;
+  MockLoginDatabaseReturn ld_return;
+
+  if (GetParam() == WORKING_BACKEND) {
+    // No autofillable logins should be left in the login DB.
+    EXPECT_CALL(ld_return,
+                OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
+  } else {
+    // The autofillable logins should still be in the login DB.
+    EXPECT_CALL(ld_return,
+                OnLoginDatabaseQueryDone(
+                    ContainsAllPasswordForms(expected_autofillable)))
+        .WillOnce(WithArg<0>(STLDeleteElements0()));
+  }
+
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE,
+      new LoginDatabaseQueryTask(login_db, true, &ld_return));
+
+  // Wait for the login DB methods to execute on the DB thread.
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+
+  if (GetParam() == WORKING_BACKEND) {
+    // Likewise, no blacklisted logins should be left in the login DB.
+    EXPECT_CALL(ld_return,
+                OnLoginDatabaseQueryDone(ContainsAllPasswordForms(empty)));
+  } else {
+    // The blacklisted logins should still be in the login DB.
+    EXPECT_CALL(ld_return,
+                OnLoginDatabaseQueryDone(
+                    ContainsAllPasswordForms(expected_blacklisted)))
+        .WillOnce(WithArg<0>(STLDeleteElements0()));
+  }
+
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE,
+      new LoginDatabaseQueryTask(login_db, false, &ld_return));
+
+  // Wait for the login DB methods to execute on the DB thread.
+  ChromeThread::PostTask(ChromeThread::DB, FROM_HERE, new SignalingTask(&done));
+  done.Wait();
+
+  STLDeleteElements(&expected_autofillable);
+  STLDeleteElements(&expected_blacklisted);
+}
+
+INSTANTIATE_TEST_CASE_P(NoBackend,
+                        PasswordStoreXTest,
+                        testing::Values(NO_BACKEND));
+INSTANTIATE_TEST_CASE_P(FailingBackend,
+                        PasswordStoreXTest,
+                        testing::Values(FAILING_BACKEND));
+INSTANTIATE_TEST_CASE_P(WorkingBackend,
+                        PasswordStoreXTest,
+                        testing::Values(WORKING_BACKEND));
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index f297108..0e0ad22 100755
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1805,17 +1805,21 @@
         'browser/parsers/metadata_parser_manager.cc',
         'browser/parsers/metadata_parser_manager.h',
         'browser/parsers/metadata_parser.cc',
+        'browser/password_manager/encryptor.h',
         'browser/password_manager/encryptor_linux.cc',
         'browser/password_manager/encryptor_mac.mm',
         'browser/password_manager/encryptor_win.cc',
-        'browser/password_manager/encryptor.h',
         'browser/password_manager/ie7_password.cc',
         'browser/password_manager/ie7_password.h',
+        'browser/password_manager/login_database.cc',
+        'browser/password_manager/login_database.h',
         'browser/password_manager/login_database_mac.cc',
         'browser/password_manager/login_database_posix.cc',
         'browser/password_manager/login_database_win.cc',
-        'browser/password_manager/login_database.cc',
-        'browser/password_manager/login_database.h',
+        'browser/password_manager/native_backend_gnome_x.cc',
+        'browser/password_manager/native_backend_gnome_x.h',
+        'browser/password_manager/native_backend_kwallet_x.cc',
+        'browser/password_manager/native_backend_kwallet_x.h',
         'browser/password_manager/password_form_manager.cc',
         'browser/password_manager/password_form_manager.h',
         'browser/password_manager/password_manager.cc',
@@ -1824,15 +1828,13 @@
         'browser/password_manager/password_store.h',
         'browser/password_manager/password_store_default.cc',
         'browser/password_manager/password_store_default.h',
-        'browser/password_manager/password_store_gnome.h',
-        'browser/password_manager/password_store_gnome.cc',
-        'browser/password_manager/password_store_kwallet.h',
-        'browser/password_manager/password_store_kwallet.cc',
-        'browser/password_manager/password_store_mac_internal.h',
-        'browser/password_manager/password_store_mac.h',
         'browser/password_manager/password_store_mac.cc',
-        'browser/password_manager/password_store_win.h',
+        'browser/password_manager/password_store_mac.h',
+        'browser/password_manager/password_store_mac_internal.h',
         'browser/password_manager/password_store_win.cc',
+        'browser/password_manager/password_store_win.h',
+        'browser/password_manager/password_store_x.cc',
+        'browser/password_manager/password_store_x.h',
         'browser/platform_util.h',
         'browser/platform_util_linux.cc',
         'browser/platform_util_chromeos.cc',
@@ -2686,10 +2688,10 @@
         }],
         ['chromeos==1', {
           'sources!': [
-            'browser/password_manager/password_store_gnome.h',
-            'browser/password_manager/password_store_gnome.cc',
-            'browser/password_manager/password_store_kwallet.h',
-            'browser/password_manager/password_store_kwallet.cc',
+            'browser/password_manager/native_backend_gnome.h',
+            'browser/password_manager/native_backend_gnome.cc',
+            'browser/password_manager/native_backend_kwallet.h',
+            'browser/password_manager/native_backend_kwallet.cc',
             'browser/platform_util_linux.cc',
           ],
           'link_settings': {
@@ -2782,12 +2784,6 @@
             'browser/importer/nss_decryptor_system_nss.cc',
             'browser/importer/nss_decryptor_system_nss.h',
             'browser/jankometer.cc',
-            'browser/password_manager/password_store_gnome.h',
-            'browser/password_manager/password_store_gnome.cc',
-            'browser/password_manager/password_store_kwallet.h',
-            'browser/password_manager/password_store_kwallet.cc',
-            'browser/password_manager/password_store_win.cc',
-            'browser/password_manager/password_store_win.h',
             'browser/renderer_host/backing_store_proxy.cc',
             'browser/renderer_host/backing_store_proxy.h',
             'browser/renderer_host/gpu_view_host.cc',
@@ -2960,10 +2956,6 @@
             'browser/history/history_publisher_none.cc',
             'browser/importer/nss_decryptor_system_nss.cc',
             'browser/importer/nss_decryptor_system_nss.h',
-            'browser/password_manager/password_store_gnome.h',
-            'browser/password_manager/password_store_gnome.cc',
-            'browser/password_manager/password_store_kwallet.h',
-            'browser/password_manager/password_store_kwallet.cc',
             'browser/power_save_blocker_stub.cc',
             'browser/views/select_file_dialog.cc',
           ],
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 282b234..c9a60d9 100755
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -873,6 +873,7 @@
         'browser/password_manager/password_store_default_unittest.cc',
         'browser/password_manager/password_store_mac_unittest.cc',
         'browser/password_manager/password_store_win_unittest.cc',
+        'browser/password_manager/password_store_x_unittest.cc',
         'browser/pref_member_unittest.cc',
         'browser/pref_service_unittest.cc',
         'browser/preferences_mock_mac.cc',