fix: ensure nativeImage serialization main->renderer (#23759)

This commit is contained in:
Shelley Vohr 2020-05-28 09:43:15 -07:00 committed by GitHub
parent e5da2caa34
commit e8ea007104
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 8 deletions

View file

@ -4,12 +4,13 @@ import * as electron from 'electron';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import objectsRegistry from './objects-registry'; import objectsRegistry from './objects-registry';
import { ipcMainInternal } from '../ipc-main-internal'; 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'; import { Size } from 'electron/main';
const v8Util = process.electronBinding('v8_util'); const v8Util = process.electronBinding('v8_util');
const eventBinding = process.electronBinding('event'); const eventBinding = process.electronBinding('event');
const features = process.electronBinding('features'); const features = process.electronBinding('features');
const { NativeImage } = process.electronBinding('native_image');
if (!features.isRemoteModuleEnabled()) { if (!features.isRemoteModuleEnabled()) {
throw new Error('remote module is disabled'); throw new Error('remote module is disabled');
@ -114,6 +115,9 @@ type MetaType = {
} | { } | {
type: 'promise', type: 'promise',
then: MetaType then: MetaType
} | {
type: 'nativeimage'
value: electron.NativeImage
} }
// Convert a real value into meta data. // 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. // Recognize certain types of objects.
if (value instanceof Buffer) { if (value instanceof Buffer) {
type = 'buffer'; type = 'buffer';
} else if (value instanceof NativeImage) {
type = 'nativeimage';
} else if (Array.isArray(value)) { } else if (Array.isArray(value)) {
type = 'array'; type = 'array';
} else if (value instanceof Error) { } else if (value instanceof Error) {
@ -147,6 +153,8 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v
type, type,
members: value.map((el: any) => valueToMeta(sender, contextId, el, optimizeSimpleObject)) 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') { } else if (type === 'object' || type === 'function') {
return { return {
type, type,

View file

@ -5,7 +5,7 @@ const { hasSwitch } = process.electronBinding('command_line');
const { NativeImage } = process.electronBinding('native_image'); const { NativeImage } = process.electronBinding('native_image');
const { CallbacksRegistry } = require('@electron/internal/renderer/remote/callbacks-registry'); 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 { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal');
const callbacksRegistry = new CallbacksRegistry(); const callbacksRegistry = new CallbacksRegistry();
@ -219,6 +219,7 @@ function metaToValue (meta) {
const types = { const types = {
value: () => meta.value, value: () => meta.value,
array: () => meta.members.map((member) => metaToValue(member)), array: () => meta.members.map((member) => metaToValue(member)),
nativeimage: () => deserialize(meta.value),
buffer: () => Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength), buffer: () => Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength),
promise: () => Promise.resolve({ then: metaToValue(meta.then) }), promise: () => Promise.resolve({ then: metaToValue(meta.then) }),
error: () => metaToError(meta), error: () => metaToError(meta),

View file

@ -366,11 +366,11 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
const w = makeWindow(); const w = makeWindow();
const remotely = makeRemotely(w); const remotely = makeRemotely(w);
it('can serialize an empty nativeImage', async () => { it('can serialize an empty nativeImage from renderer to main', async () => {
const getEmptyImage = (img: NativeImage) => img.isEmpty(); const getImageEmpty = (img: NativeImage) => img.isEmpty();
w().webContents.once('remote-get-global', (event) => { w().webContents.once('remote-get-global', (event) => {
event.returnValue = getEmptyImage; event.returnValue = getImageEmpty;
}); });
await expect(remotely(() => { await expect(remotely(() => {
@ -379,11 +379,23 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
})).to.eventually.be.true(); })).to.eventually.be.true();
}); });
it('can serialize a non-empty nativeImage', async () => { it('can serialize an empty nativeImage from main to renderer', async () => {
const getNonEmptyImage = (img: NativeImage) => img.getSize(); 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) => { w().webContents.once('remote-get-global', (event) => {
event.returnValue = getNonEmptyImage; event.returnValue = getImageSize;
}); });
await expect(remotely(() => { await expect(remotely(() => {
@ -393,6 +405,18 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
})).to.eventually.deep.equal({ width: 2, height: 2 }); })).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 () => { it('can properly create a menu with an nativeImage icon in the renderer', async () => {
await expect(remotely(() => { await expect(remotely(() => {
const { remote, nativeImage } = require('electron'); const { remote, nativeImage } = require('electron');