首页
/ Electron InputEvent 对象详解:sendInputEvent 合成输入事件的完整参考与源码剖析

Electron InputEvent 对象详解:sendInputEvent 合成输入事件的完整参考与源码剖析

2026-09-06 12:41:25作者:牧宁李

本文以 Electron 的 InputEvent 对象 为核心,完整梳理 typemodifiers 两个字段的全部取值,并结合 KeyboardInputEventMouseInputEventMouseWheelInputEvent 三个子类,讲清如何通过 webContents.sendInputEvent() 向页面注入键盘、鼠标与滚轮事件。文末进一步深入到 C++ 分发实现gin 转换器,说明事件解析、修饰键别名映射与轮询相位补偿的底层细节,帮助你在自动化测试与页面控制场景中正确构造并发送合成输入事件。

一、InputEvent 是什么:基类结构与字段全表

InputEvent 是 Electron 中表示一次输入事件的基础结构体,所有具体的输入事件对象(键盘、鼠标、滚轮)都继承自它。它本身只有两个字段:

字段 类型 说明
type string 事件类型。可取 undefined 或下表中列出的 40 个字符串之一
modifiers string[](可选) 事件的修饰键数组,可取 17 个字符串值(见下表)

type 字段的全部取值

type 的值按输入源可分为五组,完整枚举如下(依据 input-event.md):

分组 取值
鼠标事件 mouseDownmouseUpmouseMovemouseEntermouseLeavecontextMenumouseWheel
键盘事件 rawKeyDownkeyDownkeyUpchar
手势滚动/缩放 gestureScrollBegingestureScrollEndgestureScrollUpdategestureFlingStartgestureFlingCancelgesturePinchBegingesturePinchEndgesturePinchUpdate
手势点按 gestureTapDowngestureShowPressgestureTapgestureTapCancelgestureShortPressgestureLongPressgestureLongTapgestureTwoFingerTapgestureTapUnconfirmedgestureDoubleTap
触摸与指针 touchStarttouchMovetouchEndtouchCanceltouchScrollStartedpointerDownpointerUppointerMovepointerRawUpdatepointerCancelpointerCausedUaAction

modifiers 字段的全部取值

modifiers 是一个字符串数组,表示事件触发时处于按下/开启状态的修饰键:

取值 含义
shift Shift 键
control / ctrl Ctrl 键(两个名称等价)
alt Alt 键
meta / command / cmd Meta 键(macOS 的 Command,三个名称等价)
iskeypad 按键来自小键盘
isautorepeat 按键处于自动重复状态
leftbuttondown / middlebuttondown / rightbuttondown 左/中/右鼠标按钮处于按下状态
capslock / numlock 大写锁定 / 数字锁定开启
left / right 事件与左/右键相关

继承体系:三个具体子类

InputEvent 是纯基类,实际传给 sendInputEvent 的是它的子类。三个子类的文档与基类字段之外的扩展字段如下:

MouseInputEvent(extends InputEvent)

字段 类型 说明
type string 可取 mouseDownmouseUpmouseEntermouseLeavecontextMenumouseWheelmouseMove
x / y Integer 事件在页面中的坐标
button string(可选) 按下的按钮:leftmiddleright
globalX / globalY Integer(可选) 全局(屏幕)坐标
movementX / movementY Integer(可选) 相对移动量
clickCount Integer(可选) 点击计数

KeyboardInputEvent(extends InputEvent)

