| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/views/accessibility/view_ax_platform_node_delegate.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/gtest_util.h" |
| #include "ui/accessibility/ax_action_data.h" |
| #include "ui/accessibility/ax_enums.mojom.h" |
| #include "ui/accessibility/ax_node_data.h" |
| #include "ui/accessibility/platform/ax_platform_node.h" |
| #include "ui/accessibility/platform/ax_platform_node_base.h" |
| #include "ui/base/models/table_model.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/views/accessibility/ax_aura_obj_cache.h" |
| #include "ui/views/accessibility/ax_aura_obj_wrapper.h" |
| #include "ui/views/accessibility/ax_event_manager.h" |
| #include "ui/views/accessibility/ax_event_observer.h" |
| #include "ui/views/accessibility/ax_widget_obj_wrapper.h" |
| #include "ui/views/controls/button/button.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/controls/scroll_view.h" |
| #include "ui/views/controls/table/table_view.h" |
| #include "ui/views/test/views_test_base.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace views { |
| namespace test { |
| |
| namespace { |
| |
| class TestButton : public Button { |
| public: |
| TestButton() : Button(Button::PressedCallback()) {} |
| TestButton(const TestButton&) = delete; |
| TestButton& operator=(const TestButton&) = delete; |
| ~TestButton() override = default; |
| }; |
| |
| class TestAXEventObserver : public AXEventObserver { |
| public: |
| explicit TestAXEventObserver(AXAuraObjCache* cache) : cache_(cache) { |
| AXEventManager::Get()->AddObserver(this); |
| } |
| TestAXEventObserver(const TestAXEventObserver&) = delete; |
| TestAXEventObserver& operator=(const TestAXEventObserver&) = delete; |
| ~TestAXEventObserver() override { |
| AXEventManager::Get()->RemoveObserver(this); |
| } |
| |
| // AXEventObserver: |
| void OnViewEvent(View* view, ax::mojom::Event event_type) override { |
| std::vector<AXAuraObjWrapper*> out_children; |
| AXAuraObjWrapper* ax_obj = cache_->GetOrCreate(view->GetWidget()); |
| ax_obj->GetChildren(&out_children); |
| } |
| |
| private: |
| AXAuraObjCache* cache_; |
| }; |
| |
| } // namespace |
| |
| class TestTableModel : public ui::TableModel { |
| public: |
| TestTableModel() = default; |
| |
| // ui::TableModel: |
| int RowCount() override { return 10; } |
| |
| base::string16 GetText(int row, int column_id) override { |
| if (row == -1) |
| return base::string16(); |
| |
| const char* const cells[5][4] = { |
| {"Orange", "Orange", "South america", "$5"}, |
| {"Apple", "Green", "Canada", "$3"}, |
| {"Blue berries", "Blue", "Mexico", "$10.3"}, |
| {"Strawberries", "Red", "California", "$7"}, |
| {"Cantaloupe", "Orange", "South america", "$5"}, |
| }; |
| |
| return base::ASCIIToUTF16(cells[row % 5][column_id]); |
| } |
| |
| void SetObserver(ui::TableModelObserver* observer) override {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TestTableModel); |
| }; |
| |
| class ViewAXPlatformNodeDelegateTest : public ViewsTestBase { |
| public: |
| ViewAXPlatformNodeDelegateTest() = default; |
| ViewAXPlatformNodeDelegateTest(const ViewAXPlatformNodeDelegateTest&) = |
| delete; |
| ViewAXPlatformNodeDelegateTest& operator=( |
| const ViewAXPlatformNodeDelegateTest&) = delete; |
| ~ViewAXPlatformNodeDelegateTest() override = default; |
| |
| void SetUp() override { |
| ViewsTestBase::SetUp(); |
| ui::AXPlatformNode::NotifyAddAXModeFlags(ui::kAXModeComplete); |
| |
| widget_ = new Widget; |
| Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); |
| params.bounds = gfx::Rect(0, 0, 200, 200); |
| widget_->Init(std::move(params)); |
| |
| button_ = new TestButton(); |
| button_->SetID(NON_DEFAULT_VIEW_ID); |
| button_->SetSize(gfx::Size(20, 20)); |
| |
| label_ = new Label(); |
| label_->SetID(DEFAULT_VIEW_ID); |
| button_->AddChildView(label_); |
| |
| widget_->GetContentsView()->AddChildView(button_); |
| widget_->Show(); |
| } |
| |
| void TearDown() override { |
| if (!widget_->IsClosed()) |
| widget_->Close(); |
| ViewsTestBase::TearDown(); |
| } |
| |
| ViewAXPlatformNodeDelegate* button_accessibility() { |
| return static_cast<ViewAXPlatformNodeDelegate*>( |
| &button_->GetViewAccessibility()); |
| } |
| |
| ViewAXPlatformNodeDelegate* label_accessibility() { |
| return static_cast<ViewAXPlatformNodeDelegate*>( |
| &label_->GetViewAccessibility()); |
| } |
| |
| ViewAXPlatformNodeDelegate* view_accessibility(View* view) { |
| return static_cast<ViewAXPlatformNodeDelegate*>( |
| &view->GetViewAccessibility()); |
| } |
| |
| bool SetFocused(ViewAXPlatformNodeDelegate* ax_delegate, bool focused) { |
| ui::AXActionData data; |
| data.action = |
| focused ? ax::mojom::Action::kFocus : ax::mojom::Action::kBlur; |
| return ax_delegate->AccessibilityPerformAction(data); |
| } |
| |
| // Sets up a more complicated structure of Views - one parent View with four |
| // child Views. |
| View::Views SetUpExtraViews() { |
| View* parent_view = |
| widget_->GetContentsView()->AddChildView(std::make_unique<View>()); |
| View::Views views{parent_view}; |
| for (int i = 0; i < 4; i++) |
| views.push_back(parent_view->AddChildView(std::make_unique<View>())); |
| return views; |
| } |
| |
| // Adds group id information to the first 5 values in |views|. |
| void SetUpExtraViewsGroups(const View::Views& views) { |
| // v[0] g1 |
| // | | | | |
| // v[1] g1 v[2] g1 v[3] g2 v[4] |
| ASSERT_GE(views.size(), 5u); |
| |
| views[0]->SetGroup(1); |
| views[1]->SetGroup(1); |
| views[2]->SetGroup(1); |
| views[3]->SetGroup(2); |
| // Skip views[4] - no group id. |
| } |
| |
| // Adds posInSet and setSize overrides to the first 5 values in |views|. |
| void SetUpExtraViewsSetOverrides(const View::Views& views) { |
| // v[0] p4 s4 |
| // | | | | |
| // v[1] p3 s4 v[2] p2 s4 v[3] p- s- v[4] p1 s4 |
| ASSERT_GE(views.size(), 5u); |
| |
| views[0]->GetViewAccessibility().OverridePosInSet(4, 4); |
| views[1]->GetViewAccessibility().OverridePosInSet(3, 4); |
| views[2]->GetViewAccessibility().OverridePosInSet(2, 4); |
| // Skip views[3] - no override. |
| views[4]->GetViewAccessibility().OverridePosInSet(1, 4); |
| } |
| |
| protected: |
| const int DEFAULT_VIEW_ID = 0; |
| const int NON_DEFAULT_VIEW_ID = 1; |
| |
| Widget* widget_ = nullptr; |
| Button* button_ = nullptr; |
| Label* label_ = nullptr; |
| }; |
| |
| class ViewAXPlatformNodeDelegateTableTest |
| : public ViewAXPlatformNodeDelegateTest { |
| public: |
| void SetUp() override { |
| ViewAXPlatformNodeDelegateTest::SetUp(); |
| |
| std::vector<ui::TableColumn> columns; |
| columns.push_back(TestTableColumn(0, "Fruit")); |
| columns.push_back(TestTableColumn(1, "Color")); |
| columns.push_back(TestTableColumn(2, "Origin")); |
| columns.push_back(TestTableColumn(3, "Price")); |
| |
| model_ = std::make_unique<TestTableModel>(); |
| auto table = |
| std::make_unique<TableView>(model_.get(), columns, TEXT_ONLY, true); |
| table_ = table.get(); |
| widget_->GetContentsView()->AddChildView( |
| TableView::CreateScrollViewWithTable(std::move(table))); |
| } |
| |
| ui::TableColumn TestTableColumn(int id, const std::string& title) { |
| ui::TableColumn column; |
| column.id = id; |
| column.title = base::ASCIIToUTF16(title.c_str()); |
| column.sortable = true; |
| return column; |
| } |
| |
| ViewAXPlatformNodeDelegate* table_accessibility() { |
| return view_accessibility(table_); |
| } |
| |
| private: |
| std::unique_ptr<TestTableModel> model_; |
| TableView* table_ = nullptr; // Owned by parent. |
| }; |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, RoleShouldMatch) { |
| EXPECT_EQ(ax::mojom::Role::kButton, button_accessibility()->GetData().role); |
| // Since the label is a subview of |button_|, and the button is keyboard |
| // focusable, the label is assumed to form part of the button and not have a |
| // role of its own. |
| EXPECT_EQ(ax::mojom::Role::kIgnored, label_accessibility()->GetData().role); |
| // This will happen for all potentially keyboard-focusable Views with |
| // non-keyboard-focusable children, so if we make the button unfocusable, the |
| // label will be allowed to have its own role again. |
| button_->SetFocusBehavior(View::FocusBehavior::NEVER); |
| EXPECT_EQ(ax::mojom::Role::kStaticText, |
| label_accessibility()->GetData().role); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, BoundsShouldMatch) { |
| gfx::Rect bounds = gfx::ToEnclosingRect( |
| button_accessibility()->GetData().relative_bounds.bounds); |
| gfx::Rect screen_bounds = |
| button_accessibility()->GetUnclippedScreenBoundsRect(); |
| |
| EXPECT_EQ(button_->GetBoundsInScreen(), bounds); |
| EXPECT_EQ(screen_bounds, bounds); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, LabelIsChildOfButton) { |
| // Disable focus rings for this test: they introduce extra children that can |
| // be either before or after the label, which complicates correctness testing. |
| button_->SetInstallFocusRingOnFocus(false); |
| |
| // |button_| is focusable, so |label_| (as its child) should be ignored. |
| EXPECT_NE(View::FocusBehavior::NEVER, button_->GetFocusBehavior()); |
| EXPECT_EQ(1, button_accessibility()->GetChildCount()); |
| EXPECT_EQ(button_->GetNativeViewAccessible(), |
| label_accessibility()->GetParent()); |
| EXPECT_EQ(ax::mojom::Role::kIgnored, label_accessibility()->GetData().role); |
| |
| // If |button_| is no longer focusable, |label_| should show up again. |
| button_->SetFocusBehavior(View::FocusBehavior::NEVER); |
| EXPECT_EQ(1, button_accessibility()->GetChildCount()); |
| EXPECT_EQ(label_->GetNativeViewAccessible(), |
| button_accessibility()->ChildAtIndex(0)); |
| EXPECT_EQ(button_->GetNativeViewAccessible(), |
| label_accessibility()->GetParent()); |
| EXPECT_NE(ax::mojom::Role::kIgnored, label_accessibility()->GetData().role); |
| } |
| |
| // Verify Views with invisible ancestors have ax::mojom::State::kInvisible. |
| TEST_F(ViewAXPlatformNodeDelegateTest, InvisibleViews) { |
| EXPECT_TRUE(widget_->IsVisible()); |
| EXPECT_FALSE( |
| button_accessibility()->GetData().HasState(ax::mojom::State::kInvisible)); |
| EXPECT_FALSE( |
| label_accessibility()->GetData().HasState(ax::mojom::State::kInvisible)); |
| button_->SetVisible(false); |
| EXPECT_TRUE( |
| button_accessibility()->GetData().HasState(ax::mojom::State::kInvisible)); |
| EXPECT_TRUE( |
| label_accessibility()->GetData().HasState(ax::mojom::State::kInvisible)); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, WritableFocus) { |
| // Make |button_| focusable, and focus/unfocus it via |
| // ViewAXPlatformNodeDelegate. |
| button_->SetFocusBehavior(View::FocusBehavior::ALWAYS); |
| EXPECT_EQ(nullptr, button_->GetFocusManager()->GetFocusedView()); |
| EXPECT_EQ(nullptr, button_accessibility()->GetFocus()); |
| EXPECT_TRUE(SetFocused(button_accessibility(), true)); |
| EXPECT_EQ(button_, button_->GetFocusManager()->GetFocusedView()); |
| EXPECT_EQ(button_->GetNativeViewAccessible(), |
| button_accessibility()->GetFocus()); |
| EXPECT_TRUE(SetFocused(button_accessibility(), false)); |
| EXPECT_EQ(nullptr, button_->GetFocusManager()->GetFocusedView()); |
| EXPECT_EQ(nullptr, button_accessibility()->GetFocus()); |
| |
| // If not focusable at all, SetFocused() should return false. |
| button_->SetEnabled(false); |
| EXPECT_FALSE(SetFocused(button_accessibility(), true)); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, GetAuthorUniqueIdDefault) { |
| ASSERT_EQ(base::WideToUTF16(L""), label_accessibility()->GetAuthorUniqueId()); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, GetAuthorUniqueIdNonDefault) { |
| ASSERT_EQ(base::WideToUTF16(L"view_1"), |
| button_accessibility()->GetAuthorUniqueId()); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, IsOrderedSet) { |
| View::Views group_ids = SetUpExtraViews(); |
| SetUpExtraViewsGroups(group_ids); |
| // Only last element has no group id. |
| EXPECT_TRUE(view_accessibility(group_ids[0])->IsOrderedSet()); |
| EXPECT_TRUE(view_accessibility(group_ids[1])->IsOrderedSet()); |
| EXPECT_TRUE(view_accessibility(group_ids[2])->IsOrderedSet()); |
| EXPECT_TRUE(view_accessibility(group_ids[3])->IsOrderedSet()); |
| EXPECT_FALSE(view_accessibility(group_ids[4])->IsOrderedSet()); |
| |
| EXPECT_TRUE(view_accessibility(group_ids[0])->IsOrderedSetItem()); |
| EXPECT_TRUE(view_accessibility(group_ids[1])->IsOrderedSetItem()); |
| EXPECT_TRUE(view_accessibility(group_ids[2])->IsOrderedSetItem()); |
| EXPECT_TRUE(view_accessibility(group_ids[3])->IsOrderedSetItem()); |
| EXPECT_FALSE(view_accessibility(group_ids[4])->IsOrderedSetItem()); |
| |
| View::Views overrides = SetUpExtraViews(); |
| SetUpExtraViewsSetOverrides(overrides); |
| // Only overrides[3] has no override values for setSize/ posInSet. |
| EXPECT_TRUE(view_accessibility(overrides[0])->IsOrderedSet()); |
| EXPECT_TRUE(view_accessibility(overrides[1])->IsOrderedSet()); |
| EXPECT_TRUE(view_accessibility(overrides[2])->IsOrderedSet()); |
| EXPECT_FALSE(view_accessibility(overrides[3])->IsOrderedSet()); |
| EXPECT_TRUE(view_accessibility(overrides[4])->IsOrderedSet()); |
| |
| EXPECT_TRUE(view_accessibility(overrides[0])->IsOrderedSetItem()); |
| EXPECT_TRUE(view_accessibility(overrides[1])->IsOrderedSetItem()); |
| EXPECT_TRUE(view_accessibility(overrides[2])->IsOrderedSetItem()); |
| EXPECT_FALSE(view_accessibility(overrides[3])->IsOrderedSetItem()); |
| EXPECT_TRUE(view_accessibility(overrides[4])->IsOrderedSetItem()); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, SetSizeAndPosition) { |
| // Test Views with group ids. |
| View::Views group_ids = SetUpExtraViews(); |
| SetUpExtraViewsGroups(group_ids); |
| EXPECT_EQ(view_accessibility(group_ids[0])->GetSetSize(), 3); |
| EXPECT_EQ(view_accessibility(group_ids[0])->GetPosInSet(), 1); |
| EXPECT_EQ(view_accessibility(group_ids[1])->GetSetSize(), 3); |
| EXPECT_EQ(view_accessibility(group_ids[1])->GetPosInSet(), 2); |
| EXPECT_EQ(view_accessibility(group_ids[2])->GetSetSize(), 3); |
| EXPECT_EQ(view_accessibility(group_ids[2])->GetPosInSet(), 3); |
| |
| EXPECT_EQ(view_accessibility(group_ids[3])->GetSetSize(), 1); |
| EXPECT_EQ(view_accessibility(group_ids[3])->GetPosInSet(), 1); |
| |
| EXPECT_FALSE(view_accessibility(group_ids[4])->GetSetSize().has_value()); |
| EXPECT_FALSE(view_accessibility(group_ids[4])->GetPosInSet().has_value()); |
| |
| // Check if a View is ignored, it is not counted in SetSize or PosInSet |
| group_ids[1]->GetViewAccessibility().OverrideIsIgnored(true); |
| group_ids[2]->GetViewAccessibility().OverrideIsIgnored(true); |
| EXPECT_EQ(view_accessibility(group_ids[0])->GetSetSize(), 1); |
| EXPECT_EQ(view_accessibility(group_ids[0])->GetPosInSet(), 1); |
| EXPECT_FALSE(view_accessibility(group_ids[1])->GetSetSize().has_value()); |
| EXPECT_FALSE(view_accessibility(group_ids[1])->GetPosInSet().has_value()); |
| EXPECT_FALSE(view_accessibility(group_ids[2])->GetSetSize().has_value()); |
| EXPECT_FALSE(view_accessibility(group_ids[2])->GetPosInSet().has_value()); |
| group_ids[1]->GetViewAccessibility().OverrideIsIgnored(false); |
| group_ids[2]->GetViewAccessibility().OverrideIsIgnored(false); |
| |
| // Test Views with setSize/ posInSet override values set. |
| View::Views overrides = SetUpExtraViews(); |
| SetUpExtraViewsSetOverrides(overrides); |
| EXPECT_EQ(view_accessibility(overrides[0])->GetSetSize(), 4); |
| EXPECT_EQ(view_accessibility(overrides[0])->GetPosInSet(), 4); |
| EXPECT_EQ(view_accessibility(overrides[1])->GetSetSize(), 4); |
| EXPECT_EQ(view_accessibility(overrides[1])->GetPosInSet(), 3); |
| EXPECT_EQ(view_accessibility(overrides[2])->GetSetSize(), 4); |
| EXPECT_EQ(view_accessibility(overrides[2])->GetPosInSet(), 2); |
| |
| EXPECT_FALSE(view_accessibility(overrides[3])->GetSetSize().has_value()); |
| EXPECT_FALSE(view_accessibility(overrides[3])->GetPosInSet().has_value()); |
| |
| EXPECT_EQ(view_accessibility(overrides[4])->GetSetSize(), 4); |
| EXPECT_EQ(view_accessibility(overrides[4])->GetPosInSet(), 1); |
| |
| // Test Views with both group ids and setSize/ posInSet override values set. |
| // Make sure the override values take precedence when both are set. |
| // Add setSize/ posInSet overrides to the Views with group ids. |
| SetUpExtraViewsSetOverrides(group_ids); |
| EXPECT_EQ(view_accessibility(group_ids[0])->GetSetSize(), 4); |
| EXPECT_EQ(view_accessibility(group_ids[0])->GetPosInSet(), 4); |
| EXPECT_EQ(view_accessibility(group_ids[1])->GetSetSize(), 4); |
| EXPECT_EQ(view_accessibility(group_ids[1])->GetPosInSet(), 3); |
| EXPECT_EQ(view_accessibility(group_ids[2])->GetSetSize(), 4); |
| EXPECT_EQ(view_accessibility(group_ids[2])->GetPosInSet(), 2); |
| |
| EXPECT_EQ(view_accessibility(group_ids[3])->GetSetSize(), 1); |
| EXPECT_EQ(view_accessibility(group_ids[3])->GetPosInSet(), 1); |
| |
| EXPECT_EQ(view_accessibility(group_ids[4])->GetSetSize(), 4); |
| EXPECT_EQ(view_accessibility(group_ids[4])->GetPosInSet(), 1); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigation) { |
| // Adds one extra parent view with four child views to our widget. The parent |
| // view is added as the next sibling of the already present button view. |
| // |
| // Widget |
| // ++Button |
| // ++++Label |
| // 0 = ++ParentView |
| // 1 = ++++ChildView1 |
| // 2 = ++++ChildView2 |
| // 3 = ChildView3 |
| // 4 = ChildView4 |
| View::Views extra_views = SetUpExtraViews(); |
| ViewAXPlatformNodeDelegate* parent_view = view_accessibility(extra_views[0]); |
| ViewAXPlatformNodeDelegate* child_view_1 = view_accessibility(extra_views[1]); |
| ViewAXPlatformNodeDelegate* child_view_2 = view_accessibility(extra_views[2]); |
| ViewAXPlatformNodeDelegate* child_view_3 = view_accessibility(extra_views[3]); |
| ViewAXPlatformNodeDelegate* child_view_4 = view_accessibility(extra_views[4]); |
| |
| EXPECT_EQ(view_accessibility(widget_->GetContentsView())->GetNativeObject(), |
| parent_view->GetParent()); |
| EXPECT_EQ(4, parent_view->GetChildCount()); |
| |
| EXPECT_EQ(2, button_accessibility()->GetIndexInParent()); |
| EXPECT_EQ(3, parent_view->GetIndexInParent()); |
| |
| EXPECT_EQ(child_view_1->GetNativeObject(), parent_view->ChildAtIndex(0)); |
| EXPECT_EQ(child_view_2->GetNativeObject(), parent_view->ChildAtIndex(1)); |
| EXPECT_EQ(child_view_3->GetNativeObject(), parent_view->ChildAtIndex(2)); |
| EXPECT_EQ(child_view_4->GetNativeObject(), parent_view->ChildAtIndex(3)); |
| |
| EXPECT_EQ(nullptr, parent_view->GetNextSibling()); |
| EXPECT_EQ(button_accessibility()->GetNativeObject(), |
| parent_view->GetPreviousSibling()); |
| |
| EXPECT_EQ(parent_view->GetNativeObject(), child_view_1->GetParent()); |
| EXPECT_EQ(0, child_view_1->GetChildCount()); |
| EXPECT_EQ(0, child_view_1->GetIndexInParent()); |
| EXPECT_EQ(child_view_2->GetNativeObject(), child_view_1->GetNextSibling()); |
| EXPECT_EQ(nullptr, child_view_1->GetPreviousSibling()); |
| |
| EXPECT_EQ(parent_view->GetNativeObject(), child_view_2->GetParent()); |
| EXPECT_EQ(0, child_view_2->GetChildCount()); |
| EXPECT_EQ(1, child_view_2->GetIndexInParent()); |
| EXPECT_EQ(child_view_3->GetNativeObject(), child_view_2->GetNextSibling()); |
| EXPECT_EQ(child_view_1->GetNativeObject(), |
| child_view_2->GetPreviousSibling()); |
| |
| EXPECT_EQ(parent_view->GetNativeObject(), child_view_3->GetParent()); |
| EXPECT_EQ(0, child_view_3->GetChildCount()); |
| EXPECT_EQ(2, child_view_3->GetIndexInParent()); |
| EXPECT_EQ(child_view_4->GetNativeObject(), child_view_3->GetNextSibling()); |
| EXPECT_EQ(child_view_2->GetNativeObject(), |
| child_view_3->GetPreviousSibling()); |
| |
| EXPECT_EQ(parent_view->GetNativeObject(), child_view_4->GetParent()); |
| EXPECT_EQ(0, child_view_4->GetChildCount()); |
| EXPECT_EQ(3, child_view_4->GetIndexInParent()); |
| EXPECT_EQ(nullptr, child_view_4->GetNextSibling()); |
| EXPECT_EQ(child_view_3->GetNativeObject(), |
| child_view_4->GetPreviousSibling()); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, TreeNavigationWithIgnoredViews) { |
| // Adds one extra parent view with four child views to our widget. The parent |
| // view is added as the next sibling of the already present button view. |
| // |
| // Widget |
| // ++Button |
| // ++++Label |
| // 0 = ++ParentView |
| // 1 = ++++ChildView1 |
| // 2 = ++++ChildView2 |
| // 3 = ChildView3 |
| // 4 = ChildView4 |
| View::Views extra_views = SetUpExtraViews(); |
| ViewAXPlatformNodeDelegate* contents_view = |
| view_accessibility(widget_->GetContentsView()); |
| ViewAXPlatformNodeDelegate* parent_view = view_accessibility(extra_views[0]); |
| ViewAXPlatformNodeDelegate* child_view_1 = view_accessibility(extra_views[1]); |
| ViewAXPlatformNodeDelegate* child_view_2 = view_accessibility(extra_views[2]); |
| ViewAXPlatformNodeDelegate* child_view_3 = view_accessibility(extra_views[3]); |
| ViewAXPlatformNodeDelegate* child_view_4 = view_accessibility(extra_views[4]); |
| |
| // Mark the parent view and the second child view as ignored. |
| parent_view->OverrideIsIgnored(true); |
| child_view_2->OverrideIsIgnored(true); |
| |
| EXPECT_EQ(contents_view->GetNativeObject(), parent_view->GetParent()); |
| EXPECT_EQ(3, parent_view->GetChildCount()); |
| |
| EXPECT_EQ(2, button_accessibility()->GetIndexInParent()); |
| EXPECT_EQ(-1, parent_view->GetIndexInParent()); |
| |
| EXPECT_EQ(child_view_1->GetNativeObject(), parent_view->ChildAtIndex(0)); |
| EXPECT_EQ(child_view_3->GetNativeObject(), parent_view->ChildAtIndex(1)); |
| EXPECT_EQ(child_view_4->GetNativeObject(), parent_view->ChildAtIndex(2)); |
| |
| EXPECT_EQ(button_accessibility()->GetNativeObject(), |
| contents_view->ChildAtIndex(2)); |
| EXPECT_EQ(child_view_1->GetNativeObject(), contents_view->ChildAtIndex(3)); |
| EXPECT_EQ(child_view_3->GetNativeObject(), contents_view->ChildAtIndex(4)); |
| EXPECT_EQ(child_view_4->GetNativeObject(), contents_view->ChildAtIndex(5)); |
| |
| EXPECT_EQ(nullptr, parent_view->GetNextSibling()); |
| EXPECT_EQ(nullptr, parent_view->GetPreviousSibling()); |
| |
| EXPECT_EQ(contents_view->GetNativeObject(), child_view_1->GetParent()); |
| EXPECT_EQ(0, child_view_1->GetChildCount()); |
| EXPECT_EQ(3, child_view_1->GetIndexInParent()); |
| EXPECT_EQ(child_view_3->GetNativeObject(), child_view_1->GetNextSibling()); |
| EXPECT_EQ(button_accessibility()->GetNativeObject(), |
| child_view_1->GetPreviousSibling()); |
| |
| EXPECT_EQ(contents_view->GetNativeObject(), child_view_2->GetParent()); |
| EXPECT_EQ(0, child_view_2->GetChildCount()); |
| EXPECT_EQ(-1, child_view_2->GetIndexInParent()); |
| EXPECT_EQ(nullptr, child_view_2->GetNextSibling()); |
| EXPECT_EQ(nullptr, child_view_2->GetPreviousSibling()); |
| |
| EXPECT_EQ(contents_view->GetNativeObject(), child_view_3->GetParent()); |
| EXPECT_EQ(0, child_view_3->GetChildCount()); |
| EXPECT_EQ(4, child_view_3->GetIndexInParent()); |
| EXPECT_EQ(child_view_4->GetNativeObject(), child_view_3->GetNextSibling()); |
| EXPECT_EQ(child_view_1->GetNativeObject(), |
| child_view_3->GetPreviousSibling()); |
| |
| EXPECT_EQ(contents_view->GetNativeObject(), child_view_4->GetParent()); |
| EXPECT_EQ(0, child_view_4->GetChildCount()); |
| EXPECT_EQ(5, child_view_4->GetIndexInParent()); |
| EXPECT_EQ(nullptr, child_view_4->GetNextSibling()); |
| EXPECT_EQ(child_view_3->GetNativeObject(), |
| child_view_4->GetPreviousSibling()); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, OverrideHasPopup) { |
| View::Views view_ids = SetUpExtraViews(); |
| |
| view_ids[1]->GetViewAccessibility().OverrideHasPopup( |
| ax::mojom::HasPopup::kTrue); |
| view_ids[2]->GetViewAccessibility().OverrideHasPopup( |
| ax::mojom::HasPopup::kMenu); |
| |
| ui::AXNodeData node_data_0; |
| view_ids[0]->GetViewAccessibility().GetAccessibleNodeData(&node_data_0); |
| EXPECT_EQ(node_data_0.GetHasPopup(), ax::mojom::HasPopup::kFalse); |
| |
| ui::AXNodeData node_data_1; |
| view_ids[1]->GetViewAccessibility().GetAccessibleNodeData(&node_data_1); |
| EXPECT_EQ(node_data_1.GetHasPopup(), ax::mojom::HasPopup::kTrue); |
| |
| ui::AXNodeData node_data_2; |
| view_ids[2]->GetViewAccessibility().GetAccessibleNodeData(&node_data_2); |
| EXPECT_EQ(node_data_2.GetHasPopup(), ax::mojom::HasPopup::kMenu); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTest, FocusOnMenuClose) { |
| // Set Focus on the button |
| button_->SetFocusBehavior(View::FocusBehavior::ALWAYS); |
| EXPECT_EQ(nullptr, button_->GetFocusManager()->GetFocusedView()); |
| EXPECT_EQ(nullptr, button_accessibility()->GetFocus()); |
| |
| EXPECT_TRUE(SetFocused(button_accessibility(), true)); |
| EXPECT_EQ(button_->GetNativeViewAccessible(), |
| button_accessibility()->GetFocus()); |
| |
| // Fire FocusAfterMenuClose event on the button. |
| base::RunLoop run_loop; |
| ui::AXPlatformNodeBase::SetOnNotifyEventCallbackForTesting( |
| ax::mojom::Event::kFocusAfterMenuClose, run_loop.QuitClosure()); |
| button_accessibility()->FireFocusAfterMenuClose(); |
| run_loop.Run(); |
| EXPECT_EQ(button_->GetNativeViewAccessible(), |
| button_accessibility()->GetFocus()); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTableTest, TableHasHeader) { |
| EXPECT_TRUE(table_accessibility()->GetTableHasColumnOrRowHeaderNode()); |
| EXPECT_EQ(size_t{4}, table_accessibility()->GetColHeaderNodeIds().size()); |
| EXPECT_TRUE(table_accessibility()->GetColHeaderNodeIds(5).empty()); |
| } |
| |
| TEST_F(ViewAXPlatformNodeDelegateTableTest, TableHasCell) { |
| EXPECT_NE(base::nullopt, table_accessibility()->GetCellId(0, 0)); |
| EXPECT_NE(base::nullopt, table_accessibility()->GetCellId(0, 3)); |
| EXPECT_NE(base::nullopt, table_accessibility()->GetCellId(9, 3)); |
| EXPECT_DCHECK_DEATH(table_accessibility()->GetCellId(-1, 0)); |
| EXPECT_DCHECK_DEATH(table_accessibility()->GetCellId(0, -1)); |
| EXPECT_DCHECK_DEATH(table_accessibility()->GetCellId(10, 0)); |
| EXPECT_DCHECK_DEATH(table_accessibility()->GetCellId(0, 4)); |
| } |
| |
| #if defined(USE_AURA) |
| class DerivedTestView : public View { |
| public: |
| DerivedTestView() = default; |
| ~DerivedTestView() override = default; |
| |
| void OnBlur() override { SetVisible(false); } |
| }; |
| |
| using AXViewTest = ViewsTestBase; |
| |
| // Check if the destruction of the widget ends successfully if |view|'s |
| // visibility changed during destruction. |
| TEST_F(AXViewTest, LayoutCalledInvalidateRootView) { |
| // TODO(jamescook): Construct a real AutomationManagerAura rather than using |
| // this observer to simulate it. |
| AXAuraObjCache cache; |
| TestAXEventObserver observer(&cache); |
| std::unique_ptr<Widget> widget(new Widget); |
| Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); |
| params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| widget->Init(std::move(params)); |
| widget->Show(); |
| |
| View* root = widget->GetRootView(); |
| DerivedTestView* parent = new DerivedTestView(); |
| DerivedTestView* child = new DerivedTestView(); |
| root->AddChildView(parent); |
| parent->AddChildView(child); |
| child->SetFocusBehavior(DerivedTestView::FocusBehavior::ALWAYS); |
| parent->SetFocusBehavior(DerivedTestView::FocusBehavior::ALWAYS); |
| root->SetFocusBehavior(DerivedTestView::FocusBehavior::ALWAYS); |
| parent->RequestFocus(); |
| // During the destruction of parent, OnBlur will be called and change the |
| // visibility to false. |
| parent->SetVisible(true); |
| |
| cache.GetOrCreate(widget.get()); |
| } |
| #endif |
| |
| } // namespace test |
| } // namespace views |