Commit ad32b03b authored by Alan Screen's avatar Alan Screen Committed by Commit Bot

PDF compositor generate full document from individual pages

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.

This is a reland of https://crrev.com/c/1616461, which was reverted due
to a problem with death tests not detected with debug version of
components_unittests.

TBR=dcheng@chromium.org

Bug: 872935, 1024748
Change-Id: Ie22121c4bebae37882b3b2f6be808b8956c499ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1917685
Commit-Queue: Alan Screen <awscreen@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#715570}
parent 53142d1d
...@@ -138,6 +138,36 @@ void PrintPreviewMessageHandler::OnDidStartPreview( ...@@ -138,6 +138,36 @@ void PrintPreviewMessageHandler::OnDidStartPreview(
print_preview_ui->OnDidStartPreview(params, ids.request_id); 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( void PrintPreviewMessageHandler::OnDidPreviewPage(
content::RenderFrameHost* render_frame_host, content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPreviewPage_Params& params, const PrintHostMsg_DidPreviewPage_Params& params,
...@@ -188,8 +218,18 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting( ...@@ -188,8 +218,18 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
// Always try to stop the worker. // Always try to stop the worker.
StopWorker(params.document_cookie); StopWorker(params.document_cookie);
PrintPreviewUI* print_preview_ui = GetPrintPreviewUI(ids.ui_id);
if (!print_preview_ui)
return;
const PrintHostMsg_DidPrintContent_Params& content = params.content; 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; return;
if (params.expected_pages_count <= 0) { if (params.expected_pages_count <= 0) {
...@@ -197,10 +237,6 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting( ...@@ -197,10 +237,6 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
return; return;
} }
PrintPreviewUI* print_preview_ui = GetPrintPreviewUI(ids.ui_id);
if (!print_preview_ui)
return;
if (ShouldUseCompositor(print_preview_ui)) { if (ShouldUseCompositor(print_preview_ui)) {
// Don't bother compositing if this request has been cancelled already. // Don't bother compositing if this request has been cancelled already.
if (PrintPreviewUI::ShouldCancelRequest(ids)) if (PrintPreviewUI::ShouldCancelRequest(ids))
...@@ -208,16 +244,29 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting( ...@@ -208,16 +244,29 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
auto* client = PrintCompositeClient::FromWebContents(web_contents()); auto* client = PrintCompositeClient::FromWebContents(web_contents());
DCHECK(client); DCHECK(client);
auto callback = base::BindOnce(
client->DoCompositeDocumentToPdf( &PrintPreviewMessageHandler::OnCompositeOrCompleteDocumentToPdfDone,
params.document_cookie, render_frame_host, content, weak_ptr_factory_.GetWeakPtr(),
mojo::WrapCallbackWithDefaultInvokeIfNotRun( composite_document_using_individual_pages, params.expected_pages_count,
base::BindOnce( params.document_cookie, ids);
&PrintPreviewMessageHandler::OnCompositePdfDocumentDone, if (composite_document_using_individual_pages) {
weak_ptr_factory_.GetWeakPtr(), params.expected_pages_count, // Page metafile is used to composite into the document at same time.
params.document_cookie, ids), // Need to provide particulars of how many pages are required before
mojom::PdfCompositor::Status::kCompositingFailure, // document will be completed.
base::ReadOnlySharedMemoryRegion())); 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 { } else {
NotifyUIPreviewDocumentReady( NotifyUIPreviewDocumentReady(
print_preview_ui, params.expected_pages_count, ids, print_preview_ui, params.expected_pages_count, ids,
...@@ -394,7 +443,8 @@ void PrintPreviewMessageHandler::OnNupPdfConvertDone( ...@@ -394,7 +443,8 @@ void PrintPreviewMessageHandler::OnNupPdfConvertDone(
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(region)); base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(region));
} }
void PrintPreviewMessageHandler::OnCompositePdfDocumentDone( void PrintPreviewMessageHandler::OnCompositeOrCompleteDocumentToPdfDone(
bool composite_document_using_individual_pages,
int page_count, int page_count,
int document_cookie, int document_cookie,
const PrintHostMsg_PreviewIds& ids, const PrintHostMsg_PreviewIds& ids,
...@@ -403,12 +453,14 @@ void PrintPreviewMessageHandler::OnCompositePdfDocumentDone( ...@@ -403,12 +453,14 @@ void PrintPreviewMessageHandler::OnCompositePdfDocumentDone(
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
PrintPreviewUI* print_preview_ui = GetPrintPreviewUI(ids.ui_id); PrintPreviewUI* print_preview_ui = GetPrintPreviewUI(ids.ui_id);
if (status != mojom::PdfCompositor::Status::kSuccess) { 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) if (print_preview_ui)
print_preview_ui->OnPrintPreviewFailed(ids.request_id); print_preview_ui->OnPrintPreviewFailed(ids.request_id);
return; return;
} }
if (!print_preview_ui) if (!print_preview_ui)
return; return;
...@@ -439,6 +491,17 @@ void PrintPreviewMessageHandler::OnCompositePdfDocumentDone( ...@@ -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( void PrintPreviewMessageHandler::OnNupPdfDocumentConvertDone(
int page_count, int page_count,
const PrintHostMsg_PreviewIds& ids, const PrintHostMsg_PreviewIds& ids,
...@@ -480,6 +543,8 @@ bool PrintPreviewMessageHandler::OnMessageReceived( ...@@ -480,6 +543,8 @@ bool PrintPreviewMessageHandler::OnMessageReceived(
handled = true; handled = true;
IPC_BEGIN_MESSAGE_MAP(PrintPreviewMessageHandler, message) IPC_BEGIN_MESSAGE_MAP(PrintPreviewMessageHandler, message)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidStartPreview, OnDidStartPreview) IPC_MESSAGE_HANDLER(PrintHostMsg_DidStartPreview, OnDidStartPreview)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrepareDocumentForPreview,
OnDidPrepareForDocumentToPdf)
IPC_MESSAGE_HANDLER(PrintHostMsg_PrintPreviewFailed, IPC_MESSAGE_HANDLER(PrintHostMsg_PrintPreviewFailed,
OnPrintPreviewFailed) OnPrintPreviewFailed)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetDefaultPageLayout, IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetDefaultPageLayout,
......
...@@ -72,6 +72,8 @@ class PrintPreviewMessageHandler ...@@ -72,6 +72,8 @@ class PrintPreviewMessageHandler
const PrintHostMsg_PreviewIds& ids); const PrintHostMsg_PreviewIds& ids);
void OnDidStartPreview(const PrintHostMsg_DidStartPreview_Params& params, void OnDidStartPreview(const PrintHostMsg_DidStartPreview_Params& params,
const PrintHostMsg_PreviewIds& ids); const PrintHostMsg_PreviewIds& ids);
void OnDidPrepareForDocumentToPdf(int document_cookie,
const PrintHostMsg_PreviewIds& ids);
void OnDidPreviewPage(content::RenderFrameHost* render_frame_host, void OnDidPreviewPage(content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPreviewPage_Params& params, const PrintHostMsg_DidPreviewPage_Params& params,
const PrintHostMsg_PreviewIds& ids); const PrintHostMsg_PreviewIds& ids);
...@@ -106,11 +108,15 @@ class PrintPreviewMessageHandler ...@@ -106,11 +108,15 @@ class PrintPreviewMessageHandler
const PrintHostMsg_PreviewIds& ids, const PrintHostMsg_PreviewIds& ids,
mojom::PdfCompositor::Status status, mojom::PdfCompositor::Status status,
base::ReadOnlySharedMemoryRegion region); base::ReadOnlySharedMemoryRegion region);
void OnCompositePdfDocumentDone(int page_count, void OnCompositeOrCompleteDocumentToPdfDone(
int document_cookie, bool composite_document_using_individual_pages,
const PrintHostMsg_PreviewIds& ids, int page_count,
mojom::PdfCompositor::Status status, int document_cookie,
base::ReadOnlySharedMemoryRegion region); 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, void OnNupPdfConvertDone(int page_number,
const PrintHostMsg_PreviewIds& ids, const PrintHostMsg_PreviewIds& ids,
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include "chrome/grit/print_preview_resources.h" #include "chrome/grit/print_preview_resources.h"
#include "chrome/grit/print_preview_resources_map.h" #include "chrome/grit/print_preview_resources_map.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "components/printing/browser/print_manager_utils.h"
#include "components/printing/common/print_messages.h" #include "components/printing/common/print_messages.h"
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
#include "components/user_manager/user_manager.h" #include "components/user_manager/user_manager.h"
...@@ -525,6 +526,10 @@ void PrintPreviewUI::SetInitiatorTitle( ...@@ -525,6 +526,10 @@ void PrintPreviewUI::SetInitiatorTitle(
initiator_title_ = job_title; initiator_title_ = job_title;
} }
bool PrintPreviewUI::ShouldCompositeDocumentUsingIndividualPages() const {
return printing::IsOopifEnabled() && source_is_modifiable_;
}
bool PrintPreviewUI::LastPageComposited(int page_number) const { bool PrintPreviewUI::LastPageComposited(int page_number) const {
if (pages_to_render_.empty()) if (pages_to_render_.empty())
return false; return false;
......
...@@ -75,6 +75,13 @@ class PrintPreviewUI : public ConstrainedWebDialogUI { ...@@ -75,6 +75,13 @@ class PrintPreviewUI : public ConstrainedWebDialogUI {
const gfx::Size& page_size() const { return page_size_; } 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_|. // Returns true if |page_number| is the last page in |pages_to_render_|.
// |page_number| is a 0-based number. // |page_number| is a 0-based number.
bool LastPageComposited(int page_number) const; bool LastPageComposited(int page_number) const;
......
...@@ -191,12 +191,45 @@ void PrintCompositeClient::DoCompositePageToPdf( ...@@ -191,12 +191,45 @@ void PrintCompositeClient::DoCompositePageToPdf(
std::move(callback))); 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( void PrintCompositeClient::DoCompositeDocumentToPdf(
int document_cookie, int document_cookie,
content::RenderFrameHost* render_frame_host, content::RenderFrameHost* render_frame_host,
const PrintHostMsg_DidPrintContent_Params& content, const PrintHostMsg_DidPrintContent_Params& content,
mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) { mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!GetIsDocumentConcurrentlyComposited(document_cookie));
auto* compositor = GetCompositeRequest(document_cookie); auto* compositor = GetCompositeRequest(document_cookie);
auto region = content.metafile_data_region.Duplicate(); auto region = content.metafile_data_region.Duplicate();
...@@ -232,6 +265,31 @@ void PrintCompositeClient::OnDidCompositeDocumentToPdf( ...@@ -232,6 +265,31 @@ void PrintCompositeClient::OnDidCompositeDocumentToPdf(
std::move(callback).Run(status, std::move(region)); 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) { mojom::PdfCompositor* PrintCompositeClient::GetCompositeRequest(int cookie) {
auto iter = compositor_map_.find(cookie); auto iter = compositor_map_.find(cookie);
if (iter != compositor_map_.end()) { if (iter != compositor_map_.end()) {
......
...@@ -48,7 +48,8 @@ class PrintCompositeClient ...@@ -48,7 +48,8 @@ class PrintCompositeClient
// Printing single pages is only used by print preview for early return of // 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 // 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. // This is for compositing such a single preview page.
void DoCompositePageToPdf( void DoCompositePageToPdf(
int cookie, int cookie,
...@@ -56,6 +57,20 @@ class PrintCompositeClient ...@@ -56,6 +57,20 @@ class PrintCompositeClient
const PrintHostMsg_DidPrintContent_Params& content, const PrintHostMsg_DidPrintContent_Params& content,
mojom::PdfCompositor::CompositePageToPdfCallback callback); 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 // Used for compositing the entire document for print preview or actual
// printing. // printing.
void DoCompositeDocumentToPdf( void DoCompositeDocumentToPdf(
...@@ -64,6 +79,11 @@ class PrintCompositeClient ...@@ -64,6 +79,11 @@ class PrintCompositeClient
const PrintHostMsg_DidPrintContent_Params& content, const PrintHostMsg_DidPrintContent_Params& content,
mojom::PdfCompositor::CompositeDocumentToPdfCallback callback); 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; } void SetUserAgent(const std::string& user_agent) { user_agent_ = user_agent; }
private: private:
...@@ -80,6 +100,16 @@ class PrintCompositeClient ...@@ -80,6 +100,16 @@ class PrintCompositeClient
mojom::PdfCompositor::Status status, mojom::PdfCompositor::Status status,
base::ReadOnlySharedMemoryRegion region); 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. // Get the request or create a new one if none exists.
// Since printed pages always share content with its document, they share the // Since printed pages always share content with its document, they share the
// same composite request. // same composite request.
...@@ -102,6 +132,11 @@ class PrintCompositeClient ...@@ -102,6 +132,11 @@ class PrintCompositeClient
// for that document. // for that document.
std::map<int, base::flat_set<uint64_t>> printed_subframes_; 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_; std::string user_agent_;
WEB_CONTENTS_USER_DATA_KEY_DECL(); WEB_CONTENTS_USER_DATA_KEY_DECL();
......
...@@ -436,6 +436,13 @@ IPC_MESSAGE_ROUTED2(PrintHostMsg_DidStartPreview, ...@@ -436,6 +436,13 @@ IPC_MESSAGE_ROUTED2(PrintHostMsg_DidStartPreview,
PrintHostMsg_DidStartPreview_Params /* params */, PrintHostMsg_DidStartPreview_Params /* params */,
PrintHostMsg_PreviewIds /* ids */) 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 // Notify the browser of the default page layout according to the currently
// selected printer and page size. // selected printer and page size.
// |printable_area_in_points| Specifies the printable area in points. // |printable_area_in_points| Specifies the printable area in points.
......
...@@ -615,6 +615,18 @@ void RecordSiteIsolationPrintMetrics(blink::WebFrame* printed_frame) { ...@@ -615,6 +615,18 @@ void RecordSiteIsolationPrintMetrics(blink::WebFrame* printed_frame) {
cross_site_visible_frame_count); cross_site_visible_frame_count);
} }
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
bool CaptureMetafileContentInfo(const MetafileSkia& metafile,
PrintHostMsg_DidPrintContent_Params* params) {
uint32_t buf_size = metafile.GetDataSize();
if (buf_size == 0)
return false;
params->subframe_content_info = metafile.GetSubframeContentInfo();
return true;
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
bool CopyMetafileDataToReadOnlySharedMem( bool CopyMetafileDataToReadOnlySharedMem(
const MetafileSkia& metafile, const MetafileSkia& metafile,
PrintHostMsg_DidPrintContent_Params* params) { PrintHostMsg_DidPrintContent_Params* params) {
...@@ -1462,6 +1474,14 @@ PrintRenderFrameHelper::CreatePreviewDocument() { ...@@ -1462,6 +1474,14 @@ PrintRenderFrameHelper::CreatePreviewDocument() {
return CREATE_IN_PROGRESS; return CREATE_IN_PROGRESS;
} }
if (print_pages_params_->params.printed_doc_type == SkiaDocumentType::MSKP) {
// Want modifiable content of MSKP type to be collected into a document
// during individual page preview generation (to avoid separate document
// version for composition), notify to prepare to do this collection.
Send(new PrintHostMsg_DidPrepareDocumentForPreview(
routing_id(), print_pages_params_->params.document_cookie, ids));
}
while (!print_preview_context_.IsFinalPageRendered()) { while (!print_preview_context_.IsFinalPageRendered()) {
int page_number = print_preview_context_.GetNextPageNumber(); int page_number = print_preview_context_.GetNextPageNumber();
DCHECK_GE(page_number, 0); DCHECK_GE(page_number, 0);
...@@ -1528,11 +1548,22 @@ bool PrintRenderFrameHelper::FinalizePrintReadyDocument() { ...@@ -1528,11 +1548,22 @@ bool PrintRenderFrameHelper::FinalizePrintReadyDocument() {
MetafileSkia* metafile = print_preview_context_.metafile(); MetafileSkia* metafile = print_preview_context_.metafile();
PrintHostMsg_DidPreviewDocument_Params preview_params; PrintHostMsg_DidPreviewDocument_Params preview_params;
if (!CopyMetafileDataToReadOnlySharedMem(*metafile, // Modifiable content of MSKP type is collected into a document during
&preview_params.content)) { // individual page preview generation, so no need to share a separate document
LOG(ERROR) << "CopyMetafileDataToReadOnlySharedMem failed"; // version for composition.
print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED); if (print_pages_params_->params.printed_doc_type == SkiaDocumentType::MSKP) {
return false; if (!CaptureMetafileContentInfo(*metafile, &preview_params.content)) {
DLOG(ERROR) << "CaptureMetafileContentInfo failed";
print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_CAPTURE_FAILED);
return false;
}
} else {
if (!CopyMetafileDataToReadOnlySharedMem(*metafile,
&preview_params.content)) {
LOG(ERROR) << "CopyMetafileDataToReadOnlySharedMem failed";
print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED);
return false;
}
} }
preview_params.document_cookie = print_pages_params_->params.document_cookie; preview_params.document_cookie = print_pages_params_->params.document_cookie;
......
...@@ -170,7 +170,8 @@ class PrintRenderFrameHelper ...@@ -170,7 +170,8 @@ class PrintRenderFrameHelper
}; };
// These values are persisted to logs. Entries should not be renumbered and // These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused. // numeric values should never be reused. Updates need to be reflected in
// enum PrintPreviewFailureType in tools/metrics/histograms/enums.xml.
enum PrintPreviewErrorBuckets { enum PrintPreviewErrorBuckets {
PREVIEW_ERROR_NONE = 0, // Always first. PREVIEW_ERROR_NONE = 0, // Always first.
PREVIEW_ERROR_BAD_SETTING = 1, PREVIEW_ERROR_BAD_SETTING = 1,
...@@ -180,6 +181,7 @@ class PrintRenderFrameHelper ...@@ -180,6 +181,7 @@ class PrintRenderFrameHelper
PREVIEW_ERROR_MAC_DRAFT_METAFILE_INIT_FAILED_DEPRECATED = 5, PREVIEW_ERROR_MAC_DRAFT_METAFILE_INIT_FAILED_DEPRECATED = 5,
PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE_DEPRECATED = 6, PREVIEW_ERROR_PAGE_RENDERED_WITHOUT_METAFILE_DEPRECATED = 6,
PREVIEW_ERROR_INVALID_PRINTER_SETTINGS = 7, PREVIEW_ERROR_INVALID_PRINTER_SETTINGS = 7,
PREVIEW_ERROR_METAFILE_CAPTURE_FAILED = 8,
PREVIEW_ERROR_LAST_ENUM // Always last. PREVIEW_ERROR_LAST_ENUM // Always last.
}; };
......
...@@ -2,3 +2,13 @@ The pdf_compositor service should composite multiple raw pictures from different ...@@ -2,3 +2,13 @@ The pdf_compositor service should composite multiple raw pictures from different
frames into a complete one, then converts it into a pdf file within an isolated frames into a complete one, then converts it into a pdf file within an isolated
sandboxed process. Currently, it has no compositing functionality, just convert sandboxed process. Currently, it has no compositing functionality, just convert
a set of raw pictures into a pdf file within the sandboxed process. a set of raw pictures into a pdf file within the sandboxed process.
While the compositor creates single-page PDF objects it can optionally collect
those into a multi-page PDF document object. Otherwise a multi-page PDF document
is made by sending an extra multi-page metafile which contains repeats of each
of the previously processed pages all in one larger message.
Message flow when interacting with the PDF compositor is as follows:
[![IPC flow for PDF compositor
usage](ipc_flow_diagram.png)](https://docs.google.com/drawings/d/1bhm3FfLaSL42f-zw41twnOGG0kdMKMuAGoEyGuGr6HQ)
\ No newline at end of file
...@@ -154,6 +154,8 @@ void PdfCompositorImpl::CompositePageToPdf( ...@@ -154,6 +154,8 @@ void PdfCompositorImpl::CompositePageToPdf(
base::ReadOnlySharedMemoryRegion serialized_content, base::ReadOnlySharedMemoryRegion serialized_content,
const ContentToFrameMap& subframe_content_map, const ContentToFrameMap& subframe_content_map,
mojom::PdfCompositor::CompositePageToPdfCallback callback) { mojom::PdfCompositor::CompositePageToPdfCallback callback) {
if (docinfo_)
docinfo_->pages_provided++;
HandleCompositionRequest(frame_guid, std::move(serialized_content), HandleCompositionRequest(frame_guid, std::move(serialized_content),
subframe_content_map, std::move(callback)); subframe_content_map, std::move(callback));
} }
...@@ -163,10 +165,28 @@ void PdfCompositorImpl::CompositeDocumentToPdf( ...@@ -163,10 +165,28 @@ void PdfCompositorImpl::CompositeDocumentToPdf(
base::ReadOnlySharedMemoryRegion serialized_content, base::ReadOnlySharedMemoryRegion serialized_content,
const ContentToFrameMap& subframe_content_map, const ContentToFrameMap& subframe_content_map,
mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) { mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) {
DCHECK(!docinfo_);
HandleCompositionRequest(frame_guid, std::move(serialized_content), HandleCompositionRequest(frame_guid, std::move(serialized_content),
subframe_content_map, std::move(callback)); subframe_content_map, std::move(callback));
} }
void PdfCompositorImpl::PrepareForDocumentToPdf(
mojom::PdfCompositor::PrepareForDocumentToPdfCallback callback) {
DCHECK(!docinfo_);
docinfo_ = std::make_unique<DocumentInfo>(creator_);
std::move(callback).Run(mojom::PdfCompositor::Status::kSuccess);
}
void PdfCompositorImpl::CompleteDocumentToPdf(
uint32_t page_count,
mojom::PdfCompositor::CompleteDocumentToPdfCallback callback) {
DCHECK(docinfo_);
DCHECK_GT(page_count, 0U);
docinfo_->page_count = page_count;
docinfo_->callback = std::move(callback);
HandleDocumentCompletionRequest();
}
void PdfCompositorImpl::SetWebContentsURL(const GURL& url) { void PdfCompositorImpl::SetWebContentsURL(const GURL& url) {
// Record the most recent url we tried to print. This should be sufficient // Record the most recent url we tried to print. This should be sufficient
// for users using print preview by default. // for users using print preview by default.
...@@ -197,6 +217,15 @@ void PdfCompositorImpl::UpdateRequestsWithSubframeInfo( ...@@ -197,6 +217,15 @@ void PdfCompositorImpl::UpdateRequestsWithSubframeInfo(
FulfillRequest(std::move(request->serialized_content), FulfillRequest(std::move(request->serialized_content),
request->subframe_content_map, request->subframe_content_map,
std::move(request->callback)); std::move(request->callback));
// Check for a collected print preview document that was waiting on
// this page to finish.
if (docinfo_) {
if (docinfo_->page_count &&
(docinfo_->pages_written == docinfo_->page_count)) {
CompleteDocumentRequest(std::move(docinfo_->callback));
}
}
it = requests_.erase(it); it = requests_.erase(it);
continue; continue;
} }
...@@ -269,6 +298,16 @@ void PdfCompositorImpl::HandleCompositionRequest( ...@@ -269,6 +298,16 @@ void PdfCompositorImpl::HandleCompositionRequest(
std::move(callback))); std::move(callback)));
} }
void PdfCompositorImpl::HandleDocumentCompletionRequest() {
if (docinfo_->pages_written == docinfo_->page_count) {
CompleteDocumentRequest(std::move(docinfo_->callback));
return;
}
// Just need to wait on pages to percolate through processing, callback will
// be handled from UpdateRequestsWithSubframeInfo() once the pending requests
// have finished.
}
mojom::PdfCompositor::Status PdfCompositorImpl::CompositeToPdf( mojom::PdfCompositor::Status PdfCompositorImpl::CompositeToPdf(
base::ReadOnlySharedMemoryMapping shared_mem, base::ReadOnlySharedMemoryMapping shared_mem,
const ContentToFrameMap& subframe_content_map, const ContentToFrameMap& subframe_content_map,
...@@ -303,6 +342,14 @@ mojom::PdfCompositor::Status PdfCompositorImpl::CompositeToPdf( ...@@ -303,6 +342,14 @@ mojom::PdfCompositor::Status PdfCompositorImpl::CompositeToPdf(
SkCanvas* canvas = doc->beginPage(page.fSize.width(), page.fSize.height()); SkCanvas* canvas = doc->beginPage(page.fSize.width(), page.fSize.height());
canvas->drawPicture(page.fPicture); canvas->drawPicture(page.fPicture);
doc->endPage(); doc->endPage();
if (docinfo_) {
// Also collect this page into document PDF.
SkCanvas* canvas_doc =
docinfo_->doc->beginPage(page.fSize.width(), page.fSize.height());
canvas_doc->drawPicture(page.fPicture);
docinfo_->doc->endPage();
docinfo_->pages_written++;
}
} }
doc->close(); doc->close();
...@@ -318,6 +365,24 @@ mojom::PdfCompositor::Status PdfCompositorImpl::CompositeToPdf( ...@@ -318,6 +365,24 @@ mojom::PdfCompositor::Status PdfCompositorImpl::CompositeToPdf(
return mojom::PdfCompositor::Status::kSuccess; return mojom::PdfCompositor::Status::kSuccess;
} }
mojom::PdfCompositor::Status PdfCompositorImpl::CompleteDocumentToPdf(
base::ReadOnlySharedMemoryRegion* region) {
docinfo_->doc->close();
base::MappedReadOnlyRegion region_mapping =
mojo::CreateReadOnlySharedMemoryRegion(
docinfo_->compositor_stream.bytesWritten());
if (!region_mapping.IsValid()) {
DLOG(ERROR)
<< "CompleteDocumentToPdf: Cannot create new shared memory region.";
return mojom::PdfCompositor::Status::kHandleMapError;
}
docinfo_->compositor_stream.copyToAndReset(region_mapping.mapping.memory());
*region = std::move(region_mapping.region);
return mojom::PdfCompositor::Status::kSuccess;
}
void PdfCompositorImpl::CompositeSubframe(FrameInfo* frame_info) { void PdfCompositorImpl::CompositeSubframe(FrameInfo* frame_info) {
frame_info->composited = true; frame_info->composited = true;
...@@ -361,18 +426,30 @@ void PdfCompositorImpl::FulfillRequest( ...@@ -361,18 +426,30 @@ void PdfCompositorImpl::FulfillRequest(
std::move(callback).Run(status, std::move(region)); std::move(callback).Run(status, std::move(region));
} }
void PdfCompositorImpl::CompleteDocumentRequest(
CompleteDocumentToPdfCallback callback) {
base::ReadOnlySharedMemoryRegion region;
auto status = CompleteDocumentToPdf(&region);
std::move(callback).Run(status, std::move(region));
}
PdfCompositorImpl::FrameContentInfo::FrameContentInfo( PdfCompositorImpl::FrameContentInfo::FrameContentInfo(
base::ReadOnlySharedMemoryMapping content, base::ReadOnlySharedMemoryMapping content,
const ContentToFrameMap& map) const ContentToFrameMap& map)
: serialized_content(std::move(content)), subframe_content_map(map) {} : serialized_content(std::move(content)), subframe_content_map(map) {}
PdfCompositorImpl::FrameContentInfo::FrameContentInfo() {} PdfCompositorImpl::FrameContentInfo::FrameContentInfo() = default;
PdfCompositorImpl::FrameContentInfo::~FrameContentInfo() = default;
PdfCompositorImpl::FrameInfo::FrameInfo() = default;
PdfCompositorImpl::FrameContentInfo::~FrameContentInfo() {} PdfCompositorImpl::FrameInfo::~FrameInfo() = default;
PdfCompositorImpl::FrameInfo::FrameInfo() {} PdfCompositorImpl::DocumentInfo::DocumentInfo(const std::string& creator)
: doc(MakePdfDocument(creator, &compositor_stream)) {}
PdfCompositorImpl::FrameInfo::~FrameInfo() {} PdfCompositorImpl::DocumentInfo::~DocumentInfo() = default;
PdfCompositorImpl::RequestInfo::RequestInfo( PdfCompositorImpl::RequestInfo::RequestInfo(
base::ReadOnlySharedMemoryMapping content, base::ReadOnlySharedMemoryMapping content,
...@@ -383,6 +460,6 @@ PdfCompositorImpl::RequestInfo::RequestInfo( ...@@ -383,6 +460,6 @@ PdfCompositorImpl::RequestInfo::RequestInfo(
pending_subframes(pending_subframes), pending_subframes(pending_subframes),
callback(std::move(callback)) {} callback(std::move(callback)) {}
PdfCompositorImpl::RequestInfo::~RequestInfo() {} PdfCompositorImpl::RequestInfo::~RequestInfo() = default;
} // namespace printing } // namespace printing
...@@ -23,6 +23,9 @@ ...@@ -23,6 +23,9 @@
#include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver.h"
#include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkStream.h"
class SkDocument;
namespace base { namespace base {
class SingleThreadTaskRunner; class SingleThreadTaskRunner;
...@@ -67,6 +70,11 @@ class PdfCompositorImpl : public mojom::PdfCompositor { ...@@ -67,6 +70,11 @@ class PdfCompositorImpl : public mojom::PdfCompositor {
base::ReadOnlySharedMemoryRegion serialized_content, base::ReadOnlySharedMemoryRegion serialized_content,
const ContentToFrameMap& subframe_content_map, const ContentToFrameMap& subframe_content_map,
mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) override; mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) override;
void PrepareForDocumentToPdf(
mojom::PdfCompositor::PrepareForDocumentToPdfCallback callback) override;
void CompleteDocumentToPdf(
uint32_t page_count,
mojom::PdfCompositor::CompleteDocumentToPdfCallback callback) override;
void SetWebContentsURL(const GURL& url) override; void SetWebContentsURL(const GURL& url) override;
void SetUserAgent(const std::string& user_agent) override; void SetUserAgent(const std::string& user_agent) override;
...@@ -78,16 +86,31 @@ class PdfCompositorImpl : public mojom::PdfCompositor { ...@@ -78,16 +86,31 @@ class PdfCompositorImpl : public mojom::PdfCompositor {
base::OnceCallback<void(PdfCompositor::Status, base::OnceCallback<void(PdfCompositor::Status,
base::ReadOnlySharedMemoryRegion)>; base::ReadOnlySharedMemoryRegion)>;
using PrepareForDocumentToPdfCallback =
base::OnceCallback<void(PdfCompositor::Status)>;
using CompleteDocumentToPdfCallback =
base::OnceCallback<void(PdfCompositor::Status,
base::ReadOnlySharedMemoryRegion)>;
// The core function for content composition and conversion to a pdf file.
// Make this function virtual so tests can override it. // Make this function virtual so tests can override it.
virtual mojom::PdfCompositor::Status CompositeToPdf(
base::ReadOnlySharedMemoryMapping shared_mem,
const ContentToFrameMap& subframe_content_map,
base::ReadOnlySharedMemoryRegion* region);
// Make these functions virtual so tests can override them.
virtual void FulfillRequest( virtual void FulfillRequest(
base::ReadOnlySharedMemoryMapping serialized_content, base::ReadOnlySharedMemoryMapping serialized_content,
const ContentToFrameMap& subframe_content_map, const ContentToFrameMap& subframe_content_map,
CompositeToPdfCallback callback); CompositeToPdfCallback callback);
virtual void CompleteDocumentRequest(CompleteDocumentToPdfCallback callback);
private: private:
FRIEND_TEST_ALL_PREFIXES(PdfCompositorImplTest, IsReadyToComposite); FRIEND_TEST_ALL_PREFIXES(PdfCompositorImplTest, IsReadyToComposite);
FRIEND_TEST_ALL_PREFIXES(PdfCompositorImplTest, MultiLayerDependency); FRIEND_TEST_ALL_PREFIXES(PdfCompositorImplTest, MultiLayerDependency);
FRIEND_TEST_ALL_PREFIXES(PdfCompositorImplTest, DependencyLoop); FRIEND_TEST_ALL_PREFIXES(PdfCompositorImplTest, DependencyLoop);
friend class MockCompletionPdfCompositorImpl;
// The map needed during content deserialization. It stores the mapping // The map needed during content deserialization. It stores the mapping
// between content id and its actual content. // between content id and its actual content.
...@@ -139,6 +162,22 @@ class PdfCompositorImpl : public mojom::PdfCompositor { ...@@ -139,6 +162,22 @@ class PdfCompositorImpl : public mojom::PdfCompositor {
base::flat_set<uint64_t> pending_subframes; base::flat_set<uint64_t> pending_subframes;
CompositeToPdfCallback callback; CompositeToPdfCallback callback;
bool is_concurrent_doc_composition = false;
};
// Stores the concurrent document composition information.
struct DocumentInfo {
// Create the DocumentInfo object, which also creates a corresponding Skia
// document object.
explicit DocumentInfo(const std::string& creator);
~DocumentInfo();
SkDynamicMemoryWStream compositor_stream;
sk_sp<SkDocument> doc;
uint32_t pages_provided = 0;
uint32_t pages_written = 0;
uint32_t page_count = 0;
CompleteDocumentToPdfCallback callback;
}; };
// Check whether any request is waiting for the specific subframe, if so, // Check whether any request is waiting for the specific subframe, if so,
...@@ -167,11 +206,14 @@ class PdfCompositorImpl : public mojom::PdfCompositor { ...@@ -167,11 +206,14 @@ class PdfCompositorImpl : public mojom::PdfCompositor {
base::ReadOnlySharedMemoryRegion serialized_content, base::ReadOnlySharedMemoryRegion serialized_content,
const ContentToFrameMap& subframe_content_ids, const ContentToFrameMap& subframe_content_ids,
CompositeToPdfCallback callback); CompositeToPdfCallback callback);
void HandleDocumentCompletionRequest();
// The core function for content composition and conversion to a pdf file.
mojom::PdfCompositor::Status CompositeToPdf( // Document content composition support functions when document is compiled
base::ReadOnlySharedMemoryMapping shared_mem, // using individual pages' content. These are not used when document is
const ContentToFrameMap& subframe_content_map, // composited with a separate metafile object.
mojom::PdfCompositor::Status PrepareForDocumentToPdf();
mojom::PdfCompositor::Status UpdateDocumentMetadata(uint32_t page_count);
mojom::PdfCompositor::Status CompleteDocumentToPdf(
base::ReadOnlySharedMemoryRegion* region); base::ReadOnlySharedMemoryRegion* region);
// Composite the content of a subframe. // Composite the content of a subframe.
...@@ -195,6 +237,7 @@ class PdfCompositorImpl : public mojom::PdfCompositor { ...@@ -195,6 +237,7 @@ class PdfCompositorImpl : public mojom::PdfCompositor {
FrameMap frame_info_map_; FrameMap frame_info_map_;
std::vector<std::unique_ptr<RequestInfo>> requests_; std::vector<std::unique_ptr<RequestInfo>> requests_;
std::unique_ptr<DocumentInfo> docinfo_;
DISALLOW_COPY_AND_ASSIGN(PdfCompositorImpl); DISALLOW_COPY_AND_ASSIGN(PdfCompositorImpl);
}; };
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "components/crash/core/common/crash_key.h" #include "components/crash/core/common/crash_key.h"
#include "components/services/pdf_compositor/pdf_compositor_impl.h" #include "components/services/pdf_compositor/pdf_compositor_impl.h"
...@@ -41,6 +42,37 @@ class MockPdfCompositorImpl : public PdfCompositorImpl { ...@@ -41,6 +42,37 @@ class MockPdfCompositorImpl : public PdfCompositorImpl {
} }
}; };
// MockCompletionPdfCompositorImpl is used for testing related to
// Prepare/Complete document pipeline.
class MockCompletionPdfCompositorImpl : public PdfCompositorImpl {
public:
MockCompletionPdfCompositorImpl()
: PdfCompositorImpl(mojo::NullReceiver(),
false /* initialize_environment */,
nullptr /* io_task_runner */) {}
~MockCompletionPdfCompositorImpl() override = default;
MOCK_CONST_METHOD0(OnCompleteDocumentRequest, void());
MOCK_METHOD2(OnCompositeToPdf, void(uint64_t, int));
protected:
mojom::PdfCompositor::Status CompositeToPdf(
base::ReadOnlySharedMemoryMapping shared_mem,
const ContentToFrameMap& subframe_content_map,
base::ReadOnlySharedMemoryRegion* region) override {
const auto* data = shared_mem.GetMemoryAs<const TestRequestData>();
if (docinfo_)
docinfo_->pages_written++;
OnCompositeToPdf(data->frame_guid, data->page_num);
return mojom::PdfCompositor::Status::kSuccess;
}
void CompleteDocumentRequest(
CompleteDocumentToPdfCallback callback) override {
OnCompleteDocumentRequest();
}
};
class PdfCompositorImplTest : public testing::Test { class PdfCompositorImplTest : public testing::Test {
public: public:
PdfCompositorImplTest() PdfCompositorImplTest()
...@@ -65,6 +97,18 @@ class PdfCompositorImplTest : public testing::Test { ...@@ -65,6 +97,18 @@ class PdfCompositorImplTest : public testing::Test {
// A stub for testing, no implementation. // A stub for testing, no implementation.
} }
static void OnPrepareForDocumentToPdfCallback(
mojom::PdfCompositor::Status status) {
// A stub for testing, no implementation.
}
void OnCompositeOrCompleteDocumentToPdfCallback(
mojom::PdfCompositor::Status status,
base::ReadOnlySharedMemoryRegion region) {
// A stub for testing, only care about status.
status_ = status;
}
static base::ReadOnlySharedMemoryRegion CreateTestData(uint64_t frame_guid, static base::ReadOnlySharedMemoryRegion CreateTestData(uint64_t frame_guid,
int page_num) { int page_num) {
static constexpr size_t kSize = sizeof(TestRequestData); static constexpr size_t kSize = sizeof(TestRequestData);
...@@ -75,10 +119,13 @@ class PdfCompositorImplTest : public testing::Test { ...@@ -75,10 +119,13 @@ class PdfCompositorImplTest : public testing::Test {
return std::move(region.region); return std::move(region.region);
} }
mojom::PdfCompositor::Status GetStatus() const { return status_; }
private: private:
base::test::TaskEnvironment task_environment_; base::test::TaskEnvironment task_environment_;
std::unique_ptr<base::RunLoop> run_loop_; std::unique_ptr<base::RunLoop> run_loop_;
bool is_ready_; bool is_ready_;
mojom::PdfCompositor::Status status_ = mojom::PdfCompositor::Status::kSuccess;
}; };
class PdfCompositorImplCrashKeyTest : public PdfCompositorImplTest { class PdfCompositorImplCrashKeyTest : public PdfCompositorImplTest {
...@@ -323,4 +370,38 @@ TEST_F(PdfCompositorImplCrashKeyTest, SetCrashKey) { ...@@ -323,4 +370,38 @@ TEST_F(PdfCompositorImplCrashKeyTest, SetCrashKey) {
EXPECT_EQ(crash_reporter::GetCrashKeyValue("main-frame-url"), url_str); EXPECT_EQ(crash_reporter::GetCrashKeyValue("main-frame-url"), url_str);
} }
TEST_F(PdfCompositorImplTest, MultiRequestsBasicCompleteDocument) {
MockCompletionPdfCompositorImpl impl;
// Page 0 with frame 3 has content 1, which refers to frame 8.
// When the content is not available, the request is not fulfilled.
const ContentToFrameMap subframe_content_map = {{1, 8}};
impl.PrepareForDocumentToPdf(base::BindOnce(
&PdfCompositorImplTest::OnPrepareForDocumentToPdfCallback));
EXPECT_CALL(impl, OnCompositeToPdf(testing::_, testing::_)).Times(0);
impl.CompositePageToPdf(
3, CreateTestData(3, 0), subframe_content_map,
base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
testing::Mock::VerifyAndClearExpectations(&impl);
// When frame 8's content is ready, the previous request should be fulfilled.
EXPECT_CALL(impl, OnCompositeToPdf(testing::_, testing::_)).Times(1);
impl.AddSubframeContent(8, CreateTestData(8, -1), ContentToFrameMap());
testing::Mock::VerifyAndClearExpectations(&impl);
// The following requests which only depends on frame 8 should be
// immediately fulfilled.
EXPECT_CALL(impl, OnCompositeToPdf(testing::_, testing::_)).Times(1);
impl.CompositePageToPdf(
3, CreateTestData(3, 1), subframe_content_map,
base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback));
testing::Mock::VerifyAndClearExpectations(&impl);
EXPECT_CALL(impl, OnCompleteDocumentRequest()).Times(1);
impl.CompleteDocumentToPdf(
2, base::BindOnce(
&PdfCompositorImplTest::OnCompositeOrCompleteDocumentToPdfCallback,
base::Unretained(this)));
EXPECT_EQ(GetStatus(), mojom::PdfCompositor::Status::kSuccess);
}
} // namespace printing } // namespace printing
...@@ -57,13 +57,30 @@ interface PdfCompositor { ...@@ -57,13 +57,30 @@ interface PdfCompositor {
// Requests to composite the entire document and convert it into a PDF file. // Requests to composite the entire document and convert it into a PDF file.
// All the arguments carry the same meaning as CompositePageToPdf() above, // All the arguments carry the same meaning as CompositePageToPdf() above,
// except this call doesn't have |page_num|. // except this call doesn't have |page_num|. Cannot be used in conjunction
// with PrepareForDocumentToPdf()/CompleteDocumentToPdf().
CompositeDocumentToPdf(uint64 frame_guid, CompositeDocumentToPdf(uint64 frame_guid,
mojo_base.mojom.ReadOnlySharedMemoryRegion sk_region, mojo_base.mojom.ReadOnlySharedMemoryRegion sk_region,
map<uint32, uint64> subframe_content_info) map<uint32, uint64> subframe_content_info)
=> (Status status, => (Status status,
mojo_base.mojom.ReadOnlySharedMemoryRegion? pdf_region); mojo_base.mojom.ReadOnlySharedMemoryRegion? pdf_region);
// Notifies that composition is to collect individual pages from
// CompositePageToPdf() concurrently into a document. Must be issued once
// prior to any CompositePageToPdf() calls in order for concurrent collection
// to be performed.
PrepareForDocumentToPdf()
=> (Status status);
// Signals that all pages for a composite document have been sent via
// CompositePageToPdf(), allowing for document composition to be wrapped up.
// Is to be used in conjunction with PrepareForDocumentToPdf(), which must be
// made prior to this and any CompositePageToPdf() calls. This must be called
// exactly once to wrap up the document.
CompleteDocumentToPdf(uint32 pages_count)
=> (Status status,
mojo_base.mojom.ReadOnlySharedMemoryRegion? pdf_region);
// Sets the URL which is committed in the main frame of the WebContents, // Sets the URL which is committed in the main frame of the WebContents,
// for use in crash diagnosis. // for use in crash diagnosis.
SetWebContentsURL(url.mojom.Url url); SetWebContentsURL(url.mojom.Url url);
......
...@@ -50876,6 +50876,9 @@ Called by update_net_trust_anchors.py.--> ...@@ -50876,6 +50876,9 @@ Called by update_net_trust_anchors.py.-->
</enum> </enum>
<enum name="PrintPreviewFailureType"> <enum name="PrintPreviewFailureType">
<!-- This must be kept current with PrintPreviewErrorBuckets in
components/printing/renderer/print_render_frame_helper.h. -->
<int value="0" label="No error"/> <int value="0" label="No error"/>
<int value="1" label="Bad settings from print preview tab"/> <int value="1" label="Bad settings from print preview tab"/>
<int value="2" label="Copy metadata failed"/> <int value="2" label="Copy metadata failed"/>
...@@ -50884,6 +50887,7 @@ Called by update_net_trust_anchors.py.--> ...@@ -50884,6 +50887,7 @@ Called by update_net_trust_anchors.py.-->
<int value="5" label="Mac draft metafile init failed (Deprecated)"/> <int value="5" label="Mac draft metafile init failed (Deprecated)"/>
<int value="6" label="PreviewPageRendered with no metafile (Deprecated)"/> <int value="6" label="PreviewPageRendered with no metafile (Deprecated)"/>
<int value="7" label="Received bad printer settings"/> <int value="7" label="Received bad printer settings"/>
<int value="8" label="Capture metadata failed"/>
</enum> </enum>
<enum name="PrintPreviewFontTypeType"> <enum name="PrintPreviewFontTypeType">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment