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

#include "ui/accessibility/ax_constants.mojom.h"
#include "ui/accessibility/platform/ax_platform_node_base.h"
#include "ui/accessibility/platform/test_ax_node_wrapper.h"
#include "ui/accessibility/test_ax_tree_update.h"
#include "ui/accessibility/test_single_ax_tree_manager.h"

namespace ui {

AXPlatformNodeTest::AXPlatformNodeTest() = default;

AXPlatformNodeTest::~AXPlatformNodeTest() = default;

void AXPlatformNodeTest::TearDown() {
#if BUILDFLAG(HAS_NATIVE_ACCESSIBILITY)
  TestAXNodeWrapper::SetGlobalIsWebContent(false);
  TestAXNodeWrapper::ResetGlobalState();
#endif  // BUILDFLAG(HAS_NATIVE_ACCESSIBILITY)

  // Destroy the tree and make sure we're not leaking any objects.
  DestroyTree();

  ASSERT_EQ(0U, AXPlatformNodeBase::ResetInstanceCountForTesting());
}

AXTreeUpdate AXPlatformNodeTest::BuildTextField() {
  AXNodeData text_field_node;
  text_field_node.id = 1;
  text_field_node.role = ax::mojom::Role::kTextField;
  text_field_node.AddState(ax::mojom::State::kEditable);
  text_field_node.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag,
                                     "input");
  text_field_node.AddStringAttribute(ax::mojom::StringAttribute::kInputType,
                                     "text");
  text_field_node.SetValue("How now brown cow.");

  AXTreeUpdate update;
  update.root_id = text_field_node.id;
  update.nodes.push_back(text_field_node);
  update.has_tree_data = true;
  // An AXPosition will be created, and requires an AXTreeID.
  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  return update;
}

AXTreeUpdate AXPlatformNodeTest::BuildTextFieldWithSelectionRange(
    int32_t start,
    int32_t stop) {
  AXNodeData text_field_node;
  text_field_node.id = 1;
  text_field_node.role = ax::mojom::Role::kTextField;
  text_field_node.AddState(ax::mojom::State::kEditable);
  text_field_node.AddStringAttribute(ax::mojom::StringAttribute::kHtmlTag,
                                     "input");
  text_field_node.AddStringAttribute(ax::mojom::StringAttribute::kInputType,
                                     "text");
  text_field_node.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
  text_field_node.AddIntAttribute(ax::mojom::IntAttribute::kTextSelStart,
                                  start);
  text_field_node.AddIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, stop);
  text_field_node.SetValue("How now brown cow.");

  AXTreeUpdate update;
  update.root_id = text_field_node.id;
  update.nodes.push_back(text_field_node);
  update.has_tree_data = true;
  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  return update;
}

AXTreeUpdate AXPlatformNodeTest::BuildContentEditable() {
  AXNodeData content_editable_node;
  content_editable_node.id = 1;
  content_editable_node.role = ax::mojom::Role::kGroup;
  content_editable_node.AddState(ax::mojom::State::kEditable);
  content_editable_node.AddState(ax::mojom::State::kRichlyEditable);
  content_editable_node.AddBoolAttribute(
      ax::mojom::BoolAttribute::kNonAtomicTextFieldRoot, true);
  content_editable_node.SetValue("How now brown cow.");

  AXTreeUpdate update;
  update.root_id = content_editable_node.id;
  update.nodes.push_back(content_editable_node);
  update.has_tree_data = true;
  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  return update;
}

AXTreeUpdate AXPlatformNodeTest::BuildContentEditableWithSelectionRange(
    int32_t start,
    int32_t end) {
  AXNodeData content_editable_node;
  content_editable_node.id = 1;
  content_editable_node.role = ax::mojom::Role::kGroup;
  content_editable_node.AddState(ax::mojom::State::kEditable);
  content_editable_node.AddState(ax::mojom::State::kRichlyEditable);
  content_editable_node.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected,
                                         true);
  content_editable_node.AddBoolAttribute(
      ax::mojom::BoolAttribute::kNonAtomicTextFieldRoot, true);
  content_editable_node.SetValue("How now brown cow.");

  AXTreeUpdate update;
  update.root_id = content_editable_node.id;
  update.nodes.push_back(content_editable_node);

  update.has_tree_data = true;
  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  update.tree_data.sel_anchor_object_id = content_editable_node.id;
  update.tree_data.sel_focus_object_id = content_editable_node.id;
  update.tree_data.sel_anchor_offset = start;
  update.tree_data.sel_focus_offset = end;

  return update;
}

