Home | History | Annotate | Download | only in password_manager
      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.
      4 
      5 #include "chrome/browser/password_manager/password_store_mac.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/files/scoped_temp_dir.h"
      9 #include "base/memory/scoped_vector.h"
     10 #include "base/path_service.h"
     11 #include "base/scoped_observer.h"
     12 #include "base/stl_util.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "chrome/browser/password_manager/password_store_mac_internal.h"
     16 #include "chrome/common/chrome_paths.h"
     17 #include "components/password_manager/core/browser/password_store_consumer.h"
     18 #include "content/public/test/test_browser_thread.h"
     19 #include "crypto/mock_apple_keychain.h"
     20 #include "testing/gmock/include/gmock/gmock.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 
     23 using autofill::PasswordForm;
     24 using base::ASCIIToUTF16;
     25 using base::WideToUTF16;
     26 using content::BrowserThread;
     27 using crypto::MockAppleKeychain;
     28 using internal_keychain_helpers::FormsMatchForMerge;
     29 using internal_keychain_helpers::STRICT_FORM_MATCH;
     30 using password_manager::LoginDatabase;
     31 using password_manager::PasswordStore;
     32 using password_manager::PasswordStoreConsumer;
     33 using testing::_;
     34 using testing::DoAll;
     35 using testing::Invoke;
     36 using testing::WithArg;
     37 
     38 namespace {
     39 
     40 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
     41  public:
     42   MOCK_METHOD1(OnGetPasswordStoreResults,
     43                void(const std::vector<autofill::PasswordForm*>&));
     44 
     45   void CopyElements(const std::vector<autofill::PasswordForm*>& forms) {
     46     last_result.clear();
     47     for (size_t i = 0; i < forms.size(); ++i) {
     48       last_result.push_back(*forms[i]);
     49     }
     50   }
     51 
     52   std::vector<PasswordForm> last_result;
     53 };
     54 
     55 ACTION(STLDeleteElements0) {
     56   STLDeleteContainerPointers(arg0.begin(), arg0.end());
     57 }
     58 
     59 ACTION(QuitUIMessageLoop) {
     60   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     61   base::MessageLoop::current()->Quit();
     62 }
     63 
     64 class TestPasswordStoreMac : public PasswordStoreMac {
     65  public:
     66   TestPasswordStoreMac(
     67       scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
     68       scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
     69       crypto::AppleKeychain* keychain,
     70       LoginDatabase* login_db)
     71       : PasswordStoreMac(main_thread_runner,
     72                          db_thread_runner,
     73                          keychain,
     74                          login_db) {
     75   }
     76 
     77   using PasswordStoreMac::GetBackgroundTaskRunner;
     78 
     79  private:
     80   virtual ~TestPasswordStoreMac() {}
     81 
     82   DISALLOW_COPY_AND_ASSIGN(TestPasswordStoreMac);
     83 };
     84 
     85 }  // namespace
     86 
     87 #pragma mark -
     88 
     89 class PasswordStoreMacInternalsTest : public testing::Test {
     90  public:
     91   virtual void SetUp() {
     92     MockAppleKeychain::KeychainTestData test_data[] = {
     93       // Basic HTML form.
     94       { kSecAuthenticationTypeHTMLForm, "some.domain.com",
     95         kSecProtocolTypeHTTP, NULL, 0, NULL, "20020601171500Z",
     96         "joe_user", "sekrit", false },
     97       // HTML form with path.
     98       { kSecAuthenticationTypeHTMLForm, "some.domain.com",
     99         kSecProtocolTypeHTTP, "/insecure.html", 0, NULL, "19991231235959Z",
    100         "joe_user", "sekrit", false },
    101       // Secure HTML form with path.
    102       { kSecAuthenticationTypeHTMLForm, "some.domain.com",
    103         kSecProtocolTypeHTTPS, "/secure.html", 0, NULL, "20100908070605Z",
    104         "secure_user", "password", false },
    105       // True negative item.
    106       { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
    107         kSecProtocolTypeHTTP, NULL, 0, NULL, "20000101000000Z",
    108         "", "", true },
    109       // De-facto negative item, type one.
    110       { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
    111         kSecProtocolTypeHTTP, NULL, 0, NULL, "20000101000000Z",
    112         "Password Not Stored", "", false },
    113       // De-facto negative item, type two.
    114       { kSecAuthenticationTypeHTMLForm, "dont.remember.com",
    115         kSecProtocolTypeHTTPS, NULL, 0, NULL, "20000101000000Z",
    116         "Password Not Stored", " ", false },
    117       // HTTP auth basic, with port and path.
    118       { kSecAuthenticationTypeHTTPBasic, "some.domain.com",
    119         kSecProtocolTypeHTTP, "/insecure.html", 4567, "low_security",
    120         "19980330100000Z",
    121         "basic_auth_user", "basic", false },
    122       // HTTP auth digest, secure.
    123       { kSecAuthenticationTypeHTTPDigest, "some.domain.com",
    124         kSecProtocolTypeHTTPS, NULL, 0, "high_security", "19980330100000Z",
    125         "digest_auth_user", "digest", false },
    126       // An FTP password with an invalid date, for edge-case testing.
    127       { kSecAuthenticationTypeDefault, "a.server.com",
    128         kSecProtocolTypeFTP, NULL, 0, NULL, "20010203040",
    129         "abc", "123", false },
    130     };
    131 
    132     keychain_ = new MockAppleKeychain();
    133 
    134     for (unsigned int i = 0; i < arraysize(test_data); ++i) {
    135       keychain_->AddTestItem(test_data[i]);
    136     }
    137   }
    138 
    139   virtual void TearDown() {
    140     ExpectCreatesAndFreesBalanced();
    141     ExpectCreatorCodesSet();
    142     delete keychain_;
    143   }
    144 
    145  protected:
    146   // Causes a test failure unless everything returned from keychain_'s
    147   // ItemCopyAttributesAndData, SearchCreateFromAttributes, and SearchCopyNext
    148   // was correctly freed.
    149   void ExpectCreatesAndFreesBalanced() {
    150     EXPECT_EQ(0, keychain_->UnfreedSearchCount());
    151     EXPECT_EQ(0, keychain_->UnfreedKeychainItemCount());
    152     EXPECT_EQ(0, keychain_->UnfreedAttributeDataCount());
    153   }
    154 
    155   // Causes a test failure unless any Keychain items added during the test have
    156   // their creator code set.
    157   void ExpectCreatorCodesSet() {
    158     EXPECT_TRUE(keychain_->CreatorCodesSetForAddedItems());
    159   }
    160 
    161   MockAppleKeychain* keychain_;
    162 };
    163 
    164 #pragma mark -
    165 
    166 // Struct used for creation of PasswordForms from static arrays of data.
    167 struct PasswordFormData {
    168   const PasswordForm::Scheme scheme;
    169   const char* signon_realm;
    170   const char* origin;
    171   const char* action;
    172   const wchar_t* submit_element;
    173   const wchar_t* username_element;
    174   const wchar_t* password_element;
    175   const wchar_t* username_value;  // Set to NULL for a blacklist entry.
    176   const wchar_t* password_value;
    177   const bool preferred;
    178   const bool ssl_valid;
    179   const double creation_time;
    180 };
    181 
    182 // Creates and returns a new PasswordForm built from form_data. Caller is
    183 // responsible for deleting the object when finished with it.
    184 static PasswordForm* CreatePasswordFormFromData(
    185     const PasswordFormData& form_data) {
    186   PasswordForm* form = new PasswordForm();
    187   form->scheme = form_data.scheme;
    188   form->preferred = form_data.preferred;
    189   form->ssl_valid = form_data.ssl_valid;
    190   form->date_created = base::Time::FromDoubleT(form_data.creation_time);
    191   form->date_synced = form->date_created + base::TimeDelta::FromDays(1);
    192   if (form_data.signon_realm)
    193     form->signon_realm = std::string(form_data.signon_realm);
    194   if (form_data.origin)
    195     form->origin = GURL(form_data.origin);
    196   if (form_data.action)
    197     form->action = GURL(form_data.action);
    198   if (form_data.submit_element)
    199     form->submit_element = WideToUTF16(form_data.submit_element);
    200   if (form_data.username_element)
    201     form->username_element = WideToUTF16(form_data.username_element);
    202   if (form_data.password_element)
    203     form->password_element = WideToUTF16(form_data.password_element);
    204   if (form_data.username_value) {
    205     form->username_value = WideToUTF16(form_data.username_value);
    206     form->display_name = form->username_value;
    207     form->is_zero_click = true;
    208     if (form_data.password_value)
    209       form->password_value = WideToUTF16(form_data.password_value);
    210   } else {
    211     form->blacklisted_by_user = true;
    212   }
    213   form->avatar_url = GURL("https://accounts.google.com/Avatar");
    214   form->federation_url = GURL("https://accounts.google.com/login");
    215   return form;
    216 }
    217 
    218 // Macro to simplify calling CheckFormsAgainstExpectations with a useful label.
    219 #define CHECK_FORMS(forms, expectations, i) \
    220     CheckFormsAgainstExpectations(forms, expectations, #forms, i)
    221 
    222 // Ensures that the data in |forms| match |expectations|, causing test failures
    223 // for any discrepencies.
    224 // TODO(stuartmorgan): This is current order-dependent; ideally it shouldn't
    225 // matter if |forms| and |expectations| are scrambled.
    226 static void CheckFormsAgainstExpectations(
    227     const std::vector<PasswordForm*>& forms,
    228     const std::vector<PasswordFormData*>& expectations,
    229     const char* forms_label, unsigned int test_number) {
    230   const unsigned int kBufferSize = 128;
    231   char test_label[kBufferSize];
    232   snprintf(test_label, kBufferSize, "%s in test %u", forms_label, test_number);
    233 
    234   EXPECT_EQ(expectations.size(), forms.size()) << test_label;
    235   if (expectations.size() != forms.size())
    236     return;
    237 
    238   for (unsigned int i = 0; i < expectations.size(); ++i) {
    239     snprintf(test_label, kBufferSize, "%s in test %u, item %u",
    240              forms_label, test_number, i);
    241     PasswordForm* form = forms[i];
    242     PasswordFormData* expectation = expectations[i];
    243     EXPECT_EQ(expectation->scheme, form->scheme) << test_label;
    244     EXPECT_EQ(std::string(expectation->signon_realm), form->signon_realm)
    245         << test_label;
    246     EXPECT_EQ(GURL(expectation->origin), form->origin) << test_label;
    247     EXPECT_EQ(GURL(expectation->action), form->action) << test_label;
    248     EXPECT_EQ(WideToUTF16(expectation->submit_element), form->submit_element)
    249         << test_label;
    250     EXPECT_EQ(WideToUTF16(expectation->username_element),
    251               form->username_element) << test_label;
    252     EXPECT_EQ(WideToUTF16(expectation->password_element),
    253               form->password_element) << test_label;
    254     if (expectation->username_value) {
    255       EXPECT_EQ(WideToUTF16(expectation->username_value),
    256                 form->username_value) << test_label;
    257       EXPECT_EQ(WideToUTF16(expectation->username_value),
    258                 form->display_name) << test_label;
    259       EXPECT_TRUE(form->is_zero_click) << test_label;
    260       EXPECT_EQ(WideToUTF16(expectation->password_value),
    261                 form->password_value) << test_label;
    262     } else {
    263       EXPECT_TRUE(form->blacklisted_by_user) << test_label;
    264     }
    265     EXPECT_EQ(expectation->preferred, form->preferred)  << test_label;
    266     EXPECT_EQ(expectation->ssl_valid, form->ssl_valid) << test_label;
    267     EXPECT_DOUBLE_EQ(expectation->creation_time,
    268                      form->date_created.ToDoubleT()) << test_label;
    269     base::Time created = base::Time::FromDoubleT(expectation->creation_time);
    270     EXPECT_EQ(created + base::TimeDelta::FromDays(1),
    271               form->date_synced) << test_label;
    272     EXPECT_EQ(GURL("https://accounts.google.com/Avatar"), form->avatar_url);
    273     EXPECT_EQ(GURL("https://accounts.google.com/login"), form->federation_url);
    274   }
    275 }
    276 
    277 #pragma mark -
    278 
    279 TEST_F(PasswordStoreMacInternalsTest, TestKeychainToFormTranslation) {
    280   typedef struct {
    281     const PasswordForm::Scheme scheme;
    282     const char* signon_realm;
    283     const char* origin;
    284     const wchar_t* username;  // Set to NULL to check for a blacklist entry.
    285     const wchar_t* password;
    286     const bool ssl_valid;
    287     const int creation_year;
    288     const int creation_month;
    289     const int creation_day;
    290     const int creation_hour;
    291     const int creation_minute;
    292     const int creation_second;
    293   } TestExpectations;
    294 
    295   TestExpectations expected[] = {
    296     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    297       "http://some.domain.com/", L"joe_user", L"sekrit", false,
    298       2002,  6,  1, 17, 15,  0 },
    299     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    300       "http://some.domain.com/insecure.html", L"joe_user", L"sekrit", false,
    301       1999, 12, 31, 23, 59, 59 },
    302     { PasswordForm::SCHEME_HTML, "https://some.domain.com/",
    303       "https://some.domain.com/secure.html", L"secure_user", L"password", true,
    304       2010,  9,  8,  7,  6,  5 },
    305     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
    306       "http://dont.remember.com/", NULL, NULL, false,
    307       2000,  1,  1,  0,  0,  0 },
    308     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
    309       "http://dont.remember.com/", NULL, NULL, false,
    310       2000,  1,  1,  0,  0,  0 },
    311     { PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
    312       "https://dont.remember.com/", NULL, NULL, true,
    313       2000,  1,  1,  0,  0,  0 },
    314     { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
    315       "http://some.domain.com:4567/insecure.html", L"basic_auth_user", L"basic",
    316       false, 1998, 03, 30, 10, 00, 00 },
    317     { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
    318       "https://some.domain.com/", L"digest_auth_user", L"digest", true,
    319       1998,  3, 30, 10,  0,  0 },
    320     // This one gives us an invalid date, which we will treat as a "NULL" date
    321     // which is 1601.
    322     { PasswordForm::SCHEME_OTHER, "http://a.server.com/",
    323       "http://a.server.com/", L"abc", L"123", false,
    324       1601,  1,  1,  0,  0,  0 },
    325   };
    326 
    327   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(expected); ++i) {
    328     // Create our fake KeychainItemRef; see MockAppleKeychain docs.
    329     SecKeychainItemRef keychain_item =
    330         reinterpret_cast<SecKeychainItemRef>(i + 1);
    331     PasswordForm form;
    332     bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
    333         *keychain_, keychain_item, &form, true);
    334 
    335     EXPECT_TRUE(parsed) << "In iteration " << i;
    336 
    337     EXPECT_EQ(expected[i].scheme, form.scheme) << "In iteration " << i;
    338     EXPECT_EQ(GURL(expected[i].origin), form.origin) << "In iteration " << i;
    339     EXPECT_EQ(expected[i].ssl_valid, form.ssl_valid) << "In iteration " << i;
    340     EXPECT_EQ(std::string(expected[i].signon_realm), form.signon_realm)
    341         << "In iteration " << i;
    342     if (expected[i].username) {
    343       EXPECT_EQ(WideToUTF16(expected[i].username), form.username_value)
    344           << "In iteration " << i;
    345       EXPECT_EQ(WideToUTF16(expected[i].password), form.password_value)
    346           << "In iteration " << i;
    347       EXPECT_FALSE(form.blacklisted_by_user) << "In iteration " << i;
    348     } else {
    349       EXPECT_TRUE(form.blacklisted_by_user) << "In iteration " << i;
    350     }
    351     base::Time::Exploded exploded_time;
    352     form.date_created.UTCExplode(&exploded_time);
    353     EXPECT_EQ(expected[i].creation_year, exploded_time.year)
    354          << "In iteration " << i;
    355     EXPECT_EQ(expected[i].creation_month, exploded_time.month)
    356         << "In iteration " << i;
    357     EXPECT_EQ(expected[i].creation_day, exploded_time.day_of_month)
    358         << "In iteration " << i;
    359     EXPECT_EQ(expected[i].creation_hour, exploded_time.hour)
    360         << "In iteration " << i;
    361     EXPECT_EQ(expected[i].creation_minute, exploded_time.minute)
    362         << "In iteration " << i;
    363     EXPECT_EQ(expected[i].creation_second, exploded_time.second)
    364         << "In iteration " << i;
    365   }
    366 
    367   {
    368     // Use an invalid ref, to make sure errors are reported.
    369     SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(99);
    370     PasswordForm form;
    371     bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
    372         *keychain_, keychain_item, &form, true);
    373     EXPECT_FALSE(parsed);
    374   }
    375 }
    376 
    377 TEST_F(PasswordStoreMacInternalsTest, TestKeychainSearch) {
    378   struct TestDataAndExpectation {
    379     const PasswordFormData data;
    380     const size_t expected_fill_matches;
    381     const size_t expected_merge_matches;
    382   };
    383   // Most fields are left blank because we don't care about them for searching.
    384   TestDataAndExpectation test_data[] = {
    385     // An HTML form we've seen.
    386     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    387         NULL, NULL, NULL, NULL, NULL, L"joe_user", NULL, false, false, 0 },
    388       2, 2 },
    389     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    390         NULL, NULL, NULL, NULL, NULL, L"wrong_user", NULL, false, false, 0 },
    391       2, 0 },
    392     // An HTML form we haven't seen
    393     { { PasswordForm::SCHEME_HTML, "http://www.unseendomain.com/",
    394         NULL, NULL, NULL, NULL, NULL, L"joe_user", NULL, false, false, 0 },
    395       0, 0 },
    396     // Basic auth that should match.
    397     { { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
    398         NULL, NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, false, false,
    399         0 },
    400       1, 1 },
    401     // Basic auth with the wrong port.
    402     { { PasswordForm::SCHEME_BASIC, "http://some.domain.com:1111/low_security",
    403         NULL, NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, false, false,
    404         0 },
    405       0, 0 },
    406     // Digest auth we've saved under https, visited with http.
    407     { { PasswordForm::SCHEME_DIGEST, "http://some.domain.com/high_security",
    408         NULL, NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, false, false,
    409         0 },
    410       0, 0 },
    411     // Digest auth that should match.
    412     { { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
    413         NULL, NULL, NULL, NULL, NULL, L"wrong_user", NULL, false, true, 0 },
    414       1, 0 },
    415     // Digest auth with the wrong domain.
    416     { { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/other_domain",
    417         NULL, NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, false, true,
    418         0 },
    419       0, 0 },
    420     // Garbage forms should have no matches.
    421     { { PasswordForm::SCHEME_HTML, "foo/bar/baz",
    422         NULL, NULL, NULL, NULL, NULL, NULL, NULL, false, false, 0 }, 0, 0 },
    423   };
    424 
    425   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
    426   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
    427   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
    428   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
    429     scoped_ptr<PasswordForm> query_form(
    430         CreatePasswordFormFromData(test_data[i].data));
    431 
    432     // Check matches treating the form as a fill target.
    433     std::vector<PasswordForm*> matching_items =
    434         keychain_adapter.PasswordsFillingForm(query_form->signon_realm,
    435                                               query_form->scheme);
    436     EXPECT_EQ(test_data[i].expected_fill_matches, matching_items.size());
    437     STLDeleteElements(&matching_items);
    438 
    439     // Check matches treating the form as a merging target.
    440     EXPECT_EQ(test_data[i].expected_merge_matches > 0,
    441               keychain_adapter.HasPasswordsMergeableWithForm(*query_form));
    442     std::vector<SecKeychainItemRef> keychain_items;
    443     std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs =
    444         internal_keychain_helpers::
    445             ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
    446                                                               *keychain_);
    447     matching_items =
    448         internal_keychain_helpers::ExtractPasswordsMergeableWithForm(
    449             *keychain_, item_form_pairs, *query_form);
    450     EXPECT_EQ(test_data[i].expected_merge_matches, matching_items.size());
    451     STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
    452                                          item_form_pairs.end());
    453     for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
    454          i != keychain_items.end(); ++i) {
    455       keychain_->Free(*i);
    456     }
    457     STLDeleteElements(&matching_items);
    458 
    459     // None of the pre-seeded items are owned by us, so none should match an
    460     // owned-passwords-only search.
    461     matching_items = owned_keychain_adapter.PasswordsFillingForm(
    462         query_form->signon_realm, query_form->scheme);
    463     EXPECT_EQ(0U, matching_items.size());
    464     STLDeleteElements(&matching_items);
    465   }
    466 }
    467 
    468 // Changes just the origin path of |form|.
    469 static void SetPasswordFormPath(PasswordForm* form, const char* path) {
    470   GURL::Replacements replacement;
    471   std::string new_value(path);
    472   replacement.SetPathStr(new_value);
    473   form->origin = form->origin.ReplaceComponents(replacement);
    474 }
    475 
    476 // Changes just the signon_realm port of |form|.
    477 static void SetPasswordFormPort(PasswordForm* form, const char* port) {
    478   GURL::Replacements replacement;
    479   std::string new_value(port);
    480   replacement.SetPortStr(new_value);
    481   GURL signon_gurl = GURL(form->signon_realm);
    482   form->signon_realm = signon_gurl.ReplaceComponents(replacement).spec();
    483 }
    484 
    485 // Changes just the signon_ream auth realm of |form|.
    486 static void SetPasswordFormRealm(PasswordForm* form, const char* realm) {
    487   GURL::Replacements replacement;
    488   std::string new_value(realm);
    489   replacement.SetPathStr(new_value);
    490   GURL signon_gurl = GURL(form->signon_realm);
    491   form->signon_realm = signon_gurl.ReplaceComponents(replacement).spec();
    492 }
    493 
    494 TEST_F(PasswordStoreMacInternalsTest, TestKeychainExactSearch) {
    495   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
    496 
    497   PasswordFormData base_form_data[] = {
    498     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    499       "http://some.domain.com/insecure.html",
    500       NULL, NULL, NULL, NULL, L"joe_user", NULL, true, false, 0 },
    501     { PasswordForm::SCHEME_BASIC, "http://some.domain.com:4567/low_security",
    502       "http://some.domain.com:4567/insecure.html",
    503       NULL, NULL, NULL, NULL, L"basic_auth_user", NULL, true, false, 0 },
    504     { PasswordForm::SCHEME_DIGEST, "https://some.domain.com/high_security",
    505       "https://some.domain.com",
    506       NULL, NULL, NULL, NULL, L"digest_auth_user", NULL, true, true, 0 },
    507   };
    508 
    509   for (unsigned int i = 0; i < arraysize(base_form_data); ++i) {
    510     // Create a base form and make sure we find a match.
    511     scoped_ptr<PasswordForm> base_form(CreatePasswordFormFromData(
    512         base_form_data[i]));
    513     EXPECT_TRUE(keychain_adapter.HasPasswordsMergeableWithForm(*base_form));
    514     EXPECT_TRUE(keychain_adapter.HasPasswordExactlyMatchingForm(*base_form));
    515 
    516     // Make sure that the matching isn't looser than it should be by checking
    517     // that slightly altered forms don't match.
    518     std::vector<PasswordForm*> modified_forms;
    519 
    520     modified_forms.push_back(new PasswordForm(*base_form));
    521     modified_forms.back()->username_value = ASCIIToUTF16("wrong_user");
    522 
    523     modified_forms.push_back(new PasswordForm(*base_form));
    524     SetPasswordFormPath(modified_forms.back(), "elsewhere.html");
    525 
    526     modified_forms.push_back(new PasswordForm(*base_form));
    527     modified_forms.back()->scheme = PasswordForm::SCHEME_OTHER;
    528 
    529     modified_forms.push_back(new PasswordForm(*base_form));
    530     SetPasswordFormPort(modified_forms.back(), "1234");
    531 
    532     modified_forms.push_back(new PasswordForm(*base_form));
    533     modified_forms.back()->blacklisted_by_user = true;
    534 
    535     if (base_form->scheme == PasswordForm::SCHEME_BASIC ||
    536         base_form->scheme == PasswordForm::SCHEME_DIGEST) {
    537       modified_forms.push_back(new PasswordForm(*base_form));
    538       SetPasswordFormRealm(modified_forms.back(), "incorrect");
    539     }
    540 
    541     for (unsigned int j = 0; j < modified_forms.size(); ++j) {
    542       bool match = keychain_adapter.HasPasswordExactlyMatchingForm(
    543           *modified_forms[j]);
    544       EXPECT_FALSE(match) << "In modified version " << j
    545           << " of base form " << i;
    546     }
    547     STLDeleteElements(&modified_forms);
    548   }
    549 }
    550 
    551 TEST_F(PasswordStoreMacInternalsTest, TestKeychainAdd) {
    552   struct TestDataAndExpectation {
    553     PasswordFormData data;
    554     bool should_succeed;
    555   };
    556   TestDataAndExpectation test_data[] = {
    557     // Test a variety of scheme/port/protocol/path variations.
    558     { { PasswordForm::SCHEME_HTML, "http://web.site.com/",
    559         "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
    560         L"anonymous", L"knock-knock", false, false, 0 }, true },
    561     { { PasswordForm::SCHEME_HTML, "https://web.site.com/",
    562         "https://web.site.com/", NULL, NULL, NULL, NULL,
    563         L"admin", L"p4ssw0rd", false, false, 0 }, true },
    564     { { PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm",
    565         "http://a.site.com:2222/", NULL, NULL, NULL, NULL,
    566         L"username", L"password", false, false, 0 }, true },
    567     { { PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm",
    568         "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL,
    569         L"testname", L"testpass", false, false, 0 }, true },
    570     // Make sure that garbage forms are rejected.
    571     { { PasswordForm::SCHEME_HTML, "gobbledygook",
    572         "gobbledygook", NULL, NULL, NULL, NULL,
    573         L"anonymous", L"knock-knock", false, false, 0 }, false },
    574     // Test that failing to update a duplicate (forced using the magic failure
    575     // password; see MockAppleKeychain::ItemModifyAttributesAndData) is
    576     // reported.
    577     { { PasswordForm::SCHEME_HTML, "http://some.domain.com",
    578         "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
    579         L"joe_user", L"fail_me", false, false, 0 }, false },
    580   };
    581 
    582   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
    583   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
    584 
    585   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
    586     scoped_ptr<PasswordForm> in_form(
    587         CreatePasswordFormFromData(test_data[i].data));
    588     bool add_succeeded = owned_keychain_adapter.AddPassword(*in_form);
    589     EXPECT_EQ(test_data[i].should_succeed, add_succeeded);
    590     if (add_succeeded) {
    591       EXPECT_TRUE(owned_keychain_adapter.HasPasswordsMergeableWithForm(
    592           *in_form));
    593       EXPECT_TRUE(owned_keychain_adapter.HasPasswordExactlyMatchingForm(
    594           *in_form));
    595     }
    596   }
    597 
    598   // Test that adding duplicate item updates the existing item.
    599   {
    600     PasswordFormData data = {
    601       PasswordForm::SCHEME_HTML, "http://some.domain.com",
    602       "http://some.domain.com/insecure.html", NULL,
    603       NULL, NULL, NULL, L"joe_user", L"updated_password", false, false, 0
    604     };
    605     scoped_ptr<PasswordForm> update_form(CreatePasswordFormFromData(data));
    606     MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
    607     EXPECT_TRUE(keychain_adapter.AddPassword(*update_form));
    608     SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(2);
    609     PasswordForm stored_form;
    610     internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
    611                                                                 keychain_item,
    612                                                                 &stored_form,
    613                                                                 true);
    614     EXPECT_EQ(update_form->password_value, stored_form.password_value);
    615   }
    616 }
    617 
    618 TEST_F(PasswordStoreMacInternalsTest, TestKeychainRemove) {
    619   struct TestDataAndExpectation {
    620     PasswordFormData data;
    621     bool should_succeed;
    622   };
    623   TestDataAndExpectation test_data[] = {
    624     // Test deletion of an item that we add.
    625     { { PasswordForm::SCHEME_HTML, "http://web.site.com/",
    626         "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
    627         L"anonymous", L"knock-knock", false, false, 0 }, true },
    628     // Make sure we don't delete items we don't own.
    629     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    630         "http://some.domain.com/insecure.html", NULL, NULL, NULL, NULL,
    631         L"joe_user", NULL, true, false, 0 }, false },
    632   };
    633 
    634   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
    635   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
    636 
    637   // Add our test item so that we can delete it.
    638   PasswordForm* add_form = CreatePasswordFormFromData(test_data[0].data);
    639   EXPECT_TRUE(owned_keychain_adapter.AddPassword(*add_form));
    640   delete add_form;
    641 
    642   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
    643     scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
    644         test_data[i].data));
    645     EXPECT_EQ(test_data[i].should_succeed,
    646               owned_keychain_adapter.RemovePassword(*form));
    647 
    648     MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
    649     bool match = keychain_adapter.HasPasswordExactlyMatchingForm(*form);
    650     EXPECT_EQ(test_data[i].should_succeed, !match);
    651   }
    652 }
    653 
    654 TEST_F(PasswordStoreMacInternalsTest, TestFormMatch) {
    655   PasswordForm base_form;
    656   base_form.signon_realm = std::string("http://some.domain.com/");
    657   base_form.origin = GURL("http://some.domain.com/page.html");
    658   base_form.username_value = ASCIIToUTF16("joe_user");
    659 
    660   {
    661     // Check that everything unimportant can be changed.
    662     PasswordForm different_form(base_form);
    663     different_form.username_element = ASCIIToUTF16("username");
    664     different_form.submit_element = ASCIIToUTF16("submit");
    665     different_form.username_element = ASCIIToUTF16("password");
    666     different_form.password_value = ASCIIToUTF16("sekrit");
    667     different_form.action = GURL("http://some.domain.com/action.cgi");
    668     different_form.ssl_valid = true;
    669     different_form.preferred = true;
    670     different_form.date_created = base::Time::Now();
    671     EXPECT_TRUE(
    672         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
    673 
    674     // Check that path differences don't prevent a match.
    675     base_form.origin = GURL("http://some.domain.com/other_page.html");
    676     EXPECT_TRUE(
    677         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
    678   }
    679 
    680   // Check that any one primary key changing is enough to prevent matching.
    681   {
    682     PasswordForm different_form(base_form);
    683     different_form.scheme = PasswordForm::SCHEME_DIGEST;
    684     EXPECT_FALSE(
    685         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
    686   }
    687   {
    688     PasswordForm different_form(base_form);
    689     different_form.signon_realm = std::string("http://some.domain.com:8080/");
    690     EXPECT_FALSE(
    691         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
    692   }
    693   {
    694     PasswordForm different_form(base_form);
    695     different_form.username_value = ASCIIToUTF16("john.doe");
    696     EXPECT_FALSE(
    697         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
    698   }
    699   {
    700     PasswordForm different_form(base_form);
    701     different_form.blacklisted_by_user = true;
    702     EXPECT_FALSE(
    703         FormsMatchForMerge(base_form, different_form, STRICT_FORM_MATCH));
    704   }
    705 
    706   // Blacklist forms should *never* match for merging, even when identical
    707   // (and certainly not when only one is a blacklist entry).
    708   {
    709     PasswordForm form_a(base_form);
    710     form_a.blacklisted_by_user = true;
    711     PasswordForm form_b(form_a);
    712     EXPECT_FALSE(FormsMatchForMerge(form_a, form_b, STRICT_FORM_MATCH));
    713   }
    714 }
    715 
    716 TEST_F(PasswordStoreMacInternalsTest, TestFormMerge) {
    717   // Set up a bunch of test data to use in varying combinations.
    718   PasswordFormData keychain_user_1 =
    719       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    720         "http://some.domain.com/", "", L"", L"", L"", L"joe_user", L"sekrit",
    721         false, false, 1010101010 };
    722   PasswordFormData keychain_user_1_with_path =
    723       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    724         "http://some.domain.com/page.html",
    725         "", L"", L"", L"", L"joe_user", L"otherpassword",
    726         false, false, 1010101010 };
    727   PasswordFormData keychain_user_2 =
    728       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    729         "http://some.domain.com/", "", L"", L"", L"", L"john.doe", L"sesame",
    730         false, false, 958739876 };
    731   PasswordFormData keychain_blacklist =
    732       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    733         "http://some.domain.com/", "", L"", L"", L"", NULL, NULL,
    734         false, false, 1010101010 };
    735 
    736   PasswordFormData db_user_1 =
    737       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    738         "http://some.domain.com/", "http://some.domain.com/action.cgi",
    739         L"submit", L"username", L"password", L"joe_user", L"",
    740         true, false, 1212121212 };
    741   PasswordFormData db_user_1_with_path =
    742       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    743         "http://some.domain.com/page.html",
    744         "http://some.domain.com/handlepage.cgi",
    745         L"submit", L"username", L"password", L"joe_user", L"",
    746         true, false, 1234567890 };
    747   PasswordFormData db_user_3_with_path =
    748       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    749         "http://some.domain.com/page.html",
    750         "http://some.domain.com/handlepage.cgi",
    751         L"submit", L"username", L"password", L"second-account", L"",
    752         true, false, 1240000000 };
    753   PasswordFormData database_blacklist_with_path =
    754       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    755         "http://some.domain.com/path.html", "http://some.domain.com/action.cgi",
    756         L"submit", L"username", L"password", NULL, NULL,
    757         true, false, 1212121212 };
    758 
    759   PasswordFormData merged_user_1 =
    760       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    761         "http://some.domain.com/", "http://some.domain.com/action.cgi",
    762         L"submit", L"username", L"password", L"joe_user", L"sekrit",
    763         true, false, 1212121212 };
    764   PasswordFormData merged_user_1_with_db_path =
    765       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    766         "http://some.domain.com/page.html",
    767         "http://some.domain.com/handlepage.cgi",
    768         L"submit", L"username", L"password", L"joe_user", L"sekrit",
    769         true, false, 1234567890 };
    770   PasswordFormData merged_user_1_with_both_paths =
    771       { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    772         "http://some.domain.com/page.html",
    773         "http://some.domain.com/handlepage.cgi",
    774         L"submit", L"username", L"password", L"joe_user", L"otherpassword",
    775         true, false, 1234567890 };
    776 
    777   // Build up the big multi-dimensional array of data sets that will actually
    778   // drive the test. Use vectors rather than arrays so that initialization is
    779   // simple.
    780   enum {
    781     KEYCHAIN_INPUT = 0,
    782     DATABASE_INPUT,
    783     MERGE_OUTPUT,
    784     KEYCHAIN_OUTPUT,
    785     DATABASE_OUTPUT,
    786     MERGE_IO_ARRAY_COUNT  // termination marker
    787   };
    788   const unsigned int kTestCount = 4;
    789   std::vector< std::vector< std::vector<PasswordFormData*> > > test_data(
    790       MERGE_IO_ARRAY_COUNT, std::vector< std::vector<PasswordFormData*> >(
    791           kTestCount, std::vector<PasswordFormData*>()));
    792   unsigned int current_test = 0;
    793 
    794   // Test a merge with a few accounts in both systems, with partial overlap.
    795   CHECK(current_test < kTestCount);
    796   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
    797   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_2);
    798   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
    799   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1_with_path);
    800   test_data[DATABASE_INPUT][current_test].push_back(&db_user_3_with_path);
    801   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
    802   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1_with_db_path);
    803   test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_user_2);
    804   test_data[DATABASE_OUTPUT][current_test].push_back(&db_user_3_with_path);
    805 
    806   // Test a merge where Chrome has a blacklist entry, and the keychain has
    807   // a stored account.
    808   ++current_test;
    809   CHECK(current_test < kTestCount);
    810   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
    811   test_data[DATABASE_INPUT][current_test].push_back(
    812       &database_blacklist_with_path);
    813   // We expect both to be present because a blacklist could be specific to a
    814   // subpath, and we want access to the password on other paths.
    815   test_data[MERGE_OUTPUT][current_test].push_back(
    816       &database_blacklist_with_path);
    817   test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_user_1);
    818 
    819   // Test a merge where Chrome has an account, and Keychain has a blacklist
    820   // (from another browser) and the Chrome password data.
    821   ++current_test;
    822   CHECK(current_test < kTestCount);
    823   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_blacklist);
    824   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
    825   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
    826   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
    827   test_data[KEYCHAIN_OUTPUT][current_test].push_back(&keychain_blacklist);
    828 
    829   // Test that matches are done using exact path when possible.
    830   ++current_test;
    831   CHECK(current_test < kTestCount);
    832   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1);
    833   test_data[KEYCHAIN_INPUT][current_test].push_back(&keychain_user_1_with_path);
    834   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1);
    835   test_data[DATABASE_INPUT][current_test].push_back(&db_user_1_with_path);
    836   test_data[MERGE_OUTPUT][current_test].push_back(&merged_user_1);
    837   test_data[MERGE_OUTPUT][current_test].push_back(
    838       &merged_user_1_with_both_paths);
    839 
    840   for (unsigned int test_case = 0; test_case <= current_test; ++test_case) {
    841     std::vector<PasswordForm*> keychain_forms;
    842     for (std::vector<PasswordFormData*>::iterator i =
    843              test_data[KEYCHAIN_INPUT][test_case].begin();
    844          i != test_data[KEYCHAIN_INPUT][test_case].end(); ++i) {
    845       keychain_forms.push_back(CreatePasswordFormFromData(*(*i)));
    846     }
    847     std::vector<PasswordForm*> database_forms;
    848     for (std::vector<PasswordFormData*>::iterator i =
    849              test_data[DATABASE_INPUT][test_case].begin();
    850          i != test_data[DATABASE_INPUT][test_case].end(); ++i) {
    851       database_forms.push_back(CreatePasswordFormFromData(*(*i)));
    852     }
    853 
    854     std::vector<PasswordForm*> merged_forms;
    855     internal_keychain_helpers::MergePasswordForms(&keychain_forms,
    856                                                   &database_forms,
    857                                                   &merged_forms);
    858 
    859     CHECK_FORMS(keychain_forms, test_data[KEYCHAIN_OUTPUT][test_case],
    860                 test_case);
    861     CHECK_FORMS(database_forms, test_data[DATABASE_OUTPUT][test_case],
    862                 test_case);
    863     CHECK_FORMS(merged_forms, test_data[MERGE_OUTPUT][test_case], test_case);
    864 
    865     STLDeleteElements(&keychain_forms);
    866     STLDeleteElements(&database_forms);
    867     STLDeleteElements(&merged_forms);
    868   }
    869 }
    870 
    871 TEST_F(PasswordStoreMacInternalsTest, TestPasswordBulkLookup) {
    872   PasswordFormData db_data[] = {
    873     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    874       "http://some.domain.com/", "http://some.domain.com/action.cgi",
    875       L"submit", L"username", L"password", L"joe_user", L"",
    876       true, false, 1212121212 },
    877     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    878       "http://some.domain.com/page.html",
    879       "http://some.domain.com/handlepage.cgi",
    880       L"submit", L"username", L"password", L"joe_user", L"",
    881       true, false, 1234567890 },
    882     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    883       "http://some.domain.com/page.html",
    884       "http://some.domain.com/handlepage.cgi",
    885       L"submit", L"username", L"password", L"second-account", L"",
    886       true, false, 1240000000 },
    887     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
    888       "http://dont.remember.com/",
    889       "http://dont.remember.com/handlepage.cgi",
    890       L"submit", L"username", L"password", L"joe_user", L"",
    891       true, false, 1240000000 },
    892     { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
    893       "http://some.domain.com/path.html", "http://some.domain.com/action.cgi",
    894       L"submit", L"username", L"password", NULL, NULL,
    895       true, false, 1212121212 },
    896   };
    897   std::vector<PasswordForm*> database_forms;
    898   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(db_data); ++i) {
    899     database_forms.push_back(CreatePasswordFormFromData(db_data[i]));
    900   }
    901   std::vector<PasswordForm*> merged_forms =
    902       internal_keychain_helpers::GetPasswordsForForms(*keychain_,
    903                                                       &database_forms);
    904   EXPECT_EQ(2U, database_forms.size());
    905   ASSERT_EQ(3U, merged_forms.size());
    906   EXPECT_EQ(ASCIIToUTF16("sekrit"), merged_forms[0]->password_value);
    907   EXPECT_EQ(ASCIIToUTF16("sekrit"), merged_forms[1]->password_value);
    908   EXPECT_TRUE(merged_forms[2]->blacklisted_by_user);
    909 
    910   STLDeleteElements(&database_forms);
    911   STLDeleteElements(&merged_forms);
    912 }
    913 
    914 TEST_F(PasswordStoreMacInternalsTest, TestBlacklistedFiltering) {
    915   PasswordFormData db_data[] = {
    916     { PasswordForm::SCHEME_HTML, "http://dont.remember.com/",
    917       "http://dont.remember.com/",
    918       "http://dont.remember.com/handlepage.cgi",
    919       L"submit", L"username", L"password", L"joe_user", L"non_empty_password",
    920       true, false, 1240000000 },
    921     { PasswordForm::SCHEME_HTML, "https://dont.remember.com/",
    922       "https://dont.remember.com/",
    923       "https://dont.remember.com/handlepage_secure.cgi",
    924       L"submit", L"username", L"password", L"joe_user", L"non_empty_password",
    925       true, false, 1240000000 },
    926   };
    927   std::vector<PasswordForm*> database_forms;
    928   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(db_data); ++i) {
    929     database_forms.push_back(CreatePasswordFormFromData(db_data[i]));
    930   }
    931   std::vector<PasswordForm*> merged_forms =
    932       internal_keychain_helpers::GetPasswordsForForms(*keychain_,
    933                                                       &database_forms);
    934   EXPECT_EQ(2U, database_forms.size());
    935   ASSERT_EQ(0U, merged_forms.size());
    936 
    937   STLDeleteElements(&database_forms);
    938   STLDeleteElements(&merged_forms);
    939 }
    940 
    941 TEST_F(PasswordStoreMacInternalsTest, TestFillPasswordFormFromKeychainItem) {
    942   // When |extract_password_data| is false, the password field must be empty,
    943   // and |blacklisted_by_user| must be false.
    944   SecKeychainItemRef keychain_item = reinterpret_cast<SecKeychainItemRef>(1);
    945   PasswordForm form_without_extracted_password;
    946   bool parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
    947       *keychain_,
    948       keychain_item,
    949       &form_without_extracted_password,
    950       false);  // Do not extract password.
    951   EXPECT_TRUE(parsed);
    952   ASSERT_TRUE(form_without_extracted_password.password_value.empty());
    953   ASSERT_FALSE(form_without_extracted_password.blacklisted_by_user);
    954 
    955   // When |extract_password_data| is true and the keychain entry has a non-empty
    956   // password, the password field must be non-empty, and the value of
    957   // |blacklisted_by_user| must be false.
    958   keychain_item = reinterpret_cast<SecKeychainItemRef>(1);
    959   PasswordForm form_with_extracted_password;
    960   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
    961       *keychain_,
    962       keychain_item,
    963       &form_with_extracted_password,
    964       true);  // Extract password.
    965   EXPECT_TRUE(parsed);
    966   ASSERT_EQ(ASCIIToUTF16("sekrit"),
    967             form_with_extracted_password.password_value);
    968   ASSERT_FALSE(form_with_extracted_password.blacklisted_by_user);
    969 
    970   // When |extract_password_data| is true and the keychain entry has an empty
    971   // username and password (""), the password field must be empty, and the value
    972   // of |blacklisted_by_user| must be true.
    973   keychain_item = reinterpret_cast<SecKeychainItemRef>(4);
    974   PasswordForm negative_form;
    975   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
    976       *keychain_,
    977       keychain_item,
    978       &negative_form,
    979       true);  // Extract password.
    980   EXPECT_TRUE(parsed);
    981   ASSERT_TRUE(negative_form.username_value.empty());
    982   ASSERT_TRUE(negative_form.password_value.empty());
    983   ASSERT_TRUE(negative_form.blacklisted_by_user);
    984 
    985   // When |extract_password_data| is true and the keychain entry has an empty
    986   // password (""), the password field must be empty (""), and the value of
    987   // |blacklisted_by_user| must be true.
    988   keychain_item = reinterpret_cast<SecKeychainItemRef>(5);
    989   PasswordForm form_with_empty_password_a;
    990   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
    991       *keychain_,
    992       keychain_item,
    993       &form_with_empty_password_a,
    994       true);  // Extract password.
    995   EXPECT_TRUE(parsed);
    996   ASSERT_TRUE(form_with_empty_password_a.password_value.empty());
    997   ASSERT_TRUE(form_with_empty_password_a.blacklisted_by_user);
    998 
    999   // When |extract_password_data| is true and the keychain entry has a single
   1000   // space password (" "), the password field must be a single space (" "), and
   1001   // the value of |blacklisted_by_user| must be true.
   1002   keychain_item = reinterpret_cast<SecKeychainItemRef>(6);
   1003   PasswordForm form_with_empty_password_b;
   1004   parsed = internal_keychain_helpers::FillPasswordFormFromKeychainItem(
   1005       *keychain_,
   1006       keychain_item,
   1007       &form_with_empty_password_b,
   1008       true);  // Extract password.
   1009   EXPECT_TRUE(parsed);
   1010   ASSERT_EQ(ASCIIToUTF16(" "),
   1011             form_with_empty_password_b.password_value);
   1012   ASSERT_TRUE(form_with_empty_password_b.blacklisted_by_user);
   1013 }
   1014 
   1015 TEST_F(PasswordStoreMacInternalsTest, TestPasswordGetAll) {
   1016   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
   1017   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
   1018   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
   1019 
   1020   // Add a few passwords of various types so that we own some.
   1021   PasswordFormData owned_password_data[] = {
   1022     { PasswordForm::SCHEME_HTML, "http://web.site.com/",
   1023       "http://web.site.com/path/to/page.html", NULL, NULL, NULL, NULL,
   1024       L"anonymous", L"knock-knock", false, false, 0 },
   1025     { PasswordForm::SCHEME_BASIC, "http://a.site.com:2222/therealm",
   1026       "http://a.site.com:2222/", NULL, NULL, NULL, NULL,
   1027       L"username", L"password", false, false, 0 },
   1028     { PasswordForm::SCHEME_DIGEST, "https://digest.site.com/differentrealm",
   1029       "https://digest.site.com/secure.html", NULL, NULL, NULL, NULL,
   1030       L"testname", L"testpass", false, false, 0 },
   1031   };
   1032   for (unsigned int i = 0; i < arraysize(owned_password_data); ++i) {
   1033     scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
   1034         owned_password_data[i]));
   1035     owned_keychain_adapter.AddPassword(*form);
   1036   }
   1037 
   1038   std::vector<PasswordForm*> all_passwords =
   1039       keychain_adapter.GetAllPasswordFormPasswords();
   1040   EXPECT_EQ(8 + arraysize(owned_password_data), all_passwords.size());
   1041   STLDeleteElements(&all_passwords);
   1042 
   1043   std::vector<PasswordForm*> owned_passwords =
   1044       owned_keychain_adapter.GetAllPasswordFormPasswords();
   1045   EXPECT_EQ(arraysize(owned_password_data), owned_passwords.size());
   1046   STLDeleteElements(&owned_passwords);
   1047 }
   1048 
   1049 #pragma mark -
   1050 
   1051 class PasswordStoreMacTest : public testing::Test {
   1052  public:
   1053   PasswordStoreMacTest() : ui_thread_(BrowserThread::UI, &message_loop_) {}
   1054 
   1055   virtual void SetUp() {
   1056     login_db_ = new LoginDatabase();
   1057     ASSERT_TRUE(db_dir_.CreateUniqueTempDir());
   1058     base::FilePath db_file = db_dir_.path().AppendASCII("login.db");
   1059     ASSERT_TRUE(login_db_->Init(db_file));
   1060 
   1061     keychain_ = new MockAppleKeychain();
   1062 
   1063     store_ = new TestPasswordStoreMac(
   1064         base::MessageLoopProxy::current(),
   1065         base::MessageLoopProxy::current(),
   1066         keychain_,
   1067         login_db_);
   1068     ASSERT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare(), ""));
   1069   }
   1070 
   1071   virtual void TearDown() {
   1072     store_->Shutdown();
   1073     EXPECT_FALSE(store_->GetBackgroundTaskRunner().get());
   1074   }
   1075 
   1076   void WaitForStoreUpdate() {
   1077     // Do a store-level query to wait for all the operations above to be done.
   1078     MockPasswordStoreConsumer consumer;
   1079     EXPECT_CALL(consumer, OnGetPasswordStoreResults(_))
   1080         .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
   1081     store_->GetLogins(PasswordForm(), PasswordStore::ALLOW_PROMPT, &consumer);
   1082     base::MessageLoop::current()->Run();
   1083   }
   1084 
   1085   TestPasswordStoreMac* store() { return store_.get(); }
   1086 
   1087   MockAppleKeychain* keychain() { return keychain_; }
   1088 
   1089  protected:
   1090   base::MessageLoopForUI message_loop_;
   1091   content::TestBrowserThread ui_thread_;
   1092 
   1093   MockAppleKeychain* keychain_;  // Owned by store_.
   1094   LoginDatabase* login_db_;  // Owned by store_.
   1095   scoped_refptr<TestPasswordStoreMac> store_;
   1096   base::ScopedTempDir db_dir_;
   1097 };
   1098 
   1099 TEST_F(PasswordStoreMacTest, TestStoreUpdate) {
   1100   // Insert a password into both the database and the keychain.
   1101   // This is done manually, rather than through store_->AddLogin, because the
   1102   // Mock Keychain isn't smart enough to be able to support update generically,
   1103   // so some.domain.com triggers special handling to test it that make inserting
   1104   // fail.
   1105   PasswordFormData joint_data = {
   1106     PasswordForm::SCHEME_HTML, "http://some.domain.com/",
   1107     "http://some.domain.com/insecure.html", "login.cgi",
   1108     L"username", L"password", L"submit", L"joe_user", L"sekrit", true, false, 1
   1109   };
   1110   scoped_ptr<PasswordForm> joint_form(CreatePasswordFormFromData(joint_data));
   1111   login_db_->AddLogin(*joint_form);
   1112   MockAppleKeychain::KeychainTestData joint_keychain_data = {
   1113     kSecAuthenticationTypeHTMLForm, "some.domain.com",
   1114     kSecProtocolTypeHTTP, "/insecure.html", 0, NULL, "20020601171500Z",
   1115     "joe_user", "sekrit", false };
   1116   keychain_->AddTestItem(joint_keychain_data);
   1117 
   1118   // Insert a password into the keychain only.
   1119   MockAppleKeychain::KeychainTestData keychain_only_data = {
   1120     kSecAuthenticationTypeHTMLForm, "keychain.only.com",
   1121     kSecProtocolTypeHTTP, NULL, 0, NULL, "20020601171500Z",
   1122     "keychain", "only", false
   1123   };
   1124   keychain_->AddTestItem(keychain_only_data);
   1125 
   1126   struct UpdateData {
   1127     PasswordFormData form_data;
   1128     const char* password;  // NULL indicates no entry should be present.
   1129   };
   1130 
   1131   // Make a series of update calls.
   1132   UpdateData updates[] = {
   1133     // Update the keychain+db passwords (the normal password update case).
   1134     { { PasswordForm::SCHEME_HTML, "http://some.domain.com/",
   1135         "http://some.domain.com/insecure.html", "login.cgi",
   1136         L"username", L"password", L"submit", L"joe_user", L"53krit",
   1137         true, false, 2 },
   1138       "53krit",
   1139     },
   1140     // Update the keychain-only password; this simulates the initial use of a
   1141     // password stored by another browsers.
   1142     { { PasswordForm::SCHEME_HTML, "http://keychain.only.com/",
   1143         "http://keychain.only.com/login.html", "login.cgi",
   1144         L"username", L"password", L"submit", L"keychain", L"only",
   1145         true, false, 2 },
   1146       "only",
   1147     },
   1148     // Update a password that doesn't exist in either location. This tests the
   1149     // case where a form is filled, then the stored login is removed, then the
   1150     // form is submitted.
   1151     { { PasswordForm::SCHEME_HTML, "http://different.com/",
   1152         "http://different.com/index.html", "login.cgi",
   1153         L"username", L"password", L"submit", L"abc", L"123",
   1154         true, false, 2 },
   1155       NULL,
   1156     },
   1157   };
   1158   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(updates); ++i) {
   1159     scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(
   1160         updates[i].form_data));
   1161     store_->UpdateLogin(*form);
   1162   }
   1163 
   1164   WaitForStoreUpdate();
   1165 
   1166   MacKeychainPasswordFormAdapter keychain_adapter(keychain_);
   1167   for (unsigned int i = 0; i < ARRAYSIZE_UNSAFE(updates); ++i) {
   1168     scoped_ptr<PasswordForm> query_form(
   1169         CreatePasswordFormFromData(updates[i].form_data));
   1170 
   1171     std::vector<PasswordForm*> matching_items =
   1172         keychain_adapter.PasswordsFillingForm(query_form->signon_realm,
   1173                                               query_form->scheme);
   1174     if (updates[i].password) {
   1175       EXPECT_GT(matching_items.size(), 0U) << "iteration " << i;
   1176       if (matching_items.size() >= 1)
   1177         EXPECT_EQ(ASCIIToUTF16(updates[i].password),
   1178                   matching_items[0]->password_value) << "iteration " << i;
   1179     } else {
   1180       EXPECT_EQ(0U, matching_items.size()) << "iteration " << i;
   1181     }
   1182     STLDeleteElements(&matching_items);
   1183 
   1184     login_db_->GetLogins(*query_form, &matching_items);
   1185     EXPECT_EQ(updates[i].password ? 1U : 0U, matching_items.size())
   1186         << "iteration " << i;
   1187     STLDeleteElements(&matching_items);
   1188   }
   1189 }
   1190 
   1191 TEST_F(PasswordStoreMacTest, TestDBKeychainAssociation) {
   1192   // Tests that association between the keychain and login database parts of a
   1193   // password added by fuzzy (PSL) matching works.
   1194   // 1. Add a password for www.facebook.com
   1195   // 2. Get a password for m.facebook.com. This fuzzy matches and returns the
   1196   //    www.facebook.com password.
   1197   // 3. Add the returned password for m.facebook.com.
   1198   // 4. Remove both passwords.
   1199   //    -> check: that both are gone from the login DB and the keychain
   1200   // This test should in particular ensure that we don't keep passwords in the
   1201   // keychain just before we think we still have other (fuzzy-)matching entries
   1202   // for them in the login database. (For example, here if we deleted the
   1203   // www.facebook.com password from the login database, we should not be blocked
   1204   // from deleting it from the keystore just becaus the m.facebook.com password
   1205   // fuzzy-matches the www.facebook.com one.)
   1206 
   1207   // 1. Add a password for www.facebook.com
   1208   PasswordFormData www_form_data = {
   1209     PasswordForm::SCHEME_HTML, "http://www.facebook.com/",
   1210     "http://www.facebook.com/index.html", "login",
   1211     L"username", L"password", L"submit", L"joe_user", L"sekrit", true, false, 1
   1212   };
   1213   scoped_ptr<PasswordForm> www_form(CreatePasswordFormFromData(www_form_data));
   1214   login_db_->AddLogin(*www_form);
   1215   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
   1216   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
   1217   owned_keychain_adapter.AddPassword(*www_form);
   1218 
   1219   // 2. Get a password for m.facebook.com.
   1220   PasswordForm m_form(*www_form);
   1221   m_form.signon_realm = "http://m.facebook.com";
   1222   m_form.origin = GURL("http://m.facebook.com/index.html");
   1223   MockPasswordStoreConsumer consumer;
   1224   EXPECT_CALL(consumer, OnGetPasswordStoreResults(_)).WillOnce(DoAll(
   1225       WithArg<0>(Invoke(&consumer, &MockPasswordStoreConsumer::CopyElements)),
   1226       WithArg<0>(STLDeleteElements0()),
   1227       QuitUIMessageLoop()));
   1228   store_->GetLogins(m_form, PasswordStore::ALLOW_PROMPT, &consumer);
   1229   base::MessageLoop::current()->Run();
   1230   EXPECT_EQ(1u, consumer.last_result.size());
   1231 
   1232   // 3. Add the returned password for m.facebook.com.
   1233   login_db_->AddLogin(consumer.last_result[0]);
   1234   owned_keychain_adapter.AddPassword(m_form);
   1235 
   1236   // 4. Remove both passwords.
   1237   store_->RemoveLogin(*www_form);
   1238   store_->RemoveLogin(m_form);
   1239   WaitForStoreUpdate();
   1240 
   1241   std::vector<PasswordForm*> matching_items;
   1242   // No trace of www.facebook.com.
   1243   matching_items = owned_keychain_adapter.PasswordsFillingForm(
   1244       www_form->signon_realm, www_form->scheme);
   1245   EXPECT_EQ(0u, matching_items.size());
   1246   login_db_->GetLogins(*www_form, &matching_items);
   1247   EXPECT_EQ(0u, matching_items.size());
   1248   // No trace of m.facebook.com.
   1249   matching_items = owned_keychain_adapter.PasswordsFillingForm(
   1250       m_form.signon_realm, m_form.scheme);
   1251   EXPECT_EQ(0u, matching_items.size());
   1252   login_db_->GetLogins(m_form, &matching_items);
   1253   EXPECT_EQ(0u, matching_items.size());
   1254 }
   1255 
   1256 namespace {
   1257 
   1258 class PasswordsChangeObserver :
   1259     public password_manager::PasswordStore::Observer {
   1260 public:
   1261   PasswordsChangeObserver(TestPasswordStoreMac* store) : observer_(this) {
   1262     observer_.Add(store);
   1263   }
   1264 
   1265   void WaitAndVerify(PasswordStoreMacTest* test) {
   1266     test->WaitForStoreUpdate();
   1267     ::testing::Mock::VerifyAndClearExpectations(this);
   1268   }
   1269 
   1270   // password_manager::PasswordStore::Observer:
   1271   MOCK_METHOD1(OnLoginsChanged,
   1272                void(const password_manager::PasswordStoreChangeList& changes));
   1273 
   1274 private:
   1275   ScopedObserver<password_manager::PasswordStore,
   1276                 PasswordsChangeObserver> observer_;
   1277 };
   1278 
   1279 password_manager::PasswordStoreChangeList GetAddChangeList(
   1280     const PasswordForm& form) {
   1281   password_manager::PasswordStoreChange change(
   1282       password_manager::PasswordStoreChange::ADD, form);
   1283   return password_manager::PasswordStoreChangeList(1, change);
   1284 }
   1285 
   1286 // Tests RemoveLoginsCreatedBetween or RemoveLoginsSyncedBetween depending on
   1287 // |check_created|.
   1288 void CheckRemoveLoginsBetween(PasswordStoreMacTest* test, bool check_created) {
   1289   PasswordFormData www_form_data_facebook = {
   1290       PasswordForm::SCHEME_HTML, "http://www.facebook.com/",
   1291       "http://www.facebook.com/index.html", "login", L"submit", L"username",
   1292       L"password", L"joe_user", L"sekrit", true, false, 0 };
   1293   // The old form doesn't have elements names.
   1294   PasswordFormData www_form_data_facebook_old = {
   1295       PasswordForm::SCHEME_HTML, "http://www.facebook.com/",
   1296       "http://www.facebook.com/index.html", "login", L"", L"",
   1297       L"", L"joe_user", L"oldsekrit", true, false, 0 };
   1298   PasswordFormData www_form_data_other = {
   1299       PasswordForm::SCHEME_HTML, "http://different.com/",
   1300       "http://different.com/index.html", "login", L"submit", L"username",
   1301       L"password", L"different_joe_user", L"sekrit", true, false, 0 };
   1302   scoped_ptr<PasswordForm> form_facebook(
   1303       CreatePasswordFormFromData(www_form_data_facebook));
   1304   scoped_ptr<PasswordForm> form_facebook_old(
   1305       CreatePasswordFormFromData(www_form_data_facebook_old));
   1306   scoped_ptr<PasswordForm> form_other(
   1307       CreatePasswordFormFromData(www_form_data_other));
   1308   base::Time now = base::Time::Now();
   1309   // TODO(vasilii): remove the next line once crbug/374132 is fixed.
   1310   now = base::Time::FromTimeT(now.ToTimeT());
   1311   base::Time next_day = now + base::TimeDelta::FromDays(1);
   1312   if (check_created) {
   1313     form_facebook_old->date_created = now;
   1314     form_facebook->date_created = next_day;
   1315     form_other->date_created = next_day;
   1316   } else {
   1317     form_facebook_old->date_synced = now;
   1318     form_facebook->date_synced = next_day;
   1319     form_other->date_synced = next_day;
   1320   }
   1321 
   1322   PasswordsChangeObserver observer(test->store());
   1323   test->store()->AddLogin(*form_facebook_old);
   1324   test->store()->AddLogin(*form_facebook);
   1325   test->store()->AddLogin(*form_other);
   1326   EXPECT_CALL(observer, OnLoginsChanged(GetAddChangeList(*form_facebook_old)));
   1327   EXPECT_CALL(observer, OnLoginsChanged(GetAddChangeList(*form_facebook)));
   1328   EXPECT_CALL(observer, OnLoginsChanged(GetAddChangeList(*form_other)));
   1329   observer.WaitAndVerify(test);
   1330 
   1331   // Check the keychain content.
   1332   MacKeychainPasswordFormAdapter owned_keychain_adapter(test->keychain());
   1333   owned_keychain_adapter.SetFindsOnlyOwnedItems(false);
   1334   ScopedVector<PasswordForm> matching_items;
   1335   matching_items.get() = owned_keychain_adapter.PasswordsFillingForm(
   1336       form_facebook->signon_realm, form_facebook->scheme);
   1337   EXPECT_EQ(1u, matching_items.size());
   1338   matching_items.clear();
   1339   matching_items.get() = owned_keychain_adapter.PasswordsFillingForm(
   1340       form_other->signon_realm, form_other->scheme);
   1341   EXPECT_EQ(1u, matching_items.size());
   1342   matching_items.clear();
   1343 
   1344   // Remove facebook.
   1345   void (PasswordStore::*method)(base::Time, base::Time) =
   1346       check_created ? &PasswordStore::RemoveLoginsCreatedBetween
   1347                     : &PasswordStore::RemoveLoginsSyncedBetween;
   1348   (test->store()->*method)(base::Time(), next_day);
   1349   password_manager::PasswordStoreChangeList list;
   1350   form_facebook_old->password_value.clear();
   1351   form_facebook->password_value.clear();
   1352   list.push_back(password_manager::PasswordStoreChange(
   1353       password_manager::PasswordStoreChange::REMOVE, *form_facebook_old));
   1354   list.push_back(password_manager::PasswordStoreChange(
   1355       password_manager::PasswordStoreChange::REMOVE, *form_facebook));
   1356   EXPECT_CALL(observer, OnLoginsChanged(list));
   1357   list.clear();
   1358   observer.WaitAndVerify(test);
   1359 
   1360   matching_items.get() = owned_keychain_adapter.PasswordsFillingForm(
   1361       form_facebook->signon_realm, form_facebook->scheme);
   1362   EXPECT_EQ(0u, matching_items.size());
   1363   matching_items.get() = owned_keychain_adapter.PasswordsFillingForm(
   1364       form_other->signon_realm, form_other->scheme);
   1365   EXPECT_EQ(1u, matching_items.size());
   1366   matching_items.clear();
   1367 
   1368   // Remove form_other.
   1369   (test->store()->*method)(next_day, base::Time());
   1370   form_other->password_value.clear();
   1371   list.push_back(password_manager::PasswordStoreChange(
   1372       password_manager::PasswordStoreChange::REMOVE, *form_other));
   1373   EXPECT_CALL(observer, OnLoginsChanged(list));
   1374   observer.WaitAndVerify(test);
   1375   matching_items.get() = owned_keychain_adapter.PasswordsFillingForm(
   1376       form_other->signon_realm, form_other->scheme);
   1377   EXPECT_EQ(0u, matching_items.size());
   1378 }
   1379 
   1380 }  // namespace
   1381 
   1382 TEST_F(PasswordStoreMacTest, TestRemoveLoginsCreatedBetween) {
   1383   CheckRemoveLoginsBetween(this, true);
   1384 }
   1385 
   1386 TEST_F(PasswordStoreMacTest, TestRemoveLoginsSyncedBetween) {
   1387   CheckRemoveLoginsBetween(this, false);
   1388 }
   1389 
   1390 TEST_F(PasswordStoreMacTest, TestRemoveLoginsMultiProfile) {
   1391   // Make sure that RemoveLoginsCreatedBetween does affect only the correct
   1392   // profile.
   1393 
   1394   // Add a third-party password.
   1395   MockAppleKeychain::KeychainTestData keychain_data = {
   1396       kSecAuthenticationTypeHTMLForm, "some.domain.com",
   1397       kSecProtocolTypeHTTP, "/insecure.html", 0, NULL, "20020601171500Z",
   1398       "joe_user", "sekrit", false };
   1399   keychain_->AddTestItem(keychain_data);
   1400 
   1401   // Add a password through the adapter. It has the "Chrome" creator tag.
   1402   // However, it's not referenced by the password database.
   1403   MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_);
   1404   owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
   1405   PasswordFormData www_form_data1 = {
   1406       PasswordForm::SCHEME_HTML, "http://www.facebook.com/",
   1407       "http://www.facebook.com/index.html", "login", L"username", L"password",
   1408       L"submit", L"joe_user", L"sekrit", true, false, 1 };
   1409   scoped_ptr<PasswordForm> www_form(CreatePasswordFormFromData(www_form_data1));
   1410   EXPECT_TRUE(owned_keychain_adapter.AddPassword(*www_form));
   1411 
   1412   // Add a password from the current profile.
   1413   PasswordFormData www_form_data2 = {
   1414       PasswordForm::SCHEME_HTML, "http://www.facebook.com/",
   1415       "http://www.facebook.com/index.html", "login", L"username", L"password",
   1416       L"submit", L"not_joe_user", L"12345", true, false, 1 };
   1417   www_form.reset(CreatePasswordFormFromData(www_form_data2));
   1418   store_->AddLogin(*www_form);
   1419   WaitForStoreUpdate();
   1420 
   1421   ScopedVector<PasswordForm> matching_items;
   1422   login_db_->GetLogins(*www_form, &matching_items.get());
   1423   EXPECT_EQ(1u, matching_items.size());
   1424   matching_items.clear();
   1425 
   1426   store_->RemoveLoginsCreatedBetween(base::Time(), base::Time());
   1427   WaitForStoreUpdate();
   1428 
   1429   // Check the second facebook form is gone.
   1430   login_db_->GetLogins(*www_form, &matching_items.get());
   1431   EXPECT_EQ(0u, matching_items.size());
   1432 
   1433   // Check the first facebook form is still there.
   1434   matching_items.get() = owned_keychain_adapter.PasswordsFillingForm(
   1435       www_form->signon_realm, www_form->scheme);
   1436   ASSERT_EQ(1u, matching_items.size());
   1437   EXPECT_EQ(ASCIIToUTF16("joe_user"), matching_items[0]->username_value);
   1438   matching_items.clear();
   1439 
   1440   // Check the third-party password is still there.
   1441   owned_keychain_adapter.SetFindsOnlyOwnedItems(false);
   1442   matching_items.get() = owned_keychain_adapter.PasswordsFillingForm(
   1443       "http://some.domain.com/insecure.html", PasswordForm::SCHEME_HTML);
   1444   ASSERT_EQ(1u, matching_items.size());
   1445 }
   1446