Skip to content

Commit

Permalink
Implement PdfAXActionTarget::SetSelection method
Browse files Browse the repository at this point in the history
Added a new action PP_PDF_SET_SELECTION to PP_PdfAccessibilityAction and
selection specific data in PP_PdfAccessibilityActionData. Implemented
PdfAXActionTarget::SetSelection method to send selection data to plugin.
PDFiumEngine handles PP_PDF_SET_SELECTION action and set the selection
in plugin. Added the tests to validate if write action data is sent from
Mimehandler to Plugin for selection and also if the action is correctly
executed in Plugin.

Bug: 1023283
Change-Id: I9112698757d7516d38f29e88174f10da71673b1c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1934127
Commit-Queue: Mansi Awasthi <maawas@microsoft.com>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Kevin Babbitt <kbabbitt@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#727044}
  • Loading branch information
maawas authored and Commit Bot committed Dec 21, 2019
1 parent 4e8b693 commit abbdd54
Show file tree
Hide file tree
Showing 12 changed files with 333 additions and 28 deletions.
57 changes: 39 additions & 18 deletions components/pdf/renderer/pdf_accessibility_tree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,8 @@ void PdfAccessibilityTree::AddPageContent(
uint32_t link_end_text_run_index =
std::min(end_text_run_index, text_runs.size()) - 1;
AddTextToAXNode(link.text_run_index, link_end_text_run_index, text_runs,
chars, page_bounds, text_run_start_indices, link_node,
&previous_on_line_node);
chars, page_bounds, page_index, text_run_start_indices,
link_node, &previous_on_line_node);

para_node->relative_bounds.bounds.Union(
link_node->relative_bounds.bounds);
Expand Down Expand Up @@ -557,28 +557,31 @@ void PdfAccessibilityTree::AddPageContent(
uint32_t highlight_end_text_run_index =
std::min(end_text_run_index, text_runs.size()) - 1;
AddTextToAXNode(highlight.text_run_index, highlight_end_text_run_index,
text_runs, chars, page_bounds, text_run_start_indices,
highlight_node, &previous_on_line_node);
text_runs, chars, page_bounds, page_index,
text_run_start_indices, highlight_node,
&previous_on_line_node);

para_node->relative_bounds.bounds.Union(
highlight_node->relative_bounds.bounds);

text_run_index =
std::max<size_t>(highlight_end_text_run_index, text_run_index);
} else {
PP_PdfPageCharacterIndex page_char_index = {
page_index, text_run_start_indices[text_run_index]};

// This node is for the text inside the paragraph, it includes
// the text of all of the text runs.
if (!static_text_node) {
static_text_node =
CreateStaticTextNode(text_run_start_indices[text_run_index]);
static_text_node = CreateStaticTextNode(page_char_index);
para_node->child_ids.push_back(static_text_node->id);
}

const ppapi::PdfAccessibilityTextRunInfo& text_run =
text_runs[text_run_index];
// Add this text run to the current static text node.
ui::AXNodeData* inline_text_box_node = CreateInlineTextBoxNode(
text_run, chars, text_run_start_indices[text_run_index], page_bounds);
text_run, chars, page_char_index, page_bounds);
static_text_node->child_ids.push_back(inline_text_box_node->id);

