910e62b5创建于 1月15日历史提交
// Copyright 2015 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/one_shot_accessibility_tree_search.h"

#include <memory>

#include "build/build_config.h"
#include "ui/accessibility/platform/browser_accessibility.h"
#include "ui/accessibility/platform/browser_accessibility_manager.h"
#if BUILDFLAG(IS_ANDROID)
#include "content/browser/accessibility/browser_accessibility_manager_android.h"
#elif OS_FUCHSIA
#include "ui/accessibility/platform/fuchsia/browser_accessibility_manager_fuchsia.h"
#endif
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/platform/test_ax_node_id_delegate.h"

namespace content {

namespace {

#if BUILDFLAG(IS_ANDROID)
class TestBrowserAccessibilityManager
    : public BrowserAccessibilityManagerAndroid {
 public:
  explicit TestBrowserAccessibilityManager(
      const ui::AXTreeUpdate& initial_tree,
      ui::AXNodeIdDelegate& node_id_delegate)
      : BrowserAccessibilityManagerAndroid(initial_tree,
                                           nullptr,
                                           node_id_delegate,
                                           nullptr) {}
};
#elif OS_FUCHSIA
class TestBrowserAccessibilityManager
    : public ui::BrowserAccessibilityManagerFuchsia {
 public:
  explicit TestBrowserAccessibilityManager(
      const ui::AXTreeUpdate& initial_tree,
      ui::AXNodeIdDelegate& node_id_delegate)
      : BrowserAccessibilityManagerFuchsia(initial_tree,
                                           node_id_delegate,
                                           nullptr) {}
};
#else
class TestBrowserAccessibilityManager : public ui::BrowserAccessibilityManager {
 public:
  explicit TestBrowserAccessibilityManager(
      const ui::AXTreeUpdate& initial_tree,
      ui::AXNodeIdDelegate& node_id_delegate)
      : BrowserAccessibilityManager(node_id_delegate, nullptr) {
    Initialize(initial_tree);
  }
};
#endif

}  // namespace

class OneShotAccessibilityTreeSearchTest : public testing::Test {
 public:
  OneShotAccessibilityTreeSearchTest() = default;

  OneShotAccessibilityTreeSearchTest(
      const OneShotAccessibilityTreeSearchTest&) = delete;
  OneShotAccessibilityTreeSearchTest& operator=(
      const OneShotAccessibilityTreeSearchTest&) = delete;

  ~OneShotAccessibilityTreeSearchTest() override = default;

 protected:
  void SetUp() override;

  BrowserTaskEnvironment task_environment_;

