首页
/ Puppeteer Browser.addScreen 详解:在 headless 模式下动态扩展虚拟屏幕

Puppeteer Browser.addScreen 详解:在 headless 模式下动态扩展虚拟屏幕

2026-09-04 20:12:43作者:蔡怀权

本文基于 Puppeteer 官方 API 文档 docs/api/puppeteer.browser.addscreen.md,详解 Browser.addScreen() 方法的签名、AddScreenParams 全部参数、返回值 ScreenInfo 的字段含义,并结合仓库源码还原其 CDP(Chrome DevTools Protocol)底层调用链,给出可在 headless 模式下直接复制运行的多屏模拟示例(含配合 windowBounds 将窗口放到副屏、最大化验证、以及 removeScreen 清理的完整测试用例)。

一、方法定位与签名

addScreen()Browser 类上的抽象方法,作用是向当前受控浏览器新增一块虚拟屏幕,并返回所添加屏幕的信息对象。其 TypeScript 签名为:

class Browser {
  abstract addScreen(params: AddScreenParams): Promise<ScreenInfo>;
}
  • 参数params,类型为 AddScreenParams,描述新屏幕的几何位置、尺寸、色彩深度等属性;
  • 返回值Promise<ScreenInfo>,即 ScreenInfo 接口对象,包含该屏幕的完整信息(包括系统生成的 idisPrimaryavailWidth 等);
  • 重要限制(官方 Remarks 原文)Only supported in headless mode——仅支持无头模式。在 headed(有头)模式下调用没有意义,因为屏幕由操作系统真实提供;多屏能力只有在 headless 环境中才是由 Chromium 自身模拟出来的。

api/Browser.ts 中,addScreenscreens()removeScreen() 构成同一组屏幕管理 API:

// packages/puppeteer-core/src/api/Browser.ts
/**
 * Adds a new screen, returns the added screen information object.
 *
 * @remarks
 * Only supported in headless mode.
 */
abstract addScreen(params: AddScreenParams): Promise<ScreenInfo>;

/**
 * Removes a screen.
 *
 * @remarks
 * Only supported in headless mode. Fails if the primary screen id is specified.
 */
abstract removeScreen(screenId: string): Promise<void>;

由此可以确认两点:新增屏幕后可用 screens() 枚举全部屏幕;删除屏幕用 removeScreen(screenId),且主屏 id 不允许删除

二、AddScreenParams 参数逐项说明

AddScreenParams 接口定义于 api/Browser.ts(接口文档见 AddScreenParams):

/**
 * @public
 */
export interface AddScreenParams {
  left: number;
  top: number;
  width: number;
  height: number;
  workAreaInsets?: WorkAreaInsets;
  devicePixelRatio?: number;
  rotation?: number;
  colorDepth?: number;
  label?: string;
  isInternal?: boolean;
}

/**
 * @public
 */
export interface WorkAreaInsets {
  top?: number;
  left?: number;
  bottom?: number;
  right?: number;
}

各字段含义与约束如下(必填/可选与源码修饰符严格一致):

参数 类型 是否必填 说明
left number 必填 新屏幕左上角相对虚拟桌面原点的水平偏移(像素)。典型用法是紧接主屏右侧,如主屏宽 800 时传 left: 800
top number 必填 新屏幕左上角的垂直偏移(像素),通常副屏传 0
width number 必填 屏幕宽度(像素)
height number 必填 屏幕高度(像素)
workAreaInsets WorkAreaInsets 可选 工作区四边内缩,模拟任务栏/ dock 等遮挡区域。四边 top/left/bottom/right 均为可选 number。设置后 availWidth/availHeight 会相应减小
devicePixelRatio number 可选 设备像素比(DPR),用于模拟高 DPI 屏幕;未设置时 Chromium 默认返回 1
rotation number 可选 屏幕旋转角度,用于模拟竖屏/横屏方向
colorDepth number 可选 色深,如 2432,对应屏幕返回的 colorDepth
label string 可选 屏幕标签名,用于在 screens() 结果中区分屏幕,如 'secondary'
isInternal boolean 可选 是否内部屏幕,对应返回对象的 isInternal 字段

从源码结构看,workAreaInsets 的实际效果在测试断言中得到印证:屏幕高 1200 且 workAreaInsets: {bottom: 80} 时,返回的 availHeight1120(1200 − 80),即“可用高度”已扣除底部 80 像素的任务栏区域。

三、返回值 ScreenInfo 字段

addScreen() 返回的 ScreenInfo 对象描述新屏幕的最终状态,关键字段:

字段 类型 说明
id string 屏幕唯一标识,后续 removeScreen(screenId) 必须使用它
left / top number 屏幕左上角坐标,与入参一致
width / height number 屏幕宽高,与入参一致
availLeft / availTop / availWidth / availHeight number 扣除 workAreaInsets 后的工作区坐标与尺寸,是把窗口精确放进该屏幕时的可靠依据
devicePixelRatio number 设备像素比,未指定时默认 1
colorDepth number 色深,默认 24
isPrimary / isInternal / isExtended boolean 主屏/内部屏/扩展屏标志;addScreen 新增的屏幕恒为 isPrimary: falseisExtended: true
label string 入参中的屏幕标签
orientation ScreenOrientation 屏幕方向,包含 angletype(如 {angle: 0, type: 'landscapePrimary'}),见 ScreenOrientation

注意:ScreenInfo 中的 idisPrimaryisExtendedorientationavail* 字段均为 Chromium 自动生成,AddScreenParams 并不直接接收——入参负责“怎么放这块屏”,返回对象负责“这块屏最终的完整描述”。

