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

#include "components/autofill/content/renderer/password_generation_agent.h"

#include <algorithm>
#include <memory>
#include <utility>
#include <vector>

#include "base/check_op.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/task/single_thread_task_runner.h"
#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/password_autofill_agent.h"
#include "components/autofill/content/renderer/password_form_conversion_utils.h"
#include "components/autofill/content/renderer/synchronous_form_cache.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/password_form_generation_data.h"
#include "components/autofill/core/common/password_generation_util.h"
#include "components/autofill/core/common/signatures.h"
#include "content/public/renderer/render_frame.h"
#include "google_apis/gaia/gaia_urls.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/platform/web_security_origin.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_form_control_element.h"
#include "third_party/blink/public/web/web_form_element.h"
#include "third_party/blink/public/web/web_input_element.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_view.h"
#include "ui/gfx/geometry/rect.h"

using blink::WebAutofillState;
using blink::WebDocument;
using blink::WebFormControlElement;
using blink::WebFormElement;
using blink::WebInputElement;
using blink::WebLocalFrame;

using enum blink::mojom::FormControlType;

namespace autofill {

namespace {

using ::autofill::form_util::GetTextDirectionForElement;
using Logger = ::autofill::SavePasswordProgressLogger;

// Returns the renderer id of the next password field in |control_elements|
// after |new_password|. This field is likely to be the confirmation field.
// Returns a null renderer ID if there is no such field.
FieldRendererId FindConfirmationPasswordFieldId(
    const std::vector<WebFormControlElement>& control_elements,
    const WebFormControlElement& new_password) {
  auto iter = std::ranges::find(control_elements, new_password);

  if (iter == control_elements.end())
    return FieldRendererId();

  ++iter;
  for (; iter != control_elements.end(); ++iter) {
    const WebInputElement input_element = iter->DynamicTo<WebInputElement>();
    if (input_element &&
        input_element.FormControlTypeForAutofill() == kInputPassword) {
      return form_util::GetFieldRendererId(input_element);
    }
  }
  return FieldRendererId();
}


void PreviewGeneratedValue(WebInputElement& input_element,
                           const blink::WebString& value) {
  input_element.SetShouldRevealPassword(true);
  input_element.SetSuggestedValue(value);
}

void ClearPreviewedValue(WebInputElement& input_element) {
  input_element.SetShouldRevealPassword(false);
  input_element.SetSuggestedValue(blink::WebString());
}

// Mirrors the value of `element` to all other `elements` and updates their
// autofill state to `Autofilled`.
void CopyElementValueToOtherInputElements(
    const blink::WebInputElement& element,
    base::span<blink::WebInputElement> elements) {
  for (blink::WebInputElement& e : elements) {
    if (element != e) {
      e.SetAutofillValue(element.Value());
    }
    e.SetAutofillState(blink::WebAutofillState::kAutofilled);
  }
}

}  // namespace

// During prerendering, we do not want the renderer to send messages to the
// corresponding driver. Since we use a channel associated interface, we still
// need to set up the mojo connection as before (i.e., we can't defer binding
// the interface). Instead, we enqueue our messages here as post-activation
// tasks. See post-prerendering activation steps here:
// https://wicg.github.io/nav-speculation/prerendering.html#prerendering-bcs-subsection
class PasswordGenerationAgent::DeferringPasswordGenerationDriver
    : public mojom::PasswordGenerationDriver {
 public:
  explicit DeferringPasswordGenerationDriver(PasswordGenerationAgent* agent)
      : agent_(agent) {}
  ~DeferringPasswordGenerationDriver() override = default;

 private:
  template <typename F, typename... Args>
  void SendMsg(F fn, Args&&... args) {
    if (auto* driver = agent_->unsafe_driver()) {
      DCHECK(!agent_->IsPrerendering());
      DCHECK_NE(driver, this);
      (driver->*fn)(std::forward<Args>(args)...);
    }
  }
  template <typename F, typename... Args>
  void DeferMsg(F fn, Args... args) {
    if (auto* render_frame = agent_->unsafe_render_frame()) {
      DCHECK(agent_->IsPrerendering());
      render_frame->GetWebFrame()
          ->GetDocument()
          .AddPostPrerenderingActivationStep(base::BindOnce(
              &DeferringPasswordGenerationDriver::SendMsg<F, Args...>,
              weak_ptr_factory_.GetWeakPtr(), fn, std::forward<Args>(args)...));
    }
  }
  void AutomaticGenerationAvailable(
      const password_generation::PasswordGenerationUIData&
          password_generation_ui_data) override {
    DeferMsg(&mojom::PasswordGenerationDriver::AutomaticGenerationAvailable,
             password_generation_ui_data);
  }
  void PresaveGeneratedPassword(const FormData& form_data,
                                const std::u16string& password_value) override {
    DeferMsg(&mojom::PasswordGenerationDriver::PresaveGeneratedPassword,
             form_data, password_value);
  }
  void PasswordNoLongerGenerated(const FormData& form_data) override {
    DeferMsg(&mojom::PasswordGenerationDriver::PasswordNoLongerGenerated,
             form_data);
  }
#if !BUILDFLAG(IS_ANDROID)
  void ShowPasswordEditingPopup(const gfx::RectF& bounds,
                                const FormData& form_data,
                                FieldRendererId field_renderer_id,
                                const std::u16string& password_value) override {
    DeferMsg(&mojom::PasswordGenerationDriver::ShowPasswordEditingPopup, bounds,
             form_data, field_renderer_id, password_value);
  }
  void PasswordGenerationRejectedByTyping() override {
    DeferMsg(
        &mojom::PasswordGenerationDriver::PasswordGenerationRejectedByTyping);
  }
  void FrameWasScrolled() override {
    DeferMsg(&mojom::PasswordGenerationDriver::FrameWasScrolled);
  }
  void GenerationElementLostFocus() override {
    DeferMsg(&mojom::PasswordGenerationDriver::GenerationElementLostFocus);
  }
#endif  // !BUILDFLAG(IS_ANDROID)

  raw_ptr<PasswordGenerationAgent> agent_ = nullptr;
  base::WeakPtrFactory<DeferringPasswordGenerationDriver> weak_ptr_factory_{
      this};
};

// Contains information about generation status for an element for the
// lifetime of the possible interaction.
struct PasswordGenerationAgent::GenerationItemInfo {
  GenerationItemInfo(WebInputElement generation_element,
                     FormData form_data,
                     std::vector<blink::WebInputElement> password_elements)
      : generation_element(std::move(generation_element)),
        form_data(std::move(form_data)),
        password_elements(std::move(password_elements)) {}

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

