910e62b5创建于 1月15日历史提交
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <memory>

#import "base/functional/bind.h"
#import "base/location.h"
#import "base/memory/raw_ptr.h"
#import "base/notreached.h"
#import "base/strings/sys_string_conversions.h"
#import "base/time/time.h"
#import "components/affiliations/core/browser/affiliation_service.h"
#import "components/affiliations/core/browser/affiliation_utils.h"
#import "components/autofill/core/browser/data_manager/addresses/address_data_manager.h"
#import "components/autofill/core/browser/data_manager/payments/payments_data_manager.h"
#import "components/autofill/core/browser/data_manager/personal_data_manager.h"
#import "components/autofill/core/browser/data_manager/personal_data_manager_observer.h"
#import "components/password_manager/core/browser/affiliation/affiliated_match_helper.h"
#import "components/password_manager/core/browser/password_manager_util.h"
#import "components/password_manager/core/browser/password_store/password_store_change.h"
#import "components/password_manager/core/browser/password_store/password_store_consumer.h"
#import "components/password_manager/core/browser/password_store/password_store_interface.h"
#import "components/password_manager/core/browser/password_store/password_store_util.h"
#import "components/password_manager/core/browser/ui/passwords_grouper.h"
#import "ios/web/public/thread/web_task_traits.h"
#import "ios/web/public/thread/web_thread.h"
#import "ios/web_view/internal/autofill/cwv_autofill_data_manager_internal.h"
#import "ios/web_view/internal/autofill/cwv_autofill_profile_internal.h"
#import "ios/web_view/internal/autofill/cwv_credit_card_internal.h"
#import "ios/web_view/internal/passwords/cwv_password_internal.h"
#import "ios/web_view/public/cwv_autofill_data_manager_observer.h"
#import "ios/web_view/public/cwv_credential_provider_extension_utils.h"
#import "ui/base/resource/resource_bundle.h"
#import "url/gurl.h"

// Typedefs of |completionHandler| in |fetchProfilesWithCompletionHandler:|,
// |fetchCreditCardsWithCompletionHandler:|, and
// |fetchPasswordsWithCompletionHandler|.
typedef void (^CWVFetchProfilesCompletionHandler)(
    NSArray<CWVAutofillProfile*>* profiles);
typedef void (^CWVFetchCreditCardsCompletionHandler)(
    NSArray<CWVCreditCard*>* creditCards);
typedef void (^CWVFetchPasswordsCompletionHandler)(
    NSArray<CWVPassword*>* passwords);

namespace {
using PasswordFormList = std::vector<password_manager::PasswordForm>;
using PasswordStoreChangeMap =
    std::unordered_map<std::string,
                       password_manager::PasswordStoreChange::Type>;
using PasswordFormListCallback = base::OnceCallback<void(PasswordFormList)>;
}  // namespace

@interface CWVAutofillDataManager ()

// Called when WebViewPasswordStoreObserver's |OnLoginsChanged| is called.
- (void)handlePasswordStoreLoginsAdded:(NSArray<CWVPassword*>*)added
                               updated:(NSArray<CWVPassword*>*)updated
                               removed:(NSArray<CWVPassword*>*)removed;
// Called when WebViewPasswordStoreConsumer's |OnGetPasswordStoreResults| is
// invoked.
- (void)handlePasswordStoreResults:(NSArray<CWVPassword*>*)passwords;
// Called when WebViewPersonalDataManagerObserverBridge's
// |OnPersonalDataChanged| is invoked.
- (void)personalDataDidChange;
// Collects and converts autofill::AutofillProfiles stored internally in
// |_personalDataManager| to CWVAutofillProfiles.
- (NSArray<CWVAutofillProfile*>*)profiles;
// Collects and converts autofill::CreditCards stored internally in
// |_personalDataManager| to CWVCreditCards.
- (NSArray<CWVCreditCard*>*)creditCards;
// Check if the password affiliation is enabled.
- (BOOL)isPasswordAffiliationEnabled;
// Matches passwords with affilation data and invokes resultCallback.
- (void)matchAffiliationsAndUpdatePasswordsWithForms:
            (PasswordFormList)passwordForms
                                      resultCallback:
                                          (PasswordFormListCallback)completion;

