Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_x.cc
blobcaa1dd628ec0ac40a99fd66fe6b7046d605d15eb
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/password_manager/password_store_x.h"
7 #include <algorithm>
8 #include <map>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/logging.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/stl_util.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "components/password_manager/core/browser/password_store_change.h"
17 #include "components/password_manager/core/common/password_manager_pref_names.h"
18 #include "components/pref_registry/pref_registry_syncable.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/notification_service.h"
22 using autofill::PasswordForm;
23 using content::BrowserThread;
24 using password_manager::PasswordStoreChange;
25 using password_manager::PasswordStoreChangeList;
26 using password_manager::PasswordStoreDefault;
27 using std::vector;
29 namespace {
31 bool AddLoginToBackend(const scoped_ptr<PasswordStoreX::NativeBackend>& backend,
32 const PasswordForm& form,
33 PasswordStoreChangeList* changes) {
34 *changes = backend->AddLogin(form);
35 return (!changes->empty() &&
36 changes->back().type() == PasswordStoreChange::ADD);
39 } // namespace
41 PasswordStoreX::PasswordStoreX(
42 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
43 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
44 password_manager::LoginDatabase* login_db,
45 NativeBackend* backend)
46 : PasswordStoreDefault(main_thread_runner, db_thread_runner, login_db),
47 backend_(backend),
48 migration_checked_(!backend),
49 allow_fallback_(false) {}
51 PasswordStoreX::~PasswordStoreX() {}
53 PasswordStoreChangeList PasswordStoreX::AddLoginImpl(const PasswordForm& form) {
54 CheckMigration();
55 PasswordStoreChangeList changes;
56 if (use_native_backend() && AddLoginToBackend(backend_, form, &changes)) {
57 allow_fallback_ = false;
58 } else if (allow_default_store()) {
59 changes = PasswordStoreDefault::AddLoginImpl(form);
61 return changes;
64 PasswordStoreChangeList PasswordStoreX::UpdateLoginImpl(
65 const PasswordForm& form) {
66 CheckMigration();
67 PasswordStoreChangeList changes;
68 if (use_native_backend() && backend_->UpdateLogin(form, &changes)) {
69 allow_fallback_ = false;
70 } else if (allow_default_store()) {
71 changes = PasswordStoreDefault::UpdateLoginImpl(form);
73 return changes;
76 PasswordStoreChangeList PasswordStoreX::RemoveLoginImpl(
77 const PasswordForm& form) {
78 CheckMigration();
79 PasswordStoreChangeList changes;
80 if (use_native_backend() && backend_->RemoveLogin(form)) {
81 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
82 allow_fallback_ = false;
83 } else if (allow_default_store()) {
84 changes = PasswordStoreDefault::RemoveLoginImpl(form);
86 return changes;
89 PasswordStoreChangeList PasswordStoreX::RemoveLoginsCreatedBetweenImpl(
90 base::Time delete_begin,
91 base::Time delete_end) {
92 CheckMigration();
93 PasswordStoreChangeList changes;
94 if (use_native_backend() &&
95 backend_->RemoveLoginsCreatedBetween(
96 delete_begin, delete_end, &changes)) {
97 LogStatsForBulkDeletion(changes.size());
98 allow_fallback_ = false;
99 } else if (allow_default_store()) {
100 changes = PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(delete_begin,
101 delete_end);
103 return changes;
106 PasswordStoreChangeList PasswordStoreX::RemoveLoginsSyncedBetweenImpl(
107 base::Time delete_begin,
108 base::Time delete_end) {
109 CheckMigration();
110 PasswordStoreChangeList changes;
111 if (use_native_backend() &&
112 backend_->RemoveLoginsSyncedBetween(delete_begin, delete_end, &changes)) {
113 allow_fallback_ = false;
114 } else if (allow_default_store()) {
115 changes = PasswordStoreDefault::RemoveLoginsSyncedBetweenImpl(delete_begin,
116 delete_end);
118 return changes;
121 namespace {
122 struct LoginLessThan {
123 bool operator()(const PasswordForm* a, const PasswordForm* b) {
124 return a->origin < b->origin;
127 } // anonymous namespace
129 void PasswordStoreX::SortLoginsByOrigin(NativeBackend::PasswordFormList* list) {
130 // In login_database.cc, the query has ORDER BY origin_url. Simulate that.
131 std::sort(list->begin(), list->end(), LoginLessThan());
134 void PasswordStoreX::GetLoginsImpl(
135 const autofill::PasswordForm& form,
136 AuthorizationPromptPolicy prompt_policy,
137 const ConsumerCallbackRunner& callback_runner) {
138 CheckMigration();
139 std::vector<autofill::PasswordForm*> matched_forms;
140 if (use_native_backend() && backend_->GetLogins(form, &matched_forms)) {
141 SortLoginsByOrigin(&matched_forms);
142 // The native backend may succeed and return no data even while locked, if
143 // the query did not match anything stored. So we continue to allow fallback
144 // until we perform a write operation, or until a read returns actual data.
145 if (matched_forms.size() > 0)
146 allow_fallback_ = false;
147 } else if (allow_default_store()) {
148 DCHECK(matched_forms.empty());
149 PasswordStoreDefault::GetLoginsImpl(form, prompt_policy, callback_runner);
150 return;
152 // The consumer will be left hanging unless we reply.
153 callback_runner.Run(matched_forms);
156 void PasswordStoreX::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
157 CheckMigration();
158 if (use_native_backend() &&
159 backend_->GetAutofillableLogins(request->result())) {
160 SortLoginsByOrigin(request->result());
161 // See GetLoginsImpl() for why we disallow fallback conditionally here.
162 if (request->result()->size() > 0)
163 allow_fallback_ = false;
164 } else if (allow_default_store()) {
165 PasswordStoreDefault::GetAutofillableLoginsImpl(request);
166 return;
168 // The consumer will be left hanging unless we reply.
169 ForwardLoginsResult(request);
172 void PasswordStoreX::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
173 CheckMigration();
174 if (use_native_backend() &&
175 backend_->GetBlacklistLogins(request->result())) {
176 SortLoginsByOrigin(request->result());
177 // See GetLoginsImpl() for why we disallow fallback conditionally here.
178 if (request->result()->size() > 0)
179 allow_fallback_ = false;
180 } else if (allow_default_store()) {
181 PasswordStoreDefault::GetBlacklistLoginsImpl(request);
182 return;
184 // The consumer will be left hanging unless we reply.
185 ForwardLoginsResult(request);
188 bool PasswordStoreX::FillAutofillableLogins(vector<PasswordForm*>* forms) {
189 CheckMigration();
190 if (use_native_backend() && backend_->GetAutofillableLogins(forms)) {
191 // See GetLoginsImpl() for why we disallow fallback conditionally here.
192 if (forms->size() > 0)
193 allow_fallback_ = false;
194 return true;
196 if (allow_default_store())
197 return PasswordStoreDefault::FillAutofillableLogins(forms);
198 return false;
201 bool PasswordStoreX::FillBlacklistLogins(vector<PasswordForm*>* forms) {
202 CheckMigration();
203 if (use_native_backend() && backend_->GetBlacklistLogins(forms)) {
204 // See GetLoginsImpl() for why we disallow fallback conditionally here.
205 if (forms->size() > 0)
206 allow_fallback_ = false;
207 return true;
209 if (allow_default_store())
210 return PasswordStoreDefault::FillBlacklistLogins(forms);
211 return false;
214 void PasswordStoreX::CheckMigration() {
215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
216 if (migration_checked_ || !backend_.get())
217 return;
218 migration_checked_ = true;
219 ssize_t migrated = MigrateLogins();
220 if (migrated > 0) {
221 VLOG(1) << "Migrated " << migrated << " passwords to native store.";
222 } else if (migrated == 0) {
223 // As long as we are able to migrate some passwords, we know the native
224 // store is working. But if there is nothing to migrate, the "migration"
225 // can succeed even when the native store would fail. In this case we
226 // allow a later fallback to the default store. Once any later operation
227 // succeeds on the native store, we will no longer allow fallback.
228 allow_fallback_ = true;
229 } else {
230 LOG(WARNING) << "Native password store migration failed! " <<
231 "Falling back on default (unencrypted) store.";
232 backend_.reset(NULL);
236 bool PasswordStoreX::allow_default_store() {
237 if (allow_fallback_) {
238 LOG(WARNING) << "Native password store failed! " <<
239 "Falling back on default (unencrypted) store.";
240 backend_.reset(NULL);
241 // Don't warn again. We'll use the default store because backend_ is NULL.
242 allow_fallback_ = false;
244 return !backend_.get();
247 ssize_t PasswordStoreX::MigrateLogins() {
248 DCHECK(backend_.get());
249 vector<PasswordForm*> forms;
250 bool ok = PasswordStoreDefault::FillAutofillableLogins(&forms) &&
251 PasswordStoreDefault::FillBlacklistLogins(&forms);
252 if (ok) {
253 // We add all the passwords (and blacklist entries) to the native backend
254 // before attempting to remove any from the login database, to make sure we
255 // don't somehow end up with some of the passwords in one store and some in
256 // another. We'll always have at least one intact store this way.
257 for (size_t i = 0; i < forms.size(); ++i) {
258 PasswordStoreChangeList changes;
259 if (!AddLoginToBackend(backend_, *forms[i], &changes)) {
260 ok = false;
261 break;
264 if (ok) {
265 for (size_t i = 0; i < forms.size(); ++i) {
266 // If even one of these calls to RemoveLoginImpl() succeeds, then we
267 // should prefer the native backend to the now-incomplete login
268 // database. Thus we want to return a success status even in the case
269 // where some fail. The only real problem with this is that we might
270 // leave passwords in the login database and never come back to clean
271 // them out if any of these calls do fail.
272 PasswordStoreDefault::RemoveLoginImpl(*forms[i]);
274 // Finally, delete the database file itself. We remove the passwords from
275 // it before deleting the file just in case there is some problem deleting
276 // the file (e.g. directory is not writable, but file is), which would
277 // otherwise cause passwords to re-migrate next (or maybe every) time.
278 DeleteAndRecreateDatabaseFile();
281 ssize_t result = ok ? forms.size() : -1;
282 STLDeleteElements(&forms);
283 return result;