Electron 深入解析 MouseInputEvent:用 webContents.sendInputEvent 模拟鼠标输入事件
MouseInputEvent 是 Electron 中用于描述鼠标输入事件的 API 结构体,它是 webContents.sendInputEvent() 方法可接受的输入类型之一。在 UI 自动化、端到端测试、远程操控桌面应用等场景中,通过主进程向渲染进程页面"注入"鼠标事件是核心能力。读完本文,你将掌握 MouseInputEvent 的完整字段定义、继承体系、事件类型与修饰键组合方式,并能基于 Electron 源码理解一条鼠标事件从主进程 JS 对象到 Blink WebMouseEvent 再到渲染进程页面的完整投递链路。
MouseInputEvent 对象定义
MouseInputEvent 对象继承自 InputEvent,定义见 mouse-input-event.md,完整字段如下:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type |
string | 是 | 事件类型,可取 mouseDown、mouseUp、mouseEnter、mouseLeave、contextMenu、mouseWheel 或 mouseMove |
x |
Integer | 是 | 事件在页面(视口)中的 X 坐标 |
y |
Integer | 是 | 事件在页面(视口)中的 Y 坐标 |
button |
string | 否 | 按下的鼠标按钮,可取 left、middle、right |
globalX |
Integer | 否 | 全局坐标 X |
globalY |
Integer | 否 | 全局坐标 Y |
movementX |
Integer | 否 | 相对上一次鼠标移动事件的 X 位移 |
movementY |
Integer | 否 | 相对上一次鼠标移动事件的 Y 位移 |
modifiers |
string[] | 否 | 修饰键数组,继承自 InputEvent(见下文) |
几点使用要点:
type的 7 个取值覆盖了鼠标交互的全部形态:按下(mouseDown)、抬起(mouseUp)、进入/离开元素(mouseEnter/mouseLeave)、右键上下文菜单(contextMenu)、滚轮(mouseWheel)以及移动(mouseMove)。注意mouseWheel类型建议改用其子类MouseWheelInputEvent,它会补充滚轮专属字段。x/y是相对于页面视口的坐标,因此必须与目标页面当前渲染尺寸匹配;globalX/globalY则用于需要屏幕级坐标的场景。clickCount用于表达单击/双击/连击次数,配合mouseDown+mouseUp可模拟完整点击序列。
父类 InputEvent:type 与 modifiers
MouseInputEvent 继承自 InputEvent,其中 modifiers 字段是一个字符串数组,可取:
shift、control、ctrl、alt、meta、command、cmd、iskeypad、isautorepeat、leftbuttondown、middlebuttondown、rightbuttondown、capslock、numlock、left、right。
前 7 个是按键修饰符(ctrl 是 control 的别名、cmd 是 command 的别名),leftbuttondown 等三个用于表达"发送本事件时其他鼠标按钮正处于按下状态"——例如模拟按住左键拖拽时发送 mouseMove。
子类 MouseWheelInputEvent
MouseWheelInputEvent 继承自 MouseInputEvent,type 固定为 mouseWheel,并额外提供滚轮事件专属字段:
| 字段 | 类型 | 说明 |
|---|---|---|
deltaX / deltaY |
Integer | 水平和垂直滚动增量 |
wheelTicksX / wheelTicksY |
Integer | 传统滚轮刻度值 |
accelerationRatioX / accelerationRatioY |
Integer | 加速度比例 |
hasPreciseScrollingDeltas |
boolean | 是否为精确滚动增量(触控板平滑滚动场景) |
canScroll |
boolean | 当前是否可滚动 |
键盘输入则对应另一个平级结构 KeyboardInputEvent,与鼠标事件共享同一个 sendInputEvent() 入口。
实际使用:webContents.sendInputEvent
MouseInputEvent 最主要的使用方式是作为 contents.sendInputEvent(inputEvent) 的参数,该方法向页面发送一个输入事件,可接受的类型为 MouseInputEvent | MouseWheelInputEvent | KeyboardInputEvent。
文档中明确给出了一个重要前提:
包含该 contents 的
BrowserWindow必须处于聚焦状态(focused),sendInputEvent()才能生效。
一个模拟完整左键点击的示例(参考 api-web-contents-spec.ts 中的测试写法):
// 主进程:在页面 (100, 100) 位置模拟一次左键点击
w.webContents.sendInputEvent({ type: 'mouseDown', button: 'left', x: 100, y: 100 });
w.webContents.sendInputEvent({ type: 'mouseUp', button: 'left', x: 100, y: 100 });
模拟带修饰键的右键上下文菜单事件:
// 触发 contextmenu 事件
w.webContents.sendInputEvent({ type: 'contextMenu', button: 'right', x: 200, y: 150 });
模拟滚轮滚动(使用 MouseWheelInputEvent 扩展字段):
// 向下滚动 3 个滚轮刻度
w.webContents.sendInputEvent({
type: 'mouseWheel',
x: 300,
y: 300,
deltaX: 0,
deltaY: -3,
wheelTicksX: 0,
wheelTicksY: -3
});
该能力同样适用于 <webview> 标签:sendInputEvent 被列入了 web-view-methods.ts 的 webview 方法白名单,因此 webview.sendInputEvent(...) 与 webContents.sendInputEvent(...) 行为一致,详见 webview-tag.md。
源码级原理:一条鼠标事件的投递链路
Electron 在主进程侧的 C++ 实现位于 electron_api_web_contents.cc,方法在 V8 绑定处通过 .SetMethod("sendInputEvent", &WebContents::SendInputEvent) 注册(见 electron_api_web_contents.cc#L5043)。
从 SendInputEvent 的实现可以看到鼠标事件的处理流程:
- 解析事件类型:先通过
gin::GetWebInputEventType将 JS 对象中的type字符串解析为blink::WebInputEvent::Type枚举; - 鼠标事件分支:当
IsMouseEventType(type)为真时,用 gin 的转换机制把 JS 对象整体反序列化为blink::WebMouseEvent(x、y、button、clickCount、modifiers等字段在此完成映射),然后进入两条投递路径之一:- 若该
WebContents是离屏渲染(OffScreen Rendering)模式,则调用GetOffScreenRenderWidgetHostView()->SendMouseEvent(mouse_event); - 否则调用 Chromium 的
rwh->ForwardMouseEvent(mouse_event),即通过RenderWidgetHost把事件转发给渲染进程;
- 若该
- 滚轮事件分支:
kMouseWheel类型会反序列化为blink::WebMouseWheelEvent。源码中有一段值得注意的细节——由于 Chromium 要求滚轮事件携带 phase 信息(并以此做了 DCHECK 校验),Electron 会先补上phase = kPhaseBegan并以阻塞方式ForwardWheelEvent,随后再合成一个kPhaseEnded的收尾事件来完成本次滚动手势。这正是上面MouseWheelInputEvent示例中不需要手动传 phase 的原因。
也就是说,文档层面的 x/y/button/clickCount 等字段,最终都对应到 Blink 原生输入事件对象的同名属性,再经由 Chromium 的输入转发机制进入页面的 JavaScript 事件循环,被 click、mousedown、contextmenu 等标准 DOM 事件监听器捕获。测试文件 api-web-contents-spec.ts 中 describe('sendInputEvent(event)') 以及多处 { type: 'mouseDown', button: 'left', x: 100, y: 100 } 形式的用例,正是对这一链路的持续回归验证。
实践注意事项
- 窗口必须聚焦:这是文档明确声明的运行前提,自动化脚本中发送事件前应确保目标
BrowserWindow已focus(); - 事件顺序要符合真实手势:
mousedown→mouseup成对出现;双击需以clickCount: 2的第二次mouseDown表达;拖拽需在mouseMove中携带leftbuttondown修饰符; - 坐标基准是视口:
x/y相对页面视口而非屏幕,跨分辨率测试时注意与窗口实际尺寸一致; - 离屏渲染可用:源码中 OffScreen 分支独立存在,说明该 API 同样支持
offscreen模式的WebContents,这对无窗口自动化尤其有用。
相关文档
- InputEvent —— 输入事件基类(
type全集与modifiers取值) - MouseWheelInputEvent —— 滚轮事件扩展结构
- KeyboardInputEvent —— 键盘输入事件结构
- webContents.sendInputEvent() —— 发送输入事件的 API
- WebContents C++ 实现 ——
SendInputEvent事件转发实现
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00