- (void)injectAffiliationAndBrandingInformationForForms:
            (PasswordFormList)passwordForms
                                         resultCallback:
                                             (PasswordFormListCallback)
                                                 completion;

@end

namespace ios_web_view {
// C++ to ObjC bridge for PersonalDataManagerObserver.
class WebViewPersonalDataManagerObserverBridge
    : public autofill::PersonalDataManagerObserver {
 public:
  explicit WebViewPersonalDataManagerObserverBridge(
      CWVAutofillDataManager* data_manager)
      : data_manager_(data_manager) {}
  ~WebViewPersonalDataManagerObserverBridge() override = default;

  // autofill::PersonalDataManagerObserver implementation.
  void OnPersonalDataChanged() override {
    [data_manager_ personalDataDidChange];
  }

 private:
  __weak CWVAutofillDataManager* data_manager_;
};

// C++ to ObjC bridge for PasswordStoreConsumer.
class WebViewPasswordStoreConsumer
    : public password_manager::PasswordStoreConsumer {
 public:
  explicit WebViewPasswordStoreConsumer(CWVAutofillDataManager* data_manager)
      : data_manager_(data_manager) {}

  void OnGetPasswordStoreResults(
      std::vector<std::unique_ptr<password_manager::PasswordForm>> results)
      override {
    BOOL isAffiliationsEnabled = [data_manager_ isPasswordAffiliationEnabled];

    // Move forms to a regular vector.
    PasswordFormList forms;
    forms.reserve(results.size());
    for (auto& form : results) {
      forms.push_back(*form);
    }

    if (isAffiliationsEnabled) {
      auto callback = base::BindOnce(
          &WebViewPasswordStoreConsumer::OnAffiliatedPasswordsUpdated,
          weak_ptr_factory_.GetWeakPtr());
      [data_manager_
          matchAffiliationsAndUpdatePasswordsWithForms:forms
                                        resultCallback:std::move(callback)];
    } else {
      NSMutableArray<CWVPassword*>* passwords = [NSMutableArray array];
      for (auto& form : results) {
        CWVPassword* password =
            [[CWVPassword alloc] initWithPasswordForm:*form];
        [passwords addObject:password];
      }
      [data_manager_ handlePasswordStoreResults:passwords];
    }
  }

  base::WeakPtr<password_manager::PasswordStoreConsumer> GetWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

 protected:
  void OnAffiliatedPasswordsUpdated(PasswordFormList affiliated_forms) {
    NSMutableArray<CWVPassword*>* passwords = [NSMutableArray array];
    for (auto& form : affiliated_forms) {
      CWVPassword* password = [[CWVPassword alloc] initWithPasswordForm:form
                                                   isAffiliationEnabled:true];
      [passwords addObject:password];
    }
    [data_manager_ handlePasswordStoreResults:passwords];
  }

 private:
  __weak CWVAutofillDataManager* data_manager_;
  base::WeakPtrFactory<WebViewPasswordStoreConsumer> weak_ptr_factory_{this};
};

// C++ to ObjC bridge for PasswordStoreInterface::Observer.
class WebViewPasswordStoreObserver
    : public password_manager::PasswordStoreInterface::Observer {
 public:
  explicit WebViewPasswordStoreObserver(CWVAutofillDataManager* data_manager)
      : data_manager_(data_manager) {}
  void OnLoginsChanged(
      password_manager::PasswordStoreInterface* store,
      const password_manager::PasswordStoreChangeList& changes) override {
    BOOL isAffiliationsEnabled = [data_manager_ isPasswordAffiliationEnabled];

    if (isAffiliationsEnabled) {
      PasswordFormList unaffiliated_forms;
      unaffiliated_forms.reserve(changes.size());
      PasswordStoreChangeMap change_map;

      for (const password_manager::PasswordStoreChange& change : changes) {
        if (change.form().blocked_by_user) {
          continue;
        }

        password_manager::PasswordForm form = change.form();
        change_map[form.keychain_identifier] = change.type();
        unaffiliated_forms.push_back(std::move(form));
      }

      auto callback = base::BindOnce(
          &WebViewPasswordStoreObserver::OnAffiliatedPasswordsUpdated,
          weak_ptr_factory_.GetWeakPtr(), std::move(change_map));
      [data_manager_
          matchAffiliationsAndUpdatePasswordsWithForms:unaffiliated_forms
                                        resultCallback:std::move(callback)];
    } else {
      NSMutableArray* added = [NSMutableArray array];
      NSMutableArray* updated = [NSMutableArray array];
      NSMutableArray* removed = [NSMutableArray array];
      for (const password_manager::PasswordStoreChange& change : changes) {
        if (change.form().blocked_by_user) {
          continue;
        }
        CWVPassword* password =
            [[CWVPassword alloc] initWithPasswordForm:change.form()];
        switch (change.type()) {
          case password_manager::PasswordStoreChange::ADD:
            [added addObject:password];
            break;
          case password_manager::PasswordStoreChange::UPDATE:
            [updated addObject:password];
            break;
          case password_manager::PasswordStoreChange::REMOVE:
            [removed addObject:password];
            break;
          default:
            NOTREACHED();
        }
      }
      [data_manager_ handlePasswordStoreLoginsAdded:added
                                            updated:updated
                                            removed:removed];
    }
  }
  void OnLoginsRetained(password_manager::PasswordStoreInterface* store,
                        const std::vector<password_manager::PasswordForm>&
                            retained_passwords) override {
    // No op.
  }

 protected:
  void OnAffiliatedPasswordsUpdated(PasswordStoreChangeMap change_map,
                                    PasswordFormList affiliated_forms) {
    NSMutableArray* added = [NSMutableArray array];
    NSMutableArray* updated = [NSMutableArray array];
    NSMutableArray* removed = [NSMutableArray array];

    for (const auto& form : affiliated_forms) {
      CWVPassword* password = [[CWVPassword alloc] initWithPasswordForm:form
                                                   isAffiliationEnabled:true];
      auto it = change_map.find(form.keychain_identifier);
      if (it != change_map.end()) {
        switch (it->second) {
          case password_manager::PasswordStoreChange::ADD:
            [added addObject:password];
            break;
          case password_manager::PasswordStoreChange::UPDATE:
            [updated addObject:password];
            break;
          case password_manager::PasswordStoreChange::REMOVE:
            [removed addObject:password];
            break;
          default:
            NOTREACHED();
        }
      }
    }

    [data_manager_ handlePasswordStoreLoginsAdded:added
                                          updated:updated
                                          removed:removed];
  }

 private:
  __weak CWVAutofillDataManager* data_manager_;
  base::WeakPtrFactory<WebViewPasswordStoreObserver> weak_ptr_factory_{this};
};

}  // namespace ios_web_view

