diff --git a/docs/api/structures/ipc-renderer-event.md b/docs/api/structures/ipc-renderer-event.md index 135834f209..2696895862 100644 --- a/docs/api/structures/ipc-renderer-event.md +++ b/docs/api/structures/ipc-renderer-event.md @@ -1,6 +1,6 @@ # IpcRendererEvent Object extends `Event` -* `sender` [IpcRenderer](../ipc-renderer.md) - The `IpcRenderer` instance that emitted the event originally +* `sender` [IpcRenderer](../ipc-renderer.md) _Deprecated_ - The `IpcRenderer` instance that emitted the event originally * `ports` [MessagePort][][] - A list of MessagePorts that were transferred with this message [MessagePort]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md index 8636e4bedc..7e68389860 100644 --- a/docs/breaking-changes.md +++ b/docs/breaking-changes.md @@ -146,6 +146,16 @@ app.on('gpu-process-crashed', (event, killed) => { /* ... */ }) app.on('child-process-gone', (event, details) => { /* ... */ }) ``` +### Deprecated: `event.sender` in `IpcRendererEvent` + +```js +// Deprecated +ipcRenderer.on('ping', (event) => event.sender.send('pong')) + +// Replace with +ipcRenderer.on('ping', () => ipcRenderer.send('pong')) +``` + ## Planned Breaking API Changes (27.0) ### Removed: macOS 10.13 / 10.14 support diff --git a/docs/fiddles/ipc/pattern-3/preload.js b/docs/fiddles/ipc/pattern-3/preload.js index 1df2941caa..2985d2a346 100644 --- a/docs/fiddles/ipc/pattern-3/preload.js +++ b/docs/fiddles/ipc/pattern-3/preload.js @@ -1,5 +1,6 @@ const { contextBridge, ipcRenderer } = require('electron/renderer') contextBridge.exposeInMainWorld('electronAPI', { - handleCounter: (callback) => ipcRenderer.on('update-counter', () => callback()) + onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (event, value) => callback(value)), + counterValue: (value) => ipcRenderer.send('counter-value', value) }) diff --git a/docs/fiddles/ipc/pattern-3/renderer.js b/docs/fiddles/ipc/pattern-3/renderer.js index d7316a5d87..c1d97a8483 100644 --- a/docs/fiddles/ipc/pattern-3/renderer.js +++ b/docs/fiddles/ipc/pattern-3/renderer.js @@ -1,8 +1,8 @@ const counter = document.getElementById('counter') -window.electronAPI.handleCounter((event, value) => { +window.electronAPI.onUpdateCounter((value) => { const oldValue = Number(counter.innerText) const newValue = oldValue + value - counter.innerText = newValue - event.sender.send('counter-value', newValue) + counter.innerText = newValue.toString() + window.electronAPI.counterValue(newValue) }) diff --git a/docs/tutorial/ipc.md b/docs/tutorial/ipc.md index 73b5723aeb..dda77c5ec4 100644 --- a/docs/tutorial/ipc.md +++ b/docs/tutorial/ipc.md @@ -429,7 +429,7 @@ modules in the preload script to expose IPC functionality to the renderer proces const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('electronAPI', { - onUpdateCounter: (callback) => ipcRenderer.on('update-counter', callback) + onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (event, value) => callback(value)) }) ``` @@ -439,6 +439,8 @@ After loading the preload script, your renderer process should have access to th :::caution Security warning We don't directly expose the whole `ipcRenderer.on` API for [security reasons][]. Make sure to limit the renderer's access to Electron APIs as much as possible. +Also don't just pass the callback to `ipcRenderer.on` as this will leak `ipcRenderer` via `event.sender`. +Use a custom handler that invoke the `callback` only with the desired arguments. ::: :::info @@ -486,10 +488,10 @@ To tie it all together, we'll create an interface in the loaded HTML file that c Finally, to make the values update in the HTML document, we'll add a few lines of DOM manipulation so that the value of the `#counter` element is updated whenever we fire an `update-counter` event. -```javascript title='renderer.js (Renderer Process)' @ts-window-type={electronAPI:{onUpdateCounter:(callback:(event:Electron.IpcRendererEvent,value:number)=>void)=>void}} +```javascript title='renderer.js (Renderer Process)' @ts-window-type={electronAPI:{onUpdateCounter:(callback:(value:number)=>void)=>void}} const counter = document.getElementById('counter') -window.electronAPI.onUpdateCounter((_event, value) => { +window.electronAPI.onUpdateCounter((value) => { const oldValue = Number(counter.innerText) const newValue = oldValue + value counter.innerText = newValue.toString() @@ -506,17 +508,26 @@ There's no equivalent for `ipcRenderer.invoke` for main-to-renderer IPC. Instead send a reply back to the main process from within the `ipcRenderer.on` callback. We can demonstrate this with slight modifications to the code from the previous example. In the -renderer process, use the `event` parameter to send a reply back to the main process through the +renderer process, expose another API to send a reply back to the main process through the `counter-value` channel. -```javascript title='renderer.js (Renderer Process)' @ts-window-type={electronAPI:{onUpdateCounter:(callback:(event:Electron.IpcRendererEvent,value:number)=>void)=>void}} +```javascript title='preload.js (Preload Script)' +const { contextBridge, ipcRenderer } = require('electron') + +contextBridge.exposeInMainWorld('electronAPI', { + onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (event, value) => callback(value)), + counterValue: (value) => ipcRenderer.send('counter-value', value) +}) +``` + +```javascript title='renderer.js (Renderer Process)' @ts-window-type={electronAPI:{onUpdateCounter:(callback:(value:number)=>void)=>void,counterValue:(value:number)=>void}} const counter = document.getElementById('counter') -window.electronAPI.onUpdateCounter((event, value) => { +window.electronAPI.onUpdateCounter((value) => { const oldValue = Number(counter.innerText) const newValue = oldValue + value counter.innerText = newValue.toString() - event.sender.send('counter-value', newValue) + window.electronAPI.counterValue(newValue) }) ``` diff --git a/filenames.auto.gni b/filenames.auto.gni index 1d89fc05e3..cc13d8bd09 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -144,6 +144,7 @@ auto_filenames = { sandbox_bundle_deps = [ "lib/common/api/native-image.ts", "lib/common/define-properties.ts", + "lib/common/deprecate.ts", "lib/common/ipc-messages.ts", "lib/common/web-view-methods.ts", "lib/common/webpack-globals-provider.ts", @@ -269,6 +270,7 @@ auto_filenames = { "lib/common/api/native-image.ts", "lib/common/api/shell.ts", "lib/common/define-properties.ts", + "lib/common/deprecate.ts", "lib/common/init.ts", "lib/common/ipc-messages.ts", "lib/common/reset-search-paths.ts", diff --git a/lib/renderer/common-init.ts b/lib/renderer/common-init.ts index 4910ca6671..da569a4c94 100644 --- a/lib/renderer/common-init.ts +++ b/lib/renderer/common-init.ts @@ -1,5 +1,6 @@ import { ipcRenderer } from 'electron/renderer'; import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import * as deprecate from '@electron/internal/common/deprecate'; import type * as webViewInitModule from '@electron/internal/renderer/web-view/web-view-init'; import type * as windowSetupModule from '@electron/internal/renderer/window-setup'; @@ -19,7 +20,15 @@ const isWebView = mainFrame.getWebPreference('isWebView'); v8Util.setHiddenValue(global, 'ipcNative', { onMessage (internal: boolean, channel: string, ports: MessagePort[], args: any[]) { const sender = internal ? ipcRendererInternal : ipcRenderer; - sender.emit(channel, { sender, ports }, ...args); + const event = { ports }; + const warn = deprecate.warnOnce('event.sender', 'ipcRenderer'); + Object.defineProperty(event, 'sender', { + get: () => { + warn(); + return sender; + } + }); + sender.emit(channel, event, ...args); } });