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

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

#include <stddef.h>

#include <tuple>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/i18n/case_conversion.h"
#include "base/location.h"
#include "base/metrics/field_trial.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/form_tracker.h"
#include "components/autofill/content/renderer/password_autofill_agent.h"
#include "components/autofill/content/renderer/password_generation_agent.h"
#include "components/autofill/content/renderer/renderer_save_password_progress_logger.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_data_validation.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_data_predictions.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/password_form.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/autofill/core/common/save_password_progress_logger.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/origin_util.h"
#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h"
#include "net/cert/cert_status_flags.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/platform/web_keyboard_event.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/web/web_console_message.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_element_collection.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_local_frame.h"
#include "third_party/blink/public/web/web_node.h"
#include "third_party/blink/public/web/web_option_element.h"
#include "third_party/blink/public/web/web_user_gesture_indicator.h"
#include "third_party/blink/public/web/web_view.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/keycodes/keyboard_codes.h"

using blink::WebAutofillState;
using blink::WebAutofillClient;
using blink::WebConsoleMessage;
using blink::WebDocument;
using blink::WebElement;
using blink::WebElementCollection;
using blink::WebFormControlElement;
using blink::WebFormElement;
using blink::WebFrame;
using blink::WebInputElement;
using blink::WebKeyboardEvent;
using blink::WebLocalFrame;
using blink::WebNode;
using blink::WebOptionElement;
using blink::WebString;
using blink::WebUserGestureIndicator;
using blink::WebVector;