static_text += inline_text_box_node->GetStringAttribute(
Expand Down Expand Up @@ -710,8 +713,8 @@ void PdfAccessibilityTree::FindNodeOffset(uint32_t page_index,
continue;
// Look up the page-relative character index for static nodes from a map
// we built while the document was initially built.
DCHECK(base::Contains(node_id_to_char_index_in_page_, static_text->id()));
uint32_t char_index = node_id_to_char_index_in_page_[static_text->id()];
auto iter = node_id_to_page_char_index_.find(static_text->id());
uint32_t char_index = iter->second.char_index;
uint32_t len = static_text->data()
.GetStringAttribute(ax::mojom::StringAttribute::kName)
.size();
Expand All @@ -727,6 +730,18 @@ void PdfAccessibilityTree::FindNodeOffset(uint32_t page_index,
}
}

bool PdfAccessibilityTree::FindCharacterOffset(
const ui::AXNode& node,
uint32_t char_offset_in_node,
PP_PdfPageCharacterIndex* page_char_index) const {
auto iter = node_id_to_page_char_index_.find(GetId(&node));
if (iter == node_id_to_page_char_index_.end())
return false;
page_char_index->char_index = iter->second.char_index + char_offset_in_node;
page_char_index->page_index = iter->second.page_index;
return true;
}

std::string PdfAccessibilityTree::GetTextRunCharsAsUTF8(
const ppapi::PdfAccessibilityTextRunInfo& text_run,
const std::vector<PP_PrivateAccessibilityCharInfo>& chars,
Expand Down Expand Up @@ -799,21 +814,22 @@ ui::AXNodeData* PdfAccessibilityTree::CreateParagraphNode(
}

ui::AXNodeData* PdfAccessibilityTree::CreateStaticTextNode(
uint32_t char_index) {
const PP_PdfPageCharacterIndex& page_char_index) {
ui::AXNodeData* static_text_node = CreateNode(ax::mojom::Role::kStaticText);
node_id_to_char_index_in_page_[static_text_node->id] = char_index;
node_id_to_page_char_index_.emplace(static_text_node->id, page_char_index);
return static_text_node;
}

ui::AXNodeData* PdfAccessibilityTree::CreateInlineTextBoxNode(
const ppapi::PdfAccessibilityTextRunInfo& text_run,
const std::vector<PP_PrivateAccessibilityCharInfo>& chars,
uint32_t char_index,
const PP_PdfPageCharacterIndex& page_char_index,
const gfx::RectF& page_bounds) {
ui::AXNodeData* inline_text_box_node =
CreateNode(ax::mojom::Role::kInlineTextBox);

std::string chars_utf8 = GetTextRunCharsAsUTF8(text_run, chars, char_index);
std::string chars_utf8 =
GetTextRunCharsAsUTF8(text_run, chars, page_char_index.char_index);
inline_text_box_node->AddStringAttribute(ax::mojom::StringAttribute::kName,
chars_utf8);
inline_text_box_node->AddIntAttribute(ax::mojom::IntAttribute::kTextDirection,
Expand All @@ -839,10 +855,12 @@ ui::AXNodeData* PdfAccessibilityTree::CreateInlineTextBoxNode(
inline_text_box_node->relative_bounds.bounds =
ToGfxRectF(text_run.bounds) + page_bounds.OffsetFromOrigin();
std::vector<int32_t> char_offsets =
GetTextRunCharOffsets(text_run, chars, char_index);
GetTextRunCharOffsets(text_run, chars, page_char_index.char_index);
inline_text_box_node->AddIntListAttribute(
ax::mojom::IntListAttribute::kCharacterOffsets, char_offsets);
AddWordStartsAndEnds(inline_text_box_node);
node_id_to_page_char_index_.emplace(inline_text_box_node->id,
page_char_index);
return inline_text_box_node;
}

Expand Down Expand Up @@ -897,11 +915,13 @@ void PdfAccessibilityTree::AddTextToAXNode(
const std::vector<ppapi::PdfAccessibilityTextRunInfo>& text_runs,
const std::vector<PP_PrivateAccessibilityCharInfo>& chars,
const gfx::RectF& page_bounds,
uint32_t page_index,
const std::vector<uint32_t>& text_run_start_indices,
ui::AXNodeData* ax_node,
ui::AXNodeData** previous_on_line_node) {
ui::AXNodeData* ax_static_text_node =
CreateStaticTextNode(text_run_start_indices[start_text_run_index]);
PP_PdfPageCharacterIndex page_char_index = {
page_index, text_run_start_indices[start_text_run_index]};
ui::AXNodeData* ax_static_text_node = CreateStaticTextNode(page_char_index);
ax_node->child_ids.push_back(ax_static_text_node->id);
// Accumulate the text of the node.
std::string ax_name;
Expand All @@ -911,9 +931,10 @@ void PdfAccessibilityTree::AddTextToAXNode(
text_run_index <= end_text_run_index; ++text_run_index) {
const ppapi::PdfAccessibilityTextRunInfo& text_run =
text_runs[text_run_index];
page_char_index.char_index = text_run_start_indices[text_run_index];
// Add this text run to the current static text node.
ui::AXNodeData* inline_text_box_node = CreateInlineTextBoxNode(
text_run, chars, text_run_start_indices[text_run_index], page_bounds);
ui::AXNodeData* inline_text_box_node =
CreateInlineTextBoxNode(text_run, chars, page_char_index, page_bounds);
ax_static_text_node->child_ids.push_back(inline_text_box_node->id);

ax_static_text_node->relative_bounds.bounds.Union(
Expand Down
25 changes: 19 additions & 6 deletions components/pdf/renderer/pdf_accessibility_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ class PdfAccessibilityTree : public content::PluginAXTreeSource {
base::Optional<AnnotationInfo> GetPdfAnnotationInfoFromAXNode(
int32_t ax_node_id) const;

// Given the AXNode and the character offset within the AXNode, finds the
// respective page index and character index within the page. Returns
// false if the |node| is not a valid static text or inline text box
// AXNode. Used to find the character offsets of selection.
bool FindCharacterOffset(const ui::AXNode& node,
uint32_t char_offset_in_node,
PP_PdfPageCharacterIndex* page_char_index) const;

// PluginAXTreeSource implementation.
bool GetTreeData(ui::AXTreeData* tree_data) const override;
ui::AXNode* GetRoot() const override;
Expand Down Expand Up @@ -130,11 +138,12 @@ class PdfAccessibilityTree : public content::PluginAXTreeSource {
ui::AXNodeData* CreateNode(ax::mojom::Role role);
ui::AXNodeData* CreateParagraphNode(float font_size,
float heading_font_size_threshold);
ui::AXNodeData* CreateStaticTextNode(uint32_t char_index);
ui::AXNodeData* CreateStaticTextNode(
const PP_PdfPageCharacterIndex& page_char_index);
ui::AXNodeData* CreateInlineTextBoxNode(
const ppapi::PdfAccessibilityTextRunInfo& text_run,
const std::vector<PP_PrivateAccessibilityCharInfo>& chars,
uint32_t char_index,
const PP_PdfPageCharacterIndex& page_char_index,
const gfx::RectF& page_bounds);
ui::AXNodeData* CreateLinkNode(const ppapi::PdfAccessibilityLinkInfo& link,
uint32_t page_index);
Expand All @@ -148,6 +157,7 @@ class PdfAccessibilityTree : public content::PluginAXTreeSource {
const std::vector<ppapi::PdfAccessibilityTextRunInfo>& text_runs,
const std::vector<PP_PrivateAccessibilityCharInfo>& chars,
const gfx::RectF& page_bounds,
uint32_t page_index,
const std::vector<uint32_t>& text_run_start_indices,
ui::AXNodeData* ax_node,
ui::AXNodeData** previous_on_line_node);
Expand Down Expand Up @@ -181,10 +191,13 @@ class PdfAccessibilityTree : public content::PluginAXTreeSource {
PP_PrivateAccessibilityDocInfo doc_info_;
ui::AXNodeData* doc_node_;
std::vector<std::unique_ptr<ui::AXNodeData>> nodes_;
// Map from the id of each static text AXNode to the index of the
// character within its page. Used to find the node associated with
// the start or end of a selection.
std::map<int32_t, uint32_t> node_id_to_char_index_in_page_;

// Map from the id of each static text AXNode and inline text box
// AXNode to the page index and index of the character within its
// page. Used to find the node associated with the start or end of
// a selection and vice-versa.
std::map<int32_t, PP_PdfPageCharacterIndex> node_id_to_page_char_index_;

// Map between AXNode id to annotation object. Used to find the annotation
// object to which an action can be passed.
std::map<int32_t, AnnotationInfo> node_id_to_annotation_info_;
Expand Down
94 changes: 93 additions & 1 deletion components/pdf/renderer/pdf_accessibility_tree_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,6 @@ TEST_F(PdfAccessibilityTreeTest, TestEmptyPdfAxActions) {
EXPECT_FALSE(pdf_action_target->SetAccessibilityFocus());
EXPECT_FALSE(pdf_action_target->SetSelected(true));
EXPECT_FALSE(pdf_action_target->SetSelected(false));
EXPECT_FALSE(pdf_action_target->SetSelection(nullptr, 0, nullptr, 0));
EXPECT_FALSE(pdf_action_target->SetSequentialFocusNavigationStartingPoint());
EXPECT_FALSE(pdf_action_target->SetValue("test"));
EXPECT_FALSE(pdf_action_target->ShowContextMenu());
Expand Down Expand Up @@ -1094,4 +1093,97 @@ TEST_F(PdfAccessibilityTreeTest, TestZoomAndScaleChanges) {
CompareRect({{124.5f, 339.5f}, {126.0f, 19.5f}}, rect);
}

TEST_F(PdfAccessibilityTreeTest, TestSelectionActionDataConversion) {
text_runs_.emplace_back(kFirstTextRun);
text_runs_.emplace_back(kSecondTextRun);
chars_.insert(chars_.end(), std::begin(kDummyCharsData),
std::end(kDummyCharsData));
page_info_.text_run_count = text_runs_.size();
page_info_.char_count = chars_.size();
content::RenderFrame* render_frame = view_->GetMainRenderFrame();
render_frame->SetAccessibilityModeForTest(ui::AXMode::kWebContents);
ASSERT_TRUE(render_frame->GetRenderAccessibility());
ActionHandlingFakePepperPluginInstance fake_pepper_instance;
FakeRendererPpapiHost host(view_->GetMainRenderFrame(),
&fake_pepper_instance);
PP_Instance instance = 0;
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_);
ui::AXNode* root_node = pdf_accessibility_tree.GetRoot();
ASSERT_TRUE(root_node);
const std::vector<ui::AXNode*>& page_nodes = root_node->children();
ASSERT_EQ(1u, page_nodes.size());
ASSERT_TRUE(page_nodes[0]);
const std::vector<ui::AXNode*>& para_nodes = page_nodes[0]->children();
ASSERT_EQ(2u, para_nodes.size());
ASSERT_TRUE(para_nodes[0]);
const std::vector<ui::AXNode*>& static_text_nodes1 =
para_nodes[0]->children();
ASSERT_EQ(1u, static_text_nodes1.size());
ASSERT_TRUE(static_text_nodes1[0]);
const std::vector<ui::AXNode*>& inline_text_nodes1 =
static_text_nodes1[0]->children();
ASSERT_TRUE(inline_text_nodes1[0]);
ASSERT_EQ(1u, inline_text_nodes1.size());
ASSERT_TRUE(para_nodes[1]);
const std::vector<ui::AXNode*>& static_text_nodes2 =
para_nodes[1]->children();
ASSERT_EQ(1u, static_text_nodes2.size());
ASSERT_TRUE(static_text_nodes2[0]);
const std::vector<ui::AXNode*>& inline_text_nodes2 =
static_text_nodes2[0]->children();
ASSERT_TRUE(inline_text_nodes2[0]);
ASSERT_EQ(1u, inline_text_nodes2.size());

std::unique_ptr<ui::AXActionTarget> pdf_anchor_action_target =
pdf_accessibility_tree.CreateActionTarget(*inline_text_nodes1[0]);
ASSERT_EQ(ui::AXActionTarget::Type::kPdf,
pdf_anchor_action_target->GetType());
std::unique_ptr<ui::AXActionTarget> pdf_focus_action_target =
pdf_accessibility_tree.CreateActionTarget(*inline_text_nodes2[0]);
ASSERT_EQ(ui::AXActionTarget::Type::kPdf, pdf_focus_action_target->GetType());
EXPECT_TRUE(pdf_anchor_action_target->SetSelection(
pdf_anchor_action_target.get(), 1, pdf_focus_action_target.get(), 5));

PP_PdfAccessibilityActionData pdf_action_data =
fake_pepper_instance.GetReceivedActionData();
EXPECT_EQ(PP_PdfAccessibilityAction::PP_PDF_SET_SELECTION,
pdf_action_data.action);
EXPECT_EQ(0u, pdf_action_data.selection_start_index.page_index);
EXPECT_EQ(1u, pdf_action_data.selection_start_index.char_index);
EXPECT_EQ(0u, pdf_action_data.selection_end_index.page_index);
EXPECT_EQ(20u, pdf_action_data.selection_end_index.char_index);

pdf_anchor_action_target =
pdf_accessibility_tree.CreateActionTarget(*static_text_nodes1[0]);
ASSERT_EQ(ui::AXActionTarget::Type::kPdf,
pdf_anchor_action_target->GetType());
pdf_focus_action_target =
pdf_accessibility_tree.CreateActionTarget(*inline_text_nodes2[0]);
ASSERT_EQ(ui::AXActionTarget::Type::kPdf, pdf_focus_action_target->GetType());
EXPECT_TRUE(pdf_anchor_action_target->SetSelection(
pdf_anchor_action_target.get(), 1, pdf_focus_action_target.get(), 4));

pdf_action_data = fake_pepper_instance.GetReceivedActionData();
EXPECT_EQ(PP_PdfAccessibilityAction::PP_PDF_SET_SELECTION,
pdf_action_data.action);
EXPECT_EQ(0u, pdf_action_data.selection_start_index.page_index);
EXPECT_EQ(1u, pdf_action_data.selection_start_index.char_index);
EXPECT_EQ(0u, pdf_action_data.selection_end_index.page_index);
EXPECT_EQ(19u, pdf_action_data.selection_end_index.char_index);

pdf_anchor_action_target =
pdf_accessibility_tree.CreateActionTarget(*para_nodes[0]);
ASSERT_EQ(ui::AXActionTarget::Type::kPdf,
pdf_anchor_action_target->GetType());
pdf_focus_action_target =
pdf_accessibility_tree.CreateActionTarget(*para_nodes[1]);
ASSERT_EQ(ui::AXActionTarget::Type::kPdf, pdf_focus_action_target->GetType());
EXPECT_FALSE(pdf_anchor_action_target->SetSelection(
pdf_anchor_action_target.get(), 1, pdf_focus_action_target.get(), 5));
}

} // namespace pdf
31 changes: 30 additions & 1 deletion components/pdf/renderer/pdf_ax_action_target.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ PP_PdfAccessibilityScrollAlignment ConvertAXScrollToPdfScrollAlignment(

} // namespace

// static
const PdfAXActionTarget* PdfAXActionTarget::FromAXActionTarget(
const ui::AXActionTarget* ax_action_target) {
if (ax_action_target &&
ax_action_target->GetType() == ui::AXActionTarget::Type::kPdf) {
return static_cast<const PdfAXActionTarget*>(ax_action_target);
}

return nullptr;
}

PdfAXActionTarget::PdfAXActionTarget(const ui::AXNode& plugin_node,
PdfAccessibilityTree* pdf_tree_source)
: target_plugin_node_(plugin_node),
Expand Down Expand Up @@ -116,7 +127,25 @@ bool PdfAXActionTarget::SetSelection(const ui::AXActionTarget* anchor_object,
int anchor_offset,
const ui::AXActionTarget* focus_object,
int focus_offset) const {
return false;
const PdfAXActionTarget* pdf_anchor_object =
FromAXActionTarget(anchor_object);
const PdfAXActionTarget* pdf_focus_object = FromAXActionTarget(focus_object);
if (!pdf_anchor_object || !pdf_focus_object || anchor_offset < 0 ||
focus_offset < 0) {
return false;
}
PP_PdfAccessibilityActionData pdf_action_data = {};
if (!pdf_accessibility_tree_source_->FindCharacterOffset(
pdf_anchor_object->AXNode(), anchor_offset,
&pdf_action_data.selection_start_index) ||
!pdf_accessibility_tree_source_->FindCharacterOffset(
pdf_focus_object->AXNode(), focus_offset,
&pdf_action_data.selection_end_index)) {
return false;
}
pdf_action_data.action = PP_PdfAccessibilityAction::PP_PDF_SET_SELECTION;
pdf_accessibility_tree_source_->HandleAction(pdf_action_data);
return true;
}

bool PdfAXActionTarget::SetSequentialFocusNavigationStartingPoint() const {
Expand Down
5 changes: 5 additions & 0 deletions components/pdf/renderer/pdf_ax_action_target.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ class PdfAccessibilityTree;
// accessibility actions.
class PdfAXActionTarget : public ui::AXActionTarget {
public:
static const PdfAXActionTarget* FromAXActionTarget(
const ui::AXActionTarget* ax_action_target);

PdfAXActionTarget(const ui::AXNode& plugin_node, PdfAccessibilityTree* tree);
~PdfAXActionTarget() override;

const ui::AXNode& AXNode() const { return target_plugin_node_; }

protected:
// AXActionTarget overrides.
Type GetType() const override;
Expand Down
Loading

0 comments on commit abbdd54

Please sign in to comment.