  ui::TestAXNodeIdDelegate node_id_delegate_;
  std::unique_ptr<ui::BrowserAccessibilityManager> tree_;
};

void OneShotAccessibilityTreeSearchTest::SetUp() {
  ui::AXNodeData root;
  root.id = 1;
  root.role = ax::mojom::Role::kRootWebArea;
  root.SetName("Document");
  root.relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
  root.AddBoolAttribute(ax::mojom::BoolAttribute::kClipsChildren, true);

  ui::AXNodeData heading;
  heading.id = 2;
  heading.role = ax::mojom::Role::kHeading;
  heading.SetName("Heading");
  heading.relative_bounds.bounds = gfx::RectF(0, 0, 800, 50);

  ui::AXNodeData table;
  table.id = 3;
  table.role = ax::mojom::Role::kTable;
  table.AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount, 1);
  table.AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount, 2);

  ui::AXNodeData table_row;
  table_row.id = 4;
  table_row.role = ax::mojom::Role::kRow;
  table_row.AddIntAttribute(ax::mojom::IntAttribute::kTableRowIndex, 0);

  ui::AXNodeData table_column_header_1;
  table_column_header_1.id = 5;
  table_column_header_1.role = ax::mojom::Role::kColumnHeader;
  table_column_header_1.SetName("Cell1");
  table_column_header_1.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellRowIndex, 0);
  table_column_header_1.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellColumnIndex, 0);

  ui::AXNodeData table_column_header_2;
  table_column_header_2.id = 6;
  table_column_header_2.role = ax::mojom::Role::kColumnHeader;
  table_column_header_2.SetName("Cell2");
  table_column_header_2.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellRowIndex, 0);
  table_column_header_2.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellColumnIndex, 1);

  ui::AXNodeData list;
  list.id = 7;
  list.role = ax::mojom::Role::kList;
  list.relative_bounds.bounds = gfx::RectF(0, 50, 500, 500);

  ui::AXNodeData list_item_1;
  list_item_1.id = 8;
  list_item_1.role = ax::mojom::Role::kListItem;
  list_item_1.SetName("Autobots");
  list_item_1.relative_bounds.bounds = gfx::RectF(10, 10, 200, 30);

  ui::AXNodeData list_item_2;
  list_item_2.id = 9;
  list_item_2.role = ax::mojom::Role::kListItem;
  list_item_2.SetName("Decepticons");
  list_item_2.relative_bounds.bounds = gfx::RectF(10, 40, 200, 60);

  ui::AXNodeData footer;
  footer.id = 10;
  footer.role = ax::mojom::Role::kFooter;
  footer.SetName("Footer");
  footer.relative_bounds.bounds = gfx::RectF(0, 650, 100, 800);

  table_row.child_ids = {table_column_header_1.id, table_column_header_2.id};
  table.child_ids = {table_row.id};
  list.child_ids = {list_item_1.id, list_item_2.id};
  root.child_ids = {heading.id, table.id, list.id, footer.id};

  tree_ = std::make_unique<TestBrowserAccessibilityManager>(
      MakeAXTreeUpdateForTesting(root, heading, table, table_row,
                                 table_column_header_1, table_column_header_2,
                                 list, list_item_1, list_item_2, footer),
      node_id_delegate_);
}

TEST_F(OneShotAccessibilityTreeSearchTest, GetAll) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
#if BUILDFLAG(IS_MAC)
  ASSERT_EQ(13U, search.CountMatches());
#else
  ASSERT_EQ(10U, search.CountMatches());
#endif
}

TEST_F(OneShotAccessibilityTreeSearchTest, BackwardsWrapFromRoot) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.SetDirection(ui::OneShotAccessibilityTreeSearch::BACKWARDS);
  search.SetResultLimit(100);
  search.SetCanWrapToLastElement(true);
#if BUILDFLAG(IS_MAC)
  ASSERT_EQ(13U, search.CountMatches());
#else
  ASSERT_EQ(10U, search.CountMatches());
#endif
  EXPECT_EQ(1, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(10, search.GetMatchAtIndex(1)->GetId());
  EXPECT_EQ(9, search.GetMatchAtIndex(2)->GetId());
  EXPECT_EQ(8, search.GetMatchAtIndex(3)->GetId());
  EXPECT_EQ(7, search.GetMatchAtIndex(4)->GetId());
#if BUILDFLAG(IS_MAC)
  EXPECT_EQ(-3, search.GetMatchAtIndex(5)->GetId());
  EXPECT_EQ(-2, search.GetMatchAtIndex(6)->GetId());
  EXPECT_EQ(-1, search.GetMatchAtIndex(7)->GetId());
  EXPECT_EQ(6, search.GetMatchAtIndex(8)->GetId());
  EXPECT_EQ(5, search.GetMatchAtIndex(9)->GetId());
  EXPECT_EQ(4, search.GetMatchAtIndex(10)->GetId());
  EXPECT_EQ(3, search.GetMatchAtIndex(11)->GetId());
  EXPECT_EQ(2, search.GetMatchAtIndex(12)->GetId());
#else
  EXPECT_EQ(6, search.GetMatchAtIndex(5)->GetId());
  EXPECT_EQ(5, search.GetMatchAtIndex(6)->GetId());
  EXPECT_EQ(4, search.GetMatchAtIndex(7)->GetId());
  EXPECT_EQ(3, search.GetMatchAtIndex(8)->GetId());
  EXPECT_EQ(2, search.GetMatchAtIndex(9)->GetId());
#endif
}

