| // 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 "ui/accessibility/platform/browser_accessibility_manager.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <string> |
| |
| #include "base/scoped_observation.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "build/buildflag.h" |
| #include "ui/accessibility/ax_tree_observer.h" |
| #include "ui/accessibility/platform/browser_accessibility.h" |
| #if BUILDFLAG(IS_WIN) |
| #include "ui/accessibility/platform/browser_accessibility_win.h" |
| #endif |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/accessibility/ax_common.h" |
| #include "ui/accessibility/ax_tree.h" |
| #include "ui/accessibility/ax_updates_and_events.h" |
| #include "ui/accessibility/platform/test_ax_node_id_delegate.h" |
| #include "ui/accessibility/platform/test_ax_platform_tree_manager_delegate.h" |
| #include "ui/accessibility/test_ax_tree_update.h" |
| |
| #if BUILDFLAG(IS_ANDROID) |
| #include "content/browser/accessibility/browser_accessibility_manager_android.h" |
| #endif |
| |
| namespace content { |
| |
| namespace { |
| |
| class CountingAXTreeObserver : public ui::AXTreeObserver { |
| public: |
| CountingAXTreeObserver() = default; |
| ~CountingAXTreeObserver() override = default; |
| CountingAXTreeObserver(const CountingAXTreeObserver&) = delete; |
| CountingAXTreeObserver& operator=(const CountingAXTreeObserver&) = delete; |
| |
| int reparent_count() { return reparent_count_; } |
| int update_count() { return update_count_; } |
| int node_count() { return node_count_; } |
| |
| private: |
| void OnNodeReparented(ui::AXTree* tree, ui::AXNode* node) override { |
| ++reparent_count_; |
| } |
| |
| void OnAtomicUpdateFinished(ui::AXTree* tree, |
| bool root_changed, |
| const std::vector<Change>& changes) override { |
| ++update_count_; |
| node_count_ += static_cast<int>(changes.size()); |
| } |
| |
| int reparent_count_ = 0; |
| int update_count_ = 0; |
| int node_count_ = 0; |
| }; |
| |
| ui::BrowserAccessibilityManager* CreateBrowserAccessibilityManager( |
| const ui::AXTreeUpdate& initial_tree, |
| ui::AXNodeIdDelegate& node_id_delegate, |
| ui::AXPlatformTreeManagerDelegate* delegate) { |
| #if BUILDFLAG(IS_ANDROID) |
| return content::BrowserAccessibilityManagerAndroid::Create( |
| initial_tree, node_id_delegate, delegate); |
| #else |
| return ui::BrowserAccessibilityManager::Create(initial_tree, node_id_delegate, |
| delegate); |
| #endif |
| } |
| |
| } // anonymous namespace |
| |
| class BrowserAccessibilityManagerTest : public testing::Test { |
| public: |
| BrowserAccessibilityManagerTest() = default; |
| |
| BrowserAccessibilityManagerTest(const BrowserAccessibilityManagerTest&) = |
| delete; |
| BrowserAccessibilityManagerTest& operator=( |
| const BrowserAccessibilityManagerTest&) = delete; |
| |
| ~BrowserAccessibilityManagerTest() override = default; |
| |
| protected: |
| void SetUp() override; |
| |
| std::unique_ptr<ui::TestAXPlatformTreeManagerDelegate> |
| test_browser_accessibility_delegate_; |
| ui::TestAXNodeIdDelegate node_id_delegate_; |
| const content::BrowserTaskEnvironment task_environment_; |
| }; |
| |
| void BrowserAccessibilityManagerTest::SetUp() { |
| testing::Test::SetUp(); |
| test_browser_accessibility_delegate_ = |
| std::make_unique<ui::TestAXPlatformTreeManagerDelegate>(); |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, TestErrorOnCreateIsFatal) { |
| // Test that BrowserAccessibilityManager raises a fatal error |
| // (which will crash the renderer) if the same id is used in |
| // two places in the tree. |
| |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.child_ids.push_back(2); |
| root.child_ids.push_back(2); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager; |
| EXPECT_DEATH_IF_SUPPORTED( |
| manager.reset(CreateBrowserAccessibilityManager( |
| MakeAXTreeUpdateForTesting(root), node_id_delegate_, |
| test_browser_accessibility_delegate_.get())), |
| "Node 1 has 1 duplicate child ids"); |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, TestErrorOnUpdate) { |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| |
| ui::AXNodeData node2; |
| node2.id = 2; |
| root.child_ids.push_back(2); |
| |
| ui::AXNodeData node3; |
| node3.id = 3; |
| root.child_ids.push_back(3); |
| |
| ui::AXNodeData node4; |
| node4.id = 4; |
| node3.child_ids.push_back(4); |
| |
| ui::AXNodeData node5; |
| node5.id = 5; |
| root.child_ids.push_back(5); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| MakeAXTreeUpdateForTesting(root, node2, node3, node4, node5), |
| node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| // node4 has two child ids now. |
| node4.child_ids.push_back(5); |
| node4.child_ids.push_back(5); |
| ui::AXTreeUpdate update = MakeAXTreeUpdateForTesting(node4, node5); |
| update.tree_data.tree_id = manager->GetTreeID(); |
| ui::AXUpdatesAndEvents events; |
| events.updates = {update}; |
| |
| #if AX_FAIL_FAST_BUILD() |
| // Update errors are fatal in AX_FAIL_FAST_BUILD builds. |
| EXPECT_DEATH_IF_SUPPORTED(manager->OnAccessibilityEvents(events), |
| "Node 4 has 1 duplicate child ids"); |
| #else |
| ASSERT_FALSE(manager->OnAccessibilityEvents(events)); |
| #endif |
| } |
| |
| // This test depends on hypertext, which is only used on |
| // Linux and Windows. |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| TEST_F(BrowserAccessibilityManagerTest, BoundsForRange) { |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.relative_bounds.bounds = gfx::RectF(0, 0, 800, 600); |
| |
| ui::AXNodeData static_text; |
| static_text.id = 2; |
| static_text.role = ax::mojom::Role::kStaticText; |
| static_text.SetName("Hello, world."); |
| static_text.relative_bounds.bounds = gfx::RectF(100, 100, 29, 18); |
| root.child_ids.push_back(2); |
| |
| ui::AXNodeData inline_text1; |
| inline_text1.id = 3; |
| inline_text1.role = ax::mojom::Role::kInlineTextBox; |
| inline_text1.SetName("Hello, "); |
| inline_text1.relative_bounds.bounds = gfx::RectF(100, 100, 29, 9); |
| inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); |
| std::vector<int32_t> character_offsets1; |
| character_offsets1.push_back(6); // 0 |
| character_offsets1.push_back(11); // 1 |
| character_offsets1.push_back(16); // 2 |
| character_offsets1.push_back(21); // 3 |
| character_offsets1.push_back(26); // 4 |
| character_offsets1.push_back(29); // 5 |
| character_offsets1.push_back(29); // 6 (note that the space has no width) |
| inline_text1.AddIntListAttribute( |
| ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets1); |
| static_text.child_ids.push_back(3); |
| |
| ui::AXNodeData inline_text2; |
| inline_text2.id = 4; |
| inline_text2.role = ax::mojom::Role::kInlineTextBox; |
| inline_text2.SetName("world."); |
| inline_text2.relative_bounds.bounds = gfx::RectF(100, 109, 28, 9); |
| inline_text2.SetTextDirection(ax::mojom::WritingDirection::kLtr); |
| std::vector<int32_t> character_offsets2; |
| character_offsets2.push_back(5); |
| character_offsets2.push_back(10); |
| character_offsets2.push_back(15); |
| character_offsets2.push_back(20); |
| character_offsets2.push_back(25); |
| character_offsets2.push_back(28); |
| inline_text2.AddIntListAttribute( |
| ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets2); |
| static_text.child_ids.push_back(4); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| MakeAXTreeUpdateForTesting(root, static_text, inline_text1, |
| inline_text2), |
| node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_accessible = |
| manager->GetBrowserAccessibilityRoot(); |
| ASSERT_NE(nullptr, root_accessible); |
| ui::BrowserAccessibility* static_text_accessible = |
| root_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, static_text_accessible); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 1, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 5, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 7, 1, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 7, 5, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 5, 3, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 13, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| // Note that each child in the parent element is represented by a single |
| // embedded object character and not by its text. |
| // TODO(nektar): Investigate failure on Linux. |
| EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(), |
| root_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 13, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| } |
| #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| |
| TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeMultiElement) { |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.relative_bounds.bounds = gfx::RectF(0, 0, 800, 600); |
| |
| ui::AXNodeData static_text; |
| static_text.id = 2; |
| static_text.role = ax::mojom::Role::kStaticText; |
| static_text.SetName("ABC"); |
| static_text.relative_bounds.bounds = gfx::RectF(0, 20, 33, 9); |
| root.child_ids.push_back(2); |
| |
| ui::AXNodeData inline_text1; |
| inline_text1.id = 3; |
| inline_text1.role = ax::mojom::Role::kInlineTextBox; |
| inline_text1.SetName("ABC"); |
| inline_text1.relative_bounds.bounds = gfx::RectF(0, 20, 33, 9); |
| inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); |
| std::vector<int32_t> character_offsets{10, 21, 33}; |
| inline_text1.AddIntListAttribute( |
| ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets); |
| static_text.child_ids.push_back(3); |
| |
| ui::AXNodeData static_text2; |
| static_text2.id = 4; |
| static_text2.role = ax::mojom::Role::kStaticText; |
| static_text2.SetName("ABC"); |
| static_text2.relative_bounds.bounds = gfx::RectF(10, 40, 33, 9); |
| root.child_ids.push_back(4); |
| |
| ui::AXNodeData inline_text2; |
| inline_text2.id = 5; |
| inline_text2.role = ax::mojom::Role::kInlineTextBox; |
| inline_text2.SetName("ABC"); |
| inline_text2.relative_bounds.bounds = gfx::RectF(10, 40, 33, 9); |
| inline_text2.SetTextDirection(ax::mojom::WritingDirection::kLtr); |
| inline_text2.AddIntListAttribute( |
| ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets); |
| static_text2.child_ids.push_back(5); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| MakeAXTreeUpdateForTesting(root, static_text, inline_text1, |
| static_text2, inline_text2), |
| node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_accessible = |
| manager->GetBrowserAccessibilityRoot(); |
| ASSERT_NE(nullptr, root_accessible); |
| ui::BrowserAccessibility* static_text_accessible = |
| root_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, static_text_accessible); |
| ui::BrowserAccessibility* static_text_accessible2 = |
| root_accessible->PlatformGetChild(1); |
| ASSERT_NE(nullptr, static_text_accessible); |
| |
| // The first line. |
| EXPECT_EQ(gfx::Rect(0, 20, 33, 9).ToString(), |
| manager |
| ->GetRootFrameInnerTextRangeBoundsRect( |
| *static_text_accessible, 0, *static_text_accessible, 3) |
| .ToString()); |
| |
| // Part of the first line. |
| EXPECT_EQ(gfx::Rect(0, 20, 21, 9).ToString(), |
| manager |
| ->GetRootFrameInnerTextRangeBoundsRect( |
| *static_text_accessible, 0, *static_text_accessible, 2) |
| .ToString()); |
| |
| // Part of the first line. |
| EXPECT_EQ(gfx::Rect(10, 20, 23, 9).ToString(), |
| manager |
| ->GetRootFrameInnerTextRangeBoundsRect( |
| *static_text_accessible, 1, *static_text_accessible, 3) |
| .ToString()); |
| |
| // The second line. |
| EXPECT_EQ(gfx::Rect(10, 40, 33, 9).ToString(), |
| manager |
| ->GetRootFrameInnerTextRangeBoundsRect( |
| *static_text_accessible2, 0, *static_text_accessible2, 3) |
| .ToString()); |
| |
| // All of both lines. |
| EXPECT_EQ(gfx::Rect(0, 20, 43, 29).ToString(), |
| manager |
| ->GetRootFrameInnerTextRangeBoundsRect( |
| *static_text_accessible, 0, *static_text_accessible2, 3) |
| .ToString()); |
| |
| // Part of both lines. |
| EXPECT_EQ(gfx::Rect(10, 20, 23, 29).ToString(), |
| manager |
| ->GetRootFrameInnerTextRangeBoundsRect( |
| *static_text_accessible, 2, *static_text_accessible2, 1) |
| .ToString()); |
| |
| // Part of both lines in reverse order. |
| EXPECT_EQ(gfx::Rect(10, 20, 23, 29).ToString(), |
| manager |
| ->GetRootFrameInnerTextRangeBoundsRect( |
| *static_text_accessible2, 1, *static_text_accessible, 2) |
| .ToString()); |
| } |
| |
| // This test depends on hypertext, which is only used on |
| // Linux and Windows. |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) { |
| // In this example, we assume that the string "123abc" is rendered with |
| // "123" going left-to-right and "abc" going right-to-left. In other |
| // words, on-screen it would look like "123cba". This is possible to |
| // achieve if the source string had unicode control characters |
| // to switch directions. This test doesn't worry about how, though - it just |
| // tests that if something like that were to occur, |
| // GetRootFrameRangeBoundsRect returns the correct bounds for different |
| // ranges. |
| |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.relative_bounds.bounds = gfx::RectF(0, 0, 800, 600); |
| |
| ui::AXNodeData static_text; |
| static_text.id = 2; |
| static_text.role = ax::mojom::Role::kStaticText; |
| static_text.SetName("123abc"); |
| static_text.relative_bounds.bounds = gfx::RectF(100, 100, 60, 20); |
| root.child_ids.push_back(2); |
| |
| ui::AXNodeData inline_text1; |
| inline_text1.id = 3; |
| inline_text1.role = ax::mojom::Role::kInlineTextBox; |
| inline_text1.SetName("123"); |
| inline_text1.relative_bounds.bounds = gfx::RectF(100, 100, 30, 20); |
| inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); |
| std::vector<int32_t> character_offsets1; |
| character_offsets1.push_back(10); // 0 |
| character_offsets1.push_back(20); // 1 |
| character_offsets1.push_back(30); // 2 |
| inline_text1.AddIntListAttribute( |
| ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets1); |
| static_text.child_ids.push_back(3); |
| |
| ui::AXNodeData inline_text2; |
| inline_text2.id = 4; |
| inline_text2.role = ax::mojom::Role::kInlineTextBox; |
| inline_text2.SetName("abc"); |
| inline_text2.relative_bounds.bounds = gfx::RectF(130, 100, 30, 20); |
| inline_text2.SetTextDirection(ax::mojom::WritingDirection::kRtl); |
| std::vector<int32_t> character_offsets2; |
| character_offsets2.push_back(10); |
| character_offsets2.push_back(20); |
| character_offsets2.push_back(30); |
| inline_text2.AddIntListAttribute( |
| ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets2); |
| static_text.child_ids.push_back(4); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| MakeAXTreeUpdateForTesting(root, static_text, inline_text1, |
| inline_text2), |
| node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_accessible = |
| manager->GetBrowserAccessibilityRoot(); |
| ASSERT_NE(nullptr, root_accessible); |
| ui::BrowserAccessibility* static_text_accessible = |
| root_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, static_text_accessible); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 6, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 1, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 3, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 3, 1, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 3, 3, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| // This range is only two characters, but because of the direction switch |
| // the bounds are as wide as four characters. |
| EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 2, 2, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| } |
| #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| |
| // This test depends on hypertext, which is only used on |
| // Linux and Windows. |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeScrolledWindow) { |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.AddIntAttribute(ax::mojom::IntAttribute::kScrollX, 25); |
| root.AddIntAttribute(ax::mojom::IntAttribute::kScrollY, 50); |
| root.relative_bounds.bounds = gfx::RectF(0, 0, 800, 600); |
| |
| ui::AXNodeData static_text; |
| static_text.id = 2; |
| static_text.role = ax::mojom::Role::kStaticText; |
| static_text.SetName("ABC"); |
| static_text.relative_bounds.bounds = gfx::RectF(100, 100, 16, 9); |
| root.child_ids.push_back(2); |
| |
| ui::AXNodeData inline_text; |
| inline_text.id = 3; |
| inline_text.role = ax::mojom::Role::kInlineTextBox; |
| inline_text.SetName("ABC"); |
| inline_text.relative_bounds.bounds = gfx::RectF(100, 100, 16, 9); |
| inline_text.SetTextDirection(ax::mojom::WritingDirection::kLtr); |
| std::vector<int32_t> character_offsets1; |
| character_offsets1.push_back(6); // 0 |
| character_offsets1.push_back(11); // 1 |
| character_offsets1.push_back(16); // 2 |
| inline_text.AddIntListAttribute( |
| ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets1); |
| static_text.child_ids.push_back(3); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| MakeAXTreeUpdateForTesting(root, static_text, inline_text), |
| node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_accessible = |
| manager->GetBrowserAccessibilityRoot(); |
| ASSERT_NE(nullptr, root_accessible); |
| ui::BrowserAccessibility* static_text_accessible = |
| root_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, static_text_accessible); |
| |
| if (manager->UseRootScrollOffsetsWhenComputingBounds()) { |
| EXPECT_EQ(gfx::Rect(75, 50, 16, 9).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 3, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| } else { |
| EXPECT_EQ(gfx::Rect(100, 100, 16, 9).ToString(), |
| static_text_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 3, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| } |
| } |
| #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| |
| // This test depends on hypertext, which is only used on |
| // Linux and Windows. |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| TEST_F(BrowserAccessibilityManagerTest, BoundsForRangeOnParentElement) { |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.child_ids.push_back(2); |
| root.relative_bounds.bounds = gfx::RectF(0, 0, 800, 600); |
| |
| ui::AXNodeData div; |
| div.id = 2; |
| div.role = ax::mojom::Role::kGenericContainer; |
| div.relative_bounds.bounds = gfx::RectF(100, 100, 100, 20); |
| div.child_ids.push_back(3); |
| div.child_ids.push_back(4); |
| div.child_ids.push_back(5); |
| |
| ui::AXNodeData static_text1; |
| static_text1.id = 3; |
| static_text1.role = ax::mojom::Role::kStaticText; |
| static_text1.SetName("AB"); |
| static_text1.relative_bounds.bounds = gfx::RectF(100, 100, 40, 20); |
| static_text1.child_ids.push_back(6); |
| |
| ui::AXNodeData img; |
| img.id = 4; |
| img.role = ax::mojom::Role::kImage; |
| img.SetName("Test image"); |
| img.relative_bounds.bounds = gfx::RectF(140, 100, 20, 20); |
| |
| ui::AXNodeData static_text2; |
| static_text2.id = 5; |
| static_text2.role = ax::mojom::Role::kStaticText; |
| static_text2.SetName("CD"); |
| static_text2.relative_bounds.bounds = gfx::RectF(160, 100, 40, 20); |
| static_text2.child_ids.push_back(7); |
| |
| ui::AXNodeData inline_text1; |
| inline_text1.id = 6; |
| inline_text1.role = ax::mojom::Role::kInlineTextBox; |
| inline_text1.SetName("AB"); |
| inline_text1.relative_bounds.bounds = gfx::RectF(100, 100, 40, 20); |
| inline_text1.SetTextDirection(ax::mojom::WritingDirection::kLtr); |
| std::vector<int32_t> character_offsets1; |
| character_offsets1.push_back(20); // 0 |
| character_offsets1.push_back(40); // 1 |
| inline_text1.AddIntListAttribute( |
| ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets1); |
| |
| ui::AXNodeData inline_text2; |
| inline_text2.id = 7; |
| inline_text2.role = ax::mojom::Role::kInlineTextBox; |
| inline_text2.SetName("CD"); |
| inline_text2.relative_bounds.bounds = gfx::RectF(160, 100, 40, 20); |
| inline_text2.SetTextDirection(ax::mojom::WritingDirection::kLtr); |
| std::vector<int32_t> character_offsets2; |
| character_offsets2.push_back(20); // 0 |
| character_offsets2.push_back(40); // 1 |
| inline_text2.AddIntListAttribute( |
| ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets2); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| MakeAXTreeUpdateForTesting(root, div, static_text1, img, static_text2, |
| inline_text1, inline_text2), |
| node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| ui::BrowserAccessibility* root_accessible = |
| manager->GetBrowserAccessibilityRoot(); |
| ASSERT_NE(nullptr, root_accessible); |
| ui::BrowserAccessibility* div_accessible = |
| root_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, div_accessible); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(), |
| div_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 1, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(), |
| div_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 2, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(), |
| div_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 4, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(), |
| div_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 1, 3, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(), |
| div_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 1, 4, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| |
| EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(), |
| div_accessible |
| ->GetRootFrameHypertextRangeBoundsRect( |
| 0, 5, ui::AXClippingBehavior::kUnclipped) |
| .ToString()); |
| } |
| #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| |
| TEST_F(BrowserAccessibilityManagerTest, TestNextPreviousInTreeOrder) { |
| ui::TestAXTreeUpdate update(std::string(R"HTML( |
| ++1 kRootWebArea |
| ++++2 kUnknown |
| ++++3 kUnknown |
| ++++++4 kUnknown |
| ++++5 kUnknown |
| )HTML")); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| update, node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_accessible = |
| manager->GetBrowserAccessibilityRoot(); |
| ASSERT_NE(nullptr, root_accessible); |
| ASSERT_EQ(3U, root_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* node2_accessible = |
| root_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, node2_accessible); |
| ui::BrowserAccessibility* node3_accessible = |
| root_accessible->PlatformGetChild(1); |
| ASSERT_NE(nullptr, node3_accessible); |
| ASSERT_EQ(1U, node3_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* node4_accessible = |
| node3_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, node4_accessible); |
| ui::BrowserAccessibility* node5_accessible = |
| root_accessible->PlatformGetChild(2); |
| ASSERT_NE(nullptr, node5_accessible); |
| |
| EXPECT_EQ(nullptr, manager->NextInTreeOrder(nullptr)); |
| EXPECT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible)); |
| EXPECT_EQ(node3_accessible, manager->NextInTreeOrder(node2_accessible)); |
| EXPECT_EQ(node4_accessible, manager->NextInTreeOrder(node3_accessible)); |
| EXPECT_EQ(node5_accessible, manager->NextInTreeOrder(node4_accessible)); |
| EXPECT_EQ(nullptr, manager->NextInTreeOrder(node5_accessible)); |
| |
| EXPECT_EQ(nullptr, manager->PreviousInTreeOrder(nullptr, false)); |
| EXPECT_EQ(node4_accessible, |
| manager->PreviousInTreeOrder(node5_accessible, false)); |
| EXPECT_EQ(node3_accessible, |
| manager->PreviousInTreeOrder(node4_accessible, false)); |
| EXPECT_EQ(node2_accessible, |
| manager->PreviousInTreeOrder(node3_accessible, false)); |
| EXPECT_EQ(root_accessible, |
| manager->PreviousInTreeOrder(node2_accessible, false)); |
| EXPECT_EQ(nullptr, manager->PreviousInTreeOrder(root_accessible, false)); |
| |
| EXPECT_EQ(nullptr, manager->PreviousInTreeOrder(nullptr, true)); |
| EXPECT_EQ(node4_accessible, |
| manager->PreviousInTreeOrder(node5_accessible, true)); |
| EXPECT_EQ(node3_accessible, |
| manager->PreviousInTreeOrder(node4_accessible, true)); |
| EXPECT_EQ(node2_accessible, |
| manager->PreviousInTreeOrder(node3_accessible, true)); |
| EXPECT_EQ(root_accessible, |
| manager->PreviousInTreeOrder(node2_accessible, true)); |
| EXPECT_EQ(node5_accessible, |
| manager->PreviousInTreeOrder(root_accessible, true)); |
| |
| EXPECT_EQ(ax::mojom::TreeOrder::kEqual, |
| ui::BrowserAccessibilityManager::CompareNodes(*root_accessible, |
| *root_accessible)); |
| |
| EXPECT_EQ(ax::mojom::TreeOrder::kBefore, |
| ui::BrowserAccessibilityManager::CompareNodes(*node2_accessible, |
| *node3_accessible)); |
| EXPECT_EQ(ax::mojom::TreeOrder::kAfter, |
| ui::BrowserAccessibilityManager::CompareNodes(*node3_accessible, |
| *node2_accessible)); |
| |
| EXPECT_EQ(ax::mojom::TreeOrder::kBefore, |
| ui::BrowserAccessibilityManager::CompareNodes(*node2_accessible, |
| *node4_accessible)); |
| EXPECT_EQ(ax::mojom::TreeOrder::kAfter, |
| ui::BrowserAccessibilityManager::CompareNodes(*node4_accessible, |
| *node2_accessible)); |
| |
| EXPECT_EQ(ax::mojom::TreeOrder::kBefore, |
| ui::BrowserAccessibilityManager::CompareNodes(*node3_accessible, |
| *node4_accessible)); |
| EXPECT_EQ(ax::mojom::TreeOrder::kAfter, |
| ui::BrowserAccessibilityManager::CompareNodes(*node4_accessible, |
| *node3_accessible)); |
| |
| EXPECT_EQ(ax::mojom::TreeOrder::kBefore, |
| ui::BrowserAccessibilityManager::CompareNodes(*root_accessible, |
| *node2_accessible)); |
| EXPECT_EQ(ax::mojom::TreeOrder::kAfter, |
| ui::BrowserAccessibilityManager::CompareNodes(*node2_accessible, |
| *root_accessible)); |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, TestNextNonDescendantInTreeOrder) { |
| ui::TestAXTreeUpdate update(std::string(R"HTML( |
| ++1 kRootWebArea |
| ++++2 kUnknown |
| ++++3 kUnknown |
| ++++++4 kUnknown |
| ++++5 kUnknown |
| )HTML")); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| update, node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_accessible = |
| manager->GetBrowserAccessibilityRoot(); |
| ASSERT_NE(nullptr, root_accessible); |
| ASSERT_EQ(3U, root_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* node2_accessible = |
| root_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, node2_accessible); |
| ui::BrowserAccessibility* node3_accessible = |
| root_accessible->PlatformGetChild(1); |
| ASSERT_NE(nullptr, node3_accessible); |
| ASSERT_EQ(1U, node3_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* node4_accessible = |
| node3_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, node4_accessible); |
| ui::BrowserAccessibility* node5_accessible = |
| root_accessible->PlatformGetChild(2); |
| ASSERT_NE(nullptr, node5_accessible); |
| |
| EXPECT_EQ(nullptr, manager->NextNonDescendantInTreeOrder(nullptr)); |
| EXPECT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible)); |
| EXPECT_EQ(node3_accessible, |
| manager->NextNonDescendantInTreeOrder(node2_accessible)); |
| EXPECT_EQ(node5_accessible, |
| manager->NextNonDescendantInTreeOrder(node3_accessible)); |
| EXPECT_EQ(nullptr, manager->NextNonDescendantInTreeOrder(node5_accessible)); |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, TestNextPreviousTextOnlyObject) { |
| ui::TestAXTreeUpdate update(std::string(R"HTML( |
| ++1 kRootWebArea |
| ++++2 kUnknown |
| ++++3 kStaticText |
| ++++4 kUnknown |
| ++++++5 kStaticText |
| ++++++6 kUnknown |
| ++++++7 kStaticText |
| ++++8 kGenericContainer |
| ++++++9 kLineBreak |
| ++++++10 kLink |
| )HTML")); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| update, node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_accessible = |
| manager->GetBrowserAccessibilityRoot(); |
| ASSERT_NE(nullptr, root_accessible); |
| ASSERT_EQ(4U, root_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* node2_accessible = |
| root_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, node2_accessible); |
| ui::BrowserAccessibility* text1_accessible = |
| root_accessible->PlatformGetChild(1); |
| ASSERT_NE(nullptr, text1_accessible); |
| ui::BrowserAccessibility* node3_accessible = |
| root_accessible->PlatformGetChild(2); |
| ASSERT_NE(nullptr, node3_accessible); |
| ASSERT_EQ(3U, node3_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* text2_accessible = |
| node3_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, text2_accessible); |
| ui::BrowserAccessibility* node4_accessible = |
| node3_accessible->PlatformGetChild(1); |
| ASSERT_NE(nullptr, node4_accessible); |
| ui::BrowserAccessibility* text3_accessible = |
| node3_accessible->PlatformGetChild(2); |
| ASSERT_NE(nullptr, text3_accessible); |
| ui::BrowserAccessibility* node5_accessible = |
| root_accessible->PlatformGetChild(3); |
| ASSERT_NE(nullptr, node5_accessible); |
| ASSERT_EQ(2U, node5_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* text4_accessible = |
| node5_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, text4_accessible); |
| |
| EXPECT_EQ(nullptr, manager->NextTextOnlyObject(nullptr)); |
| EXPECT_EQ(text1_accessible, manager->NextTextOnlyObject(root_accessible)); |
| EXPECT_EQ(text1_accessible, manager->NextTextOnlyObject(node2_accessible)); |
| EXPECT_EQ(text2_accessible, manager->NextTextOnlyObject(text1_accessible)); |
| EXPECT_EQ(text2_accessible, manager->NextTextOnlyObject(node3_accessible)); |
| EXPECT_EQ(text3_accessible, manager->NextTextOnlyObject(text2_accessible)); |
| EXPECT_EQ(text3_accessible, manager->NextTextOnlyObject(node4_accessible)); |
| EXPECT_EQ(text4_accessible, manager->NextTextOnlyObject(text3_accessible)); |
| EXPECT_EQ(text4_accessible, manager->NextTextOnlyObject(node5_accessible)); |
| EXPECT_EQ(nullptr, manager->NextTextOnlyObject(text4_accessible)); |
| |
| EXPECT_EQ(nullptr, manager->PreviousTextOnlyObject(nullptr)); |
| EXPECT_EQ(text3_accessible, |
| manager->PreviousTextOnlyObject(text4_accessible)); |
| EXPECT_EQ(text3_accessible, |
| manager->PreviousTextOnlyObject(node5_accessible)); |
| EXPECT_EQ(text2_accessible, |
| manager->PreviousTextOnlyObject(text3_accessible)); |
| EXPECT_EQ(text2_accessible, |
| manager->PreviousTextOnlyObject(node4_accessible)); |
| EXPECT_EQ(text1_accessible, |
| manager->PreviousTextOnlyObject(text2_accessible)); |
| EXPECT_EQ(text1_accessible, |
| manager->PreviousTextOnlyObject(node3_accessible)); |
| EXPECT_EQ(nullptr, manager->PreviousTextOnlyObject(node2_accessible)); |
| EXPECT_EQ(nullptr, manager->PreviousTextOnlyObject(root_accessible)); |
| } |
| |
| // This test depends on hypertext, which is only used on |
| // Linux and Windows. |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| TEST_F(BrowserAccessibilityManagerTest, TestFindIndicesInCommonParent) { |
| ui::TestAXTreeUpdate update(std::string(R"HTML( |
| ++1 kRootWebArea |
| ++++2 kGenericContainer |
| ++++++3 kButton |
| ++++++++4 kStaticText name="Button" |
| ++++++5 kLineBreak name="\n" |
| ++++6 kParagraph |
| ++++++7 kStaticText |
| ++++++++8 kInlineTextBox name="Hello" |
| ++++++++9 kInlineTextBox name="world." |
| )HTML")); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| update, node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_accessible = |
| manager->GetBrowserAccessibilityRoot(); |
| ASSERT_NE(nullptr, root_accessible); |
| ASSERT_EQ(2U, root_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* div_accessible = |
| root_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, div_accessible); |
| ASSERT_EQ(2U, div_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* button_accessible = |
| div_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, button_accessible); |
| ASSERT_EQ(0U, button_accessible->PlatformChildCount()); |
| ASSERT_EQ(1U, button_accessible->InternalChildCount()); |
| |
| ui::BrowserAccessibility* button_text_accessible = |
| button_accessible->InternalGetChild(0); |
| ASSERT_NE(nullptr, button_text_accessible); |
| ui::BrowserAccessibility* line_break_accessible = |
| div_accessible->PlatformGetChild(1); |
| ASSERT_NE(nullptr, line_break_accessible); |
| ui::BrowserAccessibility* paragraph_accessible = |
| root_accessible->PlatformGetChild(1); |
| ASSERT_NE(nullptr, paragraph_accessible); |
| ui::BrowserAccessibility* paragraph_text_accessible = |
| paragraph_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, paragraph_text_accessible); |
| ASSERT_EQ(2U, paragraph_text_accessible->InternalChildCount()); |
| ui::BrowserAccessibility* paragraph_line1_accessible = |
| paragraph_text_accessible->InternalGetChild(0); |
| ASSERT_NE(nullptr, paragraph_line1_accessible); |
| ui::BrowserAccessibility* paragraph_line2_accessible = |
| paragraph_text_accessible->InternalGetChild(1); |
| ASSERT_NE(nullptr, paragraph_line2_accessible); |
| |
| ui::BrowserAccessibility* common_parent = nullptr; |
| size_t child_index1, child_index2; |
| EXPECT_FALSE(ui::BrowserAccessibilityManager::FindIndicesInCommonParent( |
| *root_accessible, *root_accessible, &common_parent, &child_index1, |
| &child_index2)); |
| |
| EXPECT_TRUE(ui::BrowserAccessibilityManager::FindIndicesInCommonParent( |
| *div_accessible, *paragraph_accessible, &common_parent, &child_index1, |
| &child_index2)); |
| EXPECT_EQ(root_accessible, common_parent); |
| EXPECT_EQ(0u, child_index1); |
| EXPECT_EQ(1u, child_index2); |
| |
| EXPECT_TRUE(ui::BrowserAccessibilityManager::FindIndicesInCommonParent( |
| *div_accessible, *paragraph_line1_accessible, &common_parent, |
| &child_index1, &child_index2)); |
| EXPECT_EQ(root_accessible, common_parent); |
| EXPECT_EQ(0u, child_index1); |
| EXPECT_EQ(1u, child_index2); |
| |
| EXPECT_TRUE(ui::BrowserAccessibilityManager::FindIndicesInCommonParent( |
| *line_break_accessible, *paragraph_text_accessible, &common_parent, |
| &child_index1, &child_index2)); |
| EXPECT_EQ(root_accessible, common_parent); |
| EXPECT_EQ(0u, child_index1); |
| EXPECT_EQ(1u, child_index2); |
| |
| EXPECT_TRUE(ui::BrowserAccessibilityManager::FindIndicesInCommonParent( |
| *button_text_accessible, *line_break_accessible, &common_parent, |
| &child_index1, &child_index2)); |
| EXPECT_EQ(div_accessible, common_parent); |
| EXPECT_EQ(0u, child_index1); |
| EXPECT_EQ(1u, child_index2); |
| |
| EXPECT_TRUE(ui::BrowserAccessibilityManager::FindIndicesInCommonParent( |
| *paragraph_accessible, *paragraph_line2_accessible, &common_parent, |
| &child_index1, &child_index2)); |
| EXPECT_EQ(root_accessible, common_parent); |
| EXPECT_EQ(1u, child_index1); |
| EXPECT_EQ(1u, child_index2); |
| |
| EXPECT_TRUE(ui::BrowserAccessibilityManager::FindIndicesInCommonParent( |
| *paragraph_text_accessible, *paragraph_line1_accessible, &common_parent, |
| &child_index1, &child_index2)); |
| EXPECT_EQ(paragraph_accessible, common_parent); |
| EXPECT_EQ(0u, child_index1); |
| EXPECT_EQ(0u, child_index2); |
| |
| EXPECT_TRUE(ui::BrowserAccessibilityManager::FindIndicesInCommonParent( |
| *paragraph_line1_accessible, *paragraph_line2_accessible, &common_parent, |
| &child_index1, &child_index2)); |
| EXPECT_EQ(paragraph_text_accessible, common_parent); |
| EXPECT_EQ(0u, child_index1); |
| EXPECT_EQ(1u, child_index2); |
| } |
| #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| |
| // This test depends on hypertext, which is only used on |
| // Linux and Windows. |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| TEST_F(BrowserAccessibilityManagerTest, TestGetTextForRange) { |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| |
| ui::AXNodeData div; |
| div.id = 2; |
| div.role = ax::mojom::Role::kGenericContainer; |
| root.child_ids.push_back(div.id); |
| |
| ui::AXNodeData button; |
| button.id = 3; |
| button.role = ax::mojom::Role::kButton; |
| div.child_ids.push_back(button.id); |
| |
| ui::AXNodeData button_text; |
| button_text.id = 4; |
| button_text.role = ax::mojom::Role::kStaticText; |
| button_text.SetName("Button"); |
| button.child_ids.push_back(button_text.id); |
| |
| ui::AXNodeData container; |
| container.id = 5; |
| container.role = ax::mojom::Role::kGenericContainer; |
| div.child_ids.push_back(container.id); |
| |
| ui::AXNodeData container_text; |
| container_text.id = 6; |
| container_text.role = ax::mojom::Role::kStaticText; |
| container_text.SetName("Text"); |
| container.child_ids.push_back(container_text.id); |
| |
| ui::AXNodeData line_break; |
| line_break.id = 7; |
| line_break.role = ax::mojom::Role::kLineBreak; |
| line_break.SetName("\n"); |
| div.child_ids.push_back(line_break.id); |
| |
| ui::AXNodeData paragraph; |
| paragraph.id = 8; |
| paragraph.role = ax::mojom::Role::kParagraph; |
| root.child_ids.push_back(paragraph.id); |
| |
| ui::AXNodeData paragraph_text; |
| paragraph_text.id = 9; |
| paragraph_text.role = ax::mojom::Role::kStaticText; |
| paragraph_text.SetName("Hello world."); |
| paragraph.child_ids.push_back(paragraph_text.id); |
| |
| ui::AXNodeData paragraph_line1; |
| paragraph_line1.id = 10; |
| paragraph_line1.role = ax::mojom::Role::kInlineTextBox; |
| paragraph_line1.SetName("Hello "); |
| paragraph_text.child_ids.push_back(paragraph_line1.id); |
| |
| ui::AXNodeData paragraph_line2; |
| paragraph_line2.id = 11; |
| paragraph_line2.role = ax::mojom::Role::kInlineTextBox; |
| paragraph_line2.SetName("world."); |
| paragraph_text.child_ids.push_back(paragraph_line2.id); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| MakeAXTreeUpdateForTesting(root, div, button, button_text, container, |
| container_text, line_break, paragraph, |
| paragraph_text, paragraph_line1, |
| paragraph_line2), |
| node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| ui::BrowserAccessibility* root_accessible = |
| manager->GetBrowserAccessibilityRoot(); |
| ASSERT_NE(nullptr, root_accessible); |
| ASSERT_EQ(2U, root_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* div_accessible = |
| root_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, div_accessible); |
| ASSERT_EQ(3U, div_accessible->PlatformChildCount()); |
| ui::BrowserAccessibility* button_accessible = |
| div_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, button_accessible); |
| ASSERT_EQ(0U, button_accessible->PlatformChildCount()); |
| ASSERT_EQ(1U, button_accessible->InternalChildCount()); |
| |
| ui::BrowserAccessibility* button_text_accessible = |
| button_accessible->InternalGetChild(0); |
| ASSERT_NE(nullptr, button_text_accessible); |
| ui::BrowserAccessibility* container_accessible = |
| div_accessible->PlatformGetChild(1); |
| ASSERT_NE(nullptr, container_accessible); |
| ui::BrowserAccessibility* container_text_accessible = |
| container_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, container_text_accessible); |
| ui::BrowserAccessibility* line_break_accessible = |
| div_accessible->PlatformGetChild(2); |
| ASSERT_NE(nullptr, line_break_accessible); |
| ui::BrowserAccessibility* paragraph_accessible = |
| root_accessible->PlatformGetChild(1); |
| ASSERT_NE(nullptr, paragraph_accessible); |
| ui::BrowserAccessibility* paragraph_text_accessible = |
| paragraph_accessible->PlatformGetChild(0); |
| ASSERT_NE(nullptr, paragraph_text_accessible); |
| ASSERT_EQ(2U, paragraph_text_accessible->InternalChildCount()); |
| ui::BrowserAccessibility* paragraph_line1_accessible = |
| paragraph_text_accessible->InternalGetChild(0); |
| ASSERT_NE(nullptr, paragraph_line1_accessible); |
| ui::BrowserAccessibility* paragraph_line2_accessible = |
| paragraph_text_accessible->InternalGetChild(1); |
| ASSERT_NE(nullptr, paragraph_line2_accessible); |
| |
| std::vector<const ui::BrowserAccessibility*> text_only_objects = |
| ui::BrowserAccessibilityManager::FindTextOnlyObjectsInRange( |
| *root_accessible, *root_accessible); |
| |
| EXPECT_EQ(3U, text_only_objects.size()); |
| EXPECT_EQ(container_text_accessible, text_only_objects[0]); |
| EXPECT_EQ(line_break_accessible, text_only_objects[1]); |
| EXPECT_EQ(paragraph_text_accessible, text_only_objects[2]); |
| |
| text_only_objects = |
| ui::BrowserAccessibilityManager::FindTextOnlyObjectsInRange( |
| *div_accessible, *paragraph_accessible); |
| EXPECT_EQ(3U, text_only_objects.size()); |
| EXPECT_EQ(container_text_accessible, text_only_objects[0]); |
| EXPECT_EQ(line_break_accessible, text_only_objects[1]); |
| EXPECT_EQ(paragraph_text_accessible, text_only_objects[2]); |
| |
| EXPECT_EQ(u"Text\nHello world.", |
| ui::BrowserAccessibilityManager::GetTextForRange( |
| *root_accessible, 0, *root_accessible, 16)); |
| EXPECT_EQ(u"xt\nHello world.", |
| ui::BrowserAccessibilityManager::GetTextForRange( |
| *root_accessible, 2, *root_accessible, 12)); |
| EXPECT_EQ(u"Text\nHello world.", |
| ui::BrowserAccessibilityManager::GetTextForRange( |
| *div_accessible, 0, *paragraph_accessible, 12)); |
| EXPECT_EQ(u"xt\nHello world.", |
| ui::BrowserAccessibilityManager::GetTextForRange( |
| *div_accessible, 2, *paragraph_accessible, 12)); |
| EXPECT_EQ(u"Text\n", ui::BrowserAccessibilityManager::GetTextForRange( |
| *div_accessible, 0, *div_accessible, 4)); |
| EXPECT_EQ(u"Text\n", ui::BrowserAccessibilityManager::GetTextForRange( |
| *button_accessible, 0, *line_break_accessible, 4)); |
| |
| EXPECT_EQ(u"Hello world.", |
| ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_accessible, 0, *paragraph_accessible, 12)); |
| EXPECT_EQ(u"Hello wor", |
| ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_accessible, 0, *paragraph_accessible, 9)); |
| EXPECT_EQ(u"Hello world.", |
| ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_text_accessible, 0, *paragraph_text_accessible, 12)); |
| EXPECT_EQ(u" world.", |
| ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_text_accessible, 5, *paragraph_text_accessible, 12)); |
| EXPECT_EQ(u"Hello world.", |
| ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_accessible, 0, *paragraph_text_accessible, 12)); |
| EXPECT_EQ(u"Hello ", ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_line1_accessible, 0, |
| *paragraph_line1_accessible, 6)); |
| EXPECT_EQ(u"Hello", ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_line1_accessible, 0, |
| *paragraph_line1_accessible, 5)); |
| EXPECT_EQ(u"ello ", ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_line1_accessible, 1, |
| *paragraph_line1_accessible, 6)); |
| EXPECT_EQ(u"world.", ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_line2_accessible, 0, |
| *paragraph_line2_accessible, 6)); |
| EXPECT_EQ(u"orld", ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_line2_accessible, 1, |
| *paragraph_line2_accessible, 5)); |
| EXPECT_EQ(u"Hello world.", ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_line1_accessible, 0, |
| *paragraph_line2_accessible, 6)); |
| // Start and end positions could be reversed. |
| EXPECT_EQ(u"Hello world.", ui::BrowserAccessibilityManager::GetTextForRange( |
| *paragraph_line2_accessible, 6, |
| *paragraph_line1_accessible, 0)); |
| } |
| #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(USE_ATK) |
| |
| TEST_F(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash) { |
| // Create a really simple tree with one root node and one focused child. |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.child_ids.push_back(2); |
| |
| ui::AXNodeData node2; |
| node2.id = 2; |
| |
| ui::AXTreeUpdate initial_state = MakeAXTreeUpdateForTesting(root, node2); |
| initial_state.has_tree_data = true; |
| initial_state.tree_data.focus_id = 2; |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| initial_state, node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| |
| EXPECT_EQ(1, manager->GetBrowserAccessibilityRoot()->GetId()); |
| ASSERT_NE(nullptr, manager->GetFocus()); |
| EXPECT_EQ(2, manager->GetFocus()->GetId()); |
| |
| // Now replace the tree with a new tree consisting of a single root. |
| ui::AXNodeData root2; |
| root2.id = 3; |
| root2.role = ax::mojom::Role::kRootWebArea; |
| |
| ui::AXTreeUpdate update2 = MakeAXTreeUpdateForTesting(root2); |
| update2.tree_data.tree_id = initial_state.tree_data.tree_id; |
| update2.node_id_to_clear = root.id; |
| update2.root_id = root2.id; |
| ui::AXUpdatesAndEvents events2; |
| events2.updates = {update2}; |
| ASSERT_TRUE(manager->OnAccessibilityEvents(events2)); |
| |
| // Make sure that the focused node was updated to the new root and |
| // that this doesn't crash. |
| EXPECT_EQ(3, manager->GetBrowserAccessibilityRoot()->GetId()); |
| ASSERT_NE(nullptr, manager->GetFocus()); |
| EXPECT_EQ(3, manager->GetFocus()->GetId()); |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash2) { |
| // Create a really simple tree with one root node and one focused child. |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.child_ids.push_back(2); |
| root.child_ids.push_back(3); |
| root.child_ids.push_back(4); |
| |
| ui::AXNodeData node2; |
| node2.id = 2; |
| |
| ui::AXNodeData node3; |
| node3.id = 3; |
| |
| ui::AXNodeData node4; |
| node4.id = 4; |
| |
| ui::AXTreeUpdate initial_state = |
| MakeAXTreeUpdateForTesting(root, node2, node3, node4); |
| initial_state.has_tree_data = true; |
| initial_state.tree_data.focus_id = 2; |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| initial_state, node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| |
| EXPECT_EQ(1, manager->GetBrowserAccessibilityRoot()->GetId()); |
| ASSERT_NE(nullptr, manager->GetFocus()); |
| EXPECT_EQ(2, manager->GetFocus()->GetId()); |
| |
| // Now replace the tree with a new tree consisting of a single root. |
| ui::AXNodeData root2; |
| root2.id = 3; |
| root2.role = ax::mojom::Role::kRootWebArea; |
| |
| // Make an update that explicitly clears the previous root. |
| ui::AXTreeUpdate update2 = MakeAXTreeUpdateForTesting(root2); |
| update2.tree_data.tree_id = initial_state.tree_data.tree_id; |
| update2.node_id_to_clear = root.id; |
| update2.root_id = root2.id; |
| ui::AXUpdatesAndEvents events2; |
| events2.updates = {update2}; |
| ASSERT_TRUE(manager->OnAccessibilityEvents(events2)); |
| |
| // Make sure that the focused node was updated to the new root and |
| // that this doesn't crash. |
| EXPECT_EQ(3, manager->GetBrowserAccessibilityRoot()->GetId()); |
| ASSERT_NE(nullptr, manager->GetFocus()); |
| EXPECT_EQ(3, manager->GetFocus()->GetId()); |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, TreeUpdatesAreMergedWhenPossible) { |
| ui::AXTreeUpdate tree; |
| tree.root_id = 1; |
| tree.nodes.resize(4); |
| tree.nodes[0].id = 1; |
| tree.nodes[0].role = ax::mojom::Role::kMenu; |
| tree.nodes[0].child_ids = {2, 3, 4}; |
| tree.nodes[1].id = 2; |
| tree.nodes[1].role = ax::mojom::Role::kMenuItem; |
| tree.nodes[2].id = 3; |
| tree.nodes[2].role = ax::mojom::Role::kMenuItemCheckBox; |
| tree.nodes[3].id = 4; |
| tree.nodes[3].role = ax::mojom::Role::kMenuItemRadio; |
| |
| CountingAXTreeObserver observer; |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| tree, node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| base::ScopedObservation<ui::AXTree, ui::AXTreeObserver> observation( |
| &observer); |
| observation.Observe(manager->ax_tree()); |
| |
| // Update each of the children using separate AXTreeUpdates. |
| ui::AXUpdatesAndEvents events; |
| events.updates.resize(3); |
| for (int i = 0; i < 3; i++) { |
| ui::AXTreeUpdate update; |
| update.root_id = 1; |
| update.nodes.resize(1); |
| update.nodes[0].id = 2 + i; |
| events.updates[i] = update; |
| } |
| events.updates[0].nodes[0].role = ax::mojom::Role::kMenuItemCheckBox; |
| events.updates[1].nodes[0].role = ax::mojom::Role::kMenuItemRadio; |
| events.updates[2].nodes[0].role = ax::mojom::Role::kMenuItem; |
| ASSERT_TRUE(manager->OnAccessibilityEvents(events)); |
| |
| // These should have been merged into a single tree update. |
| EXPECT_EQ(1, observer.update_count()); |
| |
| EXPECT_EQ(ax::mojom::Role::kMenuItemCheckBox, |
| manager->GetFromID(2)->GetRole()); |
| EXPECT_EQ(ax::mojom::Role::kMenuItemRadio, manager->GetFromID(3)->GetRole()); |
| EXPECT_EQ(ax::mojom::Role::kMenuItem, manager->GetFromID(4)->GetRole()); |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, TestHitTestScaled) { |
| // We're creating two linked trees, each of which has two nodes; first create |
| // the child tree's nodes & tree-update. |
| ui::AXNodeData child_root; |
| child_root.id = 1; |
| child_root.role = ax::mojom::Role::kRootWebArea; |
| child_root.SetName("child_root"); |
| child_root.relative_bounds.bounds = gfx::RectF(0, 0, 100, 100); |
| child_root.child_ids = {2}; |
| |
| ui::AXNodeData child_child; |
| child_child.id = 2; |
| child_child.role = ax::mojom::Role::kGenericContainer; |
| child_child.SetName("child_child"); |
| child_child.relative_bounds.bounds = gfx::RectF(0, 0, 100, 100); |
| |
| ui::AXTreeUpdate child_update = |
| MakeAXTreeUpdateForTesting(child_root, child_child); |
| |
| // Next, create the parent tree's nodes and tree-update, with kChildTreeId |
| // pointing to the child tree. |
| ui::AXNodeData parent_root; |
| parent_root.id = 1; |
| parent_root.role = ax::mojom::Role::kRootWebArea; |
| parent_root.SetName("parent_root"); |
| parent_root.relative_bounds.bounds = gfx::RectF(0, 0, 200, 200); |
| parent_root.child_ids = {2, 3}; |
| |
| ui::AXNodeData parent_child; |
| parent_child.id = 2; |
| parent_child.role = ax::mojom::Role::kGenericContainer; |
| parent_child.SetName("parent_child"); |
| parent_child.relative_bounds.bounds = gfx::RectF(0, 0, 100, 100); |
| |
| ui::AXNodeData parent_childtree; |
| parent_childtree.id = 3; |
| parent_childtree.AddChildTreeId(child_update.tree_data.tree_id); |
| parent_childtree.role = ax::mojom::Role::kGenericContainer; |
| parent_childtree.SetName("parent_childtree"); |
| parent_childtree.relative_bounds.bounds = gfx::RectF(100, 100, 100, 100); |
| |
| ui::AXTreeUpdate parent_update = |
| MakeAXTreeUpdateForTesting(parent_root, parent_child, parent_childtree); |
| |
| // Link the child trees to their parent trees. |
| child_update.tree_data.parent_tree_id = parent_update.tree_data.tree_id; |
| |
| // Create the two managers. |
| std::unique_ptr<ui::BrowserAccessibilityManager> parent_manager( |
| CreateBrowserAccessibilityManager(parent_update, node_id_delegate_, |
| nullptr)); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> child_manager( |
| CreateBrowserAccessibilityManager(child_update, node_id_delegate_, |
| nullptr)); |
| |
| ASSERT_EQ(parent_manager.get(), child_manager->GetManagerForRootFrame()); |
| |
| // Set scaling factor for testing to be 200% |
| parent_manager->UseCustomDeviceScaleFactorForTesting(2.0f); |
| child_manager->UseCustomDeviceScaleFactorForTesting(2.0f); |
| |
| // Run the hit-test; we should get the same result regardless of whether we |
| // start from the parent_manager or the child_manager. |
| auto* hittest1 = parent_manager->CachingAsyncHitTest(gfx::Point(75, 75)); |
| ASSERT_NE(nullptr, hittest1); |
| ASSERT_EQ("parent_child", |
| hittest1->GetStringAttribute(ax::mojom::StringAttribute::kName)); |
| |
| auto* hittest2 = child_manager->CachingAsyncHitTest(gfx::Point(75, 75)); |
| ASSERT_NE(nullptr, hittest2); |
| ASSERT_EQ("parent_child", |
| hittest2->GetStringAttribute(ax::mojom::StringAttribute::kName)); |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, TestShouldFireEventForNode) { |
| ui::TestAXTreeUpdate update(std::string(R"HTML( |
| ++1 kRootWebArea |
| ++++11 kParagraph |
| ++++++111 kStaticText |
| ++++++++1111 kInlineTextBox |
| )HTML")); |
| |
| update.nodes[2].SetName("One two three."); |
| update.nodes[3].SetName("One two three."); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| update, node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| |
| EXPECT_TRUE(manager->ShouldFireEventForNode(manager->GetFromID(1))); |
| EXPECT_TRUE(manager->ShouldFireEventForNode(manager->GetFromID(11))); |
| EXPECT_TRUE(manager->ShouldFireEventForNode(manager->GetFromID(111))); |
| #if BUILDFLAG(IS_ANDROID) |
| // On Android, ShouldFireEventForNode walks up the ancestor that's a leaf node |
| // node and the event is fired on the updated target. |
| EXPECT_TRUE(manager->ShouldFireEventForNode(manager->GetFromID(1111))); |
| #else |
| EXPECT_FALSE(manager->ShouldFireEventForNode(manager->GetFromID(1111))); |
| #endif |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, NestedChildRoot) { |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| |
| ui::AXNodeData popup_button; |
| popup_button.id = 2; |
| popup_button.role = ax::mojom::Role::kPopUpButton; |
| root.child_ids.push_back(2); |
| |
| ui::AXNodeData child_tree_root; |
| child_tree_root.id = 3; |
| child_tree_root.role = ax::mojom::Role::kGroup; |
| child_tree_root.AddIntAttribute(ax::mojom::IntAttribute::kPopupForId, 2); |
| popup_button.child_ids.push_back(3); |
| |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| MakeAXTreeUpdateForTesting(root, popup_button, child_tree_root), |
| node_id_delegate_, test_browser_accessibility_delegate_.get())); |
| |
| ASSERT_NE(manager->GetPopupRoot(), nullptr); |
| EXPECT_EQ(manager->GetPopupRoot()->GetId(), 3); |
| |
| // Test deleting child root. |
| |
| // Now remove the child root from the tree. |
| popup_button.child_ids = {}; |
| ui::AXTreeUpdate update = MakeAXTreeUpdateForTesting(popup_button); |
| update.tree_data.tree_id = manager->GetTreeID(); |
| ui::AXUpdatesAndEvents events; |
| events.updates = {update}; |
| ASSERT_TRUE(manager->OnAccessibilityEvents(events)); |
| |
| EXPECT_EQ(manager->GetPopupRoot(), nullptr); |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, TestApproximateHitTestCache) { |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| root.SetName("root"); |
| root.relative_bounds.bounds = gfx::RectF(0, 0, 200, 200); |
| root.child_ids = {2, 3}; |
| |
| ui::AXNodeData child1; |
| child1.id = 2; |
| child1.role = ax::mojom::Role::kGenericContainer; |
| child1.SetName("child1"); |
| child1.relative_bounds.bounds = gfx::RectF(0, 0, 100, 100); |
| |
| ui::AXNodeData child2; |
| child2.id = 3; |
| child2.role = ax::mojom::Role::kGenericContainer; |
| child2.SetName("child2"); |
| child2.relative_bounds.bounds = gfx::RectF(50, 50, 50, 50); |
| |
| ui::AXTreeUpdate update = MakeAXTreeUpdateForTesting(root, child1, child2); |
| |
| // Create manager. |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| update, node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| manager->BuildAXTreeHitTestCache(); |
| |
| auto* hittest1 = manager->ApproximateHitTest(gfx::Point(1, 1)); |
| ASSERT_NE(nullptr, hittest1); |
| ASSERT_EQ("child1", |
| hittest1->GetStringAttribute(ax::mojom::StringAttribute::kName)); |
| |
| auto* hittest2 = manager->CachingAsyncHitTest(gfx::Point(75, 75)); |
| ASSERT_NE(nullptr, hittest2); |
| ASSERT_EQ("child2", |
| hittest2->GetStringAttribute(ax::mojom::StringAttribute::kName)); |
| } |
| |
| TEST_F(BrowserAccessibilityManagerTest, TestOnNodeReparented) { |
| ui::AXNodeData root; |
| root.id = 1; |
| root.role = ax::mojom::Role::kRootWebArea; |
| |
| ui::AXNodeData child1; |
| child1.role = ax::mojom::Role::kGenericContainer; |
| child1.id = 2; |
| |
| ui::AXNodeData child2; |
| child2.role = ax::mojom::Role::kGenericContainer; |
| child2.id = 3; |
| |
| root.child_ids = {child1.id, child2.id}; |
| |
| const ui::AXTreeUpdate update1 = |
| MakeAXTreeUpdateForTesting(root, child1, child2); |
| CountingAXTreeObserver observer; |
| std::unique_ptr<ui::BrowserAccessibilityManager> manager( |
| CreateBrowserAccessibilityManager( |
| update1, node_id_delegate_, |
| test_browser_accessibility_delegate_.get())); |
| base::ScopedObservation<ui::AXTree, ui::AXTreeObserver> observation( |
| &observer); |
| observation.Observe(manager->ax_tree()); |
| |
| ASSERT_EQ(0, observer.reparent_count()); |
| ASSERT_EQ(0, observer.node_count()); |
| |
| // Reparenting a child found in the tree should not crash. |
| root.child_ids = {child1.id}; |
| child1.child_ids = {child2.id}; |
| ui::AXTreeUpdate update2 = MakeAXTreeUpdateForTesting(root, child1, child2); |
| update2.tree_data.tree_id = update1.tree_data.tree_id; |
| manager->ax_tree()->Unserialize(update2); |
| EXPECT_EQ(1, observer.reparent_count()); |
| EXPECT_EQ(3, observer.node_count()); |
| |
| // Reparenting a new child that is not found in the tree should trigger a |
| // DCHECK in AX_FAIL_FAST_BUILD builds, otherwise it should not crash. |
| ui::AXNode child3(manager->ax_tree(), /* parent */ nullptr, /* id */ 4, |
| /* index_in_parent */ 0u); |
| #if AX_FAIL_FAST_BUILD() |
| EXPECT_DEATH_IF_SUPPORTED( |
| manager->OnNodeReparented(manager->ax_tree(), &child3), |
| "Missing BrowserAccessibility"); |
| #else |
| manager->OnNodeReparented(manager->ax_tree(), &child3); |
| #endif |
| // We avoid checking the observer on purpose, since reparenting a non-existent |
| // node should not trigger any tree observers. The node is not in the tree, |
| // hence the normal tree update process cannot be followed. |
| } |
| |
| } // namespace content |