accessible_pane_view_unittest.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/views/accessible_pane_view.h"
  5. #include <utility>
  6. #include "base/memory/raw_ptr.h"
  7. #include "build/build_config.h"
  8. #include "build/chromeos_buildflags.h"
  9. #include "ui/base/accelerators/accelerator.h"
  10. #include "ui/views/controls/button/label_button.h"
  11. #include "ui/views/layout/fill_layout.h"
  12. #include "ui/views/test/views_test_base.h"
  13. #include "ui/views/widget/widget.h"
  14. namespace views {
  15. // TODO(alicet): bring pane rotation into views and add tests.
  16. // See browser_view.cc for details.
  17. using AccessiblePaneViewTest = ViewsTestBase;
  18. class TestBarView : public AccessiblePaneView {
  19. public:
  20. TestBarView();
  21. TestBarView(const TestBarView&) = delete;
  22. TestBarView& operator=(const TestBarView&) = delete;
  23. ~TestBarView() override;
  24. LabelButton* child_button() const { return child_button_; }
  25. LabelButton* second_child_button() const { return second_child_button_; }
  26. LabelButton* third_child_button() const { return third_child_button_; }
  27. LabelButton* not_child_button() const { return not_child_button_.get(); }
  28. View* GetDefaultFocusableChild() override;
  29. private:
  30. void Init();
  31. raw_ptr<LabelButton> child_button_;
  32. // TODO(crbug.com/1298696): Breaks views_unittests.
  33. raw_ptr<LabelButton, DegradeToNoOpWhenMTE> second_child_button_;
  34. raw_ptr<LabelButton> third_child_button_;
  35. std::unique_ptr<LabelButton> not_child_button_;
  36. };
  37. TestBarView::TestBarView() {
  38. Init();
  39. set_allow_deactivate_on_esc(true);
  40. }
  41. TestBarView::~TestBarView() = default;
  42. void TestBarView::Init() {
  43. SetLayoutManager(std::make_unique<FillLayout>());
  44. std::u16string label;
  45. child_button_ = AddChildView(std::make_unique<LabelButton>());
  46. second_child_button_ = AddChildView(std::make_unique<LabelButton>());
  47. third_child_button_ = AddChildView(std::make_unique<LabelButton>());
  48. not_child_button_ = std::make_unique<LabelButton>();
  49. }
  50. View* TestBarView::GetDefaultFocusableChild() {
  51. return child_button_;
  52. }
  53. TEST_F(AccessiblePaneViewTest, SimpleSetPaneFocus) {
  54. TestBarView* test_view = new TestBarView();
  55. std::unique_ptr<Widget> widget(new Widget());
  56. Widget::InitParams params =
  57. CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  58. params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  59. params.bounds = gfx::Rect(50, 50, 650, 650);
  60. widget->Init(std::move(params));
  61. View* root = widget->GetRootView();
  62. root->AddChildView(test_view);
  63. widget->Show();
  64. widget->Activate();
  65. // Set pane focus succeeds, focus on child.
  66. EXPECT_TRUE(test_view->SetPaneFocusAndFocusDefault());
  67. EXPECT_EQ(test_view, test_view->GetPaneFocusTraversable());
  68. EXPECT_EQ(test_view->child_button(),
  69. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  70. // Set focus on non child view, focus failed, stays on pane.
  71. EXPECT_TRUE(test_view->SetPaneFocus(test_view->not_child_button()));
  72. EXPECT_FALSE(test_view->not_child_button() ==
  73. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  74. EXPECT_EQ(test_view->child_button(),
  75. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  76. widget->CloseNow();
  77. widget.reset();
  78. }
  79. TEST_F(AccessiblePaneViewTest, SetPaneFocusAndRestore) {
  80. View* test_view_main = new View();
  81. std::unique_ptr<Widget> widget_main(new Widget());
  82. Widget::InitParams params_main =
  83. CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  84. params_main.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  85. params_main.bounds = gfx::Rect(0, 0, 20, 20);
  86. widget_main->Init(std::move(params_main));
  87. View* root_main = widget_main->GetRootView();
  88. root_main->AddChildView(test_view_main);
  89. widget_main->Show();
  90. widget_main->Activate();
  91. test_view_main->GetFocusManager()->SetFocusedView(test_view_main);
  92. EXPECT_TRUE(widget_main->IsActive());
  93. EXPECT_TRUE(test_view_main->HasFocus());
  94. TestBarView* test_view_bar = new TestBarView();
  95. std::unique_ptr<Widget> widget_bar(new Widget());
  96. Widget::InitParams params_bar =
  97. CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  98. params_bar.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  99. params_bar.bounds = gfx::Rect(50, 50, 650, 650);
  100. widget_bar->Init(std::move(params_bar));
  101. View* root_bar = widget_bar->GetRootView();
  102. root_bar->AddChildView(test_view_bar);
  103. widget_bar->Show();
  104. widget_bar->Activate();
  105. // Set pane focus succeeds, focus on child.
  106. EXPECT_TRUE(test_view_bar->SetPaneFocusAndFocusDefault());
  107. EXPECT_FALSE(test_view_main->HasFocus());
  108. EXPECT_FALSE(widget_main->IsActive());
  109. EXPECT_EQ(test_view_bar, test_view_bar->GetPaneFocusTraversable());
  110. EXPECT_EQ(test_view_bar->child_button(),
  111. test_view_bar->GetWidget()->GetFocusManager()->GetFocusedView());
  112. // Deactivate() is only reliable on Ash. On Windows it uses
  113. // ::GetNextWindow() to simply activate another window, and which one is not
  114. // predictable. On Mac, Deactivate() is not implemented. Note that
  115. // TestBarView calls set_allow_deactivate_on_esc(true), which is only
  116. // otherwise used in Ash.
  117. #if !BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
  118. // Esc should deactivate the widget.
  119. test_view_bar->AcceleratorPressed(test_view_bar->escape_key());
  120. EXPECT_TRUE(widget_main->IsActive());
  121. EXPECT_FALSE(widget_bar->IsActive());
  122. #endif
  123. widget_bar->CloseNow();
  124. widget_bar.reset();
  125. widget_main->CloseNow();
  126. widget_main.reset();
  127. }
  128. TEST_F(AccessiblePaneViewTest, TwoSetPaneFocus) {
  129. TestBarView* test_view = new TestBarView();
  130. TestBarView* test_view_2 = new TestBarView();
  131. std::unique_ptr<Widget> widget(new Widget());
  132. Widget::InitParams params =
  133. CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  134. params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  135. params.bounds = gfx::Rect(50, 50, 650, 650);
  136. widget->Init(std::move(params));
  137. View* root = widget->GetRootView();
  138. root->AddChildView(test_view);
  139. root->AddChildView(test_view_2);
  140. widget->Show();
  141. widget->Activate();
  142. // Set pane focus succeeds, focus on child.
  143. EXPECT_TRUE(test_view->SetPaneFocusAndFocusDefault());
  144. EXPECT_EQ(test_view, test_view->GetPaneFocusTraversable());
  145. EXPECT_EQ(test_view->child_button(),
  146. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  147. // Set focus on another test_view, focus move to that pane.
  148. EXPECT_TRUE(test_view_2->SetPaneFocus(test_view_2->second_child_button()));
  149. EXPECT_FALSE(test_view->child_button() ==
  150. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  151. EXPECT_EQ(test_view_2->second_child_button(),
  152. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  153. widget->CloseNow();
  154. widget.reset();
  155. }
  156. TEST_F(AccessiblePaneViewTest, PaneFocusTraversal) {
  157. TestBarView* test_view = new TestBarView();
  158. TestBarView* original_test_view = new TestBarView();
  159. std::unique_ptr<Widget> widget(new Widget());
  160. Widget::InitParams params =
  161. CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  162. params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  163. params.bounds = gfx::Rect(50, 50, 650, 650);
  164. widget->Init(std::move(params));
  165. View* root = widget->GetRootView();
  166. root->AddChildView(original_test_view);
  167. root->AddChildView(test_view);
  168. widget->Show();
  169. widget->Activate();
  170. // Set pane focus on first view.
  171. EXPECT_TRUE(original_test_view->SetPaneFocus(
  172. original_test_view->third_child_button()));
  173. // Test traversal in second view.
  174. // Set pane focus on second child.
  175. EXPECT_TRUE(test_view->SetPaneFocus(test_view->second_child_button()));
  176. // home
  177. test_view->AcceleratorPressed(test_view->home_key());
  178. EXPECT_EQ(test_view->child_button(),
  179. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  180. // end
  181. test_view->AcceleratorPressed(test_view->end_key());
  182. EXPECT_EQ(test_view->third_child_button(),
  183. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  184. // left
  185. test_view->AcceleratorPressed(test_view->left_key());
  186. EXPECT_EQ(test_view->second_child_button(),
  187. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  188. // right, right
  189. test_view->AcceleratorPressed(test_view->right_key());
  190. test_view->AcceleratorPressed(test_view->right_key());
  191. EXPECT_EQ(test_view->child_button(),
  192. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  193. // ESC
  194. test_view->AcceleratorPressed(test_view->escape_key());
  195. EXPECT_EQ(original_test_view->third_child_button(),
  196. test_view->GetWidget()->GetFocusManager()->GetFocusedView());
  197. widget->CloseNow();
  198. widget.reset();
  199. }
  200. // TODO(crbug.com/1314275): Re-enable this test
  201. #if defined(ADDRESS_SANITIZER) && defined(LEAK_SANITIZER)
  202. #define MAYBE_DoesntCrashOnEscapeWithRemovedView \
  203. DISABLED_DoesntCrashOnEscapeWithRemovedView
  204. #else
  205. #define MAYBE_DoesntCrashOnEscapeWithRemovedView \
  206. DoesntCrashOnEscapeWithRemovedView
  207. #endif
  208. TEST_F(AccessiblePaneViewTest, MAYBE_DoesntCrashOnEscapeWithRemovedView) {
  209. TestBarView* test_view1 = new TestBarView();
  210. TestBarView* test_view2 = new TestBarView();
  211. Widget widget;
  212. Widget::InitParams params =
  213. CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  214. params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  215. params.bounds = gfx::Rect(50, 50, 650, 650);
  216. widget.Init(std::move(params));
  217. View* root = widget.GetRootView();
  218. root->AddChildView(test_view1);
  219. root->AddChildView(test_view2);
  220. widget.Show();
  221. widget.Activate();
  222. View* v1 = test_view1->child_button();
  223. View* v2 = test_view2->child_button();
  224. // Do the following:
  225. // 1. focus |v1|.
  226. // 2. focus |v2|. This makes |test_view2| remember |v1| as having focus.
  227. // 3. Removes |v1| from it's parent.
  228. // 4. Presses escape on |test_view2|. Escape attempts to revert focus to |v1|
  229. // (because of step 2). Because |v1| is not in a widget this should not
  230. // attempt to focus anything.
  231. EXPECT_TRUE(test_view1->SetPaneFocus(v1));
  232. EXPECT_TRUE(test_view2->SetPaneFocus(v2));
  233. v1->parent()->RemoveChildView(v1);
  234. // This shouldn't hit a CHECK in the FocusManager.
  235. EXPECT_TRUE(test_view2->AcceleratorPressed(test_view2->escape_key()));
  236. }
  237. } // namespace views