fix: blank page when printing pdf (#43309)
This commit is contained in:
parent
f6bae51025
commit
286384258b
4 changed files with 92 additions and 17 deletions
|
@ -2899,11 +2899,7 @@ void WebContents::OnGetDeviceNameToUse(
|
||||||
if (!print_view_manager)
|
if (!print_view_manager)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto* focused_frame = web_contents()->GetFocusedFrame();
|
content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents());
|
||||||
auto* rfh = focused_frame && focused_frame->HasSelection()
|
|
||||||
? focused_frame
|
|
||||||
: web_contents()->GetPrimaryMainFrame();
|
|
||||||
|
|
||||||
print_view_manager->PrintNow(rfh, std::move(print_settings),
|
print_view_manager->PrintNow(rfh, std::move(print_settings),
|
||||||
std::move(print_callback));
|
std::move(print_callback));
|
||||||
}
|
}
|
||||||
|
@ -3100,12 +3096,13 @@ v8::Local<v8::Promise> WebContents::PrintToPDF(const base::Value& settings) {
|
||||||
auto generate_document_outline =
|
auto generate_document_outline =
|
||||||
settings.GetDict().FindBool("generateDocumentOutline");
|
settings.GetDict().FindBool("generateDocumentOutline");
|
||||||
|
|
||||||
|
content::RenderFrameHost* rfh = GetRenderFrameHostToUse(web_contents());
|
||||||
absl::variant<printing::mojom::PrintPagesParamsPtr, std::string>
|
absl::variant<printing::mojom::PrintPagesParamsPtr, std::string>
|
||||||
print_pages_params = print_to_pdf::GetPrintPagesParams(
|
print_pages_params = print_to_pdf::GetPrintPagesParams(
|
||||||
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
|
rfh->GetLastCommittedURL(), landscape, display_header_footer,
|
||||||
landscape, display_header_footer, print_background, scale,
|
print_background, scale, paper_width, paper_height, margin_top,
|
||||||
paper_width, paper_height, margin_top, margin_bottom, margin_left,
|
margin_bottom, margin_left, margin_right,
|
||||||
margin_right, std::make_optional(header_template),
|
std::make_optional(header_template),
|
||||||
std::make_optional(footer_template), prefer_css_page_size,
|
std::make_optional(footer_template), prefer_css_page_size,
|
||||||
generate_tagged_pdf, generate_document_outline);
|
generate_tagged_pdf, generate_document_outline);
|
||||||
|
|
||||||
|
@ -3125,8 +3122,7 @@ v8::Local<v8::Promise> WebContents::PrintToPDF(const base::Value& settings) {
|
||||||
absl::get<printing::mojom::PrintPagesParamsPtr>(print_pages_params));
|
absl::get<printing::mojom::PrintPagesParamsPtr>(print_pages_params));
|
||||||
params->params->document_cookie = unique_id.value_or(0);
|
params->params->document_cookie = unique_id.value_or(0);
|
||||||
|
|
||||||
manager->PrintToPdf(web_contents()->GetPrimaryMainFrame(), page_ranges,
|
manager->PrintToPdf(rfh, page_ranges, std::move(params),
|
||||||
std::move(params),
|
|
||||||
base::BindOnce(&WebContents::OnPDFCreated, GetWeakPtr(),
|
base::BindOnce(&WebContents::OnPDFCreated, GetWeakPtr(),
|
||||||
std::move(promise)));
|
std::move(promise)));
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,20 @@
|
||||||
#include "base/task/task_traits.h"
|
#include "base/task/task_traits.h"
|
||||||
#include "base/task/thread_pool.h"
|
#include "base/task/thread_pool.h"
|
||||||
#include "chrome/browser/browser_process.h"
|
#include "chrome/browser/browser_process.h"
|
||||||
|
#include "components/pdf/browser/pdf_frame_util.h"
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
|
#include "content/public/browser/render_frame_host.h"
|
||||||
|
#include "content/public/browser/web_contents.h"
|
||||||
#include "electron/buildflags/buildflags.h"
|
#include "electron/buildflags/buildflags.h"
|
||||||
|
#include "pdf/pdf_features.h"
|
||||||
#include "printing/backend/print_backend.h" // nogncheck
|
#include "printing/backend/print_backend.h" // nogncheck
|
||||||
#include "printing/units.h"
|
#include "printing/units.h"
|
||||||
#include "shell/common/thread_restrictions.h"
|
#include "shell/common/thread_restrictions.h"
|
||||||
|
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if BUILDFLAG(IS_MAC)
|
#if BUILDFLAG(IS_MAC)
|
||||||
#include <ApplicationServices/ApplicationServices.h>
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -68,6 +76,49 @@ bool IsDeviceNameValid(const std::u16string& device_name) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Duplicated from chrome/browser/printing/print_view_manager_common.cc
|
||||||
|
content::RenderFrameHost* GetFullPagePlugin(content::WebContents* contents) {
|
||||||
|
content::RenderFrameHost* full_page_plugin = nullptr;
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
contents->ForEachRenderFrameHostWithAction(
|
||||||
|
[&full_page_plugin](content::RenderFrameHost* rfh) {
|
||||||
|
auto* guest_view =
|
||||||
|
extensions::MimeHandlerViewGuest::FromRenderFrameHost(rfh);
|
||||||
|
if (guest_view && guest_view->is_full_page_plugin()) {
|
||||||
|
DCHECK_EQ(guest_view->GetGuestMainFrame(), rfh);
|
||||||
|
full_page_plugin = rfh;
|
||||||
|
return content::RenderFrameHost::FrameIterationAction::kStop;
|
||||||
|
}
|
||||||
|
return content::RenderFrameHost::FrameIterationAction::kContinue;
|
||||||
|
});
|
||||||
|
#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
return full_page_plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick the right RenderFrameHost based on the WebContents.
|
||||||
|
// Modified from chrome/browser/printing/print_view_manager_common.cc
|
||||||
|
content::RenderFrameHost* GetRenderFrameHostToUse(
|
||||||
|
content::WebContents* contents) {
|
||||||
|
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||||
|
// Pick the plugin frame host if `contents` is a PDF viewer guest. If using
|
||||||
|
// OOPIF PDF viewer, pick the PDF extension frame host.
|
||||||
|
content::RenderFrameHost* full_page_pdf_embedder_host =
|
||||||
|
chrome_pdf::features::IsOopifPdfEnabled()
|
||||||
|
? pdf_frame_util::FindFullPagePdfExtensionHost(contents)
|
||||||
|
: GetFullPagePlugin(contents);
|
||||||
|
content::RenderFrameHost* pdf_rfh = pdf_frame_util::FindPdfChildFrame(
|
||||||
|
full_page_pdf_embedder_host ? full_page_pdf_embedder_host
|
||||||
|
: contents->GetPrimaryMainFrame());
|
||||||
|
if (pdf_rfh) {
|
||||||
|
return pdf_rfh;
|
||||||
|
}
|
||||||
|
#endif // BUILDFLAG(ENABLE_PDF)
|
||||||
|
auto* focused_frame = contents->GetFocusedFrame();
|
||||||
|
return (focused_frame && focused_frame->HasSelection())
|
||||||
|
? focused_frame
|
||||||
|
: contents->GetPrimaryMainFrame();
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
||||||
const std::u16string& device_name) {
|
const std::u16string& device_name) {
|
||||||
#if BUILDFLAG(IS_WIN)
|
#if BUILDFLAG(IS_WIN)
|
||||||
|
|
|
@ -14,6 +14,11 @@ namespace gfx {
|
||||||
class Size;
|
class Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace content {
|
||||||
|
class RenderFrameHost;
|
||||||
|
class WebContents;
|
||||||
|
} // namespace content
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
// This function returns the per-platform default printer's DPI.
|
// This function returns the per-platform default printer's DPI.
|
||||||
|
@ -24,6 +29,9 @@ gfx::Size GetDefaultPrinterDPI(const std::u16string& device_name);
|
||||||
// sanity checking of device_name validity and so will crash on invalid names.
|
// sanity checking of device_name validity and so will crash on invalid names.
|
||||||
bool IsDeviceNameValid(const std::u16string& device_name);
|
bool IsDeviceNameValid(const std::u16string& device_name);
|
||||||
|
|
||||||
|
content::RenderFrameHost* GetRenderFrameHostToUse(
|
||||||
|
content::WebContents* contents);
|
||||||
|
|
||||||
// This function returns a validated device name.
|
// This function returns a validated device name.
|
||||||
// If the user passed one to webContents.print(), we check that it's valid and
|
// If the user passed one to webContents.print(), we check that it's valid and
|
||||||
// return it or fail if the network doesn't recognize it. If the user didn't
|
// return it or fail if the network doesn't recognize it. If the user didn't
|
||||||
|
|
|
@ -2204,6 +2204,10 @@ describe('webContents module', () => {
|
||||||
ifdescribe(features.isPrintingEnabled())('printToPDF()', () => {
|
ifdescribe(features.isPrintingEnabled())('printToPDF()', () => {
|
||||||
let w: BrowserWindow;
|
let w: BrowserWindow;
|
||||||
|
|
||||||
|
const containsText = (items: any[], text: RegExp) => {
|
||||||
|
return items.some(({ str }: { str: string }) => str.match(text));
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
|
@ -2326,7 +2330,7 @@ describe('webContents module', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with custom header and footer', async () => {
|
it('with custom header and footer', async () => {
|
||||||
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'print-to-pdf-small.html'));
|
await w.loadFile(path.join(fixturesPath, 'api', 'print-to-pdf-small.html'));
|
||||||
|
|
||||||
const data = await w.webContents.printToPDF({
|
const data = await w.webContents.printToPDF({
|
||||||
displayHeaderFooter: true,
|
displayHeaderFooter: true,
|
||||||
|
@ -2339,11 +2343,8 @@ describe('webContents module', () => {
|
||||||
|
|
||||||
const { items } = await page.getTextContent();
|
const { items } = await page.getTextContent();
|
||||||
|
|
||||||
// Check that generated PDF contains a header.
|
expect(containsText(items, /I'm a PDF header/)).to.be.true();
|
||||||
const containsText = (text: RegExp) => items.some(({ str }: { str: string }) => str.match(text));
|
expect(containsText(items, /I'm a PDF footer/)).to.be.true();
|
||||||
|
|
||||||
expect(containsText(/I'm a PDF header/)).to.be.true();
|
|
||||||
expect(containsText(/I'm a PDF footer/)).to.be.true();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('in landscape mode', async () => {
|
it('in landscape mode', async () => {
|
||||||
|
@ -2395,6 +2396,25 @@ describe('webContents module', () => {
|
||||||
Suspects: false
|
Suspects: false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('from an existing pdf document', async () => {
|
||||||
|
const pdfPath = path.join(fixturesPath, 'cat.pdf');
|
||||||
|
await w.loadFile(pdfPath);
|
||||||
|
|
||||||
|
// TODO(codebytere): the PDF plugin is not always ready immediately
|
||||||
|
// after the document is loaded, so we need to wait for it to be ready.
|
||||||
|
// We should find a better way to do this.
|
||||||
|
await setTimeout(3000);
|
||||||
|
|
||||||
|
const data = await w.webContents.printToPDF({});
|
||||||
|
const doc = await pdfjs.getDocument(data).promise;
|
||||||
|
expect(doc.numPages).to.equal(2);
|
||||||
|
|
||||||
|
const page = await doc.getPage(1);
|
||||||
|
|
||||||
|
const { items } = await page.getTextContent();
|
||||||
|
expect(containsText(items, /Cat: The Ideal Pet/)).to.be.true();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PictureInPicture video', () => {
|
describe('PictureInPicture video', () => {
|
||||||
|
|
Loading…
Reference in a new issue