Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: chrome/browser/password_manager/password_store_mac.cc

Issue 149160: Add an exact search method to the Keychain adapter, and modify unit tests to ... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | chrome/browser/password_manager/password_store_mac_internal.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/password_manager/password_store_mac.h" 5 #include "chrome/browser/password_manager/password_store_mac.h"
6 #include "chrome/browser/password_manager/password_store_mac_internal.h" 6 #include "chrome/browser/password_manager/password_store_mac_internal.h"
7 7
8 #include <CoreServices/CoreServices.h> 8 #include <CoreServices/CoreServices.h>
9 #include <string> 9 #include <string>
10 #include <vector> 10 #include <vector>
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
136 OSStatus result = keychain_->SearchCreateFromAttributes( 136 OSStatus result = keychain_->SearchCreateFromAttributes(
137 NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_); 137 NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_);
138 138
139 if (result != noErr) { 139 if (result != noErr) {
140 LOG(ERROR) << "Keychain lookup failed with error " << result; 140 LOG(ERROR) << "Keychain lookup failed with error " << result;
141 return; 141 return;
142 } 142 }
143 143
144 SecKeychainItemRef keychain_item; 144 SecKeychainItemRef keychain_item;
145 while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) { 145 while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) {
146 // Consumer is responsible for deleting the forms when they are done. 146 // Consumer is responsible for freeing the items.
147 items->push_back(keychain_item); 147 items->push_back(keychain_item);
148 } 148 }
149 149
150 keychain_->Free(search_ref_); 150 keychain_->Free(search_ref_);
151 search_ref_ = NULL; 151 search_ref_ = NULL;
152 } 152 }
153 153
154 #pragma mark - 154 #pragma mark -
155 155
156 // TODO(stuartmorgan): Convert most of this to private helpers in 156 // TODO(stuartmorgan): Convert most of this to private helpers in
157 // MacKeychainPaswordFormAdapter once it has sufficient higher-level public 157 // MacKeychainPaswordFormAdapter once it has sufficient higher-level public
158 // methods to provide test coverage. 158 // methods to provide test coverage.
159 namespace internal_keychain_helpers { 159 namespace internal_keychain_helpers {
160 160
161 // Takes a PasswordForm's signon_realm and parses it into its component parts,
162 // which are returned though the appropriate out parameters.
163 // Returns true if it can be successfully parsed, in which case all out params
164 // that are non-NULL will be set. If there is no port, port will be 0.
165 // If the return value is false, the state of the our params is undefined.
166 //
167 // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
168 bool ExtractSignonRealmComponents(const std::string& signon_realm,
169 std::string* server, int* port,
170 bool* is_secure,
171 std::string* security_domain) {
172 // The signon_realm will be the Origin portion of a URL for an HTML form,
173 // and the same but with the security domain as a path for HTTP auth.
174 GURL realm_as_url(signon_realm);
175 if (!realm_as_url.is_valid()) {
176 return false;
177 }
178
179 if (server)
180 *server = realm_as_url.host();
181 if (is_secure)
182 *is_secure = realm_as_url.SchemeIsSecure();
183 if (port)
184 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0;
185 if (security_domain) {
186 // Strip the leading '/' off of the path to get the security domain.
187 *security_domain = realm_as_url.path().substr(1);
188 }
189 return true;
190 }
191
192 // Returns a URL built from the given components. To create a URL without a 161 // Returns a URL built from the given components. To create a URL without a
193 // port, pass kAnyPort for the |port| parameter. 162 // port, pass kAnyPort for the |port| parameter.
194 GURL URLFromComponents(bool is_secure, const std::string& host, int port, 163 GURL URLFromComponents(bool is_secure, const std::string& host, int port,
195 const std::string& path) { 164 const std::string& path) {
196 GURL::Replacements url_components; 165 GURL::Replacements url_components;
197 std::string scheme(is_secure ? "https" : "http"); 166 std::string scheme(is_secure ? "https" : "http");
198 url_components.SetSchemeStr(scheme); 167 url_components.SetSchemeStr(scheme);
199 url_components.SetHostStr(host); 168 url_components.SetHostStr(host);
200 std::string port_string; // Must remain in scope until after we do replacing. 169 std::string port_string; // Must remain in scope until after we do replacing.
201 if (port != kAnyPort) { 170 if (port != kAnyPort) {
(...skipping 28 matching lines...) Expand all
230 &exploded_time.minute, &exploded_time.second); 199 &exploded_time.minute, &exploded_time.second);
231 free(time_string); 200 free(time_string);
232 201
233 if (assignments == 6) { 202 if (assignments == 6) {
234 *time = base::Time::FromUTCExploded(exploded_time); 203 *time = base::Time::FromUTCExploded(exploded_time);
235 return true; 204 return true;
236 } 205 }
237 return false; 206 return false;
238 } 207 }
239 208
240 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
241 SecAuthenticationType AuthTypeForScheme(PasswordForm::Scheme scheme) {
242 switch (scheme) {
243 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm;
244 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic;
245 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest;
246 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault;
247 }
248 NOTREACHED();
249 return kSecAuthenticationTypeDefault;
250 }
251
252 // Returns the PasswordForm Scheme corresponding to |auth_type|. 209 // Returns the PasswordForm Scheme corresponding to |auth_type|.
253 PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) { 210 PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) {
254 switch (auth_type) { 211 switch (auth_type) {
255 case kSecAuthenticationTypeHTMLForm: return PasswordForm::SCHEME_HTML; 212 case kSecAuthenticationTypeHTMLForm: return PasswordForm::SCHEME_HTML;
256 case kSecAuthenticationTypeHTTPBasic: return PasswordForm::SCHEME_BASIC; 213 case kSecAuthenticationTypeHTTPBasic: return PasswordForm::SCHEME_BASIC;
257 case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST; 214 case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST;
258 default: return PasswordForm::SCHEME_OTHER; 215 default: return PasswordForm::SCHEME_OTHER;
259 } 216 }
260 } 217 }
261 218
262 SecKeychainItemRef MatchingKeychainItem(const MacKeychain& keychain,
263 const PasswordForm& form) {
264 // We don't store blacklist entries in the keychain, so the answer to "what
265 // Keychain item goes with this form" is always "nothing" for blacklists.
266 if (form.blacklisted_by_user) {
267 return NULL;
268 }
269
270 // Construct a keychain search based on all the unique attributes.
271 std::string server;
272 std::string security_domain;
273 int port;
274 bool is_secure;
275 if (!ExtractSignonRealmComponents(form.signon_realm, &server, &port,
276 &is_secure, &security_domain)) {
277 // TODO(stuartmorgan): Proxies will currently fail here, since their
278 // signon_realm is not a URL. We need to detect the proxy case and handle
279 // it specially.
280 return NULL;
281 }
282
283 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
284 : kSecProtocolTypeHTTP;
285 SecAuthenticationType auth_type = AuthTypeForScheme(form.scheme);
286 std::string path = form.origin.path();
287 std::string username = WideToUTF8(form.username_value);
288
289 KeychainSearch keychain_search(keychain);
290 keychain_search.Init(server.c_str(), port, protocol, auth_type,
291 (form.scheme == PasswordForm::SCHEME_HTML) ?
292 NULL : security_domain.c_str(),
293 path.c_str(), username.c_str());
294
295 std::vector<SecKeychainItemRef> matches;
296 keychain_search.FindMatchingItems(&matches);
297
298 if (matches.size() == 0) {
299 return NULL;
300 }
301 // Free all items after the first, since we won't be returning them.
302 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1;
303 i != matches.end(); ++i) {
304 keychain.Free(*i);
305 }
306 return matches[0];
307 }
308
309 bool FillPasswordFormFromKeychainItem(const MacKeychain& keychain, 219 bool FillPasswordFormFromKeychainItem(const MacKeychain& keychain,
310 const SecKeychainItemRef& keychain_item, 220 const SecKeychainItemRef& keychain_item,
311 PasswordForm* form) { 221 PasswordForm* form) {
312 DCHECK(form); 222 DCHECK(form);
313 223
314 SecKeychainAttributeInfo attrInfo; 224 SecKeychainAttributeInfo attrInfo;
315 UInt32 tags[] = { kSecAccountItemAttr, 225 UInt32 tags[] = { kSecAccountItemAttr,
316 kSecServerItemAttr, 226 kSecServerItemAttr,
317 kSecPortItemAttr, 227 kSecPortItemAttr,
318 kSecPathItemAttr, 228 kSecPathItemAttr,
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
503 } 413 }
504 414
505 } // namespace internal_keychain_helpers 415 } // namespace internal_keychain_helpers
506 416
507 #pragma mark - 417 #pragma mark -
508 418
509 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter( 419 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
510 MacKeychain* keychain) : keychain_(keychain) { 420 MacKeychain* keychain) : keychain_(keychain) {
511 } 421 }
512 422
513 // Returns PasswordForms for each keychain entry matching |form|.
514 // Caller is responsible for deleting the returned forms.
515 std::vector<PasswordForm*> 423 std::vector<PasswordForm*>
516 MacKeychainPasswordFormAdapter::PasswordsMatchingForm( 424 MacKeychainPasswordFormAdapter::PasswordsMatchingForm(
517 const PasswordForm& query_form) { 425 const PasswordForm& query_form) {
518 std::vector<SecKeychainItemRef> keychain_items = 426 std::vector<SecKeychainItemRef> keychain_items =
519 MatchingKeychainItems(query_form.signon_realm, query_form.scheme); 427 KeychainItemsForFillingForm(query_form);
520 428
521 std::vector<PasswordForm*> keychain_forms = 429 std::vector<PasswordForm*> keychain_forms =
522 CreateFormsFromKeychainItems(keychain_items); 430 CreateFormsFromKeychainItems(keychain_items);
523 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin(); 431 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
524 i != keychain_items.end(); ++i) { 432 i != keychain_items.end(); ++i) {
525 keychain_->Free(*i); 433 keychain_->Free(*i);
526 } 434 }
527 return keychain_forms; 435 return keychain_forms;
528 } 436 }
529 437
438 PasswordForm* MacKeychainPasswordFormAdapter::PasswordExactlyMatchingForm(
439 const PasswordForm& query_form) {
440 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form);
441 if (keychain_item) {
442 PasswordForm* form = new PasswordForm();
443 internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
444 keychain_item,
445 form);
446 keychain_->Free(keychain_item);
447 return form;
448 }
449 return NULL;
450 }
451
530 bool MacKeychainPasswordFormAdapter::AddLogin(const PasswordForm& form) { 452 bool MacKeychainPasswordFormAdapter::AddLogin(const PasswordForm& form) {
531 std::string server; 453 std::string server;
532 std::string security_domain; 454 std::string security_domain;
533 int port; 455 int port;
534 bool is_secure; 456 bool is_secure;
535 if (!internal_keychain_helpers::ExtractSignonRealmComponents( 457 if (!ExtractSignonRealmComponents(form.signon_realm, &server, &port,
536 form.signon_realm, &server, &port, &is_secure, &security_domain)) { 458 &is_secure, &security_domain)) {
537 return false; 459 return false;
538 } 460 }
539 std::string username = WideToUTF8(form.username_value); 461 std::string username = WideToUTF8(form.username_value);
540 std::string password = WideToUTF8(form.password_value); 462 std::string password = WideToUTF8(form.password_value);
541 std::string path = form.origin.path(); 463 std::string path = form.origin.path();
542 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS 464 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
543 : kSecProtocolTypeHTTP; 465 : kSecProtocolTypeHTTP;
544 SecKeychainItemRef new_item = NULL; 466 SecKeychainItemRef new_item = NULL;
545 OSStatus result = keychain_->AddInternetPassword( 467 OSStatus result = keychain_->AddInternetPassword(
546 NULL, server.size(), server.c_str(), 468 NULL, server.size(), server.c_str(),
547 security_domain.size(), security_domain.c_str(), 469 security_domain.size(), security_domain.c_str(),
548 username.size(), username.c_str(), 470 username.size(), username.c_str(),
549 path.size(), path.c_str(), 471 path.size(), path.c_str(),
550 port, protocol, internal_keychain_helpers::AuthTypeForScheme(form.scheme), 472 port, protocol, AuthTypeForScheme(form.scheme),
551 password.size(), password.c_str(), &new_item); 473 password.size(), password.c_str(), &new_item);
552 474
553 if (result == noErr) { 475 if (result == noErr) {
554 SetKeychainItemCreatorCode(new_item, kChromeKeychainCreatorCode); 476 SetKeychainItemCreatorCode(new_item, kChromeKeychainCreatorCode);
555 keychain_->Free(new_item); 477 keychain_->Free(new_item);
556 } else if (result == errSecDuplicateItem) { 478 } else if (result == errSecDuplicateItem) {
557 // If we collide with an existing item, find and update it instead. 479 // If we collide with an existing item, find and update it instead.
558 SecKeychainItemRef existing_item = 480 SecKeychainItemRef existing_item = KeychainItemForForm(form);
559 internal_keychain_helpers::MatchingKeychainItem(*keychain_, form);
560 if (!existing_item) { 481 if (!existing_item) {
561 return false; 482 return false;
562 } 483 }
563 bool changed = SetKeychainItemPassword(existing_item, password); 484 bool changed = SetKeychainItemPassword(existing_item, password);
564 keychain_->Free(existing_item); 485 keychain_->Free(existing_item);
565 return changed; 486 return changed;
566 } 487 }
567 488
568 return result == noErr; 489 return result == noErr;
569 } 490 }
570 491
571 std::vector<PasswordForm*> 492 std::vector<PasswordForm*>
572 MacKeychainPasswordFormAdapter::CreateFormsFromKeychainItems( 493 MacKeychainPasswordFormAdapter::CreateFormsFromKeychainItems(
573 const std::vector<SecKeychainItemRef>& items) { 494 const std::vector<SecKeychainItemRef>& items) {
574 std::vector<PasswordForm*> keychain_forms; 495 std::vector<PasswordForm*> keychain_forms;
575 for (std::vector<SecKeychainItemRef>::const_iterator i = items.begin(); 496 for (std::vector<SecKeychainItemRef>::const_iterator i = items.begin();
576 i != items.end(); ++i) { 497 i != items.end(); ++i) {
577 PasswordForm* form = new PasswordForm(); 498 PasswordForm* form = new PasswordForm();
578 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_, 499 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
579 *i, form)) { 500 *i, form)) {
580 keychain_forms.push_back(form); 501 keychain_forms.push_back(form);
581 } 502 }
582 } 503 }
583 return keychain_forms; 504 return keychain_forms;
584 } 505 }
585 506
586 // Searches |keychain| for all items usable for the given signon_realm, and 507 std::vector<SecKeychainItemRef>
587 // returns them. The caller is responsible for calling keychain->Free 508 MacKeychainPasswordFormAdapter::KeychainItemsForFillingForm(
588 // on each of them when it is finished with them. 509 const PasswordForm& form) {
510 return MatchingKeychainItems(form.signon_realm, form.scheme, NULL, NULL);
511 }
512
513 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm(
514 const PasswordForm& form) {
515 // We don't store blacklist entries in the keychain, so the answer to "what
516 // Keychain item goes with this form" is always "nothing" for blacklists.
517 if (form.blacklisted_by_user) {
518 return NULL;
519 }
520
521 std::string path = form.origin.path();
522 std::string username = WideToUTF8(form.username_value);
523 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems(
524 form.signon_realm, form.scheme, path.c_str(), username.c_str());
525
526 if (matches.size() == 0) {
527 return NULL;
528 }
529 // Free all items after the first, since we won't be returning them.
530 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1;
531 i != matches.end(); ++i) {
532 keychain_->Free(*i);
533 }
534 return matches[0];
535 }
536
589 std::vector<SecKeychainItemRef> 537 std::vector<SecKeychainItemRef>
590 MacKeychainPasswordFormAdapter::MatchingKeychainItems( 538 MacKeychainPasswordFormAdapter::MatchingKeychainItems(
591 const std::string& signon_realm, PasswordForm::Scheme scheme) { 539 const std::string& signon_realm,
540 webkit_glue::PasswordForm::Scheme scheme,
541 const char* path, const char* username) {
592 std::vector<SecKeychainItemRef> matches; 542 std::vector<SecKeychainItemRef> matches;
593 // Construct a keychain search based on the signon_realm and scheme. 543
594 std::string server; 544 std::string server;
595 std::string security_domain; 545 std::string security_domain;
596 int port; 546 int port;
597 bool is_secure; 547 bool is_secure;
598 if (!internal_keychain_helpers::ExtractSignonRealmComponents( 548 if (!ExtractSignonRealmComponents(signon_realm, &server, &port,
599 signon_realm, &server, &port, &is_secure, &security_domain)) { 549 &is_secure, &security_domain)) {
600 // TODO(stuartmorgan): Proxies will currently fail here, since their 550 // TODO(stuartmorgan): Proxies will currently fail here, since their
601 // signon_realm is not a URL. We need to detect the proxy case and handle 551 // signon_realm is not a URL. We need to detect the proxy case and handle
602 // it specially. 552 // it specially.
603 return matches; 553 return matches;
604 } 554 }
605
606 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS 555 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
607 : kSecProtocolTypeHTTP; 556 : kSecProtocolTypeHTTP;
608 SecAuthenticationType auth_type = 557 SecAuthenticationType auth_type = AuthTypeForScheme(scheme);
609 internal_keychain_helpers::AuthTypeForScheme(scheme); 558 const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ?
559 NULL : security_domain.c_str();
610 560
611 KeychainSearch keychain_search(*keychain_); 561 KeychainSearch keychain_search(*keychain_);
612 keychain_search.Init(server.c_str(), port, protocol, auth_type, 562 keychain_search.Init(server.c_str(), port, protocol, auth_type,
613 (scheme == PasswordForm::SCHEME_HTML) ? 563 auth_domain, path, username);
614 NULL : security_domain.c_str(),
615 NULL, NULL);
616 keychain_search.FindMatchingItems(&matches); 564 keychain_search.FindMatchingItems(&matches);
617 return matches; 565 return matches;
618 } 566 }
619 567
568 // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
569 bool MacKeychainPasswordFormAdapter::ExtractSignonRealmComponents(
570 const std::string& signon_realm, std::string* server, int* port,
571 bool* is_secure, std::string* security_domain) {
572 // The signon_realm will be the Origin portion of a URL for an HTML form,
573 // and the same but with the security domain as a path for HTTP auth.
574 GURL realm_as_url(signon_realm);
575 if (!realm_as_url.is_valid()) {
576 return false;
577 }
578
579 if (server)
580 *server = realm_as_url.host();
581 if (is_secure)
582 *is_secure = realm_as_url.SchemeIsSecure();
583 if (port)
584 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0;
585 if (security_domain) {
586 // Strip the leading '/' off of the path to get the security domain.
587 *security_domain = realm_as_url.path().substr(1);
588 }
589 return true;
590 }
591
592 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
593 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
594 PasswordForm::Scheme scheme) {
595 switch (scheme) {
596 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm;
597 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic;
598 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest;
599 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault;
600 }
601 NOTREACHED();
602 return kSecAuthenticationTypeDefault;
603 }
604
620 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword( 605 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
621 const SecKeychainItemRef& keychain_item, const std::string& password) { 606 const SecKeychainItemRef& keychain_item, const std::string& password) {
622 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL, 607 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
623 password.size(), 608 password.size(),
624 password.c_str()); 609 password.c_str());
625 return result == noErr; 610 return result == noErr;
626 } 611 }
627 612
628 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode( 613 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
629 const SecKeychainItemRef& keychain_item, OSType creator_code) { 614 const SecKeychainItemRef& keychain_item, OSType creator_code) {
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
689 for (std::vector<PasswordForm*>::iterator i = database_forms.begin(); 674 for (std::vector<PasswordForm*>::iterator i = database_forms.begin();
690 i != database_forms.end(); ++i) { 675 i != database_forms.end(); ++i) {
691 login_metadata_db_->RemoveLogin(**i); 676 login_metadata_db_->RemoveLogin(**i);
692 } 677 }
693 // Delete the forms we aren't returning. 678 // Delete the forms we aren't returning.
694 STLDeleteElements(&database_forms); 679 STLDeleteElements(&database_forms);
695 STLDeleteElements(&keychain_forms); 680 STLDeleteElements(&keychain_forms);
696 681
697 NotifyConsumer(request, merged_forms); 682 NotifyConsumer(request, merged_forms);
698 } 683 }
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/password_manager/password_store_mac_internal.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698