0

[unseasoned-pdf] Implement Print Preview support.

Most of the CL is just moving lots of code out of OutOfProcessInstance
and into PdfViewPluginBase. Then make a few tweaks to PdfViewWebPlugin
to hook it up there. Along the way, address some pre-existing nits in
the code being moved. Also remove PdfViewPluginBase::paint_manager()
because it has no more external callers.

Bug: 1140629
Change-Id: I6297e87525ff58bf8cc72feee2420563bdf1f34e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2961851
Reviewed-by: Daniel Hosseinian <dhoss@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#894377}
This commit is contained in:
Lei Zhang
2021-06-21 20:28:44 +00:00
committed by Chromium LUCI CQ
parent fdc7b80a80
commit d7f8698cf2
7 changed files with 316 additions and 341 deletions

@ -21,7 +21,6 @@
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "base/values.h"
@ -76,10 +75,6 @@ namespace chrome_pdf {
namespace {
constexpr char kChromePrint[] = "chrome://print/";
constexpr char kChromeExtension[] =
"chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai";
// Constants used in handling postMessage() messages.
constexpr char kType[] = "type";
// Name of identifier field passed from JS to the plugin and back, to associate
@ -91,24 +86,10 @@ constexpr char kJSAttachmentIndex[] = "attachmentIndex";
// Save attachment data (Plugin -> Page)
constexpr char kJSSaveAttachmentDataType[] = "saveAttachmentData";
constexpr char kJSAttachmentDataToSave[] = "dataToSave";
// Reset print preview mode (Page -> Plugin)
constexpr char kJSResetPrintPreviewModeType[] = "resetPrintPreviewMode";
constexpr char kJSPrintPreviewUrl[] = "url";
constexpr char kJSPrintPreviewGrayscale[] = "grayscale";
constexpr char kJSPrintPreviewPageCount[] = "pageCount";
// Load preview page (Page -> Plugin)
constexpr char kJSLoadPreviewPageType[] = "loadPreviewPage";
constexpr char kJSPreviewPageUrl[] = "url";
constexpr char kJSPreviewPageIndex[] = "index";
constexpr base::TimeDelta kFindResultCooldown =
base::TimeDelta::FromMilliseconds(100);
// Same value as printing::COMPLETE_PREVIEW_DOCUMENT_INDEX.
constexpr int kCompletePDFIndex = -1;
// A different negative value to differentiate itself from `kCompletePDFIndex`.
constexpr int kInvalidPDFIndex = -2;
constexpr char kPPPPdfInterface[] = PPP_PDF_INTERFACE_1;
PP_Var GetLinkAtPosition(PP_Instance instance, PP_Point point) {
@ -314,32 +295,6 @@ const PPP_Pdf ppp_private = {
&PdfPrintBegin,
};
int ExtractPrintPreviewPageIndex(base::StringPiece src_url) {
// Sample `src_url` format: chrome://print/id/page_index/print.pdf
// The page_index is zero-based, but can be negative with special meanings.
std::vector<base::StringPiece> url_substr =
base::SplitStringPiece(src_url.substr(strlen(kChromePrint)), "/",
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (url_substr.size() != 3)
return kInvalidPDFIndex;
if (url_substr[2] != "print.pdf")
return kInvalidPDFIndex;
int page_index = 0;
if (!base::StringToInt(url_substr[1], &page_index))
return kInvalidPDFIndex;
return page_index;
}
bool IsPrintPreviewUrl(base::StringPiece url) {
return base::StartsWith(url, kChromePrint);
}
bool IsPreviewingPDF(int print_preview_page_count) {
return print_preview_page_count == 0;
}
void ScalePoint(float scale, pp::Point* point) {
point->set_x(static_cast<int>(point->x() * scale));
point->set_y(static_cast<int>(point->y() * scale));
@ -483,6 +438,7 @@ OutOfProcessInstance::~OutOfProcessInstance() {
RemovePerInstanceObject(kPPPPdfInterface, this);
// Explicitly destroy the PDFEngine during destruction as it may call back
// into this object.
DestroyPreviewEngine();
DestroyEngine();
}
@ -504,9 +460,8 @@ bool OutOfProcessInstance::Init(uint32_t argc,
// a CHECK as a defense-in-depth.
std::string document_url = document_url_var.AsString();
base::StringPiece document_url_piece(document_url);
is_print_preview_ = IsPrintPreviewUrl(document_url_piece);
CHECK(base::StartsWith(document_url_piece, kChromeExtension) ||
is_print_preview_);
set_is_print_preview(IsPrintPreviewUrl(document_url_piece));
ValidateDocumentUrl(document_url_piece);
// Allow the plugin to handle find requests.
SetPluginToHandleFindRequests();
@ -585,10 +540,6 @@ void OutOfProcessInstance::HandleMessage(const pp::Var& message) {
if (type == kJSSaveAttachmentType) {
HandleSaveAttachmentMessage(dict);
} else if (type == kJSResetPrintPreviewModeType) {
HandleResetPrintPreviewModeMessage(dict);
} else if (type == kJSLoadPreviewPageType) {
HandleLoadPreviewPageMessage(dict);
} else {
PdfViewPluginBase::HandleMessage(ValueFromVar(message));
}
@ -606,7 +557,7 @@ void OutOfProcessInstance::DidChangeView(const pp::View& view) {
UpdateGeometryOnViewChanged(RectFromPPRect(view.GetRect()),
view.GetDeviceScale());
if (is_print_preview_ && !stop_scrolling()) {
if (IsPrintPreview() && !stop_scrolling()) {
set_scroll_position(PointFromPPPoint(view.GetScrollOffset()));
UpdateScroll();
}
@ -767,18 +718,6 @@ void OutOfProcessInstance::DidOpen(std::unique_ptr<UrlLoader> loader,
}
}
void OutOfProcessInstance::DidOpenPreview(std::unique_ptr<UrlLoader> loader,
int32_t result) {
if (result == PP_OK) {
preview_client_ = std::make_unique<PreviewModeClient>(this);
preview_engine_ = std::make_unique<PDFiumEngine>(
preview_client_.get(), PDFiumFormFiller::ScriptOption::kNoJavaScript);
preview_engine_->HandleDocumentLoad(std::move(loader));
} else {
NOTREACHED();
}
}
void OutOfProcessInstance::SendMessage(base::Value message) {
PostMessage(VarFromValue(message));
}
@ -926,72 +865,6 @@ void OutOfProcessInstance::RotateCounterclockwise() {
engine()->RotateCounterclockwise();
}
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::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;
set_url(url);
preview_pages_info_ = base::queue<PreviewPageInfo>();
preview_document_load_state_ = DocumentLoadState::kComplete;
set_document_load_state(DocumentLoadState::kLoading);
LoadUrl(GetURL(), /*is_print_preview=*/false);
preview_engine_.reset();
InitializeEngine(std::make_unique<PDFiumEngine>(
this, PDFiumFormFiller::ScriptOption::kNoJavaScript));
engine()->SetGrayscale(dict.Get(pp::Var(kJSPrintPreviewGrayscale)).AsBool());
engine()->New(GetURL().c_str(), /*headers=*/nullptr);
paint_manager().InvalidateRect(gfx::Rect(plugin_rect().size()));
}
void OutOfProcessInstance::HandleSaveAttachmentMessage(
const pp::VarDictionary& dict) {
if (!dict.Get(pp::Var(kJSMessageId)).is_string() ||
@ -1030,38 +903,6 @@ void OutOfProcessInstance::HandleSaveAttachmentMessage(
PostMessage(message);
}
void OutOfProcessInstance::PreviewDocumentLoadComplete() {
if (preview_document_load_state_ != DocumentLoadState::kLoading ||
preview_pages_info_.empty()) {
return;
}
preview_document_load_state_ = DocumentLoadState::kComplete;
int dest_page_index = preview_pages_info_.front().second;
DCHECK_GT(dest_page_index, 0);
preview_pages_info_.pop();
DCHECK(preview_engine_);
engine()->AppendPage(preview_engine_.get(), dest_page_index);
++print_preview_loaded_page_count_;
LoadNextPreviewPage();
}
void OutOfProcessInstance::PreviewDocumentLoadFailed() {
UserMetricsRecordAction("PDF.PreviewDocumentLoadFailure");
if (preview_document_load_state_ != DocumentLoadState::kLoading ||
preview_pages_info_.empty()) {
return;
}
// Even if a print preview page failed to load, keep going.
preview_document_load_state_ = DocumentLoadState::kFailed;
preview_pages_info_.pop();
++print_preview_loaded_page_count_;
LoadNextPreviewPage();
}
pp::Instance* OutOfProcessInstance::GetPluginInstance() {
return this;
}
@ -1131,15 +972,6 @@ std::unique_ptr<UrlLoader> OutOfProcessInstance::CreateUrlLoaderInternal() {
return loader;
}
void OutOfProcessInstance::AppendBlankPrintPreviewPages() {
engine()->AppendBlankPages(print_preview_page_count_);
LoadNextPreviewPage();
}
bool OutOfProcessInstance::IsPrintPreview() {
return is_print_preview_;
}
void OutOfProcessInstance::SetSelectedText(const std::string& selected_text) {
pp::PDF::SetSelectedText(this, selected_text.c_str());
}
@ -1176,55 +1008,6 @@ void OutOfProcessInstance::ScheduleTaskOnMainThread(
PPCompletionCallbackFromResultCallback(std::move(callback)), result);
}
void OutOfProcessInstance::ProcessPreviewPageInfo(const std::string& url,
int dest_page_index) {
DCHECK(IsPrintPreview());
if (dest_page_index < 0 || dest_page_index >= print_preview_page_count_) {
NOTREACHED();
return;
}
// Print Preview JS will send the loadPreviewPage message for every page,
// including the first page in the print preview, which has already been
// loaded when handing the resetPrintPreviewMode message. Just ignore it.
if (dest_page_index == 0)
return;
int src_page_index = ExtractPrintPreviewPageIndex(url);
if (src_page_index < 0) {
NOTREACHED();
return;
}
preview_pages_info_.push(std::make_pair(url, dest_page_index));
LoadAvailablePreviewPage();
}
void OutOfProcessInstance::LoadAvailablePreviewPage() {
if (preview_pages_info_.empty() ||
document_load_state() != DocumentLoadState::kComplete ||
preview_document_load_state_ == DocumentLoadState::kLoading) {
return;
}
preview_document_load_state_ = DocumentLoadState::kLoading;
const std::string& url = preview_pages_info_.front().first;
LoadUrl(url, /*is_print_preview=*/true);
}
void OutOfProcessInstance::LoadNextPreviewPage() {
if (!preview_pages_info_.empty()) {
DCHECK_LT(print_preview_loaded_page_count_, print_preview_page_count_);
LoadAvailablePreviewPage();
return;
}
if (print_preview_loaded_page_count_ == print_preview_page_count_) {
SendPrintPreviewLoadedNotification();
}
}
void OutOfProcessInstance::DidStartLoading() {
if (did_call_start_loading_)
return;
@ -1241,19 +1024,6 @@ void OutOfProcessInstance::DidStopLoading() {
did_call_start_loading_ = false;
}
void OutOfProcessInstance::OnPrintPreviewLoaded() {
// Scroll location is retained across document loads in print preview mode, so
// there's no need to override the scroll position by scrolling again.
if (IsPreviewingPDF(print_preview_page_count_)) {
SendPrintPreviewLoadedNotification();
} else {
DCHECK_EQ(0, print_preview_loaded_page_count_);
print_preview_loaded_page_count_ = 1;
AppendBlankPrintPreviewPages();
}
OnGeometryChanged(0, 0);
}
void OutOfProcessInstance::InvokePrintDialog() {
pp::PDF::Print(this);
}

@ -10,14 +10,11 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/containers/queue.h"
#include "base/memory/weak_ptr.h"
#include "pdf/pdf_view_plugin_base.h"
#include "pdf/preview_mode_client.h"
#include "ppapi/c/private/ppp_pdf.h"
#include "ppapi/cpp/dev/printing_dev.h"
#include "ppapi/cpp/image_data.h"
@ -39,14 +36,12 @@ class VarDictionary;
namespace chrome_pdf {
class Graphics;
class PDFiumEngine;
class UrlLoader;
class OutOfProcessInstance : public PdfViewPluginBase,
public pp::Instance,
public pp::Find_Private,
public pp::Printing_Dev,
public PreviewModeClient::Client {
public pp::Printing_Dev {
public:
explicit OutOfProcessInstance(PP_Instance instance);
OutOfProcessInstance(const OutOfProcessInstance&) = delete;
@ -109,7 +104,6 @@ class OutOfProcessInstance : public PdfViewPluginBase,
const char16_t* term,
bool case_sensitive) override;
pp::Instance* GetPluginInstance() override;
bool IsPrintPreview() override;
void SetSelectedText(const std::string& selected_text) override;
void SetLinkUnderCursor(const std::string& link_under_cursor) override;
bool IsValidLink(const std::string& url) override;
@ -120,10 +114,6 @@ class OutOfProcessInstance : public PdfViewPluginBase,
int32_t result,
base::TimeDelta delay) override;
// PreviewModeClient::Client:
void PreviewDocumentLoadComplete() override;
void PreviewDocumentLoadFailed() override;
// Helper functions for implementing PPP_PDF.
void RotateClockwise();
void RotateCounterclockwise();
@ -133,8 +123,6 @@ class OutOfProcessInstance : public PdfViewPluginBase,
base::WeakPtr<PdfViewPluginBase> GetWeakPtr() override;
std::unique_ptr<UrlLoader> CreateUrlLoaderInternal() override;
void DidOpen(std::unique_ptr<UrlLoader> loader, int32_t result) override;
void DidOpenPreview(std::unique_ptr<UrlLoader> loader,
int32_t result) override;
void SendMessage(base::Value message) override;
void SaveAs() override;
void InitImageData(const gfx::Size& size) override;
@ -152,7 +140,6 @@ class OutOfProcessInstance : public PdfViewPluginBase,
void DidStartLoading() override;
void DidStopLoading() override;
void InvokePrintDialog() override;
void OnPrintPreviewLoaded() override;
void NotifySelectionChanged(const gfx::PointF& left,
int left_height,
const gfx::PointF& right,
@ -162,8 +149,6 @@ class OutOfProcessInstance : public PdfViewPluginBase,
private:
// Message handlers.
void HandleLoadPreviewPageMessage(const pp::VarDictionary& dict);
void HandleResetPrintPreviewModeMessage(const pp::VarDictionary& dict);
void HandleSaveAttachmentMessage(const pp::VarDictionary& dict);
void ResetRecentlySentFindUpdate(int32_t);
@ -172,59 +157,12 @@ class OutOfProcessInstance : public PdfViewPluginBase,
void FormDidOpen(int32_t result);
// Reduces the document to 1 page and appends `print_preview_page_count_` - 1
// blank pages to the document for print preview.
void AppendBlankPrintPreviewPages();
// Process the preview page data information. `src_url` specifies the preview
// page data location. The `src_url` is in the format:
// chrome://print/id/page_number/print.pdf
// `dest_page_index` specifies the blank page index that needs to be replaced
// with the new page data.
void ProcessPreviewPageInfo(const std::string& src_url, int dest_page_index);
// Load the next available preview page into the blank page.
void LoadAvailablePreviewPage();
// Called after a preview page has loaded or failed to load.
void LoadNextPreviewPage();
// The Pepper image data that is in sync with mutable_image_data().
pp::ImageData pepper_image_data_;
// The PreviewModeClient used for print preview. Will be passed to
// `preview_engine_`.
std::unique_ptr<PreviewModeClient> preview_client_;
// This engine is used to render the individual preview page data. This is
// used only in print preview mode. This will use `PreviewModeClient`
// interface which has very limited access to the pp::Instance.
std::unique_ptr<PDFiumEngine> preview_engine_;
// Used for submitting forms.
std::unique_ptr<UrlLoader> form_loader_;
DocumentLoadState preview_document_load_state_ = DocumentLoadState::kComplete;
// True if the plugin is loaded in print preview, otherwise false.
bool is_print_preview_ = false;
// Number of pages in print preview mode for non-PDF source, 0 if print
// previewing a PDF, and -1 if not in print preview mode.
int print_preview_page_count_ = -1;
// Number of pages loaded in print preview mode for non-PDF source. Always
// less than or equal to `print_preview_page_count_`.
int print_preview_loaded_page_count_ = -1;
// Used to manage loaded print preview page information. A `PreviewPageInfo`
// consists of data source URL string and the page index in the destination
// document.
// The URL string embeds a page number that can be found with
// ExtractPrintPreviewPageIndex(). This page number is always greater than 0.
// The page index is always in the range of [0, print_preview_page_count_).
using PreviewPageInfo = std::pair<std::string, int>;
base::queue<PreviewPageInfo> preview_pages_info_;
// Used to signal the browser about focus changes to trigger the OSK.
// TODO(abodenha@chromium.org) Implement full IME support in the plugin.
// http://crbug.com/132565

@ -28,7 +28,9 @@
#include "base/notreached.h"
#include "base/numerics/ranges.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "base/values.h"
@ -76,6 +78,15 @@ constexpr double kMinZoom = 0.01;
constexpr base::TimeDelta kAccessibilityPageDelay =
base::TimeDelta::FromMilliseconds(100);
constexpr char kChromePrintHost[] = "chrome://print/";
constexpr char kChromeExtensionHost[] =
"chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai";
// Same value as printing::COMPLETE_PREVIEW_DOCUMENT_INDEX.
constexpr int kCompletePDFIndex = -1;
// A different negative value to differentiate itself from `kCompletePDFIndex`.
constexpr int kInvalidPDFIndex = -2;
// Enumeration of pinch states.
// This should match PinchPhase enum in chrome/browser/resources/pdf/viewport.js
enum class PinchPhase {
@ -100,6 +111,28 @@ base::Value PrepareReplyMessage(base::StringPiece reply_type,
return reply;
}
int ExtractPrintPreviewPageIndex(base::StringPiece src_url) {
// Sample `src_url` format: chrome://print/id/page_index/print.pdf
// The page_index is zero-based, but can be negative with special meanings.
std::vector<base::StringPiece> url_substr =
base::SplitStringPiece(src_url.substr(strlen(kChromePrintHost)), "/",
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (url_substr.size() != 3)
return kInvalidPDFIndex;
if (url_substr[2] != "print.pdf")
return kInvalidPDFIndex;
int page_index = 0;
if (!base::StringToInt(url_substr[1], &page_index))
return kInvalidPDFIndex;
return page_index;
}
bool IsPreviewingPDF(int print_preview_page_count) {
return print_preview_page_count == 0;
}
} // namespace
PdfViewPluginBase::PdfViewPluginBase() = default;
@ -379,6 +412,10 @@ void PdfViewPluginBase::FormTextFieldFocusChange(bool in_focus) {
SetFormFieldInFocus(in_focus);
}
bool PdfViewPluginBase::IsPrintPreview() {
return is_print_preview_;
}
SkColor PdfViewPluginBase::GetBackgroundColor() {
return background_color_;
}
@ -470,6 +507,9 @@ void PdfViewPluginBase::HandleMessage(const base::Value& message) {
{"getSelectedText", &PdfViewPluginBase::HandleGetSelectedTextMessage},
{"getThumbnail", &PdfViewPluginBase::HandleGetThumbnailMessage},
{"print", &PdfViewPluginBase::HandlePrintMessage},
{"loadPreviewPage", &PdfViewPluginBase::HandleLoadPreviewPageMessage},
{"resetPrintPreviewMode",
&PdfViewPluginBase::HandleResetPrintPreviewModeMessage},
{"rotateClockwise", &PdfViewPluginBase::HandleRotateClockwiseMessage},
{"rotateCounterclockwise",
&PdfViewPluginBase::HandleRotateCounterclockwiseMessage},
@ -555,6 +595,38 @@ void PdfViewPluginBase::OnPaint(const std::vector<gfx::Rect>& paint_rects,
DoPaint(paint_rects, ready, pending);
}
void PdfViewPluginBase::PreviewDocumentLoadComplete() {
if (preview_document_load_state_ != DocumentLoadState::kLoading ||
preview_pages_info_.empty()) {
return;
}
preview_document_load_state_ = DocumentLoadState::kComplete;
int dest_page_index = preview_pages_info_.front().second;
DCHECK_GT(dest_page_index, 0);
preview_pages_info_.pop();
DCHECK(preview_engine_);
engine()->AppendPage(preview_engine_.get(), dest_page_index);
++print_preview_loaded_page_count_;
LoadNextPreviewPage();
}
void PdfViewPluginBase::PreviewDocumentLoadFailed() {
UserMetricsRecordAction("PDF.PreviewDocumentLoadFailure");
if (preview_document_load_state_ != DocumentLoadState::kLoading ||
preview_pages_info_.empty()) {
return;
}
// Even if a print preview page failed to load, keep going.
preview_document_load_state_ = DocumentLoadState::kFailed;
preview_pages_info_.pop();
++print_preview_loaded_page_count_;
LoadNextPreviewPage();
}
void PdfViewPluginBase::EnableAccessibility() {
if (accessibility_state_ == AccessibilityState::kLoaded)
return;
@ -585,6 +657,15 @@ void PdfViewPluginBase::DestroyEngine() {
engine_.reset();
}
void PdfViewPluginBase::DestroyPreviewEngine() {
preview_engine_.reset();
}
void PdfViewPluginBase::ValidateDocumentUrl(base::StringPiece document_url) {
CHECK(base::StartsWith(document_url, kChromeExtensionHost) ||
IsPrintPreview());
}
void PdfViewPluginBase::LoadUrl(const std::string& url, bool is_print_preview) {
UrlRequest request;
request.url = url;
@ -678,7 +759,7 @@ void PdfViewPluginBase::UpdateGeometryOnViewChanged(
plugin_rect_ = new_plugin_rect;
plugin_dip_size_ = new_view_rect.size();
paint_manager().SetSize(plugin_rect_.size(), device_scale_);
paint_manager_.SetSize(plugin_rect_.size(), device_scale_);
// Initialize the image data buffer if the context size changes.
const gfx::Size old_image_size = gfx::SkISizeToSize(image_data_.dimensions());
@ -839,6 +920,11 @@ void PdfViewPluginBase::SetZoom(double scale) {
OnGeometryChanged(old_zoom, device_scale_);
}
// static
bool PdfViewPluginBase::IsPrintPreviewUrl(base::StringPiece url) {
return base::StartsWith(url, kChromePrintHost);
}
void PdfViewPluginBase::HandleDisplayAnnotationsMessage(
const base::Value& message) {
engine()->DisplayAnnotations(message.FindBoolKey("display").value());
@ -898,10 +984,59 @@ void PdfViewPluginBase::HandleGetThumbnailMessage(const base::Value& message) {
GetWeakPtr(), std::move(reply)));
}
void PdfViewPluginBase::HandleLoadPreviewPageMessage(
const base::Value& message) {
const std::string& url = *message.FindStringKey("url");
int index = message.FindIntKey("index").value();
// For security reasons, crash if `url` is not for Print Preview.
CHECK(IsPrintPreview());
CHECK(IsPrintPreviewUrl(url));
ProcessPreviewPageInfo(url, index);
}
void PdfViewPluginBase::HandlePrintMessage(const base::Value& /*message*/) {
Print();
}
void PdfViewPluginBase::HandleResetPrintPreviewModeMessage(
const base::Value& message) {
const std::string& url = *message.FindStringKey("url");
bool is_grayscale = message.FindBoolKey("grayscale").value();
int print_preview_page_count = message.FindIntKey("pageCount").value();
// For security reasons, crash if `url` is not for Print Preview.
CHECK(IsPrintPreview());
CHECK(IsPrintPreviewUrl(url));
DCHECK_GE(print_preview_page_count, 0);
// 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)
DCHECK_EQ(page_index, kCompletePDFIndex);
else
DCHECK_GE(page_index, 0);
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_ = DocumentLoadState::kComplete;
document_load_state_ = DocumentLoadState::kLoading;
LoadUrl(GetURL(), /*is_print_preview=*/false);
preview_engine_.reset();
InitializeEngine(std::make_unique<PDFiumEngine>(
this, PDFiumFormFiller::ScriptOption::kNoJavaScript));
engine()->SetGrayscale(is_grayscale);
engine()->New(GetURL().c_str(), /*headers=*/nullptr);
paint_manager_.InvalidateRect(gfx::Rect(plugin_rect().size()));
}
void PdfViewPluginBase::HandleRotateClockwiseMessage(
const base::Value& /*message*/) {
engine()->RotateClockwise();
@ -1056,9 +1191,9 @@ void PdfViewPluginBase::HandleViewportMessage(const base::Value& message) {
scroll_position_at_last_raster_.y() * zoom_ratio));
}
paint_manager().SetTransform(zoom_ratio, pinch_center,
pinch_vector + paint_offset + scroll_delta,
true);
paint_manager_.SetTransform(zoom_ratio, pinch_center,
pinch_vector + paint_offset + scroll_delta,
true);
needs_reraster_ = false;
return;
}
@ -1069,7 +1204,7 @@ void PdfViewPluginBase::HandleViewportMessage(const base::Value& message) {
// 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();
paint_manager_.ClearTransform();
last_bitmap_smaller_ = false;
needs_reraster_ = true;
@ -1370,4 +1505,73 @@ void PdfViewPluginBase::HistogramCustomCounts(const char* name,
base::UmaHistogramCustomCounts(name, sample, min, max, bucket_count);
}
void PdfViewPluginBase::DidOpenPreview(std::unique_ptr<UrlLoader> loader,
int32_t result) {
DCHECK_EQ(result, PP_OK);
preview_client_ = std::make_unique<PreviewModeClient>(this);
preview_engine_ = std::make_unique<PDFiumEngine>(
preview_client_.get(), PDFiumFormFiller::ScriptOption::kNoJavaScript);
preview_engine_->HandleDocumentLoad(std::move(loader));
}
void PdfViewPluginBase::OnPrintPreviewLoaded() {
// Scroll location is retained across document loads in print preview mode, so
// there's no need to override the scroll position by scrolling again.
if (IsPreviewingPDF(print_preview_page_count_)) {
SendPrintPreviewLoadedNotification();
} else {
DCHECK_EQ(0, print_preview_loaded_page_count_);
print_preview_loaded_page_count_ = 1;
AppendBlankPrintPreviewPages();
}
OnGeometryChanged(0, 0);
}
void PdfViewPluginBase::AppendBlankPrintPreviewPages() {
engine()->AppendBlankPages(print_preview_page_count_);
LoadNextPreviewPage();
}
void PdfViewPluginBase::ProcessPreviewPageInfo(const std::string& url,
int dest_page_index) {
DCHECK(IsPrintPreview());
DCHECK_GE(dest_page_index, 0);
DCHECK_LT(dest_page_index, print_preview_page_count_);
// Print Preview JS will send the loadPreviewPage message for every page,
// including the first page in the print preview, which has already been
// loaded when handing the resetPrintPreviewMode message. Just ignore it.
if (dest_page_index == 0)
return;
int src_page_index = ExtractPrintPreviewPageIndex(url);
DCHECK_GE(src_page_index, 0);
preview_pages_info_.push(std::make_pair(url, dest_page_index));
LoadAvailablePreviewPage();
}
void PdfViewPluginBase::LoadAvailablePreviewPage() {
if (preview_pages_info_.empty() ||
document_load_state() != DocumentLoadState::kComplete ||
preview_document_load_state_ == DocumentLoadState::kLoading) {
return;
}
preview_document_load_state_ = DocumentLoadState::kLoading;
const std::string& url = preview_pages_info_.front().first;
LoadUrl(url, /*is_print_preview=*/true);
}
void PdfViewPluginBase::LoadNextPreviewPage() {
if (!preview_pages_info_.empty()) {
DCHECK_LT(print_preview_loaded_page_count_, print_preview_page_count_);
LoadAvailablePreviewPage();
return;
}
if (print_preview_loaded_page_count_ == print_preview_page_count_)
SendPrintPreviewLoadedNotification();
}
} // namespace chrome_pdf

@ -9,14 +9,17 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/containers/flat_set.h"
#include "base/containers/queue.h"
#include "base/memory/weak_ptr.h"
#include "pdf/paint_manager.h"
#include "pdf/pdf_engine.h"
#include "pdf/pdfium/pdfium_form_filler.h"
#include "pdf/preview_mode_client.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/web/web_print_params.h"
#include "third_party/skia/include/core/SkBitmap.h"
@ -52,7 +55,8 @@ struct AccessibilityViewportInfo;
// Common base to share code between the two plugin implementations,
// `OutOfProcessInstance` (Pepper) and `PdfViewWebPlugin` (Blink).
class PdfViewPluginBase : public PDFEngine::Client,
public PaintManager::Client {
public PaintManager::Client,
public PreviewModeClient::Client {
public:
using PDFEngine::Client::ScheduleTaskOnMainThread;
@ -97,17 +101,22 @@ class PdfViewPluginBase : public PDFEngine::Client,
void DocumentHasUnsupportedFeature(const std::string& feature) override;
void DocumentLoadProgress(uint32_t available, uint32_t doc_size) override;
void FormTextFieldFocusChange(bool in_focus) override;
bool IsPrintPreview() override;
SkColor GetBackgroundColor() override;
void SetIsSelecting(bool is_selecting) override;
void SelectionChanged(const gfx::Rect& left, const gfx::Rect& right) override;
void EnteredEditMode() override;
void DocumentFocusChanged(bool document_has_focus) override;
// PaintManager::Client
// PaintManager::Client:
void OnPaint(const std::vector<gfx::Rect>& paint_rects,
std::vector<PaintReadyRect>& ready,
std::vector<gfx::Rect>& pending) override;
// PreviewModeClient::Client:
void PreviewDocumentLoadComplete() override;
void PreviewDocumentLoadFailed() override;
// Enable accessibility for PDF plugin.
void EnableAccessibility();
@ -153,10 +162,14 @@ class PdfViewPluginBase : public PDFEngine::Client,
// their destructor to ensure the engine is destroyed first.
void DestroyEngine();
// Destroys the `PDFiumEngine` used for Print Preview. Subclasses should call
// this method in their destructor to ensure the engine is destroyed first.
void DestroyPreviewEngine();
const PDFiumEngine* engine() const { return engine_.get(); }
PDFiumEngine* engine() { return engine_.get(); }
PaintManager& paint_manager() { return paint_manager_; }
void ValidateDocumentUrl(base::StringPiece document_url);
// Starts loading `url`. If `is_print_preview` is `true`, load for print
// preview instead of normal PDF viewing.
@ -172,10 +185,6 @@ class PdfViewPluginBase : public PDFEngine::Client,
// Handles `LoadUrl()` result.
virtual void DidOpen(std::unique_ptr<UrlLoader> loader, int32_t result) = 0;
// Handles `LoadUrl()` result for print preview.
virtual void DidOpenPreview(std::unique_ptr<UrlLoader> loader,
int32_t result) = 0;
bool HandleInputEvent(const blink::WebInputEvent& event);
// Handles `postMessage()` calls from the embedder.
@ -284,10 +293,6 @@ class PdfViewPluginBase : public PDFEngine::Client,
// Ends the print session. Further calls to `PrintPages()` will fail.
void PrintEnd();
static constexpr bool IsSaveDataSizeValid(size_t size) {
return size > 0 && size <= kMaximumSavedFileSize;
}
// Disables browser commands because of restrictions on how the data is to be
// used (i.e. can't copy/print). `content_restrictions` should have its bits
// set by `chrome_pdf::ContentRestriction` enum values.
@ -307,9 +312,6 @@ class PdfViewPluginBase : public PDFEngine::Client,
// document.
virtual void InvokePrintDialog() = 0;
// Performs tasks necessary when the document is loaded in print preview mode.
virtual void OnPrintPreviewLoaded() = 0;
// Notifies the embedder of the top-left and bottom-right coordinates of the
// current selection.
virtual void NotifySelectionChanged(const gfx::PointF& left,
@ -371,6 +373,16 @@ class PdfViewPluginBase : public PDFEngine::Client,
bool edit_mode() const { return edit_mode_; }
void set_edit_mode(bool edit_mode) { edit_mode_ = edit_mode; }
void set_is_print_preview(bool is_print_preview) {
is_print_preview_ = is_print_preview;
}
static bool IsPrintPreviewUrl(base::StringPiece url);
static constexpr bool IsSaveDataSizeValid(size_t size) {
return size > 0 && size <= kMaximumSavedFileSize;
}
private:
// Message handlers.
void HandleDisplayAnnotationsMessage(const base::Value& message);
@ -378,7 +390,9 @@ class PdfViewPluginBase : public PDFEngine::Client,
void HandleGetPasswordCompleteMessage(const base::Value& message);
void HandleGetSelectedTextMessage(const base::Value& message);
void HandleGetThumbnailMessage(const base::Value& message);
void HandleLoadPreviewPageMessage(const base::Value& message);
void HandlePrintMessage(const base::Value& /*message*/);
void HandleResetPrintPreviewModeMessage(const base::Value& message);
void HandleRotateClockwiseMessage(const base::Value& /*message*/);
void HandleRotateCounterclockwiseMessage(const base::Value& /*message*/);
void HandleSaveMessage(const base::Value& message);
@ -437,6 +451,28 @@ class PdfViewPluginBase : public PDFEngine::Client,
int32_t max,
uint32_t bucket_count);
// Handles `LoadUrl()` result for print preview.
void DidOpenPreview(std::unique_ptr<UrlLoader> loader, int32_t result);
// Performs tasks necessary when the document is loaded in print preview mode.
void OnPrintPreviewLoaded();
// Reduces the document to 1 page and appends `print_preview_page_count_` - 1
// blank pages to the document for print preview.
void AppendBlankPrintPreviewPages();
// Process the preview page data information. `src_url` specifies the preview
// page data location. The `src_url` is in the format:
// chrome://print/id/page_number/print.pdf
// `dest_page_index` specifies the blank page index that needs to be replaced
// with the new page data.
void ProcessPreviewPageInfo(const std::string& src_url, int dest_page_index);
// Load the next available preview page into the blank page.
void LoadAvailablePreviewPage();
// Called after a preview page has loaded or failed to load.
void LoadNextPreviewPage();
std::unique_ptr<PDFiumEngine> engine_;
PaintManager paint_manager_{this};
@ -543,6 +579,37 @@ class PdfViewPluginBase : public PDFEngine::Client,
// For identifying actual print operations to avoid double logging of UMA.
bool print_pages_called_;
// The PreviewModeClient used for print preview. Will be passed to
// `preview_engine_`.
std::unique_ptr<PreviewModeClient> preview_client_;
// This engine is used to render the individual preview page data. This is
// used only in print preview mode. This will use `PreviewModeClient`
// interface which has very limited access to the pp::Instance.
std::unique_ptr<PDFiumEngine> preview_engine_;
DocumentLoadState preview_document_load_state_ = DocumentLoadState::kComplete;
// True if the plugin is loaded in print preview, otherwise false.
bool is_print_preview_ = false;
// Number of pages in print preview mode for non-PDF source, 0 if print
// previewing a PDF, and -1 if not in print preview mode.
int print_preview_page_count_ = -1;
// Number of pages loaded in print preview mode for non-PDF source. Always
// less than or equal to `print_preview_page_count_`.
int print_preview_loaded_page_count_ = -1;
// Used to manage loaded print preview page information. A `PreviewPageInfo`
// consists of data source URL string and the page index in the destination
// document.
// The URL string embeds a page number that can be found with
// ExtractPrintPreviewPageIndex(). This page number is always greater than 0.
// The page index is always in the range of [0, print_preview_page_count_).
using PreviewPageInfo = std::pair<std::string, int>;
base::queue<PreviewPageInfo> preview_pages_info_;
};
} // namespace chrome_pdf

@ -116,11 +116,6 @@ class FakePdfViewPluginBase : public PdfViewPluginBase {
MOCK_METHOD(void, DidOpen, (std::unique_ptr<UrlLoader>, int32_t), (override));
MOCK_METHOD(void,
DidOpenPreview,
(std::unique_ptr<UrlLoader>, int32_t),
(override));
void SendMessage(base::Value message) override {
sent_message_ = std::move(message);
}
@ -157,8 +152,6 @@ class FakePdfViewPluginBase : public PdfViewPluginBase {
MOCK_METHOD(void, DidStopLoading, (), (override));
MOCK_METHOD(void, OnPrintPreviewLoaded, (), (override));
MOCK_METHOD(void, InvokePrintDialog, (), (override));
MOCK_METHOD(void,

@ -167,7 +167,10 @@ class BlinkContainerWrapper final : public PdfViewWebPlugin::ContainerWrapper {
}
void UpdateTextInputState() override {
return GetFrame()->FrameWidget()->UpdateTextInputState();
// `widget` is null in Print Preview.
auto* widget = GetFrame()->FrameWidget();
if (widget)
widget->UpdateTextInputState();
}
blink::WebLocalFrame* GetFrame() override {
@ -207,6 +210,22 @@ bool PdfViewWebPlugin::InitializeCommon(
std::unique_ptr<ContainerWrapper> container_wrapper) {
container_wrapper_ = std::move(container_wrapper);
// Check if the PDF is being loaded in the PDF chrome extension. We only allow
// the plugin to be loaded in the extension and print preview to avoid
// exposing sensitive APIs directly to external websites.
std::string document_url;
auto* container = Container();
if (container) {
GURL maybe_url(container->GetDocument().Url());
if (maybe_url.is_valid())
document_url = maybe_url.possibly_invalid_spec();
}
base::StringPiece document_url_piece(document_url);
set_is_print_preview(IsPrintPreviewUrl(document_url_piece));
// TODO(crbug.com/1123621): Consider calling ValidateDocumentUrl() or
// something like it once the process model has been finalized.
absl::optional<ParsedParams> params = ParseWebPluginParams(initial_params_);
// The contents of `initial_params_` are no longer needed.
@ -232,6 +251,7 @@ void PdfViewWebPlugin::Destroy() {
if (container_wrapper_) {
// Explicitly destroy the PDFEngine during destruction as it may call back
// into this object.
DestroyPreviewEngine();
DestroyEngine();
PerProcessInitializer::GetInstance().Release();
container_wrapper_.reset();
@ -501,10 +521,6 @@ pp::Instance* PdfViewWebPlugin::GetPluginInstance() {
return nullptr;
}
bool PdfViewWebPlugin::IsPrintPreview() {
return false;
}
void PdfViewWebPlugin::SetSelectedText(const std::string& selected_text) {
selected_text_ = blink::WebString::FromUTF8(selected_text);
container_wrapper_->TextSelectionChanged(
@ -605,11 +621,6 @@ void PdfViewWebPlugin::DidOpen(std::unique_ptr<UrlLoader> loader,
}
}
void PdfViewWebPlugin::DidOpenPreview(std::unique_ptr<UrlLoader> loader,
int32_t result) {
NOTIMPLEMENTED();
}
void PdfViewWebPlugin::SendMessage(base::Value message) {
post_message_sender_.Post(std::move(message));
}
@ -681,10 +692,6 @@ void PdfViewWebPlugin::DidStopLoading() {
NOTIMPLEMENTED();
}
void PdfViewWebPlugin::OnPrintPreviewLoaded() {
NOTIMPLEMENTED();
}
void PdfViewWebPlugin::InvokePrintDialog() {
ScheduleTaskOnMainThread(
FROM_HERE,

@ -170,7 +170,6 @@ class PdfViewWebPlugin final : public PdfViewPluginBase,
const char16_t* term,
bool case_sensitive) override;
pp::Instance* GetPluginInstance() override;
bool IsPrintPreview() override;
void SetSelectedText(const std::string& selected_text) override;
void SetLinkUnderCursor(const std::string& link_under_cursor) override;
bool IsValidLink(const std::string& url) override;
@ -207,8 +206,6 @@ class PdfViewWebPlugin final : public PdfViewPluginBase,
base::WeakPtr<PdfViewPluginBase> GetWeakPtr() override;
std::unique_ptr<UrlLoader> CreateUrlLoaderInternal() override;
void DidOpen(std::unique_ptr<UrlLoader> loader, int32_t result) override;
void DidOpenPreview(std::unique_ptr<UrlLoader> loader,
int32_t result) override;
void SendMessage(base::Value message) override;
void SaveAs() override;
void InitImageData(const gfx::Size& size) override;
@ -224,7 +221,6 @@ class PdfViewWebPlugin final : public PdfViewPluginBase,
void SetPluginCanSave(bool can_save) override;
void DidStartLoading() override;
void DidStopLoading() override;
void OnPrintPreviewLoaded() override;
void InvokePrintDialog() override;
void NotifySelectionChanged(const gfx::PointF& left,
int left_height,