Skip to content

feat: add webFrameMain.printToPDF() - #52439

Merged
MarshallOfSound merged 3 commits into
mainfrom
feat/web-frame-main-print-to-pdf
Aug 3, 2026
Merged

MarshallOfSound merged 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

@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)

🤔

@ckerr ckerr closed this Jul 29, 2026
@ckerr ckerr reopened this Jul 29, 2026
ckerr
ckerr previously requested changes Jul 29, 2026

@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.

Needs an update to address merge conflicts

Also, not sure what's going on with the API Review bot, which thinks this should be ready on 2026-07-24

MarshallOfSound and others added 3 commits August 3, 2026 17:54
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
@MarshallOfSound
MarshallOfSound force-pushed the feat/web-frame-main-print-to-pdf branch from 1379418 to 289da56 Compare August 3, 2026 17:55
@MarshallOfSound
MarshallOfSound merged commit b65f0f2 into main Aug 3, 2026
168 of 174 checks passed
@github-actions github-actions Bot added the 45-x-y label Aug 3, 2026
@trop

trop Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

I was unable to backport this PR to "42-x-y" cleanly;
you will need to perform this backport manually.

@trop

trop Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

I was unable to backport this PR to "43-x-y" cleanly;
you will need to perform this backport manually.

@trop trop Bot added needs-manual-bp/42-x-y needs-manual-bp/43-x-y and removed 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. labels Aug 3, 2026
@trop

trop Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

I have automatically backported this PR to "44-x-y", please check out #52625

@trop trop Bot added in-flight/44-x-y and removed target/44-x-y PR should also be added to the "44-x-y" branch. labels Aug 3, 2026
@trop

trop Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@MarshallOfSound has manually backported this PR to "42-x-y", please check out #52626

@trop

trop Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@MarshallOfSound has manually backported this PR to "43-x-y", please check out #52627

MarshallOfSound added a commit that referenced this pull request Aug 3, 2026
* feat: add webFrameMain.printToPDF()

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.

* docs: add API history block for webFrameMain.printToPDF()

* docs: move printToPDF options into a shared structure

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.

---------

(cherry picked from commit b65f0f2)
@trop trop Bot added merged/44-x-y PR was merged to the "44-x-y" branch. and removed in-flight/44-x-y labels Aug 5, 2026
jkleinsc pushed a commit that referenced this pull request Aug 5, 2026
feat: add webFrameMain.printToPDF() (#52439)

* feat: add webFrameMain.printToPDF()

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.

* docs: add API history block for webFrameMain.printToPDF()

* docs: move printToPDF options into a shared structure

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.

---------

(cherry picked from commit b65f0f2)
@trop trop Bot added merged/42-x-y PR was merged to the "42-x-y" branch. and removed in-flight/42-x-y labels Aug 5, 2026
jkleinsc pushed a commit that referenced this pull request Aug 5, 2026
feat: add webFrameMain.printToPDF() (#52439)

* feat: add webFrameMain.printToPDF()

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.

* docs: add API history block for webFrameMain.printToPDF()

* docs: move printToPDF options into a shared structure

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.

---------

(cherry picked from commit b65f0f2)
@trop trop Bot added merged/43-x-y PR was merged to the "43-x-y" branch. and removed in-flight/43-x-y labels Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

45-x-y api-review/approved ✅ api-review/skip-delay ⏰ Skip the mandatory 7 day waiting period for new APIs merged/42-x-y PR was merged to the "42-x-y" branch. merged/43-x-y PR was merged to the "43-x-y" branch. merged/44-x-y PR was merged to the "44-x-y" branch. semver/minor backwards-compatible functionality

Projects

Status: No status

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