四、底层实现:CDP 调用链

在 CDP 实现中,addScreen 只是一次对浏览器级 DevTools 会话的 Emulation.addScreen 命令转发,实现在 cdp/Browser.ts

override async screens(): Promise<ScreenInfo[]> {
  const {screenInfos} = await this.#connection.send(
    'Emulation.getScreenInfos',
  );
  return screenInfos;
}

override async addScreen(params: AddScreenParams): Promise<ScreenInfo> {
  const {screenInfo} = await this.#connection.send(
    'Emulation.addScreen',
    params,
  );
  return screenInfo;
}

override async removeScreen(screenId: string): Promise<void> {
  return await this.#connection.send('Emulation.removeScreen', {screenId});
}

可以确认:

  1. AddScreenParams 原样作为 Emulation.addScreen 的参数发送给浏览器进程,Puppeteer 层不做数值换算;
  2. 返回值为 CDP 响应中的 screenInfo 字段,直接作为 ScreenInfo 透出;
  3. 屏幕增删查三个方法对应 CDP 命令一一对应:Emulation.addScreen / Emulation.removeScreen / Emulation.getScreenInfos

另外,该能力仅在 CDP(Chrome DevTools Protocol)连接下可用。在 BiDi 实现的 bidi/Browser.ts 中,addScreen(连同 screensremoveScreen)直接抛出 UnsupportedOperation

override screens(): Promise<ScreenInfo[]> {
  throw new UnsupportedOperation();
}

override addScreen(_params: AddScreenParams): Promise<ScreenInfo> {
  throw new UnsupportedOperation();
}

因此使用前提可归纳为:Chrome + CDP 连接 + headless 模式

五、实战示例:新增副屏、放置窗口与清理

以下示例直接取自仓库测试套件 test/src/browser.test.tsBrowser.add|removeScreen 用例,展示了完整的“添加 → 校验 → 删除”闭环:

// 1. 在主屏(800x600)右侧新增一块 1600x1200 的副屏,底部预留 80px 任务栏
const screenInfo = await browser.addScreen({
  left: 800,        // 紧贴主屏右边缘
  top: 0,
  width: 1600,
  height: 1200,
  colorDepth: 32,
  workAreaInsets: {bottom: 80},
  label: 'secondary',
});

// 2. 校验返回对象:availHeight = 1200 - 80 = 1120,isExtended 为 true
//    注意 id 由浏览器生成,用 screenInfo.id 记录以便后续删除
expect(screenInfo.isExtended).toBe(true);
expect(screenInfo.isPrimary).toBe(false);
expect(screenInfo.availHeight).toBe(1120);

// 3. screens() 应返回两块屏幕(主屏 + 新副屏)
expect((await browser.screens()).length).toBe(2);

// 4. 清理:按 id 删除副屏
await browser.removeScreen(screenInfo.id);
expect((await browser.screens()).length).toBe(1);

典型场景:把浏览器窗口开到副屏并最大化

addScreen 返回的 availLeft/availTop/availWidth/availHeightBrowserContext.newPage({type: 'window', windowBounds}) 配合,可以精确控制新窗口落在哪块屏幕上。同样的测试 test/src/browser.test.ts 演示了“副屏最大化窗口”流程:

// 添加副屏
const screenInfo = await browser.addScreen({
  left: 800,
  top: 0,
  width: 1600,
  height: 1200,
});

// 用返回的 avail* 字段把窗口放到副屏内(四周留 50/100 边距)
const page = await context.newPage({
  type: 'window',
  windowBounds: {
    left: screenInfo.availLeft + 50,
    top: screenInfo.availTop + 50,
    width: screenInfo.availWidth - 100,
    height: screenInfo.availHeight - 100,
  },
});

// 通过 page.windowId() 拿到窗口 id,再最大化(windowState: 'maximized')
const windowId = await page.windowId();
await browser.setWindowBounds(windowId, {windowState: 'maximized'});

// 清理
await browser.removeScreen(screenInfo.id);

窗口边界 API(getWindowBounds / setWindowBounds)的文档分别见 Browser.getWindowBoundsBrowser.setWindowBounds,屏幕枚举见 Browser.screens

返回对象的默认值速查

从测试断言可以整理出各字段的实际默认行为,便于编写断言或做结果校验:

场景 主屏(headless 默认 800x600) 新增副屏(left:800, 1600x1200, insets.bottom:80)
availHeight 600 1120(1200 − 80)
availLeft / availTop 0 / 0 800 / 0
colorDepth 24(未指定时的默认值) 32(按入参)
devicePixelRatio 1 1(未指定时的默认值)
isPrimary / isExtended true / false false / true
orientation {angle: 0, type: 'landscapePrimary'} {angle: 0, type: 'landscapePrimary'}

六、小结

  • Browser.addScreen(params: AddScreenParams): Promise<ScreenInfo> 是 Puppeteer 在 headless 模式下构造虚拟多显示器环境的入口:left/top/width/height 四个必填项决定屏幕摆放,workAreaInsetsdevicePixelRatiorotationcolorDepthlabelisInternal 均为可选修饰项;
  • 返回值 ScreenInfo 中的 id 是后续 removeScreen 的唯一句柄,avail* 四字段是跨屏放置窗口的可靠坐标来源;
  • 源码层面它是一次纯粹的 CDP Emulation.addScreen 转发(cdp/Browser.ts),BiDi 连接下不可用(抛 UnsupportedOperation);
  • 典型应用是多屏布局测试、副屏窗口定位与最大化行为验证,完整可运行用例可直接参考 test/src/browser.test.ts
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341