Commit 762d06c4 authored by Mansi Awasthi's avatar Mansi Awasthi Committed by Commit Bot

Reland: Add buttons in PdfAccessibilityTree

Original CL: http://crrev.com/c/2275981
Revert: http://crrev.com/c/2284446

Reason for revert:
Builder: Linux ChromiumOS MSan Tests
https://ci.chromium.org/p/chromium/builders/ci/Linux%20ChromiumOS%20MSan%20Tests/b8875468439609281712

The failures were caused as |push_button.is_read_only| was not
initialized in TestButtonNodeCreation before usage. This CL fixes this
issue.

Original description:
This CL creates button nodes using PdfAccessibilityButtonInfo sent from
plugin process and inserts them in the PdfAccessibilityTree.

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

CQ_INCLUDE_TRYBOTS=luci.chromium.try:linux_chromium_chromeos_msan_rel_ng

Bug: 1030242
Change-Id: Id821ea02f92c20b5f5eb3190e4d878c6bd52edea
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2284505Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: Ankit Kumar 🌪️ <ankk@microsoft.com>
Commit-Queue: Mansi Awasthi <maawas@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#786155}
parent ddaf5292
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/i18n/break_iterator.h" #include "base/i18n/break_iterator.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversion_utils.h" #include "base/strings/utf_string_conversion_utils.h"
#include "components/pdf/renderer/pdf_ax_action_target.h" #include "components/pdf/renderer/pdf_ax_action_target.h"
...@@ -341,6 +342,20 @@ ui::AXNodeData* CreateNode( ...@@ -341,6 +342,20 @@ ui::AXNodeData* CreateNode(
return node_ptr; return node_ptr;
} }
ax::mojom::Role GetRoleForButtonType(PP_PrivateButtonType button_type) {
switch (button_type) {
case PP_PrivateButtonType::PP_PRIVATEBUTTON_RADIOBUTTON:
return ax::mojom::Role::kRadioButton;
case PP_PrivateButtonType::PP_PRIVATEBUTTON_CHECKBOX:
return ax::mojom::Role::kCheckBox;
case PP_PrivateButtonType::PP_PRIVATEBUTTON_PUSHBUTTON:
return ax::mojom::Role::kButton;
default:
NOTREACHED();
return ax::mojom::Role::kNone;
}
}
class PdfAccessibilityTreeBuilder { class PdfAccessibilityTreeBuilder {
public: public:
explicit PdfAccessibilityTreeBuilder( explicit PdfAccessibilityTreeBuilder(
...@@ -361,6 +376,7 @@ class PdfAccessibilityTreeBuilder { ...@@ -361,6 +376,7 @@ class PdfAccessibilityTreeBuilder {
images_(page_objects.images), images_(page_objects.images),
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),
page_bounds_(page_bounds), page_bounds_(page_bounds),
page_index_(page_index), page_index_(page_index),
page_node_(page_node), page_node_(page_node),
...@@ -391,7 +407,10 @@ class PdfAccessibilityTreeBuilder { ...@@ -391,7 +407,10 @@ class PdfAccessibilityTreeBuilder {
uint32_t current_image_index = 0; uint32_t current_image_index = 0;
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;
LineHelper line_helper(text_runs_); LineHelper line_helper(text_runs_);
bool pdf_forms_enabled =
base::FeatureList::IsEnabled(chrome_pdf::features::kAccessiblePDFForm);
for (size_t text_run_index = 0; text_run_index < text_runs_.size(); for (size_t text_run_index = 0; text_run_index < text_runs_.size();
++text_run_index) { ++text_run_index) {
...@@ -430,12 +449,18 @@ class PdfAccessibilityTreeBuilder { ...@@ -430,12 +449,18 @@ class PdfAccessibilityTreeBuilder {
&text_run_index); &text_run_index);
} else if (IsObjectInTextRun(text_fields_, current_text_field_index, } else if (IsObjectInTextRun(text_fields_, current_text_field_index,
text_run_index) && text_run_index) &&
base::FeatureList::IsEnabled( pdf_forms_enabled) {
chrome_pdf::features::kAccessiblePDFForm)) {
FinishStaticNode(&static_text_node, &static_text); FinishStaticNode(&static_text_node, &static_text);
AddTextFieldToParaNode(text_fields_[current_text_field_index++], AddTextFieldToParaNode(text_fields_[current_text_field_index++],
para_node, &text_run_index); para_node, &text_run_index);
continue; continue;
} else if (IsObjectInTextRun(buttons_, current_button_index,
text_run_index) &&
pdf_forms_enabled) {
FinishStaticNode(&static_text_node, &static_text);
AddButtonToParaNode(buttons_[current_button_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]};
...@@ -502,8 +527,11 @@ class PdfAccessibilityTreeBuilder { ...@@ -502,8 +527,11 @@ class PdfAccessibilityTreeBuilder {
base::span<const ppapi::PdfAccessibilityTextFieldInfo> base::span<const ppapi::PdfAccessibilityTextFieldInfo>
remaining_text_fields = remaining_text_fields =
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::make_span(buttons_).subspan(current_button_index);
AddRemainingAnnotations(remaining_links, remaining_images, AddRemainingAnnotations(remaining_links, remaining_images,
remaining_text_fields, para_node); remaining_text_fields, remaining_buttons,
para_node);
} }
private: private:
...@@ -694,6 +722,36 @@ class PdfAccessibilityTreeBuilder { ...@@ -694,6 +722,36 @@ class PdfAccessibilityTreeBuilder {
return text_field_node; return text_field_node;
} }
ui::AXNodeData* CreateButtonNode(
const ppapi::PdfAccessibilityButtonInfo& button) {
ax::mojom::Restriction restriction = button.is_read_only
? ax::mojom::Restriction::kReadOnly
: ax::mojom::Restriction::kNone;
ui::AXNodeData* button_node =
CreateNode(GetRoleForButtonType(button.type), restriction,
render_accessibility_, nodes_);
button_node->AddStringAttribute(ax::mojom::StringAttribute::kName,
button.name);
button_node->AddState(ax::mojom::State::kFocusable);
if (button.type == PP_PRIVATEBUTTON_RADIOBUTTON ||
button.type == PP_PRIVATEBUTTON_CHECKBOX) {
ax::mojom::CheckedState checkedState =
button.is_checked ? ax::mojom::CheckedState::kTrue
: ax::mojom::CheckedState::kNone;
button_node->SetCheckedState(checkedState);
button_node->AddStringAttribute(ax::mojom::StringAttribute::kValue,
button.value);
button_node->AddIntAttribute(ax::mojom::IntAttribute::kSetSize,
button.control_count);
button_node->AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
button.control_index + 1);
}
button_node->relative_bounds.bounds = PpFloatRectToGfxRectF(button.bounds);
return button_node;
}
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,
...@@ -851,15 +909,31 @@ class PdfAccessibilityTreeBuilder { ...@@ -851,15 +909,31 @@ class PdfAccessibilityTreeBuilder {
--(*text_run_index); --(*text_run_index);
} }
void AddButtonToParaNode(const ppapi::PdfAccessibilityButtonInfo& button,
ui::AXNodeData* para_node,
size_t* text_run_index) {
// If the |text_run_index| is less than or equal to the button's text
// run index, then push the button ahead of the current text run.
ui::AXNodeData* button_node = CreateButtonNode(button);
para_node->child_ids.push_back(button_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,
ui::AXNodeData* para_node) { ui::AXNodeData* para_node) {
// If we have additional links, images or text fields to insert in the tree, // If we don't have additional links, images or form fields to insert in the
// and we don't have a paragraph node, create a new one. // tree, then return.
if (!para_node && if (links.empty() && images.empty() && text_fields.empty() &&
(!links.empty() || !images.empty() || !text_fields.empty())) { buttons.empty()) {
return;
}
// If we don't have a paragraph node, create a new one.
if (!para_node) {
para_node = CreateNode(ax::mojom::Role::kParagraph, para_node = CreateNode(ax::mojom::Role::kParagraph,
ax::mojom::Restriction::kReadOnly, ax::mojom::Restriction::kReadOnly,
render_accessibility_, nodes_); render_accessibility_, nodes_);
...@@ -885,6 +959,13 @@ class PdfAccessibilityTreeBuilder { ...@@ -885,6 +959,13 @@ class PdfAccessibilityTreeBuilder {
ui::AXNodeData* text_field_node = CreateTextFieldNode(text_field); ui::AXNodeData* text_field_node = CreateTextFieldNode(text_field);
para_node->child_ids.push_back(text_field_node->id); para_node->child_ids.push_back(text_field_node->id);
} }
// Push all the buttons not anchored to any text run to the last
// paragraph.
for (const ppapi::PdfAccessibilityButtonInfo& button : buttons) {
ui::AXNodeData* button_node = CreateButtonNode(button);
para_node->child_ids.push_back(button_node->id);
}
} }
} }
...@@ -895,6 +976,7 @@ class PdfAccessibilityTreeBuilder { ...@@ -895,6 +976,7 @@ class PdfAccessibilityTreeBuilder {
const std::vector<ppapi::PdfAccessibilityImageInfo>& images_; const std::vector<ppapi::PdfAccessibilityImageInfo>& images_;
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 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_;
......
...@@ -670,6 +670,201 @@ TEST_F(PdfAccessibilityTreeTest, TestTextFieldNodeCreation) { ...@@ -670,6 +670,201 @@ TEST_F(PdfAccessibilityTreeTest, TestTextFieldNodeCreation) {
EXPECT_EQ(0u, text_field_node->children().size()); EXPECT_EQ(0u, text_field_node->children().size());
} }
TEST_F(PdfAccessibilityTreeTest, TestButtonNodeCreation) {
// 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));
{
ppapi::PdfAccessibilityButtonInfo check_box;
check_box.bounds = PP_MakeFloatRectFromXYWH(1.0f, 1.0f, 5.0f, 6.0f);
check_box.index_in_page = 0;
check_box.text_run_index = 2;
check_box.name = "Read Only Checkbox";
check_box.value = "Yes";
check_box.is_read_only = true;
check_box.is_checked = true;
check_box.control_count = 1;
check_box.control_index = 0;
check_box.type = PP_PrivateButtonType::PP_PRIVATEBUTTON_CHECKBOX;
page_objects_.form_fields.buttons.push_back(std::move(check_box));
}
{
ppapi::PdfAccessibilityButtonInfo radio_button;
radio_button.bounds = PP_MakeFloatRectFromXYWH(1.0f, 2.0f, 5.0f, 6.0f);
radio_button.index_in_page = 1;
radio_button.text_run_index = 2;
radio_button.name = "Radio Button";
radio_button.value = "value 1";
radio_button.is_read_only = false;
radio_button.is_checked = false;
radio_button.control_count = 2;
radio_button.control_index = 0;
radio_button.type = PP_PrivateButtonType::PP_PRIVATEBUTTON_RADIOBUTTON;
page_objects_.form_fields.buttons.push_back(std::move(radio_button));
}
{
ppapi::PdfAccessibilityButtonInfo radio_button;
radio_button.bounds = PP_MakeFloatRectFromXYWH(1.0f, 3.0f, 5.0f, 6.0f);
radio_button.index_in_page = 2;
radio_button.text_run_index = 2;
radio_button.name = "Radio Button";
radio_button.value = "value 2";
radio_button.is_read_only = false;
radio_button.is_checked = true;
radio_button.control_count = 2;
radio_button.control_index = 1;
radio_button.type = PP_PrivateButtonType::PP_PRIVATEBUTTON_RADIOBUTTON;
page_objects_.form_fields.buttons.push_back(std::move(radio_button));
}
{
ppapi::PdfAccessibilityButtonInfo push_button;
push_button.bounds = PP_MakeFloatRectFromXYWH(1.0f, 4.0f, 5.0f, 6.0f);
push_button.index_in_page = 3;
push_button.text_run_index = 2;
push_button.name = "Push Button";
push_button.is_read_only = false;
push_button.type = PP_PrivateButtonType::PP_PRIVATEBUTTON_PUSHBUTTON;
page_objects_.form_fields.buttons.push_back(std::move(push_button));
}
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
* ++++++ Check Box
* ++++++ Radio Button
* ++++++ Radio Button
* ++++++ Button
*/
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(5u, 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* check_box_node = child_nodes[1];
ASSERT_TRUE(check_box_node);
EXPECT_EQ(ax::mojom::Role::kCheckBox, check_box_node->data().role);
EXPECT_EQ("Read Only Checkbox", check_box_node->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_EQ("Yes", check_box_node->GetStringAttribute(
ax::mojom::StringAttribute::kValue));
EXPECT_EQ(ax::mojom::CheckedState::kTrue,
check_box_node->data().GetCheckedState());
EXPECT_EQ(1,
check_box_node->GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
EXPECT_EQ(
1, check_box_node->GetIntAttribute(ax::mojom::IntAttribute::kPosInSet));
EXPECT_EQ(ax::mojom::Restriction::kReadOnly,
check_box_node->data().GetRestriction());
EXPECT_EQ(gfx::RectF(1.0f, 1.0f, 5.0f, 6.0f),
check_box_node->data().relative_bounds.bounds);
EXPECT_EQ(0u, check_box_node->children().size());
ui::AXNode* radio_button_node = child_nodes[2];
ASSERT_TRUE(radio_button_node);
EXPECT_EQ(ax::mojom::Role::kRadioButton, radio_button_node->data().role);
EXPECT_EQ("Radio Button", radio_button_node->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_EQ("value 1", radio_button_node->GetStringAttribute(
ax::mojom::StringAttribute::kValue));
EXPECT_EQ(ax::mojom::CheckedState::kNone,
radio_button_node->data().GetCheckedState());
EXPECT_EQ(
2, radio_button_node->GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
EXPECT_EQ(1, radio_button_node->GetIntAttribute(
ax::mojom::IntAttribute::kPosInSet));
EXPECT_NE(ax::mojom::Restriction::kReadOnly,
radio_button_node->data().GetRestriction());
EXPECT_EQ(gfx::RectF(1.0f, 2.0f, 5.0f, 6.0f),
radio_button_node->data().relative_bounds.bounds);
EXPECT_EQ(0u, radio_button_node->children().size());
radio_button_node = child_nodes[3];
ASSERT_TRUE(radio_button_node);
EXPECT_EQ(ax::mojom::Role::kRadioButton, radio_button_node->data().role);
EXPECT_EQ("Radio Button", radio_button_node->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_EQ("value 2", radio_button_node->GetStringAttribute(
ax::mojom::StringAttribute::kValue));
EXPECT_EQ(ax::mojom::CheckedState::kTrue,
radio_button_node->data().GetCheckedState());
EXPECT_EQ(
2, radio_button_node->GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
EXPECT_EQ(2, radio_button_node->GetIntAttribute(
ax::mojom::IntAttribute::kPosInSet));
EXPECT_EQ(ax::mojom::Restriction::kNone,
radio_button_node->data().GetRestriction());
EXPECT_EQ(gfx::RectF(1.0f, 3.0f, 5.0f, 6.0f),
radio_button_node->data().relative_bounds.bounds);
EXPECT_EQ(0u, radio_button_node->children().size());
ui::AXNode* push_button_node = child_nodes[4];
ASSERT_TRUE(push_button_node);
EXPECT_EQ(ax::mojom::Role::kButton, push_button_node->data().role);
EXPECT_EQ("Push Button", push_button_node->GetStringAttribute(
ax::mojom::StringAttribute::kName));
EXPECT_EQ(gfx::RectF(1.0f, 4.0f, 5.0f, 6.0f),
push_button_node->data().relative_bounds.bounds);
EXPECT_EQ(0u, push_button_node->children().size());
}
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