From e8ea007104b81accb588f27b81cc15345eaf954b Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Thu, 28 May 2020 09:43:15 -0700 Subject: [PATCH] fix: ensure nativeImage serialization main->renderer (#23759) --- lib/browser/remote/server.ts | 10 +++++++++- lib/renderer/api/remote.js | 3 ++- spec-main/api-remote-spec.ts | 36 ++++++++++++++++++++++++++++++------ 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/lib/browser/remote/server.ts b/lib/browser/remote/server.ts index 7e27c26824..2ed49817a8 100644 --- a/lib/browser/remote/server.ts +++ b/lib/browser/remote/server.ts @@ -4,12 +4,13 @@ import * as electron from 'electron'; import { EventEmitter } from 'events'; import objectsRegistry from './objects-registry'; import { ipcMainInternal } from '../ipc-main-internal'; -import { isPromise, isSerializableObject, deserialize } from '@electron/internal/common/type-utils'; +import { isPromise, isSerializableObject, deserialize, serialize } from '@electron/internal/common/type-utils'; import { Size } from 'electron/main'; const v8Util = process.electronBinding('v8_util'); const eventBinding = process.electronBinding('event'); const features = process.electronBinding('features'); +const { NativeImage } = process.electronBinding('native_image'); if (!features.isRemoteModuleEnabled()) { throw new Error('remote module is disabled'); @@ -114,6 +115,9 @@ type MetaType = { } | { type: 'promise', then: MetaType +} | { + type: 'nativeimage' + value: electron.NativeImage } // Convert a real value into meta data. @@ -124,6 +128,8 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v // Recognize certain types of objects. if (value instanceof Buffer) { type = 'buffer'; + } else if (value instanceof NativeImage) { + type = 'nativeimage'; } else if (Array.isArray(value)) { type = 'array'; } else if (value instanceof Error) { @@ -147,6 +153,8 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v type, members: value.map((el: any) => valueToMeta(sender, contextId, el, optimizeSimpleObject)) }; + } else if (type === 'nativeimage') { + return { type, value: serialize(value) }; } else if (type === 'object' || type === 'function') { return { type, diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index 504597bf58..9773a266ef 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -5,7 +5,7 @@ const { hasSwitch } = process.electronBinding('command_line'); const { NativeImage } = process.electronBinding('native_image'); const { CallbacksRegistry } = require('@electron/internal/renderer/remote/callbacks-registry'); -const { isPromise, isSerializableObject, serialize } = require('@electron/internal/common/type-utils'); +const { isPromise, isSerializableObject, serialize, deserialize } = require('@electron/internal/common/type-utils'); const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); const callbacksRegistry = new CallbacksRegistry(); @@ -219,6 +219,7 @@ function metaToValue (meta) { const types = { value: () => meta.value, array: () => meta.members.map((member) => metaToValue(member)), + nativeimage: () => deserialize(meta.value), buffer: () => Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength), promise: () => Promise.resolve({ then: metaToValue(meta.then) }), error: () => metaToError(meta), diff --git a/spec-main/api-remote-spec.ts b/spec-main/api-remote-spec.ts index 5e8480ff76..71a59c2e3c 100644 --- a/spec-main/api-remote-spec.ts +++ b/spec-main/api-remote-spec.ts @@ -366,11 +366,11 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => { const w = makeWindow(); const remotely = makeRemotely(w); - it('can serialize an empty nativeImage', async () => { - const getEmptyImage = (img: NativeImage) => img.isEmpty(); + it('can serialize an empty nativeImage from renderer to main', async () => { + const getImageEmpty = (img: NativeImage) => img.isEmpty(); w().webContents.once('remote-get-global', (event) => { - event.returnValue = getEmptyImage; + event.returnValue = getImageEmpty; }); await expect(remotely(() => { @@ -379,11 +379,23 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => { })).to.eventually.be.true(); }); - it('can serialize a non-empty nativeImage', async () => { - const getNonEmptyImage = (img: NativeImage) => img.getSize(); + it('can serialize an empty nativeImage from main to renderer', async () => { + w().webContents.once('remote-get-global', (event) => { + const emptyImage = require('electron').nativeImage.createEmpty(); + event.returnValue = emptyImage; + }); + + await expect(remotely(() => { + const image = require('electron').remote.getGlobal('someFunction'); + return image.isEmpty(); + })).to.eventually.be.true(); + }); + + it('can serialize a non-empty nativeImage from renderer to main', async () => { + const getImageSize = (img: NativeImage) => img.getSize(); w().webContents.once('remote-get-global', (event) => { - event.returnValue = getNonEmptyImage; + event.returnValue = getImageSize; }); await expect(remotely(() => { @@ -393,6 +405,18 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => { })).to.eventually.deep.equal({ width: 2, height: 2 }); }); + it('can serialize a non-empty nativeImage from main to renderer', async () => { + w().webContents.once('remote-get-global', (event) => { + const nonEmptyImage = nativeImage.createFromDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVQYlWP8//8/AwMDEwMDAwMDAwAkBgMBBMzldwAAAABJRU5ErkJggg=='); + event.returnValue = nonEmptyImage; + }); + + await expect(remotely(() => { + const image = require('electron').remote.getGlobal('someFunction'); + return image.getSize(); + })).to.eventually.deep.equal({ width: 2, height: 2 }); + }); + it('can properly create a menu with an nativeImage icon in the renderer', async () => { await expect(remotely(() => { const { remote, nativeImage } = require('electron');