Skip to content

Commit

Permalink
PDF compositor generate full document from individual pages
Browse files Browse the repository at this point in the history
During print preview when the PDF compositor is used, individual PDFs
for each page are made for the preview.  Prior art has the full
document PDF used for printing being generated by sending a separate
metafile blob for all the pages, which is overly large for long
documents.  This alters that to have the full document PDF get
composited using the same individual page metafile objects as they are
sent to the utility process for the composition.

This effectively halves the amount of IPC traffic for Skia metafile
data and eliminates potential huge metafile messages that can
overwhelm IPC limits.

This is only applicable for print preview scenarios that pass Skia
metafile into PDF compositor.  Non-modifiable content and basic
printing are unaffected in behavior.

Bug: 872935
Change-Id: I1101846a00498956b18c208ada656e65e4f175a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1616461
Commit-Queue: Alan Screen <awscreen@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#715165}
  • Loading branch information
Alan Screen authored and Commit Bot committed Nov 14, 2019
1 parent aece674 commit b1ea8aa
Show file tree
Hide file tree
Showing 16 changed files with 535 additions and 41 deletions.
101 changes: 83 additions & 18 deletions chrome/browser/printing/print_preview_message_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,36 @@ void PrintPreviewMessageHandler::OnDidStartPreview(
print_preview_ui->OnDidStartPreview(params, ids.request_id);
}

void PrintPreviewMessageHandler::OnDidPrepareForDocumentToPdf(
int document_cookie,
const PrintHostMsg_PreviewIds& ids) {
PrintPreviewUI* print_preview_ui = GetPrintPreviewUI(ids.ui_id);
if (!print_preview_ui)
return;

// Determine if document composition from individual pages is desired
// configuration. Issue a preparation call to client if that hasn't
// been done yet.
if (!print_preview_ui->ShouldCompositeDocumentUsingIndividualPages())
return;

// For case of print preview, page metafile is used to composite into
// the document PDF at same time. Need to indicate that this scenario
// is at play for the compositor.
auto* client = PrintCompositeClient::FromWebContents(web_contents());
DCHECK(client);
if (client->GetIsDocumentConcurrentlyComposited(document_cookie))
return;

client->DoPrepareForDocumentToPdf(
document_cookie,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(
&PrintPreviewMessageHandler::OnPrepareForDocumentToPdfDone,
weak_ptr_factory_.GetWeakPtr(), ids),
mojom::PdfCompositor::Status::kCompositingFailure));
}

