diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/pdf/out_of_process_instance.cc | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/pdf/out_of_process_instance.cc')
-rw-r--r-- | chromium/pdf/out_of_process_instance.cc | 614 |
1 files changed, 350 insertions, 264 deletions
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<pp::TextInput_Dev>(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<PinchPhase>(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<pp::Var> 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<PreviewPageInfo>(); - 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<PDFEngine::NamedDestination> 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<int>(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<pp::Var> 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<uint8_t> 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<PDFEngine::NamedDestination> 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<int>(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<pp::Var> 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<SaveRequestType>( + 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<PreviewPageInfo>(); + 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<PinchPhase>(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()); |