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

Unified Diff: ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.mm

Issue 2587023002: Upstream Chrome on iOS source code [8/11]. (Closed)
Patch Set: Created 4 years 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 side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.mm
diff --git a/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.mm b/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..05648dba3adcec452ce1b768bd8385583c90accf
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.mm
@@ -0,0 +1,626 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.h"
+
+#include <memory>
+
+#include "base/ios/ios_util.h"
+#include "base/ios/weak_nsobject.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/objc_property_releaser.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/memory/scoped_vector.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/password_form.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/google/core/browser/google_util.h"
+#include "components/keyed_service/core/service_access_type.h"
+#include "components/password_manager/core/browser/affiliation_utils.h"
+#include "components/password_manager/core/browser/password_bubble_experiment.h"
+#include "components/password_manager/core/browser/password_manager_constants.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_member.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/url_formatter/url_formatter.h"
+#include "ios/chrome/browser/application_context.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/experimental_flags.h"
+#include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
+#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
+#import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item.h"
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.h"
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
+#import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
+#import "ios/chrome/browser/ui/settings/password_details_collection_view_controller.h"
+#import "ios/chrome/browser/ui/settings/reauthentication_module.h"
+#import "ios/chrome/browser/ui/settings/settings_utils.h"
+#import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+#include "url/gurl.h"
+
+namespace {
+
+typedef NS_ENUM(NSInteger, SectionIdentifier) {
+ SectionIdentifierMessage = kSectionIdentifierEnumZero,
+ SectionIdentifierSavePasswordsSwitch,
+ SectionIdentifierSavedPasswords,
+ SectionIdentifierBlacklist,
+};
+
+typedef NS_ENUM(NSInteger, ItemType) {
+ ItemTypeManageAccount = kItemTypeEnumZero,
+ ItemTypeHeader,
+ ItemTypeSavePasswordsSwitch,
+ ItemTypeSavedPassword, // This is a repeated item type.
+ ItemTypeBlacklisted, // This is a repeated item type.
+};
+
+// TODO(crbug.com/669538): This function should be removed after original
+// version of GetHumanReadableOrigin will be moved to affiliation_utils.
+std::string GetHumanReadableOriginCopy(
+ const autofill::PasswordForm& password_form) {
+ password_manager::FacetURI facet_uri =
+ password_manager::FacetURI::FromPotentiallyInvalidSpec(
+ password_form.signon_realm);
+ if (facet_uri.IsValidAndroidFacetURI())
+ return facet_uri.scheme() + "://" + facet_uri.android_package_name();
+ return base::UTF16ToUTF8(url_formatter::FormatUrl(password_form.origin));
+}
+
+} // namespace
+
+@interface SavePasswordsCollectionViewController (UsedBySavePasswordsConsumer)
+// Callback called when the async request launched from
+// |getLoginsFromPasswordStore| finishes.
+- (void)onGetPasswordStoreResults:
+ (const std::vector<std::unique_ptr<autofill::PasswordForm>>&)result;
+@end
+
+namespace password_manager {
+// A bridge C++ class passing notification about finished password store
+// requests to owning Obj-C class SavePasswordsCollectionViewController.
+class SavePasswordsConsumer : public PasswordStoreConsumer {
+ public:
+ explicit SavePasswordsConsumer(
+ SavePasswordsCollectionViewController* delegate);
+ ~SavePasswordsConsumer() override;
+ void OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
+
+ private:
+ __unsafe_unretained SavePasswordsCollectionViewController* delegate_; // weak
+ DISALLOW_COPY_AND_ASSIGN(SavePasswordsConsumer);
+};
+
+SavePasswordsConsumer::SavePasswordsConsumer(
+ SavePasswordsCollectionViewController* delegate)
+ : delegate_(delegate) {}
+
+SavePasswordsConsumer::~SavePasswordsConsumer() {}
+
+void SavePasswordsConsumer::OnGetPasswordStoreResults(
+ std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
+ if (!results.empty())
+ [delegate_ onGetPasswordStoreResults:results];
+}
+} // namespace password_manager
+
+// Use the type of the items to convey the Saved/Blacklisted status.
+@interface SavedFormContentItem : CollectionViewTextItem
+@end
+@implementation SavedFormContentItem
+@end
+@interface BlacklistedFormContentItem : CollectionViewTextItem
+@end
+@implementation BlacklistedFormContentItem
+@end
+
+@interface SavePasswordsCollectionViewController ()<
+ BooleanObserver,
+ PasswordDetailsCollectionViewControllerDelegate,
+ SuccessfulReauthTimeAccessor> {
+ // The observable boolean that binds to the password manager setting state.
+ // Saved passwords are only on if the password manager is enabled.
+ base::scoped_nsobject<PrefBackedBoolean> passwordManagerEnabled_;
+ // The item related to the switch for the password manager setting.
+ base::scoped_nsobject<CollectionViewSwitchItem> savePasswordsItem_;
+ // The interface for getting and manipulating a user's saved passwords.
+ scoped_refptr<password_manager::PasswordStore> passwordStore_;
+ // A helper object for passing data about saved passwords from a finished
+ // password store request to the SavePasswordsCollectionViewController.
+ std::unique_ptr<password_manager::SavePasswordsConsumer>
+ savedPasswordsConsumer_;
+ // A helper object for passing data about blacklisted sites from a finished
+ // password store request to the SavePasswordsCollectionViewController.
+ std::unique_ptr<password_manager::SavePasswordsConsumer>
+ blacklistPasswordsConsumer_;
+ // The list of the user's saved passwords.
+ ScopedVector<autofill::PasswordForm> savedForms_;
+ // The list of the user's blacklisted sites.
+ ScopedVector<autofill::PasswordForm> blacklistedForms_;
+ // Deletion of password being asynchronous, and the method taking a reference
+ // to the PasswordForm, the PasswordForm must outlive the calls to
+ // RemoveLogin. This vector will ensure this.
+ ScopedVector<autofill::PasswordForm> deletedForms_;
+ // The current Chrome browser state.
+ ios::ChromeBrowserState* browserState_;
+ // Object storing the time of the previous successful re-authentication.
+ // This is meant to be used by the |ReauthenticationModule| for keeping
+ // re-authentications valid for a certain time interval within the scope
+ // of the Save Passwords Settings.
+ base::scoped_nsobject<NSDate> successfulReauthTime_;
+ // Module containing the reauthentication mechanism for viewing and copying
+ // passwords.
+ base::scoped_nsobject<ReauthenticationModule> reauthenticationModule_;
+
+ base::mac::ObjCPropertyReleaser
+ propertyReleaser_SavePasswordsCollectionViewController_;
+}
+// Kick off async request to get logins from password store.
+- (void)getLoginsFromPasswordStore;
+@end
+
+@implementation SavePasswordsCollectionViewController
+
+#pragma mark - Initialization
+
+- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
+ DCHECK(browserState);
+ self = [super initWithStyle:CollectionViewControllerStyleAppBar];
+ if (self) {
+ browserState_ = browserState;
+ reauthenticationModule_.reset([[ReauthenticationModule alloc]
+ initWithSuccessfulReauthTimeAccessor:self]);
+ int titleId = IDS_IOS_SAVE_PASSWORDS;
+ syncer::SyncService* syncService =
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(browserState_);
+ if (password_bubble_experiment::IsSmartLockBrandingEnabled(syncService)) {
+ titleId = IDS_IOS_SAVE_PASSWORDS_SMART_LOCK;
+ }
+ self.title = l10n_util::GetNSString(titleId);
+ self.shouldHideDoneButton = YES;
+ passwordStore_ = IOSChromePasswordStoreFactory::GetForBrowserState(
+ browserState_, ServiceAccessType::EXPLICIT_ACCESS);
+ DCHECK(passwordStore_);
+ passwordManagerEnabled_.reset([[PrefBackedBoolean alloc]
+ initWithPrefService:browserState_->GetPrefs()
+ prefName:password_manager::prefs::
+ kPasswordManagerSavingEnabled]);
+ [passwordManagerEnabled_ setObserver:self];
+ [self getLoginsFromPasswordStore];
+ [self updateEditButton];
+ [self loadModel];
+
+ propertyReleaser_SavePasswordsCollectionViewController_.Init(
+ self, [SavePasswordsCollectionViewController class]);
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [passwordManagerEnabled_ setObserver:nil];
+ [super dealloc];
+}
+
+#pragma mark - SettingsRootCollectionViewController
+
+- (void)loadModel {
+ [super loadModel];
+ CollectionViewModel* model = self.collectionViewModel;
+
+ // Manage account message.
+ CollectionViewItem* manageAccountLinkItem = [self manageAccountLinkItem];
+ if (manageAccountLinkItem) {
+ [model addSectionWithIdentifier:SectionIdentifierMessage];
+ [model addItem:manageAccountLinkItem
+ toSectionWithIdentifier:SectionIdentifierMessage];
+ }
+
+ // Save passwords switch.
+ [model addSectionWithIdentifier:SectionIdentifierSavePasswordsSwitch];
+ savePasswordsItem_.reset([[self savePasswordsItem] retain]);
+ [model addItem:savePasswordsItem_
+ toSectionWithIdentifier:SectionIdentifierSavePasswordsSwitch];
+
+ // Saved passwords.
+ if ([passwordManagerEnabled_ value]) {
+ if (!savedForms_.empty()) {
+ [model addSectionWithIdentifier:SectionIdentifierSavedPasswords];
+ CollectionViewTextItem* headerItem = [[[CollectionViewTextItem alloc]
+ initWithType:ItemTypeHeader] autorelease];
+ headerItem.text =
+ l10n_util::GetNSString(IDS_PASSWORD_MANAGER_SHOW_PASSWORDS_TAB_TITLE);
+ [model setHeader:headerItem
+ forSectionWithIdentifier:SectionIdentifierSavedPasswords];
+ for (size_t i = 0; i < savedForms_.size(); ++i) {
+ autofill::PasswordForm* form = savedForms_[i];
+ [model addItem:[self savedFormItemWithForm:form]
+ toSectionWithIdentifier:SectionIdentifierSavedPasswords];
+ }
+ }
+ if (!blacklistedForms_.empty()) {
+ [model addSectionWithIdentifier:SectionIdentifierBlacklist];
+ CollectionViewTextItem* headerItem = [[[CollectionViewTextItem alloc]
+ initWithType:ItemTypeHeader] autorelease];
+ headerItem.text =
+ l10n_util::GetNSString(IDS_PASSWORD_MANAGER_EXCEPTIONS_TAB_TITLE);
+ [model setHeader:headerItem
+ forSectionWithIdentifier:SectionIdentifierBlacklist];
+ for (size_t i = 0; i < blacklistedForms_.size(); ++i) {
+ autofill::PasswordForm* form = blacklistedForms_[i];
+ [model addItem:[self blacklistedFormItemWithForm:form]
+ toSectionWithIdentifier:SectionIdentifierBlacklist];
+ }
+ }
+ }
+}
+
+#pragma mark - Items
+
+- (CollectionViewItem*)manageAccountLinkItem {
+ CollectionViewFooterItem* footerItem = [[[CollectionViewFooterItem alloc]
+ initWithType:ItemTypeManageAccount] autorelease];
+ footerItem.text =
+ l10n_util::GetNSString(IDS_IOS_SAVE_PASSWORDS_MANAGE_ACCOUNT);
+ footerItem.linkURL = google_util::AppendGoogleLocaleParam(
+ GURL(password_manager::kPasswordManagerAccountDashboardURL),
+ GetApplicationContext()->GetApplicationLocale());
+ footerItem.linkDelegate = self;
+ return footerItem;
+}
+
+- (CollectionViewSwitchItem*)savePasswordsItem {
+ CollectionViewSwitchItem* savePasswordsItem =
+ [[[CollectionViewSwitchItem alloc]
+ initWithType:ItemTypeSavePasswordsSwitch] autorelease];
+ savePasswordsItem.text = l10n_util::GetNSString(IDS_IOS_SAVE_PASSWORDS);
+ savePasswordsItem.on = [passwordManagerEnabled_ value];
+ return savePasswordsItem;
+}
+
+- (SavedFormContentItem*)savedFormItemWithForm:(autofill::PasswordForm*)form {
+ SavedFormContentItem* passwordItem = [[[SavedFormContentItem alloc]
+ initWithType:ItemTypeSavedPassword] autorelease];
+ passwordItem.text =
+ base::SysUTF8ToNSString(GetHumanReadableOriginCopy(*form));
+ passwordItem.detailText = base::SysUTF16ToNSString(form->username_value);
+ if (experimental_flags::IsViewCopyPasswordsEnabled()) {
+ passwordItem.accessibilityTraits |= UIAccessibilityTraitButton;
+ passwordItem.accessoryType =
+ MDCCollectionViewCellAccessoryDisclosureIndicator;
+ }
+ return passwordItem;
+}
+
+- (BlacklistedFormContentItem*)blacklistedFormItemWithForm:
+ (autofill::PasswordForm*)form {
+ BlacklistedFormContentItem* passwordItem =
+ [[[BlacklistedFormContentItem alloc] initWithType:ItemTypeBlacklisted]
+ autorelease];
+ passwordItem.text =
+ base::SysUTF8ToNSString(GetHumanReadableOriginCopy(*form));
+ return passwordItem;
+}
+
+#pragma mark - MDCCollectionViewEditingDelegate
+
+- (BOOL)collectionViewAllowsEditing:(UICollectionView*)collectionView {
+ return YES;
+}
+
+#pragma mark - MDCCollectionViewStylingDelegate
+
+- (MDCCollectionViewCellStyle)collectionView:(UICollectionView*)collectionView
+ cellStyleForSection:(NSInteger)section {
+ NSInteger sectionIdentifier =
+ [self.collectionViewModel sectionIdentifierForSection:section];
+ switch (sectionIdentifier) {
+ case SectionIdentifierMessage:
+ // Display the message in the default style with no "card" UI and no
+ // section padding.
+ return MDCCollectionViewCellStyleDefault;
+ default:
+ return self.styler.cellStyle;
+ }
+}
+
+- (BOOL)collectionView:(UICollectionView*)collectionView
+ shouldHideItemBackgroundAtIndexPath:(NSIndexPath*)indexPath {
+ NSInteger sectionIdentifier =
+ [self.collectionViewModel sectionIdentifierForSection:indexPath.section];
+ switch (sectionIdentifier) {
+ case SectionIdentifierMessage:
+ // Display the message without any background image or shadowing.
+ return YES;
+ default:
+ return NO;
+ }
+}
+
+- (CGFloat)collectionView:(UICollectionView*)collectionView
+ cellHeightAtIndexPath:(NSIndexPath*)indexPath {
+ CollectionViewItem* item =
+ [self.collectionViewModel itemAtIndexPath:indexPath];
+ switch (item.type) {
+ case ItemTypeManageAccount:
+ return [MDCCollectionViewCell
+ cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds)
+ forItem:item];
+ case ItemTypeSavedPassword:
+ return MDCCellDefaultTwoLineHeight;
+ default:
+ return MDCCellDefaultOneLineHeight;
+ }
+}
+
+- (BOOL)collectionView:(UICollectionView*)collectionView
+ hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
+ NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
+ switch (type) {
+ case ItemTypeSavePasswordsSwitch:
+ return YES;
+ default:
+ return NO;
+ }
+}
+
+#pragma mark - UICollectionViewDataSource
+
+- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
+ cellForItemAtIndexPath:(NSIndexPath*)indexPath {
+ UICollectionViewCell* cell =
+ [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
+
+ if ([self.collectionViewModel itemTypeForIndexPath:indexPath] ==
+ ItemTypeSavePasswordsSwitch) {
+ CollectionViewSwitchCell* switchCell =
+ base::mac::ObjCCastStrict<CollectionViewSwitchCell>(cell);
+ [switchCell.switchView addTarget:self
+ action:@selector(savePasswordsSwitchChanged:)
+ forControlEvents:UIControlEventValueChanged];
+ }
+ return cell;
+}
+
+- (UICollectionReusableView*)collectionView:(UICollectionView*)collectionView
+ viewForSupplementaryElementOfKind:(NSString*)kind
+ atIndexPath:(NSIndexPath*)indexPath {
+ UICollectionReusableView* view = [super collectionView:collectionView
+ viewForSupplementaryElementOfKind:kind
+ atIndexPath:indexPath];
+ MDCCollectionViewTextCell* textCell =
+ base::mac::ObjCCast<MDCCollectionViewTextCell>(view);
+ if (textCell) {
+ textCell.textLabel.textColor = [[MDCPalette greyPalette] tint500];
+ }
+ return view;
+};
+
+#pragma mark - BooleanObserver
+
+- (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean {
+ DCHECK_EQ(observableBoolean, passwordManagerEnabled_.get());
+
+ // Update the item.
+ savePasswordsItem_.get().on = [passwordManagerEnabled_ value];
+
+ // Update the cell.
+ [self reconfigureCellsForItems:@[ savePasswordsItem_ ]
+ inSectionWithIdentifier:SectionIdentifierSavePasswordsSwitch];
+
+ // Update the rest of the UI.
+ [self.editor setEditing:NO];
+ [self updateEditButton];
+ [self reloadData];
+}
+
+#pragma mark - Actions
+
+- (void)savePasswordsSwitchChanged:(UISwitch*)switchView {
+ // Update the setting.
+ [passwordManagerEnabled_ setValue:switchView.on];
+
+ // Update the item.
+ savePasswordsItem_.get().on = [passwordManagerEnabled_ value];
+
+ // Update the rest of the UI.
+ [self.editor setEditing:NO];
+ [self updateEditButton];
+ [self reloadData];
+}
+
+#pragma mark - Private methods
+
+- (void)getLoginsFromPasswordStore {
+ savedPasswordsConsumer_.reset(
+ new password_manager::SavePasswordsConsumer(self));
+ passwordStore_->GetAutofillableLogins(savedPasswordsConsumer_.get());
+ blacklistPasswordsConsumer_.reset(
+ new password_manager::SavePasswordsConsumer(self));
+ passwordStore_->GetBlacklistLogins(blacklistPasswordsConsumer_.get());
+}
+
+- (void)onGetPasswordStoreResults:
+ (const std::vector<std::unique_ptr<autofill::PasswordForm>>&)result {
+ for (auto it = result.begin(); it != result.end(); ++it) {
+ // PasswordForm is needed when user wants to delete the site/password.
+ autofill::PasswordForm* form = new autofill::PasswordForm(**it);
+ if (form->blacklisted_by_user)
+ blacklistedForms_.push_back(form);
+ else
+ savedForms_.push_back(form);
+ }
+
+ [self updateEditButton];
+ [self reloadData];
+}
+
+- (BOOL)shouldShowEditButton {
+ return [passwordManagerEnabled_ value];
+}
+
+- (BOOL)editButtonEnabled {
+ DCHECK([self shouldShowEditButton]);
+ return !savedForms_.empty() || !blacklistedForms_.empty();
+}
+
+#pragma mark UICollectionViewDelegate
+
+- (void)collectionView:(UICollectionView*)collectionView
+ didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
+ [super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
+
+ // Actions should only take effect when not in editing mode.
+ if (self.editing) {
+ return;
+ }
+
+ CollectionViewModel* model = self.collectionViewModel;
+ if ([model itemTypeForIndexPath:indexPath] == ItemTypeSavedPassword) {
+ DCHECK_EQ([model sectionIdentifierForSection:indexPath.section],
+ SectionIdentifierSavedPasswords);
+ if (experimental_flags::IsViewCopyPasswordsEnabled()) {
+ DCHECK_LT(base::checked_cast<size_t>(indexPath.item), savedForms_.size());
+ autofill::PasswordForm* form = savedForms_[indexPath.item];
+ NSString* username = base::SysUTF16ToNSString(form->username_value);
+ NSString* password = base::SysUTF16ToNSString(form->password_value);
+ NSString* origin =
+ base::SysUTF8ToNSString(GetHumanReadableOriginCopy(*form));
+ base::scoped_nsobject<UIViewController> controller(
+ [[PasswordDetailsCollectionViewController alloc]
+ initWithPasswordForm:*form
+ delegate:self
+ reauthenticationModule:reauthenticationModule_
+ username:username
+ password:password
+ origin:origin]);
+ [self.navigationController pushViewController:controller animated:YES];
+ }
+ }
+}
+
+#pragma mark MDCCollectionViewEditingDelegate
+
+- (BOOL)collectionView:(UICollectionView*)collectionView
+ canEditItemAtIndexPath:(NSIndexPath*)indexPath {
+ // Only password cells are editable.
+ CollectionViewItem* item =
+ [self.collectionViewModel itemAtIndexPath:indexPath];
+ return [item isKindOfClass:[SavedFormContentItem class]] ||
+ [item isKindOfClass:[BlacklistedFormContentItem class]];
+}
+
+- (void)collectionView:(UICollectionView*)collectionView
+ willDeleteItemsAtIndexPaths:(NSArray*)indexPaths {
+ // Ensure indexPaths are sorted to maintain delete logic, and keep track of
+ // number of items deleted to adjust index for accessing elements in the
+ // forms vectors.
+ NSArray* sortedIndexPaths =
+ [indexPaths sortedArrayUsingSelector:@selector(compare:)];
+ int passwordsDeleted = 0;
+ int blacklistedDeleted = 0;
+ for (NSIndexPath* indexPath in sortedIndexPaths) {
+ // Only form items are editable.
+ CollectionViewTextItem* item =
+ base::mac::ObjCCastStrict<CollectionViewTextItem>(
+ [self.collectionViewModel itemAtIndexPath:indexPath]);
+ BOOL blacklisted = [item isKindOfClass:[BlacklistedFormContentItem class]];
+ unsigned int formIndex = (unsigned int)indexPath.item;
+ // Adjust index to account for deleted items.
+ formIndex -= blacklisted ? blacklistedDeleted : passwordsDeleted;
+ ScopedVector<autofill::PasswordForm>& forms =
+ blacklisted ? blacklistedForms_ : savedForms_;
+ DCHECK_LT(formIndex, forms.size());
+ auto formIterator = forms.begin() + formIndex;
+ passwordStore_->RemoveLogin(**formIterator);
+ deletedForms_.push_back(*formIterator);
+ forms.weak_erase(formIterator);
+ if (blacklisted) {
+ ++blacklistedDeleted;
+ } else {
+ ++passwordsDeleted;
+ }
+ }
+
+ // Must call super at the end of the child implementation.
+ [super collectionView:collectionView willDeleteItemsAtIndexPaths:indexPaths];
+}
+
+- (void)collectionView:(UICollectionView*)collectionView
+ didDeleteItemsAtIndexPaths:(NSArray*)indexPaths {
+ // Remove empty sections.
+ // TODO(crbug.com/593786): Move this logic in CollectionViewController.
+ NSMutableOrderedSet* sectionsToRemove = [NSMutableOrderedSet orderedSet];
+ // Sort and enumerate in reverse order to delete the items from the collection
+ // view model.
+ NSArray* sortedIndexPaths =
+ [indexPaths sortedArrayUsingSelector:@selector(compare:)];
+ for (NSIndexPath* indexPath in [sortedIndexPaths reverseObjectEnumerator]) {
+ if ([collectionView numberOfItemsInSection:indexPath.section] == 0) {
+ [sectionsToRemove addObject:@(indexPath.section)];
+ }
+ }
+ base::WeakNSObject<SavePasswordsCollectionViewController> weakSelf(self);
+ [self.collectionView performBatchUpdates:^{
+ base::scoped_nsobject<SavePasswordsCollectionViewController> strongSelf(
+ [weakSelf retain]);
+ if (!strongSelf)
+ return;
+ for (NSNumber* sectionNumber in sectionsToRemove) {
+ NSInteger section = [sectionNumber integerValue];
+ NSInteger sectionIdentifier = [[strongSelf collectionViewModel]
+ sectionIdentifierForSection:section];
+ [[strongSelf collectionViewModel]
+ removeSectionWithIdentifier:sectionIdentifier];
+ [[strongSelf collectionView]
+ deleteSections:[NSIndexSet indexSetWithIndex:section]];
+ }
+ }
+ completion:^(BOOL finished) {
+ base::scoped_nsobject<SavePasswordsCollectionViewController> strongSelf(
+ [weakSelf retain]);
+ if (!strongSelf)
+ return;
+ if (![strongSelf editButtonEnabled]) {
+ [strongSelf setEditing:NO];
+ }
+ [strongSelf updateEditButton];
+ }];
+}
+
+#pragma mark PasswordDetailsCollectionViewControllerDelegate
+
+- (void)deletePassword:(const autofill::PasswordForm&)form {
+ passwordStore_->RemoveLogin(form);
+ for (auto it = savedForms_.begin(); it != savedForms_.end(); ++it) {
+ if (**it == form) {
+ savedForms_.weak_erase(it);
+ return;
+ }
+ }
+ [self.navigationController popViewControllerAnimated:YES];
+}
+
+#pragma mark SuccessfulReauthTimeAccessor
+
+- (void)updateSuccessfulReauthTime {
+ successfulReauthTime_.reset([[NSDate alloc] init]);
+}
+
+- (NSDate*)lastSuccessfulReauthTime {
+ return successfulReauthTime_.get();
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698