chromium/chrome/browser/keyboard_accessory/android/password_accessory_controller_impl.h

// 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.

#ifndef CHROME_BROWSER_KEYBOARD_ACCESSORY_ANDROID_PASSWORD_ACCESSORY_CONTROLLER_IMPL_H_
#define CHROME_BROWSER_KEYBOARD_ACCESSORY_ANDROID_PASSWORD_ACCESSORY_CONTROLLER_IMPL_H_

#include <memory>
#include <string>

#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/types/optional_ref.h"
#include "chrome/browser/keyboard_accessory/android/accessory_sheet_data.h"
#include "chrome/browser/keyboard_accessory/android/affiliated_plus_profiles_provider.h"
#include "chrome/browser/keyboard_accessory/android/password_accessory_controller.h"
#include "chrome/browser/password_manager/android/all_passwords_bottom_sheet_helper.h"
#include "components/autofill/core/common/mojom/autofill_types.mojom-forward.h"
#include "components/autofill/core/common/password_generation_util.h"
#include "components/password_manager/core/browser/credential_cache.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/security_state/core/security_state.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "ui/gfx/native_widget_types.h"

class ManualFillingController;
class AllPasswordsBottomSheetController;
class Profile;

namespace plus_addresses {
class AllPlusAddressesBottomSheetController;
class PlusAddressService;
}  // namespace plus_addresses

