Files
electron/build
Samuel Attard 5ef3e5d078 refactor: implement EventEmitter natively for sandboxed preload bundles (#53892)
* refactor: implement EventEmitter natively for sandboxed preload bundles

Adds a C++ implementation of the `EventEmitter` class
(shell/common/gin_helper/node_event_emitter.cc, exposed through the
`electron_common_events` binding) and resolves `events` to it in the
bundles that run without a Node.js environment, so `ipcRenderer`,
`webFrame` and the sandboxed preload `process` object no longer pull the
`events` npm package into every sandboxed renderer.

The class provides the same instance API the `events` package did and
nothing more: on/once/off/emit/addListener/removeListener/prependListener/
prependOnceListener/removeAllListeners/listeners/rawListeners/
listenerCount/eventNames/setMaxListeners/getMaxListeners, the
'newListener' and 'removeListener' events, the max-listener console
warning and `EventEmitter.defaultMaxListeners`. As in lib/events.js the
listener state lives in `_events` / `_eventsCount` / `_maxListeners` on
the instance, so the methods are generic over their receiver and classes
can extend it. A spec runs the same scenarios against Node's EventEmitter
and this one and requires identical results.

* perf: dispatch IPC messages to listeners without going through JS

The native EventEmitter keeps `_events`, `_eventsCount` and
`_maxListeners` in internal fields on real instances (still surfaced as
the usual own data properties), caches its property keys, builds once()
wrappers by binding one shared native function instead of minting a new
function per call, and calls straight into its own C++ bodies for the
internal `this.emit()` / `this.on()` / `this.removeListener()` calls when
those methods are not overridden.

It also exports EmitEvent(), and ipc_native::EmitIPCEvent() now builds the
event object and emits on ipcRenderer / ipcRendererInternal from C++
instead of calling a JS `onMessage` trampoline that called `emit()`. In a
renderer with Node.js integration the emitters are Node's and EmitEvent()
calls their `emit` method.

* perf: call multiple listeners for one event from a JS loop

Calling N listeners through N v8::Function::Call() entries from C++ costs
about four times per listener what a JIT-compiled loop does. When an
event has more than one listener, hand the listener array to a one-line
`for (...) Reflect.apply(listener, self, args)` function compiled once per
context instead. The single-listener case is still one direct call from
C++. Also pins the listener-throws semantics (later listeners skipped,
emit rethrows) against Node's in the spec.

* fix: handle object event names and large removeAllListeners in the native EventEmitter

Deleting an event whose name is an arbitrary object asked v8::Object::Delete
to convert it to a property key, which needs to run script and is not
allowed there; convert it first. Emitting 'removeListener' for every
listener of an event kept all the temporary handles of the loop alive until
it finished; give each iteration its own HandleScope.

* fix: address review comments on the native EventEmitter

- removeListener() searches the listener array in place from the back
  instead of copying it first, so removing the newest listener (what once()
  wrappers and removeAllListeners() do) no longer costs a full copy each time.
- The 'removeListener' event reports the originally registered function when
  a once() wrapper is removed from a multi-listener event, as events@3 did.
- A once() wrapper is only marked fired after its removeListener() call
  succeeded, matching events@3's order.
2026-09-17 15:07:37 -07:00
..
2023-11-30 09:51:35 -05:00
2025-02-27 14:31:21 +01:00