void PrintPreviewMessageHandler::OnDidPreviewPage(
content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPreviewPage_Params& params,
Expand Down Expand Up @@ -188,36 +218,55 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
// Always try to stop the worker.
StopWorker(params.document_cookie);

PrintPreviewUI* print_preview_ui = GetPrintPreviewUI(ids.ui_id);
if (!print_preview_ui)
return;

const PrintHostMsg_DidPrintContent_Params& content = params.content;
if (!content.metafile_data_region.IsValid())
const bool composite_document_using_individual_pages =
print_preview_ui->ShouldCompositeDocumentUsingIndividualPages();
// Concern about valid |metafile_data_region| is only relevant if full
// document is provided on this call. When document is compiled together
// from prior individual pages then there is no content required here.
if (!composite_document_using_individual_pages &&
!content.metafile_data_region.IsValid())
return;

if (params.expected_pages_count <= 0) {
NOTREACHED();
return;
}

PrintPreviewUI* print_preview_ui = GetPrintPreviewUI(ids.ui_id);
if (!print_preview_ui)
return;

if (ShouldUseCompositor(print_preview_ui)) {
// Don't bother compositing if this request has been cancelled already.
if (PrintPreviewUI::ShouldCancelRequest(ids))
return;

auto* client = PrintCompositeClient::FromWebContents(web_contents());
DCHECK(client);

client->DoCompositeDocumentToPdf(
params.document_cookie, render_frame_host, content,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(
&PrintPreviewMessageHandler::OnCompositePdfDocumentDone,
weak_ptr_factory_.GetWeakPtr(), params.expected_pages_count,
params.document_cookie, ids),
mojom::PdfCompositor::Status::kCompositingFailure,
base::ReadOnlySharedMemoryRegion()));
auto callback = base::BindOnce(
&PrintPreviewMessageHandler::OnCompositeOrCompleteDocumentToPdfDone,
weak_ptr_factory_.GetWeakPtr(),
composite_document_using_individual_pages, params.expected_pages_count,
params.document_cookie, ids);
if (composite_document_using_individual_pages) {
// Page metafile is used to composite into the document at same time.
// Need to provide particulars of how many pages are required before
// document will be completed.
client->DoCompleteDocumentToPdf(
params.document_cookie, params.expected_pages_count,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
std::move(callback),
mojom::PdfCompositor::Status::kCompositingFailure,
base::ReadOnlySharedMemoryRegion()));
} else {
client->DoCompositeDocumentToPdf(
params.document_cookie, render_frame_host, content,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
std::move(callback),
mojom::PdfCompositor::Status::kCompositingFailure,
base::ReadOnlySharedMemoryRegion()));
}
} else {
NotifyUIPreviewDocumentReady(
print_preview_ui, params.expected_pages_count, ids,
Expand Down Expand Up @@ -394,7 +443,8 @@ void PrintPreviewMessageHandler::OnNupPdfConvertDone(
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(region));
}

void PrintPreviewMessageHandler::OnCompositePdfDocumentDone(
void PrintPreviewMessageHandler::OnCompositeOrCompleteDocumentToPdfDone(
bool composite_document_using_individual_pages,
int page_count,
int document_cookie,
const PrintHostMsg_PreviewIds& ids,
Expand All @@ -403,12 +453,14 @@ void PrintPreviewMessageHandler::OnCompositePdfDocumentDone(
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PrintPreviewUI* print_preview_ui = GetPrintPreviewUI(ids.ui_id);
if (status != mojom::PdfCompositor::Status::kSuccess) {
DLOG(ERROR) << "Compositing pdf failed with error " << status;
DLOG(ERROR) << (composite_document_using_individual_pages
? "Completion of document to"
: "Compositing")
<< " pdf failed with error " << status;
if (print_preview_ui)
print_preview_ui->OnPrintPreviewFailed(ids.request_id);
return;
}

if (!print_preview_ui)
return;

Expand Down Expand Up @@ -439,6 +491,17 @@ void PrintPreviewMessageHandler::OnCompositePdfDocumentDone(
}
}

void PrintPreviewMessageHandler::OnPrepareForDocumentToPdfDone(
const PrintHostMsg_PreviewIds& ids,
mojom::PdfCompositor::Status status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status != mojom::PdfCompositor::Status::kSuccess) {
PrintPreviewUI* print_preview_ui = GetPrintPreviewUI(ids.ui_id);
if (print_preview_ui)
print_preview_ui->OnPrintPreviewFailed(ids.request_id);
}
}

void PrintPreviewMessageHandler::OnNupPdfDocumentConvertDone(
int page_count,
const PrintHostMsg_PreviewIds& ids,
Expand Down Expand Up @@ -480,6 +543,8 @@ bool PrintPreviewMessageHandler::OnMessageReceived(
handled = true;
IPC_BEGIN_MESSAGE_MAP(PrintPreviewMessageHandler, message)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidStartPreview, OnDidStartPreview)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrepareDocumentForPreview,
OnDidPrepareForDocumentToPdf)
IPC_MESSAGE_HANDLER(PrintHostMsg_PrintPreviewFailed,
OnPrintPreviewFailed)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetDefaultPageLayout,
Expand Down
16 changes: 11 additions & 5 deletions chrome/browser/printing/print_preview_message_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ class PrintPreviewMessageHandler
const PrintHostMsg_PreviewIds& ids);
void OnDidStartPreview(const PrintHostMsg_DidStartPreview_Params& params,
const PrintHostMsg_PreviewIds& ids);
void OnDidPrepareForDocumentToPdf(int document_cookie,
const PrintHostMsg_PreviewIds& ids);
void OnDidPreviewPage(content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPreviewPage_Params& params,
const PrintHostMsg_PreviewIds& ids);
Expand Down Expand Up @@ -106,11 +108,15 @@ class PrintPreviewMessageHandler
const PrintHostMsg_PreviewIds& ids,
mojom::PdfCompositor::Status status,
base::ReadOnlySharedMemoryRegion region);
void OnCompositePdfDocumentDone(int page_count,
int document_cookie,
const PrintHostMsg_PreviewIds& ids,
mojom::PdfCompositor::Status status,
base::ReadOnlySharedMemoryRegion region);
void OnCompositeOrCompleteDocumentToPdfDone(
bool composite_document_using_individual_pages,
int page_count,
int document_cookie,
const PrintHostMsg_PreviewIds& ids,
mojom::PdfCompositor::Status status,
base::ReadOnlySharedMemoryRegion region);
void OnPrepareForDocumentToPdfDone(const PrintHostMsg_PreviewIds& ids,
mojom::PdfCompositor::Status status);

