// Copyright 2013 The Chromium Authors. All rights reserved.
// 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 <vector>

#include "base/macros.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 "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"
#include "url/gurl.h"

namespace autofill {

class PasswordAutofillAgent;

// 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() override;

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

  // mojom::PasswordGenerationAgent:
  void GeneratedPasswordAccepted(const base::string16& password) 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 UserTriggeredGeneratePassword(
      UserTriggeredGeneratePasswordCallback callback) 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.
  bool TextDidChangeInTextField(const blink::WebInputElement& element);

  // Returns true if the newly focused node caused the generation UI to show.
  bool FocusedNodeHasChanged(const blink::WebNode& node);

  // 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);

  // 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() const;

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

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

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

  const mojo::AssociatedRemote<mojom::PasswordManagerDriver>&
  GetPasswordManagerDriver();

  const mojo::AssociatedRemote<mojom::PasswordGenerationDriver>&
  GetPasswordGenerationDriver();

  // 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 SetUpUserTriggeredGeneration();

  // This is called whenever automatic generation could be offered.
  // If manual generation was already requested, automatic generation will
  // not be offered.
  void 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();

  // Show UI for editing a generated password at |generation_element_|.
  void ShowEditingPopup();

  // Signals the browser that generation was rejected. This happens when the
  // user types more characters than the maximum offer size into the password
  // field. Upon receiving this message, the browser can choose to hide the
  // generation UI or not, depending on the platform.
  void GenerationRejectedByTyping();

  // 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,
      uint32_t confirmation_password_renderer_id);

  void LogMessage(autofill::SavePasswordProgressLogger::StringID message_id);
  void LogBoolean(autofill::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.
  std::unique_ptr<FormData> CreateFormDataToPresave();

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

  // Password element that had focus last. Since Javascript could change focused
  // element after the user triggered a generation request, it is better to save
  // the last focused password element.
  blink::WebInputElement last_focused_password_element_;

  // Contains correspondence between generation enabled element and data for
  // generation.
  std::map<uint32_t, 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.
  PasswordAutofillAgent* password_agent_;

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

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

  DISALLOW_COPY_AND_ASSIGN(PasswordGenerationAgent);
};

}  // namespace autofill

#endif  // COMPONENTS_AUTOFILL_CONTENT_RENDERER_PASSWORD_GENERATION_AGENT_H_
