#include "ui/views/focus/focus_manager.h"
#include <stddef.h>
#include <string>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/icu_test_util.h"
#include "base/test/run_until.h"
#include "build/build_config.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/test_accelerator_target.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/accessible_pane_view.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/buildflags.h"
#include "ui/views/controls/button/radio_button.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/focus/focus_manager_delegate.h"
#include "ui/views/focus/focus_manager_factory.h"
#include "ui/views/focus/native_view_focus_manager.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/test/focus_manager_test.h"
#include "ui/views/test/native_widget_factory.h"
#include "ui/views/test/test_platform_native_widget.h"
#include "ui/views/test/test_views.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/widget.h"
#if defined(USE_AURA)
#include "ui/aura/client/focus_client.h"
#include "ui/views/widget/native_widget_aura.h"
#endif
namespace views {
enum FocusTestEventType { ON_FOCUS = 0, ON_BLUR };
struct FocusTestEvent {
FocusTestEventType type;
int view_id;
FocusManager::FocusChangeReason focus_change_reason;
};
class FocusTestEventList : public base::RefCounted<FocusTestEventList> {
public:
std::vector<FocusTestEvent> vec;
private:
friend class base::RefCounted<FocusTestEventList>;
~FocusTestEventList() = default;
};
class SimpleTestView : public View {
METADATA_HEADER(SimpleTestView, View)
public:
SimpleTestView(scoped_refptr<FocusTestEventList> event_list, int view_id)
: event_list_(std::move(event_list)) {
SetFocusBehavior(FocusBehavior::ALWAYS);
set_suppress_default_focus_handling();
SetID(view_id);
}
SimpleTestView(const SimpleTestView&) = delete;
SimpleTestView& operator=(const SimpleTestView&) = delete;
void OnFocus() override {
event_list_->vec.push_back({
ON_FOCUS,
GetID(),
GetFocusManager()->focus_change_reason(),
});
}
void OnBlur() override {
event_list_->vec.push_back({
ON_BLUR,
GetID(),
GetFocusManager()->focus_change_reason(),
});
}
private:
const scoped_refptr<FocusTestEventList> event_list_;
};
BEGIN_METADATA(SimpleTestView)
END_METADATA
TEST_F(FocusManagerTest, ViewFocusCallbacks) {
auto event_list = base::MakeRefCounted<FocusTestEventList>();
const int kView1ID = 1;
const int kView2ID = 2;
SimpleTestView* view1 = new SimpleTestView(event_list, kView1ID);
SimpleTestView* view2 = new SimpleTestView(event_list, kView2ID);
GetContentsView()->AddChildViewRaw(view1);
GetContentsView()->AddChildViewRaw(view2);
view1->RequestFocus();
ASSERT_EQ(1, static_cast<int>(event_list->vec.size()));
EXPECT_EQ(ON_FOCUS, event_list->vec[0].type);
EXPECT_EQ(kView1ID, event_list->vec[0].view_id);
EXPECT_EQ(FocusChangeReason::kDirectFocusChange,
event_list->vec[0].focus_change_reason);
event_list->vec.clear();
view2->RequestFocus();
ASSERT_EQ(2, static_cast<int>(event_list->vec.size()));
EXPECT_EQ(ON_BLUR, event_list->vec[0].type);
EXPECT_EQ(kView1ID, event_list->vec[0].view_id);
EXPECT_EQ(ON_FOCUS, event_list->vec[1].type);
EXPECT_EQ(kView2ID, event_list->vec[1].view_id);
EXPECT_EQ(FocusChangeReason::kDirectFocusChange,
event_list->vec[0].focus_change_reason);
EXPECT_EQ(FocusChangeReason::kDirectFocusChange,
event_list->vec[1].focus_change_reason);
event_list->vec.clear();
GetFocusManager()->ClearFocus();
ASSERT_EQ(1, static_cast<int>(event_list->vec.size()));
EXPECT_EQ(ON_BLUR, event_list->vec[0].type);
EXPECT_EQ(kView2ID, event_list->vec[0].view_id);
EXPECT_EQ(FocusChangeReason::kDirectFocusChange,
event_list->vec[0].focus_change_reason);
}
TEST_F(FocusManagerTest, FocusChangeListener) {
View* view1 = new View();
view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
View* view2 = new View();
view2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetContentsView()->AddChildViewRaw(view1);
GetContentsView()->AddChildViewRaw(view2);
TestFocusChangeListener listener;
AddFocusChangeListener(&listener);
views::View* null_view = nullptr;
view1->RequestFocus();
ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size()));
EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(null_view, view1));
listener.ClearFocusChanges();
view2->RequestFocus();
ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size()));
EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(view1, view2));
listener.ClearFocusChanges();
GetFocusManager()->ClearFocus();
ASSERT_EQ(1, static_cast<int>(listener.focus_changes().size()));
EXPECT_TRUE(listener.focus_changes()[0] == ViewPair(view2, null_view));
RemoveFocusChangeListener(&listener);
}
TEST_F(FocusManagerTest, NativeViewFocusChangeListener) {
test::WidgetTest::SimulateNativeActivate(GetWidget());
TestNativeViewFocusChangeListener widget_listener;
AddNativeViewFocusChangeListener(&widget_listener);
Widget::InitParams params1 = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
params1.bounds = gfx::Rect(10, 10, 100, 100);
params1.parent = GetWidget()->GetNativeView();
auto widget1 = std::make_unique<Widget>();
widget1->Init(std::move(params1));
widget1->Show();
Widget::InitParams params2 = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
params2.bounds = gfx::Rect(10, 10, 100, 100);
params2.parent = GetWidget()->GetNativeView();
auto widget2 = std::make_unique<Widget>();
widget2->Init(std::move(params2));
widget2->Show();
widget_listener.ClearFocusChanges();
gfx::NativeView native_view1 = widget1->GetNativeView();
test::WidgetTest::SimulateNativeActivate(widget1.get());
ASSERT_EQ(2u, widget_listener.focus_changes().size());
EXPECT_EQ(gfx::NativeView(), widget_listener.focus_changes()[0]);
EXPECT_EQ(native_view1, widget_listener.focus_changes()[1]);
widget_listener.ClearFocusChanges();
gfx::NativeView native_view2 = widget2->GetNativeView();
test::WidgetTest::SimulateNativeActivate(widget2.get());
ASSERT_EQ(2u, widget_listener.focus_changes().size());
EXPECT_EQ(gfx::NativeView(), widget_listener.focus_changes()[0]);
EXPECT_EQ(native_view2, widget_listener.focus_changes()[1]);
RemoveNativeViewFocusChangeListener(&widget_listener);
}
TEST_F(FocusManagerTest, CallsNormalAcceleratorTarget) {
FocusManager* focus_manager = GetFocusManager();
ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, ui::EF_NONE);
ui::TestAcceleratorTarget return_target(true);
ui::TestAcceleratorTarget escape_target(true);
EXPECT_EQ(return_target.accelerator_count(), 0);
EXPECT_EQ(escape_target.accelerator_count(), 0);
focus_manager->RegisterAccelerator(return_accelerator,
ui::AcceleratorManager::kNormalPriority,
&return_target);
focus_manager->RegisterAccelerator(escape_accelerator,
ui::AcceleratorManager::kNormalPriority,
&escape_target);
EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(return_target.accelerator_count(), 1);
EXPECT_EQ(escape_target.accelerator_count(), 0);
EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator));
EXPECT_EQ(return_target.accelerator_count(), 1);
EXPECT_EQ(escape_target.accelerator_count(), 1);
ui::TestAcceleratorTarget return_target2(true);
EXPECT_EQ(return_target2.accelerator_count(), 0);
focus_manager->RegisterAccelerator(return_accelerator,
ui::AcceleratorManager::kNormalPriority,
&return_target2);
EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(return_target.accelerator_count(), 1);
EXPECT_EQ(return_target2.accelerator_count(), 1);
ui::TestAcceleratorTarget return_target3(false);
EXPECT_EQ(return_target3.accelerator_count(), 0);
focus_manager->RegisterAccelerator(return_accelerator,
ui::AcceleratorManager::kNormalPriority,
&return_target3);
EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(return_target.accelerator_count(), 1);
EXPECT_EQ(return_target2.accelerator_count(), 2);
EXPECT_EQ(return_target3.accelerator_count(), 1);
focus_manager->UnregisterAccelerator(return_accelerator, &return_target2);
EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(return_target.accelerator_count(), 2);
EXPECT_EQ(return_target2.accelerator_count(), 2);
EXPECT_EQ(return_target3.accelerator_count(), 2);
focus_manager->UnregisterAccelerator(return_accelerator, &return_target);
focus_manager->UnregisterAccelerator(return_accelerator, &return_target3);
focus_manager->UnregisterAccelerator(escape_accelerator, &escape_target);
EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(return_target.accelerator_count(), 2);
EXPECT_EQ(return_target2.accelerator_count(), 2);
EXPECT_EQ(return_target3.accelerator_count(), 2);
EXPECT_FALSE(focus_manager->ProcessAccelerator(escape_accelerator));
EXPECT_EQ(escape_target.accelerator_count(), 1);
}
TEST_F(FocusManagerTest, HighPriorityHandlers) {
FocusManager* focus_manager = GetFocusManager();
ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, ui::EF_NONE);
ui::TestAcceleratorTarget escape_target_high(true);
ui::TestAcceleratorTarget escape_target_normal(true);
EXPECT_EQ(escape_target_high.accelerator_count(), 0);
EXPECT_EQ(escape_target_normal.accelerator_count(), 0);
EXPECT_FALSE(focus_manager->HasPriorityHandler(escape_accelerator));
focus_manager->RegisterAccelerator(escape_accelerator,
ui::AcceleratorManager::kHighPriority,
&escape_target_high);
EXPECT_TRUE(focus_manager->HasPriorityHandler(escape_accelerator));
EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator));
EXPECT_EQ(escape_target_high.accelerator_count(), 1);
EXPECT_EQ(escape_target_normal.accelerator_count(), 0);
focus_manager->RegisterAccelerator(escape_accelerator,
ui::AcceleratorManager::kNormalPriority,
&escape_target_normal);
EXPECT_TRUE(focus_manager->HasPriorityHandler(escape_accelerator));
EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator));
EXPECT_EQ(escape_target_high.accelerator_count(), 2);
EXPECT_EQ(escape_target_normal.accelerator_count(), 0);
focus_manager->UnregisterAccelerator(escape_accelerator, &escape_target_high);
EXPECT_FALSE(focus_manager->HasPriorityHandler(escape_accelerator));
EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator));
EXPECT_EQ(escape_target_high.accelerator_count(), 2);
EXPECT_EQ(escape_target_normal.accelerator_count(), 1);
focus_manager->RegisterAccelerator(escape_accelerator,
ui::AcceleratorManager::kHighPriority,
&escape_target_high);
EXPECT_TRUE(focus_manager->HasPriorityHandler(escape_accelerator));
EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator));
EXPECT_EQ(escape_target_high.accelerator_count(), 3);
EXPECT_EQ(escape_target_normal.accelerator_count(), 1);
focus_manager->UnregisterAccelerator(escape_accelerator,
&escape_target_normal);
EXPECT_TRUE(focus_manager->HasPriorityHandler(escape_accelerator));
EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator));
EXPECT_EQ(escape_target_high.accelerator_count(), 4);
EXPECT_EQ(escape_target_normal.accelerator_count(), 1);
focus_manager->UnregisterAccelerator(escape_accelerator, &escape_target_high);
EXPECT_FALSE(focus_manager->HasPriorityHandler(escape_accelerator));
EXPECT_FALSE(focus_manager->ProcessAccelerator(escape_accelerator));
EXPECT_EQ(escape_target_high.accelerator_count(), 4);
EXPECT_EQ(escape_target_normal.accelerator_count(), 1);
}
TEST_F(FocusManagerTest, CallsEnabledAcceleratorTargetsOnly) {
FocusManager* focus_manager = GetFocusManager();
ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
ui::TestAcceleratorTarget return_target1(true);
ui::TestAcceleratorTarget return_target2(true);
focus_manager->RegisterAccelerator(return_accelerator,
ui::AcceleratorManager::kNormalPriority,
&return_target1);
focus_manager->RegisterAccelerator(return_accelerator,
ui::AcceleratorManager::kNormalPriority,
&return_target2);
EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(0, return_target1.accelerator_count());
EXPECT_EQ(1, return_target2.accelerator_count());
return_target2.set_can_handle_accelerators(false);
EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(1, return_target1.accelerator_count());
EXPECT_EQ(1, return_target2.accelerator_count());
return_target1.set_can_handle_accelerators(false);
EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(1, return_target1.accelerator_count());
EXPECT_EQ(1, return_target2.accelerator_count());
return_target1.set_can_handle_accelerators(true);
return_target2.set_can_handle_accelerators(true);
EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(1, return_target1.accelerator_count());
EXPECT_EQ(2, return_target2.accelerator_count());
}
class SelfUnregisteringAcceleratorTarget : public ui::TestAcceleratorTarget {
public:
SelfUnregisteringAcceleratorTarget(const ui::Accelerator& accelerator,
FocusManager* focus_manager)
: accelerator_(accelerator), focus_manager_(focus_manager) {}
SelfUnregisteringAcceleratorTarget(
const SelfUnregisteringAcceleratorTarget&) = delete;
SelfUnregisteringAcceleratorTarget& operator=(
const SelfUnregisteringAcceleratorTarget&) = delete;
bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
focus_manager_->UnregisterAccelerator(accelerator, this);
return ui::TestAcceleratorTarget::AcceleratorPressed(accelerator);
}
private:
ui::Accelerator accelerator_;
raw_ptr<FocusManager> focus_manager_;
};
TEST_F(FocusManagerTest, CallsSelfDeletingAcceleratorTarget) {
FocusManager* focus_manager = GetFocusManager();
ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
SelfUnregisteringAcceleratorTarget target(return_accelerator, focus_manager);
EXPECT_EQ(target.accelerator_count(), 0);
focus_manager->RegisterAccelerator(
return_accelerator, ui::AcceleratorManager::kNormalPriority, &target);
EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(target.accelerator_count(), 1);
EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator));
EXPECT_EQ(target.accelerator_count(), 1);
}
TEST_F(FocusManagerTest, SuspendAccelerators) {
const ui::KeyEvent event(ui::EventType::kKeyPressed, ui::VKEY_RETURN,
ui::EF_NONE);
ui::Accelerator accelerator(event.key_code(), event.flags());
ui::TestAcceleratorTarget target(true);
FocusManager* focus_manager = GetFocusManager();
focus_manager->RegisterAccelerator(
accelerator, ui::AcceleratorManager::kNormalPriority, &target);
focus_manager->set_shortcut_handling_suspended(true);
EXPECT_TRUE(focus_manager->OnKeyEvent(event));
EXPECT_EQ(0, target.accelerator_count());
focus_manager->set_shortcut_handling_suspended(false);
EXPECT_FALSE(focus_manager->OnKeyEvent(event));
EXPECT_EQ(1, target.accelerator_count());
}
namespace {
class FocusInAboutToRequestFocusFromTabTraversalView : public View {
METADATA_HEADER(FocusInAboutToRequestFocusFromTabTraversalView, View)
public:
FocusInAboutToRequestFocusFromTabTraversalView() = default;
FocusInAboutToRequestFocusFromTabTraversalView(
const FocusInAboutToRequestFocusFromTabTraversalView&) = delete;
FocusInAboutToRequestFocusFromTabTraversalView& operator=(
const FocusInAboutToRequestFocusFromTabTraversalView&) = delete;
void set_view_to_focus(View* view) { view_to_focus_ = view; }
void AboutToRequestFocusFromTabTraversal(bool reverse) override {
view_to_focus_->RequestFocus();
}
private:
raw_ptr<views::View> view_to_focus_ = nullptr;
};
BEGIN_METADATA(FocusInAboutToRequestFocusFromTabTraversalView)
END_METADATA
}
TEST_F(FocusManagerTest, FocusInAboutToRequestFocusFromTabTraversal) {
views::View* v1 = new View;
v1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetContentsView()->AddChildViewRaw(v1);
FocusInAboutToRequestFocusFromTabTraversalView* v2 =
new FocusInAboutToRequestFocusFromTabTraversalView;
v2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
v2->set_view_to_focus(v1);
GetContentsView()->AddChildViewRaw(v2);
views::View* v3 = new View;
v3->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetContentsView()->AddChildViewRaw(v3);
v3->RequestFocus();
GetWidget()->GetFocusManager()->AdvanceFocus(true);
EXPECT_TRUE(v1->HasFocus());
v2->set_view_to_focus(nullptr);
}
TEST_F(FocusManagerTest, RotateFocus) {
views::AccessiblePaneView* pane1 = new AccessiblePaneView();
GetContentsView()->AddChildViewRaw(pane1);
views::View* v1 = new View;
v1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
pane1->AddChildViewRaw(v1);
views::View* v2 = new View;
v2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
pane1->AddChildViewRaw(v2);
views::AccessiblePaneView* pane2 = new AccessiblePaneView();
GetContentsView()->AddChildViewRaw(pane2);
views::View* v3 = new View;
v3->SetFocusBehavior(View::FocusBehavior::ALWAYS);
pane2->AddChildViewRaw(v3);
views::View* v4 = new View;
v4->SetFocusBehavior(View::FocusBehavior::ALWAYS);
pane2->AddChildViewRaw(v4);
std::vector<raw_ptr<views::View, VectorExperimental>> panes;
panes.push_back(pane1);
panes.push_back(pane2);
SetAccessiblePanes(panes);
FocusManager* focus_manager = GetWidget()->GetFocusManager();
using Direction = FocusManager::Direction;
using FocusCycleWrapping = FocusManager::FocusCycleWrapping;
EXPECT_TRUE(focus_manager->RotatePaneFocus(Direction::kForward,
FocusCycleWrapping::kEnabled));
EXPECT_EQ(v1, focus_manager->GetFocusedView());
focus_manager->AdvanceFocus(false);
EXPECT_EQ(v2, focus_manager->GetFocusedView());
focus_manager->AdvanceFocus(false);
EXPECT_EQ(v1, focus_manager->GetFocusedView());
EXPECT_TRUE(focus_manager->RotatePaneFocus(Direction::kForward,
FocusCycleWrapping::kEnabled));
EXPECT_EQ(v3, focus_manager->GetFocusedView());
focus_manager->AdvanceFocus(false);
EXPECT_EQ(v4, focus_manager->GetFocusedView());
focus_manager->AdvanceFocus(false);
EXPECT_EQ(v3, focus_manager->GetFocusedView());
}
TEST_F(FocusManagerTest, RadioButtonGroupTraversalWrapsToFirstView) {
FocusManager* focus_manager = GetFocusManager();
const int kRadioGroupID = 1;
views::View* grandparent_view = new View;
views::View* parent_view = new View;
grandparent_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
parent_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetContentsView()->AddChildViewRaw(grandparent_view);
grandparent_view->AddChildViewRaw(parent_view);
grandparent_view->SetOwnedGroup(kRadioGroupID);
auto* radio1 = parent_view->AddChildView(
std::make_unique<RadioButton>(u"Option 1", kRadioGroupID));
radio1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
auto* radio2 = parent_view->AddChildView(
std::make_unique<RadioButton>(u"Option 2", kRadioGroupID));
radio2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
auto* radio3 = parent_view->AddChildView(
std::make_unique<RadioButton>(u"Option 3", kRadioGroupID));
radio3->SetFocusBehavior(View::FocusBehavior::ALWAYS);
auto* other_view = GetContentsView()->AddChildView(std::make_unique<View>());
other_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
radio1->RequestFocus();
EXPECT_EQ(radio1, focus_manager->GetFocusedView());
const ui::KeyEvent down_key(ui::EventType::kKeyPressed, ui::VKEY_DOWN,
ui::EF_NONE);
focus_manager->OnKeyEvent(down_key);
EXPECT_EQ(radio2, focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(down_key);
EXPECT_EQ(radio3, focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(down_key);
EXPECT_EQ(radio1, focus_manager->GetFocusedView());
const ui::KeyEvent up_key(ui::EventType::kKeyPressed, ui::VKEY_UP,
ui::EF_NONE);
focus_manager->OnKeyEvent(up_key);
EXPECT_EQ(radio3, focus_manager->GetFocusedView());
}
TEST_F(FocusManagerTest, RadioButtonGroupOwnerUndefined) {
FocusManager* focus_manager = GetFocusManager();
const int kRadioGroupID = 1;
views::View* grandparent_view = new View;
views::View* parent_view = new View;
grandparent_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
parent_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetContentsView()->AddChildViewRaw(grandparent_view);
grandparent_view->AddChildViewRaw(parent_view);
auto* radio1 = parent_view->AddChildView(
std::make_unique<RadioButton>(u"Option 1", kRadioGroupID));
radio1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
auto* radio2 = parent_view->AddChildView(
std::make_unique<RadioButton>(u"Option 2", kRadioGroupID));
radio2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
auto* radio3 = grandparent_view->AddChildView(
std::make_unique<RadioButton>(u"Option 3", kRadioGroupID));
radio3->SetFocusBehavior(View::FocusBehavior::ALWAYS);
radio1->RequestFocus();
EXPECT_EQ(radio1, focus_manager->GetFocusedView());
const ui::KeyEvent down_key(ui::EventType::kKeyPressed, ui::VKEY_DOWN,
ui::EF_NONE);
focus_manager->OnKeyEvent(down_key);
EXPECT_EQ(radio2, focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(down_key);
EXPECT_EQ(radio1, focus_manager->GetFocusedView());
}
TEST_F(FocusManagerTest, ImplicitlyStoresFocus) {
views::View* v1 = new View;
v1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetContentsView()->AddChildViewRaw(v1);
views::View* v2 = new View;
v2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetContentsView()->AddChildViewRaw(v2);
v1->RequestFocus();
EXPECT_TRUE(v1->HasFocus());
EXPECT_EQ(v1, GetWidget()->GetFocusManager()->GetStoredFocusView());
v2->RequestFocus();
EXPECT_TRUE(v2->HasFocus());
EXPECT_EQ(v2, GetWidget()->GetFocusManager()->GetStoredFocusView());
}
namespace {
class FocusManagerArrowKeyTraversalTest
: public FocusManagerTest,
public testing::WithParamInterface<bool> {
public:
FocusManagerArrowKeyTraversalTest() = default;
FocusManagerArrowKeyTraversalTest(const FocusManagerArrowKeyTraversalTest&) =
delete;
FocusManagerArrowKeyTraversalTest& operator=(
const FocusManagerArrowKeyTraversalTest&) = delete;
~FocusManagerArrowKeyTraversalTest() override = default;
void SetUp() override {
if (testing::UnitTest::GetInstance()->current_test_info()->value_param()) {
is_rtl_ = GetParam();
if (is_rtl_) {
base::i18n::SetICUDefaultLocale("he");
}
}
FocusManagerTest::SetUp();
}
bool is_rtl_ = false;
private:
base::test::ScopedRestoreICUDefaultLocale restore_locale_;
};
INSTANTIATE_TEST_SUITE_P(All,
FocusManagerArrowKeyTraversalTest,
testing::Bool());
}
TEST_P(FocusManagerArrowKeyTraversalTest, ArrowKeyTraversal) {
FocusManager* focus_manager = GetFocusManager();
const ui::KeyEvent left_key(ui::EventType::kKeyPressed, ui::VKEY_LEFT,
ui::EF_NONE);
const ui::KeyEvent right_key(ui::EventType::kKeyPressed, ui::VKEY_RIGHT,
ui::EF_NONE);
const ui::KeyEvent up_key(ui::EventType::kKeyPressed, ui::VKEY_UP,
ui::EF_NONE);
const ui::KeyEvent down_key(ui::EventType::kKeyPressed, ui::VKEY_DOWN,
ui::EF_NONE);
std::vector<views::View*> v;
for (size_t i = 0; i < 2; ++i) {
views::View* view = new View;
view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetContentsView()->AddChildViewRaw(view);
v.push_back(view);
}
GetWidget()->widget_delegate()->SetEnableArrowKeyTraversal(false);
v[0]->RequestFocus();
focus_manager->OnKeyEvent(right_key);
EXPECT_EQ(v[0], focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(left_key);
EXPECT_EQ(v[0], focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(down_key);
EXPECT_EQ(v[0], focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(up_key);
EXPECT_EQ(v[0], focus_manager->GetFocusedView());
GetWidget()->widget_delegate()->SetEnableArrowKeyTraversal(true);
v[0]->RequestFocus();
focus_manager->OnKeyEvent(is_rtl_ ? left_key : right_key);
EXPECT_EQ(v[1], focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(is_rtl_ ? right_key : left_key);
EXPECT_EQ(v[0], focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(down_key);
EXPECT_EQ(v[1], focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(up_key);
EXPECT_EQ(v[0], focus_manager->GetFocusedView());
}
TEST_F(FocusManagerTest, SkipViewsInArrowKeyTraversal) {
FocusManager* focus_manager = GetFocusManager();
std::vector<views::View*> v;
for (size_t i = 0; i < 5; ++i) {
auto* view =
GetContentsView()->AddChildView(std::make_unique<views::View>());
view->SetGroup(12345);
view->SetFocusBehavior((i == 0 || i == 4)
? View::FocusBehavior::ALWAYS
: View::FocusBehavior::ACCESSIBLE_ONLY);
v.push_back(view);
}
v[1]->SetEnabled(false);
v[3]->SetVisible(false);
v[0]->RequestFocus();
EXPECT_EQ(v[0], focus_manager->GetFocusedView());
const ui::KeyEvent right_key(ui::EventType::kKeyPressed, ui::VKEY_RIGHT,
ui::EF_NONE);
focus_manager->OnKeyEvent(right_key);
EXPECT_EQ(v[2], focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(right_key);
EXPECT_EQ(v[4], focus_manager->GetFocusedView());
const ui::KeyEvent left_key(ui::EventType::kKeyPressed, ui::VKEY_LEFT,
ui::EF_NONE);
focus_manager->OnKeyEvent(left_key);
EXPECT_EQ(v[2], focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(left_key);
EXPECT_EQ(v[0], focus_manager->GetFocusedView());
v[1]->SetEnabled(true);
v[3]->SetVisible(true);
focus_manager->OnKeyEvent(right_key);
EXPECT_EQ(v[1], focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(right_key);
EXPECT_EQ(v[2], focus_manager->GetFocusedView());
focus_manager->OnKeyEvent(right_key);
EXPECT_EQ(v[3], focus_manager->GetFocusedView());
}
TEST_F(FocusManagerTest, StoreFocusedView) {
auto event_list = base::MakeRefCounted<FocusTestEventList>();
const int kView1ID = 1;
SimpleTestView* view = new SimpleTestView(event_list, kView1ID);
GetWidget()->GetRootView()->AddChildViewRaw(view);
view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetFocusManager()->SetFocusedView(view);
GetFocusManager()->StoreFocusedView(false);
EXPECT_EQ(nullptr, GetFocusManager()->GetFocusedView());
EXPECT_TRUE(GetFocusManager()->RestoreFocusedView());
EXPECT_EQ(view, GetFocusManager()->GetStoredFocusView());
ASSERT_EQ(3, static_cast<int>(event_list->vec.size()));
EXPECT_EQ(ON_FOCUS, event_list->vec[0].type);
EXPECT_EQ(kView1ID, event_list->vec[0].view_id);
EXPECT_EQ(FocusChangeReason::kDirectFocusChange,
event_list->vec[0].focus_change_reason);
EXPECT_EQ(ON_BLUR, event_list->vec[1].type);
EXPECT_EQ(kView1ID, event_list->vec[1].view_id);
EXPECT_EQ(FocusChangeReason::kDirectFocusChange,
event_list->vec[1].focus_change_reason);
EXPECT_EQ(ON_FOCUS, event_list->vec[2].type);
EXPECT_EQ(kView1ID, event_list->vec[2].view_id);
EXPECT_EQ(FocusChangeReason::kFocusRestore,
event_list->vec[2].focus_change_reason);
event_list->vec.clear();
GetFocusManager()->SetFocusedView(view);
GetFocusManager()->StoreFocusedView(true);
EXPECT_EQ(nullptr, GetFocusManager()->GetFocusedView());
EXPECT_TRUE(GetFocusManager()->RestoreFocusedView());
EXPECT_EQ(view, GetFocusManager()->GetStoredFocusView());
ASSERT_EQ(2, static_cast<int>(event_list->vec.size()));
EXPECT_EQ(ON_BLUR, event_list->vec[0].type);
EXPECT_EQ(kView1ID, event_list->vec[0].view_id);
EXPECT_EQ(FocusChangeReason::kDirectFocusChange,
event_list->vec[0].focus_change_reason);
EXPECT_EQ(ON_FOCUS, event_list->vec[1].type);
EXPECT_EQ(kView1ID, event_list->vec[1].view_id);
EXPECT_EQ(FocusChangeReason::kFocusRestore,
event_list->vec[1].focus_change_reason);
GetFocusManager()->ClearFocus();
}
#if BUILDFLAG(IS_MAC)
TEST_F(FocusManagerTest, StoreFocusedViewFullKeyboardAccess) {
View* view1 = new View;
View* view2 = new View;
View* view3 = new View;
view1->SetFocusBehavior(View::FocusBehavior::ACCESSIBLE_ONLY);
view2->SetFocusBehavior(View::FocusBehavior::NEVER);
view3->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetWidget()->GetRootView()->AddChildViewRaw(view1);
GetWidget()->GetRootView()->AddChildViewRaw(view2);
GetWidget()->GetRootView()->AddChildViewRaw(view3);
view1->RequestFocus();
EXPECT_EQ(view1, GetFocusManager()->GetFocusedView());
GetFocusManager()->StoreFocusedView(true);
EXPECT_EQ(nullptr, GetFocusManager()->GetFocusedView());
GetFocusManager()->SetKeyboardAccessible(false);
EXPECT_FALSE(GetFocusManager()->RestoreFocusedView());
EXPECT_EQ(view3, GetFocusManager()->GetFocusedView());
GetFocusManager()->StoreFocusedView(false);
EXPECT_EQ(nullptr, GetFocusManager()->GetFocusedView());
GetFocusManager()->SetKeyboardAccessible(true);
EXPECT_TRUE(GetFocusManager()->RestoreFocusedView());
EXPECT_EQ(view3, GetFocusManager()->GetFocusedView());
}
TEST_F(FocusManagerTest, RequestFocus) {
View* view1 = new View();
View* view2 = new View();
view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
view2->SetFocusBehavior(View::FocusBehavior::ACCESSIBLE_ONLY);
GetWidget()->GetRootView()->AddChildViewRaw(view1);
GetWidget()->GetRootView()->AddChildViewRaw(view2);
EXPECT_TRUE(GetFocusManager()->keyboard_accessible());
view1->RequestFocus();
EXPECT_EQ(view1, GetFocusManager()->GetFocusedView());
view2->RequestFocus();
EXPECT_EQ(view2, GetFocusManager()->GetFocusedView());
GetFocusManager()->SetKeyboardAccessible(false);
GetFocusManager()->ClearFocus();
EXPECT_NE(view1, GetFocusManager()->GetFocusedView());
view1->RequestFocus();
EXPECT_EQ(view1, GetFocusManager()->GetFocusedView());
view2->RequestFocus();
EXPECT_EQ(view1, GetFocusManager()->GetFocusedView());
}
#endif
namespace {
class AdvanceFocusWidgetDelegate : public WidgetDelegate {
public:
explicit AdvanceFocusWidgetDelegate(Widget* widget) : widget_(widget) {}
AdvanceFocusWidgetDelegate(const AdvanceFocusWidgetDelegate&) = delete;
AdvanceFocusWidgetDelegate& operator=(const AdvanceFocusWidgetDelegate&) =
delete;
~AdvanceFocusWidgetDelegate() override = default;
Widget* GetWidget() override { return widget_; }
const Widget* GetWidget() const override { return widget_; }
private:
raw_ptr<Widget> widget_;
};
}
class FocusManagerTestBubbleDialogDelegateView
: public BubbleDialogDelegateView {
public:
explicit FocusManagerTestBubbleDialogDelegateView(View* anchor)
: BubbleDialogDelegateView(anchor, BubbleBorder::NONE) {
DialogDelegate::SetButtons(
static_cast<int>(ui::mojom::DialogButton::kNone));
}
FocusManagerTestBubbleDialogDelegateView(
const FocusManagerTestBubbleDialogDelegateView&) = delete;
FocusManagerTestBubbleDialogDelegateView& operator=(
const FocusManagerTestBubbleDialogDelegateView&) = delete;
~FocusManagerTestBubbleDialogDelegateView() override = default;
static FocusManagerTestBubbleDialogDelegateView* CreateAndShowBubble(
View* anchor) {
auto* bubble = new FocusManagerTestBubbleDialogDelegateView(anchor);
Widget* bubble_widget = BubbleDialogDelegateView::CreateBubble(bubble);
bubble_widget->SetFocusTraversableParent(
bubble->anchor_widget()->GetFocusTraversable());
bubble_widget->SetFocusTraversableParentView(anchor);
bubble->set_close_on_deactivate(false);
bubble_widget->Show();
return bubble;
}
void UseNativeWidgetAura() { use_native_widget_aura_ = true; }
void OnBeforeBubbleWidgetInit(Widget::InitParams* params,
Widget* widget) const override {
#if defined(USE_AURA)
if (use_native_widget_aura_) {
params->native_widget =
new test::TestPlatformNativeWidget<NativeWidgetAura>(widget, false,
nullptr);
}
#endif
}
private:
bool use_native_widget_aura_ = false;
};
TEST_F(FocusManagerTest, AdvanceFocusStaysInWidget) {
View* widget_view = new View;
widget_view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
widget_view->SetBounds(20, 0, 20, 20);
GetContentsView()->AddChildViewRaw(widget_view);
Widget::InitParams params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
params.child = true;
params.bounds = gfx::Rect(10, 10, 100, 100);
params.parent = GetWidget()->GetNativeView();
auto child_widget = std::make_unique<Widget>();
std::unique_ptr<AdvanceFocusWidgetDelegate> delegate_owned =
std::make_unique<AdvanceFocusWidgetDelegate>(child_widget.get());
params.delegate = delegate_owned.get();
params.delegate->RegisterDeleteDelegateCallback(
WidgetDelegate::RegisterDeleteCallbackPassKey(),
base::DoNothingWithBoundArgs(std::move(delegate_owned)));
child_widget->Init(std::move(params));
View* view1 = new View;
view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
view1->SetBounds(0, 0, 20, 20);
View* view2 = new View;
view2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
view2->SetBounds(20, 0, 20, 20);
child_widget->client_view()->AddChildViewRaw(view1);
child_widget->client_view()->AddChildViewRaw(view2);
child_widget->Show();
view2->RequestFocus();
EXPECT_EQ(view2, GetFocusManager()->GetFocusedView());
GetFocusManager()->AdvanceFocus(false);
EXPECT_EQ(view1, GetFocusManager()->GetFocusedView());
GetFocusManager()->AdvanceFocus(true);
EXPECT_EQ(view2, GetFocusManager()->GetFocusedView());
GetFocusManager()->AdvanceFocus(true);
EXPECT_EQ(view1, GetFocusManager()->GetFocusedView());
}
TEST_F(FocusManagerTest, NavigateIntoAnchoredDialog) {
View* parent1 = new View();
View* parent2 = new View();
View* parent3 = new View();
View* parent4 = new View();
parent1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
parent2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
parent3->SetFocusBehavior(View::FocusBehavior::ALWAYS);
parent4->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetWidget()->GetRootView()->AddChildViewRaw(parent1);
GetWidget()->GetRootView()->AddChildViewRaw(parent2);
GetWidget()->GetRootView()->AddChildViewRaw(parent3);
GetWidget()->GetRootView()->AddChildViewRaw(parent4);
parent3->AddChildViewRaw(new View());
BubbleDialogDelegateView* bubble_delegate =
FocusManagerTestBubbleDialogDelegateView::CreateAndShowBubble(parent3);
Widget* bubble_widget = bubble_delegate->GetWidget();
View* child1 = new View();
View* child2 = new View();
child1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
child2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
bubble_widget->GetRootView()->AddChildViewRaw(child1);
bubble_widget->GetRootView()->AddChildViewRaw(child2);
parent1->RequestFocus();
GetWidget()->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(parent2->HasFocus());
GetWidget()->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(parent3->HasFocus());
GetWidget()->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(child1->HasFocus());
bubble_widget->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(child2->HasFocus());
bubble_widget->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(parent4->HasFocus());
GetWidget()->GetFocusManager()->AdvanceFocus(true);
EXPECT_TRUE(child2->HasFocus());
bubble_widget->GetFocusManager()->AdvanceFocus(true);
EXPECT_TRUE(child1->HasFocus());
bubble_widget->GetFocusManager()->AdvanceFocus(true);
EXPECT_TRUE(parent3->HasFocus());
}
TEST_F(FocusManagerTest, AnchoredDialogOnContainerView) {
View* parent1 = new View();
View* parent2 = new View();
View* parent3 = new View();
View* parent4 = new View();
View* parent_group = new View();
parent1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
parent2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
parent3->SetFocusBehavior(View::FocusBehavior::ALWAYS);
parent4->SetFocusBehavior(View::FocusBehavior::ALWAYS);
GetWidget()->GetRootView()->AddChildViewRaw(parent1);
GetWidget()->GetRootView()->AddChildViewRaw(parent_group);
parent_group->AddChildViewRaw(parent2);
parent_group->AddChildViewRaw(parent3);
GetWidget()->GetRootView()->AddChildViewRaw(parent4);
BubbleDialogDelegateView* bubble_delegate =
FocusManagerTestBubbleDialogDelegateView::CreateAndShowBubble(parent3);
Widget* bubble_widget = bubble_delegate->GetWidget();
View* child1 = new View();
View* child2 = new View();
child1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
child2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
bubble_widget->GetRootView()->AddChildViewRaw(child1);
bubble_widget->GetRootView()->AddChildViewRaw(child2);
parent1->RequestFocus();
GetWidget()->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(parent2->HasFocus());
GetWidget()->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(parent3->HasFocus());
GetWidget()->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(child1->HasFocus());
bubble_widget->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(child2->HasFocus());
bubble_widget->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(parent4->HasFocus());
GetWidget()->GetFocusManager()->AdvanceFocus(true);
EXPECT_TRUE(child2->HasFocus());
bubble_widget->GetFocusManager()->AdvanceFocus(true);
EXPECT_TRUE(child1->HasFocus());
bubble_widget->GetFocusManager()->AdvanceFocus(true);
EXPECT_TRUE(parent3->HasFocus());
}
TEST_F(FocusManagerTest, AnchoredDialogInPane) {
View* root_view = GetWidget()->GetRootView();
AccessiblePaneView* pane =
root_view->AddChildView(std::make_unique<AccessiblePaneView>());
View* anchor = pane->AddChildView(std::make_unique<View>());
anchor->SetFocusBehavior(View::FocusBehavior::ALWAYS);
BubbleDialogDelegateView* bubble =
FocusManagerTestBubbleDialogDelegateView::CreateAndShowBubble(anchor);
View* bubble_child = bubble->AddChildView(std::make_unique<View>());
bubble_child->SetFocusBehavior(View::FocusBehavior::ALWAYS);
pane->SetPaneFocus(anchor);
EXPECT_TRUE(anchor->HasFocus());
GetWidget()->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(bubble_child->HasFocus());
}
TEST_F(FocusManagerTest, FocusRing) {
GetContentsView()->SetLayoutManager(std::make_unique<FlexLayout>());
View* view = GetContentsView()->AddChildView(
std::make_unique<StaticSizedView>(gfx::Size(10, 10)));
GetContentsView()->SetFocusBehavior(View::FocusBehavior::ALWAYS);
view->SetFocusBehavior(View::FocusBehavior::ALWAYS);
FocusRing::Install(GetContentsView());
FocusRing::Install(view);
GetContentsView()->RequestFocus();
test::RunScheduledLayout(GetWidget());
EXPECT_TRUE(GetContentsView()->HasFocus());
EXPECT_TRUE(FocusRing::Get(GetContentsView())->GetVisible());
EXPECT_FALSE(view->HasFocus());
EXPECT_FALSE(FocusRing::Get(view)->GetVisible());
view->RequestFocus();
test::RunScheduledLayout(GetWidget());
EXPECT_FALSE(GetContentsView()->HasFocus());
EXPECT_FALSE(FocusRing::Get(GetContentsView())->GetVisible());
EXPECT_TRUE(view->HasFocus());
EXPECT_TRUE(FocusRing::Get(view)->GetVisible());
}
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
class DesktopNativeViewFocusManagerTest : public FocusManagerTest {
public:
DesktopNativeViewFocusManagerTest() = default;
DesktopNativeViewFocusManagerTest(const DesktopNativeViewFocusManagerTest&) = delete;
DesktopNativeViewFocusManagerTest& operator=(
const DesktopNativeViewFocusManagerTest&) = delete;
~DesktopNativeViewFocusManagerTest() override = default;
void SetUp() override {
set_native_widget_type(NativeWidgetType::kDesktop);
FocusManagerTest::SetUp();
}
};
TEST_F(DesktopNativeViewFocusManagerTest, AnchoredDialogInDesktopNativeWidgetAura) {
auto widget = std::make_unique<Widget>();
Widget::InitParams params = CreateParams(
Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 1024, 768);
widget->Init(std::move(params));
widget->Show();
widget->Activate();
View* parent1 = new View();
View* parent2 = new View();
parent1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
parent2->SetFocusBehavior(View::FocusBehavior::ALWAYS);
widget->GetRootView()->AddChildViewRaw(parent1);
widget->GetRootView()->AddChildViewRaw(parent2);
FocusManagerTestBubbleDialogDelegateView* bubble_delegate =
FocusManagerTestBubbleDialogDelegateView::CreateAndShowBubble(parent2);
Widget* bubble_widget = bubble_delegate->GetWidget();
bubble_delegate->UseNativeWidgetAura();
View* child = new View();
child->SetFocusBehavior(View::FocusBehavior::ALWAYS);
bubble_widget->GetRootView()->AddChildViewRaw(child);
parent1->GetViewAccessibility().SetRole(ax::mojom::Role::kGroup);
parent2->GetViewAccessibility().SetRole(ax::mojom::Role::kGroup);
child->GetViewAccessibility().SetRole(ax::mojom::Role::kButton);
parent1->GetViewAccessibility().SetName(u"Parent 1",
ax::mojom::NameFrom::kAttribute);
parent2->GetViewAccessibility().SetName(
u"", ax::mojom::NameFrom::kAttributeExplicitlyEmpty);
child->GetViewAccessibility().SetName("uChild",
ax::mojom::NameFrom::kAttribute);
widget->Activate();
parent1->RequestFocus();
aura::client::FocusClient* focus_client =
aura::client::GetFocusClient(widget->GetNativeView());
ASSERT_TRUE(base::test::RunUntil([&]() {
return widget->GetNativeView() == focus_client->GetFocusedWindow();
}));
widget->GetFocusManager()->AdvanceFocus(false);
ASSERT_TRUE(base::test::RunUntil([&]() { return parent2->HasFocus(); }));
widget->GetFocusManager()->AdvanceFocus(false);
EXPECT_TRUE(child->HasFocus());
ASSERT_NE(widget->GetNativeView(), focus_client->GetFocusedWindow());
ASSERT_EQ(bubble_widget->GetNativeView(), focus_client->GetFocusedWindow());
bubble_widget->GetFocusManager()->AdvanceFocus(true);
EXPECT_TRUE(parent2->HasFocus());
ASSERT_EQ(widget->GetNativeView(), focus_client->GetFocusedWindow());
}
#endif
#if defined(USE_AURA)
class RedirectToParentFocusManagerTest : public FocusManagerTest {
public:
RedirectToParentFocusManagerTest() = default;
RedirectToParentFocusManagerTest(const RedirectToParentFocusManagerTest&) =
delete;
RedirectToParentFocusManagerTest& operator=(
const RedirectToParentFocusManagerTest&) = delete;
~RedirectToParentFocusManagerTest() override = default;
void SetUp() override {
FocusManagerTest::SetUp();
View* anchor =
GetWidget()->GetRootView()->AddChildView(std::make_unique<View>());
anchor->SetFocusBehavior(View::FocusBehavior::ALWAYS);
bubble_ =
FocusManagerTestBubbleDialogDelegateView::CreateAndShowBubble(anchor);
Widget* bubble_widget = bubble_->GetWidget();
parent_focus_manager_ = anchor->GetFocusManager();
bubble_focus_manager_ = bubble_widget->GetFocusManager();
}
void TearDown() override {
parent_focus_manager_ = nullptr;
bubble_focus_manager_ = nullptr;
bubble_ = nullptr;
FocusManagerFactory::Install(nullptr);
FocusManagerTest::TearDown();
}
protected:
raw_ptr<FocusManager> parent_focus_manager_ = nullptr;
raw_ptr<FocusManager> bubble_focus_manager_ = nullptr;
raw_ptr<BubbleDialogDelegateView> bubble_ = nullptr;
};
TEST_F(RedirectToParentFocusManagerTest, ParentHandlesAcceleratorFromBubble) {
ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
ui::TestAcceleratorTarget parent_return_target(true);
Widget* bubble_widget = bubble_->GetWidget();
EXPECT_EQ(0, parent_return_target.accelerator_count());
parent_focus_manager_->RegisterAccelerator(
return_accelerator, ui::AcceleratorManager::kNormalPriority,
&parent_return_target);
EXPECT_TRUE(
!bubble_focus_manager_->IsAcceleratorRegistered(return_accelerator));
bubble_->set_close_on_deactivate(false);
EXPECT_TRUE(bubble_focus_manager_->ProcessAccelerator(return_accelerator));
EXPECT_EQ(parent_return_target.accelerator_count(), 1);
EXPECT_FALSE(bubble_widget->IsClosed());
bubble_focus_manager_->SetFocusedView(bubble_widget->GetRootView());
bubble_->set_close_on_deactivate(true);
EXPECT_TRUE(bubble_focus_manager_->ProcessAccelerator(return_accelerator));
EXPECT_EQ(parent_return_target.accelerator_count(), 2);
EXPECT_TRUE(bubble_widget->IsClosed());
}
TEST_F(RedirectToParentFocusManagerTest, BubbleHandlesRegisteredAccelerators) {
ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
ui::TestAcceleratorTarget parent_return_target(true);
ui::TestAcceleratorTarget bubble_return_target(true);
Widget* bubble_widget = bubble_->GetWidget();
EXPECT_EQ(0, bubble_return_target.accelerator_count());
EXPECT_EQ(0, parent_return_target.accelerator_count());
bubble_focus_manager_->RegisterAccelerator(
return_accelerator, ui::AcceleratorManager::kNormalPriority,
&bubble_return_target);
parent_focus_manager_->RegisterAccelerator(
return_accelerator, ui::AcceleratorManager::kNormalPriority,
&parent_return_target);
bubble_->set_close_on_deactivate(true);
EXPECT_TRUE(bubble_focus_manager_->ProcessAccelerator(return_accelerator));
EXPECT_EQ(1, bubble_return_target.accelerator_count());
EXPECT_EQ(0, parent_return_target.accelerator_count());
EXPECT_FALSE(bubble_widget->IsClosed());
}
TEST_F(RedirectToParentFocusManagerTest, NotProcessedAccelerator) {
ui::Accelerator return_accelerator(ui::VKEY_RETURN, ui::EF_NONE);
Widget* bubble_widget = bubble_->GetWidget();
EXPECT_TRUE(
!bubble_focus_manager_->IsAcceleratorRegistered(return_accelerator));
EXPECT_TRUE(
!parent_focus_manager_->IsAcceleratorRegistered(return_accelerator));
bubble_->set_close_on_deactivate(true);
EXPECT_FALSE(bubble_focus_manager_->ProcessAccelerator(return_accelerator));
EXPECT_FALSE(bubble_widget->IsClosed());
}
#endif
}