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

#ifndef COMPONENTS_AUTOFILL_CONTENT_RENDERER_PASSWORD_GENERATION_AGENT_H_
#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_PASSWORD_GENERATION_AGENT_H_

#include <stddef.h>

#include <map>
#include <memory>
#include <utility>

#include "base/auto_reset.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/stack_allocated.h"
#include "components/autofill/content/common/mojom/autofill_agent.mojom.h"
#include "components/autofill/content/common/mojom/autofill_driver.mojom.h"
#include "components/autofill/content/renderer/renderer_save_password_progress_logger.h"
#include "components/autofill/core/common/unique_ids.h"
#include "content/public/renderer/render_frame_observer.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/web/web_input_element.h"

namespace autofill {

class PasswordAutofillAgent;
class SynchronousFormCache;

// This class is responsible for controlling communication for password
// generation between the browser (which shows the popup and generates
// passwords) and WebKit (shows the generation icon in the password field).
class PasswordGenerationAgent : public content::RenderFrameObserver,
                                public mojom::PasswordGenerationAgent {
 public:
  // Maximum number of characters typed by user while the generation is still
  // offered. When the (kMaximumCharsForGenerationOffer + 1)-th character is
  // typed, the generation becomes unavailable.
  static const size_t kMaximumCharsForGenerationOffer = 5;

  // User can edit the generated password. If the length falls below this value,
  // the password is no longer considered generated.
  static const size_t kMinimumLengthForEditedPassword = 4;

  PasswordGenerationAgent(content::RenderFrame* render_frame,
                          PasswordAutofillAgent* password_agent,
                          blink::AssociatedInterfaceRegistry* registry);

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

  ~PasswordGenerationAgent() override;

  void BindPendingReceiver(
      mojo::PendingAssociatedReceiver<mojom::PasswordGenerationAgent>
          pending_receiver);

  // mojom::PasswordGenerationAgent:
  void GeneratedPasswordAccepted(const std::u16string& password) override;
  void GeneratedPasswordRejected() override;
  void FoundFormEligibleForGeneration(
      const PasswordFormGenerationData& form) override;
  // Sets |generation_element_| to the focused password field and responds back
  // if the generation was triggered successfully.
  void TriggeredGeneratePassword(
      TriggeredGeneratePasswordCallback callback) override;
  void FocusNextFieldAfterPasswords() override;

  // Returns true if the field being changed is one where a generated password
  // is being offered. Updates the state of the popup if necessary. `form_cache`
  // can be used to optimize form extractions occurring synchronously after this
  // function call.
  bool TextDidChangeInTextField(const blink::WebInputElement& element,
                                const SynchronousFormCache& form_cache);

  // Returns true if the newly focused node caused the generation UI to show.
  bool ShowPasswordGenerationSuggestions(
      const blink::WebInputElement& element,
      const SynchronousFormCache& form_cache);

  // Event forwarded by AutofillAgent from WebAutofillClient, informing that
  // the text field editing has ended, which means that the field is not
  // focused anymore. This is required for Android, where moving focus
  // to a non-focusable element doesn't result in |FocusedNodeHasChanged|
  // being called.
  void DidEndTextFieldEditing(const blink::WebInputElement& element);

  // Event forwarded by AutofillAgent from WebAutofillClient, informing that the
  // text field was cleared. For password fields this means that they are no
  // longer generated and should be masked.
  void TextFieldCleared(const blink::WebInputElement& element);

  // Called right before PasswordAutofillAgent filled |password_element|.
  void OnFieldAutofilled(const blink::WebInputElement& password_element);

  // Returns true iff the currently handled 'blur' event is fake and should be
  // ignored.
  bool ShouldIgnoreBlur();

#if defined(UNIT_TEST)
  // This method requests the mojom::PasswordManagerClient which binds
  // requests the binding if it wasn't bound yet.
  void RequestPasswordManagerClientForTesting() { unsafe_driver(); }
#endif

  bool IsPrerendering() const;

  // Previews the generation suggestion for the current generation element.
  void PreviewGenerationSuggestion(const std::u16string& password);

  // Clears the previewed field if it was previously previewed.
  void ClearPreviewedForm();

 private:
  class DeferringPasswordGenerationDriver;

  // Contains information about generation status for an element for the
  // lifetime of the possible interaction.
  struct GenerationItemInfo;

  // The RenderFrame* is nullptr while the PasswordGenerationAgent is pending
  // deletion, between AutofillAgent::OnDestruct() and
  // ~PasswordGenerationAgent().
  content::RenderFrame* unsafe_render_frame() const {
    return content::RenderFrameObserver::render_frame();
  }

  // Use unsafe_render_frame() instead.
  template <typename T = int>
  content::RenderFrame* render_frame(T* = 0) const {
    static_assert(
        std::is_void_v<T>,
        "Beware that the RenderFrame may become nullptr by OnDestruct() "
        "because the owner of PasswordGenerationAgent destructs itself "
        "asynchronously. Use unsafe_render_frame() instead and test that it is "
        "non-nullptr.");
  }

  // Callers should not store the returned value longer than a function scope.
  mojom::PasswordGenerationDriver* unsafe_driver();

  // RenderFrameObserver:
  void DidCommitProvisionalLoad(ui::PageTransition transition) override;
  void DidChangeScrollOffset() override;
  void OnDestruct() override;

  // Helper function which takes care of the form processing and collecting the
  // information which is required to show the generation popup. Returns true if
  // all required information is collected.
  bool SetUpTriggeredGeneration();

  // This is called whenever automatic generation could be offered, and returns
  // true if generation was offered.
  // If manual generation was already requested, automatic generation is not
  // offered.
  bool MaybeOfferAutomaticGeneration();

  // Signals the browser that it should offer automatic password generation
  // as a result of the user focusing a password field eligible for generation.
  void AutomaticGenerationAvailable();

#if !BUILDFLAG(IS_ANDROID)
  // Show UI for editing a generated password at |generation_element_|.
  void ShowEditingPopup(const SynchronousFormCache& form_cache);
#endif  // !BUILDFLAG(IS_ANDROID)

  // Stops treating a password as generated.
  void PasswordNoLongerGenerated();

  // Creates |current_generation_item_| for |element| if |element| is a
  // generation enabled element. If |current_generation_item_| is already
  // created for |element| it is not recreated.
  void MaybeCreateCurrentGenerationItem(
      blink::WebInputElement element,
      FieldRendererId confirmation_password_renderer_id,
      const SynchronousFormCache& form_cache);

  void LogMessage(SavePasswordProgressLogger::StringID message_id);
  void LogBoolean(SavePasswordProgressLogger::StringID message_id,
                  bool truth_value);

  // Creates a FormData to presave a generated password. It copies behavior
  // of CreateFromDataFromWebForm/FromUnownedInputElements. If a form
  // creating is failed, returns an empty unique_ptr. `form_cache` can be used
  // to optimize form extractions occurring synchronously after this function
  // call.
  std::optional<FormData> CreateFormDataToPresave(
      blink::WebInputElement generation_element,
      const SynchronousFormCache& form_cache);

  // Wraps an element with a write protector: The getter also returns a RAII
  // object that disallows write access.
  template <typename T>
  class Protected {
   public:
    explicit Protected() = default;
    Protected(const Protected&) = delete;
    Protected& operator=(const Protected&) = delete;
    ~Protected() = default;

    struct ProtectedValueRef {
      STACK_ALLOCATED();

     public:
      const T& value;
      base::AutoReset<bool> protector;
    };

    // Returns a reference to the value which is valid for at least the lifetime
    // of the returned protector.
    //
    // This Protected<> instance must outlive the returned reference and
    // protector.
    ProtectedValueRef GetAndProtect() {
      return {value_, base::AutoReset(&protected_, true)};
    }

    // Writes the value.
    // Crashes if a protector returned by GetAndProtect() is alive.
    void CheckedSet(T value) {
      CHECK(!protected_);
      value_ = std::move(value);
    }

    // Returns true iff a protector returned by GetAndProtect() is alive.
    bool IsProtected() const { return protected_; }

   private:
    T value_;
    bool protected_ = false;
  };

  // Contains the current element where generation is offered at the moment. It
  // can be either automatic or manual password generation.
  Protected<std::unique_ptr<GenerationItemInfo>> current_generation_item_;

  // Contains correspondence between generation enabled element and data for
  // generation.
  std::map<FieldRendererId, PasswordFormGenerationData>
      generation_enabled_fields_;

  // True iff the generation element should be marked with special HTML
  // attribute (only for experimental purposes).
  const bool mark_generation_element_;

  // Unowned pointer. Used to notify PassowrdAutofillAgent when values
  // in password fields are updated.
  const raw_ptr<PasswordAutofillAgent> password_agent_;

  mojo::AssociatedRemote<mojom::PasswordGenerationDriver>
      password_generation_client_;

  // Used for deferring messages while prerendering.
  std::unique_ptr<DeferringPasswordGenerationDriver>
      deferring_password_generation_driver_;

  mojo::AssociatedReceiver<mojom::PasswordGenerationAgent> receiver_{this};
};

}  // namespace autofill

#endif  // COMPONENTS_AUTOFILL_CONTENT_RENDERER_PASSWORD_GENERATION_AGENT_H_
