Skip to content

feat: add webFrameMain.printToPDF() - #52439

Open
MarshallOfSound wants to merge 3 commits into
mainfrom
feat/web-frame-main-print-to-pdf
Open

feat: add webFrameMain.printToPDF()#52439
MarshallOfSound wants to merge 3 commits into
mainfrom
feat/web-frame-main-print-to-pdf

Conversation

@MarshallOfSound

@MarshallOfSound MarshallOfSound commented Jul 24, 2026

Copy link
Copy Markdown
Member

Description of Change

Adds frame.printToPDF(options) to WebFrameMain, allowing the main process to print individual frames (e.g. an <iframe>) to PDF. This reuses the existing webContents.printToPDF implementation, which already operates on a single frame internally — the C++ core and JS option handling are extracted into shared helpers used by both. Print jobs within a frame tree are serialized (the same guarantee webContents.printToPDF already had, as concurrent jobs in one tree conflict in the renderer), while frames in different webContents print concurrently.

resolves #33625
resolves #22796

Checklist

Release Notes

Notes: Added webFrameMain.printToPDF() to allow printing individual frames to PDF from the main process.

@electron-cation electron-cation Bot added the new-pr 🌱 PR opened recently label Jul 24, 2026
@MarshallOfSound MarshallOfSound added target/41-x-y PR should also be added to the "41-x-y" branch. target/42-x-y PR should also be added to the "42-x-y" branch. target/43-x-y PR should also be added to the "43-x-y" branch. target/44-x-y PR should also be added to the "44-x-y" branch. semver/minor backwards-compatible functionality and removed target/41-x-y PR should also be added to the "41-x-y" branch. labels Jul 24, 2026

@jkleinsc jkleinsc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conceptually API LGTM, but we should use a structure for the print options since it is reused.

Comment thread docs/api/web-frame-main.md
})
.catch(() => {});
return next;
return printToPDF(this, options);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we simplify PrintToPDFTarget by always directing to the frame-level API?

Suggested change
return printToPDF(this, options);
return this.mainFrame.printToPDF(options);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not quite equivalent. WebContents::PrintToPDF doesn't print the primary main frame, it prints GetRenderFrameHostToUse(web_contents()) (printing_utils.cc:111) which redirects to the PDF plugin child frame when the contents is showing the PDF viewer (OOPIF or full-page plugin) and to the focused frame if it has a selection. Routing through this.mainFrame would drop that, so printToPDF() on a loaded .pdf would print the extension embedder page instead of the document. And I don't think we want that redirect inside WebFrameMain::PrintToPDF since a frame-scoped API should print the frame you asked for.

That's why both entry points funnel into printToPDF(target, options) on the JS side and PrintFrameToPDF(rfh, settings) in C++. PrintToPDFTarget is just the shim so the option translation + queueing isn't duplicated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense and I do like that both are able to reuse the JS printToPDF method.

If my understanding is correct, an <iframe> embedding a PDF would not return the actual PDF's byte data in the result of frame.printToPDF. I'm a little confused about what the behavior might be which may imply it'd be worth adding a test to confirm expectations. An explicit call out in the docs might also be worth clarifying.

Unlike webContents.printToPDF, this method prints only the contents of the frame it is called on.

If this already implies the expected behavior to what I mentioned above, then maybe that's fine too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printToPDF on an iframe with a PDF would print the PDF but in a scroll container. You'd need to do frameWithPDF.frames[0] basically to get the actual PDF iframe

@jkleinsc jkleinsc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API LGTM

@ckerr ckerr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API LGTM

@ckerr

ckerr commented Jul 24, 2026

Copy link
Copy Markdown
Member

I think the macos-arm64 CI failure here is unrelated to this PR, I think it's the same bug being addressed by #52448. If we land that first and then rebase here, it should go green.

@ckerr

ckerr commented Jul 27, 2026

Copy link
Copy Markdown
Member

rebasing to origin/main now that 52448 has landed

MarshallOfSound and others added 3 commits July 27, 2026 13:45
Allows printing an individual frame (e.g. an <iframe>) to PDF from the
main process, rather than only the full page via webContents.printToPDF.

The C++ PDF path already operates on a RenderFrameHost, so the core is
extracted to shell/browser/printing/print_to_pdf.cc and shared by both
entry points. The JS option translation and job queueing move to a
shared lib/browser/print-to-pdf.ts; jobs are serialized per frame tree
(keyed by the top frame's frameTreeNodeId, which is stable across
navigations) because concurrent print jobs within one frame tree
conflict in the renderer, while frames in different webContents still
print concurrently.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
The options table was duplicated across webContents, <webview>, and now
webFrameMain. Pull it into a PrintToPDFOptions structure and give its
margins their own PrintToPDFMargins structure so the generated typings no
longer merge them with webContents.print()'s pixel-based Margins.

No-Verification-Needed: docs-only
@ckerr
ckerr force-pushed the feat/web-frame-main-print-to-pdf branch from 68e8c8b to 1379418 Compare July 27, 2026 18:45
@jkleinsc jkleinsc added the api-review/skip-delay ⏰ Skip the mandatory 7 day waiting period for new APIs label Jul 27, 2026
@ckerr

ckerr commented Jul 28, 2026

Copy link
Copy Markdown
Member

API ReviewStarted 5 hours ago — Pending (2/2 LGTMs - ready on 2026-07-24)

🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-review/requested 🗳 api-review/skip-delay ⏰ Skip the mandatory 7 day waiting period for new APIs new-pr 🌱 PR opened recently semver/minor backwards-compatible functionality target/42-x-y PR should also be added to the "42-x-y" branch. target/43-x-y PR should also be added to the "43-x-y" branch. target/44-x-y PR should also be added to the "44-x-y" branch.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request]: printToPDF for iframe Allow intercepting contentWindow.print in Electron to print iframes at the Electron layer

4 participants