| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "libcef/browser/printing/print_view_manager.h" |
| |
| #include "include/internal/cef_types_wrappers.h" |
| #include "libcef/browser/browser_info.h" |
| #include "libcef/browser/browser_info_manager.h" |
| #include "libcef/browser/download_manager_delegate.h" |
| #include "libcef/browser/extensions/extension_web_contents_observer.h" |
| |
| #include <map> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/lazy_instance.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/task/post_task.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/printing/print_job_manager.h" |
| #include "chrome/browser/printing/print_preview_dialog_controller.h" |
| #include "chrome/browser/printing/print_preview_message_handler.h" |
| #include "chrome/browser/printing/print_view_manager.h" |
| #include "chrome/browser/printing/printer_query.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h" |
| #include "components/printing/common/print.mojom.h" |
| #include "components/printing/common/print_messages.h" |
| #include "content/browser/download/download_manager_impl.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/download_manager.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "mojo/public/cpp/bindings/associated_remote.h" |
| #include "printing/metafile_skia.h" |
| #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" |
| |
| #include "libcef/browser/thread_util.h" |
| |
| using content::BrowserThread; |
| |
| namespace printing { |
| |
| namespace { |
| |
| const int PREVIEW_UIID = 12345678; |
| |
| // Convert CefPdfPrintSettings into base::DictionaryValue. |
| void FillInDictionaryFromPdfPrintSettings( |
| const CefPdfPrintSettings& pdf_settings, |
| int request_id, |
| base::DictionaryValue& print_settings) { |
| // Fixed settings. |
| print_settings.SetIntKey(kSettingPrinterType, kPdfPrinter); |
| print_settings.SetInteger(kSettingColor, GRAY); |
| print_settings.SetInteger(kSettingDuplexMode, SIMPLEX); |
| print_settings.SetInteger(kSettingCopies, 1); |
| print_settings.SetBoolean(kSettingCollate, false); |
| print_settings.SetString(kSettingDeviceName, ""); |
| print_settings.SetBoolean(kSettingRasterizePdf, false); |
| print_settings.SetBoolean(kSettingPreviewModifiable, false); |
| print_settings.SetInteger(kSettingDpiHorizontal, 0); |
| print_settings.SetInteger(kSettingDpiVertical, 0); |
| print_settings.SetInteger(kSettingPagesPerSheet, 1); |
| |
| // User defined settings. |
| print_settings.SetBoolean(kSettingLandscape, !!pdf_settings.landscape); |
| print_settings.SetBoolean(kSettingShouldPrintSelectionOnly, |
| !!pdf_settings.selection_only); |
| print_settings.SetBoolean(kSettingShouldPrintBackgrounds, |
| !!pdf_settings.backgrounds_enabled); |
| print_settings.SetBoolean(kSettingHeaderFooterEnabled, |
| !!pdf_settings.header_footer_enabled); |
| print_settings.SetInteger(kSettingScaleFactor, pdf_settings.scale_factor > 0 |
| ? pdf_settings.scale_factor |
| : 100); |
| |
| if (pdf_settings.header_footer_enabled) { |
| print_settings.SetString( |
| kSettingHeaderFooterTitle, |
| CefString(&pdf_settings.header_footer_title).ToString16()); |
| print_settings.SetString( |
| kSettingHeaderFooterURL, |
| CefString(&pdf_settings.header_footer_url).ToString16()); |
| } |
| |
| if (pdf_settings.page_width > 0 && pdf_settings.page_height > 0) { |
| std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue); |
| dict->SetInteger(kSettingMediaSizeWidthMicrons, pdf_settings.page_width); |
| dict->SetInteger(kSettingMediaSizeHeightMicrons, pdf_settings.page_height); |
| print_settings.Set(kSettingMediaSize, std::move(dict)); |
| } |
| |
| int margin_type = DEFAULT_MARGINS; |
| switch (pdf_settings.margin_type) { |
| case PDF_PRINT_MARGIN_NONE: |
| margin_type = NO_MARGINS; |
| break; |
| case PDF_PRINT_MARGIN_MINIMUM: |
| margin_type = PRINTABLE_AREA_MARGINS; |
| break; |
| case PDF_PRINT_MARGIN_CUSTOM: |
| margin_type = CUSTOM_MARGINS; |
| break; |
| default: |
| break; |
| } |
| |
| print_settings.SetInteger(kSettingMarginsType, margin_type); |
| if (margin_type == CUSTOM_MARGINS) { |
| std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue); |
| dict->SetInteger(kSettingMarginTop, pdf_settings.margin_top); |
| dict->SetInteger(kSettingMarginRight, pdf_settings.margin_right); |
| dict->SetInteger(kSettingMarginBottom, pdf_settings.margin_bottom); |
| dict->SetInteger(kSettingMarginLeft, pdf_settings.margin_left); |
| print_settings.Set(kSettingMarginsCustom, std::move(dict)); |
| } |
| |
| // Service settings. |
| print_settings.SetInteger(kPreviewUIID, PREVIEW_UIID); |
| print_settings.SetInteger(kPreviewRequestID, request_id); |
| print_settings.SetBoolean(kIsFirstRequest, request_id != 0); |
| } |
| |
| void StopWorker(int document_cookie) { |
| if (document_cookie <= 0) |
| return; |
| scoped_refptr<PrintQueriesQueue> queue = |
| g_browser_process->print_job_manager()->queue(); |
| std::unique_ptr<PrinterQuery> printer_query = |
| queue->PopPrinterQuery(document_cookie); |
| if (printer_query.get()) { |
| base::PostTask( |
| FROM_HERE, {BrowserThread::IO}, |
| base::BindOnce(&PrinterQuery::StopWorker, std::move(printer_query))); |
| } |
| } |
| |
| // Write the PDF file to disk. |
| void SavePdfFile(scoped_refptr<base::RefCountedSharedMemoryMapping> data, |
| const base::FilePath& path, |
| const CefPrintViewManager::PdfPrintCallback& callback) { |
| CEF_REQUIRE_BLOCKING(); |
| DCHECK_GT(data->size(), 0U); |
| |
| MetafileSkia metafile; |
| metafile.InitFromData(*data); |
| |
| base::File file(path, |
| base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| bool ok = file.IsValid() && metafile.SaveTo(&file); |
| |
| if (!callback.is_null()) { |
| base::PostTask(FROM_HERE, {BrowserThread::UI}, base::Bind(callback, ok)); |
| } |
| } |
| |
| } // namespace |
| |
| struct CefPrintViewManager::PdfPrintState { |
| content::RenderFrameHost* printing_rfh_ = nullptr; |
| base::FilePath output_path_; |
| base::DictionaryValue settings_; |
| PdfPrintCallback callback_; |
| }; |
| |
| CefPrintViewManager::CefPrintViewManager(content::WebContents* web_contents) |
| : WebContentsObserver(web_contents) { |
| PrintViewManager::CreateForWebContents(web_contents); |
| PrintPreviewMessageHandler::CreateForWebContents(web_contents); |
| } |
| |
| CefPrintViewManager::~CefPrintViewManager() { |
| TerminatePdfPrintJob(); |
| } |
| |
| bool CefPrintViewManager::PrintToPDF(content::RenderFrameHost* rfh, |
| const base::FilePath& path, |
| const CefPdfPrintSettings& settings, |
| const PdfPrintCallback& callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // Don't start print again while printing is currently in progress. |
| if (pdf_print_state_) |
| return false; |
| |
| // Don't print interstitials or crashed tabs. |
| if (!web_contents() || web_contents()->ShowingInterstitialPage() || |
| web_contents()->IsCrashed()) { |
| return false; |
| } |
| |
| pdf_print_state_.reset(new PdfPrintState); |
| pdf_print_state_->printing_rfh_ = rfh; |
| pdf_print_state_->output_path_ = path; |
| pdf_print_state_->callback_ = callback; |
| |
| FillInDictionaryFromPdfPrintSettings(settings, ++next_pdf_request_id_, |
| pdf_print_state_->settings_); |
| |
| mojo::AssociatedRemote<printing::mojom::PrintRenderFrame> |
| print_render_frame_remote; |
| rfh->GetRemoteAssociatedInterfaces()->GetInterface( |
| &print_render_frame_remote); |
| print_render_frame_remote->InitiatePrintPreview({}, |
| !!settings.selection_only); |
| |
| return true; |
| } |
| |
| bool CefPrintViewManager::PrintPreviewNow(content::RenderFrameHost* rfh, |
| bool has_selection) { |
| return PrintViewManager::FromWebContents(web_contents()) |
| ->PrintPreviewNow(rfh, has_selection); |
| } |
| |
| void CefPrintViewManager::RenderFrameDeleted( |
| content::RenderFrameHost* render_frame_host) { |
| if (pdf_print_state_ && |
| render_frame_host == pdf_print_state_->printing_rfh_) { |
| TerminatePdfPrintJob(); |
| } |
| } |
| |
| void CefPrintViewManager::NavigationStopped() { |
| TerminatePdfPrintJob(); |
| } |
| |
| void CefPrintViewManager::RenderProcessGone(base::TerminationStatus status) { |
| TerminatePdfPrintJob(); |
| } |
| |
| bool CefPrintViewManager::OnMessageReceived( |
| const IPC::Message& message, |
| content::RenderFrameHost* render_frame_host) { |
| bool handled = true; |
| if (!pdf_print_state_) { |
| IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(CefPrintViewManager, message, |
| render_frame_host) |
| IPC_MESSAGE_HANDLER(PrintHostMsg_RequestPrintPreview, |
| OnRequestPrintPreview) |
| IPC_MESSAGE_HANDLER(PrintHostMsg_ShowScriptedPrintPreview, |
| OnShowScriptedPrintPreview) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| return false; |
| } |
| |
| IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(CefPrintViewManager, message, |
| render_frame_host) |
| IPC_MESSAGE_HANDLER(PrintHostMsg_DidShowPrintDialog, |
| OnDidShowPrintDialog_PrintToPdf) |
| IPC_MESSAGE_HANDLER(PrintHostMsg_RequestPrintPreview, |
| OnRequestPrintPreview_PrintToPdf) |
| IPC_MESSAGE_HANDLER(PrintHostMsg_MetafileReadyForPrinting, |
| OnMetafileReadyForPrinting_PrintToPdf) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| |
| return handled; |
| } |
| void CefPrintViewManager::OnRequestPrintPreview( |
| content::RenderFrameHost* rfh, |
| const PrintHostMsg_RequestPrintPreview_Params&) { |
| InitializePrintPreview(rfh->GetFrameTreeNodeId()); |
| } |
| |
| void CefPrintViewManager::OnShowScriptedPrintPreview( |
| content::RenderFrameHost* rfh, |
| bool source_is_modifiable) { |
| InitializePrintPreview(rfh->GetFrameTreeNodeId()); |
| } |
| |
| void CefPrintViewManager::OnDidShowPrintDialog_PrintToPdf( |
| content::RenderFrameHost* rfh) {} |
| |
| void CefPrintViewManager::OnRequestPrintPreview_PrintToPdf( |
| content::RenderFrameHost* rfh, |
| const PrintHostMsg_RequestPrintPreview_Params&) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (!pdf_print_state_) |
| return; |
| |
| DCHECK_EQ(pdf_print_state_->printing_rfh_, rfh); |
| |
| mojo::AssociatedRemote<printing::mojom::PrintRenderFrame> |
| print_render_frame_remote; |
| rfh->GetRemoteAssociatedInterfaces()->GetInterface( |
| &print_render_frame_remote); |
| print_render_frame_remote->PrintPreview(pdf_print_state_->settings_.Clone()); |
| } |
| |
| void CefPrintViewManager::OnMetafileReadyForPrinting_PrintToPdf( |
| content::RenderFrameHost* rfh, |
| const PrintHostMsg_DidPreviewDocument_Params& params, |
| const PrintHostMsg_PreviewIds& ids) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| StopWorker(params.document_cookie); |
| |
| if (!pdf_print_state_) |
| return; |
| |
| DCHECK_EQ(pdf_print_state_->printing_rfh_, rfh); |
| |
| mojo::AssociatedRemote<printing::mojom::PrintRenderFrame> |
| print_render_frame_remote; |
| rfh->GetRemoteAssociatedInterfaces()->GetInterface( |
| &print_render_frame_remote); |
| print_render_frame_remote->OnPrintPreviewDialogClosed(); |
| |
| auto shared_buf = base::RefCountedSharedMemoryMapping::CreateFromWholeRegion( |
| params.content.metafile_data_region); |
| if (!shared_buf) { |
| TerminatePdfPrintJob(); |
| return; |
| } |
| |
| const base::FilePath output_path = pdf_print_state_->output_path_; |
| const PdfPrintCallback print_callback = pdf_print_state_->callback_; |
| |
| // Reset state information. |
| pdf_print_state_.reset(); |
| |
| // Save the PDF file to disk and then execute the callback. |
| CEF_POST_USER_VISIBLE_TASK( |
| base::Bind(&SavePdfFile, shared_buf, output_path, print_callback)); |
| } |
| |
| void CefPrintViewManager::TerminatePdfPrintJob() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (!pdf_print_state_) |
| return; |
| |
| if (!pdf_print_state_->callback_.is_null()) { |
| // Execute the callback. |
| base::PostTask(FROM_HERE, {BrowserThread::UI}, |
| base::Bind(pdf_print_state_->callback_, false)); |
| } |
| |
| // Reset state information. |
| pdf_print_state_.reset(); |
| } |
| |
| void CefPrintViewManager::InitializePrintPreview(int frame_tree_node_id) { |
| PrintPreviewDialogController* dialog_controller = |
| PrintPreviewDialogController::GetInstance(); |
| if (!dialog_controller) |
| return; |
| |
| dialog_controller->PrintPreview(web_contents()); |
| |
| content::WebContents* preview_contents = |
| dialog_controller->GetPrintPreviewForContents(web_contents()); |
| |
| extensions::CefExtensionWebContentsObserver::CreateForWebContents( |
| preview_contents); |
| |
| PrintPreviewHelper::CreateForWebContents(preview_contents); |
| PrintPreviewHelper::FromWebContents(preview_contents) |
| ->Initialize(frame_tree_node_id); |
| } |
| |
| WEB_CONTENTS_USER_DATA_KEY_IMPL(CefPrintViewManager) |
| |
| // CefPrintViewManager::PrintPreviewHelper |
| |
| CefPrintViewManager::PrintPreviewHelper::PrintPreviewHelper( |
| content::WebContents* contents) |
| : content::WebContentsObserver(contents) {} |
| |
| void CefPrintViewManager::PrintPreviewHelper::Initialize( |
| int parent_frame_tree_node_id) { |
| DCHECK_EQ(parent_frame_tree_node_id_, |
| content::RenderFrameHost::kNoFrameTreeNodeId); |
| DCHECK_NE(parent_frame_tree_node_id, |
| content::RenderFrameHost::kNoFrameTreeNodeId); |
| parent_frame_tree_node_id_ = parent_frame_tree_node_id; |
| |
| auto context = web_contents()->GetBrowserContext(); |
| auto manager = content::BrowserContext::GetDownloadManager(context); |
| |
| if (!context->GetDownloadManagerDelegate()) { |
| manager->SetDelegate(new CefDownloadManagerDelegate(manager)); |
| } |
| |
| auto browser_info = |
| CefBrowserInfoManager::GetInstance()->GetBrowserInfoForFrameTreeNode( |
| parent_frame_tree_node_id_); |
| DCHECK(browser_info); |
| if (!browser_info) |
| return; |
| |
| // Associate guest state information with the owner browser. |
| browser_info->MaybeCreateFrame(web_contents()->GetMainFrame(), |
| true /* is_guest_view */); |
| } |
| |
| void CefPrintViewManager::PrintPreviewHelper::WebContentsDestroyed() { |
| auto browser_info = |
| CefBrowserInfoManager::GetInstance()->GetBrowserInfoForFrameTreeNode( |
| parent_frame_tree_node_id_); |
| DCHECK(browser_info); |
| if (!browser_info) |
| return; |
| |
| // Disassociate guest state information with the owner browser. |
| browser_info->RemoveFrame(web_contents()->GetMainFrame()); |
| } |
| |
| WEB_CONTENTS_USER_DATA_KEY_IMPL(CefPrintViewManager::PrintPreviewHelper) |
| |
| } // namespace printing |