AXTreeUpdate AXPlatformNodeTest::AXPlatformNodeTest::Build3X3Table() {
  /*
    Build a table that looks like:

    ----------------------        (A) Column Header
    |        | (A) | (B) |        (B) Column Header
    ----------------------        (C) Row Header
    |  (C)  |  1  |  2   |        (D) Row Header
    ----------------------
    |  (D)  |  3  |  4   |
    ----------------------
  */

  AXNodeData table;
  table.id = 1;
  table.role = ax::mojom::Role::kTable;

  table.AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount, 3);
  table.AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount, 3);

  table.child_ids.push_back(50);  // Header
  table.child_ids.push_back(2);   // Row 1
  table.child_ids.push_back(10);  // Row 2

  // Table column header
  AXNodeData table_row_header;
  table_row_header.id = 50;
  table_row_header.role = ax::mojom::Role::kRow;
  table_row_header.child_ids.push_back(51);
  table_row_header.child_ids.push_back(52);
  table_row_header.child_ids.push_back(53);

  AXNodeData table_column_header_1;
  table_column_header_1.id = 51;
  table_column_header_1.role = ax::mojom::Role::kColumnHeader;
  table_column_header_1.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellRowIndex, 0);
  table_column_header_1.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellColumnIndex, 0);

  AXNodeData table_column_header_2;
  table_column_header_2.id = 52;
  table_column_header_2.role = ax::mojom::Role::kColumnHeader;
  table_column_header_2.SetName("column header 1");
  table_column_header_2.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellRowIndex, 0);
  table_column_header_2.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellColumnIndex, 1);

  AXNodeData table_column_header_3;
  table_column_header_3.id = 53;
  table_column_header_3.role = ax::mojom::Role::kColumnHeader;
  // Either ax::mojom::StringAttribute::kName -or-
  // ax::mojom::StringAttribute::kDescription is acceptable for a description
  table_column_header_3.AddStringAttribute(
      ax::mojom::StringAttribute::kDescription, "column header 2");
  table_column_header_3.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellRowIndex, 0);
  table_column_header_3.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellColumnIndex, 2);

  // Row 1
  AXNodeData table_row_1;
  table_row_1.id = 2;
  table_row_1.role = ax::mojom::Role::kRow;

  AXNodeData table_row_header_1;
  table_row_header_1.id = 3;
  table_row_header_1.role = ax::mojom::Role::kRowHeader;
  table_row_header_1.SetName("row header 1");
  table_row_header_1.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellRowIndex, 1);
  table_row_header_1.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellColumnIndex, 0);
  table_row_1.child_ids.push_back(table_row_header_1.id);

  AXNodeData table_cell_1;
  table_cell_1.id = 4;
  table_cell_1.role = ax::mojom::Role::kCell;
  table_cell_1.SetName("1");
  table_cell_1.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 1);
  table_cell_1.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex,
                               1);
  table_row_1.child_ids.push_back(table_cell_1.id);

  AXNodeData table_cell_2;
  table_cell_2.id = 5;
  table_cell_2.role = ax::mojom::Role::kCell;
  table_cell_2.SetName("2");
  table_cell_2.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 1);
  table_cell_2.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex,
                               2);
  table_row_1.child_ids.push_back(table_cell_2.id);

  // Row 2
  AXNodeData table_row_2;
  table_row_2.id = 10;
  table_row_2.role = ax::mojom::Role::kRow;

  AXNodeData table_row_header_2;
  table_row_header_2.id = 11;
  table_row_header_2.role = ax::mojom::Role::kRowHeader;
  // Either ax::mojom::StringAttribute::kName -or-
  // ax::mojom::StringAttribute::kDescription is acceptable for a description
  table_row_header_2.AddStringAttribute(
      ax::mojom::StringAttribute::kDescription, "row header 2");
  table_row_header_2.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellRowIndex, 2);
  table_row_header_2.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellColumnIndex, 0);
  table_row_2.child_ids.push_back(table_row_header_2.id);

  AXNodeData table_cell_3;
  table_cell_3.id = 12;
  table_cell_3.role = ax::mojom::Role::kCell;
  table_cell_3.SetName("3");
  table_cell_3.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 2);
  table_cell_3.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex,
                               1);
  table_row_2.child_ids.push_back(table_cell_3.id);

  AXNodeData table_cell_4;
  table_cell_4.id = 13;
  table_cell_4.role = ax::mojom::Role::kCell;
  table_cell_4.SetName("4");
  table_cell_4.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 2);
  table_cell_4.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex,
                               2);
  table_row_2.child_ids.push_back(table_cell_4.id);

  AXTreeUpdate update;
  update.root_id = table.id;

  // Some of the table testing code will index into |nodes|
  // and change the state of the given node.  If you reorder
  // these, you're going to need to update the tests.
  update.nodes.push_back(table);  // 0

  update.nodes.push_back(table_row_header);       // 1
  update.nodes.push_back(table_column_header_1);  // 2
  update.nodes.push_back(table_column_header_2);  // 3
  update.nodes.push_back(table_column_header_3);  // 4

  update.nodes.push_back(table_row_1);         // 5
  update.nodes.push_back(table_row_header_1);  // 6
  update.nodes.push_back(table_cell_1);        // 7
  update.nodes.push_back(table_cell_2);        // 8

  update.nodes.push_back(table_row_2);         // 9
  update.nodes.push_back(table_row_header_2);  // 10
  update.nodes.push_back(table_cell_3);        // 11
  update.nodes.push_back(table_cell_4);        // 12

  update.has_tree_data = true;
  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  return update;
}