@implementation CWVAutofillDataManager {
  autofill::PersonalDataManager* _personalDataManager;
  std::unique_ptr<ios_web_view::WebViewPersonalDataManagerObserverBridge>
      _personalDataManagerObserverBridge;
  // These completion handlers are stored so they can be called later on when
  // |_personalDataManager| completes its initial loading.
  NSMutableArray<CWVFetchProfilesCompletionHandler>*
      _fetchProfilesCompletionHandlers;
  NSMutableArray<CWVFetchCreditCardsCompletionHandler>*
      _fetchCreditCardsCompletionHandlers;
  NSMutableArray<CWVFetchPasswordsCompletionHandler>*
      _fetchPasswordsCompletionHandlers;
  // Holds weak observers.
  NSHashTable<id<CWVAutofillDataManagerObserver>>* _observers;

  password_manager::PasswordStoreInterface* _passwordStore;
  raw_ptr<affiliations::AffiliationService> _affiliationsService;
  std::unique_ptr<password_manager::AffiliatedMatchHelper>
      _affiliatedMatchHelper;
  BOOL _isPasswordAffiliationEnabled;
  std::unique_ptr<ios_web_view::WebViewPasswordStoreConsumer>
      _passwordStoreConsumer;
  std::unique_ptr<ios_web_view::WebViewPasswordStoreObserver>
      _passwordStoreObserver;
}

