refactor: replace ipcRendererUtils.invoke() with ipcRendererInternal.invoke() ()

This commit is contained in:
Milan Burda 2019-08-24 00:45:50 +02:00 committed by Jeremy Apthorp
parent 698120daf0
commit 81e9dab52f
29 changed files with 195 additions and 164 deletions

View file

@ -254,6 +254,7 @@ auto_filenames = {
"lib/browser/guest-view-manager.js",
"lib/browser/guest-window-manager.js",
"lib/browser/init.ts",
"lib/browser/ipc-main-impl.ts",
"lib/browser/ipc-main-internal-utils.ts",
"lib/browser/ipc-main-internal.ts",
"lib/browser/navigation-controller.js",

View file

@ -1,38 +1,6 @@
import { EventEmitter } from 'events'
import { IpcMainInvokeEvent } from 'electron'
import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl'
class IpcMain extends EventEmitter {
private _invokeHandlers: Map<string, (e: IpcMainInvokeEvent, ...args: any[]) => void> = new Map();
handle: Electron.IpcMain['handle'] = (method, fn) => {
if (this._invokeHandlers.has(method)) {
throw new Error(`Attempted to register a second handler for '${method}'`)
}
if (typeof fn !== 'function') {
throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`)
}
this._invokeHandlers.set(method, async (e, ...args) => {
try {
(e as any)._reply(await Promise.resolve(fn(e, ...args)))
} catch (err) {
(e as any)._throw(err)
}
})
}
handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => {
this.handle(method, (e, ...args) => {
this.removeHandler(method)
return fn(e, ...args)
})
}
removeHandler (method: string) {
this._invokeHandlers.delete(method)
}
}
const ipcMain = new IpcMain()
const ipcMain = new IpcMainImpl()
// Do not throw exception when channel name is "error".
ipcMain.on('error', () => {})

View file

@ -327,14 +327,15 @@ WebContents.prototype._init = function () {
}
})
this.on('-ipc-invoke', function (event, channel, args) {
this.on('-ipc-invoke', function (event, internal, channel, args) {
event._reply = (result) => event.sendReply({ result })
event._throw = (error) => {
console.error(`Error occurred in handler for '${channel}':`, error)
event.sendReply({ error: error.toString() })
}
if (ipcMain._invokeHandlers.has(channel)) {
ipcMain._invokeHandlers.get(channel)(event, ...args)
const target = internal ? ipcMainInternal : ipcMain
if (target._invokeHandlers.has(channel)) {
target._invokeHandlers.get(channel)(event, ...args)
} else {
event._throw(`No handler registered for '${channel}'`)
}

View file

@ -6,6 +6,7 @@ if (process.electronBinding('features').isExtensionsEnabled()) {
const { app, webContents, BrowserWindow } = require('electron')
const { getAllWebContents } = process.electronBinding('web_contents')
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
const { Buffer } = require('buffer')
@ -160,7 +161,7 @@ const hookWebContentsEvents = function (webContents) {
// Handle the chrome.* API messages.
let nextId = 0
ipcMainUtils.handle('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) {
ipcMainUtils.handleSync('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) {
if (isBackgroundPage(event.sender)) {
throw new Error('chrome.runtime.connect is not supported in background page')
}
@ -182,7 +183,7 @@ ipcMainUtils.handle('CHROME_RUNTIME_CONNECT', function (event, extensionId, conn
return { tabId, portId }
})
ipcMainUtils.handle('CHROME_EXTENSION_MANIFEST', function (event, extensionId) {
ipcMainUtils.handleSync('CHROME_EXTENSION_MANIFEST', function (event, extensionId) {
const manifest = manifestMap[extensionId]
if (!manifest) {
throw new Error(`Invalid extensionId: ${extensionId}`)
@ -190,7 +191,7 @@ ipcMainUtils.handle('CHROME_EXTENSION_MANIFEST', function (event, extensionId) {
return manifest
})
ipcMainUtils.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extensionId, message) {
ipcMainInternal.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extensionId, message) {
if (isBackgroundPage(event.sender)) {
throw new Error('chrome.runtime.sendMessage is not supported in background page')
}
@ -203,7 +204,7 @@ ipcMainUtils.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extens
return ipcMainUtils.invokeInWebContents(page.webContents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message)
})
ipcMainUtils.handle('CHROME_TABS_SEND_MESSAGE', async function (event, tabId, extensionId, message) {
ipcMainInternal.handle('CHROME_TABS_SEND_MESSAGE', async function (event, tabId, extensionId, message) {
const contents = webContents.fromId(tabId)
if (!contents) {
throw new Error(`Sending message to unknown tab ${tabId}`)
@ -237,7 +238,7 @@ const getMessagesPath = (extensionId) => {
}
}
ipcMainUtils.handle('CHROME_GET_MESSAGES', async function (event, extensionId) {
ipcMainUtils.handleSync('CHROME_GET_MESSAGES', async function (event, extensionId) {
const messagesPath = getMessagesPath(extensionId)
return fs.promises.readFile(messagesPath)
})
@ -256,7 +257,7 @@ const getChromeStoragePath = (storageType, extensionId) => {
return path.join(app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`)
}
ipcMainUtils.handle('CHROME_STORAGE_READ', async function (event, storageType, extensionId) {
ipcMainInternal.handle('CHROME_STORAGE_READ', async function (event, storageType, extensionId) {
const filePath = getChromeStoragePath(storageType, extensionId)
try {
@ -270,7 +271,7 @@ ipcMainUtils.handle('CHROME_STORAGE_READ', async function (event, storageType, e
}
})
ipcMainUtils.handle('CHROME_STORAGE_WRITE', async function (event, storageType, extensionId, data) {
ipcMainInternal.handle('CHROME_STORAGE_WRITE', async function (event, storageType, extensionId, data) {
const filePath = getChromeStoragePath(storageType, extensionId)
try {
@ -295,7 +296,7 @@ const assertChromeExtension = function (contents, api) {
}
}
ipcMainUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', async function (event, tabId, extensionId, details) {
ipcMainInternal.handle('CHROME_TABS_EXECUTE_SCRIPT', async function (event, tabId, extensionId, details) {
assertChromeExtension(event.sender, 'chrome.tabs.executeScript()')
const contents = webContents.fromId(tabId)

View file

@ -2,7 +2,8 @@ import { dialog, Menu } from 'electron'
import * as fs from 'fs'
import * as url from 'url'
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal'
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils'
const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id: number) => void) {
return items.map(function (item) {
@ -59,7 +60,7 @@ const assertChromeDevTools = function (contents: Electron.WebContents, api: stri
}
}
ipcMainUtils.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron.IpcMainEvent, items: ContextMenuItem[], isEditMenu: boolean) {
ipcMainInternal.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron.IpcMainInvokeEvent, items: ContextMenuItem[], isEditMenu: boolean) {
return new Promise(resolve => {
assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()')
@ -71,7 +72,7 @@ ipcMainUtils.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron
})
})
ipcMainUtils.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Electron.IpcMainEvent) {
ipcMainInternal.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Electron.IpcMainInvokeEvent) {
assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()')
const result = await dialog.showOpenDialog({})
@ -83,7 +84,7 @@ ipcMainUtils.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Ele
return [path, data]
})
ipcMainUtils.handle('ELECTRON_INSPECTOR_CONFIRM', async function (event: Electron.IpcMainEvent, message: string = '', title: string = '') {
ipcMainUtils.handleSync('ELECTRON_INSPECTOR_CONFIRM', async function (event: Electron.IpcMainInvokeEvent, message: string = '', title: string = '') {
assertChromeDevTools(event.sender, 'window.confirm()')
const options = {

View file

@ -312,21 +312,33 @@ const isWebViewTagEnabled = function (contents) {
return isWebViewTagEnabledCache.get(contents)
}
const handleMessage = function (channel, handler) {
ipcMainUtils.handle(channel, (event, ...args) => {
const makeSafeHandler = function (channel, handler) {
return (event, ...args) => {
if (isWebViewTagEnabled(event.sender)) {
return handler(event, ...args)
} else {
console.error(`<webview> IPC message ${channel} sent by WebContents with <webview> disabled (${event.sender.id})`)
throw new Error('<webview> disabled')
}
})
}
}
const handleMessage = function (channel, handler) {
ipcMainInternal.handle(channel, makeSafeHandler(channel, handler))
}
const handleMessageSync = function (channel, handler) {
ipcMainUtils.handleSync(channel, makeSafeHandler(channel, handler))
}
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) {
return createGuest(event.sender, params)
})
handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) {
return createGuest(event.sender, params)
})
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
try {
attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params)
@ -345,11 +357,18 @@ ipcMainInternal.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event,
}
})
const allMethods = new Set([ ...syncMethods, ...asyncMethods ])
handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) {
const guest = getGuestForWebContents(guestInstanceId, event.sender)
if (!allMethods.has(method)) {
if (!asyncMethods.has(method)) {
throw new Error(`Invalid method: ${method}`)
}
return guest[method](...args)
})
handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) {
const guest = getGuestForWebContents(guestInstanceId, event.sender)
if (!syncMethods.has(method)) {
throw new Error(`Invalid method: ${method}`)
}

View file

@ -271,15 +271,30 @@ ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', functio
}
})
const handleMessage = function (channel, handler) {
ipcMainUtils.handle(channel, (event, guestId, ...args) => {
const makeSafeHandler = function (handler) {
return (event, guestId, ...args) => {
const guestContents = webContents.fromId(guestId)
if (!guestContents) {
throw new Error(`Invalid guestId: ${guestId}`)
}
return handler(event, guestContents, ...args)
})
}
}
const handleMessage = function (channel, handler) {
ipcMainInternal.handle(channel, makeSafeHandler(handler))
}
const handleMessageSync = function (channel, handler) {
ipcMainUtils.handleSync(channel, makeSafeHandler(handler))
}
const assertCanAccessWindow = function (contents, guestContents) {
if (!canAccessWindow(contents, guestContents)) {
console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`)
throw new Error(`Access denied to guestId: ${guestContents.id}`)
}
}
const windowMethods = new Set([
@ -289,10 +304,7 @@ const windowMethods = new Set([
])
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestContents, method, ...args) => {
if (!canAccessWindow(event.sender, guestContents)) {
console.error(`Blocked ${event.sender.getURL()} from accessing guestId: ${guestContents.id}`)
throw new Error(`Access denied to guestId: ${guestContents.id}`)
}
assertCanAccessWindow(event.sender, guestContents)
if (!windowMethods.has(method)) {
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
@ -316,20 +328,31 @@ handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestC
}
})
const webContentsMethods = new Set([
'getURL',
const webContentsMethodsAsync = new Set([
'loadURL',
'executeJavaScript',
'print'
])
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
if (!canAccessWindow(event.sender, guestContents)) {
console.error(`Blocked ${event.sender.getURL()} from accessing guestId: ${guestContents.id}`)
throw new Error(`Access denied to guestId: ${guestContents.id}`)
}
assertCanAccessWindow(event.sender, guestContents)
if (!webContentsMethods.has(method)) {
if (!webContentsMethodsAsync.has(method)) {
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
throw new Error(`Invalid method: ${method}`)
}
return guestContents[method](...args)
})
const webContentsMethodsSync = new Set([
'getURL'
])
handleMessageSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
assertCanAccessWindow(event.sender, guestContents)
if (!webContentsMethodsSync.has(method)) {
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
throw new Error(`Invalid method: ${method}`)
}

View file

@ -0,0 +1,33 @@
import { EventEmitter } from 'events'
import { IpcMainInvokeEvent } from 'electron'
export class IpcMainImpl extends EventEmitter {
private _invokeHandlers: Map<string, (e: IpcMainInvokeEvent, ...args: any[]) => void> = new Map();
handle: Electron.IpcMain['handle'] = (method, fn) => {
if (this._invokeHandlers.has(method)) {
throw new Error(`Attempted to register a second handler for '${method}'`)
}
if (typeof fn !== 'function') {
throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`)
}
this._invokeHandlers.set(method, async (e, ...args) => {
try {
(e as any)._reply(await Promise.resolve(fn(e, ...args)))
} catch (err) {
(e as any)._throw(err)
}
})
}
handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => {
this.handle(method, (e, ...args) => {
this.removeHandler(method)
return fn(e, ...args)
})
}
removeHandler (method: string) {
this._invokeHandlers.delete(method)
}
}

View file

@ -1,26 +1,15 @@
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal'
import * as errorUtils from '@electron/internal/common/error-utils'
type IPCHandler = (event: ElectronInternal.IpcMainInternalEvent, ...args: any[]) => any
type IPCHandler = (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any
const callHandler = async function (handler: IPCHandler, event: ElectronInternal.IpcMainInternalEvent, args: any[], reply: (args: any[]) => void) {
try {
const result = await handler(event, ...args)
reply([null, result])
} catch (error) {
reply([errorUtils.serialize(error)])
}
}
export const handle = function <T extends IPCHandler> (channel: string, handler: T) {
ipcMainInternal.on(channel, (event, requestId, ...args) => {
callHandler(handler, event, args, responseArgs => {
if (requestId) {
event._replyInternal(`${channel}_RESPONSE_${requestId}`, ...responseArgs)
} else {
event.returnValue = responseArgs
}
})
export const handleSync = function <T extends IPCHandler> (channel: string, handler: T) {
ipcMainInternal.on(channel, async (event, ...args) => {
try {
event.returnValue = [null, await handler(event, ...args)]
} catch (error) {
event.returnValue = [errorUtils.serialize(error)]
}
})
}

View file

@ -1,8 +1,6 @@
import { EventEmitter } from 'events'
import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl'
const emitter = new EventEmitter()
export const ipcMainInternal = new IpcMainImpl() as ElectronInternal.IpcMainInternal
// Do not throw exception when channel name is "error".
emitter.on('error', () => {})
export const ipcMainInternal = emitter as ElectronInternal.IpcMainInternal
ipcMainInternal.on('error', () => {})

View file

@ -471,11 +471,11 @@ ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
event.returnValue = null
})
ipcMainUtils.handle('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
return crashReporterInit(options)
})
ipcMainUtils.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
ipcMainInternal.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) {
return event.sender.getLastWebPreferences()
})
@ -491,7 +491,7 @@ const allowedClipboardMethods = (() => {
}
})()
ipcMainUtils.handle('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...args) {
ipcMainUtils.handleSync('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...args) {
if (!allowedClipboardMethods.has(method)) {
throw new Error(`Invalid method: ${method}`)
}
@ -502,7 +502,7 @@ ipcMainUtils.handle('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...ar
if (features.isDesktopCapturerEnabled()) {
const desktopCapturer = require('@electron/internal/browser/desktop-capturer')
ipcMainUtils.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, ...args) {
ipcMainInternal.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, ...args) {
const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources')
if (customEvent.defaultPrevented) {
@ -526,13 +526,13 @@ const getPreloadScript = async function (preloadPath) {
}
if (process.electronBinding('features').isExtensionsEnabled()) {
ipcMainUtils.handle('ELECTRON_GET_CONTENT_SCRIPTS', () => [])
ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => [])
} else {
const { getContentScripts } = require('@electron/internal/browser/chrome-extension')
ipcMainUtils.handle('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts())
ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts())
}
ipcMainUtils.handle('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) {
ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) {
const preloadPaths = event.sender._getPreloadPaths()
let contentScripts = []

View file

@ -1,5 +1,5 @@
import { nativeImage } from 'electron'
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
// |options.types| can't be empty and must be an array
function isValid (options: Electron.SourcesOptions) {
@ -16,7 +16,7 @@ export async function getSources (options: Electron.SourcesOptions) {
const { thumbnailSize = { width: 150, height: 150 } } = options
const { fetchWindowIcons = false } = options
const sources = await ipcRendererUtils.invoke<ElectronInternal.GetSourcesResult[]>('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', {
const sources = await ipcRendererInternal.invoke<ElectronInternal.GetSourcesResult[]>('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', {
captureWindow,
captureScreen,
thumbnailSize,

View file

@ -21,11 +21,12 @@ ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
return ipc.sendTo(internal, false, webContentsId, channel, args)
}
ipcRenderer.invoke = function (channel, ...args) {
return ipc.invoke(channel, args).then(({ error, result }) => {
if (error) { throw new Error(`Error invoking remote method '${channel}': ${error}`) }
return result
})
ipcRenderer.invoke = async function (channel, ...args) {
const { error, result } = await ipc.invoke(internal, channel, args)
if (error) {
throw new Error(`Error invoking remote method '${channel}': ${error}`)
}
return result
}
export default ipcRenderer

View file

@ -157,7 +157,7 @@ export function injectTo (extensionId: string, context: any) {
console.error('options are not supported')
}
ipcRendererUtils.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback)
ipcRendererInternal.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback)
},
onConnect: new Event(),
@ -172,7 +172,7 @@ export function injectTo (extensionId: string, context: any) {
details: Chrome.Tabs.ExecuteScriptDetails,
resultCallback: Chrome.Tabs.ExecuteScriptCallback = () => {}
) {
ipcRendererUtils.invoke('CHROME_TABS_EXECUTE_SCRIPT', tabId, extensionId, details)
ipcRendererInternal.invoke('CHROME_TABS_EXECUTE_SCRIPT', tabId, extensionId, details)
.then((result: any) => resultCallback([result]))
},
@ -183,7 +183,7 @@ export function injectTo (extensionId: string, context: any) {
_options: Chrome.Tabs.SendMessageDetails,
responseCallback: Chrome.Tabs.SendMessageCallback = () => {}
) {
ipcRendererUtils.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback)
ipcRendererInternal.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback)
},
onUpdated: new Event(),

View file

@ -1,9 +1,9 @@
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
const getStorage = (storageType: string, extensionId: number, callback: Function) => {
if (typeof callback !== 'function') throw new TypeError('No callback provided')
ipcRendererUtils.invoke<string>('CHROME_STORAGE_READ', storageType, extensionId)
ipcRendererInternal.invoke<string>('CHROME_STORAGE_READ', storageType, extensionId)
.then(data => {
if (data !== null) {
callback(JSON.parse(data))
@ -17,7 +17,7 @@ const getStorage = (storageType: string, extensionId: number, callback: Function
const setStorage = (storageType: string, extensionId: number, storage: Record<string, any>, callback: Function) => {
const json = JSON.stringify(storage)
ipcRendererUtils.invoke('CHROME_STORAGE_WRITE', storageType, extensionId, json)
ipcRendererInternal.invoke('CHROME_STORAGE_WRITE', storageType, extensionId, json)
.then(() => {
if (callback) callback()
})

View file

@ -1,4 +1,5 @@
import { invoke, invokeSync } from '@electron/internal/renderer/ipc-renderer-internal-utils'
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
window.onload = function () {
// Use menu API to show context menu.
@ -19,7 +20,7 @@ function completeURL (project: string, path: string) {
// The DOM implementation expects (message?: string) => boolean
(window.confirm as any) = function (message: string, title: string) {
return invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title) as boolean
return ipcRendererUtils.invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title) as boolean
}
const useEditMenuItems = function (x: number, y: number, items: ContextMenuItem[]) {
@ -32,7 +33,7 @@ const useEditMenuItems = function (x: number, y: number, items: ContextMenuItem[
const createMenu = function (x: number, y: number, items: ContextMenuItem[]) {
const isEditMenu = useEditMenuItems(x, y, items)
invoke<number>('ELECTRON_INSPECTOR_CONTEXT_MENU', items, isEditMenu).then(id => {
ipcRendererInternal.invoke<number>('ELECTRON_INSPECTOR_CONTEXT_MENU', items, isEditMenu).then(id => {
if (typeof id === 'number') {
window.DevToolsAPI!.contextMenuItemSelected(id)
}
@ -41,7 +42,7 @@ const createMenu = function (x: number, y: number, items: ContextMenuItem[]) {
}
const showFileChooserDialog = function (callback: (blob: File) => void) {
invoke<[ string, any ]>('ELECTRON_INSPECTOR_SELECT_FILE').then(([path, data]) => {
ipcRendererInternal.invoke<[ string, any ]>('ELECTRON_INSPECTOR_SELECT_FILE').then(([path, data]) => {
if (path && data) {
callback(dataToHtml5FileObject(path, data))
}

View file

@ -4,38 +4,18 @@ import * as errorUtils from '@electron/internal/common/error-utils'
type IPCHandler = (event: Electron.IpcRendererEvent, ...args: any[]) => any
export const handle = function <T extends IPCHandler> (channel: string, handler: T) {
ipcRendererInternal.on(channel, (event, requestId, ...args) => {
new Promise(resolve => resolve(handler(event, ...args))
).then(result => {
return [null, result]
}, error => {
return [errorUtils.serialize(error)]
}).then(responseArgs => {
event.sender.send(`${channel}_RESPONSE_${requestId}`, ...responseArgs)
})
})
}
let nextId = 0
export function invoke<T> (command: string, ...args: any[]) {
return new Promise<T>((resolve, reject) => {
const requestId = ++nextId
ipcRendererInternal.once(`${command}_RESPONSE_${requestId}`, (
_event, error: Electron.SerializedError, result: any
) => {
if (error) {
reject(errorUtils.deserialize(error))
} else {
resolve(result)
}
})
ipcRendererInternal.send(command, requestId, ...args)
ipcRendererInternal.on(channel, async (event, requestId, ...args) => {
const replyChannel = `${channel}_RESPONSE_${requestId}`
try {
event.sender.send(replyChannel, null, await handler(event, ...args))
} catch (error) {
event.sender.send(replyChannel, errorUtils.serialize(error))
}
})
}
export function invokeSync<T> (command: string, ...args: any[]): T {
const [ error, result ] = ipcRendererInternal.sendSync(command, null, ...args)
const [ error, result ] = ipcRendererInternal.sendSync(command, ...args)
if (error) {
throw errorUtils.deserialize(error)

View file

@ -20,3 +20,11 @@ ipcRendererInternal.sendTo = function (webContentsId, channel, ...args) {
ipcRendererInternal.sendToAll = function (webContentsId, channel, ...args) {
return ipc.sendTo(internal, true, webContentsId, channel, args)
}
ipcRendererInternal.invoke = async function<T> (channel: string, ...args: any[]) {
const { error, result } = await ipc.invoke<T>(internal, channel, args)
if (error) {
throw new Error(`Error invoking remote method '${channel}': ${error}`)
}
return result
}

View file

@ -1,5 +1,5 @@
import { webFrame } from 'electron'
import { invoke } from '@electron/internal/renderer/ipc-renderer-internal-utils'
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
let shouldLog: boolean | null = null
@ -299,7 +299,7 @@ const logSecurityWarnings = function (
const getWebPreferences = async function () {
try {
return invoke<Electron.WebPreferences>('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES')
return ipcRendererInternal.invoke<Electron.WebPreferences>('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES')
} catch (error) {
console.warn(`getLastWebPreferences() failed: ${error}`)
}

View file

@ -1,6 +1,6 @@
import { webFrame, IpcMessageEvent } from 'electron'
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
import { invoke, invokeSync } from '@electron/internal/renderer/ipc-renderer-internal-utils'
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl'
@ -93,11 +93,11 @@ export function deregisterEvents (viewInstanceId: number) {
}
export function createGuest (params: Record<string, any>): Promise<number> {
return invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params)
return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params)
}
export function createGuestSync (params: Record<string, any>): number {
return invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params)
return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params)
}
export function attachGuest (
@ -107,7 +107,7 @@ export function attachGuest (
if (embedderFrameId < 0) { // this error should not happen.
throw new Error('Invalid embedder frame')
}
invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params)
ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params)
}
export const guestViewInternalModule = {

View file

@ -1,4 +1,4 @@
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl'
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'
@ -196,7 +196,7 @@ class SrcAttribute extends WebViewAttribute {
const method = 'loadURL'
const args = [this.getValue(), opts]
ipcRendererUtils.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', guestInstanceId, method, args)
ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', guestInstanceId, method, args)
}
}

View file

@ -1,5 +1,6 @@
import { remote, webFrame } from 'electron'
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
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'
@ -253,7 +254,7 @@ export const setupMethods = (WebViewElement: typeof ElectronInternal.WebViewElem
const createNonBlockHandler = function (method: string) {
return function (this: ElectronInternal.WebViewElement, ...args: Array<any>) {
return ipcRendererUtils.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args)
return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args)
}
}

View file

@ -106,7 +106,7 @@ class LocationProxy {
}
private _invokeWebContentsMethod (method: string, ...args: any[]) {
return ipcRendererUtils.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args)
return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args)
}
private _invokeWebContentsMethodSync (method: string, ...args: any[]) {
@ -158,7 +158,7 @@ class BrowserWindowProxy {
}
public postMessage (message: any, targetOrigin: string) {
ipcRendererUtils.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin)
ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin)
}
public eval (code: string) {
@ -166,11 +166,11 @@ class BrowserWindowProxy {
}
private _invokeWindowMethod (method: string, ...args: any[]) {
return ipcRendererUtils.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, method, ...args)
return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, method, ...args)
}
private _invokeWebContentsMethod (method: string, ...args: any[]) {
return ipcRendererUtils.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args)
return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args)
}
}

View file

@ -976,12 +976,13 @@ void WebContents::Message(bool internal,
internal, channel, std::move(arguments));
}
void WebContents::Invoke(const std::string& channel,
void WebContents::Invoke(bool internal,
const std::string& channel,
base::Value arguments,
InvokeCallback callback) {
// webContents.emit('-ipc-invoke', new Event(), channel, arguments);
// webContents.emit('-ipc-invoke', new Event(), internal, channel, arguments);
EmitWithSender("-ipc-invoke", bindings_.dispatch_context(),
std::move(callback), channel, std::move(arguments));
std::move(callback), internal, channel, std::move(arguments));
}
void WebContents::MessageSync(bool internal,

View file

@ -491,7 +491,8 @@ class WebContents : public mate::TrackableObject<WebContents>,
void Message(bool internal,
const std::string& channel,
base::Value arguments) override;
void Invoke(const std::string& channel,
void Invoke(bool internal,
const std::string& channel,
base::Value arguments,
InvokeCallback callback) override;
void MessageSync(bool internal,

View file

@ -42,6 +42,7 @@ interface ElectronBrowser {
// Emits an event on |channel| from the ipcMain JavaScript object in the main
// process, and returns the response.
Invoke(
bool internal,
string channel,
mojo_base.mojom.ListValue arguments) => (mojo_base.mojom.Value result);

View file

@ -73,13 +73,14 @@ class IPCRenderer : public mate::Wrappable<IPCRenderer> {
}
v8::Local<v8::Promise> Invoke(mate::Arguments* args,
bool internal,
const std::string& channel,
const base::Value& arguments) {
electron::util::Promise<base::Value> p(args->isolate());
auto handle = p.GetHandle();
electron_browser_ptr_->get()->Invoke(
channel, arguments.Clone(),
internal, channel, arguments.Clone(),
base::BindOnce([](electron::util::Promise<base::Value> p,
base::Value result) { p.Resolve(result); },
std::move(p)));

View file

@ -20,7 +20,7 @@ declare namespace NodeJS {
sendSync(internal: boolean, channel: string, args: any[]): any;
sendToHost(channel: string, args: any[]): void;
sendTo(internal: boolean, sendToAll: boolean, webContentsId: number, channel: string, args: any[]): void;
invoke<T>(channel: string, args: any[]): Promise<{ error: string, result: T }>;
invoke<T>(internal: boolean, channel: string, args: any[]): Promise<{ error: string, result: T }>;
}
interface V8UtilBinding {

View file

@ -63,6 +63,7 @@ declare namespace Electron {
}
interface IpcRendererInternal extends Electron.IpcRenderer {
invoke<T>(channel: string, ...args: any[]): Promise<T>;
sendToAll(webContentsId: number, channel: string, ...args: any[]): void
}
@ -126,6 +127,7 @@ declare namespace ElectronInternal {
}
interface IpcMainInternal extends NodeJS.EventEmitter {
handle(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => Promise<any> | any): void;
on(channel: string, listener: (event: IpcMainInternalEvent, ...args: any[]) => void): this;
once(channel: string, listener: (event: IpcMainInternalEvent, ...args: any[]) => void): this;
}