AXTreeUpdate AXPlatformNodeTest::BuildAriaColumnAndRowCountGrids() {
  AXNodeData root;
  root.id = 1;
  root.role = ax::mojom::Role::kNone;

  // Empty Grid
  AXNodeData empty_grid;
  empty_grid.id = 2;
  empty_grid.role = ax::mojom::Role::kGrid;
  root.child_ids.push_back(empty_grid.id);

  // Grid with a cell that defines aria-rowindex (4) and aria-colindex (5)
  AXNodeData rowcolindex_grid;
  rowcolindex_grid.id = 3;
  rowcolindex_grid.role = ax::mojom::Role::kGrid;
  root.child_ids.push_back(rowcolindex_grid.id);

  AXNodeData rowcolindex_row;
  rowcolindex_row.id = 4;
  rowcolindex_row.role = ax::mojom::Role::kRow;
  rowcolindex_grid.child_ids.push_back(rowcolindex_row.id);

  AXNodeData rowcolindex_cell;
  rowcolindex_cell.id = 5;
  rowcolindex_cell.role = ax::mojom::Role::kCell;
  rowcolindex_cell.AddIntAttribute(
      ax::mojom::IntAttribute::kAriaCellColumnIndex, 5);
  rowcolindex_cell.AddIntAttribute(ax::mojom::IntAttribute::kAriaCellRowIndex,
                                   4);
  rowcolindex_row.child_ids.push_back(rowcolindex_cell.id);

  // Grid that specifies aria-rowcount (2) and aria-colcount (3)
  AXNodeData rowcolcount_grid;
  rowcolcount_grid.id = 6;
  rowcolcount_grid.role = ax::mojom::Role::kGrid;
  rowcolcount_grid.AddIntAttribute(ax::mojom::IntAttribute::kAriaRowCount, 2);
  rowcolcount_grid.AddIntAttribute(ax::mojom::IntAttribute::kAriaColumnCount,
                                   3);
  root.child_ids.push_back(rowcolcount_grid.id);

  // Grid that specifies aria-rowcount and aria-colcount are (-1)
  // ax::mojom::kUnknownAriaColumnOrRowCount
  AXNodeData unknown_grid;
  unknown_grid.id = 7;
  unknown_grid.role = ax::mojom::Role::kGrid;
  unknown_grid.AddIntAttribute(ax::mojom::IntAttribute::kAriaRowCount,
                               ax::mojom::kUnknownAriaColumnOrRowCount);
  unknown_grid.AddIntAttribute(ax::mojom::IntAttribute::kAriaColumnCount,
                               ax::mojom::kUnknownAriaColumnOrRowCount);
  root.child_ids.push_back(unknown_grid.id);

  AXTreeUpdate update;
  update.root_id = root.id;
  update.nodes.push_back(root);
  update.nodes.push_back(empty_grid);
  update.nodes.push_back(rowcolindex_grid);
  update.nodes.push_back(rowcolindex_row);
  update.nodes.push_back(rowcolindex_cell);
  update.nodes.push_back(rowcolcount_grid);
  update.nodes.push_back(unknown_grid);

  update.has_tree_data = true;
  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  return update;
}

AXTreeUpdate AXPlatformNodeTest::BuildListBox(
    bool option_1_is_selected,
    bool option_2_is_selected,
    bool option_3_is_selected,
    const std::vector<ax::mojom::State>& additional_state) {
  AXNodeData listbox;
  listbox.id = 1;
  listbox.role = ax::mojom::Role::kListBox;
  listbox.SetName("ListBox");
  for (auto state : additional_state)
    listbox.AddState(state);

  AXNodeData option_1;
  option_1.id = 2;
  option_1.role = ax::mojom::Role::kListBoxOption;
  option_1.SetName("Option1");
  if (option_1_is_selected)
    option_1.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
  listbox.child_ids.push_back(option_1.id);

  AXNodeData option_2;
  option_2.id = 3;
  option_2.role = ax::mojom::Role::kListBoxOption;
  option_2.SetName("Option2");
  if (option_2_is_selected)
    option_2.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
  listbox.child_ids.push_back(option_2.id);

  AXNodeData option_3;
  option_3.id = 4;
  option_3.role = ax::mojom::Role::kListBoxOption;
  option_3.SetName("Option3");
  if (option_3_is_selected)
    option_3.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
  listbox.child_ids.push_back(option_3.id);

  AXTreeUpdate update;
  update.root_id = listbox.id;
  update.nodes.push_back(listbox);
  update.nodes.push_back(option_1);
  update.nodes.push_back(option_2);
  update.nodes.push_back(option_3);

  update.has_tree_data = true;
  update.tree_data.tree_id = AXTreeID::CreateNewAXTreeID();
  return update;
}

void AXPlatformNodeTest::SetTree(std::unique_ptr<AXTree> tree) {
#if BUILDFLAG_INTERNAL_HAS_NATIVE_ACCESSIBILITY()
  if (ax_tree_) {
    // Make sure to reset the observers, as this tree is about to be destroyed.
    TestAXNodeWrapper::ResetGlobalState();
  }
#endif  // BUILDFLAG_INTERNAL_HAS_NATIVE_ACCESSIBILITY()

  TestSingleAXTreeManager::SetTree(std::move(tree));
}

}  // namespace ui