From abbdd54e285a99398d605396897d879620e150f2 Mon Sep 17 00:00:00 2001 From: Mansi Awasthi Date: Sat, 21 Dec 2019 11:17:54 +0000 Subject: [PATCH] Implement PdfAXActionTarget::SetSelection method 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 Reviewed-by: Daniel Cheng Reviewed-by: Lei Zhang Reviewed-by: Kevin Babbitt Cr-Commit-Position: refs/heads/master@{#727044} --- .../pdf/renderer/pdf_accessibility_tree.cc | 57 +++++++---- .../pdf/renderer/pdf_accessibility_tree.h | 25 +++-- .../pdf_accessibility_tree_browsertest.cc | 94 ++++++++++++++++++- .../pdf/renderer/pdf_ax_action_target.cc | 31 +++++- .../pdf/renderer/pdf_ax_action_target.h | 5 + pdf/pdfium/accessibility_unittest.cc | 61 ++++++++++++ pdf/pdfium/pdfium_engine.cc | 48 ++++++++++ pdf/pdfium/pdfium_engine.h | 7 ++ pdf/pdfium/pdfium_page.cc | 4 + pdf/pdfium/pdfium_page.h | 4 + ppapi/c/private/ppp_pdf.h | 18 +++- ppapi/proxy/ppapi_messages.h | 7 ++ 12 files changed, 333 insertions(+), 28 deletions(-) diff --git a/components/pdf/renderer/pdf_accessibility_tree.cc b/components/pdf/renderer/pdf_accessibility_tree.cc index 63d6d3220d0294..c100e0884bbef7 100644 --- a/components/pdf/renderer/pdf_accessibility_tree.cc +++ b/components/pdf/renderer/pdf_accessibility_tree.cc @@ -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); @@ -557,8 +557,9 @@ 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); @@ -566,11 +567,13 @@ void PdfAccessibilityTree::AddPageContent( text_run_index = std::max(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); } @@ -578,7 +581,7 @@ void PdfAccessibilityTree::AddPageContent( 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( @@ -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(); @@ -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& chars, @@ -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& 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, @@ -839,10 +855,12 @@ ui::AXNodeData* PdfAccessibilityTree::CreateInlineTextBoxNode( inline_text_box_node->relative_bounds.bounds = ToGfxRectF(text_run.bounds) + page_bounds.OffsetFromOrigin(); std::vector 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; } @@ -897,11 +915,13 @@ void PdfAccessibilityTree::AddTextToAXNode( const std::vector& text_runs, const std::vector& chars, const gfx::RectF& page_bounds, + uint32_t page_index, const std::vector& 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; @@ -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( diff --git a/components/pdf/renderer/pdf_accessibility_tree.h b/components/pdf/renderer/pdf_accessibility_tree.h index 38e979d64628cb..39a78f06037d52 100644 --- a/components/pdf/renderer/pdf_accessibility_tree.h +++ b/components/pdf/renderer/pdf_accessibility_tree.h @@ -67,6 +67,14 @@ class PdfAccessibilityTree : public content::PluginAXTreeSource { base::Optional 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; @@ -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& 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); @@ -148,6 +157,7 @@ class PdfAccessibilityTree : public content::PluginAXTreeSource { const std::vector& text_runs, const std::vector& chars, const gfx::RectF& page_bounds, + uint32_t page_index, const std::vector& text_run_start_indices, ui::AXNodeData* ax_node, ui::AXNodeData** previous_on_line_node); @@ -181,10 +191,13 @@ class PdfAccessibilityTree : public content::PluginAXTreeSource { PP_PrivateAccessibilityDocInfo doc_info_; ui::AXNodeData* doc_node_; std::vector> 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 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 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 node_id_to_annotation_info_; diff --git a/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc b/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc index ce408e847d3abc..8d8e4d3030bc6c 100644 --- a/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc +++ b/components/pdf/renderer/pdf_accessibility_tree_browsertest.cc @@ -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()); @@ -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& page_nodes = root_node->children(); + ASSERT_EQ(1u, page_nodes.size()); + ASSERT_TRUE(page_nodes[0]); + const std::vector& para_nodes = page_nodes[0]->children(); + ASSERT_EQ(2u, para_nodes.size()); + ASSERT_TRUE(para_nodes[0]); + const std::vector& static_text_nodes1 = + para_nodes[0]->children(); + ASSERT_EQ(1u, static_text_nodes1.size()); + ASSERT_TRUE(static_text_nodes1[0]); + const std::vector& 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& static_text_nodes2 = + para_nodes[1]->children(); + ASSERT_EQ(1u, static_text_nodes2.size()); + ASSERT_TRUE(static_text_nodes2[0]); + const std::vector& inline_text_nodes2 = + static_text_nodes2[0]->children(); + ASSERT_TRUE(inline_text_nodes2[0]); + ASSERT_EQ(1u, inline_text_nodes2.size()); + + std::unique_ptr 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 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 diff --git a/components/pdf/renderer/pdf_ax_action_target.cc b/components/pdf/renderer/pdf_ax_action_target.cc index 5c455d7deb6270..6ca7c61c9f32d6 100644 --- a/components/pdf/renderer/pdf_ax_action_target.cc +++ b/components/pdf/renderer/pdf_ax_action_target.cc @@ -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(ax_action_target); + } + + return nullptr; +} + PdfAXActionTarget::PdfAXActionTarget(const ui::AXNode& plugin_node, PdfAccessibilityTree* pdf_tree_source) : target_plugin_node_(plugin_node), @@ -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 { diff --git a/components/pdf/renderer/pdf_ax_action_target.h b/components/pdf/renderer/pdf_ax_action_target.h index 37554e9b9c679e..b8d90cc65f53d1 100644 --- a/components/pdf/renderer/pdf_ax_action_target.h +++ b/components/pdf/renderer/pdf_ax_action_target.h @@ -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; diff --git a/pdf/pdfium/accessibility_unittest.cc b/pdf/pdfium/accessibility_unittest.cc index af575fa217da2c..060715d24430ba 100644 --- a/pdf/pdfium/accessibility_unittest.cc +++ b/pdf/pdfium/accessibility_unittest.cc @@ -518,4 +518,65 @@ TEST_F(AccessibilityTest, GetAccessibilityHighlightInfo) { } } +TEST_F(AccessibilityTest, TestSelectionActionHandling) { + struct Selection { + uint32_t start_page_index; + uint32_t start_char_index; + uint32_t end_page_index; + uint32_t end_char_index; + }; + + struct TestCase { + Selection action; + Selection expected_result; + }; + + static constexpr TestCase kTestCases[] = { + {{0, 0, 0, 0}, {0, 0, 0, 0}}, + {{0, 0, 1, 5}, {0, 0, 1, 5}}, + // Selection action data with invalid char index. + // GetSelection() should return the previous selection in this case. + {{0, 0, 0, 50}, {0, 0, 1, 5}}, + // Selection action data for reverse selection where start selection + // index is greater than end selection index. GetSelection() should + // return the sanitized selection value where start selection index + // is less than end selection index. + {{1, 10, 0, 5}, {0, 5, 1, 10}}, + {{0, 10, 0, 4}, {0, 4, 0, 10}}, + // Selection action data with invalid page index. + // GetSelection() should return the previous selection in this case. + {{0, 10, 2, 4}, {0, 4, 0, 10}}, + }; + + TestClient client; + std::unique_ptr engine = + InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf")); + ASSERT_TRUE(engine); + + for (const auto& test_case : kTestCases) { + PP_PdfAccessibilityActionData action_data; + action_data.action = PP_PdfAccessibilityAction::PP_PDF_SET_SELECTION; + const Selection& sel_action = test_case.action; + action_data.selection_start_index.page_index = sel_action.start_page_index; + action_data.selection_start_index.char_index = sel_action.start_char_index; + action_data.selection_end_index.page_index = sel_action.end_page_index; + action_data.selection_end_index.char_index = sel_action.end_char_index; + + engine->HandleAccessibilityAction(action_data); + Selection actual_selection; + engine->GetSelection( + &actual_selection.start_page_index, &actual_selection.start_char_index, + &actual_selection.end_page_index, &actual_selection.end_char_index); + const Selection& expected_selection = test_case.expected_result; + EXPECT_EQ(actual_selection.start_page_index, + expected_selection.start_page_index); + EXPECT_EQ(actual_selection.start_char_index, + expected_selection.start_char_index); + EXPECT_EQ(actual_selection.end_page_index, + expected_selection.end_page_index); + EXPECT_EQ(actual_selection.end_char_index, + expected_selection.end_char_index); + } +} + } // namespace chrome_pdf diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc index 11e59e347d7689..351e54e0fd925c 100644 --- a/pdf/pdfium/pdfium_engine.cc +++ b/pdf/pdfium/pdfium_engine.cc @@ -2028,6 +2028,14 @@ void PDFiumEngine::HandleAccessibilityAction( ScrollToGlobalPoint(action_data.target_rect, action_data.target_point); break; } + case PP_PdfAccessibilityAction::PP_PDF_SET_SELECTION: { + if (IsPageCharacterIndexInBounds(action_data.selection_start_index) && + IsPageCharacterIndexInBounds(action_data.selection_end_index)) { + SetSelection(action_data.selection_start_index, + action_data.selection_end_index); + } + break; + } default: NOTREACHED(); break; @@ -3544,6 +3552,12 @@ bool PDFiumEngine::PageIndexInBounds(int index) const { return index >= 0 && index < static_cast(pages_.size()); } +bool PDFiumEngine::IsPageCharacterIndexInBounds( + const PP_PdfPageCharacterIndex& index) const { + return PageIndexInBounds(index.page_index) && + pages_[index.page_index]->IsCharIndexInBounds(index.char_index); +} + float PDFiumEngine::GetToolbarHeightInScreenCoords() { return client_->GetToolbarHeightInScreenCoords(); } @@ -3554,6 +3568,40 @@ FPDF_BOOL PDFiumEngine::Pause_NeedToPauseNow(IFSDK_PAUSE* param) { engine->progressive_paint_timeout_; } +void PDFiumEngine::SetSelection( + const PP_PdfPageCharacterIndex& selection_start_index, + const PP_PdfPageCharacterIndex& selection_end_index) { + SelectionChangeInvalidator selection_invalidator(this); + selection_.clear(); + + PP_PdfPageCharacterIndex sel_start_index = selection_start_index; + PP_PdfPageCharacterIndex sel_end_index = selection_end_index; + if (sel_end_index.page_index < sel_start_index.page_index) { + std::swap(sel_end_index.page_index, sel_start_index.page_index); + std::swap(sel_end_index.char_index, sel_start_index.char_index); + } + + if (sel_end_index.page_index == sel_start_index.page_index && + sel_end_index.char_index < sel_start_index.char_index) { + std::swap(sel_end_index.char_index, sel_start_index.char_index); + } + + for (uint32_t i = sel_start_index.page_index; i <= sel_end_index.page_index; + ++i) { + int32_t char_count = pages_[i]->GetCharCount(); + if (char_count <= 0) + continue; + int32_t start_char_index = 0; + int32_t end_char_index = char_count; + if (i == sel_start_index.page_index) + start_char_index = sel_start_index.char_index; + if (i == sel_end_index.page_index) + end_char_index = sel_end_index.char_index; + selection_.push_back(PDFiumRange(pages_[i].get(), start_char_index, + end_char_index - start_char_index)); + } +} + void PDFiumEngine::SetCaretPosition(const pp::Point& position) { // TODO(dsinclair): Handle caret position ... } diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h index b66953773406fd..7b4dbbc6618afc 100644 --- a/pdf/pdfium/pdfium_engine.h +++ b/pdf/pdfium/pdfium_engine.h @@ -515,6 +515,8 @@ class PDFiumEngine : public PDFEngine, int form_type); bool PageIndexInBounds(int index) const; + bool IsPageCharacterIndexInBounds( + const PP_PdfPageCharacterIndex& index) const; // Gets the height of the top toolbar in screen coordinates. This is // independent of whether it is hidden or not at the moment. @@ -553,6 +555,11 @@ class PDFiumEngine : public PDFEngine, // IFSDK_PAUSE callbacks static FPDF_BOOL Pause_NeedToPauseNow(IFSDK_PAUSE* param); + // Used for text selection. Given the start and end of selection, sets the + // text range in |selection_|. + void SetSelection(const PP_PdfPageCharacterIndex& selection_start_index, + const PP_PdfPageCharacterIndex& selection_end_index); + PDFEngine::Client* const client_; // The current document layout. diff --git a/pdf/pdfium/pdfium_page.cc b/pdf/pdfium/pdfium_page.cc index 0a1cec5427afce..e6f48534aa745f 100644 --- a/pdf/pdfium/pdfium_page.cc +++ b/pdf/pdfium/pdfium_page.cc @@ -691,6 +691,10 @@ int PDFiumPage::GetCharCount() { return FPDFText_CountChars(GetTextPage()); } +bool PDFiumPage::IsCharIndexInBounds(int index) { + return index >= 0 && index < GetCharCount(); +} + PDFiumPage::Area PDFiumPage::GetLinkTarget(FPDF_LINK link, LinkTarget* target) { FPDF_DEST dest_link = FPDFLink_GetDest(engine_->doc(), link); if (dest_link) diff --git a/pdf/pdfium/pdfium_page.h b/pdf/pdfium/pdfium_page.h index 806a7cc060ccbb..23a8cfe8caccf3 100644 --- a/pdf/pdfium/pdfium_page.h +++ b/pdf/pdfium/pdfium_page.h @@ -122,6 +122,10 @@ class PDFiumPage { // Gets the number of characters in the page. int GetCharCount(); + // Returns true if the given |char_index| lies within the character range + // of the page. + bool IsCharIndexInBounds(int char_index); + // Given a rectangle in page coordinates, computes the range of continuous // characters which lie inside that rectangle. Returns false without // modifying the out parameters if no character lies inside the rectangle. diff --git a/ppapi/c/private/ppp_pdf.h b/ppapi/c/private/ppp_pdf.h index da1c5a3aeec084..8c59b35255f4d1 100644 --- a/ppapi/c/private/ppp_pdf.h +++ b/ppapi/c/private/ppp_pdf.h @@ -69,8 +69,10 @@ typedef enum { PP_PDF_DO_DEFAULT_ACTION = 2, // Action specifying a command to scroll to the global point. PP_PDF_SCROLL_TO_GLOBAL_POINT = 3, + // Sets text selection. + PP_PDF_SET_SELECTION = 4, // Last enum value marker. - PP_PDF_ACCESSIBILITYACTION_LAST = PP_PDF_SCROLL_TO_GLOBAL_POINT + PP_PDF_ACCESSIBILITYACTION_LAST = PP_PDF_SET_SELECTION } PP_PdfAccessibilityAction; PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_PdfAccessibilityAction, 4); @@ -105,6 +107,14 @@ typedef enum { } PP_PdfAccessibilityAnnotationType; PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_PdfAccessibilityAnnotationType, 4); +struct PP_PdfPageCharacterIndex { + // Index of PDF page. + uint32_t page_index; + // Character index within the PDF page. + uint32_t char_index; +}; +PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_PdfPageCharacterIndex, 8); + struct PP_PdfAccessibilityActionData { // Accessibility action type. PP_PdfAccessibilityAction action; @@ -122,8 +132,12 @@ struct PP_PdfAccessibilityActionData { PP_PdfAccessibilityScrollAlignment horizontal_scroll_alignment; // Vertical scroll alignment with respect to the viewport PP_PdfAccessibilityScrollAlignment vertical_scroll_alignment; + // Page and character index of start of selection. + PP_PdfPageCharacterIndex selection_start_index; + // Page and character index of exclusive end of selection. + PP_PdfPageCharacterIndex selection_end_index; }; -PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(PP_PdfAccessibilityActionData, 48); +PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(PP_PdfAccessibilityActionData, 64); struct PPP_Pdf_1_1 { // Returns an absolute URL if the position is over a link. diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index 2eaa68bee0d2ff..88911ac10645a4 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h @@ -243,6 +243,13 @@ IPC_STRUCT_TRAITS_BEGIN(PP_PdfAccessibilityActionData) IPC_STRUCT_TRAITS_MEMBER(page_index) IPC_STRUCT_TRAITS_MEMBER(horizontal_scroll_alignment) IPC_STRUCT_TRAITS_MEMBER(vertical_scroll_alignment) + IPC_STRUCT_TRAITS_MEMBER(selection_start_index) + IPC_STRUCT_TRAITS_MEMBER(selection_end_index) +IPC_STRUCT_TRAITS_END() + +IPC_STRUCT_TRAITS_BEGIN(PP_PdfPageCharacterIndex) + IPC_STRUCT_TRAITS_MEMBER(page_index) + IPC_STRUCT_TRAITS_MEMBER(char_index) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(PP_PdfPrintPresetOptions_Dev)