活动窗口仿真与页面可见性状态
比较 BotBrowser headless 与 headful 会话中的 focus、blur、visibilityState 和 document.hidden,不把 active 状态当作真人存在。
BotBrowser Team
启用 --bot-always-active 后,BotBrowser 可以让声明的浏览器上下文保持活动窗口行为,例如 document.visibilityState === "visible" 和 document.hidden === false。这项能力不等于桌面前台窗口、真人、身份或物理屏幕一定在前台。活动窗口文档 介绍 flag 和默认值;页面同时给出 focus/blur 与 Page Visibility 的语义区别、headless/headful 对照 fixture 以及最小化隐私证据。更大的指纹边界见浏览器指纹基础。
TL;DR
focus和blur描述窗口或元素焦点变化;visibilitychange、document.visibilityState和document.hidden描述文档可见性。--bot-always-active在 BotBrowser PRO 及以上保持受保护的 active 模式,但 active 不代表真人、唯一身份、可信任或保证前台。- 用相同 profile、页面和 fixture 比较 headless 与 headful。只记录状态、事件计数和有界结果,不记录页面内容或账户标识。
- 普通浏览器可能在后台标签、最小化窗口或不可见文档中报告
hidden,具体事件顺序由浏览器和平台决定。 - flag 不可用或显式关闭时,应保留普通可见性结果和产品 fallback,不要伪造真人活动结论。
Contents
- 焦点不是页面可见性
- 活动窗口仿真改变了什么
- 构建三模式对照 fixture
- 读取 visibilityState 与 document.hidden
- 有意使用 flag 与备用路径
- 最小化证据和隐私主张
- 定位第一个差异字段
- Practical conclusion
焦点不是页面可见性
W3C Page Visibility 2 规范 定义可见性模型和 visibilitychange 事件。它回答文档是否对浏览器可见,不回答是否有人在看、窗口是否接收键盘或显示器是否开启。MDN Page Visibility API 也把后台标签和最小化窗口视为生命周期变化。
focus 与 blur 属于另一层。窗口焦点决定输入接收者,元素焦点决定哪个控件接收键盘。窗口失焦不一定让文档隐藏;可见文档也可能没有焦点元素。因此每个事件都只是观察值,不是注意力、身份或授权证明。
需要比较显示上下文时,可把本 fixture 与设备像素比策略及屏幕和视口属性配合使用;前者说明几何配置,当前页面聚焦生命周期状态。
| 信号 | 浏览器含义 | 不能证明 |
|---|---|---|
focus / blur | 窗口或焦点元素改变焦点 | 真人注意力、身份或物理前台 |
visibilitychange | 文档可见性状态发生变化 | 显示器电源、用户意图或阅读行为 |
document.visibilityState | 文档当前状态,例如 visible 或 hidden | 所有浏览器通用的平台状态 |
document.hidden | 文档可见性的布尔视图 | 真人存在或可信任 |
相对活动窗口文档,这里增加了每个信号的含义和可复现验证;文档页面本身仍负责配置说明。
活动窗口仿真改变了什么
启用 --bot-always-active 时,即使宿主窗口失去焦点,BotBrowser 也会维持受保护的活动窗口行为。文档说明 PRO 默认启用,并且 headless、headful 都适用。这是授权上下文的浏览器行为,不是关于真人或桌面的报告。
页面可能看到 visibilityState 保持 visible、hidden 保持 false,且宿主失焦时没有可见性转换。焦点事件仍有自己的时序,所以 fixture 应记录实际读取的值。该 flag 不会补充缺失 API、修复应用状态机,也不保证物理窗口处于前台。Document.visibilityState 是快照;visibilitychange 是变化通知。
构建三模式对照 fixture
准备同源页面和三种声明模式:启用仿真的 headless、启用仿真的 headful、以及关闭 flag 后主动失焦的 headful 标签。固定 profile、浏览器版本和页面 revision。下面脚本只保存有界浏览器观察结果。
const result = {
fixtureId: 'visibility-boundary-01',
flag: 'unknown',
visibilityState: document.visibilityState,
hidden: document.hidden,
focusEvents: 0,
blurEvents: 0,
visibilityChanges: 0,
outcome: 'initial',
};
window.addEventListener(
'focus',
() => {
result.focusEvents += 1;
},
{ passive: true }
);
window.addEventListener(
'blur',
() => {
result.blurEvents += 1;
},
{ passive: true }
);
document.addEventListener('visibilitychange', () => {
result.visibilityChanges += 1;
result.visibilityState = document.visibilityState;
result.hidden = document.hidden;
});
window.sampleVisibility = flag => ({
...result,
flag,
visibilityState: document.visibilityState,
hidden: document.hidden,
outcome: 'sampled',
});
| 模式 | Flag | 宿主动作 | 预期记录形状 |
|---|---|---|---|
| Headless | enabled | 没有桌面窗口 | visible、false、有界事件 |
| Headful | enabled | 让宿主窗口失焦 | visible、false、有界事件 |
| Headful | disabled | 切换到另一个标签 | 按浏览器上下文得到 hidden 或 visible |
最后一行故意保留条件:runner 无法执行失焦时,应记录 action-unavailable,不能伪造 hidden。BotBrowser 支持 active-window 仿真 fixture 的回放与比较,但 BotBrowser 不能证明真人存在或物理窗口状态;证据只说明该 profile 和上下文。生命周期问题可另看长时间浏览器上下文韧性。
读取 visibilityState 与 document.hidden
在 visibilitychange 处理器中读取 document.visibilityState;document.hidden 是布尔形式。不要使用页面加载时缓存的值决定是否恢复任务。任务 owner 必须阻止可见转换重新唤醒已经结束的任务。
let taskActive = false;
function readVisibility() {
return {
state: document.visibilityState,
hidden: document.hidden,
};
}
document.addEventListener('visibilitychange', () => {
const current = readVisibility();
if (current.hidden) pauseOwnedTask();
else if (taskActive) resumeOwnedTask();
});
这段代码表达任务归属,不是所有产品都必须暂停。播放器可以遵循用户偏好,编辑器则应保留本地保存。focusEvents、blurEvents 和 visibilityChanges 必须作为不同计数记录。
Headless、headful 与未聚焦标签
Headless 和 headful 是运行模式,不是有人在场的状态。Headful 窗口可能被遮挡或最小化,未聚焦标签在某些布局中仍然可见。用同一 profile 运行,并记录模式、宿主动作、观测值和结果。visible 不证明有人观看;hidden 也不说明是谁切换了标签。
Page Visibility API 指南 还说明可见性会影响后台任务和定时器。不要把时间差变成身份分类器;测试应用结果和清理路径,而不是假定的人类毫秒数。
有意使用 flag 与备用路径
--bot-always-active=false 是普通可见性控制组。解释差异前先确认 entitlement、实际参数和 profile。分别记录 flagRequested、flagEffective 和 fallback。BotBrowser 可以回放授权 fixture 并观察结果,但不能证明真人存在或物理窗口状态。flag 不可用时,让浏览器管理事件,并保留暂停、保存、重试等正常交互。
高级功能参考记录了受保护模式。这是授权上下文的一致性功能,不是前台渲染或身份连续性的承诺。
最小化证据和隐私主张
保留如下短记录,并删除无界追踪:
{
"fixtureId": "visibility-boundary-01",
"browserMode": "headful",
"flagEffective": true,
"visibilityState": "visible",
"hidden": false,
"focusEvents": 1,
"blurEvents": 1,
"visibilityChanges": 0,
"outcome": "stable-visible"
}
浏览器版本、系统、profile revision 和日期可以放在测试元数据中。不要保存账户、私有路径、DOM 文本、按键或跨站标识。active 不是真人,active 不是身份,active 也不保证前台。BotBrowser 负责使用指南约束授权和保留时间。
定位第一个差异字段
先查配置:flagEffective 为 false 时检查许可证、参数拼写和 profile。flag 生效但 visibilityState 不同,则比较模式和宿主动作。blurEvents 增加而 visibilityChanges 为零,说明只有焦点变化。若发生可见性转换,应在处理器中读取状态,并确认结束的任务没有被恢复。
交接记录应包含 fixtureId、模式、有效 flag、第一个差异字段、预期与实际分支和下一步。不要为了让测试看起来真实而发送凭据或账户数据。
Public sources
- W3C Page Visibility 2
- MDN: Page Visibility API
- MDN: Document.visibilityState
- BotBrowser: Active Window Emulation
- BotBrowser: Advanced Features, active-window emulation
- BotBrowser: Responsible Use
Practical conclusion
把活动窗口仿真视为有界一致性控制。用 visibilitychange、document.visibilityState 和 document.hidden处理文档生命周期,把 focus 与 blur留在交互层。比较三种模式,测试 --bot-always-active=false,最小化证据,并明确 active 不能证明真人、身份、同意或保证前台。
本文属于指南: 什么是浏览器指纹?原理、风险、信号与隐私边界