Repository: electron/electron
Pull request: fix: ensure correct `WebContents` when checking `PDFReadyToPrint` (31-x-y)

Backport of #43943

See that PR for details.


Notes: none.

Changed files:
filename: shell/browser/electron_pdf_document_helper_client.cc
patch: @@ -27,13 +27,23 @@ void ElectronPDFDocumentHelperClient::UpdateContentRestrictions(
   // allowed there won't be a printing restriction passed, so we can use this
   // second call to notify that the pdf document is ready to print.
   if (!(content_restrictions & chrome_pdf::kContentRestrictionPrint)) {
-    content::WebContents* web_contents =
-        content::WebContents::FromRenderFrameHost(render_frame_host);
-    electron::api::WebContents* api_web_contents =
-        electron::api::WebContents::From(
-            web_contents->GetOutermostWebContents());
-    if (api_web_contents) {
-      api_web_contents->PDFReadyToPrint();
+    // It's a WebView - emit the event on the WebView webContents.
+    auto* guest_view = extensions::MimeHandlerViewGuest::FromRenderFrameHost(
+        render_frame_host);
+    if (guest_view) {
+      auto* gv_api_wc =
+          electron::api::WebContents::From(guest_view->embedder_web_contents());
+      if (gv_api_wc)
+        gv_api_wc->PDFReadyToPrint();
+      return;
+    }
+
+    auto* wc = content::WebContents::FromRenderFrameHost(render_frame_host);
+    if (wc) {
+      auto* api_wc =
+          electron::api::WebContents::From(wc->GetOuterWebContents());
+      if (api_wc)
+        api_wc->PDFReadyToPrint();
     }
   }
 }

filename: spec/api-web-contents-spec.ts
patch: @@ -3,6 +3,7 @@ import { AddressInfo } from 'node:net';
 import * as path from 'node:path';
 import * as fs from 'node:fs';
 import * as http from 'node:http';
+import * as url from 'node:url';
 import { BrowserWindow, ipcMain, webContents, session, app, BrowserView, WebContents } from 'electron/main';
 import { closeAllWindows } from './lib/window-helpers';
 import { ifdescribe, defer, waitUntil, listen, ifit } from './lib/spec-helpers';
@@ -11,7 +12,6 @@ import { setTimeout } from 'node:timers/promises';
 
 const pdfjs = require('pdfjs-dist');
 const fixturesPath = path.resolve(__dirname, 'fixtures');
-const mainFixturesPath = path.resolve(__dirname, 'fixtures');
 const features = process._linkedBinding('electron_common_features');
 
 describe('webContents module', () => {
@@ -1055,7 +1055,7 @@ describe('webContents module', () => {
       }).to.throw('\'icon\' parameter is required');
 
       expect(() => {
-        w.webContents.startDrag({ file: __filename, icon: path.join(mainFixturesPath, 'blank.png') });
+        w.webContents.startDrag({ file: __filename, icon: path.join(fixturesPath, 'blank.png') });
       }).to.throw(/Failed to load image from path (.+)/);
     });
   });
@@ -2353,6 +2353,45 @@ describe('webContents module', () => {
       const { items } = await page.getTextContent();
       expect(containsText(items, /Cat: The Ideal Pet/)).to.be.true();
     });
+
+    it('from an existing pdf document in a WebView', async () => {
+      const win = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          webviewTag: true
+        }
+      });
+
+      await win.loadURL('about:blank');
+      const webContentsCreated = once(app, 'web-contents-created') as Promise<[any, WebContents]>;
+
+      const src = url.format({
+        pathname: `${fixturesPath.replaceAll('\\', '/')}/cat.pdf`,
+        protocol: 'file',
+        slashes: true
+      });
+      await win.webContents.executeJavaScript(`
+        new Promise((resolve, reject) => {
+          const webview = new WebView()
+          webview.setAttribute('src', '${src}')
+          document.body.appendChild(webview)
+          webview.addEventListener('did-finish-load', () => {
+            resolve()
+          })
+        })
+      `);
+
+      const [, webContents] = await webContentsCreated;
+
+      await once(webContents, '-pdf-ready-to-print');
+
+      const data = await 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', () => {