Commit dc71a087 authored by Mansi Awasthi's avatar Mansi Awasthi Committed by Commit Bot

Add choice fields in PdfAccessibilityTree

This CL creates choice field nodes using PdfAccessibilityChoiceFieldInfo
sent from plugin process and inserts them in the PdfAccessibilityTree.

This CL also includes test to validate the creation of choice field
nodes in PdfAccessibilityTree.

Bug: 1030242
Change-Id: Ia5654344970624b06cd6f352090625dc77007879
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2287070
Commit-Queue: Mansi Awasthi <maawas@microsoft.com>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: Ankit Kumar 🌪️ <ankk@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#789335}
parent a0b6b7cf
...@@ -377,6 +377,7 @@ class PdfAccessibilityTreeBuilder { ...@@ -377,6 +377,7 @@ class PdfAccessibilityTreeBuilder {
highlights_(page_objects.highlights), highlights_(page_objects.highlights),
text_fields_(page_objects.form_fields.text_fields), text_fields_(page_objects.form_fields.text_fields),
buttons_(page_objects.form_fields.buttons), buttons_(page_objects.form_fields.buttons),
choice_fields_(page_objects.form_fields.choice_fields),
page_bounds_(page_bounds), page_bounds_(page_bounds),
page_index_(page_index), page_index_(page_index),
page_node_(page_node), page_node_(page_node),
...@@ -408,6 +409,7 @@ class PdfAccessibilityTreeBuilder { ...@@ -408,6 +409,7 @@ class PdfAccessibilityTreeBuilder {
uint32_t current_highlight_index = 0; uint32_t current_highlight_index = 0;
uint32_t current_text_field_index = 0; uint32_t current_text_field_index = 0;
uint32_t current_button_index = 0; uint32_t current_button_index = 0;
uint32_t current_choice_field_index = 0;
LineHelper line_helper(text_runs_); LineHelper line_helper(text_runs_);
bool pdf_forms_enabled = bool pdf_forms_enabled =
base::FeatureList::IsEnabled(chrome_pdf::features::kAccessiblePDFForm); base::FeatureList::IsEnabled(chrome_pdf::features::kAccessiblePDFForm);
...@@ -461,6 +463,13 @@ class PdfAccessibilityTreeBuilder { ...@@ -461,6 +463,13 @@ class PdfAccessibilityTreeBuilder {
AddButtonToParaNode(buttons_[current_button_index++], para_node, AddButtonToParaNode(buttons_[current_button_index++], para_node,
&text_run_index); &text_run_index);
continue; continue;
} else if (IsObjectInTextRun(choice_fields_, current_choice_field_index,
text_run_index) &&
pdf_forms_enabled) {
FinishStaticNode(&static_text_node, &static_text);
AddChoiceFieldToParaNode(choice_fields_[current_choice_field_index++],
para_node, &text_run_index);
continue;
} else { } else {
PP_PdfPageCharacterIndex page_char_index = { PP_PdfPageCharacterIndex page_char_index = {
page_index_, text_run_start_indices_[text_run_index]}; page_index_, text_run_start_indices_[text_run_index]};
...@@ -529,9 +538,12 @@ class PdfAccessibilityTreeBuilder { ...@@ -529,9 +538,12 @@ class PdfAccessibilityTreeBuilder {
base::make_span(text_fields_).subspan(current_text_field_index); base::make_span(text_fields_).subspan(current_text_field_index);
base::span<const ppapi::PdfAccessibilityButtonInfo> remaining_buttons = base::span<const ppapi::PdfAccessibilityButtonInfo> remaining_buttons =
base::make_span(buttons_).subspan(current_button_index); base::make_span(buttons_).subspan(current_button_index);
base::span<const ppapi::PdfAccessibilityChoiceFieldInfo>
remaining_choice_fields =
base::make_span(choice_fields_).subspan(current_text_field_index);
AddRemainingAnnotations(remaining_links, remaining_images, AddRemainingAnnotations(remaining_links, remaining_images,
remaining_text_fields, remaining_buttons, remaining_text_fields, remaining_buttons,
para_node); remaining_choice_fields, para_node);
} }
private: private:
...@@ -752,6 +764,120 @@ class PdfAccessibilityTreeBuilder { ...@@ -752,6 +764,120 @@ class PdfAccessibilityTreeBuilder {
return button_node; return button_node;
} }
ui::AXNodeData* CreateListboxOptionNode(
const ppapi::PdfAccessibilityChoiceFieldOptionInfo& choice_field_option,
ax::mojom::Restriction restriction) {
ui::AXNodeData* listbox_option_node =
CreateNode(ax::mojom::Role::kListBoxOption, restriction,
render_accessibility_, nodes_);
listbox_option_node->AddStringAttribute(ax::mojom::StringAttribute::kName,
choice_field_option.name);
listbox_option_node->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected,
choice_field_option.is_selected);
listbox_option_node->AddState(ax::mojom::State::kFocusable);
return listbox_option_node;
}
ui::AXNodeData* CreateListboxNode(
const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field,
ui::AXNodeData* control_node) {
ax::mojom::Restriction restriction = choice_field.is_read_only
? ax::mojom::Restriction::kReadOnly
: ax::mojom::Restriction::kNone;
ui::AXNodeData* listbox_node = CreateNode(
ax::mojom::Role::kListBox, restriction, render_accessibility_, nodes_);
if (choice_field.type != PP_PRIVATECHOICEFIELD_COMBOBOX) {
listbox_node->AddStringAttribute(ax::mojom::StringAttribute::kName,
choice_field.name);
}
ui::AXNodeData* first_selected_option = nullptr;
for (const ppapi::PdfAccessibilityChoiceFieldOptionInfo& option :
choice_field.options) {
ui::AXNodeData* listbox_option_node =
CreateListboxOptionNode(option, restriction);
if (!first_selected_option && listbox_option_node->GetBoolAttribute(
ax::mojom::BoolAttribute::kSelected)) {
first_selected_option = listbox_option_node;
}
// TODO(bug 1030242): add |listbox_option_node| specific bounds here.
listbox_option_node->relative_bounds.bounds =
PpFloatRectToGfxRectF(choice_field.bounds);
listbox_node->child_ids.push_back(listbox_option_node->id);
}
if (control_node && first_selected_option) {
control_node->AddIntAttribute(
ax::mojom::IntAttribute::kActivedescendantId,
first_selected_option->id);
}
if (choice_field.is_multi_select)
listbox_node->AddState(ax::mojom::State::kMultiselectable);
listbox_node->AddState(ax::mojom::State::kFocusable);
listbox_node->relative_bounds.bounds =
PpFloatRectToGfxRectF(choice_field.bounds);
return listbox_node;
}
ui::AXNodeData* CreateComboboxInputNode(
const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field,
ax::mojom::Restriction restriction) {
ax::mojom::Role input_role = choice_field.has_editable_text_box
? ax::mojom::Role::kTextFieldWithComboBox
: ax::mojom::Role::kComboBoxMenuButton;
ui::AXNodeData* combobox_input_node =
CreateNode(input_role, restriction, render_accessibility_, nodes_);
combobox_input_node->AddStringAttribute(ax::mojom::StringAttribute::kName,
choice_field.name);
for (const ppapi::PdfAccessibilityChoiceFieldOptionInfo& option :
choice_field.options) {
if (option.is_selected) {
combobox_input_node->AddStringAttribute(
ax::mojom::StringAttribute::kValue, option.name);
break;
}
}
combobox_input_node->AddState(ax::mojom::State::kFocusable);
combobox_input_node->relative_bounds.bounds =
PpFloatRectToGfxRectF(choice_field.bounds);
return combobox_input_node;
}
ui::AXNodeData* CreateComboboxNode(
const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field) {
ax::mojom::Restriction restriction = choice_field.is_read_only
? ax::mojom::Restriction::kReadOnly
: ax::mojom::Restriction::kNone;
ui::AXNodeData* combobox_node =
CreateNode(ax::mojom::Role::kComboBoxGrouping, restriction,
render_accessibility_, nodes_);
ui::AXNodeData* input_element =
CreateComboboxInputNode(choice_field, restriction);
ui::AXNodeData* list_element =
CreateListboxNode(choice_field, input_element);
input_element->AddIntListAttribute(
ax::mojom::IntListAttribute::kControlsIds,
std::vector<int32_t>{list_element->id});
combobox_node->child_ids.push_back(input_element->id);
combobox_node->child_ids.push_back(list_element->id);
combobox_node->AddState(ax::mojom::State::kFocusable);
combobox_node->relative_bounds.bounds =
PpFloatRectToGfxRectF(choice_field.bounds);
return combobox_node;
}
ui::AXNodeData* CreateChoiceFieldNode(
const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field) {
if (choice_field.type == PP_PRIVATECHOICEFIELD_LISTBOX)
return CreateListboxNode(choice_field, nullptr);
return CreateComboboxNode(choice_field);
}
void AddTextToAXNode(uint32_t start_text_run_index, void AddTextToAXNode(uint32_t start_text_run_index,
uint32_t end_text_run_index, uint32_t end_text_run_index,
ui::AXNodeData* ax_node, ui::AXNodeData* ax_node,
...@@ -919,16 +1045,28 @@ class PdfAccessibilityTreeBuilder { ...@@ -919,16 +1045,28 @@ class PdfAccessibilityTreeBuilder {
--(*text_run_index); --(*text_run_index);
} }
void AddChoiceFieldToParaNode(
const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field,
ui::AXNodeData* para_node,
size_t* text_run_index) {
// If the |text_run_index| is less than or equal to the choice_field's text
// run index, then push the choice_field ahead of the current text run.
ui::AXNodeData* choice_field_node = CreateChoiceFieldNode(choice_field);
para_node->child_ids.push_back(choice_field_node->id);
--(*text_run_index);
}
void AddRemainingAnnotations( void AddRemainingAnnotations(
base::span<const ppapi::PdfAccessibilityLinkInfo> links, base::span<const ppapi::PdfAccessibilityLinkInfo> links,
base::span<const ppapi::PdfAccessibilityImageInfo> images, base::span<const ppapi::PdfAccessibilityImageInfo> images,
base::span<const ppapi::PdfAccessibilityTextFieldInfo> text_fields, base::span<const ppapi::PdfAccessibilityTextFieldInfo> text_fields,
base::span<const ppapi::PdfAccessibilityButtonInfo> buttons, base::span<const ppapi::PdfAccessibilityButtonInfo> buttons,
base::span<const ppapi::PdfAccessibilityChoiceFieldInfo> choice_fields,
ui::AXNodeData* para_node) { ui::AXNodeData* para_node) {
// If we don't have additional links, images or form fields to insert in the // If we don't have additional links, images or form fields to insert in the
// tree, then return. // tree, then return.
if (links.empty() && images.empty() && text_fields.empty() && if (links.empty() && images.empty() && text_fields.empty() &&
buttons.empty()) { buttons.empty() && choice_fields.empty()) {
return; return;
} }
...@@ -966,6 +1104,14 @@ class PdfAccessibilityTreeBuilder { ...@@ -966,6 +1104,14 @@ class PdfAccessibilityTreeBuilder {
ui::AXNodeData* button_node = CreateButtonNode(button); ui::AXNodeData* button_node = CreateButtonNode(button);
para_node->child_ids.push_back(button_node->id); para_node->child_ids.push_back(button_node->id);
} }
// Push all the choice fields not anchored to any text run to the last
// paragraph.
for (const ppapi::PdfAccessibilityChoiceFieldInfo& choice_field :
choice_fields) {
ui::AXNodeData* choice_field_node = CreateChoiceFieldNode(choice_field);
para_node->child_ids.push_back(choice_field_node->id);
}
} }
} }
...@@ -977,6 +1123,7 @@ class PdfAccessibilityTreeBuilder { ...@@ -977,6 +1123,7 @@ class PdfAccessibilityTreeBuilder {
const std::vector<ppapi::PdfAccessibilityHighlightInfo>& highlights_; const std::vector<ppapi::PdfAccessibilityHighlightInfo>& highlights_;
const std::vector<ppapi::PdfAccessibilityTextFieldInfo>& text_fields_; const std::vector<ppapi::PdfAccessibilityTextFieldInfo>& text_fields_;
const std::vector<ppapi::PdfAccessibilityButtonInfo>& buttons_; const std::vector<ppapi::PdfAccessibilityButtonInfo>& buttons_;
const std::vector<ppapi::PdfAccessibilityChoiceFieldInfo>& choice_fields_;
const gfx::RectF& page_bounds_; const gfx::RectF& page_bounds_;
uint32_t page_index_; uint32_t page_index_;
ui::AXNodeData* page_node_; ui::AXNodeData* page_node_;
......
...@@ -865,6 +865,453 @@ TEST_F(PdfAccessibilityTreeTest, TestButtonNodeCreation) { ...@@ -865,6 +865,453 @@ TEST_F(PdfAccessibilityTreeTest, TestButtonNodeCreation) {
EXPECT_EQ(0u, push_button_node->children().size()); EXPECT_EQ(0u, push_button_node->children().size());
} }
TEST_F(PdfAccessibilityTreeTest, TestListboxNodeCreation) {
// Enable feature flag
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
chrome_pdf::features::kAccessiblePDFForm);
text_runs_.emplace_back(kFirstTextRun);
text_runs_.emplace_back(kSecondTextRun);
chars_.insert(chars_.end(), std::begin(kDummyCharsData),
std::end(kDummyCharsData));
struct ListboxOptionInfo {
std::string name;
bool is_selected;
};
const ListboxOptionInfo kExpectedOptions[][3] = {
{{"Alpha", false}, {"Beta", true}, {"Gamma", true}},
{{"Foo", false}, {"Bar", true}, {"Qux", false}}};
const gfx::RectF kExpectedBounds[] = {{1.0f, 1.0f, 5.0f, 6.0f},
{1.0f, 10.0f, 5.0f, 6.0f}};
{
ppapi::PdfAccessibilityChoiceFieldInfo choice_field;
choice_field.bounds = PP_MakeFloatRectFromXYWH(1.0f, 1.0f, 5.0f, 6.0f);
choice_field.index_in_page = 0;
choice_field.text_run_index = 2;
choice_field.type = PP_PRIVATECHOICEFIELD_LISTBOX;
choice_field.name = "List Box";
choice_field.is_read_only = false;
choice_field.is_multi_select = true;
choice_field.has_editable_text_box = false;
for (const ListboxOptionInfo& expected_option : kExpectedOptions[0]) {
ppapi::PdfAccessibilityChoiceFieldOptionInfo choice_field_option;
choice_field_option.name = expected_option.name;
choice_field_option.is_selected = expected_option.is_selected;
choice_field.options.push_back(std::move(choice_field_option));
}
page_objects_.form_fields.choice_fields.push_back(std::move(choice_field));
}
{
ppapi::PdfAccessibilityChoiceFieldInfo choice_field;
choice_field.bounds = PP_MakeFloatRectFromXYWH(1.0f, 10.0f, 5.0f, 6.0f);
choice_field.index_in_page = 1;
choice_field.text_run_index = 2;
choice_field.type = PP_PRIVATECHOICEFIELD_LISTBOX;
choice_field.name = "Read Only List Box";
choice_field.is_read_only = true;
choice_field.is_multi_select = false;
choice_field.has_editable_text_box = false;
for (const ListboxOptionInfo& expected_option : kExpectedOptions[1]) {
ppapi::PdfAccessibilityChoiceFieldOptionInfo choice_field_option;
choice_field_option.name = expected_option.name;
choice_field_option.is_selected = expected_option.is_selected;
choice_field.options.push_back(std::move(choice_field_option));
}
page_objects_.form_fields.choice_fields.push_back(std::move(choice_field));
}
page_info_.text_run_count = text_runs_.size();
page_info_.char_count = chars_.size();
content::RenderFrame* render_frame = view_->GetMainRenderFrame();
ASSERT_TRUE(render_frame);
render_frame->SetAccessibilityModeForTest(ui::AXMode::kWebContents);
ASSERT_TRUE(render_frame->GetRenderAccessibility());
FakeRendererPpapiHost host(view_->GetMainRenderFrame());
PP_Instance instance = 0;
pdf::PdfAccessibilityTree pdf_accessibility_tree(&host, instance);
pdf_accessibility_tree.SetAccessibilityViewportInfo(viewport_info_);
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
/*
* Expected tree structure
* Document
* ++ Region
* ++++ Paragraph
* ++++++ Static Text
* ++++ Paragraph
* ++++++ Static Text
* ++++++ Listbox
* ++++++++ Listbox Option
* ++++++++ Listbox Option
* ++++++++ Listbox Option
* ++++++ Listbox
* ++++++++ Listbox Option
* ++++++++ Listbox Option
* ++++++++ Listbox Option
*/
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
ASSERT_TRUE(root_node);
EXPECT_EQ(ax::mojom::Role::kDocument, root_node->data().role);
ASSERT_EQ(1u, root_node->children().size());
ui::AXNode* page_node = root_node->children()[0];
ASSERT_TRUE(page_node);
EXPECT_EQ(ax::mojom::Role::kRegion, page_node->data().role);
ASSERT_EQ(2u, page_node->children().size());
ui::AXNode* paragraph_node = page_node->children()[0];
ASSERT_TRUE(paragraph_node);
EXPECT_EQ(ax::mojom::Role::kParagraph, paragraph_node->data().role);
ASSERT_EQ(1u, paragraph_node->children().size());
ui::AXNode* static_text_node = paragraph_node->children()[0];
ASSERT_TRUE(static_text_node);
EXPECT_EQ(ax::mojom::Role::kStaticText, static_text_node->data().role);
ASSERT_EQ(1u, static_text_node->children().size());
paragraph_node = page_node->children()[1];
ASSERT_TRUE(paragraph_node);
EXPECT_EQ(ax::mojom::Role::kParagraph, paragraph_node->data().role);
const std::vector<ui::AXNode*>& child_nodes = paragraph_node->children();
ASSERT_EQ(3u, child_nodes.size());
static_text_node = child_nodes[0];
ASSERT_TRUE(static_text_node);
EXPECT_EQ(ax::mojom::Role::kStaticText, static_text_node->data().role);
ASSERT_EQ(1u, static_text_node->children().size());
{
ui::AXNode* listbox_node = child_nodes[1];
ASSERT_TRUE(listbox_node);
EXPECT_EQ(ax::mojom::Role::kListBox, listbox_node->data().role);
EXPECT_NE(ax::mojom::Restriction::kReadOnly,
listbox_node->data().GetRestriction());
EXPECT_EQ("List Box", listbox_node->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_TRUE(
listbox_node->data().HasState(ax::mojom::State::kMultiselectable));
EXPECT_TRUE(listbox_node->data().HasState(ax::mojom::State::kFocusable));
EXPECT_EQ(kExpectedBounds[0], listbox_node->data().relative_bounds.bounds);
ASSERT_EQ(base::size(kExpectedOptions[0]), listbox_node->children().size());
const std::vector<ui::AXNode*>& listbox_child_nodes =
listbox_node->children();
for (size_t i = 0; i < listbox_child_nodes.size(); i++) {
EXPECT_EQ(ax::mojom::Role::kListBoxOption,
listbox_child_nodes[i]->data().role);
EXPECT_NE(ax::mojom::Restriction::kReadOnly,
listbox_child_nodes[i]->data().GetRestriction());
EXPECT_EQ(kExpectedOptions[0][i].name,
listbox_child_nodes[i]->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_EQ(kExpectedOptions[0][i].is_selected,
listbox_child_nodes[i]->GetBoolAttribute(
ax::mojom::BoolAttribute::kSelected));
EXPECT_TRUE(listbox_child_nodes[i]->data().HasState(
ax::mojom::State::kFocusable));
EXPECT_EQ(kExpectedBounds[0],
listbox_child_nodes[i]->data().relative_bounds.bounds);
}
}
{
ui::AXNode* listbox_node = child_nodes[2];
ASSERT_TRUE(listbox_node);
EXPECT_EQ(ax::mojom::Role::kListBox, listbox_node->data().role);
EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
listbox_node->data().GetRestriction());
EXPECT_EQ("Read Only List Box", listbox_node->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_FALSE(
listbox_node->data().HasState(ax::mojom::State::kMultiselectable));
EXPECT_TRUE(listbox_node->data().HasState(ax::mojom::State::kFocusable));
EXPECT_EQ(kExpectedBounds[1], listbox_node->data().relative_bounds.bounds);
ASSERT_EQ(base::size(kExpectedOptions[1]), listbox_node->children().size());
const std::vector<ui::AXNode*>& listbox_child_nodes =
listbox_node->children();
for (size_t i = 0; i < listbox_child_nodes.size(); i++) {
EXPECT_EQ(ax::mojom::Role::kListBoxOption,
listbox_child_nodes[i]->data().role);
EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
listbox_child_nodes[i]->data().GetRestriction());
EXPECT_EQ(kExpectedOptions[1][i].name,
listbox_child_nodes[i]->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_EQ(kExpectedOptions[1][i].is_selected,
listbox_child_nodes[i]->GetBoolAttribute(
ax::mojom::BoolAttribute::kSelected));
EXPECT_TRUE(listbox_child_nodes[i]->data().HasState(
ax::mojom::State::kFocusable));
EXPECT_EQ(kExpectedBounds[1],
listbox_child_nodes[i]->data().relative_bounds.bounds);
}
}
}
TEST_F(PdfAccessibilityTreeTest, TestComboboxNodeCreation) {
// Enable feature flag
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
chrome_pdf::features::kAccessiblePDFForm);
text_runs_.emplace_back(kFirstTextRun);
text_runs_.emplace_back(kSecondTextRun);
chars_.insert(chars_.end(), std::begin(kDummyCharsData),
std::end(kDummyCharsData));
struct ComboboxOptionInfo {
std::string name;
bool is_selected;
};
const ComboboxOptionInfo kExpectedOptions[][3] = {
{{"Albania", false}, {"Belgium", true}, {"Croatia", true}},
{{"Apple", false}, {"Banana", true}, {"Cherry", false}}};
const gfx::RectF kExpectedBounds[] = {{1.0f, 1.0f, 5.0f, 6.0f},
{1.0f, 10.0f, 5.0f, 6.0f}};
{
ppapi::PdfAccessibilityChoiceFieldInfo choice_field;
choice_field.bounds = PP_MakeFloatRectFromXYWH(1.0f, 1.0f, 5.0f, 6.0f);
choice_field.index_in_page = 0;
choice_field.text_run_index = 2;
choice_field.type = PP_PRIVATECHOICEFIELD_COMBOBOX;
choice_field.name = "Editable Combo Box";
choice_field.is_read_only = false;
choice_field.is_multi_select = true;
choice_field.has_editable_text_box = true;
for (const ComboboxOptionInfo& expected_option : kExpectedOptions[0]) {
ppapi::PdfAccessibilityChoiceFieldOptionInfo choice_field_option;
choice_field_option.name = expected_option.name;
choice_field_option.is_selected = expected_option.is_selected;
choice_field.options.push_back(std::move(choice_field_option));
}
page_objects_.form_fields.choice_fields.push_back(std::move(choice_field));
}
{
ppapi::PdfAccessibilityChoiceFieldInfo choice_field;
choice_field.bounds = PP_MakeFloatRectFromXYWH(1.0f, 10.0f, 5.0f, 6.0f);
choice_field.index_in_page = 1;
choice_field.text_run_index = 2;
choice_field.type = PP_PRIVATECHOICEFIELD_COMBOBOX;
choice_field.name = "Read Only Combo Box";
choice_field.is_read_only = true;
choice_field.is_multi_select = false;
choice_field.has_editable_text_box = false;
for (const ComboboxOptionInfo& expected_option : kExpectedOptions[1]) {
ppapi::PdfAccessibilityChoiceFieldOptionInfo choice_field_option;
choice_field_option.name = expected_option.name;
choice_field_option.is_selected = expected_option.is_selected;
choice_field.options.push_back(std::move(choice_field_option));
}
page_objects_.form_fields.choice_fields.push_back(std::move(choice_field));
}
page_info_.text_run_count = text_runs_.size();
page_info_.char_count = chars_.size();
content::RenderFrame* render_frame = view_->GetMainRenderFrame();
ASSERT_TRUE(render_frame);
render_frame->SetAccessibilityModeForTest(ui::AXMode::kWebContents);
ASSERT_TRUE(render_frame->GetRenderAccessibility());
FakeRendererPpapiHost host(view_->GetMainRenderFrame());
PP_Instance instance = 0;
pdf::PdfAccessibilityTree pdf_accessibility_tree(&host, instance);
pdf_accessibility_tree.SetAccessibilityViewportInfo(viewport_info_);
pdf_accessibility_tree.SetAccessibilityDocInfo(doc_info_);
pdf_accessibility_tree.SetAccessibilityPageInfo(page_info_, text_runs_,
chars_, page_objects_);
/*
* Expected tree structure
* Document
* ++ Region
* ++++ Paragraph
* ++++++ Static Text
* ++++ Paragraph
* ++++++ Static Text
* ++++++ Combobox Grouping
* ++++++++ Text Field With Combobox
* ++++++++ Listbox
* ++++++++++ Listbox Option
* ++++++++++ Listbox Option
* ++++++++++ Listbox Option
* ++++++ Combobox Grouping
* ++++++++ Combobox Menu Button
* ++++++++ Listbox
* ++++++++++ Listbox Option
* ++++++++++ Listbox Option
* ++++++++++ Listbox Option
*/
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
ASSERT_TRUE(root_node);
EXPECT_EQ(ax::mojom::Role::kDocument, root_node->data().role);
ASSERT_EQ(1u, root_node->children().size());
ui::AXNode* page_node = root_node->children()[0];
ASSERT_TRUE(page_node);
EXPECT_EQ(ax::mojom::Role::kRegion, page_node->data().role);
ASSERT_EQ(2u, page_node->children().size());
ui::AXNode* paragraph_node = page_node->children()[0];
ASSERT_TRUE(paragraph_node);
EXPECT_EQ(ax::mojom::Role::kParagraph, paragraph_node->data().role);
ASSERT_EQ(1u, paragraph_node->children().size());
ui::AXNode* static_text_node = paragraph_node->children()[0];
ASSERT_TRUE(static_text_node);
EXPECT_EQ(ax::mojom::Role::kStaticText, static_text_node->data().role);
ASSERT_EQ(1u, static_text_node->children().size());
paragraph_node = page_node->children()[1];
ASSERT_TRUE(paragraph_node);
EXPECT_EQ(ax::mojom::Role::kParagraph, paragraph_node->data().role);
const std::vector<ui::AXNode*>& child_nodes = paragraph_node->children();
ASSERT_EQ(3u, child_nodes.size());
static_text_node = child_nodes[0];
ASSERT_TRUE(static_text_node);
EXPECT_EQ(ax::mojom::Role::kStaticText, static_text_node->data().role);
ASSERT_EQ(1u, static_text_node->children().size());
{
ui::AXNode* combobox_node = child_nodes[1];
ASSERT_TRUE(combobox_node);
EXPECT_EQ(ax::mojom::Role::kComboBoxGrouping, combobox_node->data().role);
EXPECT_NE(ax::mojom::Restriction::kReadOnly,
combobox_node->data().GetRestriction());
EXPECT_TRUE(combobox_node->data().HasState(ax::mojom::State::kFocusable));
EXPECT_EQ(kExpectedBounds[0], combobox_node->data().relative_bounds.bounds);
ASSERT_EQ(2u, combobox_node->children().size());
const std::vector<ui::AXNode*>& combobox_child_nodes =
combobox_node->children();
ui::AXNode* combobox_input_node = combobox_child_nodes[0];
EXPECT_EQ(ax::mojom::Role::kTextFieldWithComboBox,
combobox_input_node->data().role);
EXPECT_NE(ax::mojom::Restriction::kReadOnly,
combobox_input_node->data().GetRestriction());
EXPECT_EQ("Editable Combo Box", combobox_input_node->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_EQ("Belgium", combobox_input_node->GetStringAttribute(
ax::mojom::StringAttribute::kValue));
EXPECT_TRUE(
combobox_input_node->data().HasState(ax::mojom::State::kFocusable));
EXPECT_EQ(kExpectedBounds[0],
combobox_input_node->data().relative_bounds.bounds);
ui::AXNode* combobox_popup_node = combobox_child_nodes[1];
EXPECT_EQ(ax::mojom::Role::kListBox, combobox_popup_node->data().role);
EXPECT_NE(ax::mojom::Restriction::kReadOnly,
combobox_popup_node->data().GetRestriction());
EXPECT_TRUE(combobox_popup_node->data().HasState(
ax::mojom::State::kMultiselectable));
EXPECT_EQ(kExpectedBounds[0],
combobox_popup_node->data().relative_bounds.bounds);
ASSERT_EQ(base::size(kExpectedOptions[0]),
combobox_popup_node->children().size());
const std::vector<ui::AXNode*>& popup_child_nodes =
combobox_popup_node->children();
for (size_t i = 0; i < popup_child_nodes.size(); i++) {
EXPECT_EQ(ax::mojom::Role::kListBoxOption,
popup_child_nodes[i]->data().role);
EXPECT_NE(ax::mojom::Restriction::kReadOnly,
popup_child_nodes[i]->data().GetRestriction());
EXPECT_EQ(kExpectedOptions[0][i].name,
popup_child_nodes[i]->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_EQ(kExpectedOptions[0][i].is_selected,
popup_child_nodes[i]->GetBoolAttribute(
ax::mojom::BoolAttribute::kSelected));
EXPECT_TRUE(
popup_child_nodes[i]->data().HasState(ax::mojom::State::kFocusable));
EXPECT_EQ(kExpectedBounds[0],
popup_child_nodes[i]->data().relative_bounds.bounds);
}
EXPECT_EQ(popup_child_nodes[1]->data().id,
combobox_input_node->GetIntAttribute(
ax::mojom::IntAttribute::kActivedescendantId));
const auto& controls_ids = combobox_input_node->GetIntListAttribute(
ax::mojom::IntListAttribute::kControlsIds);
ASSERT_EQ(1u, controls_ids.size());
EXPECT_EQ(controls_ids[0], combobox_popup_node->data().id);
}
{
ui::AXNode* combobox_node = child_nodes[2];
ASSERT_TRUE(combobox_node);
EXPECT_EQ(ax::mojom::Role::kComboBoxGrouping, combobox_node->data().role);
EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
combobox_node->data().GetRestriction());
EXPECT_TRUE(combobox_node->data().HasState(ax::mojom::State::kFocusable));
EXPECT_EQ(kExpectedBounds[1], combobox_node->data().relative_bounds.bounds);
ASSERT_EQ(2u, combobox_node->children().size());
const std::vector<ui::AXNode*>& combobox_child_nodes =
combobox_node->children();
ui::AXNode* combobox_input_node = combobox_child_nodes[0];
EXPECT_EQ(ax::mojom::Role::kComboBoxMenuButton,
combobox_input_node->data().role);
EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
combobox_input_node->data().GetRestriction());
EXPECT_EQ("Read Only Combo Box", combobox_input_node->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_EQ("Banana", combobox_input_node->GetStringAttribute(
ax::mojom::StringAttribute::kValue));
EXPECT_TRUE(
combobox_input_node->data().HasState(ax::mojom::State::kFocusable));
EXPECT_EQ(kExpectedBounds[1],
combobox_input_node->data().relative_bounds.bounds);
ui::AXNode* combobox_popup_node = combobox_child_nodes[1];
EXPECT_EQ(ax::mojom::Role::kListBox, combobox_popup_node->data().role);
EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
combobox_popup_node->data().GetRestriction());
EXPECT_EQ(kExpectedBounds[1],
combobox_popup_node->data().relative_bounds.bounds);
ASSERT_EQ(base::size(kExpectedOptions[1]),
combobox_popup_node->children().size());
const std::vector<ui::AXNode*>& popup_child_nodes =
combobox_popup_node->children();
for (size_t i = 0; i < popup_child_nodes.size(); i++) {
EXPECT_EQ(ax::mojom::Role::kListBoxOption,
popup_child_nodes[i]->data().role);
EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
popup_child_nodes[i]->data().GetRestriction());
EXPECT_EQ(kExpectedOptions[1][i].name,
popup_child_nodes[i]->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_EQ(kExpectedOptions[1][i].is_selected,
popup_child_nodes[i]->GetBoolAttribute(
ax::mojom::BoolAttribute::kSelected));
EXPECT_TRUE(
popup_child_nodes[i]->data().HasState(ax::mojom::State::kFocusable));
EXPECT_EQ(kExpectedBounds[1],
popup_child_nodes[i]->data().relative_bounds.bounds);
}
EXPECT_EQ(popup_child_nodes[1]->data().id,
combobox_input_node->GetIntAttribute(
ax::mojom::IntAttribute::kActivedescendantId));
const auto& controls_ids = combobox_input_node->GetIntListAttribute(
ax::mojom::IntListAttribute::kControlsIds);
ASSERT_EQ(1u, controls_ids.size());
EXPECT_EQ(controls_ids[0], combobox_popup_node->data().id);
}
}
TEST_F(PdfAccessibilityTreeTest, TestPreviousNextOnLine) { TEST_F(PdfAccessibilityTreeTest, TestPreviousNextOnLine) {
text_runs_.emplace_back(kFirstRunMultiLine); text_runs_.emplace_back(kFirstRunMultiLine);
text_runs_.emplace_back(kSecondRunMultiLine); text_runs_.emplace_back(kSecondRunMultiLine);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment