blob: e50d0ac58134573854b14271a83be6f62d90af94 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h"
#include <optional>
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h"
#include "chrome/browser/ui/autofill/test_autofill_popup_controller_autofill_client.h"
#include "components/autofill/core/browser/ui/suggestion.h"
#include "components/autofill/core/browser/ui/suggestion_type.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_active_popup.h"
#include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_tree_id.h"
#include "ui/accessibility/ax_tree_manager.h"
#include "ui/accessibility/ax_tree_manager_map.h"
#include "ui/accessibility/platform/ax_platform_node_base.h"
#include "ui/accessibility/platform/ax_platform_node_delegate.h"
#include "ui/gfx/range/range.h"
#if !BUILDFLAG(IS_CHROMEOS_ASH)
#include "content/public/test/scoped_accessibility_mode_override.h"
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
namespace autofill {
namespace {
using ::testing::_;
using ::testing::AllOf;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::Field;
using ::testing::Matcher;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::Return;
using SingleEntryRemovalMethod =
autofill::AutofillMetrics::SingleEntryRemovalMethod;
Matcher<const AutofillPopupDelegate::SuggestionPosition&>
EqualsSuggestionPosition(AutofillPopupDelegate::SuggestionPosition position) {
return AllOf(
Field(&AutofillPopupDelegate::SuggestionPosition::row, position.row),
Field(&AutofillPopupDelegate::SuggestionPosition::sub_popup_level,
position.sub_popup_level));
}
} // namespace
using AutofillPopupControllerImplTest = AutofillSuggestionControllerTestBase<
TestAutofillPopupControllerAutofillClient<>>;
TEST_F(AutofillPopupControllerImplTest, SubPopupIsCreatedWithViewFromParent) {
base::WeakPtr<AutofillSuggestionController> sub_controller =
client().popup_controller(manager()).OpenSubPopup(
{0, 0, 10, 10}, {}, AutoselectFirstSuggestion(false));
EXPECT_TRUE(sub_controller);
}
TEST_F(AutofillPopupControllerImplTest,
DelegateMethodsAreCalledOnlyByRootPopup) {
EXPECT_CALL(manager().external_delegate(), OnPopupShown()).Times(0);
base::WeakPtr<AutofillSuggestionController> sub_controller =
client().popup_controller(manager()).OpenSubPopup(
{0, 0, 10, 10}, {}, AutoselectFirstSuggestion(false));
EXPECT_CALL(manager().external_delegate(), OnPopupHidden()).Times(0);
sub_controller->Hide(SuggestionHidingReason::kUserAborted);
EXPECT_CALL(manager().external_delegate(), OnPopupHidden());
client().popup_controller(manager()).Hide(
SuggestionHidingReason::kUserAborted);
}
TEST_F(AutofillPopupControllerImplTest, EventsAreDelegatedToChildrenAndView) {
EXPECT_CALL(manager().external_delegate(), OnPopupShown()).Times(0);
base::WeakPtr<AutofillSuggestionController> sub_controller =
client().popup_controller(manager()).OpenSubPopup(
{0, 0, 10, 10}, {}, AutoselectFirstSuggestion(false));
content::NativeWebKeyboardEvent event = CreateKeyPressEvent(ui::VKEY_LEFT);
EXPECT_CALL(*client().sub_popup_view(), HandleKeyPressEvent)
.WillOnce(Return(true));
EXPECT_CALL(*client().popup_view(), HandleKeyPressEvent).Times(0);
EXPECT_TRUE(client().popup_controller(manager()).HandleKeyPressEvent(event));
EXPECT_CALL(*client().sub_popup_view(), HandleKeyPressEvent)
.WillOnce(Return(false));
EXPECT_CALL(*client().popup_view(), HandleKeyPressEvent).Times(1);
EXPECT_FALSE(client().popup_controller(manager()).HandleKeyPressEvent(event));
}
// Tests that the controller forwards calls to perform a button action (such as
// clicking a close button on a suggestion) to its delegate.
TEST_F(AutofillPopupControllerImplTest, ButtonActionsAreSentToDelegate) {
ShowSuggestions(manager(), {SuggestionType::kCompose});
EXPECT_CALL(manager().external_delegate(),
DidPerformButtonActionForSuggestion);
client().popup_controller(manager()).PerformButtonActionForSuggestion(0);
}
// The second popup is also the second "sub_popup_level". This test asserts that
// the information regarding the popup level is passed on to the delegate.
TEST_F(AutofillPopupControllerImplTest, PopupForwardsSuggestionPosition) {
base::WeakPtr<AutofillSuggestionController> sub_controller =
client().popup_controller(manager()).OpenSubPopup(
{0, 0, 10, 10}, {Suggestion(SuggestionType::kAddressEntry)},
AutoselectFirstSuggestion(false));
ASSERT_TRUE(sub_controller);
static_cast<AutofillPopupControllerImpl*>(sub_controller.get())
->SetViewForTesting(client().sub_popup_view()->GetWeakPtr());
EXPECT_CALL(manager().external_delegate(),
DidAcceptSuggestion(_, EqualsSuggestionPosition(
{.row = 0, .sub_popup_level = 1})));
task_environment()->FastForwardBy(base::Milliseconds(1000));
sub_controller->AcceptSuggestion(/*index=*/0);
}
TEST_F(AutofillPopupControllerImplTest,
ManualFallBackTriggerSource_IgnoresClickOutsideCheck) {
ShowSuggestions(manager(), {SuggestionType::kAddressEntry},
AutofillSuggestionTriggerSource::kManualFallbackAddress);
// Generate a popup, so it can be hidden later. It doesn't matter what the
// external_delegate thinks is being shown in the process, since we are just
// testing the popup here.
test::GenerateTestAutofillPopup(&manager().external_delegate());
EXPECT_TRUE(client()
.popup_controller(manager())
.ShouldIgnoreMouseObservedOutsideItemBoundsCheck());
}
// Tests that the popup controller queries the view for its screen location.
TEST_F(AutofillPopupControllerImplTest, GetPopupScreenLocationCallsView) {
ShowSuggestions(manager(), {SuggestionType::kCompose});
using PopupScreenLocation = AutofillClient::PopupScreenLocation;
constexpr gfx::Rect kSampleRect = gfx::Rect(123, 234);
EXPECT_CALL(*client().popup_view(), GetPopupScreenLocation)
.WillOnce(Return(PopupScreenLocation{.bounds = kSampleRect}));
EXPECT_THAT(client().popup_controller(manager()).GetPopupScreenLocation(),
Optional(Field(&PopupScreenLocation::bounds, kSampleRect)));
}
// Tests that a change to a text field hides a popup with a Compose suggestion.
TEST_F(AutofillPopupControllerImplTest, HidesOnFieldChangeForComposeEntries) {
ShowSuggestions(manager(), {SuggestionType::kCompose});
EXPECT_CALL(client().popup_controller(manager()),
Hide(SuggestionHidingReason::kFieldValueChanged));
manager().NotifyObservers(
&AutofillManager::Observer::OnBeforeTextFieldDidChange, FormGlobalId(),
FieldGlobalId());
}
// Tests that Compose saved state notification popup gets hidden after 2
// seconds, but not after 1 second.
TEST_F(AutofillPopupControllerImplTest,
TimedHideComposeSavedStateNotification) {
ShowSuggestions(manager(), {SuggestionType::kComposeSavedStateNotification});
test::GenerateTestAutofillPopup(&manager().external_delegate());
::testing::MockFunction<void()> check;
{
::testing::InSequence s;
EXPECT_CALL(check, Call);
EXPECT_CALL(client().popup_controller(manager()),
Hide(SuggestionHidingReason::kFadeTimerExpired));
}
task_environment()->FastForwardBy(base::Seconds(1));
check.Call();
task_environment()->FastForwardBy(base::Seconds(1));
Mock::VerifyAndClearExpectations(&client().popup_controller(manager()));
}
TEST_F(AutofillPopupControllerImplTest,
PopupHidesOnWebContentsFocusLossIfViewIsNotFocused) {
ShowSuggestions(manager(), {SuggestionType::kAddressEntry});
EXPECT_CALL(*client().popup_view(), HasFocus).WillOnce(Return(false));
EXPECT_CALL(*client().popup_view(), Hide);
client().popup_controller(manager()).Hide(
SuggestionHidingReason::kFocusChanged);
Mock::VerifyAndClearExpectations(client().popup_view());
}
TEST_F(AutofillPopupControllerImplTest,
PopupDoesntHideOnWebContentsFocusLossIfViewIsFocused) {
ShowSuggestions(manager(), {SuggestionType::kAddressEntry});
EXPECT_CALL(*client().popup_view(), HasFocus).WillOnce(Return(true));
EXPECT_CALL(*client().popup_view(), Hide).Times(0);
client().popup_controller(manager()).Hide(
SuggestionHidingReason::kFocusChanged);
Mock::VerifyAndClearExpectations(client().popup_view());
}
TEST_F(AutofillPopupControllerImplTest,
PopupDoesntHideOnEndEditingFromRendererIfViewIsFocused) {
ShowSuggestions(manager(), {SuggestionType::kAddressEntry});
EXPECT_CALL(*client().popup_view(), HasFocus).WillOnce(Return(true));
EXPECT_CALL(*client().popup_view(), Hide).Times(0);
client().popup_controller(manager()).Hide(
SuggestionHidingReason::kEndEditing);
Mock::VerifyAndClearExpectations(client().popup_view());
}
TEST_F(AutofillPopupControllerImplTest,
RemoveAutocompleteSuggestion_IgnoresClickOutsideCheck) {
ShowSuggestions(manager(), {SuggestionType::kAutocompleteEntry,
SuggestionType::kAutocompleteEntry});
// Generate a popup, so it can be hidden later. It doesn't matter what the
// external_delegate thinks is being shown in the process, since we are just
// testing the popup here.
test::GenerateTestAutofillPopup(&manager().external_delegate());
EXPECT_CALL(manager().external_delegate(),
RemoveSuggestion(
Field(&Suggestion::type, SuggestionType::kAutocompleteEntry)))
.WillOnce(Return(true));
// Remove the first entry. The popup should be redrawn since its size has
// changed.
EXPECT_CALL(*client().popup_view(), OnSuggestionsChanged());
EXPECT_TRUE(client().popup_controller(manager()).RemoveSuggestion(
0,
AutofillMetrics::SingleEntryRemovalMethod::kKeyboardShiftDeletePressed));
Mock::VerifyAndClearExpectations(client().popup_view());
EXPECT_TRUE(client()
.popup_controller(manager())
.ShouldIgnoreMouseObservedOutsideItemBoundsCheck());
}
// Tests that if the popup is shown in the *main frame*, changing the zoom hides
// the popup.
TEST_F(AutofillPopupControllerImplTest, HideInMainFrameOnZoomChange) {
zoom::ZoomController::CreateForWebContents(web_contents());
ShowSuggestions(manager(), {SuggestionType::kAddressEntry});
test::GenerateTestAutofillPopup(&manager().external_delegate());
// Triggered by OnZoomChanged().
EXPECT_CALL(client().popup_controller(manager()),
Hide(SuggestionHidingReason::kContentAreaMoved));
// Override the default ON_CALL behavior to do nothing to avoid destroying the
// hide helper. We want to test ZoomObserver events explicitly.
EXPECT_CALL(client().popup_controller(manager()),
Hide(SuggestionHidingReason::kWidgetChanged))
.WillOnce(Return());
auto* zoom_controller = zoom::ZoomController::FromWebContents(web_contents());
zoom_controller->SetZoomLevel(zoom_controller->GetZoomLevel() + 1.0);
// Verify and clear before TearDown() closes the popup.
Mock::VerifyAndClearExpectations(&client().popup_controller(manager()));
}
TEST_F(AutofillPopupControllerImplTest,
SuggestionFiltration_NoFilteringByDefault) {
AutofillPopupController& controller = client().popup_controller(manager());
ShowSuggestions(manager(), {Suggestion(u"abc")});
EXPECT_EQ(controller.GetSuggestions().size(), 1u);
EXPECT_EQ(controller.GetSuggestionFilterMatches().size(), 0u);
}
TEST_F(AutofillPopupControllerImplTest,
SuggestionFiltration_SuggestionChangeNotifications) {
AutofillPopupController& controller = client().popup_controller(manager());
ShowSuggestions(manager(), {
Suggestion(u"abc"),
Suggestion(u"axx"),
});
EXPECT_CALL(*client().popup_view(), OnSuggestionsChanged());
controller.SetFilter(AutofillPopupController::SuggestionFilter(u"ab"));
EXPECT_CALL(*client().popup_view(), OnSuggestionsChanged());
controller.SetFilter(std::nullopt);
}
TEST_F(AutofillPopupControllerImplTest, SuggestionFiltration_MatchingMainText) {
AutofillPopupController& controller = client().popup_controller(manager());
ShowSuggestions(manager(), {
Suggestion(u"abc"),
Suggestion(u"abx"),
Suggestion(u"axx"),
});
EXPECT_EQ(controller.GetSuggestions().size(), 3u);
EXPECT_EQ(controller.GetSuggestionFilterMatches().size(), 0u);
controller.SetFilter(AutofillPopupController::SuggestionFilter(u"ab"));
EXPECT_EQ(controller.GetSuggestions().size(), 2u);
EXPECT_EQ(controller.GetSuggestionFilterMatches().size(), 2u);
EXPECT_THAT(controller.GetSuggestionFilterMatches(),
::testing::ElementsAre(
AutofillPopupController::SuggestionFilterMatch{
.main_text_match = gfx::Range(0, 2),
},
AutofillPopupController::SuggestionFilterMatch{
.main_text_match = gfx::Range(0, 2),
}));
controller.SetFilter(AutofillPopupController::SuggestionFilter(u"abcdefg"));
EXPECT_EQ(controller.GetSuggestions().size(), 0u);
EXPECT_EQ(controller.GetSuggestionFilterMatches().size(), 0u);
controller.SetFilter(std::nullopt);
EXPECT_EQ(controller.GetSuggestions().size(), 3u);
EXPECT_EQ(controller.GetSuggestionFilterMatches().size(), 0u);
}
TEST_F(AutofillPopupControllerImplTest,
SuggestionFiltration_SuggestionIsDeletedFromFilteredList) {
AutofillPopupController& controller = client().popup_controller(manager());
ShowSuggestions(manager(), {
Suggestion(u"abc"),
Suggestion(u"abx"),
Suggestion(u"axx"),
});
controller.SetFilter(AutofillPopupController::SuggestionFilter(u"ab"));
EXPECT_EQ(controller.GetSuggestions().size(), 2u);
EXPECT_CALL(manager().external_delegate(), RemoveSuggestion)
.WillOnce(Return(true));
controller.RemoveSuggestion(
0, AutofillMetrics::SingleEntryRemovalMethod::kDeleteButtonClicked);
EXPECT_EQ(controller.GetSuggestions().size(), 1u);
EXPECT_EQ(controller.GetSuggestionFilterMatches().size(), 1u);
controller.SetFilter(std::nullopt);
EXPECT_EQ(controller.GetSuggestions().size(), 2u);
EXPECT_EQ(controller.GetSuggestionFilterMatches().size(), 0u);
}
TEST_F(AutofillPopupControllerImplTest,
SuggestionFiltration_FooterSuggestionsAreNotFiltratable) {
using enum SuggestionType;
AutofillPopupController& controller = client().popup_controller(manager());
ShowSuggestions(manager(), {
Suggestion(u"abc", kAddressEntry),
Suggestion(u"abx", kAddressEntry),
Suggestion(kSeparator),
Suggestion(kClearForm),
});
controller.SetFilter(AutofillPopupController::SuggestionFilter(u"ab"));
EXPECT_EQ(controller.GetSuggestions().size(), 4u);
EXPECT_THAT(controller.GetSuggestions(),
ElementsAre(Field(&Suggestion::type, kAddressEntry),
Field(&Suggestion::type, kAddressEntry),
Field(&Suggestion::type, kSeparator),
Field(&Suggestion::type, kClearForm)));
controller.SetFilter(AutofillPopupController::SuggestionFilter(u"abc"));
EXPECT_EQ(controller.GetSuggestions().size(), 3u);
EXPECT_THAT(controller.GetSuggestions(),
ElementsAre(Field(&Suggestion::type, kAddressEntry),
Field(&Suggestion::type, kSeparator),
Field(&Suggestion::type, kClearForm)));
controller.SetFilter(AutofillPopupController::SuggestionFilter(u"abcdef"));
EXPECT_EQ(controller.GetSuggestions().size(), 2u);
EXPECT_THAT(controller.GetSuggestions(),
ElementsAre(Field(&Suggestion::type, kSeparator),
Field(&Suggestion::type, kClearForm)));
}
TEST_F(AutofillPopupControllerImplTest, RemoveSuggestion) {
ShowSuggestions(manager(),
{SuggestionType::kAddressEntry, SuggestionType::kAddressEntry,
SuggestionType::kAutofillOptions});
// Generate a popup, so it can be hidden later. It doesn't matter what the
// external_delegate thinks is being shown in the process, since we are just
// testing the popup here.
test::GenerateTestAutofillPopup(&manager().external_delegate());
EXPECT_CALL(
manager().external_delegate(),
RemoveSuggestion(Field(&Suggestion::type, SuggestionType::kAddressEntry)))
.WillRepeatedly(Return(true));
// Remove the first entry. The popup should be redrawn since its size has
// changed.
EXPECT_CALL(*client().popup_view(), OnSuggestionsChanged());
EXPECT_TRUE(client().popup_controller(manager()).RemoveSuggestion(
0, SingleEntryRemovalMethod::kKeyboardShiftDeletePressed));
Mock::VerifyAndClearExpectations(client().popup_view());
// Remove the next entry. The popup should then be hidden since there are
// no Autofill entries left.
EXPECT_CALL(client().popup_controller(manager()),
Hide(SuggestionHidingReason::kNoSuggestions));
EXPECT_TRUE(client().popup_controller(manager()).RemoveSuggestion(
0, SingleEntryRemovalMethod::kKeyboardShiftDeletePressed));
}
TEST_F(AutofillPopupControllerImplTest,
RemoveAutocompleteSuggestion_AnnounceText) {
ShowSuggestions(manager(), {Suggestion(u"main text",
SuggestionType::kAutocompleteEntry)});
test::GenerateTestAutofillPopup(&manager().external_delegate());
EXPECT_CALL(manager().external_delegate(),
RemoveSuggestion(
Field(&Suggestion::type, SuggestionType::kAutocompleteEntry)))
.WillOnce(Return(true));
EXPECT_CALL(*client().popup_view(),
AxAnnounce(Eq(u"Entry main text has been deleted")));
EXPECT_TRUE(client().popup_controller(manager()).RemoveSuggestion(
0, SingleEntryRemovalMethod::kKeyboardShiftDeletePressed));
}
TEST_F(AutofillPopupControllerImplTest,
RemoveAutocompleteSuggestion_NoMetricsEmittedOnFail) {
base::HistogramTester histogram_tester;
ShowSuggestions(manager(), {SuggestionType::kAutocompleteEntry});
test::GenerateTestAutofillPopup(&manager().external_delegate());
EXPECT_CALL(manager().external_delegate(),
RemoveSuggestion(
Field(&Suggestion::type, SuggestionType::kAutocompleteEntry)))
.WillOnce(Return(false));
EXPECT_FALSE(client().popup_controller(manager()).RemoveSuggestion(
0, SingleEntryRemovalMethod::kKeyboardShiftDeletePressed));
histogram_tester.ExpectUniqueSample(
"Autofill.Autocomplete.SingleEntryRemovalMethod",
SingleEntryRemovalMethod::kKeyboardShiftDeletePressed, 0);
histogram_tester.ExpectUniqueSample(
"Autocomplete.Events2",
AutofillMetrics::AutocompleteEvent::AUTOCOMPLETE_SUGGESTION_DELETED, 0);
}
TEST_F(AutofillPopupControllerImplTest,
RemoveAutocompleteSuggestion_MetricsEmittedOnSuccess) {
base::HistogramTester histogram_tester;
ShowSuggestions(manager(), {SuggestionType::kAutocompleteEntry});
test::GenerateTestAutofillPopup(&manager().external_delegate());
EXPECT_CALL(manager().external_delegate(),
RemoveSuggestion(
Field(&Suggestion::type, SuggestionType::kAutocompleteEntry)))
.WillOnce(Return(true));
EXPECT_TRUE(client().popup_controller(manager()).RemoveSuggestion(
0, SingleEntryRemovalMethod::kKeyboardShiftDeletePressed));
histogram_tester.ExpectUniqueSample(
"Autofill.Autocomplete.SingleEntryRemovalMethod",
SingleEntryRemovalMethod::kKeyboardShiftDeletePressed, 1);
histogram_tester.ExpectUniqueSample(
"Autocomplete.Events2",
AutofillMetrics::AutocompleteEvent::AUTOCOMPLETE_SUGGESTION_DELETED, 1);
// Also no autofill metrics are emitted.
histogram_tester.ExpectUniqueSample("Autofill.ProfileDeleted.Popup", 1, 0);
histogram_tester.ExpectUniqueSample(
"Autofill.ProfileDeleted.KeyboardAccessory", 1, 0);
histogram_tester.ExpectUniqueSample("Autofill.ProfileDeleted.Any", 1, 0);
}
TEST_F(AutofillPopupControllerImplTest,
RemoveAddressSuggestion_NoMetricsEmittedOnFail) {
base::HistogramTester histogram_tester;
ShowSuggestions(manager(), {SuggestionType::kAddressEntry});
test::GenerateTestAutofillPopup(&manager().external_delegate());
EXPECT_CALL(
manager().external_delegate(),
RemoveSuggestion(Field(&Suggestion::type, SuggestionType::kAddressEntry)))
.WillOnce(Return(false));
EXPECT_FALSE(client().popup_controller(manager()).RemoveSuggestion(
0, SingleEntryRemovalMethod::kKeyboardShiftDeletePressed));
histogram_tester.ExpectUniqueSample("Autofill.ProfileDeleted.Popup", 1, 0);
histogram_tester.ExpectUniqueSample(
"Autofill.ProfileDeleted.KeyboardAccessory", 1, 0);
histogram_tester.ExpectUniqueSample("Autofill.ProfileDeleted.Any", 1, 0);
}
TEST_F(AutofillPopupControllerImplTest,
RemoveAddressSuggestion_MetricsEmittedOnSuccess) {
base::HistogramTester histogram_tester;
ShowSuggestions(manager(), {SuggestionType::kAddressEntry});
test::GenerateTestAutofillPopup(&manager().external_delegate());
EXPECT_CALL(
manager().external_delegate(),
RemoveSuggestion(Field(&Suggestion::type, SuggestionType::kAddressEntry)))
.WillOnce(Return(true));
EXPECT_TRUE(client().popup_controller(manager()).RemoveSuggestion(
0, SingleEntryRemovalMethod::kKeyboardShiftDeletePressed));
histogram_tester.ExpectUniqueSample("Autofill.ProfileDeleted.Any", 1, 1);
if constexpr (BUILDFLAG(IS_ANDROID)) {
histogram_tester.ExpectUniqueSample("Autofill.ProfileDeleted.Popup", 1, 0);
histogram_tester.ExpectUniqueSample(
"Autofill.ProfileDeleted.KeyboardAccessory", 1, 1);
} else {
histogram_tester.ExpectUniqueSample("Autofill.ProfileDeleted.Popup", 1, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ProfileDeleted.KeyboardAccessory", 1, 0);
}
// No autocomplete deletion metrics are emitted.
histogram_tester.ExpectUniqueSample(
"Autofill.Autocomplete.SingleEntryRemovalMethod",
SingleEntryRemovalMethod::kKeyboardShiftDeletePressed, 0);
histogram_tester.ExpectUniqueSample(
"Autocomplete.Events2",
AutofillMetrics::AutocompleteEvent::AUTOCOMPLETE_SUGGESTION_DELETED, 0);
}
TEST_F(AutofillPopupControllerImplTest,
RemoveCreditCardSuggestion_NoMetricsEmitted) {
base::HistogramTester histogram_tester;
ShowSuggestions(manager(), {SuggestionType::kCreditCardEntry});
test::GenerateTestAutofillPopup(&manager().external_delegate());
EXPECT_CALL(manager().external_delegate(),
RemoveSuggestion(
Field(&Suggestion::type, SuggestionType::kCreditCardEntry)))
.WillOnce(Return(true));
EXPECT_TRUE(client().popup_controller(manager()).RemoveSuggestion(
0, SingleEntryRemovalMethod::kKeyboardShiftDeletePressed));
histogram_tester.ExpectUniqueSample(
"Autofill.Autocomplete.SingleEntryRemovalMethod",
SingleEntryRemovalMethod::kKeyboardShiftDeletePressed, 0);
histogram_tester.ExpectUniqueSample(
"Autocomplete.Events2",
AutofillMetrics::AutocompleteEvent::AUTOCOMPLETE_SUGGESTION_DELETED, 0);
histogram_tester.ExpectUniqueSample("Autofill.ProfileDeleted.Popup", 1, 0);
histogram_tester.ExpectUniqueSample(
"Autofill.ProfileDeleted.KeyboardAccessory", 1, 0);
histogram_tester.ExpectUniqueSample("Autofill.ProfileDeleted.Any", 1, 0);
}
TEST_F(AutofillPopupControllerImplTest, UnselectingClearsPreview) {
EXPECT_CALL(manager().external_delegate(), ClearPreviewedForm());
client().popup_controller(manager()).UnselectSuggestion();
}
#if !BUILDFLAG(IS_CHROMEOS_ASH)
namespace {
class MockAutofillDriver : public ContentAutofillDriver {
public:
using ContentAutofillDriver::ContentAutofillDriver;
MockAutofillDriver(MockAutofillDriver&) = delete;
MockAutofillDriver& operator=(MockAutofillDriver&) = delete;
~MockAutofillDriver() override = default;
MOCK_METHOD(ui::AXTreeID, GetAxTreeId, (), (const override));
};
class AutofillPopupControllerForPopupAxTest
: public AutofillSuggestionControllerForTest {
public:
using AutofillSuggestionControllerForTest::
AutofillSuggestionControllerForTest;
using AutofillSuggestionControllerForTest::FireControlsChangedEvent;
MOCK_METHOD(ui::AXPlatformNode*,
GetRootAXPlatformNodeForWebContents,
(),
(override));
};
class MockAxTreeManager : public ui::AXTreeManager {
public:
MockAxTreeManager() = default;
MockAxTreeManager(MockAxTreeManager&) = delete;
MockAxTreeManager& operator=(MockAxTreeManager&) = delete;
~MockAxTreeManager() override = default;
MOCK_METHOD(ui::AXNode*,
GetNodeFromTree,
(const ui::AXTreeID& tree_id, const int32_t node_id),
(const override));
MOCK_METHOD(ui::AXPlatformNodeDelegate*,
GetDelegate,
(const ui::AXTreeID tree_id, const int32_t node_id),
(const override));
MOCK_METHOD(ui::AXPlatformNodeDelegate*,
GetRootDelegate,
(const ui::AXTreeID tree_id),
(const override));
MOCK_METHOD(ui::AXTreeID, GetTreeID, (), (const override));
MOCK_METHOD(ui::AXTreeID, GetParentTreeID, (), (const override));
MOCK_METHOD(ui::AXNode*, GetRootAsAXNode, (), (const override));
MOCK_METHOD(ui::AXNode*, GetParentNodeFromParentTree, (), (const override));
};
class MockAxPlatformNodeDelegate : public ui::AXPlatformNodeDelegate {
public:
MockAxPlatformNodeDelegate() = default;
MockAxPlatformNodeDelegate(MockAxPlatformNodeDelegate&) = delete;
MockAxPlatformNodeDelegate& operator=(MockAxPlatformNodeDelegate&) = delete;
~MockAxPlatformNodeDelegate() override = default;
MOCK_METHOD(ui::AXPlatformNode*, GetFromNodeID, (int32_t id), (override));
MOCK_METHOD(ui::AXPlatformNode*,
GetFromTreeIDAndNodeID,
(const ui::AXTreeID& tree_id, int32_t id),
(override));
};
class MockAxPlatformNode : public ui::AXPlatformNodeBase {
public:
MockAxPlatformNode() = default;
MockAxPlatformNode(MockAxPlatformNode&) = delete;
MockAxPlatformNode& operator=(MockAxPlatformNode&) = delete;
~MockAxPlatformNode() override = default;
MOCK_METHOD(ui::AXPlatformNodeDelegate*, GetDelegate, (), (const override));
};
} // namespace
using AutofillPopupControllerImplTestAccessibilityBase =
AutofillSuggestionControllerTestBase<
TestAutofillPopupControllerAutofillClient<
NiceMock<AutofillPopupControllerForPopupAxTest>>,
NiceMock<MockAutofillDriver>>;
class AutofillPopupControllerImplTestAccessibility
: public AutofillPopupControllerImplTestAccessibilityBase {
public:
static constexpr int kAxUniqueId = 123;
AutofillPopupControllerImplTestAccessibility()
: accessibility_mode_override_(ui::AXMode::kScreenReader) {}
AutofillPopupControllerImplTestAccessibility(
AutofillPopupControllerImplTestAccessibility&) = delete;
AutofillPopupControllerImplTestAccessibility& operator=(
AutofillPopupControllerImplTestAccessibility&) = delete;
~AutofillPopupControllerImplTestAccessibility() override = default;
void SetUp() override {
AutofillPopupControllerImplTestAccessibilityBase::SetUp();
ON_CALL(driver(), GetAxTreeId()).WillByDefault(Return(test_tree_id_));
ON_CALL(client().popup_controller(manager()),
GetRootAXPlatformNodeForWebContents)
.WillByDefault(Return(&mock_ax_platform_node_));
ON_CALL(mock_ax_platform_node_, GetDelegate)
.WillByDefault(Return(&mock_ax_platform_node_delegate_));
ON_CALL(*client().popup_view(), GetAxUniqueId)
.WillByDefault(Return(std::optional<int32_t>(kAxUniqueId)));
ON_CALL(mock_ax_platform_node_delegate_, GetFromTreeIDAndNodeID)
.WillByDefault(Return(&mock_ax_platform_node_));
}
void TearDown() override {
// This needs to bo reset explicit because having the mode set to
// `kScreenReader` causes mocked functions to get called with
// `mock_ax_platform_node_delegate` after it has been destroyed.
accessibility_mode_override_.ResetMode();
AutofillPopupControllerImplTestAccessibilityBase::TearDown();
}
protected:
content::ScopedAccessibilityModeOverride accessibility_mode_override_;
MockAxPlatformNodeDelegate mock_ax_platform_node_delegate_;
MockAxPlatformNode mock_ax_platform_node_;
ui::AXTreeID test_tree_id_ = ui::AXTreeID::CreateNewAXTreeID();
};
// Test for successfully firing controls changed event for popup show/hide.
TEST_F(AutofillPopupControllerImplTestAccessibility,
FireControlsChangedEventDuringShowAndHide) {
ShowSuggestions(manager(), {SuggestionType::kAddressEntry});
// Manually fire the event for popup show since setting the test view results
// in the fire controls changed event not being sent.
client().popup_controller(manager()).FireControlsChangedEvent(true);
EXPECT_EQ(kAxUniqueId, ui::GetActivePopupAxUniqueId());
client().popup_controller(manager()).DoHide();
EXPECT_EQ(std::nullopt, ui::GetActivePopupAxUniqueId());
}
// Test for attempting to fire controls changed event when ax tree manager
// fails to retrieve the ax platform node associated with the popup.
// No event is fired and global active popup ax unique id is not set.
TEST_F(AutofillPopupControllerImplTestAccessibility,
FireControlsChangedEventNoAxPlatformNode) {
EXPECT_CALL(mock_ax_platform_node_delegate_, GetFromTreeIDAndNodeID)
.WillOnce(Return(nullptr));
ShowSuggestions(manager(), {SuggestionType::kAddressEntry});
// Manually fire the event for popup show since setting the test view results
// in the fire controls changed event not being sent.
client().popup_controller(manager()).FireControlsChangedEvent(true);
EXPECT_EQ(std::nullopt, ui::GetActivePopupAxUniqueId());
}
// Test for attempting to fire controls changed event when failing to retrieve
// the autofill popup's ax unique id. No event is fired and the global active
// popup ax unique id is not set.
TEST_F(AutofillPopupControllerImplTestAccessibility,
FireControlsChangedEventNoPopupAxUniqueId) {
EXPECT_CALL(*client().popup_view(), GetAxUniqueId)
.WillOnce(Return(std::nullopt));
ShowSuggestions(manager(), {SuggestionType::kAddressEntry});
// Manually fire the event for popup show since setting the test view results
// in the fire controls changed event not being sent.
client().popup_controller(manager()).FireControlsChangedEvent(true);
EXPECT_EQ(std::nullopt, ui::GetActivePopupAxUniqueId());
}
#endif
} // namespace autofill