refactor: use typeutils for nativeImage serialization (#23693)

This commit is contained in:
Shelley Vohr 2020-05-22 08:56:57 -07:00 committed by GitHub
parent 75847a0c5b
commit 762f7bcca2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 43 deletions

View file

@ -4,7 +4,7 @@ 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 } from '@electron/internal/common/type-utils'; import { isPromise, isSerializableObject, deserialize } 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');
@ -234,7 +234,10 @@ type MetaTypeFromRenderer = {
} | { } | {
type: 'object', type: 'object',
name: string, name: string,
members: { name: string, value: MetaTypeFromRenderer }[] members: {
name: string,
value: MetaTypeFromRenderer
}[]
} | { } | {
type: 'function-with-return-value', type: 'function-with-return-value',
value: MetaTypeFromRenderer value: MetaTypeFromRenderer
@ -245,7 +248,12 @@ type MetaTypeFromRenderer = {
length: number length: number
} | { } | {
type: 'nativeimage', type: 'nativeimage',
value: { size: Size, buffer: Buffer, scaleFactor: number, dataURL: string }[] value: {
size: Size,
buffer: Buffer,
scaleFactor: number,
dataURL: string
}[]
} }
const fakeConstructor = (constructor: Function, name: string) => const fakeConstructor = (constructor: Function, name: string) =>
@ -263,15 +271,8 @@ const fakeConstructor = (constructor: Function, name: string) =>
const unwrapArgs = function (sender: electron.WebContents, frameId: number, contextId: string, args: any[]) { const unwrapArgs = function (sender: electron.WebContents, frameId: number, contextId: string, args: any[]) {
const metaToValue = function (meta: MetaTypeFromRenderer): any { const metaToValue = function (meta: MetaTypeFromRenderer): any {
switch (meta.type) { switch (meta.type) {
case 'nativeimage': { case 'nativeimage':
const image = electron.nativeImage.createEmpty(); return deserialize(meta.value);
for (const rep of meta.value) {
const { size, scaleFactor, dataURL } = rep;
const { width, height } = size;
image.addRepresentation({ dataURL, scaleFactor, width, height });
}
return image;
}
case 'value': case 'value':
return meta.value; return meta.value;
case 'remote-object': case 'remote-object':

View file

@ -20,10 +20,10 @@ const serializableTypes = [
Date, Date,
Error, Error,
RegExp, RegExp,
ArrayBuffer, ArrayBuffer
NativeImage
]; ];
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#Supported_types
export function isSerializableObject (value: any) { export function isSerializableObject (value: any) {
return value === null || ArrayBuffer.isView(value) || serializableTypes.some(type => value instanceof type); return value === null || ArrayBuffer.isView(value) || serializableTypes.some(type => value instanceof type);
} }
@ -34,18 +34,55 @@ const objectMap = function (source: Object, mapper: (value: any) => any) {
return Object.fromEntries(targetEntries); return Object.fromEntries(targetEntries);
}; };
export function serialize (value: any): any { function serializeNativeImage (image: any) {
if (value instanceof NativeImage) { const representations = [];
const representations = []; const scaleFactors = image.getScaleFactors();
for (const scaleFactor of value.getScaleFactors()) {
const size = value.getSize(scaleFactor); // Use Buffer when there's only one representation for better perf.
const dataURL = value.toDataURL({ scaleFactor }); // This avoids compressing to/from PNG where it's not necessary to
// ensure uniqueness of dataURLs (since there's only one).
if (scaleFactors.length === 1) {
const scaleFactor = scaleFactors[0];
const size = image.getSize(scaleFactor);
const buffer = image.toBitmap({ scaleFactor });
representations.push({ scaleFactor, size, buffer });
} else {
// Construct from dataURLs to ensure that they are not lost in creation.
for (const scaleFactor of scaleFactors) {
const size = image.getSize(scaleFactor);
const dataURL = image.toDataURL({ scaleFactor });
representations.push({ scaleFactor, size, dataURL }); representations.push({ scaleFactor, size, dataURL });
} }
return { __ELECTRON_SERIALIZED_NativeImage__: true, representations }; }
} else if (value instanceof Buffer) { return { __ELECTRON_SERIALIZED_NativeImage__: true, representations };
return { __ELECTRON_SERIALIZED_Buffer__: true, data: value }; }
} else if (Array.isArray(value)) {
function deserializeNativeImage (value: any) {
const image = nativeImage.createEmpty();
// Use Buffer when there's only one representation for better perf.
// This avoids compressing to/from PNG where it's not necessary to
// ensure uniqueness of dataURLs (since there's only one).
if (value.representations.length === 1) {
const { buffer, size, scaleFactor } = value.representations[0];
const { width, height } = size;
image.addRepresentation({ buffer, scaleFactor, width, height });
} else {
// Construct from dataURLs to ensure that they are not lost in creation.
for (const rep of value.representations) {
const { dataURL, size, scaleFactor } = rep;
const { width, height } = size;
image.addRepresentation({ dataURL, scaleFactor, width, height });
}
}
return image;
}
export function serialize (value: any): any {
if (value instanceof NativeImage) {
return serializeNativeImage(value);
} if (Array.isArray(value)) {
return value.map(serialize); return value.map(serialize);
} else if (isSerializableObject(value)) { } else if (isSerializableObject(value)) {
return value; return value;
@ -58,16 +95,7 @@ export function serialize (value: any): any {
export function deserialize (value: any): any { export function deserialize (value: any): any {
if (value && value.__ELECTRON_SERIALIZED_NativeImage__) { if (value && value.__ELECTRON_SERIALIZED_NativeImage__) {
const image = nativeImage.createEmpty(); return deserializeNativeImage(value);
for (const rep of value.representations) {
const { size, scaleFactor, dataURL } = rep;
const { width, height } = size;
image.addRepresentation({ dataURL, scaleFactor, width, height });
}
return image;
} else if (value && value.__ELECTRON_SERIALIZED_Buffer__) {
const { buffer, byteOffset, byteLength } = value.data;
return Buffer.from(buffer, byteOffset, byteLength);
} else if (Array.isArray(value)) { } else if (Array.isArray(value)) {
return value.map(deserialize); return value.map(deserialize);
} else if (isSerializableObject(value)) { } else if (isSerializableObject(value)) {

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 } = require('@electron/internal/common/type-utils'); const { isPromise, isSerializableObject, serialize } = 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();
@ -37,14 +37,7 @@ function wrapArgs (args, visited = new Set()) {
} }
if (value instanceof NativeImage) { if (value instanceof NativeImage) {
const images = []; return { type: 'nativeimage', value: serialize(value) };
for (const scaleFactor of value.getScaleFactors()) {
const size = value.getSize(scaleFactor);
const buffer = value.toBitmap({ scaleFactor });
const dataURL = value.toDataURL({ scaleFactor });
images.push({ buffer, scaleFactor, size, dataURL });
}
return { type: 'nativeimage', value: images };
} else if (Array.isArray(value)) { } else if (Array.isArray(value)) {
visited.add(value); visited.add(value);
const meta = { const meta = {