Browse Source

fix: NativeImage serialization of <webview>.capturePage() result (#20825) (#21105)

Milan Burda 5 years ago
parent
commit
988c57369a

+ 1 - 1
filenames.gni

@@ -56,12 +56,12 @@ filenames = {
     "lib/common/api/shell.js",
     "lib/common/atom-binding-setup.ts",
     "lib/common/buffer-utils.js",
-    "lib/common/clipboard-utils.js",
     "lib/common/crash-reporter.js",
     "lib/common/error-utils.js",
     "lib/common/init.ts",
     "lib/common/parse-features-string.js",
     "lib/common/reset-search-paths.ts",
+    "lib/common/type-utils.js",
     "lib/common/web-view-methods.js",
     "lib/renderer/callbacks-registry.js",
     "lib/renderer/chrome-api.ts",

+ 7 - 0
lib/browser/guest-view-manager.js

@@ -4,6 +4,7 @@ const { webContents } = require('electron')
 const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
 const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
 const parseFeaturesString = require('@electron/internal/common/parse-features-string')
+const { serialize } = require('@electron/internal/common/type-utils')
 const {
   syncMethods,
   asyncCallbackMethods,
@@ -386,6 +387,12 @@ handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstance
   return guest[method](...args)
 })
 
+handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', async function (event, guestInstanceId, args) {
+  const guest = getGuestForWebContents(guestInstanceId, event.sender)
+
+  return serialize(await guest.capturePage(...args))
+})
+
 // Returns WebContents from its guest id hosted in given webContents.
 const getGuestForWebContents = function (guestInstanceId, contents) {
   const guest = getGuest(guestInstanceId)

+ 2 - 2
lib/browser/rpc-server.js

@@ -18,7 +18,7 @@ const objectsRegistry = require('@electron/internal/browser/objects-registry')
 const guestViewManager = require('@electron/internal/browser/guest-view-manager')
 const bufferUtils = require('@electron/internal/common/buffer-utils')
 const errorUtils = require('@electron/internal/common/error-utils')
-const clipboardUtils = require('@electron/internal/common/clipboard-utils')
+const typeUtils = require('@electron/internal/common/type-utils')
 
 const hasProp = {}.hasOwnProperty
 
@@ -493,7 +493,7 @@ ipcMainUtils.handle('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...ar
     throw new Error(`Invalid method: ${method}`)
   }
 
-  return clipboardUtils.serialize(electron.clipboard[method](...clipboardUtils.deserialize(args)))
+  return typeUtils.serialize(electron.clipboard[method](...typeUtils.deserialize(args)))
 })
 
 const readFile = util.promisify(fs.readFile)

+ 3 - 3
lib/common/api/clipboard.js

@@ -4,13 +4,13 @@ const clipboard = process.electronBinding('clipboard')
 
 if (process.type === 'renderer') {
   const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')
-  const clipboardUtils = require('@electron/internal/common/clipboard-utils')
+  const typeUtils = require('@electron/internal/common/type-utils')
 
   const makeRemoteMethod = function (method) {
     return (...args) => {
-      args = clipboardUtils.serialize(args)
+      args = typeUtils.serialize(args)
       const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args)
-      return clipboardUtils.deserialize(result)
+      return typeUtils.deserialize(result)
     }
   }
 

+ 0 - 0
lib/common/clipboard-utils.js → lib/common/type-utils.js


+ 0 - 1
lib/common/web-view-methods.js

@@ -64,7 +64,6 @@ exports.asyncCallbackMethods = new Set([
 
 exports.asyncPromiseMethods = new Set([
   'loadURL',
-  'capturePage',
   'executeJavaScript',
   'printToPDF'
 ])

+ 9 - 0
lib/renderer/web-view/web-view-impl.ts

@@ -3,6 +3,7 @@ import { deprecate, remote, webFrame } from 'electron'
 import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
 import * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal'
 import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'
+import { deserialize } from '@electron/internal/common/type-utils'
 import {
   syncMethods,
   asyncCallbackMethods,
@@ -275,6 +276,14 @@ export const setupMethods = (WebViewElement: typeof ElectronInternal.WebViewElem
   for (const method of asyncPromiseMethods) {
     (WebViewElement.prototype as Record<string, any>)[method] = deprecate.promisify(createPromiseHandler(method))
   }
+
+  const createCapturePageHandler = function () {
+    return function (this: ElectronInternal.WebViewElement, ...args: Array<any>) {
+      return ipcRendererUtils.invoke('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', this.getWebContentsId(), args).then(image => deserialize(image))
+    }
+  }
+
+  WebViewElement.prototype.capturePage = deprecate.promisify(createCapturePageHandler())
 }
 
 export const webViewImplModule = {

+ 31 - 0
spec/webview-spec.js

@@ -1281,6 +1281,37 @@ describe('<webview> tag', function () {
     })
   })
 
+  describe('<webview>.capturePage()', () => {
+    it('returns a Promise with a NativeImage', async () => {
+      const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'
+      await loadWebView(webview, { src })
+
+      const image = await webview.capturePage()
+      const imgBuffer = image.toPNG()
+
+      // Check the 25th byte in the PNG.
+      // Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha
+      expect(imgBuffer[25]).to.equal(6)
+    })
+
+    // TODO(miniak): remove when promisification is complete
+    it('invokes callback with a NativeImage', (done) => {
+      webview.addEventListener('did-finish-load', () => {
+        webview.capturePage(function (image) {
+          const imgBuffer = image.toPNG()
+
+          // Check the 25th byte in the PNG.
+          // Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha
+          expect(imgBuffer[25]).to.equal(6)
+          done()
+        })
+      })
+
+      webview.src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'
+      document.body.appendChild(webview)
+    })
+  })
+
   describe('<webview>.printToPDF()', () => {
     before(function () {
       if (!features.isPrintingEnabled()) {

+ 1 - 0
typings/internal-electron.d.ts

@@ -124,6 +124,7 @@ declare namespace ElectronInternal {
     // Created in web-view-impl
     public getWebContents(): Electron.WebContents;
     public getWebContentsId(): number;
+    public capturePage(rect?: Electron.Rectangle): Promise<Electron.NativeImage>;
   }
 }