// Copyright 2013 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/aura/window_tree_host.h" #include "base/command_line.h" #include "base/containers/contains.h" #include "base/feature_list.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" #include "base/metrics/field_trial_params.h" #include "base/no_destructor.h" #include "base/observer_list.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "components/viz/common/features.h" #include "components/viz/common/surfaces/frame_sink_id.h" #include "components/viz/host/host_frame_sink_manager.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/env.h" #include "ui/aura/host_frame_rate_throttler.h" #include "ui/aura/native_window_occlusion_tracker.h" #include "ui/aura/scoped_keyboard_hook.h" #include "ui/aura/scoped_simple_keyboard_hook.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_targeter.h" #include "ui/aura/window_tree_host_observer.h" #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" #include "ui/base/ime/init/input_method_factory.h" #include "ui/base/ime/input_method.h" #include "ui/base/layout.h" #include "ui/base/ui_base_features.h" #include "ui/base/view_prop.h" #include "ui/compositor/compositor.h" #include "ui/compositor/compositor_switches.h" #include "ui/compositor/layer.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/point3_f.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/icc_profile.h" #include "ui/gfx/switches.h" #include "ui/platform_window/platform_window_init_properties.h" namespace aura { namespace { const char kWindowTreeHostForAcceleratedWidget[] = "__AURA_WINDOW_TREE_HOST_ACCELERATED_WIDGET__"; // Returns the cc::Layer used by a ui::Layer. This is a bit ugly, and is done to // avoid exposing cc::Layer outside of ui::Layer. cc::Layer* ccLayerFromUiLayer(ui::Layer* layer) { return static_cast(layer)->GetCcLayer(); } #if DCHECK_IS_ON() class ScopedLocalSurfaceIdValidator { public: explicit ScopedLocalSurfaceIdValidator(Window* window) : window_(window), local_surface_id_(window ? window->GetLocalSurfaceId() : viz::LocalSurfaceId()) {} ScopedLocalSurfaceIdValidator(const ScopedLocalSurfaceIdValidator&) = delete; ScopedLocalSurfaceIdValidator& operator=( const ScopedLocalSurfaceIdValidator&) = delete; ~ScopedLocalSurfaceIdValidator() { if (window_) { DCHECK_EQ(local_surface_id_, window_->GetLocalSurfaceId()); } } private: const raw_ptr window_; const viz::LocalSurfaceId local_surface_id_; }; #else class ScopedLocalSurfaceIdValidator { public: explicit ScopedLocalSurfaceIdValidator(Window* window) {} ~ScopedLocalSurfaceIdValidator() {} }; #endif } // namespace // In order for viz to drop all references to resources used by the browser // (and renderers) a CompositorFrame with *no* references to other surfaces // must be generated. To do this a solid color layer is inserted into the // layer tree, and the existing root layer is removed. Adding a layer at the top // (keeping existing layer tree) is not enough, as the CompositorFrame still // contains information about the surfaces/resources that are obscured. Once the // frame is generated, the root layer (from `window_`) is inserted back in the // Compositor's root layer and then the Compositor is hidden. class WindowTreeHost::HideHelper { public: explicit HideHelper(WindowTreeHost* host) : host_(host), compositor_root_layer_( ccLayerFromUiLayer(host->window()->layer())->mutable_parent()), layer_for_transition_( std::make_unique(ui::LAYER_SOLID_COLOR)) { layer_for_transition_->SetColor(SK_ColorWHITE); aura::Window* host_window = host_->window(); ui::Layer* host_window_layer = host_window->layer(); ui::Compositor* compositor = host_->compositor(); // SetRootLayer() resets the `compositor_` member in Layer. If // SetRootLayer() were used, it would mean the existing layer hierarchy // would no longer think it is in a compositor. As this state is temporary, // and purely to release resources, SetRootLayer() is not used. // We do need to disable ticking of animations since the animation code // expects that its callers ensure that any ticking animations reference // element IDs for layers that are currently in the layer tree. DCHECK_EQ(host_window_layer, compositor->root_layer()); compositor->DisableAnimations(); ccLayerFromUiLayer(host_window_layer)->RemoveFromParent(); layer_for_transition_->SetBounds(host_window->bounds()); compositor_root_layer_->AddChild( ccLayerFromUiLayer(layer_for_transition_.get())); layer_for_transition_->OnDeviceScaleFactorChanged( host_window->layer()->device_scale_factor()); // Request a presentation frame. Once the frame is generated the real root // layer is added back (from the destructor). compositor->RequestPresentationTimeForNextFrame(base::BindOnce( &HideHelper::OnFramePresented, weak_ptr_factory_.GetWeakPtr())); } ~HideHelper() { ui::Layer* host_window_layer = host_->window()->layer(); compositor_root_layer_->AddChild(ccLayerFromUiLayer(host_window_layer)); host_window_layer->OnDeviceScaleFactorChanged( layer_for_transition_->device_scale_factor()); DCHECK_EQ(host_window_layer, host_->compositor()->root_layer()); host_->compositor()->EnableAnimations(); } private: void OnFramePresented(const gfx::PresentationFeedback& feedback) { host_->FinishHideTransition(); // WARNING: this has been deleted. } raw_ptr host_; scoped_refptr compositor_root_layer_; std::unique_ptr layer_for_transition_; base::WeakPtrFactory weak_ptr_factory_{this}; }; WindowTreeHost::VideoCaptureLock::~VideoCaptureLock() { if (host_) host_->DecrementVideoCaptureCount(); } WindowTreeHost::VideoCaptureLock::VideoCaptureLock(WindowTreeHost* host) : host_(host->GetWeakPtr()) {} //////////////////////////////////////////////////////////////////////////////// // WindowTreeHost, public: WindowTreeHost::~WindowTreeHost() { DCHECK(!compositor_) << "compositor must be destroyed before root window"; } // static WindowTreeHost* WindowTreeHost::GetForAcceleratedWidget( gfx::AcceleratedWidget widget) { return reinterpret_cast( ui::ViewProp::GetValue(widget, kWindowTreeHostForAcceleratedWidget)); } void WindowTreeHost::InitHost() { display::Display display = display::Screen::GetScreen()->GetDisplayNearestWindow(window()); device_scale_factor_ = display.device_scale_factor(); UpdateRootWindowSize(); InitCompositor(); Env::GetInstance()->NotifyHostInitialized(this); } void WindowTreeHost::AddObserver(WindowTreeHostObserver* observer) { observers_.AddObserver(observer); } void WindowTreeHost::RemoveObserver(WindowTreeHostObserver* observer) { observers_.RemoveObserver(observer); } bool WindowTreeHost::HasObserver(const WindowTreeHostObserver* observer) const { return observers_.HasObserver(observer); } base::WeakPtr WindowTreeHost::GetWeakPtr() { return weak_factory_.GetWeakPtr(); } gfx::Transform WindowTreeHost::GetRootTransform() const { gfx::Transform transform; transform.Scale(device_scale_factor_, device_scale_factor_); transform *= window()->layer()->transform(); return transform; } void WindowTreeHost::SetRootTransform(const gfx::Transform& transform) { window()->SetTransform(transform); UpdateRootWindowSize(); } gfx::Transform WindowTreeHost::GetInverseRootTransform() const { gfx::Transform invert; gfx::Transform transform = GetRootTransform(); if (!transform.GetInverse(&invert)) return transform; return invert; } void WindowTreeHost::SetDisplayTransformHint(gfx::OverlayTransform transform) { if (compositor()->display_transform_hint() == transform) return; compositor()->SetDisplayTransformHint(transform); UpdateCompositorScaleAndSize(GetBoundsInPixels().size()); } gfx::Transform WindowTreeHost::GetRootTransformForLocalEventCoordinates() const { return GetRootTransform(); } gfx::Transform WindowTreeHost::GetInverseRootTransformForLocalEventCoordinates() const { gfx::Transform invert; gfx::Transform transform = GetRootTransformForLocalEventCoordinates(); if (!transform.GetInverse(&invert)) return transform; return invert; } void WindowTreeHost::UpdateCompositorScaleAndSize( const gfx::Size& new_size_in_pixels) { gfx::Rect new_bounds(new_size_in_pixels); if (compositor_->display_transform_hint() == gfx::OVERLAY_TRANSFORM_ROTATE_90 || compositor_->display_transform_hint() == gfx::OVERLAY_TRANSFORM_ROTATE_270) { new_bounds.Transpose(); } // Allocate a new LocalSurfaceId for the new size or scale factor. window_->AllocateLocalSurfaceId(); ScopedLocalSurfaceIdValidator lsi_validator(window()); compositor_->SetScaleAndSize(device_scale_factor_, new_bounds.size(), window_->GetLocalSurfaceId()); } void WindowTreeHost::ConvertDIPToScreenInPixels(gfx::Point* point) const { ConvertDIPToPixels(point); gfx::Point location = GetLocationOnScreenInPixels(); point->Offset(location.x(), location.y()); } void WindowTreeHost::ConvertScreenInPixelsToDIP(gfx::Point* point) const { gfx::Point location = GetLocationOnScreenInPixels(); point->Offset(-location.x(), -location.y()); ConvertPixelsToDIP(point); } void WindowTreeHost::ConvertDIPToPixels(gfx::Point* point) const { gfx::PointF point_f{*point}; ConvertDIPToPixels(&point_f); *point = gfx::ToFlooredPoint(point_f); } void WindowTreeHost::ConvertDIPToPixels(gfx::PointF* point) const { *point = GetRootTransform().MapPoint(*point); } void WindowTreeHost::ConvertPixelsToDIP(gfx::Point* point) const { gfx::PointF point_f{*point}; ConvertPixelsToDIP(&point_f); *point = gfx::ToFlooredPoint(point_f); } void WindowTreeHost::ConvertPixelsToDIP(gfx::PointF* point) const { *point = GetInverseRootTransform().MapPoint(*point); } void WindowTreeHost::SetCursor(gfx::NativeCursor cursor) { last_cursor_ = cursor; // A lot of code seems to depend on NULL cursors actually showing an arrow, // so just pass everything along to the host. SetCursorNative(cursor); } void WindowTreeHost::OnCursorVisibilityChanged(bool show) { // Clear any existing mouse hover effects when the cursor becomes invisible. // Note we do not need to dispatch a mouse enter when the cursor becomes // visible because that can only happen in response to a mouse event, which // will trigger its own mouse enter. if (!show) { ui::EventDispatchDetails details = dispatcher()->DispatchMouseExitAtPoint( nullptr, dispatcher()->GetLastMouseLocationInRoot(), ui::EF_CURSOR_HIDE); if (details.dispatcher_destroyed) return; } OnCursorVisibilityChangedNative(show); } void WindowTreeHost::MoveCursorToLocationInDIP( const gfx::Point& location_in_dip) { gfx::Point host_location(location_in_dip); ConvertDIPToPixels(&host_location); MoveCursorToInternal(location_in_dip, host_location); } void WindowTreeHost::MoveCursorToLocationInPixels( const gfx::Point& location_in_pixels) { gfx::Point root_location(location_in_pixels); ConvertPixelsToDIP(&root_location); MoveCursorToInternal(root_location, location_in_pixels); } ui::InputMethod* WindowTreeHost::GetInputMethod() { if (!input_method_) { input_method_owned_ = ui::CreateInputMethod(this, GetAcceleratedWidget()); input_method_ = input_method_owned_.get(); } return input_method_; } void WindowTreeHost::SetSharedInputMethod(ui::InputMethod* input_method) { input_method_ = input_method; input_method_owned_.reset(); } ui::EventDispatchDetails WindowTreeHost::DispatchKeyEventPostIME( ui::KeyEvent* event) { // If dispatch to IME is already disabled we shouldn't reach here. DCHECK(!dispatcher_->should_skip_ime()); dispatcher_->set_skip_ime(true); // InputMethod::DispatchKeyEvent() is called in PRE_DISPATCH phase, so event // target is reset here to avoid issues in subsequent processing phases. ui::Event::DispatcherApi(event).set_target(nullptr); // We should bypass event rewriters here as they've been tried before. ui::EventDispatchDetails dispatch_details = GetEventSink()->OnEventFromSource(event); if (!dispatch_details.dispatcher_destroyed) dispatcher_->set_skip_ime(false); return dispatch_details; } ui::EventSink* WindowTreeHost::GetEventSink() { return dispatcher_.get(); } int64_t WindowTreeHost::GetDisplayId() { return display::Screen::GetScreen()->GetDisplayNearestWindow(window()).id(); } void WindowTreeHost::Show() { // Ensure that compositor has been properly initialized, see InitCompositor() // and InitHost(). DCHECK(compositor()); DCHECK_EQ(compositor()->root_layer(), window()->layer()); OnAcceleratedWidgetMadeVisible(true); ShowImpl(); window()->Show(); } void WindowTreeHost::Hide() { HideImpl(); OnAcceleratedWidgetMadeVisible(false); } gfx::Rect WindowTreeHost::GetBoundsInDIP() const { aura::Window* root_window = const_cast(window()); display::Screen* screen = display::Screen::GetScreen(); gfx::Rect screen_bounds = GetBoundsInPixels(); return screen->ScreenToDIPRectInWindow(root_window, screen_bounds); } gfx::Rect WindowTreeHost::GetBoundsInAcceleratedWidgetPixelCoordinates() { return gfx::Rect(GetBoundsInPixels().size()); } std::unique_ptr WindowTreeHost::CaptureSystemKeyEvents( absl::optional> dom_codes) { // TODO(joedow): Remove the simple hook class/logic once this flag is removed. if (!base::FeatureList::IsEnabled(features::kSystemKeyboardLock)) return std::make_unique(std::move(dom_codes)); if (CaptureSystemKeyEventsImpl(std::move(dom_codes))) return std::make_unique(weak_factory_.GetWeakPtr()); return nullptr; } bool WindowTreeHost::ShouldSendKeyEventToIme() { return true; } bool WindowTreeHost::IsNativeWindowOcclusionEnabled() const { return native_window_occlusion_enabled_; } void WindowTreeHost::SetNativeWindowOcclusionState( Window::OcclusionState state, const SkRegion& occluded_region) { if (occlusion_state_ == state && occluded_region_ == occluded_region) return; occlusion_state_ = state; occluded_region_ = occluded_region; if (compositor() && accelerated_widget_made_visible_ && NativeWindowOcclusionTracker:: IsNativeWindowOcclusionTrackingAlwaysEnabled(this)) { if (ShouldThrottleWhenOccluded()) { // Throttling doesn't update the visibility. if (occlusion_state_ == Window::OcclusionState::OCCLUDED) HostFrameRateThrottler::GetInstance().AddHost(this); else HostFrameRateThrottler::GetInstance().RemoveHost(this); } else { const bool visible = CalculateCompositorVisibilityFromOcclusionState(); // Transitioning to hidden means the compositor state hasn't been updated // yet, but will. In this case, always route through // UpdateCompositorVisibility() to ensure state is correctly cleaned up. if (visible != compositor()->IsVisible() || is_transitioning_to_hidden()) UpdateCompositorVisibility(visible); } } for (WindowTreeHostObserver& observer : observers_) observer.OnOcclusionStateChanged(this, state, occluded_region); } void WindowTreeHost::UpdateRootWindowSize() { // Validate that the LocalSurfaceId does not change. bool compositor_inited = !!compositor()->root_layer(); ScopedLocalSurfaceIdValidator lsi_validator(compositor_inited ? window() : nullptr); window()->SetBounds(CalculateRootWindowBounds()); } gfx::Rect WindowTreeHost::CalculateRootWindowBounds() const { return GetTransformedRootWindowBoundsFromPixelSize( GetBoundsInPixels().size()); } std::unique_ptr WindowTreeHost::RequestUnadjustedMovement() { NOTIMPLEMENTED(); return nullptr; } bool WindowTreeHost::SupportsMouseLock() { return false; } void WindowTreeHost::LockMouse(Window* window) { Window* root_window = window->GetRootWindow(); DCHECK(root_window); auto* cursor_client = client::GetCursorClient(root_window); if (cursor_client) { cursor_client->HideCursor(); cursor_client->LockCursor(); } } void WindowTreeHost::UnlockMouse(Window* window) { Window* root_window = window->GetRootWindow(); DCHECK(root_window); if (window->HasCapture()) window->ReleaseCapture(); auto* cursor_client = client::GetCursorClient(root_window); if (cursor_client) { cursor_client->UnlockCursor(); cursor_client->ShowCursor(); } } std::unique_ptr WindowTreeHost::CreateVideoCaptureLock() { if (!NativeWindowOcclusionTracker:: IsNativeWindowOcclusionTrackingAlwaysEnabled(this)) { return nullptr; } // Throtting doesn't actually change the visibility, so no need for the lock. if (ShouldThrottleWhenOccluded()) return nullptr; ++video_capture_count_; MaybeUpdateComposibleVisibilityForVideoLockCountChange(); // WrapUnique() is used as constructor is private. return base::WrapUnique(new VideoCaptureLock(this)); } //////////////////////////////////////////////////////////////////////////////// // WindowTreeHost, protected: WindowTreeHost::WindowTreeHost(std::unique_ptr window) : window_(window.release()) { // See header for details on ownership. if (!window_) window_ = new Window(nullptr); auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(window_); device_scale_factor_ = display.device_scale_factor(); #if BUILDFLAG(IS_WIN) // The feature state is necessary but not sufficient for checking if // occlusion is enabled. It may be disabled by other means (e.g., policy). native_window_occlusion_enabled_ = !base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kHeadless) && base::FeatureList::IsEnabled(features::kCalculateNativeWinOcclusion); #endif } void WindowTreeHost::UpdateCompositorVisibility(bool visible) { if (!compositor()) return; if (ShouldThrottleWhenOccluded()) { // If ShouldThrottleWhenOccluded() is true, then this function should only // be called if visibility is changed externally. In this case, assume // `occlusion_state_` is being ignored, and that throtting should be // disabled. For the most part, if ShouldThrottleWhenOccluded() is true, // the handling of occlusion changing is done in // SetNativeWindowOcclusionState(). HostFrameRateThrottler::GetInstance().RemoveHost(this); } if (visible) { if (is_transitioning_to_hidden()) RestoreHideTransitionState(); } else { if (is_transitioning_to_hidden()) return; if (ShouldReleaseResourcesWhenHidden()) { StartReleasingResourcesForHide(); // Compositor visibility is changed once transition completes. return; } } compositor()->SetVisible(visible); } void WindowTreeHost::DestroyCompositor() { if (!compositor_) return; HostFrameRateThrottler::GetInstance().RemoveHost(this); // Explicitly delete the HideHelper early as it makes use of `compositor_` // and `window_`. hide_helper_.reset(); compositor_->RemoveObserver(this); compositor_.reset(); if (NativeWindowOcclusionTracker:: IsNativeWindowOcclusionTrackingAlwaysEnabled(this)) { NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(this); } } void WindowTreeHost::DestroyDispatcher() { Env::GetInstance()->NotifyHostDestroyed(this); delete window_; window_ = nullptr; dispatcher_.reset(); // TODO(beng): this comment is no longer quite valid since this function // isn't called from WED, and WED isn't a subclass of Window. So it seems // like we could just rely on ~Window now. // Destroy child windows while we're still valid. This is also done by // ~Window, but by that time any calls to virtual methods overriden here (such // as GetRootWindow()) result in Window's implementation. By destroying here // we ensure GetRootWindow() still returns this. // window()->RemoveOrDestroyChildren(); } void WindowTreeHost::OnAcceleratedWidgetMadeVisible(bool value) { if (accelerated_widget_made_visible_ == value) return; accelerated_widget_made_visible_ = value; // Always update the compositor (ignoring occlusion-state) as it is entirely // possible the occlusion-state is out of date at this point. It is expected // that the proper occlusion state is provided soon after this. UpdateCompositorVisibility(value); } void WindowTreeHost::CreateCompositor(bool force_software_compositor, bool use_external_begin_frame_control, bool enable_compositing_based_throttling, size_t memory_limit_when_visible_mb) { Env* env = Env::GetInstance(); ui::ContextFactory* context_factory = env->context_factory(); DCHECK(context_factory); compositor_ = std::make_unique( context_factory->AllocateFrameSinkId(), context_factory, base::ThreadTaskRunnerHandle::Get(), ui::IsPixelCanvasRecordingEnabled(), use_external_begin_frame_control, force_software_compositor, enable_compositing_based_throttling, memory_limit_when_visible_mb); #if BUILDFLAG(IS_CHROMEOS_ASH) compositor_->AddObserver(this); #endif if (!dispatcher()) { window()->Init(ui::LAYER_NOT_DRAWN); window()->set_host(this); window()->SetName("RootWindow"); dispatcher_ = std::make_unique(this); } } void WindowTreeHost::InitCompositor() { DCHECK(!compositor_->root_layer()); compositor_->SetScaleAndSize(device_scale_factor_, GetBoundsInPixels().size(), window()->GetLocalSurfaceId()); compositor_->SetRootLayer(window()->layer()); display::Display display = display::Screen::GetScreen()->GetDisplayNearestWindow(window()); compositor_->SetDisplayColorSpaces(display.color_spaces()); } void WindowTreeHost::OnAcceleratedWidgetAvailable() { compositor_->SetAcceleratedWidget(GetAcceleratedWidget()); prop_ = std::make_unique( GetAcceleratedWidget(), kWindowTreeHostForAcceleratedWidget, this); if (NativeWindowOcclusionTracker:: IsNativeWindowOcclusionTrackingAlwaysEnabled(this)) { NativeWindowOcclusionTracker::EnableNativeWindowOcclusionTracking(this); } } void WindowTreeHost::OnHostMovedInPixels() { TRACE_EVENT0("ui", "WindowTreeHost::OnHostMovedInPixels"); for (WindowTreeHostObserver& observer : observers_) observer.OnHostMovedInPixels(this); } void WindowTreeHost::OnHostResizedInPixels( const gfx::Size& new_size_in_pixels) { // The compositor is deleted from WM_DESTROY, but we don't delete things until // WM_NCDESTROY, and it must be possible to still get some messages between // these two. if (!compositor_) return; display::Display display = display::Screen::GetScreen()->GetDisplayNearestWindow(window()); // If we don't have the actual display, don't overwrite the scale factor with // the default value. See https://crbug.com/1285476 for details. if (display.is_valid()) device_scale_factor_ = display.device_scale_factor(); UpdateRootWindowSize(); // Passing |new_size_in_pixels| to set compositor size. It could be different // from GetBoundsInPixels() on Windows to contain extra space for window // transition animations and should be used to set compositor size instead of // GetBoundsInPixels() in such case. UpdateCompositorScaleAndSize(new_size_in_pixels); for (WindowTreeHostObserver& observer : observers_) observer.OnHostResized(this); } void WindowTreeHost::OnHostWorkspaceChanged() { for (WindowTreeHostObserver& observer : observers_) observer.OnHostWorkspaceChanged(this); } void WindowTreeHost::OnHostDisplayChanged() { if (!compositor_) return; display::Display display = display::Screen::GetScreen()->GetDisplayNearestWindow(window()); compositor_->SetDisplayColorSpaces(display.color_spaces()); } void WindowTreeHost::OnHostCloseRequested() { for (WindowTreeHostObserver& observer : observers_) observer.OnHostCloseRequested(this); } void WindowTreeHost::OnHostLostWindowCapture() { // It is possible for this function to be called during destruction, after the // root window has already been destroyed (e.g. when the ui::PlatformWindow is // destroyed, and during destruction, it loses capture. See more details in // http://crbug.com/770670) if (!window()) return; Window* capture_window = client::GetCaptureWindow(window()); if (capture_window && capture_window->GetRootWindow() == window()) capture_window->ReleaseCapture(); } void WindowTreeHost::OnDisplayMetricsChanged(const display::Display& display, uint32_t metrics) { if (metrics & DisplayObserver::DISPLAY_METRIC_COLOR_SPACE && compositor_ && display.id() == GetDisplayId()) compositor_->SetDisplayColorSpaces(display.color_spaces()); // Chrome OS is handled in WindowTreeHostManager::OnDisplayMetricsChanged. // Chrome OS requires additional handling for the bounds that we do not need to // do for other OSes. #if !BUILDFLAG(IS_CHROMEOS_ASH) if (metrics & DISPLAY_METRIC_DEVICE_SCALE_FACTOR && display.id() == GetDisplayId()) OnHostResizedInPixels(GetBoundsInPixels().size()); #endif } gfx::Rect WindowTreeHost::GetTransformedRootWindowBoundsFromPixelSize( const gfx::Size& size_in_pixels) const { return GetInverseRootTransform().MapRect(gfx::Rect(size_in_pixels)); } void WindowTreeHost::SetNativeWindowOcclusionEnabled(bool enable) { native_window_occlusion_enabled_ = enable; // TODO(crbug.com/1051306) If enabled is false, make this // turn off native window occlusion on this window. Only Windows has // native window occlusion currently. } //////////////////////////////////////////////////////////////////////////////// // WindowTreeHost, private: void WindowTreeHost::DecrementVideoCaptureCount() { DCHECK_GT(video_capture_count_, 0); --video_capture_count_; MaybeUpdateComposibleVisibilityForVideoLockCountChange(); } void WindowTreeHost::MaybeUpdateComposibleVisibilityForVideoLockCountChange() { // Should only be called if the occlusion is applied to the compositor. DCHECK(!ShouldThrottleWhenOccluded()); // Only need to check for changes when transitioning between lock and no lock. if (video_capture_count_ > 1 || !compositor() || !accelerated_widget_made_visible_) { return; } const bool visible = CalculateCompositorVisibilityFromOcclusionState(); if (visible != compositor()->IsVisible() || is_transitioning_to_hidden()) UpdateCompositorVisibility(visible); } bool WindowTreeHost::CalculateCompositorVisibilityFromOcclusionState() const { switch (occlusion_state_) { case Window::OcclusionState::UNKNOWN: return true; case Window::OcclusionState::VISIBLE: return true; case Window::OcclusionState::OCCLUDED: { // The compositor needs to be visible when capturing video. return video_capture_count_ != 0; } case Window::OcclusionState::HIDDEN: // TODO: this should really return true if `video_capture_count_` is // not zero, but this likely needs other changes to really work (such // as on windows when an HWND is iconified it is sized to 0x0). return false; } } bool WindowTreeHost::ShouldReleaseResourcesWhenHidden() const { #if BUILDFLAG(IS_WIN) if (!base::FeatureList::IsEnabled( features::kApplyNativeOcclusionToCompositor) || !IsNativeWindowOcclusionEnabled()) { return false; } const std::string type = base::GetFieldTrialParamValueByFeature( features::kApplyNativeOcclusionToCompositor, features::kApplyNativeOcclusionToCompositorType); return type == features::kApplyNativeOcclusionToCompositorTypeRelease; #else return false; #endif } bool WindowTreeHost::ShouldThrottleWhenOccluded() const { #if BUILDFLAG(IS_WIN) if (!base::FeatureList::IsEnabled( features::kApplyNativeOcclusionToCompositor) || !IsNativeWindowOcclusionEnabled()) { return false; } const std::string type = base::GetFieldTrialParamValueByFeature( features::kApplyNativeOcclusionToCompositor, features::kApplyNativeOcclusionToCompositorType); return type == features::kApplyNativeOcclusionToCompositorTypeThrottle; #else return false; #endif } void WindowTreeHost::RestoreHideTransitionState() { DCHECK(is_transitioning_to_hidden()); hide_helper_.reset(); } void WindowTreeHost::FinishHideTransition() { DCHECK(is_transitioning_to_hidden()); compositor_->SetVisible(false); RestoreHideTransitionState(); } // static const base::flat_set& WindowTreeHost::GetThrottledHostsForTesting() { return HostFrameRateThrottler::GetInstance().hosts(); } void WindowTreeHost::StartReleasingResourcesForHide() { DCHECK(!is_transitioning_to_hidden()); if (compositor_->size().IsEmpty()) { // This should generally only happen during startup as Compositor silently // ignores changing the size to empty. compositor_->SetVisible(false); return; } hide_helper_ = std::make_unique(this); } void WindowTreeHost::MoveCursorToInternal(const gfx::Point& root_location, const gfx::Point& host_location) { last_cursor_request_position_in_host_ = host_location; MoveCursorToScreenLocationInPixels(host_location); client::CursorClient* cursor_client = client::GetCursorClient(window()); if (cursor_client) { const display::Display& display = display::Screen::GetScreen()->GetDisplayNearestWindow(window()); cursor_client->SetDisplay(display); } dispatcher()->OnCursorMovedToRootLocation(root_location); } void WindowTreeHost::OnCompositingEnded(ui::Compositor* compositor) { if (!holding_pointer_moves_) return; dispatcher_->ReleasePointerMoves(); holding_pointer_moves_ = false; } void WindowTreeHost::OnCompositingChildResizing(ui::Compositor* compositor) { if (!Env::GetInstance()->throttle_input_on_resize() || holding_pointer_moves_) return; dispatcher_->HoldPointerMoves(); holding_pointer_moves_ = true; } void WindowTreeHost::OnFrameSinksToThrottleUpdated( const base::flat_set& ids) { for (auto& observer : observers_) observer.OnCompositingFrameSinksToThrottleUpdated(this, ids); } } // namespace aura