void OnNupPdfConvertDone(int page_number,
const PrintHostMsg_PreviewIds& ids,
Expand Down
5 changes: 5 additions & 0 deletions chrome/browser/ui/webui/print_preview/print_preview_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "chrome/grit/print_preview_resources.h"
#include "chrome/grit/print_preview_resources_map.h"
#include "components/prefs/pref_service.h"
#include "components/printing/browser/print_manager_utils.h"
#include "components/printing/common/print_messages.h"
#include "components/strings/grit/components_strings.h"
#include "components/user_manager/user_manager.h"
Expand Down Expand Up @@ -525,6 +526,10 @@ void PrintPreviewUI::SetInitiatorTitle(
initiator_title_ = job_title;
}

bool PrintPreviewUI::ShouldCompositeDocumentUsingIndividualPages() const {
return printing::IsOopifEnabled() && source_is_modifiable_;
}

bool PrintPreviewUI::LastPageComposited(int page_number) const {
if (pages_to_render_.empty())
return false;
Expand Down
7 changes: 7 additions & 0 deletions chrome/browser/ui/webui/print_preview/print_preview_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ class PrintPreviewUI : public ConstrainedWebDialogUI {

const gfx::Size& page_size() const { return page_size_; }

// Determines if the PDF compositor is being used to generate full document
// from individual pages, which can avoid the need for an extra composite
// request containing all of the pages together.
// TODO(awscreen): Can remove this method once all modifiable content is
// handled with MSKP document type.
bool ShouldCompositeDocumentUsingIndividualPages() const;

// Returns true if |page_number| is the last page in |pages_to_render_|.
// |page_number| is a 0-based number.
bool LastPageComposited(int page_number) const;
Expand Down
58 changes: 58 additions & 0 deletions components/printing/browser/print_composite_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,45 @@ void PrintCompositeClient::DoCompositePageToPdf(
std::move(callback)));
}

void PrintCompositeClient::DoPrepareForDocumentToPdf(
int document_cookie,
mojom::PdfCompositor::PrepareForDocumentToPdfCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!GetIsDocumentConcurrentlyComposited(document_cookie));

is_doc_concurrently_composited_set_.insert(document_cookie);
auto* compositor = GetCompositeRequest(document_cookie);
compositor->PrepareForDocumentToPdf(
base::BindOnce(&PrintCompositeClient::OnDidPrepareForDocumentToPdf,
std::move(callback)));
}

void PrintCompositeClient::DoCompleteDocumentToPdf(
int document_cookie,
uint32_t pages_count,
mojom::PdfCompositor::CompleteDocumentToPdfCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(GetIsDocumentConcurrentlyComposited(document_cookie));

auto* compositor = GetCompositeRequest(document_cookie);

// Since this class owns compositor, compositor will be gone when this class
// is destructed. Mojo won't call its callback in that case so it is safe to
// use unretained |this| pointer here.
compositor->CompleteDocumentToPdf(
pages_count,
base::BindOnce(&PrintCompositeClient::OnDidCompleteDocumentToPdf,
base::Unretained(this), document_cookie,
std::move(callback)));
}

void PrintCompositeClient::DoCompositeDocumentToPdf(
int document_cookie,
content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPrintContent_Params& content,
mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!GetIsDocumentConcurrentlyComposited(document_cookie));

auto* compositor = GetCompositeRequest(document_cookie);
auto region = content.metafile_data_region.Duplicate();
Expand Down Expand Up @@ -232,6 +265,31 @@ void PrintCompositeClient::OnDidCompositeDocumentToPdf(
std::move(callback).Run(status, std::move(region));
}

// static
void PrintCompositeClient::OnDidPrepareForDocumentToPdf(
mojom::PdfCompositor::PrepareForDocumentToPdfCallback callback,
mojom::PdfCompositor::Status status) {
std::move(callback).Run(status);
}