- (instancetype)
     initWithPersonalDataManager:
         (autofill::PersonalDataManager*)personalDataManager
                   passwordStore:
                       (password_manager::PasswordStoreInterface*)passwordStore
             affiliationsService:
                 (affiliations::AffiliationService*)affiliationsService
    isPasswordAffiliationEnabled:(BOOL)isPasswordAffiliationEnabled {
  self = [super init];
  if (self) {
    _personalDataManager = personalDataManager;
    _passwordStore = passwordStore;
    _affiliationsService = affiliationsService;
    _affiliatedMatchHelper =
        std::make_unique<password_manager::AffiliatedMatchHelper>(
            affiliationsService);
    _isPasswordAffiliationEnabled = isPasswordAffiliationEnabled;
    _passwordStoreObserver =
        std::make_unique<ios_web_view::WebViewPasswordStoreObserver>(self);
    _passwordStore->AddObserver(_passwordStoreObserver.get());
    _personalDataManagerObserverBridge = std::make_unique<
        ios_web_view::WebViewPersonalDataManagerObserverBridge>(self);
    _personalDataManager->AddObserver(_personalDataManagerObserverBridge.get());
    _fetchProfilesCompletionHandlers = [NSMutableArray array];
    _fetchCreditCardsCompletionHandlers = [NSMutableArray array];
    _fetchPasswordsCompletionHandlers = [NSMutableArray array];
    _observers = [NSHashTable weakObjectsHashTable];
  }
  return self;
}

- (void)dealloc {
  _personalDataManager->RemoveObserver(
      _personalDataManagerObserverBridge.get());
  _passwordStore->RemoveObserver(_passwordStoreObserver.get());
}

#pragma mark - Public Methods

- (void)addObserver:(__weak id<CWVAutofillDataManagerObserver>)observer {
  [_observers addObject:observer];
}

- (void)removeObserver:(__weak id<CWVAutofillDataManagerObserver>)observer {
  [_observers removeObject:observer];
}

- (void)fetchProfilesWithCompletionHandler:
    (void (^)(NSArray<CWVAutofillProfile*>* profiles))completionHandler {
  // If data is already loaded, return the existing data asynchronously to match
  // client expectation. Otherwise, save the |completionHandler| and wait for
  // |personalDataDidChange| to be invoked.
  if (_personalDataManager->IsDataLoaded()) {
    NSArray<CWVAutofillProfile*>* profiles = [self profiles];
    web::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, base::BindOnce(^{
                                               completionHandler(profiles);
                                             }));
  } else {
    [_fetchProfilesCompletionHandlers addObject:completionHandler];
  }
}

- (void)updateProfile:(CWVAutofillProfile*)profile {
  _personalDataManager->address_data_manager().UpdateProfile(
      *profile.internalProfile);
}

- (void)deleteProfile:(CWVAutofillProfile*)profile {
  _personalDataManager->address_data_manager().RemoveProfile(
      profile.internalProfile->guid());
}

- (UIImage*)fetchIconForCreditCard:(CWVCreditCard*)creditCard {
  // Check if custom card art is available.
  GURL cardArtURL = _personalDataManager->payments_data_manager().GetCardArtURL(
      *creditCard.internalCard);
  if (!cardArtURL.is_empty() && cardArtURL.is_valid()) {
    if (const gfx::Image* image =
            _personalDataManager->payments_data_manager()
                .GetCachedCardArtImageForUrl(cardArtURL)) {
      return image->ToUIImage();
    }
  }

  // Otherwise, try to get the default card icon
  autofill::Suggestion::Icon icon =
      creditCard.internalCard->CardIconForAutofillSuggestion();
  return icon == autofill::Suggestion::Icon::kNoIcon
             ? nil
             : ui::ResourceBundle::GetSharedInstance()
                   .GetNativeImageNamed(
                       autofill::CreditCard::IconResourceId(icon))
                   .ToUIImage();
}

- (void)fetchCreditCardsWithCompletionHandler:
    (void (^)(NSArray<CWVCreditCard*>* creditCards))completionHandler {
  // If data is already loaded, return the existing data asynchronously to match
  // client expectation. Otherwise, save the |completionHandler| and wait for
  // |personalDataDidChange| to be invoked.
  if (_personalDataManager->IsDataLoaded()) {
    NSArray<CWVCreditCard*>* creditCards = [self creditCards];
    web::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, base::BindOnce(^{
                                               completionHandler(creditCards);
                                             }));
  } else {
    [_fetchCreditCardsCompletionHandlers addObject:completionHandler];
  }
}

