1 // Copyright (c) 2011 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. 4 5 #include "base/basictypes.h" 6 #include "base/memory/scoped_temp_dir.h" 7 #include "base/stl_util-inl.h" 8 #include "base/string_util.h" 9 #include "base/time.h" 10 #include "base/utf_string_conversions.h" 11 #include "base/synchronization/waitable_event.h" 12 #include "chrome/browser/password_manager/password_store_change.h" 13 #include "chrome/browser/password_manager/password_store_consumer.h" 14 #include "chrome/browser/password_manager/password_store_default.h" 15 #include "chrome/browser/password_manager/password_form_data.h" 16 #include "chrome/browser/prefs/pref_service.h" 17 #include "chrome/browser/webdata/web_data_service.h" 18 #include "chrome/common/pref_names.h" 19 #include "chrome/test/signaling_task.h" 20 #include "chrome/test/testing_profile.h" 21 #include "content/common/notification_details.h" 22 #include "content/common/notification_observer_mock.h" 23 #include "content/common/notification_registrar.h" 24 #include "content/common/notification_source.h" 25 #include "testing/gmock/include/gmock/gmock.h" 26 #include "testing/gtest/include/gtest/gtest.h" 27 28 using base::WaitableEvent; 29 using testing::_; 30 using testing::DoAll; 31 using testing::ElementsAreArray; 32 using testing::Pointee; 33 using testing::Property; 34 using testing::WithArg; 35 using webkit_glue::PasswordForm; 36 37 namespace { 38 39 class MockPasswordStoreConsumer : public PasswordStoreConsumer { 40 public: 41 MOCK_METHOD2(OnPasswordStoreRequestDone, 42 void(CancelableRequestProvider::Handle, 43 const std::vector<webkit_glue::PasswordForm*>&)); 44 }; 45 46 class MockWebDataServiceConsumer : public WebDataServiceConsumer { 47 public: 48 MOCK_METHOD2(OnWebDataServiceRequestDone, void(WebDataService::Handle, 49 const WDTypedResult*)); 50 }; 51 52 // This class will add and remove a mock notification observer from 53 // the DB thread. 54 class DBThreadObserverHelper : 55 public base::RefCountedThreadSafe<DBThreadObserverHelper, 56 BrowserThread::DeleteOnDBThread> { 57 public: 58 DBThreadObserverHelper() : done_event_(true, false) {} 59 60 void Init(PasswordStore* password_store) { 61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 62 BrowserThread::PostTask( 63 BrowserThread::DB, 64 FROM_HERE, 65 NewRunnableMethod(this, 66 &DBThreadObserverHelper::AddObserverTask, 67 make_scoped_refptr(password_store))); 68 done_event_.Wait(); 69 } 70 71 virtual ~DBThreadObserverHelper() { 72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 73 registrar_.RemoveAll(); 74 } 75 76 NotificationObserverMock& observer() { 77 return observer_; 78 } 79 80 protected: 81 friend class base::RefCountedThreadSafe<DBThreadObserverHelper>; 82 83 void AddObserverTask(PasswordStore* password_store) { 84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 85 registrar_.Add(&observer_, 86 NotificationType::LOGINS_CHANGED, 87 Source<PasswordStore>(password_store)); 88 done_event_.Signal(); 89 } 90 91 WaitableEvent done_event_; 92 NotificationRegistrar registrar_; 93 NotificationObserverMock observer_; 94 }; 95 96 } // anonymous namespace 97 98 typedef std::vector<PasswordForm*> VectorOfForms; 99 100 class PasswordStoreDefaultTest : public testing::Test { 101 protected: 102 PasswordStoreDefaultTest() 103 : ui_thread_(BrowserThread::UI, &message_loop_), 104 db_thread_(BrowserThread::DB) { 105 } 106 107 virtual void SetUp() { 108 ASSERT_TRUE(db_thread_.Start()); 109 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 110 111 profile_.reset(new TestingProfile()); 112 113 login_db_.reset(new LoginDatabase()); 114 ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append( 115 FILE_PATH_LITERAL("login_test")))); 116 117 wds_ = new WebDataService(); 118 ASSERT_TRUE(wds_->Init(temp_dir_.path())); 119 } 120 121 virtual void TearDown() { 122 wds_->Shutdown(); 123 MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask); 124 MessageLoop::current()->Run(); 125 db_thread_.Stop(); 126 } 127 128 MessageLoopForUI message_loop_; 129 BrowserThread ui_thread_; 130 BrowserThread db_thread_; // PasswordStore, WDS schedule work on this thread. 131 132 scoped_ptr<LoginDatabase> login_db_; 133 scoped_ptr<TestingProfile> profile_; 134 scoped_refptr<WebDataService> wds_; 135 ScopedTempDir temp_dir_; 136 }; 137 138 ACTION(STLDeleteElements0) { 139 STLDeleteContainerPointers(arg0.begin(), arg0.end()); 140 } 141 142 ACTION(QuitUIMessageLoop) { 143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 144 MessageLoop::current()->Quit(); 145 } 146 147 MATCHER(EmptyWDResult, "") { 148 return static_cast<const WDResult<std::vector<PasswordForm*> >*>( 149 arg)->GetValue().empty(); 150 } 151 152 TEST_F(PasswordStoreDefaultTest, NonASCIIData) { 153 // Prentend that the migration has already taken place. 154 profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated, 155 true); 156 157 // Initializing the PasswordStore shouldn't trigger a migration. 158 scoped_refptr<PasswordStoreDefault> store( 159 new PasswordStoreDefault(login_db_.release(), profile_.get(), 160 wds_.get())); 161 store->Init(); 162 163 // Some non-ASCII password form data. 164 PasswordFormData form_data[] = { 165 { PasswordForm::SCHEME_HTML, 166 "http://foo.example.com", 167 "http://foo.example.com/origin", 168 "http://foo.example.com/action", 169 L"", 170 L"?", 171 L"", 172 L" ", 173 L"", 174 true, false, 1 }, 175 }; 176 177 // Build the expected forms vector and add the forms to the store. 178 VectorOfForms expected_forms; 179 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(form_data); ++i) { 180 PasswordForm* form = CreatePasswordFormFromData(form_data[i]); 181 expected_forms.push_back(form); 182 store->AddLogin(*form); 183 } 184 185 // The PasswordStore schedules tasks to run on the DB thread so we schedule 186 // yet another task to notify us that it's safe to carry on with the test. 187 WaitableEvent done(false, false); 188 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 189 new SignalingTask(&done)); 190 done.Wait(); 191 192 MockPasswordStoreConsumer consumer; 193 194 // Make sure we quit the MessageLoop even if the test fails. 195 ON_CALL(consumer, OnPasswordStoreRequestDone(_, _)) 196 .WillByDefault(QuitUIMessageLoop()); 197 198 // We expect to get the same data back, even though it's not all ASCII. 199 EXPECT_CALL(consumer, 200 OnPasswordStoreRequestDone(_, 201 ContainsAllPasswordForms(expected_forms))) 202 .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop())); 203 204 store->GetAutofillableLogins(&consumer); 205 MessageLoop::current()->Run(); 206 207 STLDeleteElements(&expected_forms); 208 } 209 210 TEST_F(PasswordStoreDefaultTest, Migration) { 211 PasswordFormData autofillable_data[] = { 212 { PasswordForm::SCHEME_HTML, 213 "http://foo.example.com", 214 "http://foo.example.com/origin", 215 "http://foo.example.com/action", 216 L"submit_element", 217 L"username_element", 218 L"password_element", 219 L"username_value", 220 L"password_value", 221 true, false, 1 }, 222 { PasswordForm::SCHEME_HTML, 223 "http://bar.example.com", 224 "http://bar.example.com/origin", 225 "http://bar.example.com/action", 226 L"submit_element", 227 L"username_element", 228 L"password_element", 229 L"username_value", 230 L"password_value", 231 true, false, 2 }, 232 { PasswordForm::SCHEME_HTML, 233 "http://baz.example.com", 234 "http://baz.example.com/origin", 235 "http://baz.example.com/action", 236 L"submit_element", 237 L"username_element", 238 L"password_element", 239 L"username_value", 240 L"password_value", 241 true, false, 3 }, 242 }; 243 PasswordFormData blacklisted_data[] = { 244 { PasswordForm::SCHEME_HTML, 245 "http://blacklisted.example.com", 246 "http://blacklisted.example.com/origin", 247 "http://blacklisted.example.com/action", 248 L"submit_element", 249 L"username_element", 250 L"password_element", 251 NULL, 252 NULL, 253 false, false, 1 }, 254 { PasswordForm::SCHEME_HTML, 255 "http://blacklisted2.example.com", 256 "http://blacklisted2.example.com/origin", 257 "http://blacklisted2.example.com/action", 258 L"submit_element", 259 L"username_element", 260 L"password_element", 261 NULL, 262 NULL, 263 false, false, 2 }, 264 }; 265 266 // Build the expected forms vectors and populate the WDS with logins. 267 VectorOfForms expected_autofillable; 268 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(autofillable_data); ++i) { 269 PasswordForm* form = CreatePasswordFormFromData(autofillable_data[i]); 270 expected_autofillable.push_back(form); 271 wds_->AddLogin(*form); 272 } 273 VectorOfForms expected_blacklisted; 274 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(blacklisted_data); ++i) { 275 PasswordForm* form = CreatePasswordFormFromData(blacklisted_data[i]); 276 expected_blacklisted.push_back(form); 277 wds_->AddLogin(*form); 278 } 279 280 // The WDS schedules tasks to run on the DB thread so we schedule yet another 281 // task to notify us that it's safe to carry on with the test. 282 WaitableEvent done(false, false); 283 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 284 new SignalingTask(&done)); 285 done.Wait(); 286 287 // Initializing the PasswordStore should trigger a migration. 288 scoped_refptr<PasswordStore> store( 289 new PasswordStoreDefault(login_db_.release(), 290 profile_.get(), wds_.get())); 291 store->Init(); 292 293 // Check that the migration preference has not been initialized; 294 ASSERT_TRUE(NULL == profile_->GetPrefs()->FindPreference( 295 prefs::kLoginDatabaseMigrated)); 296 297 // Again, the WDS schedules tasks to run on the DB thread, so schedule a task 298 // to signal us when it is safe to continue. 299 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 300 new SignalingTask(&done)); 301 done.Wait(); 302 303 // Let the WDS callbacks proceed so the logins can be migrated. 304 MessageLoop::current()->RunAllPending(); 305 306 MockPasswordStoreConsumer consumer; 307 308 // Make sure we quit the MessageLoop even if the test fails. 309 ON_CALL(consumer, OnPasswordStoreRequestDone(_, _)) 310 .WillByDefault(QuitUIMessageLoop()); 311 312 // The autofillable forms should have been migrated from the WDS to the login 313 // database. 314 EXPECT_CALL(consumer, 315 OnPasswordStoreRequestDone(_, 316 ContainsAllPasswordForms(expected_autofillable))) 317 .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop())); 318 319 store->GetAutofillableLogins(&consumer); 320 MessageLoop::current()->Run(); 321 322 // The blacklisted forms should have been migrated from the WDS to the login 323 // database. 324 EXPECT_CALL(consumer, 325 OnPasswordStoreRequestDone(_, 326 ContainsAllPasswordForms(expected_blacklisted))) 327 .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop())); 328 329 store->GetBlacklistLogins(&consumer); 330 MessageLoop::current()->Run(); 331 332 // Check that the migration updated the migrated preference. 333 ASSERT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kLoginDatabaseMigrated)); 334 335 MockWebDataServiceConsumer wds_consumer; 336 337 // No autofillable logins should be left in the WDS. 338 EXPECT_CALL(wds_consumer, 339 OnWebDataServiceRequestDone(_, EmptyWDResult())); 340 341 wds_->GetAutofillableLogins(&wds_consumer); 342 343 // Wait for the WDS methods to execute on the DB thread. 344 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 345 new SignalingTask(&done)); 346 done.Wait(); 347 348 // Handle the callback from the WDS. 349 MessageLoop::current()->RunAllPending(); 350 351 // Likewise, no blacklisted logins should be left in the WDS. 352 EXPECT_CALL(wds_consumer, 353 OnWebDataServiceRequestDone(_, EmptyWDResult())); 354 355 wds_->GetBlacklistLogins(&wds_consumer); 356 357 // Wait for the WDS methods to execute on the DB thread. 358 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 359 new SignalingTask(&done)); 360 done.Wait(); 361 362 // Handle the callback from the WDS. 363 MessageLoop::current()->RunAllPending(); 364 365 STLDeleteElements(&expected_autofillable); 366 STLDeleteElements(&expected_blacklisted); 367 368 store->Shutdown(); 369 } 370 371 TEST_F(PasswordStoreDefaultTest, MigrationAlreadyDone) { 372 PasswordFormData wds_data[] = { 373 { PasswordForm::SCHEME_HTML, 374 "http://bar.example.com", 375 "http://bar.example.com/origin", 376 "http://bar.example.com/action", 377 L"submit_element", 378 L"username_element", 379 L"password_element", 380 L"username_value", 381 L"password_value", 382 true, false, 1 }, 383 }; 384 385 // Build the expected forms vector and populate the WDS with logins. 386 VectorOfForms unexpected_autofillable; 387 for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(wds_data); ++i) { 388 PasswordForm* form = CreatePasswordFormFromData(wds_data[i]); 389 unexpected_autofillable.push_back(form); 390 wds_->AddLogin(*form); 391 } 392 393 // The WDS schedules tasks to run on the DB thread so we schedule yet another 394 // task to notify us that it's safe to carry on with the test. 395 WaitableEvent done(false, false); 396 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 397 new SignalingTask(&done)); 398 done.Wait(); 399 400 // Prentend that the migration has already taken place. 401 profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated, 402 true); 403 404 // Initializing the PasswordStore shouldn't trigger a migration. 405 scoped_refptr<PasswordStore> store( 406 new PasswordStoreDefault(login_db_.release(), profile_.get(), 407 wds_.get())); 408 store->Init(); 409 410 MockPasswordStoreConsumer consumer; 411 // Make sure we quit the MessageLoop even if the test fails. 412 ON_CALL(consumer, OnPasswordStoreRequestDone(_, _)) 413 .WillByDefault(QuitUIMessageLoop()); 414 415 // No forms should be migrated. 416 VectorOfForms empty; 417 EXPECT_CALL(consumer, 418 OnPasswordStoreRequestDone(_, 419 ContainsAllPasswordForms(empty))) 420 .WillOnce(QuitUIMessageLoop()); 421 422 store->GetAutofillableLogins(&consumer); 423 MessageLoop::current()->Run(); 424 425 STLDeleteElements(&unexpected_autofillable); 426 427 store->Shutdown(); 428 } 429 430 TEST_F(PasswordStoreDefaultTest, Notifications) { 431 // Prentend that the migration has already taken place. 432 profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated, 433 true); 434 435 // Initializing the PasswordStore shouldn't trigger a migration. 436 scoped_refptr<PasswordStore> store( 437 new PasswordStoreDefault(login_db_.release(), profile_.get(), 438 wds_.get())); 439 store->Init(); 440 441 PasswordFormData form_data = 442 { PasswordForm::SCHEME_HTML, 443 "http://bar.example.com", 444 "http://bar.example.com/origin", 445 "http://bar.example.com/action", 446 L"submit_element", 447 L"username_element", 448 L"password_element", 449 L"username_value", 450 L"password_value", 451 true, false, 1 }; 452 scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data)); 453 454 scoped_refptr<DBThreadObserverHelper> helper = new DBThreadObserverHelper; 455 helper->Init(store); 456 457 const PasswordStoreChange expected_add_changes[] = { 458 PasswordStoreChange(PasswordStoreChange::ADD, *form), 459 }; 460 461 EXPECT_CALL(helper->observer(), 462 Observe(NotificationType(NotificationType::LOGINS_CHANGED), 463 Source<PasswordStore>(store), 464 Property(&Details<const PasswordStoreChangeList>::ptr, 465 Pointee(ElementsAreArray( 466 expected_add_changes))))); 467 468 // Adding a login should trigger a notification. 469 store->AddLogin(*form); 470 471 // The PasswordStore schedules tasks to run on the DB thread so we schedule 472 // yet another task to notify us that it's safe to carry on with the test. 473 WaitableEvent done(false, false); 474 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 475 new SignalingTask(&done)); 476 done.Wait(); 477 478 // Change the password. 479 form->password_value = WideToUTF16(L"a different password"); 480 481 const PasswordStoreChange expected_update_changes[] = { 482 PasswordStoreChange(PasswordStoreChange::UPDATE, *form), 483 }; 484 485 EXPECT_CALL(helper->observer(), 486 Observe(NotificationType(NotificationType::LOGINS_CHANGED), 487 Source<PasswordStore>(store), 488 Property(&Details<const PasswordStoreChangeList>::ptr, 489 Pointee(ElementsAreArray( 490 expected_update_changes))))); 491 492 // Updating the login with the new password should trigger a notification. 493 store->UpdateLogin(*form); 494 495 // Wait for PasswordStore to send the notification. 496 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 497 new SignalingTask(&done)); 498 done.Wait(); 499 500 const PasswordStoreChange expected_delete_changes[] = { 501 PasswordStoreChange(PasswordStoreChange::REMOVE, *form), 502 }; 503 504 EXPECT_CALL(helper->observer(), 505 Observe(NotificationType(NotificationType::LOGINS_CHANGED), 506 Source<PasswordStore>(store), 507 Property(&Details<const PasswordStoreChangeList>::ptr, 508 Pointee(ElementsAreArray( 509 expected_delete_changes))))); 510 511 // Deleting the login should trigger a notification. 512 store->RemoveLogin(*form); 513 514 // Wait for PasswordStore to send the notification. 515 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, 516 new SignalingTask(&done)); 517 done.Wait(); 518 519 store->Shutdown(); 520 } 521