From c30a6232df03e1efbd9f3b226777b07e087a1122 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 12 Oct 2020 14:27:29 +0200 Subject: BASELINE: Update Chromium to 85.0.4183.140 Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen --- chromium/pdf/BUILD.gn | 6 +- chromium/pdf/accessibility.cc | 14 +- chromium/pdf/document_attachment_info.cc | 16 + chromium/pdf/document_attachment_info.h | 36 ++ chromium/pdf/document_layout.h | 2 +- chromium/pdf/geometry_conversions.cc | 23 - chromium/pdf/geometry_conversions.h | 23 - chromium/pdf/out_of_process_instance.cc | 614 ++++++++++++--------- chromium/pdf/out_of_process_instance.h | 26 +- chromium/pdf/pdf_engine.h | 15 +- chromium/pdf/pdf_features.cc | 14 +- chromium/pdf/pdf_features.h | 3 +- chromium/pdf/pdfium/accessibility_unittest.cc | 9 +- .../pdf/pdfium/pdfium_api_string_buffer_adapter.cc | 35 +- .../pdf/pdfium/pdfium_api_string_buffer_adapter.h | 80 ++- .../pdf/pdfium/pdfium_assert_matching_enums.cc | 15 + chromium/pdf/pdfium/pdfium_engine.cc | 234 +++++--- chromium/pdf/pdfium/pdfium_engine.h | 28 +- chromium/pdf/pdfium/pdfium_engine_unittest.cc | 156 +++++- chromium/pdf/pdfium/pdfium_form_filler.cc | 15 +- chromium/pdf/pdfium/pdfium_page.cc | 180 ++++-- chromium/pdf/pdfium/pdfium_page.h | 157 ++++-- chromium/pdf/pdfium/pdfium_page_unittest.cc | 166 +++++- chromium/pdf/pdfium/pdfium_permissions.cc | 2 + chromium/pdf/pdfium/pdfium_print.cc | 2 +- chromium/pdf/pdfium/pdfium_test_base.cc | 3 +- chromium/pdf/ppapi_migration/README.md | 5 + .../pdf/ppapi_migration/geometry_conversions.cc | 23 + .../pdf/ppapi_migration/geometry_conversions.h | 23 + chromium/pdf/preview_mode_client.cc | 3 +- 30 files changed, 1346 insertions(+), 582 deletions(-) create mode 100644 chromium/pdf/document_attachment_info.cc create mode 100644 chromium/pdf/document_attachment_info.h delete mode 100644 chromium/pdf/geometry_conversions.cc delete mode 100644 chromium/pdf/geometry_conversions.h create mode 100644 chromium/pdf/ppapi_migration/README.md create mode 100644 chromium/pdf/ppapi_migration/geometry_conversions.cc create mode 100644 chromium/pdf/ppapi_migration/geometry_conversions.h (limited to 'chromium/pdf') diff --git a/chromium/pdf/BUILD.gn b/chromium/pdf/BUILD.gn index 495d0258eb1..c0e96cb5df3 100644 --- a/chromium/pdf/BUILD.gn +++ b/chromium/pdf/BUILD.gn @@ -49,6 +49,8 @@ if (enable_pdf) { "accessibility.cc", "accessibility.h", "chunk_stream.h", + "document_attachment_info.cc", + "document_attachment_info.h", "document_layout.cc", "document_layout.h", "document_loader.h", @@ -60,8 +62,6 @@ if (enable_pdf) { "draw_utils/coordinates.h", "draw_utils/shadow.cc", "draw_utils/shadow.h", - "geometry_conversions.cc", - "geometry_conversions.h", "out_of_process_instance.cc", "out_of_process_instance.h", "page_orientation.cc", @@ -75,6 +75,8 @@ if (enable_pdf) { "pdf_ppapi.cc", "pdf_transform.cc", "pdf_transform.h", + "ppapi_migration/geometry_conversions.cc", + "ppapi_migration/geometry_conversions.h", "preview_mode_client.cc", "preview_mode_client.h", "range_set.cc", diff --git a/chromium/pdf/accessibility.cc b/chromium/pdf/accessibility.cc index 7cf9b5df260..ed259653f69 100644 --- a/chromium/pdf/accessibility.cc +++ b/chromium/pdf/accessibility.cc @@ -123,6 +123,7 @@ void GetAccessibilityHighlightInfo( highlight_info.index_in_page = i; highlight_info.bounds = std::move(cur_highlight_info.bounds); highlight_info.color = cur_highlight_info.color; + highlight_info.note_text = std::move(cur_highlight_info.note_text); if (!GetEnclosingTextRunRangeForCharRange( text_runs, cur_highlight_info.start_char_index, @@ -165,6 +166,15 @@ void GetAccessibilityTextFieldInfo( } } +void GetAccessibilityFormFieldInfo( + PDFEngine* engine, + int32_t page_index, + uint32_t text_run_count, + pp::PDF::PrivateAccessibilityFormFieldInfo* form_fields) { + GetAccessibilityTextFieldInfo(engine, page_index, text_run_count, + &form_fields->text_fields); +} + } // namespace bool GetAccessibilityInfo( @@ -253,8 +263,8 @@ bool GetAccessibilityInfo( &page_objects->images); GetAccessibilityHighlightInfo(engine, page_index, *text_runs, &page_objects->highlights); - GetAccessibilityTextFieldInfo(engine, page_index, page_info->text_run_count, - &page_objects->text_fields); + GetAccessibilityFormFieldInfo(engine, page_index, page_info->text_run_count, + &page_objects->form_fields); return true; } diff --git a/chromium/pdf/document_attachment_info.cc b/chromium/pdf/document_attachment_info.cc new file mode 100644 index 00000000000..c516cf6418d --- /dev/null +++ b/chromium/pdf/document_attachment_info.cc @@ -0,0 +1,16 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "pdf/document_attachment_info.h" + +namespace chrome_pdf { + +DocumentAttachmentInfo::DocumentAttachmentInfo() = default; + +DocumentAttachmentInfo::DocumentAttachmentInfo( + const DocumentAttachmentInfo& other) = default; + +DocumentAttachmentInfo::~DocumentAttachmentInfo() = default; + +} // namespace chrome_pdf diff --git a/chromium/pdf/document_attachment_info.h b/chromium/pdf/document_attachment_info.h new file mode 100644 index 00000000000..9c36e1e0644 --- /dev/null +++ b/chromium/pdf/document_attachment_info.h @@ -0,0 +1,36 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PDF_DOCUMENT_ATTACHMENT_INFO_H_ +#define PDF_DOCUMENT_ATTACHMENT_INFO_H_ + +#include "base/strings/string16.h" + +namespace chrome_pdf { + +struct DocumentAttachmentInfo { + DocumentAttachmentInfo(); + + DocumentAttachmentInfo(const DocumentAttachmentInfo& other); + + ~DocumentAttachmentInfo(); + + // The attachment's name. + base::string16 name; + + // The attachment's size in bytes. + uint32_t size_bytes = 0; + + // The creation date of the attachment. It stores the arbitrary string saved + // in field "CreationDate". + base::string16 creation_date; + + // Last modified date of the attachment. It stores the arbitrary string saved + // in field "ModDate". + base::string16 modified_date; +}; + +} // namespace chrome_pdf + +#endif // PDF_DOCUMENT_ATTACHMENT_INFO_H_ diff --git a/chromium/pdf/document_layout.h b/chromium/pdf/document_layout.h index c56cbc6bca7..e1907466314 100644 --- a/chromium/pdf/document_layout.h +++ b/chromium/pdf/document_layout.h @@ -8,7 +8,7 @@ #include #include -#include "base/logging.h" +#include "base/check_op.h" #include "pdf/draw_utils/coordinates.h" #include "pdf/page_orientation.h" #include "ppapi/cpp/rect.h" diff --git a/chromium/pdf/geometry_conversions.cc b/chromium/pdf/geometry_conversions.cc deleted file mode 100644 index 37a7bb0c358..00000000000 --- a/chromium/pdf/geometry_conversions.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "pdf/geometry_conversions.h" - -#include "ppapi/c/pp_rect.h" -#include "ppapi/c/pp_size.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/size.h" - -namespace chrome_pdf { - -gfx::Rect RectFromPPRect(const PP_Rect& pp_rect) { - return gfx::Rect(pp_rect.point.x, pp_rect.point.y, pp_rect.size.width, - pp_rect.size.height); -} - -gfx::Size SizeFromPPSize(const PP_Size& pp_size) { - return gfx::Size(pp_size.width, pp_size.height); -} - -} // namespace chrome_pdf diff --git a/chromium/pdf/geometry_conversions.h b/chromium/pdf/geometry_conversions.h deleted file mode 100644 index d9220034511..00000000000 --- a/chromium/pdf/geometry_conversions.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PDF_GEOMETRY_CONVERSIONS_H_ -#define PDF_GEOMETRY_CONVERSIONS_H_ - -struct PP_Rect; -struct PP_Size; - -namespace gfx { -class Rect; -class Size; -} // namespace gfx - -namespace chrome_pdf { - -gfx::Rect RectFromPPRect(const PP_Rect& pp_rect); -gfx::Size SizeFromPPSize(const PP_Size& pp_size); - -} // namespace chrome_pdf - -#endif // PDF_GEOMETRY_CONVERSIONS_H_ diff --git a/chromium/pdf/out_of_process_instance.cc b/chromium/pdf/out_of_process_instance.cc index 9c44da580b2..8214e9716ce 100644 --- a/chromium/pdf/out_of_process_instance.cc +++ b/chromium/pdf/out_of_process_instance.cc @@ -16,6 +16,7 @@ #include "base/feature_list.h" #include "base/logging.h" #include "base/metrics/histogram_functions.h" +#include "base/notreached.h" #include "base/numerics/ranges.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" @@ -109,7 +110,7 @@ constexpr char kJSPrintType[] = "print"; // Save (Page -> Plugin) constexpr char kJSSaveType[] = "save"; constexpr char kJSToken[] = "token"; -constexpr char kJSForce[] = "force"; +constexpr char kJSSaveRequestType[] = "saveRequestType"; // Save Data (Plugin -> Page) constexpr char kJSSaveDataType[] = "saveData"; constexpr char kJSFileName[] = "fileName"; @@ -117,6 +118,8 @@ constexpr char kJSDataToSave[] = "dataToSave"; constexpr char kJSHasUnsavedChanges[] = "hasUnsavedChanges"; // Consume save token (Plugin -> Page) constexpr char kJSConsumeSaveTokenType[] = "consumeSaveToken"; +// Notify when touch selection occurs (Plugin -> Page) +constexpr char kJSTouchSelectionOccurredType[] = "touchSelectionOccurred"; // Go to page (Plugin -> Page) constexpr char kJSGoToPageType[] = "goToPage"; constexpr char kJSPageNumber[] = "page"; @@ -161,6 +164,9 @@ constexpr char kJSRotateCounterclockwiseType[] = "rotateCounterclockwise"; // Toggle two-up view (Page -> Plugin) constexpr char kJSSetTwoUpViewType[] = "setTwoUpView"; constexpr char kJSEnableTwoUpView[] = "enableTwoUpView"; +// Display annotations (Page -> Plugin) +constexpr char kJSDisplayAnnotationsType[] = "displayAnnotations"; +constexpr char kJSDisplayAnnotations[] = "display"; // Select all text in the document (Page -> Plugin) constexpr char kJSSelectAllType[] = "selectAll"; // Get the selected text in the document (Page -> Plugin) @@ -180,10 +186,17 @@ constexpr char kJSNamedDestinationPageNumber[] = "pageNumber"; constexpr char kJSSetIsSelectingType[] = "setIsSelecting"; constexpr char kJSIsSelecting[] = "isSelecting"; +// Editing forms in document (Plugin -> Page) +constexpr char kJSSetIsEditingType[] = "setIsEditing"; + // Notify when a form field is focused (Plugin -> Page) constexpr char kJSFieldFocusType[] = "formFocusChange"; constexpr char kJSFieldFocus[] = "focused"; +// Notify when document is focused (Plugin -> Page) +constexpr char kJSDocumentFocusChangedType[] = "documentFocusChanged"; +constexpr char kJSDocumentHasFocus[] = "hasFocus"; + constexpr int kFindResultCooldownMs = 100; // Do not save forms with over 100 MB. This cap should be kept in sync with and @@ -449,6 +462,8 @@ OutOfProcessInstance::~OutOfProcessInstance() { bool OutOfProcessInstance::Init(uint32_t argc, const char* argn[], const char* argv[]) { + DCHECK(!engine_); + pp::Var document_url_var = pp::URLUtil_Dev::Get()->GetDocumentURL(this); if (!document_url_var.is_string()) return false; @@ -478,7 +493,8 @@ bool OutOfProcessInstance::Init(uint32_t argc, text_input_ = std::make_unique(this); - bool enable_javascript = false; + bool enable_javascript = true; + bool has_edits = false; const char* stream_url = nullptr; const char* original_url = nullptr; const char* top_level_url = nullptr; @@ -499,7 +515,10 @@ bool OutOfProcessInstance::Init(uint32_t argc, success = base::StringToInt(argv[i], &top_toolbar_height_in_viewport_coords_); } else if (strcmp(argn[i], "javascript") == 0) { - enable_javascript = (strcmp(argv[i], "allow") == 0); + if (base::FeatureList::IsEnabled(features::kPdfHonorJsContentSettings)) + enable_javascript = (strcmp(argv[i], "allow") == 0); + } else if (strcmp(argn[i], "has-edits") == 0) { + has_edits = true; } if (!success) return false; @@ -511,10 +530,7 @@ bool OutOfProcessInstance::Init(uint32_t argc, if (!stream_url) stream_url = original_url; - if (!engine_) { - // TODO(tsepez): fix lifetime issue, conditionalize javascript. - engine_ = PDFEngine::Create(this, true); - } + engine_ = PDFEngine::Create(this, enable_javascript); // If we're in print preview mode we don't need to load the document yet. // A |kJSResetPrintPreviewModeType| message will be sent to the plugin letting @@ -525,6 +541,7 @@ bool OutOfProcessInstance::Init(uint32_t argc, LoadUrl(stream_url, /*is_print_preview=*/false); url_ = original_url; + edit_mode_ = has_edits; pp::PDF::SetCrashData(GetPluginInstance(), original_url, top_level_url); return engine_->New(original_url, headers); } @@ -539,259 +556,35 @@ void OutOfProcessInstance::HandleMessage(const pp::Var& message) { std::string type = dict.Get(kType).AsString(); if (type == kJSViewportType) { - pp::Var layout_options_var = dict.Get(kJSLayoutOptions); - if (!layout_options_var.is_undefined()) { - DocumentLayout::Options layout_options; - layout_options.FromVar(layout_options_var); - // TODO(crbug.com/1013800): Eliminate need to get document size from here. - document_size_ = engine_->ApplyDocumentLayout(layout_options); - OnGeometryChanged(zoom_, device_scale_); - } - - if (!(dict.Get(pp::Var(kJSXOffset)).is_number() && - dict.Get(pp::Var(kJSYOffset)).is_number() && - dict.Get(pp::Var(kJSZoom)).is_number() && - dict.Get(pp::Var(kJSPinchPhase)).is_number())) { - NOTREACHED(); - return; - } - received_viewport_message_ = true; - stop_scrolling_ = false; - PinchPhase pinch_phase = - static_cast(dict.Get(pp::Var(kJSPinchPhase)).AsInt()); - double zoom = dict.Get(pp::Var(kJSZoom)).AsDouble(); - double zoom_ratio = zoom / zoom_; - - pp::FloatPoint scroll_offset(dict.Get(pp::Var(kJSXOffset)).AsDouble(), - dict.Get(pp::Var(kJSYOffset)).AsDouble()); - - if (pinch_phase == PINCH_START) { - scroll_offset_at_last_raster_ = scroll_offset; - last_bitmap_smaller_ = false; - needs_reraster_ = false; - return; - } - - // When zooming in, we set a layer transform to avoid unneeded rerasters. - // Also, if we're zooming out and the last time we rerastered was when - // we were even further zoomed out (i.e. we pinch zoomed in and are now - // pinch zooming back out in the same gesture), we update the layer - // transform instead of rerastering. - if (pinch_phase == PINCH_UPDATE_ZOOM_IN || - (pinch_phase == PINCH_UPDATE_ZOOM_OUT && zoom_ratio > 1.0)) { - if (!(dict.Get(pp::Var(kJSPinchX)).is_number() && - dict.Get(pp::Var(kJSPinchY)).is_number() && - dict.Get(pp::Var(kJSPinchVectorX)).is_number() && - dict.Get(pp::Var(kJSPinchVectorY)).is_number())) { - NOTREACHED(); - return; - } - - pp::Point pinch_center(dict.Get(pp::Var(kJSPinchX)).AsDouble(), - dict.Get(pp::Var(kJSPinchY)).AsDouble()); - // Pinch vector is the panning caused due to change in pinch - // center between start and end of the gesture. - pp::Point pinch_vector = - pp::Point(dict.Get(kJSPinchVectorX).AsDouble() * zoom_ratio, - dict.Get(kJSPinchVectorY).AsDouble() * zoom_ratio); - pp::Point scroll_delta; - // If the rendered document doesn't fill the display area we will - // use |paint_offset| to anchor the paint vertically into the same place. - // We use the scroll bars instead of the pinch vector to get the actual - // position on screen of the paint. - pp::Point paint_offset; - - if (plugin_size_.width() > GetDocumentPixelWidth() * zoom_ratio) { - // We want to keep the paint in the middle but it must stay in the same - // position relative to the scroll bars. - paint_offset = pp::Point(0, (1 - zoom_ratio) * pinch_center.y()); - scroll_delta = - pp::Point(0, (scroll_offset.y() - - scroll_offset_at_last_raster_.y() * zoom_ratio)); - - pinch_vector = pp::Point(); - last_bitmap_smaller_ = true; - } else if (last_bitmap_smaller_) { - pinch_center = pp::Point((plugin_size_.width() / device_scale_) / 2, - (plugin_size_.height() / device_scale_) / 2); - const double zoom_when_doc_covers_plugin_width = - zoom_ * plugin_size_.width() / GetDocumentPixelWidth(); - paint_offset = pp::Point( - (1 - zoom / zoom_when_doc_covers_plugin_width) * pinch_center.x(), - (1 - zoom_ratio) * pinch_center.y()); - pinch_vector = pp::Point(); - scroll_delta = - pp::Point((scroll_offset.x() - - scroll_offset_at_last_raster_.x() * zoom_ratio), - (scroll_offset.y() - - scroll_offset_at_last_raster_.y() * zoom_ratio)); - } - - paint_manager_.SetTransform(zoom_ratio, pinch_center, - pinch_vector + paint_offset + scroll_delta, - true); - needs_reraster_ = false; - return; - } - - if (pinch_phase == PINCH_UPDATE_ZOOM_OUT || pinch_phase == PINCH_END) { - // We reraster on pinch zoom out in order to solve the invalid regions - // that appear after zooming out. - // On pinch end the scale is again 1.f and we request a reraster - // in the new position. - paint_manager_.ClearTransform(); - last_bitmap_smaller_ = false; - needs_reraster_ = true; - - // If we're rerastering due to zooming out, we need to update - // |scroll_offset_at_last_raster_|, in case the user continues the - // gesture by zooming in. - scroll_offset_at_last_raster_ = scroll_offset; - } - - // Bound the input parameters. - zoom = std::max(kMinZoom, zoom); - DCHECK(dict.Get(pp::Var(kJSUserInitiated)).is_bool()); - - SetZoom(zoom); - scroll_offset = BoundScrollOffsetToDocument(scroll_offset); - engine_->ScrolledToXPosition(scroll_offset.x() * device_scale_); - engine_->ScrolledToYPosition(scroll_offset.y() * device_scale_); + HandleViewportMessage(dict); } else if (type == kJSGetPasswordCompleteType) { - if (!dict.Get(pp::Var(kJSPassword)).is_string()) { - NOTREACHED(); - return; - } - if (password_callback_) { - pp::CompletionCallbackWithOutput callback = *password_callback_; - password_callback_.reset(); - *callback.output() = dict.Get(pp::Var(kJSPassword)).pp_var(); - callback.Run(PP_OK); - } else { - NOTREACHED(); - } + HandleGetPasswordCompleteMessage(dict); } else if (type == kJSPrintType) { Print(); } else if (type == kJSSaveType) { - if (!(dict.Get(pp::Var(kJSToken)).is_string() && - dict.Get(pp::Var(kJSForce)).is_bool())) { - NOTREACHED(); - return; - } - const bool force = dict.Get(pp::Var(kJSForce)).AsBool(); - if (force) { - // |force| being true means the user has entered annotation mode. In which - // case, assume the user will make edits and prefer saving using the - // plugin data. - pp::PDF::SetPluginCanSave(this, true); - SaveToBuffer(dict.Get(pp::Var(kJSToken)).AsString()); - } else { - SaveToFile(dict.Get(pp::Var(kJSToken)).AsString()); - } + HandlePrintMessage(dict); } else if (type == kJSRotateClockwiseType) { RotateClockwise(); } else if (type == kJSRotateCounterclockwiseType) { RotateCounterclockwise(); } else if (type == kJSSetTwoUpViewType) { - SetTwoUpView(dict.Get(pp::Var(kJSEnableTwoUpView)).AsBool()); + HandleSetTwoUpViewMessage(dict); + } else if (type == kJSDisplayAnnotationsType) { + HandleDisplayAnnotations(dict); } else if (type == kJSSelectAllType) { engine_->SelectAll(); } else if (type == kJSBackgroundColorChangedType) { - if (!dict.Get(pp::Var(kJSBackgroundColor)).is_string()) { - NOTREACHED(); - return; - } - base::HexStringToUInt(dict.Get(pp::Var(kJSBackgroundColor)).AsString(), - &background_color_); + HandleBackgroundColorChangedMessage(dict); } else if (type == kJSResetPrintPreviewModeType) { - if (!(dict.Get(pp::Var(kJSPrintPreviewUrl)).is_string() && - dict.Get(pp::Var(kJSPrintPreviewGrayscale)).is_bool() && - dict.Get(pp::Var(kJSPrintPreviewPageCount)).is_int())) { - NOTREACHED(); - return; - } - - // For security reasons, crash if the URL that is trying to be loaded here - // isn't a print preview one. - std::string url = dict.Get(pp::Var(kJSPrintPreviewUrl)).AsString(); - CHECK(IsPrintPreview()); - CHECK(IsPrintPreviewUrl(url)); - - int print_preview_page_count = - dict.Get(pp::Var(kJSPrintPreviewPageCount)).AsInt(); - if (print_preview_page_count < 0) { - NOTREACHED(); - return; - } - - // The page count is zero if the print preview source is a PDF. In which - // case, the page index for |url| should be at |kCompletePDFIndex|. - // When the page count is not zero, then the source is not PDF. In which - // case, the page index for |url| should be non-negative. - bool is_previewing_pdf = IsPreviewingPDF(print_preview_page_count); - int page_index = ExtractPrintPreviewPageIndex(url); - if (is_previewing_pdf) { - if (page_index != kCompletePDFIndex) { - NOTREACHED(); - return; - } - } else { - if (page_index < 0) { - NOTREACHED(); - return; - } - } - - print_preview_page_count_ = print_preview_page_count; - print_preview_loaded_page_count_ = 0; - url_ = url; - preview_pages_info_ = base::queue(); - preview_document_load_state_ = LOAD_STATE_COMPLETE; - document_load_state_ = LOAD_STATE_LOADING; - LoadUrl(url_, /*is_print_preview=*/false); - preview_engine_.reset(); - engine_ = PDFEngine::Create(this, false); - engine_->SetGrayscale(dict.Get(pp::Var(kJSPrintPreviewGrayscale)).AsBool()); - engine_->New(url_.c_str(), nullptr /* empty header */); - - paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_)); + HandleResetPrintPreviewModeMessage(dict); } else if (type == kJSLoadPreviewPageType) { - if (!(dict.Get(pp::Var(kJSPreviewPageUrl)).is_string() && - dict.Get(pp::Var(kJSPreviewPageIndex)).is_int())) { - NOTREACHED(); - return; - } - - std::string url = dict.Get(pp::Var(kJSPreviewPageUrl)).AsString(); - // For security reasons we crash if the URL that is trying to be loaded here - // isn't a print preview one. - CHECK(IsPrintPreview()); - CHECK(IsPrintPreviewUrl(url)); - ProcessPreviewPageInfo(url, dict.Get(pp::Var(kJSPreviewPageIndex)).AsInt()); + HandleLoadPreviewPageMessage(dict); } else if (type == kJSStopScrollingType) { stop_scrolling_ = true; } else if (type == kJSGetSelectedTextType) { - std::string selected_text = engine_->GetSelectedText(); - // Always return unix newlines to JS. - base::ReplaceChars(selected_text, "\r", std::string(), &selected_text); - pp::VarDictionary reply; - reply.Set(pp::Var(kType), pp::Var(kJSGetSelectedTextReplyType)); - reply.Set(pp::Var(kJSSelectedText), selected_text); - PostMessage(reply); + HandleGetSelectedTextMessage(); } else if (type == kJSGetNamedDestinationType) { - if (!dict.Get(pp::Var(kJSGetNamedDestination)).is_string()) { - NOTREACHED(); - return; - } - base::Optional named_destination = - engine_->GetNamedDestination( - dict.Get(pp::Var(kJSGetNamedDestination)).AsString()); - pp::VarDictionary reply; - reply.Set(pp::Var(kType), pp::Var(kJSGetNamedDestinationReplyType)); - reply.Set( - pp::Var(kJSNamedDestinationPageNumber), - named_destination ? static_cast(named_destination->page) : -1); - PostMessage(reply); + HandleGetNamedDestinationMessage(dict); } else { NOTREACHED(); } @@ -1444,6 +1237,12 @@ void OutOfProcessInstance::NotifySelectedFindResultChanged( SelectedFindResultChanged(current_find_index); } +void OutOfProcessInstance::NotifyTouchSelectionOccurred() { + pp::VarDictionary message; + message.Set(kType, kJSTouchSelectionOccurredType); + PostMessage(message); +} + void OutOfProcessInstance::GetDocumentPassword( pp::CompletionCallbackWithOutput callback) { if (password_callback_) { @@ -1458,7 +1257,7 @@ void OutOfProcessInstance::GetDocumentPassword( PostMessage(message); } -bool OutOfProcessInstance::ShouldSaveEdits() const { +bool OutOfProcessInstance::CanSaveEdits() const { return edit_mode_ && base::FeatureList::IsEnabled(features::kSaveEditedPDFForm); } @@ -1476,7 +1275,7 @@ void OutOfProcessInstance::SaveToBuffer(const std::string& token) { edit_mode_ && !base::FeatureList::IsEnabled(features::kSaveEditedPDFForm); message.Set(kJSHasUnsavedChanges, pp::Var(has_unsaved_changes)); - if (ShouldSaveEdits()) { + if (CanSaveEdits()) { std::vector data = engine_->GetSaveData(); if (IsSaveDataSizeValid(data.size())) { pp::VarArrayBuffer buffer(data.size()); @@ -1485,7 +1284,7 @@ void OutOfProcessInstance::SaveToBuffer(const std::string& token) { message.Set(kJSDataToSave, buffer); } } else { - DCHECK(base::FeatureList::IsEnabled(features::kPDFAnnotations)); +#if defined(OS_CHROMEOS) uint32_t length = engine_->GetLoadedByteSize(); if (IsSaveDataSizeValid(length)) { pp::VarArrayBuffer buffer(length); @@ -1493,20 +1292,18 @@ void OutOfProcessInstance::SaveToBuffer(const std::string& token) { message.Set(kJSDataToSave, buffer); } } +#else + NOTREACHED(); +#endif } PostMessage(message); } void OutOfProcessInstance::SaveToFile(const std::string& token) { - if (!ShouldSaveEdits()) { - engine_->KillFormFocus(); - ConsumeSaveToken(token); - pp::PDF::SaveAs(this); - return; - } - - SaveToBuffer(token); + engine_->KillFormFocus(); + ConsumeSaveToken(token); + pp::PDF::SaveAs(this); } void OutOfProcessInstance::ConsumeSaveToken(const std::string& token) { @@ -1702,22 +1499,299 @@ void OutOfProcessInstance::RotateCounterclockwise() { engine_->RotateCounterclockwise(); } -void OutOfProcessInstance::SetTwoUpView(bool enable_two_up_view) { - DCHECK(base::FeatureList::IsEnabled(features::kPDFTwoUpView)); - engine_->SetTwoUpView(enable_two_up_view); -} - // static std::string OutOfProcessInstance::GetFileNameFromUrl(const std::string& url) { // Generate a file name. Unfortunately, MIME type can't be provided, since it // requires IO. base::string16 file_name = net::GetSuggestedFilename( - GURL(url), std::string() /* content_disposition */, - std::string() /* referrer_charset */, std::string() /* suggested_name */, - std::string() /* mime_type */, std::string() /* default_name */); + GURL(url), /*content_disposition=*/std::string(), + /*referrer_charset=*/std::string(), /*suggested_name=*/std::string(), + /*mime_type=*/std::string(), /*default_name=*/std::string()); return base::UTF16ToUTF8(file_name); } +void OutOfProcessInstance::HandleBackgroundColorChangedMessage( + const pp::VarDictionary& dict) { + if (!dict.Get(pp::Var(kJSBackgroundColor)).is_string()) { + NOTREACHED(); + return; + } + base::HexStringToUInt(dict.Get(pp::Var(kJSBackgroundColor)).AsString(), + &background_color_); +} + +void OutOfProcessInstance::HandleDisplayAnnotations( + const pp::VarDictionary& dict) { + if (!dict.Get(pp::Var(kJSDisplayAnnotations)).is_bool()) { + NOTREACHED(); + return; + } + + engine_->DisplayAnnotations( + dict.Get(pp::Var(kJSDisplayAnnotations)).AsBool()); +} + +void OutOfProcessInstance::HandleGetNamedDestinationMessage( + const pp::VarDictionary& dict) { + if (!dict.Get(pp::Var(kJSGetNamedDestination)).is_string()) { + NOTREACHED(); + return; + } + base::Optional named_destination = + engine_->GetNamedDestination( + dict.Get(pp::Var(kJSGetNamedDestination)).AsString()); + pp::VarDictionary reply; + reply.Set(pp::Var(kType), pp::Var(kJSGetNamedDestinationReplyType)); + reply.Set(pp::Var(kJSNamedDestinationPageNumber), + named_destination ? static_cast(named_destination->page) : -1); + PostMessage(reply); +} + +void OutOfProcessInstance::HandleGetPasswordCompleteMessage( + const pp::VarDictionary& dict) { + if (!dict.Get(pp::Var(kJSPassword)).is_string() || !password_callback_) { + NOTREACHED(); + return; + } + + pp::CompletionCallbackWithOutput callback = *password_callback_; + password_callback_.reset(); + *callback.output() = dict.Get(pp::Var(kJSPassword)).pp_var(); + callback.Run(PP_OK); +} + +void OutOfProcessInstance::HandleGetSelectedTextMessage() { + std::string selected_text = engine_->GetSelectedText(); + // Always return unix newlines to JS. + base::ReplaceChars(selected_text, "\r", std::string(), &selected_text); + pp::VarDictionary reply; + reply.Set(pp::Var(kType), pp::Var(kJSGetSelectedTextReplyType)); + reply.Set(pp::Var(kJSSelectedText), selected_text); + PostMessage(reply); +} + +void OutOfProcessInstance::HandleLoadPreviewPageMessage( + const pp::VarDictionary& dict) { + if (!(dict.Get(pp::Var(kJSPreviewPageUrl)).is_string() && + dict.Get(pp::Var(kJSPreviewPageIndex)).is_int())) { + NOTREACHED(); + return; + } + + std::string url = dict.Get(pp::Var(kJSPreviewPageUrl)).AsString(); + // For security reasons we crash if the URL that is trying to be loaded here + // isn't a print preview one. + CHECK(IsPrintPreview()); + CHECK(IsPrintPreviewUrl(url)); + ProcessPreviewPageInfo(url, dict.Get(pp::Var(kJSPreviewPageIndex)).AsInt()); +} + +void OutOfProcessInstance::HandlePrintMessage(const pp::VarDictionary& dict) { + if (!(dict.Get(pp::Var(kJSToken)).is_string() && + dict.Get(pp::Var(kJSSaveRequestType)).is_int())) { + NOTREACHED(); + return; + } + const SaveRequestType request_type = static_cast( + dict.Get(pp::Var(kJSSaveRequestType)).AsInt()); + switch (request_type) { + case SaveRequestType::kAnnotation: + // In annotation mode, assume the user will make edits and prefer saving + // using the plugin data. + pp::PDF::SetPluginCanSave(this, true); + SaveToBuffer(dict.Get(pp::Var(kJSToken)).AsString()); + break; + case SaveRequestType::kOriginal: + pp::PDF::SetPluginCanSave(this, false); + SaveToFile(dict.Get(pp::Var(kJSToken)).AsString()); + pp::PDF::SetPluginCanSave(this, CanSaveEdits()); + break; + case SaveRequestType::kEdited: + SaveToBuffer(dict.Get(pp::Var(kJSToken)).AsString()); + break; + } +} + +void OutOfProcessInstance::HandleResetPrintPreviewModeMessage( + const pp::VarDictionary& dict) { + if (!(dict.Get(pp::Var(kJSPrintPreviewUrl)).is_string() && + dict.Get(pp::Var(kJSPrintPreviewGrayscale)).is_bool() && + dict.Get(pp::Var(kJSPrintPreviewPageCount)).is_int())) { + NOTREACHED(); + return; + } + + // For security reasons, crash if the URL that is trying to be loaded here + // isn't a print preview one. + std::string url = dict.Get(pp::Var(kJSPrintPreviewUrl)).AsString(); + CHECK(IsPrintPreview()); + CHECK(IsPrintPreviewUrl(url)); + + int print_preview_page_count = + dict.Get(pp::Var(kJSPrintPreviewPageCount)).AsInt(); + if (print_preview_page_count < 0) { + NOTREACHED(); + return; + } + + // The page count is zero if the print preview source is a PDF. In which + // case, the page index for |url| should be at |kCompletePDFIndex|. + // When the page count is not zero, then the source is not PDF. In which + // case, the page index for |url| should be non-negative. + bool is_previewing_pdf = IsPreviewingPDF(print_preview_page_count); + int page_index = ExtractPrintPreviewPageIndex(url); + if ((is_previewing_pdf && page_index != kCompletePDFIndex) || + (!is_previewing_pdf && page_index < 0)) { + NOTREACHED(); + return; + } + + print_preview_page_count_ = print_preview_page_count; + print_preview_loaded_page_count_ = 0; + url_ = url; + preview_pages_info_ = base::queue(); + preview_document_load_state_ = LOAD_STATE_COMPLETE; + document_load_state_ = LOAD_STATE_LOADING; + LoadUrl(url_, /*is_print_preview=*/false); + preview_engine_.reset(); + engine_ = PDFEngine::Create(this, false); + engine_->SetGrayscale(dict.Get(pp::Var(kJSPrintPreviewGrayscale)).AsBool()); + engine_->New(url_.c_str(), /*headers=*/nullptr); + + paint_manager_.InvalidateRect(pp::Rect(pp::Point(), plugin_size_)); +} + +void OutOfProcessInstance::HandleSetTwoUpViewMessage( + const pp::VarDictionary& dict) { + if (!base::FeatureList::IsEnabled(features::kPDFTwoUpView) || + !dict.Get(pp::Var(kJSEnableTwoUpView)).is_bool()) { + NOTREACHED(); + return; + } + + engine_->SetTwoUpView(dict.Get(pp::Var(kJSEnableTwoUpView)).AsBool()); +} + +void OutOfProcessInstance::HandleViewportMessage( + const pp::VarDictionary& dict) { + pp::Var layout_options_var = dict.Get(kJSLayoutOptions); + if (!layout_options_var.is_undefined()) { + DocumentLayout::Options layout_options; + layout_options.FromVar(layout_options_var); + // TODO(crbug.com/1013800): Eliminate need to get document size from here. + document_size_ = engine_->ApplyDocumentLayout(layout_options); + OnGeometryChanged(zoom_, device_scale_); + } + + if (!(dict.Get(pp::Var(kJSXOffset)).is_number() && + dict.Get(pp::Var(kJSYOffset)).is_number() && + dict.Get(pp::Var(kJSZoom)).is_number() && + dict.Get(pp::Var(kJSPinchPhase)).is_number())) { + NOTREACHED(); + return; + } + received_viewport_message_ = true; + stop_scrolling_ = false; + PinchPhase pinch_phase = + static_cast(dict.Get(pp::Var(kJSPinchPhase)).AsInt()); + double zoom = dict.Get(pp::Var(kJSZoom)).AsDouble(); + double zoom_ratio = zoom / zoom_; + + pp::FloatPoint scroll_offset(dict.Get(pp::Var(kJSXOffset)).AsDouble(), + dict.Get(pp::Var(kJSYOffset)).AsDouble()); + + if (pinch_phase == PINCH_START) { + scroll_offset_at_last_raster_ = scroll_offset; + last_bitmap_smaller_ = false; + needs_reraster_ = false; + return; + } + + // When zooming in, we set a layer transform to avoid unneeded rerasters. + // Also, if we're zooming out and the last time we rerastered was when + // we were even further zoomed out (i.e. we pinch zoomed in and are now + // pinch zooming back out in the same gesture), we update the layer + // transform instead of rerastering. + if (pinch_phase == PINCH_UPDATE_ZOOM_IN || + (pinch_phase == PINCH_UPDATE_ZOOM_OUT && zoom_ratio > 1.0)) { + if (!(dict.Get(pp::Var(kJSPinchX)).is_number() && + dict.Get(pp::Var(kJSPinchY)).is_number() && + dict.Get(pp::Var(kJSPinchVectorX)).is_number() && + dict.Get(pp::Var(kJSPinchVectorY)).is_number())) { + NOTREACHED(); + return; + } + + pp::Point pinch_center(dict.Get(pp::Var(kJSPinchX)).AsDouble(), + dict.Get(pp::Var(kJSPinchY)).AsDouble()); + // Pinch vector is the panning caused due to change in pinch + // center between start and end of the gesture. + pp::Point pinch_vector = + pp::Point(dict.Get(kJSPinchVectorX).AsDouble() * zoom_ratio, + dict.Get(kJSPinchVectorY).AsDouble() * zoom_ratio); + pp::Point scroll_delta; + // If the rendered document doesn't fill the display area we will + // use |paint_offset| to anchor the paint vertically into the same place. + // We use the scroll bars instead of the pinch vector to get the actual + // position on screen of the paint. + pp::Point paint_offset; + + if (plugin_size_.width() > GetDocumentPixelWidth() * zoom_ratio) { + // We want to keep the paint in the middle but it must stay in the same + // position relative to the scroll bars. + paint_offset = pp::Point(0, (1 - zoom_ratio) * pinch_center.y()); + scroll_delta = pp::Point( + 0, + (scroll_offset.y() - scroll_offset_at_last_raster_.y() * zoom_ratio)); + + pinch_vector = pp::Point(); + last_bitmap_smaller_ = true; + } else if (last_bitmap_smaller_) { + pinch_center = pp::Point((plugin_size_.width() / device_scale_) / 2, + (plugin_size_.height() / device_scale_) / 2); + const double zoom_when_doc_covers_plugin_width = + zoom_ * plugin_size_.width() / GetDocumentPixelWidth(); + paint_offset = pp::Point( + (1 - zoom / zoom_when_doc_covers_plugin_width) * pinch_center.x(), + (1 - zoom_ratio) * pinch_center.y()); + pinch_vector = pp::Point(); + scroll_delta = pp::Point( + (scroll_offset.x() - scroll_offset_at_last_raster_.x() * zoom_ratio), + (scroll_offset.y() - scroll_offset_at_last_raster_.y() * zoom_ratio)); + } + + paint_manager_.SetTransform(zoom_ratio, pinch_center, + pinch_vector + paint_offset + scroll_delta, + true); + needs_reraster_ = false; + return; + } + + if (pinch_phase == PINCH_UPDATE_ZOOM_OUT || pinch_phase == PINCH_END) { + // We reraster on pinch zoom out in order to solve the invalid regions + // that appear after zooming out. + // On pinch end the scale is again 1.f and we request a reraster + // in the new position. + paint_manager_.ClearTransform(); + last_bitmap_smaller_ = false; + needs_reraster_ = true; + + // If we're rerastering due to zooming out, we need to update + // |scroll_offset_at_last_raster_|, in case the user continues the + // gesture by zooming in. + scroll_offset_at_last_raster_ = scroll_offset; + } + + // Bound the input parameters. + zoom = std::max(kMinZoom, zoom); + DCHECK(dict.Get(pp::Var(kJSUserInitiated)).is_bool()); + + SetZoom(zoom); + scroll_offset = BoundScrollOffsetToDocument(scroll_offset); + engine_->ScrolledToXPosition(scroll_offset.x() * device_scale_); + engine_->ScrolledToYPosition(scroll_offset.y() * device_scale_); +} + void OutOfProcessInstance::PreviewDocumentLoadComplete() { if (preview_document_load_state_ != LOAD_STATE_LOADING || preview_pages_info_.empty()) { @@ -1917,15 +1991,27 @@ void OutOfProcessInstance::IsSelectingChanged(bool is_selecting) { PostMessage(message); } -void OutOfProcessInstance::IsEditModeChanged(bool is_edit_mode) { - edit_mode_ = is_edit_mode; - pp::PDF::SetPluginCanSave(this, ShouldSaveEdits()); +void OutOfProcessInstance::EnteredEditMode() { + edit_mode_ = true; + pp::PDF::SetPluginCanSave(this, CanSaveEdits()); + if (CanSaveEdits()) { + pp::VarDictionary message; + message.Set(kType, kJSSetIsEditingType); + PostMessage(message); + } } float OutOfProcessInstance::GetToolbarHeightInScreenCoords() { return top_toolbar_height_in_viewport_coords_ * device_scale_; } +void OutOfProcessInstance::DocumentFocusChanged(bool document_has_focus) { + pp::VarDictionary message; + message.Set(pp::Var(kType), pp::Var(kJSDocumentFocusChangedType)); + message.Set(pp::Var(kJSDocumentHasFocus), pp::Var(document_has_focus)); + PostMessage(message); +} + void OutOfProcessInstance::ProcessPreviewPageInfo(const std::string& url, int dest_page_index) { DCHECK(IsPrintPreview()); diff --git a/chromium/pdf/out_of_process_instance.h b/chromium/pdf/out_of_process_instance.h index db8f4b476e4..b39d0dec7b4 100644 --- a/chromium/pdf/out_of_process_instance.h +++ b/chromium/pdf/out_of_process_instance.h @@ -111,6 +111,7 @@ class OutOfProcessInstance : public pp::Instance, void UpdateTickMarks(const std::vector& tickmarks) override; void NotifyNumberOfFindResultsChanged(int total, bool final_result) override; void NotifySelectedFindResultChanged(int current_find_index) override; + void NotifyTouchSelectionOccurred() override; void GetDocumentPassword( pp::CompletionCallbackWithOutput callback) override; void Beep() override; @@ -143,8 +144,9 @@ class OutOfProcessInstance : public pp::Instance, uint32_t GetBackgroundColor() override; void IsSelectingChanged(bool is_selecting) override; void SelectionChanged(const pp::Rect& left, const pp::Rect& right) override; - void IsEditModeChanged(bool is_edit_mode) override; + void EnteredEditMode() override; float GetToolbarHeightInScreenCoords() override; + void DocumentFocusChanged(bool document_has_focus) override; // PreviewModeClient::Client implementation. void PreviewDocumentLoadComplete() override; @@ -153,13 +155,24 @@ class OutOfProcessInstance : public pp::Instance, // Helper functions for implementing PPP_PDF. void RotateClockwise(); void RotateCounterclockwise(); - void SetTwoUpView(bool enable_two_up_view); // Creates a file name for saving a PDF file, given the source URL. Exposed // for testing. static std::string GetFileNameFromUrl(const std::string& url); private: + // Message handlers. + void HandleBackgroundColorChangedMessage(const pp::VarDictionary& dict); + void HandleDisplayAnnotations(const pp::VarDictionary& dict); + void HandleGetNamedDestinationMessage(const pp::VarDictionary& dict); + void HandleGetPasswordCompleteMessage(const pp::VarDictionary& dict); + void HandleGetSelectedTextMessage(); + void HandleLoadPreviewPageMessage(const pp::VarDictionary& dict); + void HandlePrintMessage(const pp::VarDictionary& dict); + void HandleResetPrintPreviewModeMessage(const pp::VarDictionary& dict); + void HandleSetTwoUpViewMessage(const pp::VarDictionary& dict); + void HandleViewportMessage(const pp::VarDictionary& dict); + void ResetRecentlySentFindUpdate(int32_t); // Called whenever the plugin geometry changes to update the location of the @@ -184,7 +197,7 @@ class OutOfProcessInstance : public pp::Instance, // frame's origin. pp::URLLoader CreateURLLoaderInternal(); - bool ShouldSaveEdits() const; + bool CanSaveEdits() const; void SaveToFile(const std::string& token); void SaveToBuffer(const std::string& token); void ConsumeSaveToken(const std::string& token); @@ -210,6 +223,13 @@ class OutOfProcessInstance : public pp::Instance, LOAD_STATE_FAILED, }; + // Must match SaveRequestType in chrome/browser/resources/pdf/constants.js. + enum class SaveRequestType { + kAnnotation = 0, + kOriginal = 1, + kEdited = 2, + }; + // Set new zoom scale. void SetZoom(double scale); diff --git a/chromium/pdf/pdf_engine.h b/chromium/pdf/pdf_engine.h index 514e82ea49c..0b4babcdf9e 100644 --- a/chromium/pdf/pdf_engine.h +++ b/chromium/pdf/pdf_engine.h @@ -56,6 +56,7 @@ class VarDictionary; namespace chrome_pdf { +struct DocumentAttachmentInfo; struct DocumentMetadata; // Do one time initialization of the SDK. @@ -176,6 +177,8 @@ class PDFEngine { // Updates the index of the currently selected search item. virtual void NotifySelectedFindResultChanged(int current_find_index) {} + virtual void NotifyTouchSelectionOccurred() {} + // Prompts the user for a password to open this document. The callback is // called when the password is retrieved. virtual void GetDocumentPassword( @@ -258,12 +261,15 @@ class PDFEngine { virtual void SelectionChanged(const pp::Rect& left, const pp::Rect& right) { } - // Sets edit mode state. - virtual void IsEditModeChanged(bool is_edit_mode) {} + // Notifies the client that the PDF has been edited. + virtual void EnteredEditMode() {} // Gets the height of the top toolbar in screen coordinates. This is // independent of whether it is hidden or not at the moment. virtual float GetToolbarHeightInScreenCoords() = 0; + + // Notifies the client about focus changes for the document. + virtual void DocumentFocusChanged(bool document_has_focus) {} }; struct AccessibilityLinkInfo { @@ -283,6 +289,7 @@ class PDFEngine { int char_count; pp::FloatRect bounds; uint32_t color; + std::string note_text; }; struct AccessibilityTextFieldInfo { @@ -336,6 +343,7 @@ class PDFEngine { virtual void RotateClockwise() = 0; virtual void RotateCounterclockwise() = 0; virtual void SetTwoUpView(bool enable) = 0; + virtual void DisplayAnnotations(bool display) = 0; // Applies the document layout options proposed by a call to // PDFEngine::Client::ProposeDocumentLayout(), returning the overall size of @@ -365,6 +373,9 @@ class PDFEngine { // Checks the permissions associated with this document. virtual bool HasPermission(DocumentPermission permission) const = 0; virtual void SelectAll() = 0; + // Gets the list of DocumentAttachmentInfo from the document. + virtual const std::vector& + GetDocumentAttachmentInfoList() const = 0; // Gets metadata about the document. virtual const DocumentMetadata& GetDocumentMetadata() const = 0; // Gets the number of pages in the document. diff --git a/chromium/pdf/pdf_features.cc b/chromium/pdf/pdf_features.cc index ae908960a06..df8ae4c88ab 100644 --- a/chromium/pdf/pdf_features.cc +++ b/chromium/pdf/pdf_features.cc @@ -13,17 +13,15 @@ const base::Feature kAccessiblePDFForm = {"AccessiblePDFForm", const base::Feature kAccessiblePDFHighlight = { "AccessiblePDFHighlight", base::FEATURE_ENABLED_BY_DEFAULT}; -const base::Feature kPDFAnnotations = {"PDFAnnotations", -#if defined(OS_CHROMEOS) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif // defined(OS_CHROMEOS) -}; +const base::Feature kPdfHonorJsContentSettings = { + "PdfHonorJsContentSettings", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kPDFTwoUpView = {"PDFTwoUpView", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kPDFViewerUpdate = {"PDFViewerUpdate", + base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kSaveEditedPDFForm = {"SaveEditedPDFForm", #if defined(OS_CHROMEOS) base::FEATURE_ENABLED_BY_DEFAULT @@ -33,7 +31,7 @@ const base::Feature kSaveEditedPDFForm = {"SaveEditedPDFForm", }; const base::Feature kTabAcrossPDFAnnotations = { - "TabAcrossPDFAnnotations", base::FEATURE_DISABLED_BY_DEFAULT}; + "TabAcrossPDFAnnotations", base::FEATURE_ENABLED_BY_DEFAULT}; } // namespace features } // namespace chrome_pdf diff --git a/chromium/pdf/pdf_features.h b/chromium/pdf/pdf_features.h index eb698f22b6b..6dca39fa395 100644 --- a/chromium/pdf/pdf_features.h +++ b/chromium/pdf/pdf_features.h @@ -15,8 +15,9 @@ namespace features { extern const base::Feature kAccessiblePDFForm; extern const base::Feature kAccessiblePDFHighlight; -extern const base::Feature kPDFAnnotations; +extern const base::Feature kPdfHonorJsContentSettings; extern const base::Feature kPDFTwoUpView; +extern const base::Feature kPDFViewerUpdate; extern const base::Feature kSaveEditedPDFForm; extern const base::Feature kTabAcrossPDFAnnotations; diff --git a/chromium/pdf/pdfium/accessibility_unittest.cc b/chromium/pdf/pdfium/accessibility_unittest.cc index f23e103ffde..98eb154f5ae 100644 --- a/chromium/pdf/pdfium/accessibility_unittest.cc +++ b/chromium/pdf/pdfium/accessibility_unittest.cc @@ -485,7 +485,7 @@ TEST_F(AccessibilityTest, GetAccessibilityHighlightInfo) { constexpr uint32_t kHighlightNoColor = MakeARGB(0, 0, 0, 0); static const pp::PDF::PrivateAccessibilityHighlightInfo kExpectedHighlightInfo[] = { - {"", 0, 0, 1, {{5, 196}, {49, 26}}, kHighlightDefaultColor}, + {"Text Note", 0, 0, 1, {{5, 196}, {49, 26}}, kHighlightDefaultColor}, {"", 1, 2, 1, {{110, 196}, {77, 26}}, kHighlightRedColor}, {"", 2, 3, 1, {{192, 196}, {13, 26}}, kHighlightNoColor}}; @@ -520,6 +520,7 @@ TEST_F(AccessibilityTest, GetAccessibilityHighlightInfo) { EXPECT_EQ(highlight_info.text_run_count, kExpectedHighlightInfo[i].text_run_count); EXPECT_EQ(highlight_info.color, kExpectedHighlightInfo[i].color); + EXPECT_EQ(highlight_info.note_text, kExpectedHighlightInfo[i].note_text); } } @@ -563,12 +564,12 @@ TEST_F(AccessibilityTest, GetAccessibilityTextFieldInfo) { CompareRect(kExpectedPageRect, page_info.bounds); EXPECT_EQ(text_runs.size(), page_info.text_run_count); EXPECT_EQ(chars.size(), page_info.char_count); - ASSERT_EQ(page_objects.text_fields.size(), + ASSERT_EQ(page_objects.form_fields.text_fields.size(), base::size(kExpectedTextFieldInfo)); - for (size_t i = 0; i < page_objects.text_fields.size(); ++i) { + for (size_t i = 0; i < page_objects.form_fields.text_fields.size(); ++i) { const pp::PDF::PrivateAccessibilityTextFieldInfo& text_field_info = - page_objects.text_fields[i]; + page_objects.form_fields.text_fields[i]; EXPECT_EQ(kExpectedTextFieldInfo[i].name, text_field_info.name); EXPECT_EQ(kExpectedTextFieldInfo[i].value, text_field_info.value); EXPECT_EQ(kExpectedTextFieldInfo[i].is_read_only, diff --git a/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.cc b/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.cc index 382b3a9385c..9e02e34906f 100644 --- a/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.cc +++ b/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.cc @@ -6,14 +6,13 @@ #include -#include - #include "base/check_op.h" -#include "base/strings/string16.h" #include "base/strings/string_util.h" namespace chrome_pdf { +namespace internal { + template PDFiumAPIStringBufferAdapter::PDFiumAPIStringBufferAdapter( StringType* str, @@ -52,36 +51,30 @@ void PDFiumAPIStringBufferAdapter::Close(size_t actual_size) { } } -template -PDFiumAPIStringBufferSizeInBytesAdapter:: - PDFiumAPIStringBufferSizeInBytesAdapter(StringType* str, +PDFiumAPIStringBufferSizeInBytesAdapter:: + PDFiumAPIStringBufferSizeInBytesAdapter(base::string16* str, size_t expected_size, bool check_expected_size) - : adapter_(str, - expected_size / sizeof(typename StringType::value_type), - check_expected_size) { - DCHECK(expected_size % sizeof(typename StringType::value_type) == 0); + : adapter_(str, expected_size / sizeof(base::char16), check_expected_size) { + DCHECK(expected_size % sizeof(base::char16) == 0); } -template -PDFiumAPIStringBufferSizeInBytesAdapter< - StringType>::~PDFiumAPIStringBufferSizeInBytesAdapter() = default; +PDFiumAPIStringBufferSizeInBytesAdapter:: + ~PDFiumAPIStringBufferSizeInBytesAdapter() = default; -template -void* PDFiumAPIStringBufferSizeInBytesAdapter::GetData() { +void* PDFiumAPIStringBufferSizeInBytesAdapter::GetData() { return adapter_.GetData(); } -template -void PDFiumAPIStringBufferSizeInBytesAdapter::Close( - size_t actual_size) { - DCHECK(actual_size % sizeof(typename StringType::value_type) == 0); - adapter_.Close(actual_size / sizeof(typename StringType::value_type)); +void PDFiumAPIStringBufferSizeInBytesAdapter::Close(size_t actual_size) { + DCHECK(actual_size % sizeof(base::char16) == 0); + adapter_.Close(actual_size / sizeof(base::char16)); } // explicit instantiations template class PDFiumAPIStringBufferAdapter; template class PDFiumAPIStringBufferAdapter; -template class PDFiumAPIStringBufferSizeInBytesAdapter; + +} // namespace internal } // namespace chrome_pdf diff --git a/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.h b/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.h index d3a413cd5a7..9c2587a19bf 100644 --- a/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.h +++ b/chromium/pdf/pdfium/pdfium_api_string_buffer_adapter.h @@ -7,16 +7,21 @@ #include +#include + +#include "base/callback.h" #include "base/macros.h" #include "base/numerics/safe_math.h" +#include "base/strings/string16.h" namespace chrome_pdf { +namespace internal { + // Helper to deal with the fact that many PDFium APIs write the null-terminator -// into string buffers that are passed to them, but the PDF plugin likes to pass -// in std::strings / base::string16s, where one should not count on the internal +// into string buffers that are passed to them, but the PDF code likes to use +// std::strings / base::string16s, where one should not count on the internal // string buffers to be null-terminated. - template class PDFiumAPIStringBufferAdapter { public: @@ -56,11 +61,11 @@ class PDFiumAPIStringBufferAdapter { }; // Helper to deal with the fact that many PDFium APIs write the null-terminator -// into string buffers that are passed to them, but the PDF plugin likes to pass -// in std::strings / base::string16s, where one should not count on the internal +// into string buffers that are passed to them, but the PDF code likes to use +// std::strings / base::string16s, where one should not count on the internal // string buffers to be null-terminated. This version is suitable for APIs that -// work in terms of number of bytes instead of the number of characters. -template +// work in terms of number of bytes instead of the number of characters. Though +// for std::strings, PDFiumAPIStringBufferAdapter is equivalent. class PDFiumAPIStringBufferSizeInBytesAdapter { public: // |str| is the string to write into. @@ -69,19 +74,19 @@ class PDFiumAPIStringBufferSizeInBytesAdapter { // character in bytes. // |check_expected_size| whether to check the actual number of bytes // written into |str| against |expected_size| when calling Close(). - PDFiumAPIStringBufferSizeInBytesAdapter(StringType* str, + PDFiumAPIStringBufferSizeInBytesAdapter(base::string16* str, size_t expected_size, bool check_expected_size); ~PDFiumAPIStringBufferSizeInBytesAdapter(); // Returns a pointer to |str_|'s buffer. The buffer's size is large enough to - // hold |expected_size_| + sizeof(StringType::value_type) bytes, so the PDFium - // API that uses the pointer has space to write a null-terminator. + // hold |expected_size_| + sizeof(base::char16) bytes, so the PDFium API that + // uses the pointer has space to write a null-terminator. void* GetData(); - // Resizes |str_| to |actual_size| - sizeof(StringType::value_type) bytes, - // thereby removing the extra null-terminator. This must be called prior to - // the adapter's destruction. The pointer returned by GetData() should be + // Resizes |str_| to |actual_size| - sizeof(base::char16) bytes, thereby + // removing the extra null-terminator. This must be called prior to the + // adapter's destruction. The pointer returned by GetData() should be // considered invalid. void Close(size_t actual_size); @@ -91,9 +96,56 @@ class PDFiumAPIStringBufferSizeInBytesAdapter { } private: - PDFiumAPIStringBufferAdapter adapter_; + PDFiumAPIStringBufferAdapter adapter_; }; +template +StringType CallPDFiumStringBufferApi( + base::RepeatingCallback api, + bool check_expected_size) { + StringType str; + ReturnType expected_size = api.Run(nullptr, 0); + if (expected_size > 0) { + AdapterType api_string_adapter(&str, expected_size, check_expected_size); + auto* data = reinterpret_cast(api_string_adapter.GetData()); + api_string_adapter.Close(api.Run(data, expected_size)); + } + return str; +} + +} // namespace internal + +// Helper function to call PDFium APIs where the output buffer is expected to +// hold UTF-16 data, and the buffer length is specified in bytes. +template +base::string16 CallPDFiumWideStringBufferApi( + base::RepeatingCallback api, + bool check_expected_size) { + using adapter_type = internal::PDFiumAPIStringBufferSizeInBytesAdapter; + return internal::CallPDFiumStringBufferApi( + api, check_expected_size); +} + +// Helper function to call PDFium APIs where the output buffer is expected to +// hold ASCII or UTF-8 data, and the buffer length is specified in bytes. +template +std::string CallPDFiumStringBufferApi( + base::RepeatingCallback api, + bool check_expected_size) { + using adapter_type = internal::PDFiumAPIStringBufferAdapter; + return internal::CallPDFiumStringBufferApi( + api, check_expected_size); +} + +// Expose internal::PDFiumAPIStringBufferAdapter for special cases that cannot +// use the CallPDFiumStringBuffer* functions above. +template +using PDFiumAPIStringBufferAdapter = + internal::PDFiumAPIStringBufferAdapter; + } // namespace chrome_pdf #endif // PDF_PDFIUM_PDFIUM_API_STRING_BUFFER_ADAPTER_H_ diff --git a/chromium/pdf/pdfium/pdfium_assert_matching_enums.cc b/chromium/pdf/pdfium/pdfium_assert_matching_enums.cc index a58ecff0daa..fc52f804e9c 100644 --- a/chromium/pdf/pdfium/pdfium_assert_matching_enums.cc +++ b/chromium/pdf/pdfium/pdfium_assert_matching_enums.cc @@ -4,10 +4,12 @@ #include "build/build_config.h" #include "pdf/pdf.h" +#include "pdf/pdf_engine.h" #include "ppapi/c/pp_input_event.h" #include "ppapi/c/private/ppb_pdf.h" #include "ppapi/c/private/ppp_pdf.h" #include "third_party/pdfium/public/fpdf_edit.h" +#include "third_party/pdfium/public/fpdf_formfill.h" #include "third_party/pdfium/public/fpdf_fwlevent.h" #include "third_party/pdfium/public/fpdf_sysfontinfo.h" #include "third_party/pdfium/public/fpdfview.h" @@ -229,6 +231,19 @@ STATIC_ASSERT_ENUM(PP_TEXTRENDERINGMODE_FILLSTROKECLIP, FPDF_TEXTRENDERMODE_FILL_STROKE_CLIP); STATIC_ASSERT_ENUM(PP_TEXTRENDERINGMODE_CLIP, FPDF_TEXTRENDERMODE_CLIP); +STATIC_ASSERT_ENUM(chrome_pdf::PDFEngine::FormType::kNone, FORMTYPE_NONE); +STATIC_ASSERT_ENUM(chrome_pdf::PDFEngine::FormType::kAcroForm, + FORMTYPE_ACRO_FORM); +STATIC_ASSERT_ENUM(chrome_pdf::PDFEngine::FormType::kXFAFull, + FORMTYPE_XFA_FULL); +STATIC_ASSERT_ENUM(chrome_pdf::PDFEngine::FormType::kXFAForeground, + FORMTYPE_XFA_FOREGROUND); +STATIC_ASSERT_ENUM(chrome_pdf::PDFEngine::FormType::kCount, FORMTYPE_COUNT); + +STATIC_ASSERT_ENUM(PP_PRIVATEBUTTON_PUSHBUTTON, FPDF_FORMFIELD_PUSHBUTTON); +STATIC_ASSERT_ENUM(PP_PRIVATEBUTTON_CHECKBOX, FPDF_FORMFIELD_CHECKBOX); +STATIC_ASSERT_ENUM(PP_PRIVATEBUTTON_RADIOBUTTON, FPDF_FORMFIELD_RADIOBUTTON); + #if defined(OS_WIN) STATIC_ASSERT_ENUM(chrome_pdf::kEmf, FPDF_PRINTMODE_EMF); STATIC_ASSERT_ENUM(chrome_pdf::kTextOnly, FPDF_PRINTMODE_TEXTONLY); diff --git a/chromium/pdf/pdfium/pdfium_engine.cc b/chromium/pdf/pdfium/pdfium_engine.cc index 090a8edcbdf..66f988b568b 100644 --- a/chromium/pdf/pdfium/pdfium_engine.cc +++ b/chromium/pdf/pdfium/pdfium_engine.cc @@ -28,6 +28,7 @@ #include "gin/array_buffer.h" #include "gin/public/gin_embedders.h" #include "gin/public/isolate_holder.h" +#include "gin/public/v8_platform.h" #include "pdf/document_loader_impl.h" #include "pdf/draw_utils/coordinates.h" #include "pdf/draw_utils/shadow.h" @@ -68,20 +69,6 @@ using printing::kPixelsPerInch; namespace chrome_pdf { -static_assert(static_cast(PDFEngine::FormType::kNone) == FORMTYPE_NONE, - "None form types must match"); -static_assert(static_cast(PDFEngine::FormType::kAcroForm) == - FORMTYPE_ACRO_FORM, - "AcroForm form types must match"); -static_assert(static_cast(PDFEngine::FormType::kXFAFull) == - FORMTYPE_XFA_FULL, - "XFA full form types must match"); -static_assert(static_cast(PDFEngine::FormType::kXFAForeground) == - FORMTYPE_XFA_FOREGROUND, - "XFA foreground form types must match"); -static_assert(static_cast(PDFEngine::FormType::kCount) == FORMTYPE_COUNT, - "Form type counts must match"); - namespace { constexpr int32_t kHighlightColorR = 153; @@ -234,8 +221,10 @@ bool IsV8Initialized() { void SetUpV8() { const char* recommended = FPDF_GetRecommendedV8Flags(); v8::V8::SetFlagsFromString(recommended, strlen(recommended)); - gin::IsolateHolder::Initialize(gin::IsolateHolder::kNonStrictMode, - gin::ArrayBufferAllocator::SharedInstance()); + gin::IsolateHolder::Initialize( + gin::IsolateHolder::kNonStrictMode, + static_cast( + FPDF_GetArrayBufferAllocatorSharedInstance())); DCHECK(!g_isolate_holder); g_isolate_holder = new gin::IsolateHolder( base::ThreadTaskRunnerHandle::Get(), gin::IsolateHolder::kSingleThread, @@ -365,18 +354,44 @@ void SetLinkUnderCursor(pp::Instance* instance, pp::PDF::SetLinkUnderCursor(instance, link_under_cursor.c_str()); } +base::string16 GetAttachmentAttribute(FPDF_ATTACHMENT attachment, + FPDF_BYTESTRING field) { + return CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAttachment_GetStringValue, attachment, field), + /*check_expected_size=*/true); +} + +unsigned long GetAttachmentFileLengthInBytes(FPDF_ATTACHMENT attachment) { + unsigned long actual_length_bytes; + bool is_attachment_readable = FPDFAttachment_GetFile( + attachment, /*buffer=*/nullptr, /*buflen=*/0, &actual_length_bytes); + DCHECK(is_attachment_readable); + return actual_length_bytes; +} + +base::string16 GetAttachmentName(FPDF_ATTACHMENT attachment) { + return CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAttachment_GetName, attachment), + /*check_expected_size=*/true); +} + } // namespace void InitializeSDK(bool enable_v8) { FPDF_LIBRARY_CONFIG config; - config.version = 2; + config.version = 3; config.m_pUserFontPaths = nullptr; if (enable_v8) { SetUpV8(); config.m_pIsolate = v8::Isolate::GetCurrent(); + // NOTE: static_cast<> prior to assigning to (void*) is safer since it + // will manipulate the pointer value should gin::V8Platform someday have + // multiple base classes. + config.m_pPlatform = static_cast(gin::V8Platform::Get()); } else { config.m_pIsolate = nullptr; + config.m_pPlatform = nullptr; } config.m_v8EmbedderSlot = gin::kEmbedderPDFium; FPDF_InitLibraryWithConfig(&config); @@ -746,6 +761,8 @@ void PDFiumEngine::FinishLoadingDocument() { if (need_update) LoadPageInfo(); + LoadDocumentAttachmentInfoList(); + LoadDocumentMetadata(); if (called_do_document_action_) @@ -948,7 +965,7 @@ void PDFiumEngine::KillFormFocus() { void PDFiumEngine::UpdateFocus(bool has_focus) { base::AutoReset updating_focus_guard(&updating_focus_, true); if (has_focus) { - focus_item_type_ = last_focused_item_type_; + UpdateFocusItemType(last_focused_item_type_); if (focus_item_type_ == FocusElementType::kPage && PageIndexInBounds(last_focused_page_) && last_focused_annot_index_ != -1) { @@ -962,7 +979,7 @@ void PDFiumEngine::UpdateFocus(bool has_focus) { } else { last_focused_item_type_ = focus_item_type_; if (focus_item_type_ == FocusElementType::kDocument) { - focus_item_type_ = FocusElementType::kNone; + UpdateFocusItemType(FocusElementType::kNone); } else if (focus_item_type_ == FocusElementType::kPage) { FPDF_ANNOTATION last_focused_annot = nullptr; FPDF_BOOL ret = FORM_GetFocusedAnnot(form(), &last_focused_page_, @@ -991,21 +1008,15 @@ bool PDFiumEngine::ReadLoadedBytes(uint32_t length, void* buffer) { void PDFiumEngine::SetFormSelectedText(FPDF_FORMHANDLE form_handle, FPDF_PAGE page) { - unsigned long form_sel_text_len = - FORM_GetSelectedText(form_handle, page, nullptr, 0); + base::string16 selected_form_text16 = CallPDFiumWideStringBufferApi( + base::BindRepeating(&FORM_GetSelectedText, form_handle, page), + /*check_expected_size=*/false); // If form selected text is empty and there was no previous form text - // selection, exit early because nothing has changed. When |form_sel_text_len| - // is 2, that represents a wide string with just a NUL-terminator. - if (form_sel_text_len <= 2 && selected_form_text_.empty()) + // selection, exit early because nothing has changed. + if (selected_form_text16.empty() && selected_form_text_.empty()) return; - base::string16 selected_form_text16; - PDFiumAPIStringBufferSizeInBytesAdapter string_adapter( - &selected_form_text16, form_sel_text_len, false); - string_adapter.Close(FORM_GetSelectedText( - form_handle, page, string_adapter.GetData(), form_sel_text_len)); - // Update previous and current selections, then compare them to check if // selection has changed. If so, set plugin text selection. std::string selected_form_text = selected_form_text_; @@ -1098,6 +1109,9 @@ void PDFiumEngine::OnMultipleClick(int click_count, selection_.push_back(PDFiumRange(pages_[page_index].get(), start_index, end_index - start_index)); + + if (handling_long_press_) + client_->NotifyTouchSelectionOccurred(); } bool PDFiumEngine::OnLeftMouseDown(const pp::MouseInputEvent& event) { @@ -1123,7 +1137,7 @@ bool PDFiumEngine::OnLeftMouseDown(const pp::MouseInputEvent& event) { return true; if (page_index != -1) { - focus_item_type_ = FocusElementType::kPage; + UpdateFocusItemType(FocusElementType::kPage); last_focused_page_ = page_index; double page_x; double page_y; @@ -1554,6 +1568,17 @@ bool PDFiumEngine::OnKeyDown(const pp::KeyboardInputEvent& event) { OnChar(synthesized); } +#if !defined(OS_MACOSX) + // macOS doesn't have keyboard-triggered context menus. + // Scroll focused annotation into view when context menu is invoked through + // keyboard . + if (event.GetKeyCode() == FWL_VKEY_F10 && + (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_SHIFTKEY)) { + DCHECK(!rv); + ScrollFocusedAnnotationIntoView(); + } +#endif + return rv; } @@ -1574,8 +1599,17 @@ bool PDFiumEngine::OnChar(const pp::KeyboardInputEvent& event) { return false; base::string16 str = base::UTF8ToUTF16(event.GetCharacterText().AsString()); - return !!FORM_OnChar(form(), pages_[last_focused_page_]->GetPage(), str[0], - event.GetModifiers()); + bool rv = !!FORM_OnChar(form(), pages_[last_focused_page_]->GetPage(), str[0], + event.GetModifiers()); + + // Scroll editable form text into view on char events. We should not scroll + // focused annotation on escape char event since escape char is used to + // dismiss focus from form controls. + if (rv && editable_form_text_area_ && event.GetKeyCode() != ui::VKEY_ESCAPE) { + ScrollFocusedAnnotationIntoView(); + } + + return rv; } void PDFiumEngine::StartFind(const std::string& text, bool case_sensitive) { @@ -1974,6 +2008,14 @@ void PDFiumEngine::SetTwoUpView(bool enable) { ProposeNextDocumentLayout(); } +void PDFiumEngine::DisplayAnnotations(bool display) { + if (render_annots_ == display) + return; + + render_annots_ = display; + InvalidateAllPages(); +} + void PDFiumEngine::InvalidateAllPages() { CancelPaints(); StopFind(); @@ -2124,6 +2166,12 @@ void PDFiumEngine::SelectAll() { } } +const std::vector& +PDFiumEngine::GetDocumentAttachmentInfoList() const { + DCHECK(document_loaded_); + return doc_attachment_info_list_; +} + const DocumentMetadata& PDFiumEngine::GetDocumentMetadata() const { DCHECK(document_loaded_); return doc_metadata_; @@ -2142,14 +2190,9 @@ pp::VarArray PDFiumEngine::GetBookmarks() { pp::VarDictionary PDFiumEngine::TraverseBookmarks(FPDF_BOOKMARK bookmark, unsigned int depth) { pp::VarDictionary dict; - base::string16 title; - unsigned long buffer_size = FPDFBookmark_GetTitle(bookmark, nullptr, 0); - if (buffer_size > 0) { - PDFiumAPIStringBufferSizeInBytesAdapter api_string_adapter( - &title, buffer_size, true); - api_string_adapter.Close(FPDFBookmark_GetTitle( - bookmark, api_string_adapter.GetData(), buffer_size)); - } + base::string16 title = CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFBookmark_GetTitle, bookmark), + /*check_expected_size=*/true); dict.Set(pp::Var("title"), pp::Var(base::UTF16ToUTF8(title))); FPDF_DEST dest = FPDFBookmark_GetDest(doc(), bookmark); @@ -2173,15 +2216,11 @@ pp::VarDictionary PDFiumEngine::TraverseBookmarks(FPDF_BOOKMARK bookmark, } else { // Extract URI for bookmarks linking to an external page. FPDF_ACTION action = FPDFBookmark_GetAction(bookmark); - buffer_size = FPDFAction_GetURIPath(doc(), action, nullptr, 0); - if (buffer_size > 0) { - std::string uri; - PDFiumAPIStringBufferAdapter api_string_adapter( - &uri, buffer_size, true); - api_string_adapter.Close(FPDFAction_GetURIPath( - doc(), action, api_string_adapter.GetData(), buffer_size)); + std::string uri = CallPDFiumStringBufferApi( + base::BindRepeating(&FPDFAction_GetURIPath, doc(), action), + /*check_expected_size=*/true); + if (!uri.empty()) dict.Set(pp::Var("uri"), pp::Var(uri)); - } } pp::VarArray children; @@ -2328,6 +2367,7 @@ void PDFiumEngine::SetGrayscale(bool grayscale) { } void PDFiumEngine::HandleLongPress(const pp::TouchInputEvent& event) { + base::AutoReset handling_long_press_guard(&handling_long_press_, true); pp::FloatPoint fp = event.GetTouchByIndex(PP_TOUCHLIST_TYPE_TARGETTOUCHES, 0).position(); pp::Point point; @@ -2719,7 +2759,8 @@ void PDFiumEngine::LoadForm() { kFormHighlightColor); FPDF_SetFormFieldHighlightAlpha(form(), kFormHighlightAlpha); - if (base::FeatureList::IsEnabled(features::kTabAcrossPDFAnnotations)) { + if (base::FeatureList::IsEnabled(features::kTabAcrossPDFAnnotations) && + !client_->IsPrintPreview()) { static constexpr FPDF_ANNOTATION_SUBTYPE kFocusableAnnotSubtypes[] = { FPDF_ANNOT_LINK, FPDF_ANNOT_HIGHLIGHT, FPDF_ANNOT_WIDGET}; FPDF_BOOL ret = FPDFAnnot_SetFocusableSubtypes( @@ -3558,12 +3599,12 @@ void PDFiumEngine::SetSelecting(bool selecting) { client_->IsSelectingChanged(selecting); } -void PDFiumEngine::SetEditMode(bool edit_mode) { - if (edit_mode_ == edit_mode) +void PDFiumEngine::EnteredEditMode() { + if (edit_mode_) return; - edit_mode_ = edit_mode; - client_->IsEditModeChanged(edit_mode_); + edit_mode_ = true; + client_->EnteredEditMode(); } void PDFiumEngine::SetInFormTextArea(bool in_form_text_area) { @@ -3671,7 +3712,30 @@ void PDFiumEngine::SetSelection( } } -void PDFiumEngine::ScrollIntoView(const pp::Rect& rect) { +void PDFiumEngine::ScrollFocusedAnnotationIntoView() { + FPDF_ANNOTATION annot; + int page_index; + if (!FORM_GetFocusedAnnot(form(), &page_index, &annot)) + return; + + ScrollAnnotationIntoView(annot, page_index); + FPDFPage_CloseAnnot(annot); +} + +void PDFiumEngine::ScrollAnnotationIntoView(FPDF_ANNOTATION annot, + int page_index) { + if (!PageIndexInBounds(page_index)) + return; + + FS_RECTF annot_rect; + if (!FPDFAnnot_GetRect(annot, &annot_rect)) + return; + + pp::Rect rect = pages_[page_index]->PageToScreen( + pp::Point(), /*zoom=*/1.0, annot_rect.left, annot_rect.top, + annot_rect.right, annot_rect.bottom, + layout_.options().default_page_orientation()); + pp::Rect visible_rect = GetVisibleRect(); if (visible_rect.Contains(rect)) return; @@ -3778,6 +3842,28 @@ void PDFiumEngine::GetSelection(uint32_t* selection_start_page_index, } } +void PDFiumEngine::LoadDocumentAttachmentInfoList() { + DCHECK(document_loaded_); + + int attachment_count = FPDFDoc_GetAttachmentCount(doc()); + if (attachment_count <= 0) + return; + + doc_attachment_info_list_.resize(attachment_count); + for (int i = 0; i < attachment_count; ++i) { + FPDF_ATTACHMENT attachment = FPDFDoc_GetAttachment(doc(), i); + DCHECK(attachment); + + doc_attachment_info_list_[i].name = GetAttachmentName(attachment); + doc_attachment_info_list_[i].size_bytes = + GetAttachmentFileLengthInBytes(attachment); + doc_attachment_info_list_[i].creation_date = + GetAttachmentAttribute(attachment, "CreationDate"); + doc_attachment_info_list_[i].modified_date = + GetAttachmentAttribute(attachment, "ModDate"); + } +} + void PDFiumEngine::LoadDocumentMetadata() { DCHECK(document_loaded_); @@ -3793,17 +3879,9 @@ void PDFiumEngine::LoadDocumentMetadata() { std::string PDFiumEngine::GetMetadataByField(FPDF_BYTESTRING field) const { DCHECK(doc()); - size_t size = - FPDF_GetMetaText(doc(), field, /*buffer=*/nullptr, /*buflen=*/0); - if (size == 0) - return std::string(); - - base::string16 value; - PDFiumAPIStringBufferSizeInBytesAdapter string_adapter( - &value, size, /*check_expected_size=*/false); - string_adapter.Close( - FPDF_GetMetaText(doc(), field, string_adapter.GetData(), size)); - return base::UTF16ToUTF8(value); + return base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDF_GetMetaText, doc(), field), + /*check_expected_size=*/false)); } PdfVersion PDFiumEngine::GetDocumentVersion() const { @@ -3869,7 +3947,7 @@ bool PDFiumEngine::HandleTabEventWithModifiers(uint32_t modifiers) { bool PDFiumEngine::HandleTabForward(uint32_t modifiers) { if (focus_item_type_ == FocusElementType::kNone) { - focus_item_type_ = FocusElementType::kDocument; + UpdateFocusItemType(FocusElementType::kDocument); return true; } @@ -3887,17 +3965,17 @@ bool PDFiumEngine::HandleTabForward(uint32_t modifiers) { if (did_tab_forward) { last_focused_page_ = page_index; - focus_item_type_ = FocusElementType::kPage; + UpdateFocusItemType(FocusElementType::kPage); } else { last_focused_page_ = -1; - focus_item_type_ = FocusElementType::kNone; + UpdateFocusItemType(FocusElementType::kNone); } return did_tab_forward; } bool PDFiumEngine::HandleTabBackward(uint32_t modifiers) { if (focus_item_type_ == FocusElementType::kDocument) { - focus_item_type_ = FocusElementType::kNone; + UpdateFocusItemType(FocusElementType::kNone); return false; } @@ -3915,7 +3993,7 @@ bool PDFiumEngine::HandleTabBackward(uint32_t modifiers) { if (did_tab_backward) { last_focused_page_ = page_index; - focus_item_type_ = FocusElementType::kPage; + UpdateFocusItemType(FocusElementType::kPage); } else { // No focusable annotation found in pages. Possible scenarios: // Case 1: |focus_item_type_| is None. Since no object in any page can take @@ -3928,11 +4006,11 @@ bool PDFiumEngine::HandleTabBackward(uint32_t modifiers) { case FocusElementType::kNone: did_tab_backward = true; last_focused_page_ = -1; - focus_item_type_ = FocusElementType::kDocument; + UpdateFocusItemType(FocusElementType::kDocument); KillFormFocus(); break; case FocusElementType::kDocument: - focus_item_type_ = FocusElementType::kNone; + UpdateFocusItemType(FocusElementType::kNone); break; default: NOTREACHED(); @@ -3942,6 +4020,16 @@ bool PDFiumEngine::HandleTabBackward(uint32_t modifiers) { return did_tab_backward; } +void PDFiumEngine::UpdateFocusItemType(FocusElementType focus_item_type) { + if (focus_item_type_ == focus_item_type) + return; + if (focus_item_type_ == FocusElementType::kDocument) + client_->DocumentFocusChanged(false); + focus_item_type_ = focus_item_type; + if (focus_item_type_ == FocusElementType::kDocument) + client_->DocumentFocusChanged(true); +} + #if defined(PDF_ENABLE_XFA) void PDFiumEngine::UpdatePageCount() { InvalidateAllPages(); diff --git a/chromium/pdf/pdfium/pdfium_engine.h b/chromium/pdf/pdfium/pdfium_engine.h index cf507e8d469..2261904a961 100644 --- a/chromium/pdf/pdfium/pdfium_engine.h +++ b/chromium/pdf/pdfium/pdfium_engine.h @@ -17,6 +17,7 @@ #include "base/optional.h" #include "base/time/time.h" #include "base/timer/timer.h" +#include "pdf/document_attachment_info.h" #include "pdf/document_layout.h" #include "pdf/document_loader.h" #include "pdf/document_metadata.h" @@ -99,6 +100,7 @@ class PDFiumEngine : public PDFEngine, void RotateClockwise() override; void RotateCounterclockwise() override; void SetTwoUpView(bool enable) override; + void DisplayAnnotations(bool display) override; pp::Size ApplyDocumentLayout(const DocumentLayout::Options& options) override; std::string GetSelectedText() override; bool CanEditText() override; @@ -113,6 +115,8 @@ class PDFiumEngine : public PDFEngine, std::string GetLinkAtPosition(const pp::Point& point) override; bool HasPermission(DocumentPermission permission) const override; void SelectAll() override; + const std::vector& GetDocumentAttachmentInfoList() + const override; const DocumentMetadata& GetDocumentMetadata() const override; int GetNumberOfPages() override; pp::VarArray GetBookmarks() override; @@ -564,7 +568,7 @@ class PDFiumEngine : public PDFEngine, const pp::Point& global_point); // Set if the document has any local edits. - void SetEditMode(bool edit_mode); + void EnteredEditMode(); // Navigates to a link destination depending on the type of destination. // Returns false if |area| is not a link. @@ -580,12 +584,18 @@ class PDFiumEngine : public PDFEngine, void SetSelection(const PP_PdfPageCharacterIndex& selection_start_index, const PP_PdfPageCharacterIndex& selection_end_index); - // Given |rect| in document coordinates, scroll the |rect| into view if not - // already in view. - void ScrollIntoView(const pp::Rect& rect); + // Scroll the current focused annotation into view if not already in view. + void ScrollFocusedAnnotationIntoView(); + + // Given |annot|, scroll the |annot| into view if not already in view. + void ScrollAnnotationIntoView(FPDF_ANNOTATION annot, int page_index); void OnFocusedAnnotationUpdated(FPDF_ANNOTATION annot, int page_index); + // Read the attachments' information inside the PDF document, and set + // |doc_attachment_info_list_|. To be called after the document is loaded. + void LoadDocumentAttachmentInfoList(); + // Fetches and populates the fields of |doc_metadata_|. To be called after the // document is loaded. void LoadDocumentMetadata(); @@ -606,6 +616,10 @@ class PDFiumEngine : public PDFEngine, bool HandleTabForward(uint32_t modifiers); bool HandleTabBackward(uint32_t modifiers); + // Updates the currently focused object stored in |focus_item_type_|. Notifies + // |client_| about document focus change, if any. + void UpdateFocusItemType(FocusElementType focus_item_type); + void UpdateLinkUnderCursor(const std::string& target_url); void SetLinkUnderCursorForAnnotation(FPDF_ANNOTATION annot, int page_index); @@ -710,6 +724,9 @@ class PDFiumEngine : public PDFEngine, // Timer for touch long press detection. base::OneShotTimer touch_timer_; + // Set to true when handling long touch press. + bool handling_long_press_ = false; + // Set to true when updating plugin focus. bool updating_focus_ = false; @@ -792,6 +809,9 @@ class PDFiumEngine : public PDFEngine, // Shadow matrix for generating the page shadow bitmap. std::unique_ptr page_shadow_; + // A list of information of document attachments. + std::vector doc_attachment_info_list_; + // Stores parsed document metadata. DocumentMetadata doc_metadata_; diff --git a/chromium/pdf/pdfium/pdfium_engine_unittest.cc b/chromium/pdf/pdfium/pdfium_engine_unittest.cc index 4aa29dd8451..978be627972 100644 --- a/chromium/pdf/pdfium/pdfium_engine_unittest.cc +++ b/chromium/pdf/pdfium/pdfium_engine_unittest.cc @@ -4,8 +4,10 @@ #include "pdf/pdfium/pdfium_engine.h" +#include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "pdf/document_attachment_info.h" #include "pdf/document_layout.h" #include "pdf/document_metadata.h" #include "pdf/pdf_features.h" @@ -185,6 +187,53 @@ TEST_F(PDFiumEngineTest, ApplyDocumentLayoutAvoidsInfiniteLoop) { CompareSize({343, 1463}, engine->ApplyDocumentLayout(options)); } +TEST_F(PDFiumEngineTest, GetDocumentAttachmentInfo) { + NiceMock client; + std::unique_ptr engine = + InitializeEngine(&client, FILE_PATH_LITERAL("embedded_attachments.pdf")); + ASSERT_TRUE(engine); + + const std::vector& attachments = + engine->GetDocumentAttachmentInfoList(); + ASSERT_EQ(3u, attachments.size()); + + { + const DocumentAttachmentInfo& attachment = attachments[0]; + EXPECT_EQ("1.txt", base::UTF16ToUTF8(attachment.name)); + EXPECT_EQ(4u, attachment.size_bytes); + EXPECT_EQ("D:20170712214438-07'00'", + base::UTF16ToUTF8(attachment.creation_date)); + EXPECT_EQ("D:20160115091400", base::UTF16ToUTF8(attachment.modified_date)); + } + + { + const DocumentAttachmentInfo& attachment = attachments[1]; + EXPECT_EQ("attached.pdf", base::UTF16ToUTF8(attachment.name)); + EXPECT_EQ(5869u, attachment.size_bytes); + EXPECT_EQ("D:20170712214443-07'00'", + base::UTF16ToUTF8(attachment.creation_date)); + EXPECT_EQ("D:20170712214410", base::UTF16ToUTF8(attachment.modified_date)); + } + + { + // Test attachments with no creation date or last modified date. + const DocumentAttachmentInfo& attachment = attachments[2]; + EXPECT_EQ("附錄.txt", base::UTF16ToUTF8(attachment.name)); + EXPECT_EQ(5u, attachment.size_bytes); + EXPECT_THAT(attachment.creation_date, IsEmpty()); + EXPECT_THAT(attachment.modified_date, IsEmpty()); + } +} + +TEST_F(PDFiumEngineTest, NoDocumentAttachmentInfo) { + NiceMock client; + std::unique_ptr engine = + InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf")); + ASSERT_TRUE(engine); + + EXPECT_EQ(0u, engine->GetDocumentAttachmentInfoList().size()); +} + TEST_F(PDFiumEngineTest, GetDocumentMetadata) { NiceMock client; std::unique_ptr engine = @@ -229,6 +278,17 @@ TEST_F(PDFiumEngineTest, GetBadPdfVersion) { } // namespace +class TabbingTestClient : public TestClient { + public: + TabbingTestClient() = default; + ~TabbingTestClient() override = default; + TabbingTestClient(const TabbingTestClient&) = delete; + TabbingTestClient& operator=(const TabbingTestClient&) = delete; + + // Mock PDFEngine::Client methods. + MOCK_METHOD1(DocumentFocusChanged, void(bool)); +}; + class PDFiumEngineTabbingTest : public PDFiumTestBase { public: PDFiumEngineTabbingTest() = default; @@ -269,6 +329,10 @@ class PDFiumEngineTabbingTest : public PDFiumTestBase { return engine->link_under_cursor_; } + void ScrollFocusedAnnotationIntoView(PDFiumEngine* engine) { + engine->ScrollFocusedAnnotationIntoView(); + } + protected: base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; @@ -378,13 +442,20 @@ TEST_F(PDFiumEngineTabbingTest, TabbingForwardTest) { * ++ Page 2 * ++++ Annotation */ - TestClient client; + TabbingTestClient client; std::unique_ptr engine = InitializeEngine( &client, FILE_PATH_LITERAL("annotation_form_fields.pdf")); ASSERT_TRUE(engine); ASSERT_EQ(2, engine->GetNumberOfPages()); + static constexpr bool kExpectedFocusState[] = {true, false}; + { + InSequence sequence; + for (auto focused : kExpectedFocusState) + EXPECT_CALL(client, DocumentFocusChanged(focused)); + } + ASSERT_EQ(PDFiumEngine::FocusElementType::kNone, GetFocusedElementType(engine.get())); EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); @@ -423,13 +494,20 @@ TEST_F(PDFiumEngineTabbingTest, TabbingBackwardTest) { * ++ Page 2 * ++++ Annotation */ - TestClient client; + TabbingTestClient client; std::unique_ptr engine = InitializeEngine( &client, FILE_PATH_LITERAL("annotation_form_fields.pdf")); ASSERT_TRUE(engine); ASSERT_EQ(2, engine->GetNumberOfPages()); + static constexpr bool kExpectedFocusState[] = {true, false}; + { + InSequence sequence; + for (auto focused : kExpectedFocusState) + EXPECT_CALL(client, DocumentFocusChanged(focused)); + } + ASSERT_EQ(PDFiumEngine::FocusElementType::kNone, GetFocusedElementType(engine.get())); EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); @@ -512,13 +590,20 @@ TEST_F(PDFiumEngineTabbingTest, NoFocusableItemTabbingTest) { * ++ Page 1 * ++ Page 2 */ - TestClient client; + TabbingTestClient client; std::unique_ptr engine = InitializeEngine(&client, FILE_PATH_LITERAL("hello_world2.pdf")); ASSERT_TRUE(engine); ASSERT_EQ(2, engine->GetNumberOfPages()); + static constexpr bool kExpectedFocusState[] = {true, false, true, false}; + { + InSequence sequence; + for (auto focused : kExpectedFocusState) + EXPECT_CALL(client, DocumentFocusChanged(focused)); + } + ASSERT_EQ(PDFiumEngine::FocusElementType::kNone, GetFocusedElementType(engine.get())); EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); @@ -552,13 +637,20 @@ TEST_F(PDFiumEngineTabbingTest, RestoringDocumentFocusTest) { * ++ Page 2 * ++++ Annotation */ - TestClient client; + TabbingTestClient client; std::unique_ptr engine = InitializeEngine( &client, FILE_PATH_LITERAL("annotation_form_fields.pdf")); ASSERT_TRUE(engine); ASSERT_EQ(2, engine->GetNumberOfPages()); + static constexpr bool kExpectedFocusState[] = {true, false, true}; + { + InSequence sequence; + for (auto focused : kExpectedFocusState) + EXPECT_CALL(client, DocumentFocusChanged(focused)); + } + EXPECT_EQ(PDFiumEngine::FocusElementType::kNone, GetFocusedElementType(engine.get())); EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); @@ -590,13 +682,20 @@ TEST_F(PDFiumEngineTabbingTest, RestoringAnnotFocusTest) { * ++ Page 2 * ++++ Annotation */ - TestClient client; + TabbingTestClient client; std::unique_ptr engine = InitializeEngine( &client, FILE_PATH_LITERAL("annotation_form_fields.pdf")); ASSERT_TRUE(engine); ASSERT_EQ(2, engine->GetNumberOfPages()); + static constexpr bool kExpectedFocusState[] = {true, false}; + { + InSequence sequence; + for (auto focused : kExpectedFocusState) + EXPECT_CALL(client, DocumentFocusChanged(focused)); + } + EXPECT_EQ(PDFiumEngine::FocusElementType::kNone, GetFocusedElementType(engine.get())); EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); @@ -773,4 +872,51 @@ TEST_F(PDFiumEngineTabbingTest, MaintainViewportWhenFocusIsUpdated) { EXPECT_EQ(0, GetLastFocusedPage(engine.get())); } +TEST_F(PDFiumEngineTabbingTest, ScrollFocusedAnnotationIntoView) { + StrictMock client; + std::unique_ptr engine = InitializeEngine( + &client, FILE_PATH_LITERAL("annotation_form_fields.pdf")); + ASSERT_TRUE(engine); + ASSERT_EQ(2, engine->GetNumberOfPages()); + engine->PluginSizeUpdated(pp::Size(60, 40)); + + { + InSequence sequence; + static constexpr PP_Point kScrollValues[] = {{510, 478}, {510, 478}}; + + for (const auto& scroll_value : kScrollValues) { + EXPECT_CALL(client, ScrollToY(scroll_value.y, false)) + .WillOnce(Invoke([&engine, &scroll_value]() { + engine->ScrolledToYPosition(scroll_value.y); + })); + EXPECT_CALL(client, ScrollToX(scroll_value.x)) + .WillOnce(Invoke([&engine, &scroll_value]() { + engine->ScrolledToXPosition(scroll_value.x); + })); + } + } + + EXPECT_EQ(PDFiumEngine::FocusElementType::kNone, + GetFocusedElementType(engine.get())); + EXPECT_EQ(-1, GetLastFocusedPage(engine.get())); + + // Tabbing to bring the document into focus. + ASSERT_TRUE(HandleTabEvent(engine.get(), 0)); + EXPECT_EQ(PDFiumEngine::FocusElementType::kDocument, + GetFocusedElementType(engine.get())); + + // Tab to an annotation. + ASSERT_TRUE(HandleTabEvent(engine.get(), 0)); + EXPECT_EQ(PDFiumEngine::FocusElementType::kPage, + GetFocusedElementType(engine.get())); + + // Scroll focused annotation out of viewport. + static constexpr PP_Point kScrollPosition = {242, 746}; + engine->ScrolledToXPosition(kScrollPosition.x); + engine->ScrolledToYPosition(kScrollPosition.y); + + // Scroll the focused annotation into view. + ScrollFocusedAnnotationIntoView(engine.get()); +} + } // namespace chrome_pdf diff --git a/chromium/pdf/pdfium/pdfium_form_filler.cc b/chromium/pdf/pdfium/pdfium_form_filler.cc index 4a142b4cba5..d85bd2ceef1 100644 --- a/chromium/pdf/pdfium/pdfium_form_filler.cc +++ b/chromium/pdf/pdfium/pdfium_form_filler.cc @@ -194,7 +194,7 @@ FPDF_SYSTEMTIME PDFiumFormFiller::Form_GetLocalTime(FPDF_FORMFILLINFO* param) { // static void PDFiumFormFiller::Form_OnChange(FPDF_FORMFILLINFO* param) { PDFiumEngine* engine = GetEngine(param); - engine->SetEditMode(true); + engine->EnteredEditMode(); } // static @@ -285,17 +285,8 @@ void PDFiumFormFiller::Form_OnFocusChange(FPDF_FORMFILLINFO* param, // Maintain viewport if we are updating focus. This is to ensure that we don't // scroll the focused annotation into view when focus is regained. - if (!engine->updating_focus_) { - FS_RECTF annot_rect; - if (!FPDFAnnot_GetRect(annot, &annot_rect)) - return; - - pp::Rect screen_rect = engine->pages_[page_index]->PageToScreen( - pp::Point(), /*zoom=*/1.0, annot_rect.left, annot_rect.top, - annot_rect.right, annot_rect.bottom, - engine->layout_.options().default_page_orientation()); - engine->ScrollIntoView(screen_rect); - } + if (!engine->updating_focus_) + engine->ScrollAnnotationIntoView(annot, page_index); engine->OnFocusedAnnotationUpdated(annot, page_index); } diff --git a/chromium/pdf/pdfium/pdfium_page.cc b/chromium/pdf/pdfium/pdfium_page.cc index 4f5f5af65b8..6f95affddc8 100644 --- a/chromium/pdf/pdfium/pdfium_page.cc +++ b/chromium/pdf/pdfium/pdfium_page.cc @@ -186,24 +186,6 @@ bool FloatEquals(float f1, float f2) { kEpsilonScale * fmaxf(fmaxf(fabsf(f1), fabsf(f2)), kEpsilonScale); } -using GetFormFieldPropertyFunction = - base::RepeatingCallback; - -// Helper method to fetch string properties of form fields. -std::string GetFormFieldProperty(GetFormFieldPropertyFunction function) { - base::string16 data; - size_t buffer_size = function.Run(nullptr, 0); - if (buffer_size > 0) { - PDFiumAPIStringBufferSizeInBytesAdapter api_string_adapter( - &data, buffer_size, true); - api_string_adapter.Close(function.Run( - reinterpret_cast(api_string_adapter.GetData()), - buffer_size)); - } - return base::UTF16ToUTF8(data); -} - // Count overlaps across text annotations. template uint32_t CountOverlaps(const std::vector& first_set, @@ -472,6 +454,18 @@ PDFiumPage::GetTextRunInfo(int start_char_index) { info.direction = PP_PRIVATEDIRECTION_NONE; return info; } + + // If the first character in a text run is a space, we need to start + // |text_run_bounds| from the space character instead of the first + // non-space unicode character. + pp::FloatRect text_run_bounds = + actual_start_char_index > start_char_index + ? GetFloatCharRectInPixels(page, text_page, start_char_index) + : pp::FloatRect(); + + // Pdfium trims more than 1 consecutive spaces to 1 space. + DCHECK_LE(actual_start_char_index - start_char_index, 1); + int char_index = actual_start_char_index; // Set text run's style info from the first character of the text run. @@ -494,8 +488,8 @@ PDFiumPage::GetTextRunInfo(int start_char_index) { AddCharSizeToAverageCharSize(start_char_rect.Floatsize(), &avg_char_size, &non_whitespace_chars_count); - // Add first char to text run. - pp::FloatRect text_run_bounds = start_char_rect; + // Add first non-space char to text run. + text_run_bounds = text_run_bounds.Union(start_char_rect); PP_PrivateDirection char_direction = GetDirectionFromAngle(FPDFText_GetCharAngle(text_page, char_index)); if (char_index < chars_count) @@ -676,6 +670,7 @@ PDFiumPage::GetHighlightInfo() { highlight.bounding_rect.x(), highlight.bounding_rect.y(), highlight.bounding_rect.width(), highlight.bounding_rect.height()); cur_info.color = highlight.color; + cur_info.note_text = highlight.note_text; highlight_info.push_back(std::move(cur_info)); } return highlight_info; @@ -900,16 +895,11 @@ gfx::PointF PDFiumPage::TransformPageToScreenXY(const gfx::PointF& xy) { PDFiumPage::Area PDFiumPage::GetURITarget(FPDF_ACTION uri_action, LinkTarget* target) const { if (target) { - size_t buffer_size = - FPDFAction_GetURIPath(engine_->doc(), uri_action, nullptr, 0); - if (buffer_size > 0) { - PDFiumAPIStringBufferAdapter api_string_adapter( - &target->url, buffer_size, true); - void* data = api_string_adapter.GetData(); - size_t bytes_written = - FPDFAction_GetURIPath(engine_->doc(), uri_action, data, buffer_size); - api_string_adapter.Close(bytes_written); - } + std::string url = CallPDFiumStringBufferApi( + base::BindRepeating(&FPDFAction_GetURIPath, engine_->doc(), uri_action), + /*check_expected_size=*/true); + if (!url.empty()) + target->url = url; } return WEBLINK_AREA; } @@ -956,6 +946,8 @@ void PDFiumPage::PopulateWebLinks() { ScopedFPDFPageLink links(FPDFLink_LoadWebLinks(GetTextPage())); int count = FPDFLink_CountWebLinks(links.get()); for (int i = 0; i < count; ++i) { + // WARNING: FPDFLink_GetURL() is not compatible with + // CallPDFiumWideStringBufferApi(). base::string16 url; int url_length = FPDFLink_GetURL(links.get(), i, nullptr, 0); if (url_length > 0) { @@ -1141,16 +1133,11 @@ void PDFiumPage::PopulateImageAltTextForStructElement( auto it = marked_content_id_image_map.find(marked_content_id); if (it != marked_content_id_image_map.end() && images_[it->second].alt_text.empty()) { - size_t buffer_size = - FPDF_StructElement_GetAltText(current_element, nullptr, 0); - if (buffer_size > 0) { - base::string16 alt_text; - PDFiumAPIStringBufferSizeInBytesAdapter - api_string_adapter(&alt_text, buffer_size, true); - api_string_adapter.Close(FPDF_StructElement_GetAltText( - current_element, api_string_adapter.GetData(), buffer_size)); - images_[it->second].alt_text = base::UTF16ToUTF8(alt_text); - } + images_[it->second].alt_text = + base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDF_StructElement_GetAltText, + current_element), + /*check_expected_size=*/true)); } } int children_count = FPDF_StructElement_CountChildren(current_element); @@ -1182,11 +1169,7 @@ void PDFiumPage::PopulateAnnotations() { break; } case FPDF_ANNOT_WIDGET: { - // TODO(crbug.com/1030242): Populate other types of form fields too. - if (FPDFAnnot_GetFormFieldType(engine_->form(), annot.get()) == - FPDF_FORMFIELD_TEXTFIELD) { - PopulateTextField(annot.get()); - } + PopulateFormField(annot.get()); break; } default: @@ -1230,6 +1213,14 @@ void PDFiumPage::PopulateHighlight(FPDF_ANNOTATION annot) { highlight.color = MakeARGB(255, 255, 255, 0); } + // Retrieve the contents of the popup note associated with highlight. + // See table 164 in ISO 32000-1 standard for more details around "Contents" + // key in a highlight annotation. + static constexpr char kContents[] = "Contents"; + highlight.note_text = base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAnnot_GetStringValue, annot, kContents), + /*check_expected_size=*/true)); + highlights_.push_back(std::move(highlight)); } @@ -1239,21 +1230,81 @@ void PDFiumPage::PopulateTextField(FPDF_ANNOTATION annot) { DCHECK_EQ(FPDFAnnot_GetFormFieldType(form_handle, annot), FPDF_FORMFIELD_TEXTFIELD); + TextField text_field; + if (!PopulateFormFieldProperties(annot, &text_field)) + return; + + text_field.value = base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAnnot_GetFormFieldValue, form_handle, annot), + /*check_expected_size=*/true)); + text_fields_.push_back(std::move(text_field)); +} + +void PDFiumPage::PopulateChoiceField(FPDF_ANNOTATION annot) { + DCHECK(annot); + FPDF_FORMHANDLE form_handle = engine_->form(); + int form_field_type = FPDFAnnot_GetFormFieldType(form_handle, annot); + DCHECK(form_field_type == FPDF_FORMFIELD_LISTBOX || + form_field_type == FPDF_FORMFIELD_COMBOBOX); + + ChoiceField choice_field; + if (!PopulateFormFieldProperties(annot, &choice_field)) + return; + + int options_count = FPDFAnnot_GetOptionCount(form_handle, annot); + if (options_count < 0) + return; + + choice_field.options.resize(options_count); + for (int i = 0; i < options_count; ++i) { + choice_field.options[i].name = + base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAnnot_GetOptionLabel, form_handle, annot, + i), + /*check_expected_size=*/true)); + choice_field.options[i].is_selected = + FPDFAnnot_IsOptionSelected(form_handle, annot, i); + } + choice_fields_.push_back(std::move(choice_field)); +} + +void PDFiumPage::PopulateFormField(FPDF_ANNOTATION annot) { + DCHECK_EQ(FPDFAnnot_GetSubtype(annot), FPDF_ANNOT_WIDGET); + int form_field_type = FPDFAnnot_GetFormFieldType(engine_->form(), annot); + + // TODO(crbug.com/1030242): Populate other types of form fields too. + switch (form_field_type) { + case FPDF_FORMFIELD_COMBOBOX: + case FPDF_FORMFIELD_LISTBOX: { + PopulateChoiceField(annot); + break; + } + case FPDF_FORMFIELD_TEXTFIELD: { + PopulateTextField(annot); + break; + } + default: + break; + } +} + +bool PDFiumPage::PopulateFormFieldProperties(FPDF_ANNOTATION annot, + FormField* form_field) { + DCHECK(annot); FS_RECTF rect; if (!FPDFAnnot_GetRect(annot, &rect)) - return; + return false; - TextField text_field; - // We use the bounding box of the text field as the bounding rect. - text_field.bounding_rect = + // We use the bounding box of the form field as the bounding rect. + form_field->bounding_rect = PageToScreen(pp::Point(), 1.0, rect.left, rect.top, rect.right, rect.bottom, PageOrientation::kOriginal); - text_field.value = GetFormFieldProperty( - base::BindRepeating(FPDFAnnot_GetFormFieldValue, form_handle, annot)); - text_field.name = GetFormFieldProperty( - base::BindRepeating(FPDFAnnot_GetFormFieldName, form_handle, annot)); - text_field.flags = FPDFAnnot_GetFormFieldFlags(form_handle, annot); - text_fields_.push_back(std::move(text_field)); + FPDF_FORMHANDLE form_handle = engine_->form(); + form_field->name = base::UTF16ToUTF8(CallPDFiumWideStringBufferApi( + base::BindRepeating(&FPDFAnnot_GetFormFieldName, form_handle, annot), + /*check_expected_size=*/true)); + form_field->flags = FPDFAnnot_GetFormFieldFlags(form_handle, annot); + return true; } bool PDFiumPage::GetUnderlyingTextRangeForRect(const pp::FloatRect& rect, @@ -1385,12 +1436,31 @@ PDFiumPage::Highlight::Highlight(const Highlight& that) = default; PDFiumPage::Highlight::~Highlight() = default; +PDFiumPage::FormField::FormField() = default; + +PDFiumPage::FormField::FormField(const FormField& that) = default; + +PDFiumPage::FormField::~FormField() = default; + PDFiumPage::TextField::TextField() = default; PDFiumPage::TextField::TextField(const TextField& that) = default; PDFiumPage::TextField::~TextField() = default; +PDFiumPage::ChoiceFieldOption::ChoiceFieldOption() = default; + +PDFiumPage::ChoiceFieldOption::ChoiceFieldOption( + const ChoiceFieldOption& that) = default; + +PDFiumPage::ChoiceFieldOption::~ChoiceFieldOption() = default; + +PDFiumPage::ChoiceField::ChoiceField() = default; + +PDFiumPage::ChoiceField::ChoiceField(const ChoiceField& that) = default; + +PDFiumPage::ChoiceField::~ChoiceField() = default; + // static uint32_t PDFiumPage::CountLinkHighlightOverlaps( const std::vector& links, diff --git a/chromium/pdf/pdfium/pdfium_page.h b/chromium/pdf/pdfium/pdfium_page.h index 5a3aefae9da..baaec24e081 100644 --- a/chromium/pdf/pdfium/pdfium_page.h +++ b/chromium/pdf/pdfium/pdfium_page.h @@ -178,63 +178,10 @@ class PDFiumPage { FRIEND_TEST_ALL_PREFIXES(PDFiumPageLinkTest, TestLinkGeneration); FRIEND_TEST_ALL_PREFIXES(PDFiumPageHighlightTest, TestPopulateHighlights); FRIEND_TEST_ALL_PREFIXES(PDFiumPageTextFieldTest, TestPopulateTextFields); + FRIEND_TEST_ALL_PREFIXES(PDFiumPageChoiceFieldTest, TestPopulateChoiceFields); FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountPartialOverlaps); FRIEND_TEST_ALL_PREFIXES(PDFiumPageOverlappingTest, CountCompleteOverlaps); - // Returns a link index if the given character index is over a link, or -1 - // otherwise. - int GetLink(int char_index, LinkTarget* target); - // Calculate the locations of any links on the page. - void CalculateLinks(); - // Populates weblinks on the page. - void PopulateWebLinks(); - // Populates annotation links on the page. - void PopulateAnnotationLinks(); - // Calculate the locations of images on the page. - void CalculateImages(); - // Populate annotations like highlight and text field on the page. - void PopulateAnnotations(); - // Populate |highlights_| with |annot|. - void PopulateHighlight(FPDF_ANNOTATION annot); - // Populate |text_fields_| with |annot|. - void PopulateTextField(FPDF_ANNOTATION annot); - // Returns link type and fills target associated with a destination. Returns - // NONSELECTABLE_AREA if detection failed. - Area GetDestinationTarget(FPDF_DEST destination, LinkTarget* target); - // Returns link type and fills target associated with a URI action. Returns - // NONSELECTABLE_AREA if detection failed. - Area GetURITarget(FPDF_ACTION uri_action, LinkTarget* target) const; - // Calculates the set of character indices on which text runs need to be - // broken for page objects such as links and images. - void CalculatePageObjectTextRunBreaks(); - // Set text run style information based on a character of the text run. - void CalculateTextRunStyleInfo( - int char_index, - pp::PDF::PrivateAccessibilityTextStyleInfo* style_info); - // Returns a boolean indicating if the character at index |char_index| has the - // same text style as the text run. - bool AreTextStyleEqual( - int char_index, - const pp::PDF::PrivateAccessibilityTextStyleInfo& style); - - // Key : Marked content id for the image element as specified in the - // struct tree. - // Value : Index of image in the |images_| vector. - using MarkedContentIdToImageMap = std::map; - // Traverses the entire struct tree of the page recursively and extracts the - // alt text from struct tree elements corresponding to the marked content IDs - // present in |marked_content_id_image_map|. - void PopulateImageAltText( - const MarkedContentIdToImageMap& marked_content_id_image_map); - // Traverses a struct element and its sub-tree recursively and extracts the - // alt text from struct elements corresponding to the marked content IDs - // present in |marked_content_id_image_map|. Uses |visited_elements| to guard - // against malformed struct trees. - void PopulateImageAltTextForStructElement( - const MarkedContentIdToImageMap& marked_content_id_image_map, - FPDF_STRUCTELEMENT current_element, - std::set* visited_elements); - class ScopedUnloadPreventer { public: explicit ScopedUnloadPreventer(PDFiumPage* page); @@ -284,25 +231,114 @@ class PDFiumPage { // Color of the highlight in ARGB. Alpha is stored in the first 8 MSBs. RGB // follows after it with each using 8 bytes. uint32_t color; + + // Text of the popup note associated with highlight. + std::string note_text; + }; + + // Represents a form field within the page. + struct FormField { + FormField(); + FormField(const FormField& other); + ~FormField(); + + pp::Rect bounding_rect; + // Represents the name of form field as defined in the field dictionary. + std::string name; + // Represents the flags of form field as defined in the field dictionary. + int flags; }; // Represents a text field within the page. - struct TextField { + struct TextField : FormField { TextField(); TextField(const TextField& other); ~TextField(); - // Represents the name of form field as defined in the field dictionary. - std::string name; std::string value; - pp::Rect bounding_rect; - // Represents the flags of form field as defined in the field dictionary. - int flags; }; + // Represents a choice field option. + struct ChoiceFieldOption { + ChoiceFieldOption(); + ChoiceFieldOption(const ChoiceFieldOption& other); + ~ChoiceFieldOption(); + + std::string name; + bool is_selected; + }; + + // Represents a choice field within the page. + struct ChoiceField : FormField { + ChoiceField(); + ChoiceField(const ChoiceField& other); + ~ChoiceField(); + + std::vector options; + }; + + // Returns a link index if the given character index is over a link, or -1 + // otherwise. + int GetLink(int char_index, LinkTarget* target); + // Calculate the locations of any links on the page. + void CalculateLinks(); + // Populates weblinks on the page. + void PopulateWebLinks(); + // Populates annotation links on the page. + void PopulateAnnotationLinks(); + // Calculate the locations of images on the page. + void CalculateImages(); + // Populate annotations like highlight and text field on the page. + void PopulateAnnotations(); + // Populate |highlights_| with |annot|. + void PopulateHighlight(FPDF_ANNOTATION annot); + // Populate |text_fields_| with |annot|. + void PopulateTextField(FPDF_ANNOTATION annot); + // Populate |choice_fields_| with |annot|. + void PopulateChoiceField(FPDF_ANNOTATION annot); + // Populate form fields like text field and choice field on the page. + void PopulateFormField(FPDF_ANNOTATION annot); + // Returns link type and fills target associated with a destination. Returns + // NONSELECTABLE_AREA if detection failed. + Area GetDestinationTarget(FPDF_DEST destination, LinkTarget* target); + // Returns link type and fills target associated with a URI action. Returns + // NONSELECTABLE_AREA if detection failed. + Area GetURITarget(FPDF_ACTION uri_action, LinkTarget* target) const; + // Calculates the set of character indices on which text runs need to be + // broken for page objects such as links and images. + void CalculatePageObjectTextRunBreaks(); + // Set text run style information based on a character of the text run. + void CalculateTextRunStyleInfo( + int char_index, + pp::PDF::PrivateAccessibilityTextStyleInfo* style_info); + // Returns a boolean indicating if the character at index |char_index| has the + // same text style as the text run. + bool AreTextStyleEqual( + int char_index, + const pp::PDF::PrivateAccessibilityTextStyleInfo& style); + + // Key : Marked content id for the image element as specified in the + // struct tree. + // Value : Index of image in the |images_| vector. + using MarkedContentIdToImageMap = std::map; + // Traverses the entire struct tree of the page recursively and extracts the + // alt text from struct tree elements corresponding to the marked content IDs + // present in |marked_content_id_image_map|. + void PopulateImageAltText( + const MarkedContentIdToImageMap& marked_content_id_image_map); + // Traverses a struct element and its sub-tree recursively and extracts the + // alt text from struct elements corresponding to the marked content IDs + // present in |marked_content_id_image_map|. Uses |visited_elements| to guard + // against malformed struct trees. + void PopulateImageAltTextForStructElement( + const MarkedContentIdToImageMap& marked_content_id_image_map, + FPDF_STRUCTELEMENT current_element, + std::set* visited_elements); static uint32_t CountLinkHighlightOverlaps( const std::vector& links, const std::vector& highlights); + bool PopulateFormFieldProperties(FPDF_ANNOTATION annot, + FormField* form_field); PDFiumEngine* engine_; ScopedFPDFPage page_; @@ -317,6 +353,7 @@ class PDFiumPage { bool calculated_annotations_ = false; std::vector highlights_; std::vector text_fields_; + std::vector choice_fields_; bool logged_overlapping_annotations_ = false; bool calculated_page_object_text_run_breaks_ = false; // The set of character indices on which text runs need to be broken for page diff --git a/chromium/pdf/pdfium/pdfium_page_unittest.cc b/chromium/pdf/pdfium/pdfium_page_unittest.cc index 7906515599d..fae64ca8f2e 100644 --- a/chromium/pdf/pdfium/pdfium_page_unittest.cc +++ b/chromium/pdf/pdfium/pdfium_page_unittest.cc @@ -8,6 +8,8 @@ #include #include "base/check.h" +#include "base/optional.h" +#include "base/strings/string_util.h" #include "base/test/gtest_util.h" #include "pdf/pdfium/pdfium_engine.h" #include "pdf/pdfium/pdfium_test_base.h" @@ -234,6 +236,80 @@ TEST_F(PDFiumPageImageTest, TestImageAltText) { using PDFiumPageTextTest = PDFiumTestBase; +TEST_F(PDFiumPageTextTest, TestTextRunBounds) { + TestClient client; + std::unique_ptr engine = InitializeEngine( + &client, FILE_PATH_LITERAL("leading_trailing_spaces_per_text_run.pdf")); + ASSERT_TRUE(engine); + + constexpr int kFirstRunStartIndex = 0; + constexpr int kFirstRunEndIndex = 20; + constexpr int kPageIndex = 0; + base::Optional text_run_info_1 = + engine->GetTextRunInfo(kPageIndex, kFirstRunStartIndex); + ASSERT_TRUE(text_run_info_1.has_value()); + + const auto& actual_text_run_1 = text_run_info_1.value(); + EXPECT_EQ(21u, actual_text_run_1.len); + + EXPECT_TRUE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kFirstRunStartIndex))); + pp::FloatRect text_run_bounds = actual_text_run_1.bounds; + EXPECT_TRUE(text_run_bounds.Contains( + engine->GetCharBounds(kPageIndex, kFirstRunStartIndex))); + + // Last non-space character should fall in the bounding box of the text run. + // Text run looks like this: + // " Hello, world! \r\n "<17 characters> + // " \r\n "<4 characters> + // " "<1 character> + // Finally generated text run: " Hello, world! \r\n \r\n " + constexpr int kFirstRunLastNonSpaceCharIndex = 13; + EXPECT_FALSE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kFirstRunLastNonSpaceCharIndex))); + EXPECT_TRUE(text_run_bounds.Contains( + engine->GetCharBounds(kPageIndex, kFirstRunLastNonSpaceCharIndex))); + + EXPECT_TRUE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kFirstRunEndIndex))); + pp::FloatRect end_char_rect = + engine->GetCharBounds(kPageIndex, kFirstRunEndIndex); + EXPECT_FALSE(text_run_bounds.Contains(end_char_rect)); + // Equals to the length of the previous text run. + constexpr int kSecondRunStartIndex = 21; + constexpr int kSecondRunEndIndex = 36; + // Test the properties of second text run. + // Note: The leading spaces in second text run are accounted for in the end + // of first text run. Hence we won't see a space leading the second text run. + base::Optional text_run_info_2 = + engine->GetTextRunInfo(kPageIndex, kSecondRunStartIndex); + ASSERT_TRUE(text_run_info_2.has_value()); + + const auto& actual_text_run_2 = text_run_info_2.value(); + EXPECT_EQ(16u, actual_text_run_2.len); + + EXPECT_FALSE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kSecondRunStartIndex))); + text_run_bounds = actual_text_run_2.bounds; + EXPECT_TRUE(text_run_bounds.Contains( + engine->GetCharBounds(kPageIndex, kSecondRunStartIndex))); + + // Last non-space character should fall in the bounding box of the text run. + // Text run looks like this: + // "Goodbye, world! "<19 characters> + // Finally generated text run: "Goodbye, world! " + constexpr int kSecondRunLastNonSpaceCharIndex = 35; + EXPECT_FALSE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kSecondRunLastNonSpaceCharIndex))); + EXPECT_TRUE(text_run_bounds.Contains( + engine->GetCharBounds(kPageIndex, kSecondRunLastNonSpaceCharIndex))); + + EXPECT_TRUE(base::IsUnicodeWhitespace( + engine->GetCharUnicode(kPageIndex, kSecondRunEndIndex))); + EXPECT_FALSE(text_run_bounds.Contains( + engine->GetCharBounds(kPageIndex, kSecondRunEndIndex))); +} + TEST_F(PDFiumPageTextTest, GetTextRunInfo) { TestClient client; std::unique_ptr engine = @@ -333,7 +409,7 @@ TEST_F(PDFiumPageTextTest, TestHighlightTextRunInfo) { {7, PP_MakeFloatRectFromXYWH(106.66666f, 198.66667f, 73.333336f, 18.666672f), PP_PrivateDirection::PP_PRIVATEDIRECTION_LTR, kExpectedStyle}, - {2, PP_MakeFloatRectFromXYWH(188.0f, 202.66667f, 9.333333f, 14.666667f), + {2, PP_MakeFloatRectFromXYWH(181.33333f, 192.0f, 16.0f, 25.333344f), PP_PrivateDirection::PP_PRIVATEDIRECTION_NONE, kExpectedStyle}, {2, PP_MakeFloatRectFromXYWH(198.66667f, 202.66667f, 21.333328f, 10.666672f), @@ -437,6 +513,94 @@ TEST_F(PDFiumPageTextFieldTest, TestPopulateTextFields) { } } +using PDFiumPageChoiceFieldTest = PDFiumTestBase; + +TEST_F(PDFiumPageChoiceFieldTest, TestPopulateChoiceFields) { + struct ExpectedChoiceFieldOption { + const char* name; + bool is_selected; + }; + + struct ExpectedChoiceField { + const char* name; + std::vector options; + pp::Rect bounding_rect; + int flags; + }; + + static const ExpectedChoiceField kExpectedChoiceFields[] = { + {"Listbox_SingleSelect", + {{"Foo", false}, {"Bar", false}, {"Qux", false}}, + {138, 296, 135, 41}, + 0}, + {"Combo1", + {{"Apple", false}, {"Banana", true}, {"Cherry", false}}, + {138, 230, 135, 41}, + 131072}, + {"Listbox_ReadOnly", + {{"Dog", false}, {"Elephant", false}, {"Frog", false}}, + {138, 96, 135, 41}, + 1}, + {"Listbox_MultiSelectMultipleIndices", + { + {"Albania", false}, + {"Belgium", true}, + {"Croatia", false}, + {"Denmark", true}, + {"Estonia", false}, + }, + {138, 430, 135, 41}, + 2097152}, + {"Listbox_MultiSelectMultipleValues", + { + {"Alpha", false}, + {"Beta", false}, + {"Gamma", true}, + {"Delta", false}, + {"Epsilon", true}, + }, + {138, 496, 135, 41}, + 2097152}, + {"Listbox_MultiSelectMultipleMismatch", + { + {"Alligator", true}, + {"Bear", false}, + {"Cougar", true}, + {"Deer", false}, + {"Echidna", false}, + }, + {138, 563, 135, 41}, + 2097152}}; + + TestClient client; + std::unique_ptr engine = + InitializeEngine(&client, FILE_PATH_LITERAL("form_choice_fields.pdf")); + ASSERT_TRUE(engine); + ASSERT_EQ(1, engine->GetNumberOfPages()); + + PDFiumPage* page = GetPDFiumPageForTest(engine.get(), 0); + ASSERT_TRUE(page); + page->PopulateAnnotations(); + size_t choice_fields_count = page->choice_fields_.size(); + ASSERT_EQ(base::size(kExpectedChoiceFields), choice_fields_count); + + for (size_t i = 0; i < choice_fields_count; ++i) { + EXPECT_EQ(kExpectedChoiceFields[i].name, page->choice_fields_[i].name); + size_t choice_field_options_count = page->choice_fields_[i].options.size(); + ASSERT_EQ(base::size(kExpectedChoiceFields[i].options), + choice_field_options_count); + for (size_t j = 0; j < choice_field_options_count; ++j) { + EXPECT_EQ(kExpectedChoiceFields[i].options[j].name, + page->choice_fields_[i].options[j].name); + EXPECT_EQ(kExpectedChoiceFields[i].options[j].is_selected, + page->choice_fields_[i].options[j].is_selected); + } + CompareRect(kExpectedChoiceFields[i].bounding_rect, + page->choice_fields_[i].bounding_rect); + EXPECT_EQ(kExpectedChoiceFields[i].flags, page->choice_fields_[i].flags); + } +} + using PDFiumPageOverlappingTest = PDFiumTestBase; // The following scenarios are covered across both test cases: diff --git a/chromium/pdf/pdfium/pdfium_permissions.cc b/chromium/pdf/pdfium/pdfium_permissions.cc index db3c98032b7..b24c6f42a58 100644 --- a/chromium/pdf/pdfium/pdfium_permissions.cc +++ b/chromium/pdf/pdfium/pdfium_permissions.cc @@ -4,6 +4,8 @@ #include "pdf/pdfium/pdfium_permissions.h" +#include "base/notreached.h" + namespace chrome_pdf { // static diff --git a/chromium/pdf/pdfium/pdfium_print.cc b/chromium/pdf/pdfium/pdfium_print.cc index 5cb79dce728..be5dd656dda 100644 --- a/chromium/pdf/pdfium/pdfium_print.cc +++ b/chromium/pdf/pdfium/pdfium_print.cc @@ -9,11 +9,11 @@ #include #include "base/strings/string_number_conversions.h" -#include "pdf/geometry_conversions.h" #include "pdf/pdf_transform.h" #include "pdf/pdfium/pdfium_engine.h" #include "pdf/pdfium/pdfium_mem_buffer_file_read.h" #include "pdf/pdfium/pdfium_mem_buffer_file_write.h" +#include "pdf/ppapi_migration/geometry_conversions.h" #include "ppapi/c/dev/ppp_printing_dev.h" #include "ppapi/c/private/ppp_pdf.h" #include "printing/nup_parameters.h" diff --git a/chromium/pdf/pdfium/pdfium_test_base.cc b/chromium/pdf/pdfium/pdfium_test_base.cc index 95c48c11c9b..b220fb92535 100644 --- a/chromium/pdf/pdfium/pdfium_test_base.cc +++ b/chromium/pdf/pdfium/pdfium_test_base.cc @@ -103,10 +103,11 @@ PDFiumTestBase::InitializeEngineWithoutLoading( void PDFiumTestBase::InitializePDFium() { FPDF_LIBRARY_CONFIG config; - config.version = 2; + config.version = 3; config.m_pUserFontPaths = nullptr; config.m_pIsolate = nullptr; config.m_v8EmbedderSlot = 0; + config.m_pPlatform = nullptr; FPDF_InitLibraryWithConfig(&config); } diff --git a/chromium/pdf/ppapi_migration/README.md b/chromium/pdf/ppapi_migration/README.md new file mode 100644 index 00000000000..f6426f94eba --- /dev/null +++ b/chromium/pdf/ppapi_migration/README.md @@ -0,0 +1,5 @@ +# PPAPI migration utilities + +This directory contains utilities for bridging from legacy APIs during the PDF +viewer's [migration from Pepper](https://crbug.com/702993). The utilities should +be designed for easy removal once the migration is complete. diff --git a/chromium/pdf/ppapi_migration/geometry_conversions.cc b/chromium/pdf/ppapi_migration/geometry_conversions.cc new file mode 100644 index 00000000000..a6399e04938 --- /dev/null +++ b/chromium/pdf/ppapi_migration/geometry_conversions.cc @@ -0,0 +1,23 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "pdf/ppapi_migration/geometry_conversions.h" + +#include "ppapi/c/pp_rect.h" +#include "ppapi/c/pp_size.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace chrome_pdf { + +gfx::Rect RectFromPPRect(const PP_Rect& pp_rect) { + return gfx::Rect(pp_rect.point.x, pp_rect.point.y, pp_rect.size.width, + pp_rect.size.height); +} + +gfx::Size SizeFromPPSize(const PP_Size& pp_size) { + return gfx::Size(pp_size.width, pp_size.height); +} + +} // namespace chrome_pdf diff --git a/chromium/pdf/ppapi_migration/geometry_conversions.h b/chromium/pdf/ppapi_migration/geometry_conversions.h new file mode 100644 index 00000000000..8806207abc7 --- /dev/null +++ b/chromium/pdf/ppapi_migration/geometry_conversions.h @@ -0,0 +1,23 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PDF_PPAPI_MIGRATION_GEOMETRY_CONVERSIONS_H_ +#define PDF_PPAPI_MIGRATION_GEOMETRY_CONVERSIONS_H_ + +struct PP_Rect; +struct PP_Size; + +namespace gfx { +class Rect; +class Size; +} // namespace gfx + +namespace chrome_pdf { + +gfx::Rect RectFromPPRect(const PP_Rect& pp_rect); +gfx::Size SizeFromPPSize(const PP_Size& pp_size); + +} // namespace chrome_pdf + +#endif // PDF_PPAPI_MIGRATION_GEOMETRY_CONVERSIONS_H_ diff --git a/chromium/pdf/preview_mode_client.cc b/chromium/pdf/preview_mode_client.cc index cf355047c84..ea3cdac0669 100644 --- a/chromium/pdf/preview_mode_client.cc +++ b/chromium/pdf/preview_mode_client.cc @@ -146,8 +146,7 @@ void PreviewModeClient::FormTextFieldFocusChange(bool in_focus) { } bool PreviewModeClient::IsPrintPreview() { - NOTREACHED(); - return false; + return true; } float PreviewModeClient::GetToolbarHeightInScreenCoords() { -- cgit v1.2.1