Skip to content

Commit

Permalink
feat: add a getScopedToSender API on ipcMain
Browse files Browse the repository at this point in the history
This solves the usecase where folks for either security or use-case
reasons only want to listen for message X coming from webContents Y.

Instead of checking the `sender` or `sender.id` each time and hoping
you don't miss a listener or writing your own abstraction around IPC
(let's be real we've all done that before) this provides a low-overhead
first-party API for solving this problem.

This use-case has actually come up in internal core code as well so
we can benefit from this API internally.
  • Loading branch information
MarshallOfSound committed Mar 20, 2019
1 parent 1c7b302 commit 47e7126
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 14 deletions.
11 changes: 11 additions & 0 deletions docs/api/ipc-main.md
Expand Up @@ -88,6 +88,17 @@ Removes the specified `listener` from the listener array for the specified

Removes listeners of the specified `channel`.

### `ipcMain.getScopedToSender(sender)`

* `sender` [WebContents](web-contents.md)

Returns `IpcMain`

Gets a new instance of `IpcMain` that is scoped to only emit ipc messages from
the given sender. This is useful for ensuring the messages you are handling
only come from a webContents you trust without having to check the sender
for every message.

## Event object

The documentation for the `event` object passed to the `callback` can be found
Expand Down
1 change: 1 addition & 0 deletions filenames.auto.gni
Expand Up @@ -86,6 +86,7 @@ auto_filenames = {
"docs/api/structures/notification-action.md",
"docs/api/structures/point.md",
"docs/api/structures/printer-info.md",
"docs/api/structures/process-memory-info.md",
"docs/api/structures/process-metric.md",
"docs/api/structures/product.md",
"docs/api/structures/rectangle.md",
Expand Down
1 change: 1 addition & 0 deletions filenames.gni
Expand Up @@ -42,6 +42,7 @@ filenames = {
"lib/browser/guest-window-manager.js",
"lib/browser/init.ts",
"lib/browser/ipc-main-internal-utils.ts",
"lib/browser/ipc-main-creator.ts",
"lib/browser/ipc-main-internal.ts",
"lib/browser/navigation-controller.js",
"lib/browser/objects-registry.js",
Expand Down
9 changes: 2 additions & 7 deletions lib/browser/api/ipc-main.ts
@@ -1,8 +1,3 @@
import { EventEmitter } from 'events'
import { createIndependentIpcMain } from '@electron/internal/browser/ipc-main-creator'

const emitter = new EventEmitter()

// Do not throw exception when channel name is "error".
emitter.on('error', () => {})

export default emitter
export default createIndependentIpcMain()
49 changes: 49 additions & 0 deletions lib/browser/ipc-main-creator.ts
@@ -0,0 +1,49 @@
import {
EventEmitter
} from 'events'

enum IpcMainHeirachy {
ROOT = 'ROOT',
SCOPED = 'SCOPED',
}

export const createIndependentIpcMain = () => {
const senderScoped = new Map < number,
IpcMain >()

class IpcMain extends EventEmitter implements Electron.IpcMain {
constructor (private heirachy: IpcMainHeirachy) {
super()
// Do not throw exception when channel name is "error".
this.on('error', () => {})
}

getScopedToSender (webContents: Electron.WebContents) {
const senderId = webContents.id
let scoped = senderScoped.get(senderId)
if (!scoped) {
// Store based on senderId here and don't persist the webContents to avoid it leaking
scoped = new IpcMain(IpcMainHeirachy.SCOPED)
senderScoped.set(senderId, scoped)
}
return scoped
}

emit (channel: string, event: Electron.IpcMainEvent, ...args: any[]): boolean {
let handled = false
// If this is the root instance of IpcMain and we have an event with a valid sender
// we should try emit the event on the scoped IpcMain instance
if (this.heirachy === IpcMainHeirachy.ROOT && event && event.sender && event.sender.id) {
const scoped = senderScoped.get(event.sender.id)
if (scoped) {
handled = scoped.emit(channel, event, ...args) || handled
}
}
handled = super.emit(channel, event, ...args) || handled
return handled
}
}

const ipcMain = new IpcMain(IpcMainHeirachy.ROOT)
return ipcMain
}
9 changes: 2 additions & 7 deletions lib/browser/ipc-main-internal.ts
@@ -1,8 +1,3 @@
import { EventEmitter } from 'events'
import { createIndependentIpcMain } from '@electron/internal/browser/ipc-main-creator'

const emitter = new EventEmitter()

// Do not throw exception when channel name is "error".
emitter.on('error', () => {})

export const ipcMainInternal = emitter as ElectronInternal.IpcMainInternal
export const ipcMainInternal = createIndependentIpcMain() as ElectronInternal.IpcMainInternal

0 comments on commit 47e7126

Please sign in to comment.