chromium/components/autofill/content/renderer/autofill_agent_browsertest.cc

// Copyright 2021 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/autofill_agent.h"

#include <stdint.h>

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

#include "base/feature_list.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "components/autofill/content/common/mojom/autofill_driver.mojom.h"
#include "components/autofill/content/renderer/autofill_agent_test_api.h"
#include "components/autofill/content/renderer/autofill_renderer_test.h"
#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/form_tracker.h"
#include "components/autofill/content/renderer/test_utils.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/field_data_manager.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_data_test_api.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
#include "components/autofill/core/common/unique_ids.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/features_generated.h"
#include "third_party/blink/public/common/metrics/document_update_reason.h"
#include "third_party/blink/public/web/web_autofill_state.h"
#include "third_party/blink/public/web/web_form_control_element.h"
#include "third_party/blink/public/web/web_frame_widget.h"

namespace autofill {

namespace {

_;
AllOf;
AtMost;
DoAll;
ElementsAre;
ElementsAreArray;
Eq;
Field;
IsEmpty;
IsNull;
Matcher;
NiceMock;
Optional;
Property;
SaveArg;
SizeIs;

constexpr CallTimerState kCallTimerStateDummy =;

class MockAutofillAgent : public AutofillAgent {};

class MockFormTracker : public FormTracker {};

template <typename... Args>
auto FieldsAre(Args&&... matchers) {}

// Matches a `FormData` whose `FormData::fields`' `FormFieldData::id_attribute`
// match `id_attributes`.
template <typename... Args>
auto HasFieldsWithIdAttributes(Args&&... id_attributes) {}

// Matches a `FormData` with a specific `FormData::renderer_id`.
auto HasFormId(FormRendererId expectation) {}

// Matches a `FormData` with a specific `FormData::id_attribute`.
auto HasFormIdAttribute(std::u16string id_attribute) {}

auto HasFieldIdAttribute(std::u16string id_attribute) {}

auto HasSelectedText(std::u16string selected_text) {}

auto HasValue(std::u16string value) {}

// Matches a FormData with |num| FormData::fields.
auto HasNumFields(size_t num) {}

// Matches a FormData with |num| FormData::child_frames.
auto HasNumChildFrames(size_t num) {}

// Matches a container with a single element which (the element) matches all
// |element_matchers|.
auto HasSingleElementWhich(auto... element_matchers) {
  return AllOf(SizeIs(1), ElementsAre(AllOf(element_matchers...)));
}

auto HasType(FormControlType type) {}

// TODO(crbug.com/41268731): Add many more test cases.
class AutofillAgentTest : public test::AutofillRendererTest {};

class AutofillAgentTestWithFeatures : public AutofillAgentTest {};

TEST_F(AutofillAgentTestWithFeatures, FormsSeen_Empty) {}

TEST_F(AutofillAgentTestWithFeatures, FormsSeen_NoEmpty) {}

TEST_F(AutofillAgentTestWithFeatures, FormsSeen_NewFormUnowned) {}

TEST_F(AutofillAgentTestWithFeatures, FormsSeen_NewForm) {}

TEST_F(AutofillAgentTestWithFeatures, FormsSeen_NewIframe) {}

TEST_F(AutofillAgentTestWithFeatures, FormsSeen_UpdatedForm) {}

// Tests that when AutofillDetectRemovedFormControls is enabled, Autofill is
// directly notified of removed form elements.
TEST_F(AutofillAgentTestWithFeatures, FormsSeen_RemovedInput) {}

TEST_F(AutofillAgentTestWithFeatures, TriggerFormExtractionWithResponse) {}

AutofillAgentShadowDomTest;

// Tests that unassociated form control elements in a Shadow DOM tree that do
// not have a form ancestor are extracted correctly.
TEST_F(AutofillAgentShadowDomTest, UnownedUnassociatedElements) {}

// Tests that unassociated form control elements whose closest shadow-tree
// including form ancestor is not in a shadow tree are extracted correctly.
TEST_F(AutofillAgentShadowDomTest, UnassociatedElementsOwnedByNonShadowForm) {}

// Tests that form control elements that are placed into a slot that is a child
// of a form inside a shadow DOM are not considered to be owned by the form
// inside the shadow DOM, but are considered to be unowned. This is consistent
// with how the DOM handles these form control elements - the "elements" of the
// form "ft" are considered to be empty.
TEST_F(AutofillAgentShadowDomTest, FormControlInsideSlotWithinFormInShadowDom) {}

// Tests that a form that is inside a shadow tree and does not have a
// shadow-tree-including form ancestor is extracted correctly.
TEST_F(AutofillAgentShadowDomTest, ElementsOwnedByFormInShadowTree) {}

// Tests that a form whose shadow-tree including descendants include another
// form element, is extracted correctly.
TEST_F(AutofillAgentShadowDomTest, NestedForms) {}

// Tests that explicit form associations are handled correctly.
TEST_F(AutofillAgentShadowDomTest, NestedFormsWithAssociation) {}

// Tests that multiple nested shadow DOM forms are extracted properly.
TEST_F(AutofillAgentShadowDomTest, MultipleNestedForms) {}

// Tests that nested shadow DOM forms are extracted properly even if the nesting
// is multiple levels deep.
TEST_F(AutofillAgentShadowDomTest, DeepNestedForms) {}

class AutofillAgentTestExtractForms : public AutofillAgentTestWithFeatures {};

TEST_F(AutofillAgentTestExtractForms, CallbackIsCalledIfFormIsNotFound) {}

TEST_F(AutofillAgentTestExtractForms, CallbackIsCalledForForm) {}

TEST_F(AutofillAgentTestExtractForms, CallbackIsCalledForFormlessFields) {}

TEST_F(AutofillAgentTestExtractForms, CallbackIsCalledForContentEditable) {}

TEST_F(AutofillAgentTestWithFeatures,
       TriggerFormExtractionWithResponse_CalledTwice) {}

// Tests that `AutofillDriver::TriggerSuggestions()` triggers
// `AutofillAgent::AskForValuesToFill()` (which will ultimately trigger
// suggestions).
TEST_F(AutofillAgentTestWithFeatures, TriggerSuggestions) {}

TEST_F(AutofillAgentTestWithFeatures,
       TriggerSuggestionsForElementWithDatalist) {}

// Tests that `AutofillDriver::TriggerSuggestions()` works for contenteditables.
TEST_F(AutofillAgentTestWithFeatures, TriggerSuggestionsForContenteditable) {}

// Tests that AutofillAgent::ApplyFormAction(kFill, kPreview) and
// AutofillAgent::ClearPreviewedForm correctly set/reset the autofill state of a
// field.
TEST_F(AutofillAgentTest, PreviewThenClear) {}

// Tests that when JS modifies a value, the autofill state is only lost if the
// changes were not simple reformatting changes.
TEST_F(AutofillAgentTest, JavaScriptChangedValue_AutofillState) {}

class AutofillAgentSubmissionTest : public AutofillAgentTest,
                                    public testing::WithParamInterface<int> {};

INSTANTIATE_TEST_SUITE_P();

// Test that AutofillAgent::JavaScriptChangedValue updates the
// last interacted saved state.
TEST_P(AutofillAgentSubmissionTest,
       JavaScriptChangedValueUpdatesLastInteractedSavedState) {}

// Test that AutofillAgent::ApplyFormAction(mojom::ActionPersistence::kFill)
// updates the last interacted saved state when the <input>s have no containing
// <form>.
TEST_P(AutofillAgentSubmissionTest,
       FormlessApplyFormActionUpdatesLastInteractedSavedState) {}

// Test that AutofillAgent::ApplyFormAction(mojom::ActionPersistence::kFill)
// updates the last interacted saved state when the <input>s have a containing
// <form>.
TEST_P(AutofillAgentSubmissionTest,
       FormApplyFormActionUpdatesLastInteractedSavedState) {}

TEST_P(AutofillAgentSubmissionTest,
       HideElementTriggersFormTracker_DisplayNone) {}

TEST_P(AutofillAgentSubmissionTest,
       HideElementTriggersFormTracker_VisibilityHidden) {}

TEST_P(AutofillAgentSubmissionTest, HideElementTriggersFormTracker_TypeHidden) {}

TEST_P(AutofillAgentSubmissionTest, HideElementTriggersFormTracker_HiddenTrue) {}

TEST_P(AutofillAgentSubmissionTest, HideElementTriggersFormTracker_ShadowDom) {}

// Test that an inferred form submission as a result of a page deleting ALL of
// the <input>s (that the user has edited) on a page with no <form> sends the
// contents of all of the fields to the browser.
TEST_P(AutofillAgentSubmissionTest,
       FormlessOnInferredFormSubmissionAfterXhrAndAllInputsRemoved) {}

// Tests that an inferred form submission as a result of a page deleting ALL of
// the <input>s that the user has edited but NOT ALL of the <inputs> on the page
// sends the user-edited <inputs> to the browser.
TEST_P(AutofillAgentSubmissionTest,
       FormlessOnInferredFormSubmissionAfterXhrAndSomeInputsRemoved) {}

// Test scenario WHERE:
// - AutofillAgent::OnProbablyFormSubmitted() is called as a result of a page
// navigation. AND
// - There is no <form> element.
// AND
// - An <input> other than the last interacted <input> is hidden.
// THAT
// The edited <input>s are sent to the browser.
TEST_P(AutofillAgentSubmissionTest,
       FormlessOnNavigationAfterSomeInputsRemoved) {}

// Test that in the scenario that:
// - The user autofills a form which dynamically removes -
//   during autofill - `AutofillAgent::last_queried_element_` from the DOM
//   hierarchy.
// THAT
// - Inferred form submission as a result of the page removing the <form> from
//   the DOM hierarchy does not send fields which were removed from the DOM
//   hierarchy at autofill time.
TEST_P(AutofillAgentSubmissionTest,
       OnInferredFormSubmissionAfterAutofillRemovesLastQueriedElement) {}

class AutofillAgentTestNavigationReset : public AutofillAgentTest {};

TEST_F(AutofillAgentTestNavigationReset, NavigationResetsIsDomContentLoaded) {}

// Test fixture for FocusedElementChanged().
class AutofillAgentTestFocus : public AutofillAgentTest {};

// Tests that when the focus moves from field to field, FocusedElementChanged()
// fires FocusOnFormField() and FocusOnNonFormField().
TEST_F(AutofillAgentTestFocus, FireFocusEventsWhenCyclingThroughFields) {}

// Tests that when the focus switches between an uneditable <div> and
// a field, FocusedElementChanged() fires FocusOnFormField() and
// FocusOnNonFormField().
TEST_F(AutofillAgentTestFocus,
       FireFocusEventsWhenSwitchingBetweenFieldAndNonField) {}

// Tests that FocusedElementChanged() treats null as a non-FormField.
TEST_F(AutofillAgentTestFocus, FireFocusEventsForNullElement) {}

// Test fixture for caret position extraction and movement detection.
class AutofillAgentTestCaret
    : public AutofillAgentTest,
      public ::testing::WithParamInterface<FormControlType> {};

INSTANTIATE_TEST_SUITE_P();

// Tests that AskForValuesToFill() is parameterized with the caret position.
TEST_P(AutofillAgentTestCaret, AskForValuesToFillContainsCaret) {}

// Tests that CaretMovedInFormField() is fired for each caret movement, provided
// there's enough time between the movements.
TEST_P(AutofillAgentTestCaret, MovingCaretSlowlyFiresEvent) {}

// Tests that CaretMovedInFormField() is fired in a throttled manner when the
// caret moves fast.
TEST_P(AutofillAgentTestCaret, MovingCaretFastThrottlesEvent) {}

// Tests that selecting text fires CaretMovedInFormField() with the text
// selection.
TEST_P(AutofillAgentTestCaret, SelectionFiresEvent) {}

// Tests fixture for click handling.
class AutofillAgentTestClick
    : public AutofillAgentTest,
      public ::testing::WithParamInterface<const char*> {};

INSTANTIATE_TEST_SUITE_P();

// Tests that clicking on a field triggers AskForValuesToFillOnClick().
// TODO(crbug.com/342126797): Fix Android's OnAskForValuesToFill() event.
#if !BUILDFLAG(IS_ANDROID)
#define MAYBE_AskForValuesToFillOnClick
#else
#define MAYBE_AskForValuesToFillOnClick
#endif
TEST_P(AutofillAgentTestClick, MAYBE_AskForValuesToFillOnClick) {}

// Tests that DOMContentLoaded() emits a metric.
TEST_F(AutofillAgentTest, DOMContentLoadedEmitsMetric) {}

}  // namespace

}  // namespace autofill