Home | History | Annotate | Download | only in password_manager
      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 <windows.h>
      6 #include <wincrypt.h>
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/memory/scoped_temp_dir.h"
     12 #include "base/message_loop.h"
     13 #include "base/stl_util-inl.h"
     14 #include "base/time.h"
     15 #include "base/synchronization/waitable_event.h"
     16 #include "chrome/browser/password_manager/password_form_data.h"
     17 #include "chrome/browser/password_manager/password_store_consumer.h"
     18 #include "chrome/browser/password_manager/password_store_win.h"
     19 #include "chrome/browser/password_manager/ie7_password.h"
     20 #include "chrome/browser/prefs/pref_service.h"
     21 #include "chrome/browser/webdata/web_data_service.h"
     22 #include "chrome/common/pref_names.h"
     23 #include "chrome/test/signaling_task.h"
     24 #include "chrome/test/testing_profile.h"
     25 #include "content/browser/browser_thread.h"
     26 #include "testing/gmock/include/gmock/gmock.h"
     27 #include "testing/gtest/include/gtest/gtest.h"
     28 
     29 using base::WaitableEvent;
     30 using testing::_;
     31 using testing::DoAll;
     32 using testing::WithArg;
     33 using webkit_glue::PasswordForm;
     34 
     35 namespace {
     36 
     37 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
     38  public:
     39   MOCK_METHOD2(OnPasswordStoreRequestDone,
     40                void(CancelableRequestProvider::Handle,
     41                     const std::vector<webkit_glue::PasswordForm*>&));
     42 };
     43 
     44 class MockWebDataServiceConsumer : public WebDataServiceConsumer {
     45 public:
     46   MOCK_METHOD2(OnWebDataServiceRequestDone,
     47                void(WebDataService::Handle, const WDTypedResult*));
     48 };
     49 
     50 }  // anonymous namespace
     51 
     52 typedef std::vector<PasswordForm*> VectorOfForms;
     53 
     54 class PasswordStoreWinTest : public testing::Test {
     55  protected:
     56   PasswordStoreWinTest()
     57       : ui_thread_(BrowserThread::UI, &message_loop_),
     58         db_thread_(BrowserThread::DB) {
     59   }
     60 
     61   bool CreateIE7PasswordInfo(const std::wstring& url, const base::Time& created,
     62                              IE7PasswordInfo* info) {
     63     // Copied from chrome/browser/importer/importer_unittest.cc
     64     // The username is "abcdefgh" and the password "abcdefghijkl".
     65     unsigned char data[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x2c\x00\x00\x00"
     66                            "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00"
     67                            "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00"
     68                            "\x00\x00\x00\x00\x4e\xfa\x67\x76\x22\x94\xc8\x01"
     69                            "\x08\x00\x00\x00\x12\x00\x00\x00\x4e\xfa\x67\x76"
     70                            "\x22\x94\xc8\x01\x0c\x00\x00\x00\x61\x00\x62\x00"
     71                            "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00"
     72                            "\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00"
     73                            "\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00"
     74                            "\x6c\x00\x00\x00";
     75     DATA_BLOB input = {0};
     76     DATA_BLOB url_key = {0};
     77     DATA_BLOB output = {0};
     78 
     79     input.pbData = data;
     80     input.cbData = sizeof(data);
     81 
     82     url_key.pbData = reinterpret_cast<unsigned char*>(
     83         const_cast<wchar_t*>(url.data()));
     84     url_key.cbData = static_cast<DWORD>((url.size() + 1) *
     85                                         sizeof(std::wstring::value_type));
     86 
     87     if (!CryptProtectData(&input, NULL, &url_key, NULL, NULL,
     88                           CRYPTPROTECT_UI_FORBIDDEN, &output))
     89       return false;
     90 
     91     std::vector<unsigned char> encrypted_data;
     92     encrypted_data.resize(output.cbData);
     93     memcpy(&encrypted_data.front(), output.pbData, output.cbData);
     94 
     95     LocalFree(output.pbData);
     96 
     97     info->url_hash = ie7_password::GetUrlHash(url);
     98     info->encrypted_data = encrypted_data;
     99     info->date_created = created;
    100 
    101     return true;
    102   }
    103 
    104   virtual void SetUp() {
    105     ASSERT_TRUE(db_thread_.Start());
    106     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    107 
    108     profile_.reset(new TestingProfile());
    109 
    110     login_db_.reset(new LoginDatabase());
    111     ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append(
    112         FILE_PATH_LITERAL("login_test"))));
    113 
    114     wds_ = new WebDataService();
    115     ASSERT_TRUE(wds_->Init(temp_dir_.path()));
    116   }
    117 
    118   virtual void TearDown() {
    119     if (wds_.get())
    120       wds_->Shutdown();
    121     MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask);
    122     MessageLoop::current()->Run();
    123     db_thread_.Stop();
    124   }
    125 
    126   MessageLoopForUI message_loop_;
    127   BrowserThread ui_thread_;
    128   BrowserThread db_thread_;  // PasswordStore, WDS schedule work on this thread.
    129 
    130   scoped_ptr<LoginDatabase> login_db_;
    131   scoped_ptr<TestingProfile> profile_;
    132   scoped_refptr<WebDataService> wds_;
    133   ScopedTempDir temp_dir_;
    134 };
    135 
    136 ACTION(STLDeleteElements0) {
    137   STLDeleteContainerPointers(arg0.begin(), arg0.end());
    138 }
    139 
    140 ACTION(QuitUIMessageLoop) {
    141   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    142   MessageLoop::current()->Quit();
    143 }
    144 
    145 MATCHER(EmptyWDResult, "") {
    146   return static_cast<const WDResult<std::vector<PasswordForm*> >*>(
    147       arg)->GetValue().empty();
    148 }
    149 
    150 // Hangs flakily, http://crbug.com/71385.
    151 TEST_F(PasswordStoreWinTest, DISABLED_ConvertIE7Login) {
    152   IE7PasswordInfo password_info;
    153   ASSERT_TRUE(CreateIE7PasswordInfo(L"http://example.com/origin",
    154                                     base::Time::FromDoubleT(1),
    155                                     &password_info));
    156   // Verify the URL hash
    157   ASSERT_EQ(L"39471418FF5453FEEB3731E382DEB5D53E14FAF9B5",
    158             password_info.url_hash);
    159 
    160   // This IE7 password will be retrieved by the GetLogins call.
    161   wds_->AddIE7Login(password_info);
    162 
    163   // The WDS schedules tasks to run on the DB thread so we schedule yet another
    164   // task to notify us that it's safe to carry on with the test.
    165   WaitableEvent done(false, false);
    166   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
    167       new SignalingTask(&done));
    168   done.Wait();
    169 
    170   // Prentend that the migration has already taken place.
    171   profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
    172                                             true);
    173 
    174   // Initializing the PasswordStore shouldn't trigger a migration.
    175   scoped_refptr<PasswordStore> store(
    176       new PasswordStoreWin(login_db_.release(), profile_.get(), wds_.get()));
    177   EXPECT_TRUE(store->Init());
    178 
    179   MockPasswordStoreConsumer consumer;
    180 
    181   // Make sure we quit the MessageLoop even if the test fails.
    182   ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
    183       .WillByDefault(QuitUIMessageLoop());
    184 
    185   PasswordFormData form_data = {
    186     PasswordForm::SCHEME_HTML,
    187     "http://example.com/",
    188     "http://example.com/origin",
    189     "http://example.com/action",
    190     L"submit_element",
    191     L"username_element",
    192     L"password_element",
    193     L"",
    194     L"",
    195     true, false, 1,
    196   };
    197   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
    198 
    199   PasswordFormData expected_form_data = {
    200     PasswordForm::SCHEME_HTML,
    201     "http://example.com/",
    202     "http://example.com/origin",
    203     "http://example.com/action",
    204     L"submit_element",
    205     L"username_element",
    206     L"password_element",
    207     L"abcdefgh",
    208     L"abcdefghijkl",
    209     true, false, 1,
    210   };
    211   std::vector<PasswordForm*> forms;
    212   forms.push_back(CreatePasswordFormFromData(expected_form_data));
    213 
    214   // The IE7 password should be returned.
    215   EXPECT_CALL(consumer,
    216       OnPasswordStoreRequestDone(_,
    217           ContainsAllPasswordForms(forms)))
    218       .WillOnce(QuitUIMessageLoop());
    219 
    220   store->GetLogins(*form, &consumer);
    221   MessageLoop::current()->Run();
    222 
    223   STLDeleteElements(&forms);
    224 
    225   store->Shutdown();
    226 }
    227 
    228 TEST_F(PasswordStoreWinTest, OutstandingWDSQueries) {
    229   // Prentend that the migration has already taken place.
    230   profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
    231                                             true);
    232 
    233   // Initializing the PasswordStore shouldn't trigger a migration.
    234   scoped_refptr<PasswordStore> store(
    235       new PasswordStoreWin(login_db_.release(), profile_.get(), wds_.get()));
    236   EXPECT_TRUE(store->Init());
    237 
    238   PasswordFormData form_data = {
    239     PasswordForm::SCHEME_HTML,
    240     "http://example.com/",
    241     "http://example.com/origin",
    242     "http://example.com/action",
    243     L"submit_element",
    244     L"username_element",
    245     L"password_element",
    246     L"",
    247     L"",
    248     true, false, 1,
    249   };
    250   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
    251 
    252   MockPasswordStoreConsumer consumer;
    253   store->GetLogins(*form, &consumer);
    254 
    255   // Release the PSW and the WDS before the query can return.
    256   store->Shutdown();
    257   store = NULL;
    258   wds_->Shutdown();
    259   wds_ = NULL;
    260 
    261   MessageLoop::current()->RunAllPending();
    262 }
    263 
    264 // Hangs flakily, see http://crbug.com/43836.
    265 TEST_F(PasswordStoreWinTest, DISABLED_MultipleWDSQueriesOnDifferentThreads) {
    266   IE7PasswordInfo password_info;
    267   ASSERT_TRUE(CreateIE7PasswordInfo(L"http://example.com/origin",
    268                                     base::Time::FromDoubleT(1),
    269                                     &password_info));
    270   wds_->AddIE7Login(password_info);
    271 
    272   // The WDS schedules tasks to run on the DB thread so we schedule yet another
    273   // task to notify us that it's safe to carry on with the test.
    274   WaitableEvent done(false, false);
    275   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
    276       new SignalingTask(&done));
    277   done.Wait();
    278 
    279   // Prentend that the migration has already taken place.
    280   profile_->GetPrefs()->RegisterBooleanPref(prefs::kLoginDatabaseMigrated,
    281                                             true);
    282 
    283   // Initializing the PasswordStore shouldn't trigger a migration.
    284   scoped_refptr<PasswordStore> store(
    285       new PasswordStoreWin(login_db_.release(), profile_.get(), wds_.get()));
    286   EXPECT_TRUE(store->Init());
    287 
    288   MockPasswordStoreConsumer password_consumer;
    289   // Make sure we quit the MessageLoop even if the test fails.
    290   ON_CALL(password_consumer, OnPasswordStoreRequestDone(_, _))
    291       .WillByDefault(QuitUIMessageLoop());
    292 
    293   PasswordFormData form_data = {
    294     PasswordForm::SCHEME_HTML,
    295     "http://example.com/",
    296     "http://example.com/origin",
    297     "http://example.com/action",
    298     L"submit_element",
    299     L"username_element",
    300     L"password_element",
    301     L"",
    302     L"",
    303     true, false, 1,
    304   };
    305   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
    306 
    307   PasswordFormData expected_form_data = {
    308     PasswordForm::SCHEME_HTML,
    309     "http://example.com/",
    310     "http://example.com/origin",
    311     "http://example.com/action",
    312     L"submit_element",
    313     L"username_element",
    314     L"password_element",
    315     L"abcdefgh",
    316     L"abcdefghijkl",
    317     true, false, 1,
    318   };
    319   std::vector<PasswordForm*> forms;
    320   forms.push_back(CreatePasswordFormFromData(expected_form_data));
    321 
    322   // The IE7 password should be returned.
    323   EXPECT_CALL(password_consumer,
    324       OnPasswordStoreRequestDone(_,
    325           ContainsAllPasswordForms(forms)))
    326       .WillOnce(QuitUIMessageLoop());
    327 
    328   store->GetLogins(*form, &password_consumer);
    329 
    330   MockWebDataServiceConsumer wds_consumer;
    331 
    332   EXPECT_CALL(wds_consumer,
    333     OnWebDataServiceRequestDone(_, _))
    334     .WillOnce(QuitUIMessageLoop());
    335 
    336   wds_->GetIE7Login(password_info, &wds_consumer);
    337 
    338   // Run the MessageLoop twice: once for the GetIE7Login that PasswordStoreWin
    339   // schedules on the DB thread and once for the one we just scheduled on the UI
    340   // thread.
    341   MessageLoop::current()->Run();
    342   MessageLoop::current()->Run();
    343 
    344   STLDeleteElements(&forms);
    345 
    346   store->Shutdown();
    347 }
    348 
    349 TEST_F(PasswordStoreWinTest, Migration) {
    350   PasswordFormData autofillable_data[] = {
    351     { PasswordForm::SCHEME_HTML,
    352       "http://foo.example.com",
    353       "http://foo.example.com/origin",
    354       "http://foo.example.com/action",
    355       L"submit_element",
    356       L"username_element",
    357       L"password_element",
    358       L"username_value",
    359       L"password_value",
    360       true, false, 1 },
    361     { PasswordForm::SCHEME_HTML,
    362       "http://bar.example.com",
    363       "http://bar.example.com/origin",
    364       "http://bar.example.com/action",
    365       L"submit_element",
    366       L"username_element",
    367       L"password_element",
    368       L"username_value",
    369       L"password_value",
    370       true, false, 2 },
    371     { PasswordForm::SCHEME_HTML,
    372       "http://baz.example.com",
    373       "http://baz.example.com/origin",
    374       "http://baz.example.com/action",
    375       L"submit_element",
    376       L"username_element",
    377       L"password_element",
    378       L"username_value",
    379       L"password_value",
    380       true, false, 3 },
    381   };
    382   PasswordFormData blacklisted_data[] = {
    383     { PasswordForm::SCHEME_HTML,
    384       "http://blacklisted.example.com",
    385       "http://blacklisted.example.com/origin",
    386       "http://blacklisted.example.com/action",
    387       L"submit_element",
    388       L"username_element",
    389       L"password_element",
    390       NULL,
    391       NULL,
    392       false, false, 1 },
    393     { PasswordForm::SCHEME_HTML,
    394       "http://blacklisted2.example.com",
    395       "http://blacklisted2.example.com/origin",
    396       "http://blacklisted2.example.com/action",
    397       L"submit_element",
    398       L"username_element",
    399       L"password_element",
    400       NULL,
    401       NULL,
    402       false, false, 2 },
    403   };
    404 
    405   VectorOfForms expected_autofillable;
    406   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(autofillable_data); ++i) {
    407     expected_autofillable.push_back(
    408         CreatePasswordFormFromData(autofillable_data[i]));
    409   }
    410 
    411   VectorOfForms expected_blacklisted;
    412   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(blacklisted_data); ++i) {
    413     expected_blacklisted.push_back(
    414         CreatePasswordFormFromData(blacklisted_data[i]));
    415   }
    416 
    417   // Populate the WDS with logins that should be migrated.
    418   for (VectorOfForms::iterator it = expected_autofillable.begin();
    419        it != expected_autofillable.end(); ++it) {
    420     wds_->AddLogin(**it);
    421   }
    422   for (VectorOfForms::iterator it = expected_blacklisted.begin();
    423        it != expected_blacklisted.end(); ++it) {
    424     wds_->AddLogin(**it);
    425   }
    426 
    427   // The WDS schedules tasks to run on the DB thread so we schedule yet another
    428   // task to notify us that it's safe to carry on with the test.
    429   WaitableEvent done(false, false);
    430   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
    431       new SignalingTask(&done));
    432   done.Wait();
    433 
    434   // Initializing the PasswordStore should trigger a migration.
    435   scoped_refptr<PasswordStore> store(
    436       new PasswordStoreWin(login_db_.release(), profile_.get(), wds_.get()));
    437   store->Init();
    438 
    439   // Check that the migration preference has not been initialized;
    440   ASSERT_TRUE(NULL == profile_->GetPrefs()->FindPreference(
    441       prefs::kLoginDatabaseMigrated));
    442 
    443   // Again, the WDS schedules tasks to run on the DB thread, so schedule a task
    444   // to signal us when it is safe to continue.
    445   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
    446       new SignalingTask(&done));
    447   done.Wait();
    448 
    449   // Let the WDS callbacks proceed so the logins can be migrated.
    450   MessageLoop::current()->RunAllPending();
    451 
    452   MockPasswordStoreConsumer consumer;
    453 
    454   // Make sure we quit the MessageLoop even if the test fails.
    455   ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
    456       .WillByDefault(QuitUIMessageLoop());
    457 
    458   // The autofillable forms should have been migrated from the WDS to the login
    459   // database.
    460   EXPECT_CALL(consumer,
    461       OnPasswordStoreRequestDone(_,
    462           ContainsAllPasswordForms(expected_autofillable)))
    463       .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
    464 
    465   store->GetAutofillableLogins(&consumer);
    466   MessageLoop::current()->Run();
    467 
    468   // The blacklisted forms should have been migrated from the WDS to the login
    469   // database.
    470   EXPECT_CALL(consumer,
    471       OnPasswordStoreRequestDone(_,
    472           ContainsAllPasswordForms(expected_blacklisted)))
    473       .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
    474 
    475   store->GetBlacklistLogins(&consumer);
    476   MessageLoop::current()->Run();
    477 
    478   // Check that the migration updated the migrated preference.
    479   ASSERT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kLoginDatabaseMigrated));
    480 
    481   MockWebDataServiceConsumer wds_consumer;
    482 
    483   // No autofillable logins should be left in the WDS.
    484   EXPECT_CALL(wds_consumer,
    485       OnWebDataServiceRequestDone(_, EmptyWDResult()));
    486 
    487   wds_->GetAutofillableLogins(&wds_consumer);
    488 
    489   // Wait for the WDS methods to execute on the DB thread.
    490   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
    491       new SignalingTask(&done));
    492   done.Wait();
    493 
    494   // Handle the callback from the WDS.
    495   MessageLoop::current()->RunAllPending();
    496 
    497   // Likewise, no blacklisted logins should be left in the WDS.
    498   EXPECT_CALL(wds_consumer,
    499       OnWebDataServiceRequestDone(_, EmptyWDResult()));
    500 
    501   wds_->GetBlacklistLogins(&wds_consumer);
    502 
    503   // Wait for the WDS methods to execute on the DB thread.
    504   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
    505       new SignalingTask(&done));
    506   done.Wait();
    507 
    508   // Handle the callback from the WDS.
    509   MessageLoop::current()->RunAllPending();
    510 
    511   STLDeleteElements(&expected_autofillable);
    512   STLDeleteElements(&expected_blacklisted);
    513 }
    514 
    515 TEST_F(PasswordStoreWinTest, EmptyLogins) {
    516   scoped_refptr<PasswordStore> store(
    517       new PasswordStoreWin(login_db_.release(), profile_.get(), wds_.get()));
    518   store->Init();
    519 
    520   PasswordFormData form_data = {
    521     PasswordForm::SCHEME_HTML,
    522     "http://example.com/",
    523     "http://example.com/origin",
    524     "http://example.com/action",
    525     L"submit_element",
    526     L"username_element",
    527     L"password_element",
    528     L"",
    529     L"",
    530     true, false, 1,
    531   };
    532   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
    533 
    534   MockPasswordStoreConsumer consumer;
    535 
    536   // Make sure we quit the MessageLoop even if the test fails.
    537   ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
    538       .WillByDefault(QuitUIMessageLoop());
    539 
    540   VectorOfForms expect_none;
    541   // expect that we get no results;
    542   EXPECT_CALL(consumer, OnPasswordStoreRequestDone(
    543       _, ContainsAllPasswordForms(expect_none)))
    544       .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
    545 
    546   store->GetLogins(*form, &consumer);
    547   MessageLoop::current()->Run();
    548 
    549   store->Shutdown();
    550 }
    551 
    552 TEST_F(PasswordStoreWinTest, EmptyBlacklistLogins) {
    553   scoped_refptr<PasswordStore> store(
    554       new PasswordStoreWin(login_db_.release(), profile_.get(), wds_.get()));
    555   store->Init();
    556 
    557   MockPasswordStoreConsumer consumer;
    558 
    559   // Make sure we quit the MessageLoop even if the test fails.
    560   ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
    561       .WillByDefault(QuitUIMessageLoop());
    562 
    563   VectorOfForms expect_none;
    564   // expect that we get no results;
    565   EXPECT_CALL(consumer, OnPasswordStoreRequestDone(
    566       _, ContainsAllPasswordForms(expect_none)))
    567       .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
    568 
    569   store->GetBlacklistLogins(&consumer);
    570   MessageLoop::current()->Run();
    571 
    572   store->Shutdown();
    573 }
    574 
    575 TEST_F(PasswordStoreWinTest, EmptyAutofillableLogins) {
    576   scoped_refptr<PasswordStore> store(
    577       new PasswordStoreWin(login_db_.release(), profile_.get(), wds_.get()));
    578   store->Init();
    579 
    580   MockPasswordStoreConsumer consumer;
    581 
    582   // Make sure we quit the MessageLoop even if the test fails.
    583   ON_CALL(consumer, OnPasswordStoreRequestDone(_, _))
    584       .WillByDefault(QuitUIMessageLoop());
    585 
    586   VectorOfForms expect_none;
    587   // expect that we get no results;
    588   EXPECT_CALL(consumer, OnPasswordStoreRequestDone(
    589       _, ContainsAllPasswordForms(expect_none)))
    590       .WillOnce(DoAll(WithArg<1>(STLDeleteElements0()), QuitUIMessageLoop()));
    591 
    592   store->GetAutofillableLogins(&consumer);
    593   MessageLoop::current()->Run();
    594 
    595   store->Shutdown();
    596 }
    597