chromium/chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_controller.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_controller.h"

#include <memory>
#include <string>

#include "base/check.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/keyboard_accessory/android/manual_filling_controller.h"
#include "chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_bridge.h"
#include "chrome/browser/touch_to_fill/password_manager/password_generation/android/touch_to_fill_password_generation_controller.h"
#include "components/autofill/core/common/password_generation_util.h"
#include "components/password_manager/content/browser/content_password_manager_driver.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"

using autofill::password_generation::PasswordGenerationType;
using password_manager::metrics_util::GenerationDialogChoice;
using ShouldShowAction = ManualFillingController::ShouldShowAction;

TouchToFillPasswordGenerationController::
    ~TouchToFillPasswordGenerationController() {
  HideTouchToFill();
  RemoveSuppressShowingImeCallback();
}

TouchToFillPasswordGenerationController::
    TouchToFillPasswordGenerationController(
        base::WeakPtr<password_manager::ContentPasswordManagerDriver>
            frame_driver,
        content::WebContents* web_contents,
        PasswordGenerationElementData generation_element_data,
        std::unique_ptr<TouchToFillPasswordGenerationBridge> bridge,
        OnDismissedCallback on_dismissed_callback,
        base::WeakPtr<ManualFillingController> manual_filling_controller)
    : frame_driver_(frame_driver),
      web_contents_(web_contents),
      generation_element_data_(std::move(generation_element_data)),
      bridge_(std::move(bridge)),
      on_dismissed_callback_(std::move(on_dismissed_callback)),
      manual_filling_controller_(manual_filling_controller) {
  CHECK(bridge_);
  CHECK(on_dismissed_callback_);
  suppress_showing_ime_callback_ = base::BindRepeating([]() {
    // This controller exists only while the TTF is being shown, so
    // always suppress the keyboard.
    return true;
  });
}

bool TouchToFillPasswordGenerationController::ShowTouchToFill(
    std::string account_display_name,
    PasswordGenerationType type,
    PrefService* pref_service) {
  password_generation_type_ = type;

  std::u16string generated_password =
      frame_driver_->GetPasswordGenerationHelper()->GeneratePassword(
          web_contents_->GetLastCommittedURL().DeprecatedGetOriginAsURL(), type,
          generation_element_data_.form_signature,
          generation_element_data_.field_signature,
          generation_element_data_.max_password_length);
  if (!bridge_->Show(web_contents_, pref_service, this,
                     std::move(generated_password),
                     std::move(account_display_name))) {
    return false;
  }

  AddSuppressShowingImeCallback();
  return true;
}

void TouchToFillPasswordGenerationController::HideTouchToFill() {
  bridge_->Hide();
}

void TouchToFillPasswordGenerationController::OnDismissed(
    bool generated_password_accepted) {
  GenerationDialogChoice choice = generated_password_accepted
                                      ? GenerationDialogChoice::kAccepted
                                      : GenerationDialogChoice::kRejected;
  password_manager::metrics_util::LogGenerationDialogChoice(
      choice, password_generation_type_);

  if (on_dismissed_callback_) {
    std::exchange(on_dismissed_callback_, base::NullCallback()).Run();
  }
}

void TouchToFillPasswordGenerationController::OnGeneratedPasswordAccepted(
    const std::u16string& password) {
  frame_driver_->GeneratedPasswordAccepted(
      generation_element_data_.form_data,
      generation_element_data_.generation_element_id, password);
  frame_driver_->FocusNextFieldAfterPasswords();
}

void TouchToFillPasswordGenerationController::OnGeneratedPasswordRejected() {
  // TODO (crbug.com/1495639) Trigger Keyboard Accessory here.
}

void TouchToFillPasswordGenerationController::AddSuppressShowingImeCallback() {
  if (suppress_showing_ime_callback_added_) {
    return;
  }
  frame_driver_->render_frame_host()
      ->GetRenderWidgetHost()
      ->AddSuppressShowingImeCallback(suppress_showing_ime_callback_);
  suppress_showing_ime_callback_added_ = true;
}

void TouchToFillPasswordGenerationController::
    RemoveSuppressShowingImeCallback() {
  if (!suppress_showing_ime_callback_added_) {
    return;
  }
  if (frame_driver_) {
    frame_driver_->render_frame_host()
        ->GetRenderWidgetHost()
        ->RemoveSuppressShowingImeCallback(suppress_showing_ime_callback_,
                                           /*trigger_ime=*/false);
  }
  suppress_showing_ime_callback_added_ = false;
}