namespace autofill {

namespace {

// Time to wait, in ms, o ensure that only a single select change will be acted
// upon, instead of multiple in close succession (debounce time).
size_t kWaitTimeForSelectOptionsChangesMs = 50;

// Whether the "single click" autofill feature is enabled, through command-line
// or field trial.
bool IsSingleClickEnabled() {
  return base::FeatureList::IsEnabled(features::kSingleClickAutofill);
}

// Gets all the data list values (with corresponding label) for the given
// element.
void GetDataListSuggestions(const WebInputElement& element,
                            std::vector<base::string16>* values,
                            std::vector<base::string16>* labels) {
  for (const auto& option : element.FilteredDataListOptions()) {
    values->push_back(option.Value().Utf16());
    if (option.Value() != option.Label())
      labels->push_back(option.Label().Utf16());
    else
      labels->push_back(base::string16());
  }
}

// Trim the vector before sending it to the browser process to ensure we
// don't send too much data through the IPC.
void TrimStringVectorForIPC(std::vector<base::string16>* strings) {
  // Limit the size of the vector.
  if (strings->size() > kMaxListSize)
    strings->resize(kMaxListSize);

  // Limit the size of the strings in the vector.
  for (size_t i = 0; i < strings->size(); ++i) {
    if ((*strings)[i].length() > kMaxDataLength)
      (*strings)[i].resize(kMaxDataLength);
  }
}

}  // namespace

AutofillAgent::ShowSuggestionsOptions::ShowSuggestionsOptions()
    : autofill_on_empty_values(false),
      requires_caret_at_end(false),
      show_full_suggestion_list(false),
      show_password_suggestions_only(false),
      autoselect_first_suggestion(false) {}

AutofillAgent::AutofillAgent(content::RenderFrame* render_frame,
                             PasswordAutofillAgent* password_autofill_agent,
                             PasswordGenerationAgent* password_generation_agent,
                             service_manager::BinderRegistry* registry)
    : content::RenderFrameObserver(render_frame),
      form_cache_(render_frame->GetWebFrame()),
      password_autofill_agent_(password_autofill_agent),
      password_generation_agent_(password_generation_agent),
      autofill_query_id_(0),
      query_node_autofill_state_(WebAutofillState::kNotFilled),
      ignore_text_changes_(false),
      is_popup_possibly_visible_(false),
      is_generation_popup_possibly_visible_(false),
      is_user_gesture_required_(true),
      is_secure_context_required_(false),
      form_tracker_(render_frame),
      binding_(this),
      weak_ptr_factory_(this) {
  render_frame->GetWebFrame()->SetAutofillClient(this);
  password_autofill_agent->SetAutofillAgent(this);
  AddFormObserver(this);
  registry->AddInterface(
      base::Bind(&AutofillAgent::BindRequest, base::Unretained(this)));
}

AutofillAgent::~AutofillAgent() {
  RemoveFormObserver(this);
}

void AutofillAgent::BindRequest(mojom::AutofillAgentRequest request) {
  binding_.Bind(std::move(request));
}

bool AutofillAgent::FormDataCompare::operator()(const FormData& lhs,
                                                const FormData& rhs) const {
  return std::tie(lhs.name, lhs.origin, lhs.action, lhs.is_form_tag) <
         std::tie(rhs.name, rhs.origin, rhs.action, rhs.is_form_tag);
}

void AutofillAgent::DidCommitProvisionalLoad(bool is_new_navigation,
                                             bool is_same_document_navigation) {
  blink::WebFrame* frame = render_frame()->GetWebFrame();
  // TODO(dvadym): check if we need to check if it is main frame navigation
  // http://crbug.com/443155
  if (frame->Parent())
    return;  // Not a top-level navigation.

  if (is_same_document_navigation)
    return;

  // Navigation to a new page or a page refresh.

  element_.Reset();

  form_cache_.Reset();
  ResetLastInteractedElements();
  OnFormNoLongerSubmittable();
}

void AutofillAgent::DidFinishDocumentLoad() {
  ProcessForms();
}

void AutofillAgent::DidChangeScrollOffset() {
  if (element_.IsNull())
    return;

  if (!focus_requires_scroll_) {
    // Post a task here since scroll offset may change during layout.
    // (https://crbug.com/804886)
    weak_ptr_factory_.InvalidateWeakPtrs();
    render_frame()
        ->GetTaskRunner(blink::TaskType::kInternalUserInteraction)
        ->PostTask(FROM_HERE,
                   base::BindOnce(&AutofillAgent::DidChangeScrollOffsetImpl,
                                  weak_ptr_factory_.GetWeakPtr(), element_));
  } else if (!IsKeyboardAccessoryEnabled()) {
    HidePopup();
  }
}

void AutofillAgent::DidChangeScrollOffsetImpl(
    const WebFormControlElement& element) {
  if (element != element_ || element_.IsNull() || focus_requires_scroll_ ||
      !is_popup_possibly_visible_ || !element_.Focused())
    return;

  FormData form;
  FormFieldData field;
  if (form_util::FindFormAndFieldForFormControlElement(element_, &form,
                                                       &field)) {
    GetAutofillDriver()->TextFieldDidScroll(
        form, field,
        render_frame()->GetRenderView()->ElementBoundsInWindow(element_));
  }

  // Ignore subsequent scroll offset changes.
  if (!IsKeyboardAccessoryEnabled())
    HidePopup();
}

void AutofillAgent::FocusedNodeChanged(const WebNode& node) {
  was_focused_before_now_ = false;

  if ((IsKeyboardAccessoryEnabled() || !focus_requires_scroll_) &&
      WebUserGestureIndicator::IsProcessingUserGesture(
          node.IsNull() ? nullptr : node.GetDocument().GetFrame())) {
    focused_node_was_last_clicked_ = true;
    HandleFocusChangeComplete();
  }

  HidePopup();

  if (node.IsNull() || !node.IsElementNode()) {
    if (!last_interacted_form_.IsNull()) {
      // Focus moved away from the last interacted form to somewhere else on
      // the page.
      GetAutofillDriver()->FocusNoLongerOnForm();
    }
    return;
  }

  WebElement web_element = node.ToConst<WebElement>();
  const WebInputElement* element = ToWebInputElement(&web_element);

  if (!last_interacted_form_.IsNull() &&
      (!element || last_interacted_form_ != element->Form())) {
    // The focused element is not part of the last interacted form (could be
    // in a different form).
    GetAutofillDriver()->FocusNoLongerOnForm();
    return;
  }

  if (!element || !element->IsEnabled() || element->IsReadOnly() ||
      !element->IsTextField())
    return;

  element_ = *element;

  FormData form;
  FormFieldData field;
  if (form_util::FindFormAndFieldForFormControlElement(element_, &form,
                                                       &field)) {
    GetAutofillDriver()->FocusOnFormField(
        form, field,
        render_frame()->GetRenderView()->ElementBoundsInWindow(element_));
  }
}

void AutofillAgent::OnDestruct() {
  Shutdown();
  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
}

void AutofillAgent::FireHostSubmitEvents(const WebFormElement& form,
                                         bool known_success,
                                         SubmissionSource source) {
  FormData form_data;
  if (!form_util::ExtractFormData(form, &form_data))
    return;

  FireHostSubmitEvents(form_data, known_success, source);
}

void AutofillAgent::FireHostSubmitEvents(const FormData& form_data,
                                         bool known_success,
                                         SubmissionSource source) {
  // We don't want to fire duplicate submission event.
  if (!submitted_forms_.insert(form_data).second)
    return;

  GetAutofillDriver()->FormSubmitted(form_data, known_success, source,
                                     base::TimeTicks::Now());
}

void AutofillAgent::Shutdown() {
  binding_.Close();
  weak_ptr_factory_.InvalidateWeakPtrs();
}

void AutofillAgent::TextFieldDidEndEditing(const WebInputElement& element) {
  GetAutofillDriver()->DidEndTextFieldEditing();
}

void AutofillAgent::SetUserGestureRequired(bool required) {
  form_tracker_.set_user_gesture_required(required);
}

void AutofillAgent::TextFieldDidChange(const WebFormControlElement& element) {
  form_tracker_.TextFieldDidChange(element);
}

void AutofillAgent::OnTextFieldDidChange(const WebInputElement& element) {
  if (password_generation_agent_ &&
      password_generation_agent_->TextDidChangeInTextField(element)) {
    is_popup_possibly_visible_ = true;
    return;
  }

  if (password_autofill_agent_->TextDidChangeInTextField(element)) {
    is_popup_possibly_visible_ = true;
    element_ = element;
    return;
  }

  ShowSuggestionsOptions options;
  options.requires_caret_at_end = true;
  ShowSuggestions(element, options);

  FormData form;
  FormFieldData field;
  if (form_util::FindFormAndFieldForFormControlElement(element, &form,
                                                       &field)) {
    GetAutofillDriver()->TextFieldDidChange(
        form, field,
        render_frame()->GetRenderView()->ElementBoundsInWindow(element),
        base::TimeTicks::Now());
  }
}

void AutofillAgent::TextFieldDidReceiveKeyDown(const WebInputElement& element,
                                               const WebKeyboardEvent& event) {
  if (event.windows_key_code == ui::VKEY_DOWN ||
      event.windows_key_code == ui::VKEY_UP) {
    ShowSuggestionsOptions options;
    options.autofill_on_empty_values = true;
    options.requires_caret_at_end = true;
    options.autoselect_first_suggestion =
        ShouldAutoselectFirstSuggestionOnArrowDown();
    ShowSuggestions(element, options);
  }
}

void AutofillAgent::OpenTextDataListChooser(const WebInputElement& element) {
  ShowSuggestionsOptions options;
  options.autofill_on_empty_values = true;
  ShowSuggestions(element, options);
}

void AutofillAgent::DataListOptionsChanged(const WebInputElement& element) {
  if (!is_popup_possibly_visible_ || !element.Focused())
    return;

  OnProvisionallySaveForm(WebFormElement(), element,
                          ElementChangeSource::TEXTFIELD_CHANGED);
}

void AutofillAgent::UserGestureObserved() {
  password_autofill_agent_->UserGestureObserved();
}

void AutofillAgent::DoAcceptDataListSuggestion(
    const base::string16& suggested_value) {
  if (element_.IsNull())
    return;

  WebInputElement* input_element = ToWebInputElement(&element_);
  DCHECK(input_element);
  base::string16 new_value = suggested_value;
  // If this element takes multiple values then replace the last part with
  // the suggestion.
  if (input_element->IsMultiple() && input_element->IsEmailField()) {
    base::string16 value = input_element->EditingValue().Utf16();
    std::vector<base::StringPiece16> parts =
        base::SplitStringPiece(value, base::ASCIIToUTF16(","),
                               base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
    if (parts.size() == 0)
      parts.push_back(base::StringPiece16());

    base::string16 last_part = parts.back().as_string();
    // We want to keep just the leading whitespace.
    for (size_t i = 0; i < last_part.size(); ++i) {
      if (!base::IsUnicodeWhitespace(last_part[i])) {
        last_part = last_part.substr(0, i);
        break;
      }
    }
    last_part.append(suggested_value);
    parts.back() = last_part;

    new_value = base::JoinString(parts, base::ASCIIToUTF16(","));
  }
  DoFillFieldWithValue(new_value, input_element);
}

void AutofillAgent::TriggerRefillIfNeeded(const FormData& form) {
  if (!base::FeatureList::IsEnabled(features::kAutofillDynamicForms))
    return;
  FormFieldData field;
  FormData updated_form;
  if (form_util::FindFormAndFieldForFormControlElement(element_, &updated_form,
                                                       &field) &&
      !form.DynamicallySameFormAs(updated_form)) {
    base::TimeTicks forms_seen_timestamp = base::TimeTicks::Now();
    WebLocalFrame* frame = render_frame()->GetWebFrame();
    std::vector<FormData> forms;
    forms.push_back(updated_form);
    // Always communicate to browser process for topmost frame.
    if (!forms.empty() || !frame->Parent())
      GetAutofillDriver()->FormsSeen(forms, forms_seen_timestamp);
  }
}

// mojom::AutofillAgent:
void AutofillAgent::FillForm(int32_t id, const FormData& form) {
  if (element_.IsNull())
    return;

  if (id != autofill_query_id_ && id != kNoQueryId)
    return;

  was_last_action_fill_ = true;

  if (base::FeatureList::IsEnabled(features::kAutofillDynamicForms))
    ReplaceElementIfNowInvalid(form);

  query_node_autofill_state_ = element_.GetAutofillState();
  form_util::FillForm(form, element_);
  if (!element_.Form().IsNull())
    UpdateLastInteractedForm(element_.Form());

  GetAutofillDriver()->DidFillAutofillFormData(form, base::TimeTicks::Now());

  TriggerRefillIfNeeded(form);
}

void AutofillAgent::PreviewForm(int32_t id, const FormData& form) {
  if (element_.IsNull())
    return;

  if (id != autofill_query_id_)
    return;

  query_node_autofill_state_ = element_.GetAutofillState();
  form_util::PreviewForm(form, element_);

  GetAutofillDriver()->DidPreviewAutofillFormData();
}

void AutofillAgent::FieldTypePredictionsAvailable(
    const std::vector<FormDataPredictions>& forms) {
  bool attach_predictions_to_dom =
      base::FeatureList::IsEnabled(features::kAutofillShowTypePredictions);
  for (const auto& form : forms) {
    form_cache_.ShowPredictions(form, attach_predictions_to_dom);
  }
}

void AutofillAgent::ClearSection() {
  if (element_.IsNull())
    return;

  form_cache_.ClearSectionWithElement(element_);
}

void AutofillAgent::ClearPreviewedForm() {
  // TODO(crbug.com/816533): It is very rare, but it looks like the |element_|
  // can be null if a provisional load was committed immediately prior to
  // clearing the previewed form.
  if (element_.IsNull())
    return;

  if (password_autofill_agent_->DidClearAutofillSelection(element_))
    return;

  form_util::ClearPreviewedFormWithElement(element_,
                                           query_node_autofill_state_);
}

void AutofillAgent::FillFieldWithValue(const base::string16& value) {
  if (element_.IsNull())
    return;

  WebInputElement* input_element = ToWebInputElement(&element_);
  if (input_element) {
    DoFillFieldWithValue(value, input_element);
    input_element->SetAutofillState(WebAutofillState::kAutofilled);
  }
}

void AutofillAgent::PreviewFieldWithValue(const base::string16& value) {
  if (element_.IsNull())
    return;

  WebInputElement* input_element = ToWebInputElement(&element_);
  if (input_element)
    DoPreviewFieldWithValue(value, input_element);
}

void AutofillAgent::AcceptDataListSuggestion(const base::string16& value) {
  DoAcceptDataListSuggestion(value);
}

void AutofillAgent::FillPasswordSuggestion(const base::string16& username,
                                           const base::string16& password) {
  if (element_.IsNull())
    return;

  bool handled =
      password_autofill_agent_->FillSuggestion(element_, username, password);
  DCHECK(handled);
}

void AutofillAgent::PreviewPasswordSuggestion(const base::string16& username,
                                              const base::string16& password) {
  if (element_.IsNull())
    return;

  bool handled = password_autofill_agent_->PreviewSuggestion(
      element_, blink::WebString::FromUTF16(username),
      blink::WebString::FromUTF16(password));
  DCHECK(handled);
}

void AutofillAgent::ShowInitialPasswordAccountSuggestions(
    int32_t key,
    const PasswordFormFillData& form_data) {
  std::vector<blink::WebInputElement> elements;
  std::unique_ptr<RendererSavePasswordProgressLogger> logger;
  if (password_autofill_agent_->logging_state_active()) {
    logger.reset(new RendererSavePasswordProgressLogger(
        GetPasswordManagerDriver().get()));
    logger->LogMessage(SavePasswordProgressLogger::
                           STRING_ON_SHOW_INITIAL_PASSWORD_ACCOUNT_SUGGESTIONS);
  }
  password_autofill_agent_->GetFillableElementFromFormData(
      key, form_data, logger.get(), &elements);

  // If wait_for_username is true, we don't want to initially show form options
  // until the user types in a valid username.
  if (form_data.wait_for_username)
    return;

  ShowSuggestionsOptions options;
  options.autofill_on_empty_values = true;
  options.show_full_suggestion_list = true;
  for (auto element : elements)
    ShowSuggestions(element, options);
}

bool AutofillAgent::CollectFormlessElements(FormData* output) {
  if (render_frame() == nullptr || render_frame()->GetWebFrame() == nullptr)
    return false;

  WebDocument document = render_frame()->GetWebFrame()->GetDocument();

  // Build up the FormData from the unowned elements. This logic mostly
  // mirrors the construction of the synthetic form in form_cache.cc, but
  // happens at submit-time so we can capture the modifications the user
  // has made, and doesn't depend on form_cache's internal state.
  std::vector<WebElement> fieldsets;
  std::vector<WebFormControlElement> control_elements =
      form_util::GetUnownedAutofillableFormFieldElements(document.All(),
                                                         &fieldsets);

  if (control_elements.size() > form_util::kMaxParseableFields)
    return false;

  const form_util::ExtractMask extract_mask =
      static_cast<form_util::ExtractMask>(form_util::EXTRACT_VALUE |
                                          form_util::EXTRACT_OPTIONS);

  return form_util::UnownedCheckoutFormElementsAndFieldSetsToFormData(
      fieldsets, control_elements, nullptr, document, extract_mask, output,
      nullptr);
}

void AutofillAgent::ShowSuggestions(const WebFormControlElement& element,
                                    const ShowSuggestionsOptions& options) {
  if (!element.IsEnabled() || element.IsReadOnly())
    return;
  if (!element.SuggestedValue().IsEmpty())
    return;

  const WebInputElement* input_element = ToWebInputElement(&element);
  if (input_element) {
    if (!input_element->IsTextField())
      return;
    if (!input_element->SuggestedValue().IsEmpty())
      return;
  } else {
    DCHECK(form_util::IsTextAreaElement(element));
    if (!element.ToConst<WebFormControlElement>().SuggestedValue().IsEmpty())
      return;
  }

  // Don't attempt to autofill with values that are too large or if filling
  // criteria are not met.
  WebString value = element.EditingValue();
  if (value.length() > kMaxDataLength ||
      (!options.autofill_on_empty_values && value.IsEmpty()) ||
      (options.requires_caret_at_end &&
       (element.SelectionStart() != element.SelectionEnd() ||
        element.SelectionEnd() != static_cast<int>(value.length())))) {
    // Any popup currently showing is obsolete.
    HidePopup();
    return;
  }

  element_ = element;
  if (form_util::IsAutofillableInputElement(input_element) &&
      password_autofill_agent_->ShowSuggestions(
          *input_element, options.show_full_suggestion_list,
          is_generation_popup_possibly_visible_)) {
    is_popup_possibly_visible_ = true;
    return;
  }

  if (is_generation_popup_possibly_visible_)
    return;

  if (options.show_password_suggestions_only)
    return;

  // Password field elements should only have suggestions shown by the password
  // autofill agent.
  if (input_element && input_element->IsPasswordFieldForAutofill() &&
      !query_password_suggestion_) {
    return;
  }

  QueryAutofillSuggestions(element, options.autoselect_first_suggestion);
}

void AutofillAgent::SetQueryPasswordSuggestion(bool query) {
  query_password_suggestion_ = query;
}

void AutofillAgent::SetSecureContextRequired(bool required) {
  is_secure_context_required_ = required;
}

void AutofillAgent::SetFocusRequiresScroll(bool require) {
  focus_requires_scroll_ = require;
}

void AutofillAgent::QueryAutofillSuggestions(
    const WebFormControlElement& element,
    bool autoselect_first_suggestion) {
  if (!element.GetDocument().GetFrame())
    return;

  DCHECK(ToWebInputElement(&element) || form_util::IsTextAreaElement(element));

  static int query_counter = 0;
  autofill_query_id_ = query_counter++;

  FormData form;
  FormFieldData field;
  if (!form_util::FindFormAndFieldForFormControlElement(element, &form,
                                                        &field)) {
    // If we didn't find the cached form, at least let autocomplete have a shot
    // at providing suggestions.
    WebFormControlElementToFormField(element, nullptr, form_util::EXTRACT_VALUE,
                                     &field);
  }

  if (is_secure_context_required_ &&
      !(element.GetDocument().IsSecureContext())) {
    LOG(WARNING) << "Autofill suggestions are disabled because the document "
                    "isn't a secure context.";
    return;
  }

  std::vector<base::string16> data_list_values;
  std::vector<base::string16> data_list_labels;
  const WebInputElement* input_element = ToWebInputElement(&element);
  if (input_element) {
    // Find the datalist values and send them to the browser process.
    GetDataListSuggestions(*input_element,
                           &data_list_values,
                           &data_list_labels);
    TrimStringVectorForIPC(&data_list_values);
    TrimStringVectorForIPC(&data_list_labels);
  }

  is_popup_possibly_visible_ = true;

  GetAutofillDriver()->SetDataList(data_list_values, data_list_labels);
  GetAutofillDriver()->QueryFormFieldAutofill(
      autofill_query_id_, form, field,
      render_frame()->GetRenderView()->ElementBoundsInWindow(element_),
      autoselect_first_suggestion);
}

void AutofillAgent::DoFillFieldWithValue(const base::string16& value,
                                         WebInputElement* node) {
  form_tracker_.set_ignore_control_changes(true);
  node->SetAutofillValue(blink::WebString::FromUTF16(value));
  password_autofill_agent_->UpdateStateForTextChange(*node);
  form_tracker_.set_ignore_control_changes(false);
}

void AutofillAgent::DoPreviewFieldWithValue(const base::string16& value,
                                            WebInputElement* node) {
  query_node_autofill_state_ = element_.GetAutofillState();
  node->SetSuggestedValue(blink::WebString::FromUTF16(value));
  node->SetAutofillState(WebAutofillState::kPreviewed);
  form_util::PreviewSuggestion(node->SuggestedValue().Utf16(),
                               node->Value().Utf16(), node);
}

void AutofillAgent::ProcessForms() {
  // Record timestamp of when the forms are first seen. This is used to
  // measure the overhead of the Autofill feature.
  base::TimeTicks forms_seen_timestamp = base::TimeTicks::Now();

  WebLocalFrame* frame = render_frame()->GetWebFrame();
  std::vector<FormData> forms = form_cache_.ExtractNewForms();

  // Always communicate to browser process for topmost frame.
  if (!forms.empty() || !frame->Parent()) {
    GetAutofillDriver()->FormsSeen(forms, forms_seen_timestamp);
  }
}

void AutofillAgent::HidePopup() {
  if (!is_popup_possibly_visible_)
    return;
  is_popup_possibly_visible_ = false;
  is_generation_popup_possibly_visible_ = false;

  GetAutofillDriver()->HidePopup();
}

bool AutofillAgent::IsUserGesture() const {
  return WebUserGestureIndicator::IsProcessingUserGesture(
      render_frame()->GetWebFrame());
}

void AutofillAgent::DidAssociateFormControlsDynamically() {
  // If the control flow is here than the document was at least loaded. The
  // whole page doesn't have to be loaded.
  ProcessForms();
  password_autofill_agent_->OnDynamicFormsSeen();
  if (password_generation_agent_)
    password_generation_agent_->OnDynamicFormsSeen();
}

void AutofillAgent::DidCompleteFocusChangeInFrame() {
  WebDocument doc = render_frame()->GetWebFrame()->GetDocument();
  WebElement focused_element;
  if (!doc.IsNull())
    focused_element = doc.FocusedElement();
  // PasswordGenerationAgent needs to know about focus changes, even if there is
  // no focused element.
  if (password_generation_agent_ &&
      password_generation_agent_->FocusedNodeHasChanged(focused_element)) {
    is_generation_popup_possibly_visible_ = true;
    is_popup_possibly_visible_ = true;
  }
  if (!focused_element.IsNull() && password_autofill_agent_)
    password_autofill_agent_->FocusedNodeHasChanged(focused_element);

  if (!IsKeyboardAccessoryEnabled() && focus_requires_scroll_)
    HandleFocusChangeComplete();
}

void AutofillAgent::DidReceiveLeftMouseDownOrGestureTapInNode(
    const WebNode& node) {
  DCHECK(!node.IsNull());
  focused_node_was_last_clicked_ = node.Focused();

  if (IsKeyboardAccessoryEnabled() || !focus_requires_scroll_)
    HandleFocusChangeComplete();
}

void AutofillAgent::SelectControlDidChange(
    const WebFormControlElement& element) {
  form_tracker_.SelectControlDidChange(element);
}

void AutofillAgent::SelectFieldOptionsChanged(
    const blink::WebFormControlElement& element) {
  if (!was_last_action_fill_ || element_.IsNull())
    return;

  // Since a change of a select options often come in batches, use a timer
  // to wait for other changes. Stop the timer if it was already running. It
  // will be started again for this change.
  if (on_select_update_timer_.IsRunning())
    on_select_update_timer_.AbandonAndStop();

  // Start the timer to notify the driver that the select field was updated
  // after the options have finished changing,
  on_select_update_timer_.Start(
      FROM_HERE,
      base::TimeDelta::FromMilliseconds(kWaitTimeForSelectOptionsChangesMs),
      base::BindRepeating(&AutofillAgent::SelectWasUpdated,
                          weak_ptr_factory_.GetWeakPtr(), element));
}

void AutofillAgent::SelectWasUpdated(
    const blink::WebFormControlElement& element) {
  // Look for the form and field associated with the select element. If they are
  // found, notify the driver that the the form was modified dynamically.
  FormData form;
  FormFieldData field;
  if (form_util::FindFormAndFieldForFormControlElement(element, &form,
                                                       &field) &&
      !field.option_values.empty()) {
    GetAutofillDriver()->SelectFieldOptionsDidChange(form);
  }
}

void AutofillAgent::FormControlElementClicked(
    const WebFormControlElement& element,
    bool was_focused) {
  last_clicked_form_control_element_for_testing_ = element;
  last_clicked_form_control_element_was_focused_for_testing_ = was_focused;
  was_last_action_fill_ = false;

  const WebInputElement* input_element = ToWebInputElement(&element);
  if (!input_element && !form_util::IsTextAreaElement(element))
    return;

  ShowSuggestionsOptions options;
  options.autofill_on_empty_values = true;
  // Show full suggestions when clicking on an already-focused form field.
  options.show_full_suggestion_list = element.IsAutofilled() || was_focused;

  if (!IsSingleClickEnabled()) {
    // On  the initial click (not focused yet), only show password suggestions.
    options.show_password_suggestions_only = !was_focused;
  }
  ShowSuggestions(element, options);
}

void AutofillAgent::HandleFocusChangeComplete() {
  WebElement focused_element =
      render_frame()->GetWebFrame()->GetDocument().FocusedElement();

  if (focused_node_was_last_clicked_ && !focused_element.IsNull() &&
      focused_element.IsFormControlElement() &&
      (form_util::IsTextInput(blink::ToWebInputElement(&focused_element)) ||
       focused_element.HasHTMLTagName("textarea"))) {
    FormControlElementClicked(focused_element.ToConst<WebFormControlElement>(),
                              was_focused_before_now_);
  }

  was_focused_before_now_ = true;
  focused_node_was_last_clicked_ = false;
}

void AutofillAgent::AjaxSucceeded() {
  form_tracker_.AjaxSucceeded();
}

void AutofillAgent::OnProvisionallySaveForm(
    const WebFormElement& form,
    const WebFormControlElement& element,
    ElementChangeSource source) {
  if (source == ElementChangeSource::WILL_SEND_SUBMIT_EVENT) {
    // Fire the form submission event to avoid missing submission when web site
    // handles the onsubmit event, this also gets the form before Javascript
    // could change it.
    // We don't clear submitted_forms_ because OnFormSubmitted will normally be
    // invoked afterwards and we don't want to fire the same event twice.
    FireHostSubmitEvents(form, /*known_success=*/false,
                         SubmissionSource::FORM_SUBMISSION);
    ResetLastInteractedElements();
    return;
  } else if (source == ElementChangeSource::TEXTFIELD_CHANGED ||
             source == ElementChangeSource::SELECT_CHANGED) {
    // Remember the last form the user interacted with.
    if (!element.Form().IsNull()) {
      UpdateLastInteractedForm(element.Form());
    } else {
      // Remove invisible elements
      for (auto it = formless_elements_user_edited_.begin();
           it != formless_elements_user_edited_.end();) {
        if (form_util::IsWebElementVisible(*it)) {
          it = formless_elements_user_edited_.erase(it);
        } else {
          ++it;
        }
      }
      formless_elements_user_edited_.insert(element);
      provisionally_saved_form_ = std::make_unique<FormData>();
      if (!CollectFormlessElements(provisionally_saved_form_.get())) {
        provisionally_saved_form_.reset();
      } else {
        last_interacted_form_.Reset();
      }
    }

    if (source == ElementChangeSource::TEXTFIELD_CHANGED)
      OnTextFieldDidChange(*ToWebInputElement(&element));
    else {
      FormData form;
      FormFieldData field;
      if (form_util::FindFormAndFieldForFormControlElement(element, &form,
                                                           &field)) {
        GetAutofillDriver()->SelectControlDidChange(
            form, field,
            render_frame()->GetRenderView()->ElementBoundsInWindow(element));
      }
    }
  }
}

void AutofillAgent::OnProbablyFormSubmitted() {
  FormData form_data;
  if (GetSubmittedForm(&form_data)) {
    FireHostSubmitEvents(form_data, /*known_success=*/false,
                         SubmissionSource::PROBABLY_FORM_SUBMITTED);
  }
  ResetLastInteractedElements();
  OnFormNoLongerSubmittable();
}

void AutofillAgent::OnFormSubmitted(const WebFormElement& form) {
  // Fire the submission event here because WILL_SEND_SUBMIT_EVENT is skipped
  // if javascript calls submit() directly.
  FireHostSubmitEvents(form, /*known_success=*/false,
                       SubmissionSource::FORM_SUBMISSION);
  ResetLastInteractedElements();
  OnFormNoLongerSubmittable();
}

void AutofillAgent::OnInferredFormSubmission(SubmissionSource source) {
  // Only handle iframe for FRAME_DETACHED or main frame for
  // SAME_DOCUMENT_NAVIGATION.
  if ((source == SubmissionSource::FRAME_DETACHED &&
       !render_frame()->GetWebFrame()->Parent()) ||
      (source == SubmissionSource::SAME_DOCUMENT_NAVIGATION &&
       render_frame()->GetWebFrame()->Parent())) {
    ResetLastInteractedElements();
    OnFormNoLongerSubmittable();
    return;
  }

  if (source == SubmissionSource::FRAME_DETACHED) {
    // Should not access the frame because it is now detached. Instead, use
    // |provisionally_saved_form_|.
    if (provisionally_saved_form_)
      FireHostSubmitEvents(*provisionally_saved_form_, /*known_success=*/true,
                           source);
  } else {
    FormData form_data;
    if (GetSubmittedForm(&form_data))
      FireHostSubmitEvents(form_data, /*known_success=*/true, source);
  }
  ResetLastInteractedElements();
  OnFormNoLongerSubmittable();
}

void AutofillAgent::AddFormObserver(Observer* observer) {
  form_tracker_.AddObserver(observer);
}

void AutofillAgent::RemoveFormObserver(Observer* observer) {
  form_tracker_.RemoveObserver(observer);
}

bool AutofillAgent::GetSubmittedForm(FormData* form) {
  if (!last_interacted_form_.IsNull()) {
    if (form_util::ExtractFormData(last_interacted_form_, form)) {
      return true;
    } else if (provisionally_saved_form_) {
      *form = *provisionally_saved_form_;
      return true;
    }
  } else if (formless_elements_user_edited_.size() != 0 &&
             !form_util::IsSomeControlElementVisible(
                 formless_elements_user_edited_)) {
    // we check if all the elements the user has interacted with are gone,
    // to decide if submission has occurred, and use the
    // provisionally_saved_form_ saved in OnProvisionallySaveForm() if fail to
    // construct form.
    if (CollectFormlessElements(form)) {
      return true;
    } else if (provisionally_saved_form_) {
      *form = *provisionally_saved_form_;
      return true;
    }
  }
  return false;
}

void AutofillAgent::ResetLastInteractedElements() {
  last_interacted_form_.Reset();
  last_clicked_form_control_element_for_testing_.Reset();
  formless_elements_user_edited_.clear();
  provisionally_saved_form_.reset();
}

void AutofillAgent::UpdateLastInteractedForm(blink::WebFormElement form) {
  last_interacted_form_ = form;
  provisionally_saved_form_ = std::make_unique<FormData>();
  if (!form_util::ExtractFormData(last_interacted_form_,
                                  provisionally_saved_form_.get())) {
    provisionally_saved_form_.reset();
  }
}

void AutofillAgent::OnFormNoLongerSubmittable() {
  submitted_forms_.clear();
}

void AutofillAgent::ReplaceElementIfNowInvalid(const FormData& original_form) {
  // If the document is invalid, bail out.
  if (element_.GetDocument().IsNull())
    return;

  if (!element_.Form().IsNull()) {
    // If |element_|'s parent form has no elements, |element_| is now invalid
    // and should be updated.
    WebVector<WebFormControlElement> form_elements;
    element_.Form().GetFormControlElements(form_elements);
    if (!form_elements.empty())
      return;
  }

  // Try to find the new version of the form.
  WebFormElement form_element;
  WebVector<WebFormElement> forms;
  element_.GetDocument().Forms(forms);
  for (const WebFormElement& form : forms) {
    if (original_form.name == form.GetName().Utf16() ||
        original_form.name == form.GetAttribute("id").Utf16()) {
      form_element = form;
      break;
    }
  }

  WebVector<WebFormControlElement> elements;
  if (form_element.IsNull()) {
    // Could not find the new version of the form, get all the unowned elements.
    std::vector<WebElement> fieldsets;
    elements = form_util::GetUnownedAutofillableFormFieldElements(
        element_.GetDocument().All(), &fieldsets);
  } else {
    // Get all the elements of the new version of the form.
    form_element.GetFormControlElements(elements);
  }

  // Try to find the new version of the last interacted element.
  for (const WebFormControlElement& element : elements) {
    if (element_.NameForAutofill() == element.NameForAutofill()) {
      element_ = element;
      return;
    }
  }
}

const mojom::AutofillDriverPtr& AutofillAgent::GetAutofillDriver() {
  if (!autofill_driver_) {
    render_frame()->GetRemoteInterfaces()->GetInterface(
        mojo::MakeRequest(&autofill_driver_));
  }

  return autofill_driver_;
}

const mojom::PasswordManagerDriverAssociatedPtr&
AutofillAgent::GetPasswordManagerDriver() {
  DCHECK(password_autofill_agent_);
  return password_autofill_agent_->GetPasswordManagerDriver();
}

}  // namespace autofill
