
See this doc: go/chromium-coil-change for more info BUG=1210385 Change-Id: I6cd8548301e360bae8e71384dbd8ef11aa6c985f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2902904 Reviewed-by: My Nguyen <myy@chromium.org> Commit-Queue: My Nguyen <myy@chromium.org> Auto-Submit: John Palmer <jopalmer@chromium.org> Cr-Commit-Position: refs/heads/master@{#885851}
91 lines
5.5 KiB
Markdown
91 lines
5.5 KiB
Markdown
# Windows Native Window Occlusion Detection
|
|
|
|
## Background
|
|
|
|
Ui::aura has an
|
|
[API](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/window_occlusion_tracker.h)
|
|
to track which aura windows are occluded, i.e., covered by
|
|
one or more other windows. If a window is occluded, Chromium treats foreground
|
|
tabs as if they were background tabs; rendering stops, and js is throttled. On
|
|
ChromeOS, since all windows are aura windows, this is sufficient to determine
|
|
if a Chromium window is covered by other windows. On Windows, we need to
|
|
consider native app windows when determining if a Chromium window is occluded.
|
|
This is implemented in
|
|
[native_window_occlusion_tracker_win.cc](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/native_window_occlusion_tracker_win.cc).
|
|
|
|
## Implementation
|
|
When the core WindowOcclusionTracker decides to track a WindowTreeHost, it
|
|
calls EnableNativeWindowOcclusionTracking. On non-Windows platforms, this
|
|
does nothing. On Windows, it calls ::Enable on the singleton
|
|
NativeWindowOcclusionTrackerWin object, creating it first, if it hasn't already
|
|
been created.
|
|
|
|
When NativeWindowOcclusionTrackerWin starts tracking a WindowTreeHost, it adds
|
|
the HWND of the host's root window to a map of Windows HWNDs it is tracking,
|
|
and its corresponding aura Window. It also starts observing the window to know
|
|
when its visibility changes, or it is destroyed.
|
|
|
|
The main work of occlusion calculation is done by a helper class,
|
|
WindowOcclusionCalculator, which runs on a separate COM task runner, in order
|
|
to not block the UI thread. If the WindowOcclusionCalculator is tracking any
|
|
windows, it
|
|
[registers](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/native_window_occlusion_tracker_win.cc?q=WindowOcclusionCalculator::RegisterEventHooks)
|
|
a set of
|
|
[event](https://docs.microsoft.com/en-us/windows/win32/winauto/event-constants)
|
|
hooks with Windows, in order to know when
|
|
the occlusion state might need to be recalculated. These events include window
|
|
move/resize, minimize/restore, foreground window changing, etc. Most of these
|
|
are global event hooks, so that we get notified of events for all Windows
|
|
windows. For windows that could possibly
|
|
occlude Chromium windows, (i.e., fully visible windows on the current virtual
|
|
desktop), we register for EVENT_OBJECT_LOCATIONCHANGE events for the window's
|
|
process. pids_for_location_change_hook_ keeps track of which pids are hooked,
|
|
and is
|
|
[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)
|
|
if the process no longer has any windows open.
|
|
|
|
When the event handler gets notified of an event, it usually kicks off new
|
|
occlusion calculation, which runs after a 16ms timer. It doesn't do a new
|
|
occlusion calculation if the timer is currently running. 16ms corresponds to the
|
|
interval between frames when displaying 60 frames per second(FPS). There's
|
|
no point in doing occlusion calculations more frequently than frames are
|
|
displayed. If the user is in the middle of moving a window around, occlusion
|
|
isn't calculated until the window stops moving, because moving a window is
|
|
essentially modal, and there's no point in recalculating occlusion over and
|
|
over again for each incremental move event.
|
|
|
|
To calculate occlusion, we first mark minimized Chromium windows as hidden, and
|
|
Chromium windows on a different virtual desktop as occluded. We compute the
|
|
SKRegion for the virtual screen, which takes multiple monitor configurations
|
|
into account, and set the initial unoccluded_desktop_region_ to the screen
|
|
region. Then, we enumerate all the HWNDs, in z-order (topmost window first).
|
|
For each occluding window (visible, not transparent, etc), we save the current
|
|
unoccluded_desktop_region_, and subtract the window's window_rect from the
|
|
unoccluded_desktop_region_ . If the hwnd is not a root Chromium window, we
|
|
continue to the next hwnd. If it is a root Chromium window, then we have seen
|
|
all the windows above it, and know whether it is occluded or not. We determine
|
|
this by checking if subtracting its window_rect from the
|
|
unoccluded_desktop_region_ actually changed the unoccluded_desktop_region_. If
|
|
not, that means previous windows occluded the current window's window_rect, and
|
|
it is occluded, otherwise, not.
|
|
Once the occlusion state of all root Chromium windows has been determined, the
|
|
WindowOcclusionTracker posts a task to the ui thread to run a callback on the
|
|
NativeWindowOcclusionTrackerWin object. That callback is
|
|
[NativeWindowOcclusionTrackerWin::UpdateOcclusionState](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/native_window_occlusion_tracker_win.cc;l=226?q=NativeWindowOcclusionTrackerWin::UpdateOcclusionState)
|
|
, and is passed
|
|
root_window_hwnds_occlusion_state_, which is a map between root window HWNDs
|
|
and their calculated occlusion state.
|
|
NativeWindowOcclusionTrackerWin::UpdateOcclusionState iterates over those HWNDs,
|
|
finds the corresponding root window, and calls SetNativeWindowOcclusionState on
|
|
its WindowTreeHost, with the corresponding HWND's occlusion state from the map.
|
|
If the screen is locked, however, it sets the occlusion state to OCCLUDED.
|
|
|
|
## Miscellaneous
|
|
|
|
* If a window is falsely determined to be occluded, the content area will be
|
|
white.
|
|
* When the screen is locked, all Chromium windows are considered occluded.
|
|
* Windows on other virtual desktops are considered occluded.
|
|
* Transparent windows, cloaked windows, floating windows, non-rectangular
|
|
windows, etc, are not considered occluding.
|