void PrintCompositeClient::OnDidCompleteDocumentToPdf(
int document_cookie,
mojom::PdfCompositor::CompleteDocumentToPdfCallback callback,
mojom::PdfCompositor::Status status,
base::ReadOnlySharedMemoryRegion region) {
RemoveCompositeRequest(document_cookie);
// Clear all stored printed subframes.
printed_subframes_.erase(document_cookie);
// No longer concurrently compositing this document.
is_doc_concurrently_composited_set_.erase(document_cookie);
std::move(callback).Run(status, std::move(region));
}

bool PrintCompositeClient::GetIsDocumentConcurrentlyComposited(
int cookie) const {
return base::Contains(is_doc_concurrently_composited_set_, cookie);
}

mojom::PdfCompositor* PrintCompositeClient::GetCompositeRequest(int cookie) {
auto iter = compositor_map_.find(cookie);
if (iter != compositor_map_.end()) {
Expand Down
37 changes: 36 additions & 1 deletion components/printing/browser/print_composite_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,29 @@ class PrintCompositeClient

// Printing single pages is only used by print preview for early return of
// rendered results. In this case, the pages share the content with printed
// document. The entire document will always be printed and sent at the end.
// document. The document can be collected from the individual pages,
// avoiding the need to also send the entire document again as a large blob.
// This is for compositing such a single preview page.
void DoCompositePageToPdf(
int cookie,
content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPrintContent_Params& content,
mojom::PdfCompositor::CompositePageToPdfCallback callback);

// Notifies compositor to collect individual pages into a document
// when processing the individual pages for preview.
void DoPrepareForDocumentToPdf(
int document_cookie,
mojom::PdfCompositor::PrepareForDocumentToPdfCallback callback);

// Notifies compositor of the total number of pages being concurrently
// collected into the document, allowing for completion of the composition
// when all pages have been received.
void DoCompleteDocumentToPdf(
int document_cookie,
uint32_t pages_count,
mojom::PdfCompositor::CompleteDocumentToPdfCallback callback);

// Used for compositing the entire document for print preview or actual
// printing.
void DoCompositeDocumentToPdf(
Expand All @@ -64,6 +79,11 @@ class PrintCompositeClient
const PrintHostMsg_DidPrintContent_Params& content,
mojom::PdfCompositor::CompositeDocumentToPdfCallback callback);

// Get the concurrent composition status for a document. Identifies if the
// full document will be compiled from the individual pages; if not then a
// separate document object will need to be provided.
bool GetIsDocumentConcurrentlyComposited(int cookie) const;

void SetUserAgent(const std::string& user_agent) { user_agent_ = user_agent; }

private:
Expand All @@ -80,6 +100,16 @@ class PrintCompositeClient
mojom::PdfCompositor::Status status,
base::ReadOnlySharedMemoryRegion region);

static void OnDidPrepareForDocumentToPdf(
mojom::PdfCompositor::PrepareForDocumentToPdfCallback callback,
mojom::PdfCompositor::Status status);

void OnDidCompleteDocumentToPdf(
int document_cookie,
mojom::PdfCompositor::CompleteDocumentToPdfCallback callback,
mojom::PdfCompositor::Status status,
base::ReadOnlySharedMemoryRegion region);

// Get the request or create a new one if none exists.
// Since printed pages always share content with its document, they share the
// same composite request.
Expand All @@ -102,6 +132,11 @@ class PrintCompositeClient
// for that document.
std::map<int, base::flat_set<uint64_t>> printed_subframes_;

// Stores the set of cookies for documents that are doing concurrently
// composition using individual pages, so that no separate composite request
// with full-document blob is required.
base::flat_set<int> is_doc_concurrently_composited_set_;

std::string user_agent_;

WEB_CONTENTS_USER_DATA_KEY_DECL();
Expand Down
7 changes: 7 additions & 0 deletions components/printing/common/print_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,13 @@ IPC_MESSAGE_ROUTED2(PrintHostMsg_DidStartPreview,
PrintHostMsg_DidStartPreview_Params /* params */,
PrintHostMsg_PreviewIds /* ids */)

// Notify the browser of preparing to print the document, for cases where
// the document will be collected from the individual pages instead of being
// provided by an extra metafile at end containing all pages.
IPC_MESSAGE_ROUTED2(PrintHostMsg_DidPrepareDocumentForPreview,
int /* document_cookie */,
PrintHostMsg_PreviewIds /* ids */)

// Notify the browser of the default page layout according to the currently
// selected printer and page size.
// |printable_area_in_points| Specifies the printable area in points.
Expand Down
Loading

0 comments on commit b1ea8aa

Please sign in to comment.