// 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_IOS_BROWSER_AUTOFILL_DRIVER_IOS_H_
#define COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_H_

#import <set>
#import <string>

#import "base/containers/flat_set.h"
#import "base/containers/span.h"
#import "base/memory/raw_ptr.h"
#import "base/memory/raw_ref.h"
#import "base/memory/weak_ptr.h"
#import "components/autofill/core/browser/foundations/autofill_client.h"
#import "components/autofill/core/browser/foundations/browser_autofill_manager.h"
#import "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
#import "components/autofill/ios/browser/form_fetch_batcher.h"
#import "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#import "url/origin.h"

namespace web {
class WebFrame;
class WebState;
}

@protocol AutofillDriverIOSBridge;

namespace mojo {
template <typename Interface>
class PendingRemote;
}  // namespace mojo

namespace autofill {

namespace mojom {
class AutofillVisibilityObserver;
}  // namespace mojom

// Histogram for recording the renderer event used to infer a form submission.
inline constexpr char kAutofillSubmissionDetectionSourceHistogram[] =
    "Autofill.SubmissionDetectionSource.AutofillAgent";

// Histogram for recording whether a form submission was detected after a form
// removal event.
inline constexpr char kFormSubmissionAfterFormRemovalHistogram[] =
    "Autofill.iOS.FormRemoval.SubmissionDetected";

// Histogram for recording the number of removed unowned fields in a form
// removal event.
inline constexpr char kFormRemovalRemovedUnownedFieldsHistogram[] =
    "Autofill.iOS.FormRemoval.RemovedUnownedFields";

class AutofillDriverIOSFactory;
class AutofillDriverRouter;

// AutofillDriverIOS drives the Autofill flow in the browser process based
// on communication from JavaScript and from the external world.
//
// AutofillDriverIOS communicates with an AutofillDriverIOSBridge, which in
// Chrome is implemented by AutofillAgent, and a BrowserAutofillManager.
//
// AutofillDriverIOS is associated with exactly one WebFrame and its lifecycle
// is bound to that WebFrame. Since WebFrames do not survive cross-document
// navigations, AutofillDriverIOS does not survive them either.
//
// AutofillDriverIOS is final because its constructor and destructor calls
// AutofillManager::SetLifecycleState(), which must be called at the very
// end/beginning of con-/destruction.
class AutofillDriverIOS final : public AutofillDriver,
                                public AutofillManager::Observer {
 public:
  // Returns the AutofillDriverIOS for `web_state` and `web_frame`. Creates the
  // driver if necessary.
  static AutofillDriverIOS* FromWebStateAndWebFrame(web::WebState* web_state,
                                                    web::WebFrame* web_frame);

  // Convenience method that grabs the frame associated with `token` and returns
  // the associated driver. Creates the driver if `token` refers to a valid
  // frame but no driver exists; returns nullptr if `token` does not refer to a
  // valid frame.
  static AutofillDriverIOS* FromWebStateAndLocalFrameToken(
      web::WebState* web_state,
      LocalFrameToken token);

  AutofillDriverIOS(web::WebState* web_state,
                    web::WebFrame* web_frame,
                    AutofillClient* client,
                    AutofillDriverRouter* router,
                    id<AutofillDriverIOSBridge> bridge,
                    base::PassKey<AutofillDriverIOSFactory>);

  ~AutofillDriverIOS() override;

  // AutofillDriver:
  LocalFrameToken GetFrameToken() const override;
  std::optional<LocalFrameToken> Resolve(FrameToken query) override;
  AutofillDriverIOS* GetParent() override;
  bool IsActive() const override;
  bool IsEmbedded() const override;
  AutofillClient& GetAutofillClient() override;
  BrowserAutofillManager& GetAutofillManager() override;
  ukm::SourceId GetPageUkmSourceId() const override;
  bool IsPolicyControlledFeatureAutofillEnabled() const override;
  bool IsPolicyControlledFeatureManualTextEnabled() const override;
  bool CanShowAutofillUi() const override;
  base::flat_set<FieldGlobalId> ApplyFormAction(
      mojom::FormActionType action_type,
      mojom::ActionPersistence action_persistence,
      base::span<const FormFieldData> fields,
      const FillId& fill_id,
      bool supports_refill,
      const url::Origin& triggered_origin,
      const absl::flat_hash_map<FieldGlobalId, FieldType>& field_type_map)
      override;
  void ApplyFieldAction(mojom::FieldActionType action_type,
                        mojom::ActionPersistence action_persistence,
                        const FieldGlobalId& field_id,
                        const std::u16string& value) override;
  void ExtractFormWithField(
      FieldGlobalId field_id,
      base::OnceCallback<void(AutofillDriver*, const std::optional<FormData>&)>
          response_callback) override;
  void ExposeDomNodeIdsInAllFrames() override;
  void SendTypePredictionsToRenderer(const FormStructure& form) override;
  void RendererShouldClearPreviewedForm() override;
  void RendererShouldTriggerSuggestions(
      const FieldGlobalId& field_id,
      AutofillSuggestionTriggerSource trigger_source) override;
  void RendererShouldAcceptDataListSuggestion(
      const FieldGlobalId& field_id,
      const std::u16string& value) override;
  void TriggerFormExtractionInDriverFrame(
      AutofillDriverRouterAndFormForestPassKey pass_key) override;
  void TriggerFormExtractionInAllFrames(
      base::OnceCallback<void(bool)> form_extraction_finished_callback)
      override;
  void ClearFormCacheInAllFrames() override;
  void ObserveFieldVisibility(
      const FieldGlobalId& field_id,
      mojo::PendingRemote<mojom::AutofillVisibilityObserver> observer) override;
  void GetFourDigitCombinationsFromDom(
      base::OnceCallback<void(const std::vector<std::string>&)>
          potential_matches) override;
  void ExtractLabeledTextNodeValue(
      const std::u16string& value_regex,
      const std::u16string& label_regex,
      uint32_t number_of_ancestor_levels_to_search,
      base::OnceCallback<void(const std::string& amount)> response_callback)
      override;
  void GetNonceForEmailVerification(
      FieldGlobalId email_field_id,
      base::OnceCallback<void(const std::optional<std::string>&)> callback)
      override;
  void SendEmailVerificationToken(FieldGlobalId email_field_id,
                                  const std::string& email,
                                  const std::string& token) override;
  void UpdateEmailVerificationState(
      const FieldGlobalId& email_field_id,
      mojom::EmailVerificationState state) override;
  bool IsSafeToFill(const FormFieldData& field,
                    FieldType filled_type,
                    const url::Origin& main_origin,
                    const url::Origin& trigger_origin) const override;

  void RendererShouldSetSuggestionAvailability(
      const FieldGlobalId& field_id,
      mojom::AutofillSuggestionAvailability suggestion_availability) override;
  std::optional<net::IsolationInfo> GetIsolationInfo() override;
  void ScrollFieldIntoView(FieldGlobalId field_id) override;

  bool is_processed() const { return processed_; }
  void set_processed(bool processed) { processed_ = processed; }
  web::WebFrame* web_frame() const;
  base::WeakPtr<AutofillDriverIOS> GetWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

  // Methods routed by AutofillDriverRouter. These are a subset of the methods
  // in mojom::AutofillDriver; that interface is content-specific, but to
  // simplify interaction with the Router, we duplicate some methods (with a few
  // irrelevant args omitted). See
  // components/autofill/content/common/mojom/autofill_driver.mojom
  // for further documentation of each method.
  // TODO(crbug.com/514243241): Make these functions take FormData by value to
  // avoid copying (as done for FormsSeen).
  void AskForValuesToFill(const FormData& form, const FieldGlobalId& field_id);
  void DidAutofillForm(const FormData& form);
  void FormsSeen(std::vector<FormData> updated_forms,
                 const std::vector<FormGlobalId>& removed_forms);
  void FormSubmitted(const FormData& form,
                     mojom::SubmissionSource submission_source);
  void CaretMovedInFormField(const FormData& form,
                             const FieldGlobalId& field_id,
                             const gfx::Rect& caret_bounds);
  void TextFieldValueChanged(const FormData& form,
                             const FieldGlobalId& field_id,
                             base::TimeTicks timestamp);

  // AutofillDriverIOS:

  // Notification that forms or formless fields have been removed. Since Bling's
  // renderer does not have API's to detect async form submissions, we use he
  // removal last interacted form or formless field as an indication that the
  // form was submitted asynchronously.
  void FormsRemoved(const std::set<FormRendererId>& removed_forms,
                    const std::set<FieldRendererId>& removed_unowned_fields);

  // Unregisters the driver as a standalone node which means that the
  // corresponding frame is now invalid. It is possible to unregister without
  // deleting the frame so it is definitely possible that the frame lives while
  // not being registered. Can't be rolled back where the driver cannot be
  // re-registered after being unregistered.
  void Unregister();

  // Called when form extraction was triggered on the driver's frame. Called
  // as soon as the extraction request is started regardless of the results.
  void OnDidTriggerFormFetch();

  // Scans to find all eligible forms in the frame's document. If batching is
  // enabled and `immediately` is true, runs this scan and the batch
  // immediately altogether.
  void ScanForms(bool immediately = false);

  // Fetches forms filtered by `form_name` and calls `caller_completion` with
  // the form fetch results upon completion of the fetch.
  void FetchFormsFilteredByName(const std::u16string& form_name,
                                FormFetchCompletion completion);

 private:
  friend class AutofillDriverIOSTestApi;

  // Represents the last form or formless field where the user entered data.
  struct LastInteractedForm {
    // Snapshot of the last interacted form or formless form.
    FormData form_data;

    // Renderer id of the last interacted formless field or `FieldRendererId()`
    // if the last interaction was not with a single formless field.
    // TODO: crbug.com/40266699 - Convert to FieldGlobalId.
    FieldRendererId formless_field;
  };

  void SetParent(base::WeakPtr<AutofillDriverIOS> parent);

  // Sets `this` as the parent of the frame identified by `token` and with
  // `form` as parent.
  void SetSelfAsParent(const FormData& form, LocalFrameToken token);

  // Updates the saved information about the last interacted form or formless
  // field.
  // - `form_data`: `FormData` version of the interacted form or
  // formless form.
  // - `formless_field`: Renderer id of the interacted formless
  // field. Default to `FieldRendererId()` when the user interaction was not
  // with a single formless field.
  void UpdateLastInteractedForm(
      const FormData& form_data,
      const FieldRendererId& formless_field = FieldRendererId());
  // Clears the saved information about the last interacted form or formless
  // field.
  void ClearLastInteractedForm();

  // Updates the snapshot of the last interacted form or formless form with
  // field data in `FieldDataManager`. Called before sending a submitted form to
  // `AutofillManager`.
  void UpdateLastInteractedFormFromFieldDataManager();

  // Whether a form submission can be inferred after a form removal event.
  bool DetectFormSubmissionAfterFormRemoval(
      const std::set<FormRendererId>& removed_forms,
      const std::set<FieldRendererId>& removed_unowned_fields) const;

  // AutofillManager::Observer:
  void OnAutofillManagerStateChanged(
      AutofillManager& manager,
      AutofillManager::LifecycleState old_state,
      AutofillManager::LifecycleState new_state) override;
  void OnAfterFormsSeen(AutofillManager& manager,
                        base::span<const FormGlobalId> updated_forms,
                        base::span<const FormGlobalId> removed_forms) override;

  // Logs metrics related to form removal events.
  void RecordFormRemoval(bool submission_detected,
                         int removed_forms_count,
                         int removed_unowned_fields_count);

  // Logs metrics related to triggered form extraction.
  void RecordTriggeredFormExtractionMetrics();

  // The WebState with which this object is associated.
  raw_ptr<web::WebState> web_state_ = nullptr;

  // The id of the WebFrame with which this object is associated.
  // "" if frame messaging is disabled.
  std::string web_frame_id_;

  // A LocalFrameToken containing a value equivalent to `web_frame_id_` if that
  // string is populated with a valid 128-bit hex value, or empty otherwise.
  LocalFrameToken local_frame_token_;

  // The driver of this frame's parent frame, if it is known and valid. Always
  // null for the main (root) frame.
  base::WeakPtr<AutofillDriverIOS> parent_ = nullptr;

  // All RemoteFrameTokens that have ever been dispatched from this frame to
  // a child frame.
  base::flat_set<RemoteFrameToken> known_child_frames_;

  // AutofillDriverIOSBridge instance that is passed in.
  __weak id<AutofillDriverIOSBridge> bridge_;

  // Whether the initial processing has been done (JavaScript observers have
  // been enabled and the forms have been extracted).
  bool processed_ = false;

  // Information about the last form or formless field where the user entered
  // data. Used for form submission detection.
  std::optional<LastInteractedForm> last_interacted_form_;

  // The embedder's AutofillClient instance.
  raw_ref<AutofillClient> client_;

  std::unique_ptr<BrowserAutofillManager> manager_;

  base::ScopedObservation<AutofillManager, AutofillManager::Observer>
      manager_observation_{this};

  raw_ptr<AutofillDriverRouter> router_;

  // True if the drive was once unregistered.
  bool unregistered_ = false;

  // Counter for the number of form extractions that were triggered during the
  // driver's lifetime. The counter doesn't care whether the extraction
  // actually happened for real where it focuses on the trigger.
  int form_extraction_trigger_count_ = 0;

  // FetchRequestBatcher used exclusively for batching document form scans.
  FormFetchBatcher document_scan_batcher_;

  // FetchRequestBatcher used exclusively for batching filtered document form
  // scans.
  FormFetchBatcher document_filtered_scan_batcher_;

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

}  // namespace autofill

#endif  // COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_H_
