blob: 2c38a3c67c87eb8ddd6985eee9031aac87da04e8 [file] [log] [blame] [view]
David Bienvenubded26a2020-09-21 17:17:571# Windows Native Window Occlusion Detection
2
3## Background
4
5Ui::aura has an
John Palmer046f9872021-05-24 01:24:566[API](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/window_occlusion_tracker.h)
David Bienvenubded26a2020-09-21 17:17:577to track which aura windows are occluded, i.e., covered by
8one or more other windows. If a window is occluded, Chromium treats foreground
9tabs as if they were background tabs; rendering stops, and js is throttled. On
10ChromeOS, since all windows are aura windows, this is sufficient to determine
11if a Chromium window is covered by other windows. On Windows, we need to
12consider native app windows when determining if a Chromium window is occluded.
13This is implemented in
John Palmer046f9872021-05-24 01:24:5614[native_window_occlusion_tracker_win.cc](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/native_window_occlusion_tracker_win.cc).
David Bienvenubded26a2020-09-21 17:17:5715
16## Implementation
17When the core WindowOcclusionTracker decides to track a WindowTreeHost, it
18calls EnableNativeWindowOcclusionTracking. On non-Windows platforms, this
19does nothing. On Windows, it calls ::Enable on the singleton
20NativeWindowOcclusionTrackerWin object, creating it first, if it hasn't already
21been created.
22
23When NativeWindowOcclusionTrackerWin starts tracking a WindowTreeHost, it adds
24the HWND of the host's root window to a map of Windows HWNDs it is tracking,
25and its corresponding aura Window. It also starts observing the window to know
26when its visibility changes, or it is destroyed.
27
28The main work of occlusion calculation is done by a helper class,
29WindowOcclusionCalculator, which runs on a separate COM task runner, in order
30to not block the UI thread. If the WindowOcclusionCalculator is tracking any
31windows, it
John Palmer046f9872021-05-24 01:24:5632[registers](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/native_window_occlusion_tracker_win.cc?q=WindowOcclusionCalculator::RegisterEventHooks)
David Bienvenubded26a2020-09-21 17:17:5733a set of
34[event](https://docs.microsoft.com/en-us/windows/win32/winauto/event-constants)
35hooks with Windows, in order to know when
36the occlusion state might need to be recalculated. These events include window
37move/resize, minimize/restore, foreground window changing, etc. Most of these
38are global event hooks, so that we get notified of events for all Windows
39windows. For windows that could possibly
40occlude Chromium windows, (i.e., fully visible windows on the current virtual
41desktop), we register for EVENT_OBJECT_LOCATIONCHANGE events for the window's
42process. pids_for_location_change_hook_ keeps track of which pids are hooked,
43and is
John Palmer046f9872021-05-24 01:24:5644[used to remove the hook](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/native_window_occlusion_tracker_win.cc;drc=eeee643ae963e1d78c7457184f8af93f48bba9d3;l=443)
David Bienvenubded26a2020-09-21 17:17:5745if the process no longer has any windows open.
46
47When the event handler gets notified of an event, it usually kicks off new
48occlusion calculation, which runs after a 16ms timer. It doesn't do a new
49occlusion calculation if the timer is currently running. 16ms corresponds to the
50interval between frames when displaying 60 frames per second(FPS). There's
51no point in doing occlusion calculations more frequently than frames are
52displayed. If the user is in the middle of moving a window around, occlusion
53isn't calculated until the window stops moving, because moving a window is
54essentially modal, and there's no point in recalculating occlusion over and
55over again for each incremental move event.
56
57To calculate occlusion, we first mark minimized Chromium windows as hidden, and
58Chromium windows on a different virtual desktop as occluded. We compute the
59SKRegion for the virtual screen, which takes multiple monitor configurations
60into account, and set the initial unoccluded_desktop_region_ to the screen
61region. Then, we enumerate all the HWNDs, in z-order (topmost window first).
62For each occluding window (visible, not transparent, etc), we save the current
63unoccluded_desktop_region_, and subtract the window's window_rect from the
64unoccluded_desktop_region_ . If the hwnd is not a root Chromium window, we
65continue to the next hwnd. If it is a root Chromium window, then we have seen
66all the windows above it, and know whether it is occluded or not. We determine
67this by checking if subtracting its window_rect from the
68unoccluded_desktop_region_ actually changed the unoccluded_desktop_region_. If
69not, that means previous windows occluded the current window's window_rect, and
70it is occluded, otherwise, not.
71Once the occlusion state of all root Chromium windows has been determined, the
72WindowOcclusionTracker posts a task to the ui thread to run a callback on the
73NativeWindowOcclusionTrackerWin object. That callback is
John Palmer046f9872021-05-24 01:24:5674[NativeWindowOcclusionTrackerWin::UpdateOcclusionState](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/native_window_occlusion_tracker_win.cc;l=226?q=NativeWindowOcclusionTrackerWin::UpdateOcclusionState)
David Bienvenubded26a2020-09-21 17:17:5775, and is passed
76root_window_hwnds_occlusion_state_, which is a map between root window HWNDs
77and their calculated occlusion state.
78NativeWindowOcclusionTrackerWin::UpdateOcclusionState iterates over those HWNDs,
79finds the corresponding root window, and calls SetNativeWindowOcclusionState on
80its WindowTreeHost, with the corresponding HWND's occlusion state from the map.
81If the screen is locked, however, it sets the occlusion state to OCCLUDED.
82
83## Miscellaneous
84
85 * If a window is falsely determined to be occluded, the content area will be
86white.
87 * When the screen is locked, all Chromium windows are considered occluded.
88 * Windows on other virtual desktops are considered occluded.
89 * Transparent windows, cloaked windows, floating windows, non-rectangular
90 windows, etc, are not considered occluding.