  ~GenerationItemInfo() = default;

  // Element where we want to trigger password generation UI.
  blink::WebInputElement generation_element;

  // FormData for the generation element.
  FormData form_data;

  // Password elements (new password only or both new password and
  // confirmation password) in the form.
  std::vector<blink::WebInputElement> password_elements;

  // If the password field at |generation_element_| contains a generated
  // password.
  bool password_is_generated = false;

  // True if the last password generation was manually triggered.
  bool is_manually_triggered = false;

  // True if a password was generated and the user edited it. Used for UMA
  // stats.
  bool password_edited = false;

  // True if the user was editing a generated password, left that state by
  // removing below `kMinimumLengthForEditedPassword` characters, but did not
  // change focus or delete the password fully (in that case the field should
  // remain revealed).
  bool password_revealed_after_editing = false;

  // True if the generation popup was shown during this navigation. Used to
  // track UMA stats per page visit rather than per display, since the former
  // is more interesting.
  // TODO(crbug.com/40577440): Remove this or change the description of the
  // logged event as calling AutomaticgenerationStatusChanged will no longer
  // imply that a popup is shown. This could instead be logged with the
  // metrics collected on the browser process.
  bool generation_popup_shown = false;

  // True if the editing popup was shown during this navigation. Used to track
  // UMA stats per page rather than per display, since the former is more
  // interesting.
  bool editing_popup_shown = false;

  // True when PasswordGenerationAgent updates other password fields on the page
  // due to the generated password being edited. It's used to suppress the fake
  // blur events coming from there.
  bool updating_other_password_fields = false;