// Use either PasswordAccessoryController::GetOrCreate or
// PasswordAccessoryController::GetIfExisting to obtain instances of this class.
// This class exists for every tab and should never store state based on the
// contents of one of its frames. This can cause cross-origin hazards.
class PasswordAccessoryControllerImpl
    : public PasswordAccessoryController,
      public AffiliatedPlusProfilesProvider::Observer,
      public content::WebContentsObserver,
      public content::WebContentsUserData<PasswordAccessoryControllerImpl> {
 public:
  using PasswordDriverSupplierForFocusedFrame =
      base::RepeatingCallback<password_manager::PasswordManagerDriver*(
          content::WebContents*)>;
  using ShowMigrationWarningCallback = base::RepeatingCallback<void(
      gfx::NativeWindow,
      Profile*,
      password_manager::metrics_util::PasswordMigrationWarningTriggers)>;

  PasswordAccessoryControllerImpl(const PasswordAccessoryControllerImpl&) =
      delete;
  PasswordAccessoryControllerImpl& operator=(
      const PasswordAccessoryControllerImpl&) = delete;

  ~PasswordAccessoryControllerImpl() override;

  // AccessoryController:
  void RegisterFillingSourceObserver(FillingSourceObserver observer) override;
  std::optional<autofill::AccessorySheetData> GetSheetData() const override;
  void OnFillingTriggered(
      autofill::FieldGlobalId focused_field_id,
      const autofill::AccessorySheetField& selection) override;
  void OnPasskeySelected(const std::vector<uint8_t>& passkey_id) override;
  void OnOptionSelected(autofill::AccessoryAction selected_action) override;
  void OnToggleChanged(autofill::AccessoryAction toggled_action,
                       bool enabled) override;

  // PasswordAccessoryController:
  void RegisterPlusProfilesProvider(
      base::WeakPtr<AffiliatedPlusProfilesProvider> provider) override;
  void RefreshSuggestionsForField(
      autofill::mojom::FocusedFieldType focused_field_type) override;
  void OnGenerationRequested(
      autofill::password_generation::PasswordGenerationType type) override;
  void UpdateCredManReentryUi(
      autofill::mojom::FocusedFieldType focused_field_type) override;
  base::WeakPtr<PasswordAccessoryController> AsWeakPtr() override;

  // Like |CreateForWebContents|, it creates the controller and attaches it to
  // the given |web_contents|. Upon creation, a |credential_cache| is required
  // that will be queried for credentials.
  static void CreateForWebContents(
      content::WebContents* web_contents,
      password_manager::CredentialCache* credential_cache);

  // Like |CreateForWebContents|, it creates the controller and attaches it to
  // the given |web_contents|. Additionally, it allows inject a manual filling
  // controller and a |PasswordManagerClient|.
  static void CreateForWebContentsForTesting(
      content::WebContents* web_contents,
      password_manager::CredentialCache* credential_cache,
      base::WeakPtr<ManualFillingController> manual_filling_controller,
      password_manager::PasswordManagerClient* password_client,
      PasswordDriverSupplierForFocusedFrame driver_supplier,
      ShowMigrationWarningCallback show_migration_warning_callback);

  // Returns true if the current site attached to `web_contents_` has a SECURE
  // security level.
  bool IsSecureSite() const;

#if defined(UNIT_TEST)
  // Used for testing to set `security_level_for_testing_`.
  void SetSecurityLevelForTesting(
      security_state::SecurityLevel security_level) {
    security_level_for_testing_ = security_level;
  }
#endif
 protected:
  // This constructor can also be used by |CreateForWebContentsForTesting|
  // to inject a fake |ManualFillingController| and a fake
  // |PasswordManagerClient|.
  PasswordAccessoryControllerImpl(
      content::WebContents* web_contents,
      password_manager::CredentialCache* credential_cache,
      base::WeakPtr<ManualFillingController> manual_filling_controller,
      password_manager::PasswordManagerClient* password_client,
      PasswordDriverSupplierForFocusedFrame driver_supplier,
      ShowMigrationWarningCallback show_migration_warning_callback);

 private:
  friend class content::WebContentsUserData<PasswordAccessoryControllerImpl>;

  // This struct is used to remember the meta information about the last focused
  // field.
  struct LastFocusedFieldInfo {
    LastFocusedFieldInfo(url::Origin focused_origin,
                         autofill::mojom::FocusedFieldType focused_field,
                         bool manual_generation_available);

    // Records the origin at the time of focusing the field to double-check that
    // the frame origin hasn't changed.
    url::Origin origin;

    // Records the last focused field type to infer whether the accessory is
    // available and whether passwords or usernames will be fillable.
    autofill::mojom::FocusedFieldType focused_field_type =
        autofill::mojom::FocusedFieldType::kUnknown;

    // If true, manual generation will be available for the focused field.
    bool is_manual_generation_available = false;
  };

  // WebContentsObserver:
  void WebContentsDestroyed() override;

  // Constructs a vector of available manual fallback actions subject to
  // enabled features and available user data.
  std::vector<autofill::FooterCommand> CreateManagePasswordsFooter() const;

  // Enables or disables saving for the focused origin. This involves removing
  // or adding blocklisted entry in the |PasswordStore|.
  void ChangeCurrentOriginSavePasswordsStatus(bool enabled);

  // Returns true if |suggestion| matches a credential for |origin|.
  bool AppearsInSuggestions(const std::u16string& suggestion,
                            bool is_password,
                            const url::Origin& origin) const;

  // Returns true if the `origin` of a focused field allows to show
  // the option toggle to recover from a "never save" state.
  bool ShouldShowRecoveryToggle(const url::Origin& origin) const;

  // Lazy-initializes and returns the ManualFillingController for the current
  // |web_contents_|. The lazy initialization allows injecting mocks for tests.
  base::WeakPtr<ManualFillingController> GetManualFillingController();

  // Instructs |AllPasswordsBottomSheetController| to show all passwords.
  void ShowAllPasswords();

  url::Origin GetFocusedFrameOrigin() const;

  // Returns true if authentication should be triggered before filling
  // |selection| in to the field.
  bool ShouldTriggerBiometricReauth(
      const autofill::AccessorySheetField& selection) const;

  // Called when the biometric authentication completes. If |auth_succeeded| is
  // true, |selection| will be passed on to be filled.
  void OnReauthCompleted(autofill::AccessorySheetField selection,
                         bool auth_succeeded);

  // Sends |selection| to the renderer to be filled, if it's a valid
  // entry for the origin of the frame that is currently focused.
  void FillSelection(const autofill::AccessorySheetField& selection);

  // Called From |AllPasswordsBottomSheetController| when
  // the Bottom Sheet view is destroyed.
  void AllPasswordsSheetDismissed();

  // Fills `plus_address` into the currently focused field. Called when the
  // manually triggered plus address creation bottom sheet is accepted by the
  // user.
  void OnPlusAddressCreated(const std::string& plus_address);

  // Triggers the filling `plus_address` into the currently focused field.
  void OnPlusAddressSelected(
      base::optional_ref<const std::string> plus_address);

  // Fetches suggestions and propagates them to the frontend.
  void RefreshSuggestions();

  // AffiliatedPlusProfilesProvider::Observer:
  void OnAffiliatedPlusProfilesFetched() override;

  content::WebContents& GetWebContents() const;

  // Keeps track of credentials which are stored for all origins in this tab.
  const raw_ptr<password_manager::CredentialCache> credential_cache_;

  // The password accessory controller object to forward client requests to.
  base::WeakPtr<ManualFillingController> manual_filling_controller_;

  // The plus profiles provider that is used to generate the plus profiles
  // section for the frontend.
  base::WeakPtr<AffiliatedPlusProfilesProvider> plus_profiles_provider_;

  // The password manager client is used to update the save passwords status
  // for the currently focused origin.
  const raw_ptr<password_manager::PasswordManagerClient> password_client_;

  // The authenticator used to trigger a biometric re-auth before filling.
  // null, if there is no ongoing authentication.
  std::unique_ptr<device_reauth::DeviceAuthenticator> authenticator_;

  // Information about the currently focused field. This is the only place
  // allowed to store frame-specific data. If a new field is focused or focus is
  // lost, this data needs to be reset to std::nullopt to make sure that data
  // related to a former frame isn't displayed incorrectly in a different one.
  std::optional<LastFocusedFieldInfo> last_focused_field_info_ = std::nullopt;

  // The observer to notify if available suggestions change.
  FillingSourceObserver source_observer_;

  // Callback that returns a |PasswordManagerDriver| corresponding to the
  // currently-focused frame of the passed-in |WebContents|.
  PasswordDriverSupplierForFocusedFrame driver_supplier_;

  // Controller for the all passwords bottom sheet. Created on demand during the
  // first call to |ShowAllPasswords()|.
  std::unique_ptr<AllPasswordsBottomSheetController>
      all_passords_bottom_sheet_controller_;

  // Helper for determining whether a bottom sheet showing passwords is useful.
  AllPasswordsBottomSheetHelper all_passwords_helper_{
      password_client_->GetProfilePasswordStore(),
      password_client_->GetAccountPasswordStore()};

  // Security level used for testing only.
  security_state::SecurityLevel security_level_for_testing_ =
      security_state::NONE;

  // Callback attempting to display the migration warning when invoked.
  // Used to facilitate injecting a mock bridge in tests.
  ShowMigrationWarningCallback show_migration_warning_callback_;

  const raw_ptr<const plus_addresses::PlusAddressService> plus_address_service_;

  std::unique_ptr<plus_addresses::AllPlusAddressesBottomSheetController>
      all_plus_addresses_bottom_sheet_controller_;

  base::WeakPtrFactory<PasswordAccessoryControllerImpl> weak_ptr_factory_{this};

  WEB_CONTENTS_USER_DATA_KEY_DECL();
};

#endif  // CHROME_BROWSER_KEYBOARD_ACCESSORY_ANDROID_PASSWORD_ACCESSORY_CONTROLLER_IMPL_H_