- (void)fetchPasswordsWithCompletionHandler:
    (void (^)(NSArray<CWVPassword*>* passwords))completionHandler {
  [_fetchPasswordsCompletionHandlers addObject:completionHandler];

  // Fetch is already pending.
  if (_passwordStoreConsumer) {
    return;
  }

  _passwordStoreConsumer.reset(
      new ios_web_view::WebViewPasswordStoreConsumer(self));
  _passwordStore->GetAllLogins(_passwordStoreConsumer->GetWeakPtr());
}

- (void)updatePassword:(CWVPassword*)password
           newUsername:(nullable NSString*)newUsername
           newPassword:(nullable NSString*)newPassword
             timestamp:(NSDate*)timestamp {
  password_manager::PasswordForm* passwordForm =
      [password internalPasswordForm];
  passwordForm->date_password_modified = base::Time::FromNSDate(timestamp);

  // Only change the password if it actually changed and not empty.
  if (newPassword && newPassword.length > 0 &&
      ![newPassword isEqualToString:password.password]) {
    passwordForm->password_value = base::SysNSStringToUTF16(newPassword);
  }

  // Because a password's primary key depends on its username, changing the
  // username requires that |UpdateLoginWithPrimaryKey| is called instead.
  if (newUsername && newUsername.length > 0 &&
      ![newUsername isEqualToString:password.username]) {
    // Make a local copy of the old password before updating it.
    auto oldPasswordForm = *passwordForm;
    passwordForm->username_value = base::SysNSStringToUTF16(newUsername);
    auto newPasswordForm = *passwordForm;
    _passwordStore->UpdateLoginWithPrimaryKey(newPasswordForm, oldPasswordForm);
  } else {
    _passwordStore->UpdateLogin(*passwordForm);
  }
}

- (void)deletePassword:(CWVPassword*)password {
  _passwordStore->RemoveLogin(FROM_HERE, *[password internalPasswordForm]);
}

- (void)addNewPasswordForUsername:(NSString*)username
                         password:(NSString*)password
                             site:(NSString*)site
                        timestamp:(NSDate*)timestamp {
  password_manager::PasswordForm form;

  DCHECK_GT(username.length, 0ul);
  DCHECK_GT(password.length, 0ul);
  GURL url(base::SysNSStringToUTF8(site));
  DCHECK(url.is_valid());

  form.url = password_manager_util::StripAuthAndParams(url);
  form.signon_realm = form.url.DeprecatedGetOriginAsURL().spec();
  form.username_value = base::SysNSStringToUTF16(username);
  form.password_value = base::SysNSStringToUTF16(password);
  form.date_created = base::Time::FromNSDate(timestamp);

  _passwordStore->AddLogin(form);
}

- (void)addNewPasswordForUsername:(NSString*)username
                serviceIdentifier:(NSString*)serviceIdentifier
               keychainIdentifier:(NSString*)keychainIdentifier
                        timestamp:(NSDate*)timestamp {
  password_manager::PasswordForm form;

  GURL url(base::SysNSStringToUTF8(serviceIdentifier));
  DCHECK(url.is_valid());

  form.url = password_manager_util::StripAuthAndParams(url);
  form.signon_realm = form.url.DeprecatedGetOriginAsURL().spec();
  form.username_value = base::SysNSStringToUTF16(username);
  form.keychain_identifier = base::SysNSStringToUTF8(keychainIdentifier);
  form.date_created = base::Time::FromNSDate(timestamp);

  _passwordStore->AddLogin(form);
}

#pragma mark - Private Methods

- (BOOL)isPasswordAffiliationEnabled {
  return _isPasswordAffiliationEnabled;
}

- (void)injectAffiliationAndBrandingInformationForForms:
            (PasswordFormList)passwordForms
                                         resultCallback:
                                             (PasswordFormListCallback)
                                                 completion {
  _affiliatedMatchHelper->InjectAffiliationAndBrandingInformation(
      passwordForms,
      base::BindOnce(&password_manager::GetLoginsOrEmptyListOnFailure)
          .Then(std::move(completion)));
}