字段 类型 说明
type string 可取 rawKeyDownkeyDownkeyUpchar
keyCode string 将作为键盘事件发送的字符,只能使用合法的 Accelerator 键码(如 'a''Escape''Tab'

MouseWheelInputEvent(extends MouseInputEvent)

字段 类型 说明
type string 只能为 mouseWheel
deltaX / deltaY Integer(可选) 滚动增量
wheelTicksX / wheelTicksY Integer(可选) 滚轮刻度增量
accelerationRatioX / accelerationRatioY Integer(可选) 加速度比
hasPreciseScrollingDeltas boolean(可选) 是否为精确滚动增量
canScroll boolean(可选) 页面是否可滚动

二、实战用法:webContents.sendInputEvent()

InputEvent 结构体的主要消费入口是 webContents.sendInputEvent(inputEvent)

const { app, BrowserWindow } = require('electron');

app.whenReady().then(() => {
  const win = new BrowserWindow({ width: 800, height: 600 });
  win.focus(); // 关键:窗口必须处于聚焦状态,sendInputEvent 才能生效

  win.loadFile('index.html').then(() => {
    const wc = win.webContents;

    // 键盘:按 Ctrl+Shift+Z 组合键
    wc.sendInputEvent({ type: 'keyDown', keyCode: 'Z', modifiers: ['shift', 'ctrl'] });
    wc.sendInputEvent({ type: 'keyUp', keyCode: 'Z', modifiers: ['shift', 'ctrl'] });

    // 键盘:输入一个字符(char 事件直接产生文本)
    wc.sendInputEvent({ type: 'char', keyCode: 'a' });

    // 鼠标:在 (100, 100) 位置模拟一次左键单击
    wc.sendInputEvent({ type: 'mouseDown', button: 'left', x: 100, y: 100 });
    wc.sendInputEvent({ type: 'mouseUp', button: 'left', x: 100, y: 100 });

    // 滚轮:向下滚动
    wc.sendInputEvent({ type: 'mouseWheel', x: 100, y: 100, deltaY: -120 });
  });
});

要点说明:

  1. 参数类型inputEvent 接受 MouseInputEvent | MouseWheelInputEvent | KeyboardInputEvent 三种对象之一,它们都包含 InputEvent 基类的 type / modifiers 字段。
  2. 聚焦前提:官方文档明确提示,sendInputEvent() 生效要求包含该内容的 BrowserWindow 处于聚焦状态(见 web-contents.md 中的 NOTE)。这是使用中最容易踩的坑:无头测试或隐藏窗口场景下需先 focus()
  3. keyCode 的合法性KeyboardInputEvent.keyCode 必须使用合法的 Accelerator 键码(如 'A''Escape''Tab'),否则事件构造会失败。
  4. webview 标签页同样支持<webview>.sendInputEvent(event) 直接转发到 webContents.sendInputEventsendInputEvent 在 webview 同步方法白名单中被归类为异步方法,见 web-view-methods.ts

三、源码剖析:事件是如何被解析与分发的

3.1 type 字符串 → blink 枚举的转换

类型解析发生在 blink_converter.ccConverter<blink::WebInputEvent::Type>::FromV8 通过 BLINK_EVENT_TYPES() 宏将 JS 侧的 type 字符串逐个映射到 blink::WebInputEvent::Type 枚举(如 mouseDownkMouseDownkeyDownkKeyDown)。两个值得注意的实现细节:

  • 匹配不区分大小写:宏内使用 base::EqualsCaseInsensitiveASCII 比较,因此 'Keydown''keyDown' 等价。
  • 枚举覆盖面比 API 更大:宏表包含了上文第一组表格中的全部 40 个类型(含 gesture、touch、pointer 系列),即所有 type 取值都能被解析,解析器本身不会报错。

随后 GetWebInputEventType 从事件对象中取出 type 字段用于分发决策;Converter<blink::WebInputEvent>::FromV8 再把 modifiers 数组按位或合并为 blink::WebInputEvent::Modifiers 位掩码,并由 C++ 侧自动填充时间戳 base::TimeTicks::Now(),JS 调用者无需也不能指定时间戳。

3.2 modifiers 的名称规范与别名映射

修饰键的字符串名与位掩码的映射定义在 blink_converter.cc,分两张表:

  • 规范表(既可传入也可返回)shiftcontrolaltmetaiskeypadisautorepeatleftbuttondownmiddlebuttondownrightbuttondowncapslocknumlockleftright
  • 别名字典(只接受、不返回)cmdcommand 都映射到 metactrl 映射到 control

这说明源码层面 ctrl/command/cmd 只是为书写习惯提供的别名,事件对象序列化回 JS 时只会输出规范名。另外从 ToV8 实现 可以看出,键盘/鼠标事件会转换为对应子类对象返回,其余类型则退化为只含 typemodifiers 的普通对象——这也解释了为什么基类文档只定义这两个字段。

3.3 SendInputEvent 的三条分发路径

C++ 侧入口是 WebContents::SendInputEvent(经 第 5043 行 注册为 JS 方法 sendInputEvent)。它先解析 type,再走三条互斥路径:

路径一:鼠标事件(IsMouseEventType

转换为 blink::WebMouseEvent 后,若 WebContents 是离屏渲染(OSR)模式,走 GetOffScreenRenderWidgetHostView()->SendMouseEvent();否则调用 rwh->ForwardMouseEvent() 转发给 content::RenderWidgetHost

路径二:键盘事件(IsKeyboardEventType

构造 input::NativeWebKeyboardEvent,其中有一个向后兼容行为:如果传入 type: 'keyDown',C++ 侧会静默将其改写为 rawKeyDown 再转发(源码注释标明是为兼容旧用法,见 第 3963–3966 行)。键盘事件的 keyCode 字符串还会经 KeyboardCodeFromStr 解析为 ui::KeyboardCode,并推导 dom_code / dom_key;对 charrawKeyDown 类型,当键码对应的是可打印字符(如 '+'、空格)时会用字符本身而非键名,保证页面收到正确的文本(blink_converter.cc 第 311–319 行)。

路径三:滚轮事件(kMouseWheel

这是实现中最精巧的一处。Chromium 期望滚轮事件携带完整的相位(phase)信息并对此做 DCHECK 校验,因此非 OSR 模式下,SendInputEvent 会做如下处理(第 3976–3990 行):

  1. 把用户事件标记为 phase = kPhaseBegandispatch_type = kBlocking 后转发;
  2. 紧接着再合成一条 delta_x/delta_y 均为 0 的 kPhaseEnded 事件dispatch_type = kEventNonBlocking)以结束本次滚动序列。

也就是说,JS 侧发一条 mouseWheel 事件,C++ 侧实际向渲染进程投递了两条事件。这一补偿逻辑是页面滚动行为“一次到位”、不会停在中间相位的原因。

兜底:三条路径的 ConvertFromV8 全部失败时,会抛出 Invalid event object 异常(第 3996–3997 行)。从源码结构看,虽然 40 种 type 都能通过解析,但 SendInputEvent 只实际分发鼠标、键盘、滚轮三类;gesture/touch/pointer 类型目前会走到兜底逻辑,构造这类对象传入不会得到有效分发。

3.4 测试用例中的典型用法

仓库测试套件大量使用 sendInputEvent 驱动页面行为,可作为构造事件的真实参照:

  • api-web-contents-spec.tsdescribe('sendInputEvent(event)') 覆盖组合键({ type: 'keyDown', keyCode: 'Z', modifiers: ['shift', 'ctrl'] })、char 输入、以及 'Space'/'Plus' 这类字符键的处理;
  • api-browser-window-spec.ts:用 { type: 'keyDown', keyCode: 'Escape' } 触发页面行为;
  • chromium-spec.ts:构造 Tab / Shift+Tab 按键事件验证焦点在窗口与 webview 之间的移动;
  • autofill-spec.ts:用 Tab 键切换表单焦点后依次发送 char 事件填写自动补全字段。

这些用例共同印证了实战规律:键盘输入通常由 keyDown + char + keyUp 组合模拟,点击由 mouseDown + mouseUp 成对模拟

四、使用注意事项小结

  1. 窗口必须先聚焦BrowserWindow 未聚焦时 sendInputEvent 可能不产生效果,自动化脚本中记得在发送前 win.focus()
  2. keyCode 必须合法:只能使用 Accelerator 支持的键码字符串,char 类型用于向可编辑元素输入文本,keyDown/keyUp 用于触发按键处理逻辑。
  3. keyDown 会被改写为 rawKeyDown:从源码看这是刻意的向后兼容行为,依赖 e.type === 'keydown' 的页面逻辑需注意区分。
  4. 修饰键别名ctrlcommandcmd 可自由书写,但规范名是 controlmeta;事件序列化回 JS 时只会返回规范名。
  5. 滚轮事件是成对投递的:单条 mouseWheel 输入会在 C++ 侧展开为 kPhaseBegan + 合成 kPhaseEnded 两条事件,页面收到的滚动是完整闭环的。
  6. 坐标语义x/y 是页面坐标,globalX/globalY 是屏幕全局坐标,两者不可混用。

通过本文,你可以完整掌握 InputEvent 基类与三个子类的全部字段、sendInputEvent 的正确调用姿势,以及从 JS 对象到 blink::WebInputEvent 的解析与分发链路,从而在 Electron 应用开发与自动化测试中可靠地构造合成输入事件。

登录后查看全文
热门项目推荐
相关项目推荐