1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69

docs / windows_virtual_desktop_handling.md [blame]

#  Windows 10 Virtual Desktop support

Windows 10 introduced Virtual Desktop support. Virtual Desktops are similar to
Chrome OS and Mac workspaces. A virtual desktop is a collection of windows.
Every window belongs to a virtual desktop.  When a virtual desktop is selected
to be active, the windows associated with that virtual desktop are displayed on
the screen. When a virtual desktop is hidden, all of its windows are also
hidden. This enables the user to create multiple working environments and to
switch between them. An app (e.g., Chromium) can have windows open on
different virtual desktops, and thus may need to be Virtual Desktop-aware.

The user-facing Chromium support for virtual desktops consists of two things:

  * When launching the browser with session restore, browser windows are moved
  to the virtual desktop they were on when the browser shutdown.
  * When opening a URL with the browser, either open it in a window on the
  current virtual desktop, or open a new window on the current virtual desktop.
  Don't open it in a tab in a window on another virtual desktop.

The core UI principles are that windows should be restored to the desktop they
were shut down on, and opening an app window shouldn't change the current
virtual desktop. Only the user should be able to change virtual desktops, or
move windows between virtual desktops.

Windows 10 exposes the COM interface
[IVirtualDesktopManager](https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ivirtualdesktopmanager)
to access the Virtual Desktop functionality. To make sure that opening a URL
stays on the current virtual desktop,
[BrowserView::IsOnCurrentWorkspace](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/frame/browser_view.cc?q=%20BrowserView::IsOnCurrentWorkspace)
uses the IVirtualDesktopManager method
[IsWindowOnCurrentVirtualDesktop](https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ivirtualdesktopmanager-iswindowoncurrentvirtualdesktop).
[BrowserMatches](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/browser_finder.cc?q=BrowserMatches)
in browser_finder.cc only returns a browser window on the current desktop.

To restore browser windows to the desktop they were last open on,
[BrowserDesktopWindowTreeHostWin](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc)
implements GetWorkspace by using the
[GetWindowDesktopId](https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ivirtualdesktopmanager-getwindowdesktopid) method on
IVirtualDesktopManager, and restores the workspace using
[MoveWindowToDesktop](https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ivirtualdesktopmanager-movewindowtodesktop),
in its ::Init method.

The actual implementation is a bit more complicated in order to avoid
calling COM methods on the UI thread, or destroying COM objects on the UI
thread, since doing so can cause nested message loops and re-entrant calls,
leading to blocked UI threads and crashes. The
[VirtualDesktopHelper](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/frame/browser_desktop_window_tree_host_win.cc?q=VirtualDesktopHelper&sq=&ss=chromium%2Fchromium%2Fsrc)
class does the workspace handling for BrowserDesktopWindowTreeHostWin, including
doing all the COM operations on a separate COM task runner. The GetWorkspace
method is synchronous so VirtualDesktopHelper has to remember and return the
most recent virtual desktop. Windows has no notification of a window changing
virtual desktops, so BrowserDesktopWindowTreeHostWin updates the virtual desktop
of a window whenever it gets focus, and if it has changed, calls
WindowTreeHost::OnHostWorkspaceChanged. This means that if a window is moved
to a different virtual desktop, but doesn't get focus before the browser is shut
down, the browser window will be restored to the previous virtual desktop.

Windows on different virtual desktops share the same coordinate system, so code
that iterates over windows generally needs to be Virtual Desktop-aware.
For example, the following places in code ignore windows not on the current
virtual desktop:

 * [LocalProcessWindowFinder::GetProcessWindowAtPoint](https://source.chromium.org/chromium/chromium/src/+/main:ui/display/win/local_process_window_finder_win.cc?q=LocalProcessWindowFinder::ShouldStopIterating&ss=chromium%2Fchromium%2Fsrc)
 * third_party/webrtc/modules/desktop_capture/win/window_capture_utils.cc
 * [Native Window occlusion tracker](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/native_window_occlusion_tracker_win.cc?q=WindowCanOccludeOtherWindowsOnCurrentVirtualDesktop&ss=chromium%2Fchromium%2Fsrc),
 when determining if a Chromium window is occluded/covered by other windows.
 Windows not on the current virtual desktop are considered occluded.