mirror of
https://github.com/electron/electron.git
synced 2026-10-28 11:44:46 +08:00
* 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.