  // True if the user explicitly rejected the generation by clicking the cancel
  // button. In this case, the generation popup should not be shown again in
  // this navigation unless explicitly triggered from the context menu.
  bool generation_rejected = false;
};

PasswordGenerationAgent::PasswordGenerationAgent(
    content::RenderFrame* render_frame,
    PasswordAutofillAgent* password_agent,
    blink::AssociatedInterfaceRegistry* registry)
    : content::RenderFrameObserver(render_frame),
      mark_generation_element_(
          base::CommandLine::ForCurrentProcess()->HasSwitch(
              switches::kShowAutofillSignatures)),
      password_agent_(password_agent) {
  registry->AddInterface<mojom::PasswordGenerationAgent>(base::BindRepeating(
      &PasswordGenerationAgent::BindPendingReceiver, base::Unretained(this)));
  password_agent_->SetPasswordGenerationAgent(this);
}

PasswordGenerationAgent::~PasswordGenerationAgent() {
  // Reset the pointer to `this` to avoid its dangling.
  password_agent_->SetPasswordGenerationAgent(nullptr);
}

void PasswordGenerationAgent::BindPendingReceiver(
    mojo::PendingAssociatedReceiver<mojom::PasswordGenerationAgent>
        pending_receiver) {
  receiver_.Bind(std::move(pending_receiver));
}

void PasswordGenerationAgent::DidCommitProvisionalLoad(
    ui::PageTransition transition) {
  // Update stats for primary main frame navigation.
  if (auto* frame = unsafe_render_frame();
      frame && frame->GetWebFrame()->IsOutermostMainFrame()) {
    auto [current_generation_item, auto_protect] =
        current_generation_item_.GetAndProtect();
    if (current_generation_item) {
      if (current_generation_item->password_edited) {
        password_generation::LogPasswordGenerationEvent(
            password_generation::PASSWORD_EDITED);
      }
      if (current_generation_item->generation_popup_shown) {
        password_generation::LogPasswordGenerationEvent(
            password_generation::GENERATION_POPUP_SHOWN);
      }
      if (current_generation_item->editing_popup_shown) {
        password_generation::LogPasswordGenerationEvent(
            password_generation::EDITING_POPUP_SHOWN);
      }
    }
  }

  // Safe because DidCommitProvisionalLoad() is not called reentrantly.
  current_generation_item_.CheckedSet(nullptr);
  generation_enabled_fields_.clear();
}

void PasswordGenerationAgent::DidChangeScrollOffset() {
#if !BUILDFLAG(IS_ANDROID)
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  auto* driver = unsafe_driver();
  if (!driver || !current_generation_item) {
    return;
  }
  driver->FrameWasScrolled();
#endif  // !BUILDFLAG(IS_ANDROID)
}

void PasswordGenerationAgent::OnDestruct() {
  receiver_.reset();
}

void PasswordGenerationAgent::OnFieldAutofilled(
    const WebInputElement& password_element) {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  if (current_generation_item &&
      current_generation_item->password_is_generated &&
      current_generation_item->generation_element == password_element) {
    password_generation::LogPasswordGenerationEvent(
        password_generation::PASSWORD_DELETED_BY_AUTOFILLING);
    PasswordNoLongerGenerated();
    current_generation_item->generation_element.SetShouldRevealPassword(false);
  }
}

bool PasswordGenerationAgent::ShouldIgnoreBlur() {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  return current_generation_item &&
         current_generation_item->updating_other_password_fields;
}

bool PasswordGenerationAgent::IsPrerendering() const {
  auto* frame = unsafe_render_frame();
  return frame && frame->GetWebFrame()->GetDocument().IsPrerendering();
}

void PasswordGenerationAgent::PreviewGenerationSuggestion(
    const std::u16string& password) {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  CHECK(current_generation_item);

  for (blink::WebInputElement& password_field :
       current_generation_item->password_elements) {
    PreviewGeneratedValue(password_field,
                          blink::WebString::FromUtf16(password));
  }
}

void PasswordGenerationAgent::ClearPreviewedForm() {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  if (!current_generation_item) {
    return;
  }

  for (blink::WebInputElement& password_field :
       current_generation_item->password_elements) {
    if (password_field.SuggestedValue().IsEmpty())
      continue;

    ClearPreviewedValue(password_field);
  }
}

void PasswordGenerationAgent::GeneratedPasswordAccepted(
    const std::u16string& password) {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  // Check that the navigation in between didn't reset the state.
  if (!current_generation_item) {
    return;
  }
  CHECK(!password.empty());
  CHECK_LE(kMinimumLengthForEditedPassword, password.size());
  current_generation_item->password_is_generated = true;
  current_generation_item->password_edited = false;
  password_generation::LogPasswordGenerationEvent(
      password_generation::PASSWORD_ACCEPTED);
  LogMessage(Logger::STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED);

  // Preview needs to be cleared before filling to be removed correctly.
  password_agent_->autofill_agent().ClearPreviewedForm();

  for (blink::WebInputElement& password_element :
       current_generation_item->password_elements) {
    base::AutoReset<bool> auto_reset_update_confirmation_password(
        &current_generation_item->updating_other_password_fields, true);
    password_element.SetAutofillValue(blink::WebString::FromUtf16(password));
    // crbug.com/1467893: JS can clear the generated password. In this case
    // consider filling unsuccessful and don't presave the password.
    if (password_element.Value().IsEmpty()) {
      return;
    }
    password_agent_->TrackAutofilledElement(password_element);
  }
  CHECK(std::ranges::contains(current_generation_item->password_elements,
                              current_generation_item->generation_element));

  std::optional<FormData> presaved_form_data = CreateFormDataToPresave(
      current_generation_item->generation_element, /*form_cache=*/{});
  const std::u16string generated_password =
      current_generation_item->generation_element.Value().Utf16();
  if (auto* driver = unsafe_driver(); driver && presaved_form_data) {
    CHECK(!generated_password.empty());
    driver->PresaveGeneratedPassword(*presaved_form_data, generated_password);
  }

  // Call UpdateStateForTextChange after the corresponding PasswordFormManager
  // is notified that the password was generated.
  for (const blink::WebInputElement& password_element :
       current_generation_item->password_elements) {
    // Needed to notify password_autofill_agent that the content of the field
    // has changed. Without this we will overwrite the generated
    // password with an Autofilled password when saving.
    // https://crbug.com/493455
    password_agent_->autofill_agent().UpdateStateForTextChange(
        password_element, FieldPropertiesFlags::kUserTyped, /*form_cache=*/{});
  }
}

void PasswordGenerationAgent::GeneratedPasswordRejected() {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  if (current_generation_item) {
    current_generation_item->generation_rejected = true;
  }
}

void PasswordGenerationAgent::FocusNextFieldAfterPasswords() {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  if (!current_generation_item) {
    return;
  }

  for (const WebInputElement& password_element :
       current_generation_item->password_elements) {
    if (auto* frame = unsafe_render_frame();
        frame && password_element == password_agent_->last_queried_element()
                                         .DynamicTo<WebInputElement>()) {
      frame->GetWebView()->AdvanceFocus(false);
    }
  }
}

std::optional<FormData> PasswordGenerationAgent::CreateFormDataToPresave(
    WebInputElement generation_element,
    const SynchronousFormCache& form_cache) {
  CHECK(!generation_element.IsNull());
  // Since the form for presaving should match a form in the browser, create it
  // with the same algorithm (to match html attributes, action, etc.).
  WebFormElement form = generation_element.GetOwningFormForAutofill();
  return form
             ? password_agent_->GetFormDataFromWebForm(form, form_cache)
             : password_agent_->GetFormDataFromUnownedInputElements(form_cache);
}

void PasswordGenerationAgent::FoundFormEligibleForGeneration(
    const PasswordFormGenerationData& form) {
  generation_enabled_fields_[form.new_password_renderer_id] = form;

  if (mark_generation_element_) {
    WebFormControlElement new_password_input =
        form_util::GetFormControlByRendererId(form.new_password_renderer_id);
    if (new_password_input) {
      // Mark the input element with renderer id
      // |form.new_password_renderer_id|.
      new_password_input.SetAttribute("password_creation_field", "1");
    }
  }
}

void PasswordGenerationAgent::TriggeredGeneratePassword(
    TriggeredGeneratePasswordCallback callback) {
  if (auto* frame = unsafe_render_frame();
      frame && SetUpTriggeredGeneration()) {
    LogMessage(Logger::STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP);
    auto [current_generation_item, auto_protect] =
        current_generation_item_.GetAndProtect();
    // If the field is not |type=password|, the list of suggestions
    // should not be populated with passwords to avoid filling them in a
    // clear-text field.
    // `FormControlTypeForAutofill()` is deliberately not used.
    bool is_generation_element_password_type =
        current_generation_item->generation_element
            .FormControlType()  // nocheck
        == kInputPassword;
    password_generation::PasswordGenerationUIData password_generation_ui_data(
        gfx::RectF(frame->ConvertViewportToWindow(
            current_generation_item->generation_element.BoundsInWidget())),
        current_generation_item->generation_element.MaxLength(),
        current_generation_item->generation_element.NameForAutofill().Utf16(),
        form_util::GetFieldRendererId(
            current_generation_item->generation_element),
        is_generation_element_password_type,
        GetTextDirectionForElement(current_generation_item->generation_element),
        current_generation_item->form_data,
        current_generation_item->generation_rejected);
    std::move(callback).Run(std::move(password_generation_ui_data));
    current_generation_item->generation_popup_shown = true;
  } else {
    std::move(callback).Run(std::nullopt);
  }
}

bool PasswordGenerationAgent::SetUpTriggeredGeneration() {
  const WebInputElement last_focused_password_element =
      password_agent_->last_queried_element().DynamicTo<WebInputElement>();
  if (!last_focused_password_element ||
      last_focused_password_element.IsReadOnly()) {
    return false;
  }

  FieldRendererId last_focused_password_element_id =
      form_util::GetFieldRendererId(last_focused_password_element);

  bool is_automatic_generation_available = false;
  auto it = generation_enabled_fields_.find(last_focused_password_element_id);

  if (it != generation_enabled_fields_.end()) {
    is_automatic_generation_available = true;
    MaybeCreateCurrentGenerationItem(
        last_focused_password_element,
        it->second.confirmation_password_renderer_id, /*form_cache=*/{});
  } else {
    auto* frame = unsafe_render_frame();
    blink::WebDocument document =
        frame ? frame->GetWebFrame()->GetDocument() : WebDocument();
    if (!document) {
      return false;
    }
    WebFormElement form =
        last_focused_password_element.GetOwningFormForAutofill();
    std::vector<WebFormControlElement> control_elements =
        form_util::GetOwnedAutofillableFormControls(document, form);

    MaybeCreateCurrentGenerationItem(
        last_focused_password_element,
        FindConfirmationPasswordFieldId(control_elements,
                                        last_focused_password_element),
        /*form_cache=*/{});
  }

  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  if (!current_generation_item) {
    return false;
  }

  if (current_generation_item->generation_element !=
      last_focused_password_element) {
    return false;
  }

  current_generation_item->is_manually_triggered =
      !is_automatic_generation_available;
  return true;
}

bool PasswordGenerationAgent::ShowPasswordGenerationSuggestions(
    const WebInputElement& element,
    const SynchronousFormCache& form_cache) {
  CHECK(element);

  auto it =
      generation_enabled_fields_.find(form_util::GetFieldRendererId(element));
  if (it != generation_enabled_fields_.end()) {
    MaybeCreateCurrentGenerationItem(
        element, it->second.confirmation_password_renderer_id, form_cache);
  }

  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  if (!current_generation_item ||
      element != current_generation_item->generation_element) {
    return false;
  }

  if (current_generation_item->password_is_generated) {
    size_t password_length =
        current_generation_item->generation_element.Value().length();
    if (password_length < kMinimumLengthForEditedPassword) {
      // Password is too short to be considered generated.
      PasswordNoLongerGenerated();
      if (password_length == 0) {
        current_generation_item->generation_element.SetShouldRevealPassword(
            false);
      }
      return MaybeOfferAutomaticGeneration();
    }
    current_generation_item->generation_element.SetShouldRevealPassword(true);
#if !BUILDFLAG(IS_ANDROID)
    ShowEditingPopup(form_cache);
#endif  // !BUILDFLAG(IS_ANDROID)
    return true;
  }

  // Assume that if the password field has less than
  // |kMaximumCharsForGenerationOffer| characters then the user is not finished
  // typing their password and display the password suggestion.
  if (!element.IsReadOnly() && element.IsEnabled() &&
      element.Value().length() <= kMaximumCharsForGenerationOffer) {
    return MaybeOfferAutomaticGeneration();
  }

  return false;
}

void PasswordGenerationAgent::DidEndTextFieldEditing(
    const blink::WebInputElement& element) {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  if (element && current_generation_item &&
      element == current_generation_item->generation_element) {
#if !BUILDFLAG(IS_ANDROID)
    if (auto* driver = unsafe_driver()) {
      driver->GenerationElementLostFocus();
    }
#endif  // !BUILDFLAG(IS_ANDROID)
    current_generation_item->password_revealed_after_editing = false;
    current_generation_item->generation_element.SetShouldRevealPassword(false);
  }
}

void PasswordGenerationAgent::TextFieldCleared(
    const blink::WebInputElement& element) {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  if (current_generation_item &&
      current_generation_item->generation_element == element) {
    if (current_generation_item->password_is_generated) {
      PasswordNoLongerGenerated();
    }
    current_generation_item->password_revealed_after_editing = false;
    current_generation_item->generation_element.SetShouldRevealPassword(false);
  }
}

bool PasswordGenerationAgent::TextDidChangeInTextField(
    const WebInputElement& element,
    const SynchronousFormCache& form_cache) {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  if (!current_generation_item ||
      current_generation_item->generation_element != element) {
    // Presave the username if it has been changed.
    if (current_generation_item &&
        current_generation_item->password_is_generated && element &&
        element.GetOwningFormForAutofill() ==
            current_generation_item->generation_element
                .GetOwningFormForAutofill()) {
      const std::u16string generated_password =
          current_generation_item->generation_element.Value().Utf16();
      if (generated_password.empty()) {
        // JS cleared the generated password in the meantime. Consider the user
        // left the generation state.
        PasswordNoLongerGenerated();
      } else {
        std::optional<FormData> presaved_form_data = CreateFormDataToPresave(
            current_generation_item->generation_element, form_cache);
        if (auto* driver = unsafe_driver(); driver && presaved_form_data) {
          driver->PresaveGeneratedPassword(*presaved_form_data,
                                           generated_password);
        }
      }
    }
    return false;
  }

  if (!current_generation_item->password_is_generated) {
    if (element.Value().length() == 0) {
      MaybeOfferAutomaticGeneration();
    } else {
      // User has rejected the feature and has started typing a password.
#if !BUILDFLAG(IS_ANDROID)
      if (auto* driver = unsafe_driver()) {
        driver->PasswordGenerationRejectedByTyping();
      }
#endif  // !BUILDFLAG(IS_ANDROID)
      // If the user is still modifying the field after leaving the editing
      // state without fully clearing, it should remain revealed.
      current_generation_item->generation_element.SetShouldRevealPassword(
          current_generation_item->password_revealed_after_editing);
    }
  } else {
    const bool leave_editing_state =
        current_generation_item->password_is_generated &&
        element.Value().length() < kMinimumLengthForEditedPassword;
    if (!current_generation_item->password_is_generated ||
        leave_editing_state) {
      // The call may pop up a generation prompt, replacing the editing prompt
      // if it was previously shown.
      MaybeOfferAutomaticGeneration();
    }
    if (leave_editing_state) {
      // Tell the browser that the state isn't "editing" anymore. The browser
      // should hide the editing prompt if it wasn't replaced above.
      current_generation_item->password_revealed_after_editing = true;
      PasswordNoLongerGenerated();
    } else if (current_generation_item->password_is_generated) {
      current_generation_item->password_edited = true;
      base::AutoReset<bool> auto_reset_update_confirmation_password(
          &current_generation_item->updating_other_password_fields, true);
      // Mirror edits to any confirmation password fields.
      CopyElementValueToOtherInputElements(
          element, current_generation_item->password_elements);
      // Even though `form_cache` is available, we previously ran
      // `CopyElementValueToOtherInputElements()` which triggers
      // `WebFormControlElement::SetValue()`, dispatching events that might
      // change the DOM. Therefore `form_cache` may be outdated.
      std::optional<FormData> presaved_form_data = CreateFormDataToPresave(
          current_generation_item->generation_element, /*form_cache=*/{});
      std::u16string generated_password =
          current_generation_item->generation_element.Value().Utf16();
      if (auto* driver = unsafe_driver(); driver && presaved_form_data) {
        CHECK(!generated_password.empty());
        driver->PresaveGeneratedPassword(*presaved_form_data,
                                         generated_password);
      }
    }

    // Notify `password_agent_` of text changes to the other confirmation
    // password fields.
    for (const blink::WebInputElement& password_element :
         current_generation_item->password_elements) {
      // `PasswordNoLongerGenerated()` and
      // `CopyElementValueToOtherInputElements()` both call
      // `SetAutofillValue()`, dispatching events that might modify the DOM,
      // making `form_cache` outdated, which is why it must not used in this
      // call.
      password_agent_->autofill_agent().UpdateStateForTextChange(
          password_element, FieldPropertiesFlags::kUserTyped,
          /*form_cache=*/{});
    }
  }
  return true;
}

bool PasswordGenerationAgent::MaybeOfferAutomaticGeneration() {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  // TODO(crbug.com/40580560): Add this check to the generation element class.
  if (current_generation_item->is_manually_triggered) {
    return false;
  }
  AutomaticGenerationAvailable();
  return true;
}

void PasswordGenerationAgent::AutomaticGenerationAvailable() {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  DCHECK(current_generation_item);
  DCHECK(current_generation_item->generation_element);

  LogMessage(Logger::STRING_GENERATION_RENDERER_AUTOMATIC_GENERATION_AVAILABLE);
  // If the field is not |type=password|, the list of suggestions
  // should not be populated with passwordS to avoid filling them in a
  // clear-text field.
  // `FormControlTypeForAutofill()` is deliberately not used.
  bool is_generation_element_password_type =
      current_generation_item->generation_element.FormControlType()  // nocheck
      == kInputPassword;
  if (!unsafe_render_frame() || !unsafe_driver()) {
    return;
  }
  password_generation::PasswordGenerationUIData password_generation_ui_data(
      gfx::RectF(unsafe_render_frame()->ConvertViewportToWindow(
          current_generation_item->generation_element.BoundsInWidget())),
      current_generation_item->generation_element.MaxLength(),
      current_generation_item->generation_element.NameForAutofill().Utf16(),
      form_util::GetFieldRendererId(
          current_generation_item->generation_element),
      is_generation_element_password_type,
      GetTextDirectionForElement(current_generation_item->generation_element),
      current_generation_item->form_data,
      current_generation_item->generation_rejected);
  current_generation_item->generation_popup_shown = true;
  unsafe_driver()->AutomaticGenerationAvailable(password_generation_ui_data);
}

#if !BUILDFLAG(IS_ANDROID)
void PasswordGenerationAgent::ShowEditingPopup(
    const SynchronousFormCache& form_cache) {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();

  if (!unsafe_render_frame()) {
    return;
  }
  gfx::RectF bounding_box(unsafe_render_frame()->ConvertViewportToWindow(
      current_generation_item->generation_element.BoundsInWidget()));

  std::optional<FormData> form_data = CreateFormDataToPresave(
      current_generation_item->generation_element, form_cache);
  DCHECK(form_data);

  FieldRendererId generation_element_renderer_id =
      form_util::GetFieldRendererId(
          current_generation_item->generation_element);
  std::u16string password_value =
      current_generation_item->generation_element.Value().Utf16();

  if (auto* driver = unsafe_driver()) {
    driver->ShowPasswordEditingPopup(bounding_box, *form_data,
                                     generation_element_renderer_id,
                                     password_value);
  }
  current_generation_item->editing_popup_shown = true;
}
#endif  // !BUILDFLAG(IS_ANDROID)

void PasswordGenerationAgent::PasswordNoLongerGenerated() {
  auto [current_generation_item, auto_protect] =
      current_generation_item_.GetAndProtect();
  DCHECK(current_generation_item);
  DCHECK(current_generation_item->password_is_generated);
  // Do not treat the password as generated, either here or in the browser.
  current_generation_item->password_is_generated = false;
  current_generation_item->password_edited = false;

  for (WebInputElement& password : current_generation_item->password_elements) {
    password.SetAutofillState(WebAutofillState::kNotFilled);
  }
  password_generation::LogPasswordGenerationEvent(
      password_generation::PASSWORD_DELETED);

  // Clear all other password fields.
  for (WebInputElement& element : current_generation_item->password_elements) {
    base::AutoReset<bool> auto_reset_update_confirmation_password(
        &current_generation_item->updating_other_password_fields, true);
    if (current_generation_item->generation_element != element) {
      element.SetAutofillValue(blink::WebString());
    }
  }

  // The above call to SetAutofillValue() dispatches events that may change the
  // DOM. Therefore, any cached FormData may be outdated, and we must not use a
  // form cache.
  std::optional<FormData> presaved_form_data = CreateFormDataToPresave(
      current_generation_item->generation_element, /*form_cache=*/{});
  if (auto* driver = unsafe_driver(); driver && presaved_form_data) {
    driver->PasswordNoLongerGenerated(*presaved_form_data);
  }
}

void PasswordGenerationAgent::MaybeCreateCurrentGenerationItem(
    WebInputElement generation_element,
    FieldRendererId confirmation_password_renderer_id,
    const SynchronousFormCache& form_cache) {
  std::optional<FormData> form_data;
  std::vector<blink::WebInputElement> passwords;

  {
    auto [current_generation_item, auto_protect] =
        current_generation_item_.GetAndProtect();
    // Do not create |current_generation_item| if it already is created for
    // |generation_element| or the user accepted generated password. So if the
    // user accepted the generated password, generation is not offered on any
    // other field.
    if (current_generation_item &&
        (current_generation_item->generation_element == generation_element ||
         current_generation_item->password_is_generated)) {
      return;
    }

    WebFormElement form_element = generation_element.GetOwningFormForAutofill();
    form_data =
        form_element
            ? password_agent_->GetFormDataFromWebForm(form_element, form_cache)
            : password_agent_->GetFormDataFromUnownedInputElements(form_cache);

    if (!form_data) {
      return;
    }

    passwords = {generation_element};

    WebFormControlElement confirmation_password =
        form_util::GetFormControlByRendererId(
            confirmation_password_renderer_id);

    if (confirmation_password) {
      WebInputElement input =
          confirmation_password.DynamicTo<WebInputElement>();
      if (input) {
        passwords.push_back(input);
      }
    }
  }

  if (current_generation_item_.IsProtected()) {
    return;  // May happen due to reentrant calls (caused by JS handlers).
  }
  current_generation_item_.CheckedSet(std::make_unique<GenerationItemInfo>(
      generation_element, std::move(*form_data), std::move(passwords)));

  generation_element.MaybeSetHasBeenPasswordField();

  generation_element.SetAttribute("aria-autocomplete", "list");
}

mojom::PasswordGenerationDriver* PasswordGenerationAgent::unsafe_driver() {
  if (IsPrerendering()) {
    if (!deferring_password_generation_driver_) {
      deferring_password_generation_driver_ =
          std::make_unique<DeferringPasswordGenerationDriver>(this);
    }
    return deferring_password_generation_driver_.get();
  }

  // Lazily bind this interface.
  if (auto* frame = unsafe_render_frame();
      frame && !password_generation_client_) {
    frame->GetRemoteAssociatedInterfaces()->GetInterface(
        &password_generation_client_);
  }

  return password_generation_client_.get();
}

void PasswordGenerationAgent::LogMessage(Logger::StringID message_id) {
  if (!password_agent_->logging_state_active() ||
      !password_agent_->unsafe_driver()) {
    return;
  }
  RendererSavePasswordProgressLogger logger(password_agent_->unsafe_driver());
  logger.LogMessage(message_id);
}

void PasswordGenerationAgent::LogBoolean(Logger::StringID message_id,
                                         bool truth_value) {
  if (!password_agent_->logging_state_active() ||
      !password_agent_->unsafe_driver()) {
    return;
  }
  RendererSavePasswordProgressLogger logger(password_agent_->unsafe_driver());
  logger.LogBoolean(message_id, truth_value);
}

}  // namespace autofill