TEST_F(OneShotAccessibilityTreeSearchTest, NoCycle) {
  // If you set a result limit of 1, you won't get the root node back as
  // the first match.
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.SetResultLimit(1);
  ASSERT_EQ(1U, search.CountMatches());
  EXPECT_NE(1, search.GetMatchAtIndex(0)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, ForwardsWithStartNode) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.SetStartNode(tree_->GetFromID(7));
  ASSERT_EQ(3U, search.CountMatches());
  EXPECT_EQ(8, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(9, search.GetMatchAtIndex(1)->GetId());
  EXPECT_EQ(10, search.GetMatchAtIndex(2)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, BackwardsWithStartNode) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.SetStartNode(tree_->GetFromID(4));
  search.SetDirection(ui::OneShotAccessibilityTreeSearch::BACKWARDS);
  ASSERT_EQ(3U, search.CountMatches());
  EXPECT_EQ(3, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(2, search.GetMatchAtIndex(1)->GetId());
  EXPECT_EQ(1, search.GetMatchAtIndex(2)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, BackwardsWithStartNodeForAndroid) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.SetStartNode(tree_->GetFromID(4));
  search.SetDirection(ui::OneShotAccessibilityTreeSearch::BACKWARDS);
  search.SetResultLimit(3);
  search.SetCanWrapToLastElement(true);
  ASSERT_EQ(3U, search.CountMatches());
  EXPECT_EQ(3, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(2, search.GetMatchAtIndex(1)->GetId());
  EXPECT_EQ(1, search.GetMatchAtIndex(2)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, ForwardsWithStartNodeAndScope) {
  ui::OneShotAccessibilityTreeSearch search(tree_->GetFromID(7));
  search.SetStartNode(tree_->GetFromID(8));
  ASSERT_EQ(2U, search.CountMatches());
  EXPECT_EQ(9, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(10, search.GetMatchAtIndex(1)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, ResultLimitZero) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.SetResultLimit(0);
  ASSERT_EQ(0U, search.CountMatches());
}

TEST_F(OneShotAccessibilityTreeSearchTest, ResultLimitFive) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.SetResultLimit(5);
  ASSERT_EQ(5U, search.CountMatches());
}

TEST_F(OneShotAccessibilityTreeSearchTest, DescendantsOnlyOfRoot) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.SetStartNode(tree_->GetFromID(1));
  search.SetImmediateDescendantsOnly(true);
  ASSERT_EQ(4U, search.CountMatches());
  EXPECT_EQ(2, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(3, search.GetMatchAtIndex(1)->GetId());
  EXPECT_EQ(7, search.GetMatchAtIndex(2)->GetId());
  EXPECT_EQ(10, search.GetMatchAtIndex(3)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, DescendantsOnlyOfNode) {
  ui::OneShotAccessibilityTreeSearch search(tree_->GetFromID(7));
  search.SetImmediateDescendantsOnly(true);
  ASSERT_EQ(2U, search.CountMatches());
  EXPECT_EQ(8, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(9, search.GetMatchAtIndex(1)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, DescendantsOnlyOfNodeWithStartNode) {
  ui::OneShotAccessibilityTreeSearch search(tree_->GetFromID(7));
  search.SetStartNode(tree_->GetFromID(8));
  search.SetImmediateDescendantsOnly(true);
  ASSERT_EQ(1U, search.CountMatches());
  EXPECT_EQ(9, search.GetMatchAtIndex(0)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest,
       DescendantsOnlyOfNodeWithStartNodeBackwardsTableCell) {
  ui::OneShotAccessibilityTreeSearch search(tree_->GetFromID(3));
  search.SetStartNode(tree_->GetFromID(5));
  search.SetDirection(ui::OneShotAccessibilityTreeSearch::BACKWARDS);
  search.SetImmediateDescendantsOnly(true);
  ASSERT_EQ(0U, search.CountMatches());
}

TEST_F(OneShotAccessibilityTreeSearchTest,
       DescendantsOnlyOfNodeWithStartNodeBackwardsListItem) {
  ui::OneShotAccessibilityTreeSearch search(tree_->GetFromID(7));
  search.SetStartNode(tree_->GetFromID(9));
  search.SetImmediateDescendantsOnly(true);
  search.SetDirection(ui::OneShotAccessibilityTreeSearch::BACKWARDS);
  ASSERT_EQ(1U, search.CountMatches());
  EXPECT_EQ(8, search.GetMatchAtIndex(0)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, OnscreenOnly) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.SetOnscreenOnly(true);
  ASSERT_EQ(7U, search.CountMatches());
  EXPECT_EQ(1, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(2, search.GetMatchAtIndex(1)->GetId());
  EXPECT_EQ(3, search.GetMatchAtIndex(2)->GetId());
  EXPECT_EQ(4, search.GetMatchAtIndex(3)->GetId());
  EXPECT_EQ(7, search.GetMatchAtIndex(4)->GetId());
  EXPECT_EQ(8, search.GetMatchAtIndex(5)->GetId());
  EXPECT_EQ(9, search.GetMatchAtIndex(6)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, CaseInsensitiveStringMatch) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.SetSearchText("eCEptiCOn");
  ASSERT_EQ(1U, search.CountMatches());
  EXPECT_EQ(9, search.GetMatchAtIndex(0)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, OnePredicateTableCell) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.AddPredicate(
      [](ui::BrowserAccessibility* start, ui::BrowserAccessibility* current) {
        return current->GetRole() == ax::mojom::Role::kColumnHeader;
      });
  ASSERT_EQ(2U, search.CountMatches());
  EXPECT_EQ(5, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(6, search.GetMatchAtIndex(1)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, OnePredicateListItem) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.AddPredicate(
      [](ui::BrowserAccessibility* start, ui::BrowserAccessibility* current) {
        return current->GetRole() == ax::mojom::Role::kListItem;
      });
  ASSERT_EQ(2U, search.CountMatches());
  EXPECT_EQ(8, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(9, search.GetMatchAtIndex(1)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, TwoPredicatesTableRowAndCell) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.AddPredicate(
      [](ui::BrowserAccessibility* start, ui::BrowserAccessibility* current) {
        return (current->GetRole() == ax::mojom::Role::kRow);
      });
  search.AddPredicate(
      [](ui::BrowserAccessibility* start, ui::BrowserAccessibility* current) {
        return (current->GetRole() == ax::mojom::Role::kColumnHeader);
      });
  ASSERT_EQ(3U, search.CountMatches());
  EXPECT_EQ(4, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(5, search.GetMatchAtIndex(1)->GetId());
  EXPECT_EQ(6, search.GetMatchAtIndex(2)->GetId());
}

TEST_F(OneShotAccessibilityTreeSearchTest, TwoPredicatesListItem) {
  ui::OneShotAccessibilityTreeSearch search(
      tree_->GetBrowserAccessibilityRoot());
  search.AddPredicate(
      [](ui::BrowserAccessibility* start, ui::BrowserAccessibility* current) {
        return (current->GetRole() == ax::mojom::Role::kList);
      });
  search.AddPredicate(
      [](ui::BrowserAccessibility* start, ui::BrowserAccessibility* current) {
        return (current->GetRole() == ax::mojom::Role::kListItem);
      });
  ASSERT_EQ(3U, search.CountMatches());
  EXPECT_EQ(7, search.GetMatchAtIndex(0)->GetId());
  EXPECT_EQ(8, search.GetMatchAtIndex(1)->GetId());
  EXPECT_EQ(9, search.GetMatchAtIndex(2)->GetId());
}

}  // namespace content