Screen Capture API:用户控制与隐私边界
使用 getDisplayMedia 实现明确选择、可见提示、撤销和隐私边界,安全处理屏幕、窗口与标签页捕获。
BotBrowser Team
BotBrowser 可以在声明的 profile 中重复经过授权的 getDisplayMedia() 流程,并验证取消、停止和 ended 状态;但不能替用户选择屏幕、隐藏浏览器提示,也不能让捕获的像素对接收它的页面保持私密。用户控制与隐私边界不同于虚拟摄像头隐私以及Document Picture-in-Picture 权限边界。
TL;DR
Screen Capture API 的访问需要用户选择。应从可见操作发起请求,说明将共享什么,任务结束时停止每条 track,并把流视为敏感应用数据。getDisplayMedia() 不是静默截图 API、永久权限,也不是捕获表面私密性的证明。
Contents
捕获契约
应用应在用户操作(例如点击)后、临时用户激活仍然有效时调用 navigator.mediaDevices.getDisplayMedia()。从计时器或后台消息调用可能以 InvalidStateError 拒绝。浏览器会显示显示器、窗口或标签页选择器;代码不能静默指定表面。返回的 MediaStream 包含 track,其生命周期应对用户可见。
选择器是控制边界,不是身份信号。NotAllowedError 可能表示取消、策略限制或浏览器决定。只记录恢复界面所需的结果,不要记录完整能力对象、屏幕名称、文档文本或持久设备标签。
捕获像素仍是应用数据。不要因为捕获成功就上传流。应像处理摄像头或麦克风一样执行访问控制、保留期限、脱敏与同意管理。浏览器提示只说明共享正在进行,并不使接收方可信。
隐私感知测试矩阵
使用合成内容,每行使用新 profile,并明确记录用户操作、来源、约束与停止动作。
| 行 | 流程 | 预期观察 | 隐私含义 |
|---|---|---|---|
| A | 点击共享后取消选择器 | Promise 以受控错误拒绝 | 用户拒绝选择,未授予表面 |
| B | 选择合成标签页 | 有视频 track 且出现共享提示 | 标签页像素成为页面数据 |
| C | 从停止按钮调用 track.stop() | track 结束,界面回到空闲 | 应用主动结束了捕获 |
| D | 关闭共享标签页或在浏览器中撤销 | 触发 ended 或流变为非活动 | 用户或浏览器撤销了访问 |
| E | 没有激活时请求 | 请求被拒绝 | 后台脚本不能静默启动捕获 |
紧凑结果可以是 {"browser":"Chrome 155","mode":"headed","surface":"synthetic-tab","result":"started","stopped":true,"capturedBytes":null}。除非有独立且受治理的私有测量,否则保持 capturedBytes 为 null。公开 artifact 应证明生命周期,而不是保存用户屏幕。
撤销与安全清理
为每条 track 添加 ended 监听器,并提供可见的停止控件。浏览器界面、导航、标签页关闭或策略也可能结束流。清除引用、移除预览,并将焦点返回发起控件。隐藏预览并不会停止源。
如果使用 recorder,应在释放流前关闭它,并将 dataavailable 视为敏感输出。自动化测试优先使用短片段、明确的保留策略和合成 fixture。不要把原始帧、OCR、账户数据或完整屏幕名称写入日志。
与相邻媒体 API 的差异
媒体设备与虚拟摄像头隐私讨论 enumerateDevices()、摄像头和麦克风权限,以及虚拟设备列表边界。Screen Capture 不会把屏幕枚举为稳定设备,也不会静默选择它,而是展示新的用户选择并返回显示流。
Document Picture-in-Picture 权限边界创建由浏览器管理的 same-origin 窗口,但不会暴露另一表面的像素。Screen Capture 将选定像素传入 MediaStream,因此数据最小化和撤销处理是核心。
不要把这些 API 合并成一个“媒体访问”权限。它们各自有不同的用户手势、浏览器中介、失败模式和隐私审查。Picture-in-Picture 成功不代表支持屏幕捕获,虚拟摄像头结果也不代表用户选择了某个显示器。
BotBrowser 能控制什么
BotBrowser 支持在合成页面、声明的浏览器版本和新 profile 中重复 Screen Capture 按钮流程;它可以验证取消、测试标签页、track.stop() 和 ended。它不能替真实用户选择屏幕、改变浏览器选择器、隐藏共享提示,也不能保证所有操作系统行为完全一致。
实践结论
把 Screen Capture API 当作由用户控制、可撤销且包含敏感数据的流。以可见操作开始,先描述表面,使用合成测试,停止所有 track,只保留受控生命周期结果。隐私边界由浏览器中介、应用清理和有纪律的像素处理共同构成。
便于支持排查的生命周期
这个契约有四个可观察锚点:getDisplayMedia()、MediaStreamTrack.readyState、ended 和 track.stop()。同时记录 displaySurface、嵌入 iframe 的 Permissions-Policy 值和浏览器版本。被拒绝的请求不会返回 MediaStream;已启动的请求会让视频 track 保持 live,直到 stop() 或 ended 改变状态。
嵌入测试必须分开验证策略分支。display-capture 的默认允许列表是 self,因此同源 iframe 可能获得资格;跨源 iframe 需要显式委托并写明合作方来源,例如 display-capture=(self) example.test。不要假定缺少委托必然拒绝;应把来源、声明的策略、激活结果和 track 状态放在不同测试行。
便于支持排查的生命周期。
支持记录应从选择器打开前开始:记录可见控件、来源、浏览器版本、系统和临时 profile。应明确预期结果:用户选择合成标签页,页面收到视频 track,共享提示保持可见,停止控件结束 track。这样支持人员能处理“屏幕共享失败”,而无需索取屏幕内容。
第一条负面路径是取消。用户可以打开选择器后决定不共享任何内容。应将其视为正常结果,保留原任务并简短说明,不要自动重试。重复提示可能把明确拒绝变成骚扰。若指标用途已有记录,聚合数据只需保留 cancelled 这样的受控结果。
第二条负面路径是缺少激活。书签、定时器或后台消息加载的页面不能在没有当前用户手势时启动选择器。用合成页面测试该路径并记录拒绝类别。不要通过模拟点击或更宽权限来修复;正确的产品响应是提供解释清楚的可见控件。
成功路径也需要终态。预览可以在流活动期间保持可见,但隐藏预览不等于停止捕获。让同一对象管理所有 track,对有意创建的每条视频和音频 track 调用 stop(),并在状态变化时更新控件。如果浏览器先结束 track,就让 ended 处理同一套清理逻辑,避免页面错误地显示仍在共享。
减少跨越边界的数据。
捕获内容可能包含私人消息、账户页面、通知或无关窗口。最安全的 fixture 是一个带有“测试表面”大标签的简单合成页面,没有账户状态。在真实产品中,要在选择器打开前说明将选择的表面,并把接收方限制在当前任务。只有用户理解效果时才裁剪、脱敏或模糊内容;隐藏预览不会从流中移除像素。
不要向分析系统发送序列化 MediaStream、屏幕标题、完整能力对象或原始帧。requested、cancelled、started、stopped、ended 这些事件通常足以回答可靠性问题。需要录制时使用独立数据路径、访问控制、保留期限和删除负责人。公开文档使用合成示例,把内容字段保持为 null。
安全比较浏览器变化。
如果版本变化带来不同结果,应先重复同一个合成 fixture,再修改应用代码。保持来源、激活方式、请求表面、profile 生命周期和停止动作不变。比较第一个变化字段:选择器、流创建、track 状态、共享提示或清理。单一成功/失败标签无法定位边界。
有界面运行与自动化运行可能因显示配置、策略或激活时机而不同。把这些变量写在结果开头,不要把单个 runner 的观察扩大为所有操作系统的结论。BotBrowser 可以重复声明的上下文,但浏览器控制选择器,用户作出选择。
简明的隐私审查。
发布前回答五个问题:控件是否可见且支持键盘?文案是否说明目标是标签页、窗口还是显示器?取消后任务是否保留?每条退出路径是否停止 track 并移除预览?日志是否排除帧、屏幕名称、账户标识和无关 profile 字段?这些检查足以放进一次 pull request,避免意外保留捕获内容。
支持流程也应执行同样的审查。只请求浏览器版本、可见结果、首个失败生命周期行,以及用户取消还是浏览器结束了 track。合成复现能够回答浏览器问题时,不要索取客户屏幕录制。若私有证据不可避免,应通过受限渠道收集最少内容,并按既定时间表删除。
保护用户的实现细节。
把支持记录保存为有界的一行,包括 displaySurface、浏览器构建版本、激活来源和首个终止状态。将请求结果与原因分开:策略拒绝不等于浏览器不支持显示捕获,live track 也不等于保留像素安全。
让请求靠近解释它的控件。“共享测试标签页”比“开始”更明确,尤其是在产品还提供摄像头或麦克风时。说明可能的目标以及浏览器会显示选择器;不要承诺应用能限制所选标签页中的所有内容。用户对所选表面负责,应用对收到的 MediaStream 负责。
预览应有清晰状态和可键盘操作的停止按钮。“正在共享所选测试标签页”比只靠颜色的标记更好。track 结束时播报变化并恢复任务。把录制状态与共享状态分开:流可以可见但不录制,录制也可以停止而预览仍活动。ended 事件是具体的生命周期锚点。
嵌入页面中的 Permissions Policy 可能改变流程。顶层页面能请求显示捕获时,iframe 仍可能无权请求。把嵌入和顶层情况作为不同测试行,记录声明的策略,并在页面内解释拒绝。不要为了 fixture 通过而放宽全站策略;这是独立的产品和安全决策,应有负责人,并在测试日期旁记录策略值。
接收方也需要有界契约。如果通过 peer connection 发送流,应记录发送哪些 track、何时移除以及如何通知远端结束。如果只在本地渲染,应让预览元素与 track 由同一生命周期所有者管理。停止后,远端连接不能成为保留帧的借口;停止事件应释放本地引用并结束任务。
自动化验证使用绘制已知图案和测试标签的页面,不要使用桌面或客户账户。runner 可以验证视频 track 存在、合成标签页尺寸非零以及停止后 track 进入 ended。回答生命周期不需要检查像素。记录 readyState、track.kind 和停止时间,不保存图像。
浏览器拒绝请求时,应保留原始操作并提供上传、静态导出或页面说明等受支持的替代方式。替代路径不能静默切换到摄像头或麦克风;每种媒体源有不同预期,未解释的切换会造成隐私回退。
当浏览器选择器、Permissions Policy 变化,或应用加入 recorder、远端传输时,重新检查矩阵。把测试日期和浏览器版本放在结果旁;如果结果变化,记录首个变化行和下一位负责人。不要把合成标签页结论扩大到所有屏幕、窗口、账户或操作系统。
解读结果时不要过度推断。
MediaStream 契约是支持判断的具体锚点。已启动的流只能证明声明的浏览器和用户流程为该合成表面创建了流;不能证明其他表面可用、其他来源会得到相同结果,或内容可以安全保留。停止 track 只能证明该 track 已清理,不能擦除其他地方已写入的副本。ended 只能证明应用观察到终止,不能说明是浏览器还是用户导致的。
在发布说明和翻译页面中,把操作、可观察结果和限制放在一起。这样也能避免混淆相邻 API:虚拟摄像头描述媒体设备,Document Picture-in-Picture 描述辅助展示窗口,Screen Capture 描述用户选择的显示流。每种 API 都有独立的控制和隐私契约。
来源
本文属于指南: Web 平台能力面:一致性复核地图