- (void)matchAffiliationsAndUpdatePasswordsWithForms:
            (PasswordFormList)passwordForms
                                      resultCallback:
                                          (PasswordFormListCallback)completion {
  // Convert forms to Facets to fetch affilation data.
  std::vector<affiliations::FacetURI> facets;
  facets.reserve(passwordForms.size());
  for (const auto& form : passwordForms) {
    facets.emplace_back(affiliations::FacetURI::FromPotentiallyInvalidSpec(
        GetFacetRepresentation(form)));
  }

  __weak __typeof(self) weakSelf = self;
  auto updateAffiliationsCallback = base::BindOnce(
      ^(PasswordFormListCallback innerCompletion) {
        [weakSelf
            injectAffiliationAndBrandingInformationForForms:passwordForms
                                             resultCallback:
                                                 std::move(innerCompletion)];
      },
      std::move(completion));

  _affiliationsService->UpdateAffiliationsAndBranding(
      facets, std::move(updateAffiliationsCallback));
}

- (void)handlePasswordStoreLoginsAdded:(NSArray<CWVPassword*>*)added
                               updated:(NSArray<CWVPassword*>*)updated
                               removed:(NSArray<CWVPassword*>*)removed {
  for (id<CWVAutofillDataManagerObserver> observer in _observers) {
    [observer autofillDataManager:self
        didChangePasswordsByAdding:added
                          updating:updated
                          removing:removed];
  }
}

- (void)handlePasswordStoreResults:(NSArray<CWVPassword*>*)passwords {
  for (CWVFetchPasswordsCompletionHandler completionHandler in
           _fetchPasswordsCompletionHandlers) {
    completionHandler(passwords);
  }
  [_fetchPasswordsCompletionHandlers removeAllObjects];
  _passwordStoreConsumer.reset();
}

- (void)personalDataDidChange {
  // Invoke completionHandlers if they are still outstanding.
  if (_personalDataManager->IsDataLoaded()) {
    if (_fetchProfilesCompletionHandlers.count > 0) {
      NSArray<CWVAutofillProfile*>* profiles = [self profiles];
      for (CWVFetchProfilesCompletionHandler completionHandler in
               _fetchProfilesCompletionHandlers) {
        completionHandler(profiles);
      }
      [_fetchProfilesCompletionHandlers removeAllObjects];
    }
    if (_fetchCreditCardsCompletionHandlers.count > 0) {
      NSArray<CWVCreditCard*>* creditCards = [self creditCards];
      for (CWVFetchCreditCardsCompletionHandler completionHandler in
               _fetchCreditCardsCompletionHandlers) {
        completionHandler(creditCards);
      }
      [_fetchCreditCardsCompletionHandlers removeAllObjects];
    }
  }
  for (id<CWVAutofillDataManagerObserver> observer in _observers) {
    [observer autofillDataManagerDataDidChange:self];
  }
}

- (NSArray<CWVAutofillProfile*>*)profiles {
  NSMutableArray* profiles = [NSMutableArray array];
  for (const autofill::AutofillProfile* internalProfile :
       _personalDataManager->address_data_manager().GetProfiles()) {
    CWVAutofillProfile* profile =
        [[CWVAutofillProfile alloc] initWithProfile:*internalProfile];
    [profiles addObject:profile];
  }
  return [profiles copy];
}

- (NSArray<CWVCreditCard*>*)creditCards {
  NSMutableArray* creditCards = [NSMutableArray array];
  for (const autofill::CreditCard* internalCard :
       _personalDataManager->payments_data_manager().GetCreditCards()) {
    CWVCreditCard* creditCard =
        [[CWVCreditCard alloc] initWithCreditCard:*internalCard];
    [creditCards addObject:creditCard];
  }
  return [creditCards copy];
}

- (void)shutDown {
  _personalDataManager->RemoveObserver(
      _personalDataManagerObserverBridge.get());
  _passwordStore->RemoveObserver(_passwordStoreObserver.get());
}

@end