001Notes
Changing What a Website Can Observe
On this page12
How Hardened Chromium substitutes visibility, focus, lifecycle, idle, screen, fullscreen, API, and cursor signals—and which secondary signals remain.
Article details
- Status
- Building Publicly
- Subcategory
- Hardened Chromium
- Last reviewed
- 7 Sept 2026
12 sections
What stock websites observe
const observations = {
hidden: document.hidden,
visibility: document.visibilityState,
focused: document.hasFocus(),
extendedScreen: screen.isExtended,
screenOrigin: [screen.availLeft, screen.availTop],
};
document.addEventListener('visibilitychange', record);
window.addEventListener('focus', record);
window.addEventListener('blur', record);
The visibility hook is in Blink—and in Content
flowchart TD
UI[Real tab/window visibility] --> WC[WebContentsImpl]
WC --> LSM[PageLifecycleStateManager]
LSM --> RVH[RenderViewHostImpl]
RVH --> Doc[Blink Document]
Doc --> JS[document.hidden / visibilityState]
Doc --> Event[visibilitychange]
| Hook | Change |
|---|---|
WebContentsImpl::CalculatePageVisibilityState | Maps ordinary background content to kVisible for the renderer |
RenderViewHostImpl | Avoids initializing a non-prerendered hardened page as hidden |
Document::hidden | Returns false while the document remains attached |
Document::DidChangeVisibilityState | Suppresses standard and legacy visibility events in hardened mode |
Document::DispatchUnloadEvents | Avoids manufacturing a final hardened visibility transition during unload |
What this bypasses—and what it does not
Focus without activation signals
Keeping background pages on the active path
flowchart LR
Hidden[Tab becomes background] --> VisibleFacade[Renderer receives visible]
Hidden --> Priority[Force foreground priority]
Hidden --> NoFreeze[Skip ordinary freezing]
Hidden --> NoDiscard[Reject automatic discard]
NoFreeze --> Running[Timers/tasks/animation remain active]
VisibleFacade --> Running
Priority --> Running
Stable active/unlocked idle state
Current-screen-only geometry
flowchart TD
OS[OS virtual desktop: several displays] --> Current[Display containing window]
Current --> Normalize[Translate origin to 0,0]
Normalize --> Screen[window.screen]
Normalize --> Details[getScreenDetails: one generic screen]
Move[moveTo / window.open coordinates] --> Clamp[Clamp to current display]
Virtual fullscreen
Removing powerful surfaces
Cursor boundary events
Verification map
Detection and compatibility limits
| Real event | Stock page observation | Hardened page observation |
|---|---|---|
| User selects another tab | Hidden state and visibilitychange | Visible state; no visibility event |
| Browser window loses focus | hasFocus() false and focus/blur transitions | hasFocus() true; activation transition suppressed |
| Page becomes freeze-eligible | Tasks may stop | Ordinary freezing skipped |
| Memory policy chooses a tab | Tab may be automatically discarded | Non-external discard rejected |
| Window moves to another display | Global coordinates/topology may change | Coordinates normalized to current display |
| Page requests fullscreen | Native window transition plus DOM state | DOM fullscreen state without native transition |
| OS reports idle/locked | Idle Detector can change | Stable active/unlocked facade |