| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // 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/feature_list.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/trace_event/trace_event.h" |
| #include "build/build_config.h" |
| #include "components/viz/common/features.h" |
| #include "ui/aura/client/capture_client.h" |
| #include "ui/aura/client/cursor_client.h" |
| #include "ui/aura/env.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/ime/input_method.h" |
| #include "ui/base/ime/input_method_factory.h" |
| #include "ui/base/layout.h" |
| #include "ui/base/ui_base_features.h" |
| #include "ui/base/view_prop.h" |
| #include "ui/compositor/compositor_switches.h" |
| #include "ui/compositor/dip_util.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/platform_window/platform_window_init_properties.h" |
| |
| #if defined(OS_WIN) |
| #include "ui/aura/native_window_occlusion_tracker_win.h" |
| #endif // OS_WIN |
| |
| namespace aura { |
| |
| namespace { |
| |
| const char kWindowTreeHostForAcceleratedWidget[] = |
| "__AURA_WINDOW_TREE_HOST_ACCELERATED_WIDGET__"; |
| |
| #if DCHECK_IS_ON() |
| class ScopedLocalSurfaceIdValidator { |
| public: |
| explicit ScopedLocalSurfaceIdValidator(Window* window) |
| : window_(window), |
| local_surface_id_( |
| window ? window->GetLocalSurfaceIdAllocation().local_surface_id() |
| : viz::LocalSurfaceId()) {} |
| ~ScopedLocalSurfaceIdValidator() { |
| if (window_) { |
| DCHECK_EQ(local_surface_id_, |
| window_->GetLocalSurfaceIdAllocation().local_surface_id()); |
| } |
| } |
| |
| private: |
| Window* const window_; |
| const viz::LocalSurfaceId local_surface_id_; |
| DISALLOW_COPY_AND_ASSIGN(ScopedLocalSurfaceIdValidator); |
| }; |
| #else |
| class ScopedLocalSurfaceIdValidator { |
| public: |
| explicit ScopedLocalSurfaceIdValidator(Window* window) {} |
| ~ScopedLocalSurfaceIdValidator() {} |
| }; |
| #endif |
| |
| #if defined(OS_WIN) |
| bool IsNativeWindowOcclusionEnabled() { |
| return base::FeatureList::IsEnabled(features::kCalculateNativeWinOcclusion); |
| } |
| #endif // OS_WIN |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // WindowTreeHost, public: |
| |
| WindowTreeHost::~WindowTreeHost() { |
| if (display::Screen::GetScreen()) |
| display::Screen::GetScreen()->RemoveObserver(this); |
| DCHECK(!compositor_) << "compositor must be destroyed before root window"; |
| if (owned_input_method_) { |
| delete input_method_; |
| input_method_ = nullptr; |
| } |
| } |
| |
| // static |
| WindowTreeHost* WindowTreeHost::GetForAcceleratedWidget( |
| gfx::AcceleratedWidget widget) { |
| return reinterpret_cast<WindowTreeHost*>( |
| ui::ViewProp::GetValue(widget, kWindowTreeHostForAcceleratedWidget)); |
| } |
| |
| void WindowTreeHost::InitHost() { |
| display::Display display = |
| display::Screen::GetScreen()->GetDisplayNearestWindow(window()); |
| device_scale_factor_ = display.device_scale_factor(); |
| |
| UpdateRootWindowSizeInPixels(); |
| InitCompositor(); |
| window()->env()->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); |
| } |
| |
| ui::EventSink* WindowTreeHost::event_sink() { |
| return dispatcher_.get(); |
| } |
| |
| base::WeakPtr<WindowTreeHost> 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); |
| UpdateRootWindowSizeInPixels(); |
| } |
| |
| gfx::Transform WindowTreeHost::GetInverseRootTransform() const { |
| gfx::Transform invert; |
| gfx::Transform transform = GetRootTransform(); |
| if (!transform.GetInverse(&invert)) |
| return transform; |
| return invert; |
| } |
| |
| 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::UpdateRootWindowSizeInPixels() { |
| // Validate that the LocalSurfaceId does not change. |
| bool compositor_inited = !!compositor()->root_layer(); |
| ScopedLocalSurfaceIdValidator lsi_validator(compositor_inited ? window() |
| : nullptr); |
| gfx::Rect transformed_bounds_in_pixels = |
| GetTransformedRootWindowBoundsInPixels(GetBoundsInPixels().size()); |
| window()->SetBounds(transformed_bounds_in_pixels); |
| } |
| |
| 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 { |
| auto point_3f = gfx::Point3F(gfx::PointF(*point)); |
| GetRootTransform().TransformPoint(&point_3f); |
| *point = gfx::ToFlooredPoint(point_3f.AsPointF()); |
| } |
| |
| void WindowTreeHost::ConvertPixelsToDIP(gfx::Point* point) const { |
| auto point_3f = gfx::Point3F(gfx::PointF(*point)); |
| GetInverseRootTransform().TransformPoint(&point_3f); |
| *point = gfx::ToFlooredPoint(point_3f.AsPointF()); |
| } |
| |
| 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_ = |
| ui::CreateInputMethod(this, GetAcceleratedWidget()).release(); |
| owned_input_method_ = true; |
| } |
| return input_method_; |
| } |
| |
| void WindowTreeHost::SetSharedInputMethod(ui::InputMethod* input_method) { |
| DCHECK(!input_method_); |
| input_method_ = input_method; |
| owned_input_method_ = false; |
| } |
| |
| ui::EventDispatchDetails WindowTreeHost::DispatchKeyEventPostIME( |
| ui::KeyEvent* event, |
| DispatchKeyEventPostIMECallback callback) { |
| // If dispatch to IME is already disabled we shouldn't reach here. |
| DCHECK(!dispatcher_->should_skip_ime()); |
| dispatcher_->set_skip_ime(true); |
| // We should bypass event rewriters here as they've been tried before. |
| ui::EventDispatchDetails dispatch_details = |
| event_sink()->OnEventFromSource(event); |
| if (!dispatch_details.dispatcher_destroyed) |
| dispatcher_->set_skip_ime(false); |
| RunDispatchKeyEventPostIMECallback(event, std::move(callback)); |
| 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()); |
| compositor()->SetVisible(true); |
| ShowImpl(); |
| window()->Show(); |
| } |
| |
| void WindowTreeHost::Hide() { |
| HideImpl(); |
| if (compositor()) |
| compositor()->SetVisible(false); |
| } |
| |
| std::unique_ptr<ScopedKeyboardHook> WindowTreeHost::CaptureSystemKeyEvents( |
| base::Optional<base::flat_set<ui::DomCode>> 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<ScopedSimpleKeyboardHook>(std::move(dom_codes)); |
| |
| if (CaptureSystemKeyEventsImpl(std::move(dom_codes))) |
| return std::make_unique<ScopedKeyboardHook>(weak_factory_.GetWeakPtr()); |
| return nullptr; |
| } |
| |
| bool WindowTreeHost::ShouldSendKeyEventToIme() { |
| return true; |
| } |
| |
| void WindowTreeHost::EnableNativeWindowOcclusionTracking() { |
| #if defined(OS_WIN) |
| if (IsNativeWindowOcclusionEnabled()) { |
| NativeWindowOcclusionTrackerWin::GetOrCreateInstance()->Enable(window()); |
| } |
| #endif // OS_WIN |
| } |
| |
| void WindowTreeHost::DisableNativeWindowOcclusionTracking() { |
| #if defined(OS_WIN) |
| if (IsNativeWindowOcclusionEnabled()) { |
| occlusion_state_ = Window::OcclusionState::UNKNOWN; |
| NativeWindowOcclusionTrackerWin::GetOrCreateInstance()->Disable(window()); |
| } |
| #endif // OS_WIN |
| } |
| |
| void WindowTreeHost::SetNativeWindowOcclusionState( |
| Window::OcclusionState state) { |
| if (occlusion_state_ != state) { |
| occlusion_state_ = state; |
| for (WindowTreeHostObserver& observer : observers_) |
| observer.OnOcclusionStateChanged(this, state); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // WindowTreeHost, protected: |
| |
| WindowTreeHost::WindowTreeHost(std::unique_ptr<Window> window) |
| : window_(window.release()), // See header for details on ownership. |
| occlusion_state_(Window::OcclusionState::UNKNOWN), |
| last_cursor_(ui::CursorType::kNull), |
| input_method_(nullptr), |
| owned_input_method_(false), |
| weak_factory_(this) { |
| if (!window_) |
| window_ = new Window(nullptr); |
| display::Screen::GetScreen()->AddObserver(this); |
| auto display = display::Screen::GetScreen()->GetDisplayNearestWindow(window_); |
| device_scale_factor_ = display.device_scale_factor(); |
| } |
| |
| void WindowTreeHost::IntializeDeviceScaleFactor(float device_scale_factor) { |
| DCHECK(!compositor_->root_layer()) << "Only call this before InitHost()"; |
| device_scale_factor_ = device_scale_factor; |
| } |
| |
| void WindowTreeHost::DestroyCompositor() { |
| if (compositor_) { |
| compositor_->RemoveObserver(this); |
| compositor_.reset(); |
| } |
| } |
| |
| void WindowTreeHost::DestroyDispatcher() { |
| 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::CreateCompositor( |
| const viz::FrameSinkId& frame_sink_id, |
| bool force_software_compositor, |
| ui::ExternalBeginFrameClient* external_begin_frame_client, |
| bool are_events_in_pixels, |
| const char* trace_environment_name, |
| bool automatically_allocate_surface_ids) { |
| DCHECK(window()->env()); |
| Env* env = window()->env(); |
| ui::ContextFactory* context_factory = env->context_factory(); |
| DCHECK(context_factory); |
| ui::ContextFactoryPrivate* context_factory_private = |
| env->context_factory_private(); |
| compositor_ = std::make_unique<ui::Compositor>( |
| (!context_factory_private || frame_sink_id.is_valid()) |
| ? frame_sink_id |
| : context_factory_private->AllocateFrameSinkId(), |
| context_factory, context_factory_private, |
| base::ThreadTaskRunnerHandle::Get(), ui::IsPixelCanvasRecordingEnabled(), |
| external_begin_frame_client, force_software_compositor, |
| trace_environment_name, automatically_allocate_surface_ids); |
| #if defined(OS_CHROMEOS) |
| compositor_->AddObserver(this); |
| #endif |
| if (!dispatcher()) { |
| window()->Init(ui::LAYER_NOT_DRAWN); |
| window()->set_host(this); |
| window()->SetName("RootWindow"); |
| dispatcher_ = |
| std::make_unique<WindowEventDispatcher>(this, are_events_in_pixels); |
| } |
| } |
| |
| void WindowTreeHost::InitCompositor() { |
| DCHECK(!compositor_->root_layer()); |
| compositor_->SetScaleAndSize(device_scale_factor_, GetBoundsInPixels().size(), |
| window()->GetLocalSurfaceIdAllocation()); |
| compositor_->SetRootLayer(window()->layer()); |
| |
| display::Display display = |
| display::Screen::GetScreen()->GetDisplayNearestWindow(window()); |
| compositor_->SetDisplayColorSpace(display.color_space()); |
| } |
| |
| void WindowTreeHost::OnAcceleratedWidgetAvailable() { |
| compositor_->SetAcceleratedWidget(GetAcceleratedWidget()); |
| prop_.reset(new ui::ViewProp(GetAcceleratedWidget(), |
| kWindowTreeHostForAcceleratedWidget, this)); |
| } |
| |
| void WindowTreeHost::OnHostMovedInPixels( |
| const gfx::Point& new_location_in_pixels) { |
| TRACE_EVENT1("ui", "WindowTreeHost::OnHostMovedInPixels", "origin", |
| new_location_in_pixels.ToString()); |
| |
| for (WindowTreeHostObserver& observer : observers_) |
| observer.OnHostMovedInPixels(this, new_location_in_pixels); |
| } |
| |
| void WindowTreeHost::OnHostResizedInPixels( |
| const gfx::Size& new_size_in_pixels, |
| const viz::LocalSurfaceIdAllocation& new_local_surface_id_allocation) { |
| // TODO(jonross) Unify all OnHostResizedInPixels to have both |
| // viz::LocalSurfaceId and allocation time as optional parameters. |
| display::Display display = |
| display::Screen::GetScreen()->GetDisplayNearestWindow(window()); |
| device_scale_factor_ = display.device_scale_factor(); |
| UpdateRootWindowSizeInPixels(); |
| |
| // Allocate a new LocalSurfaceId for the new state. |
| viz::LocalSurfaceIdAllocation local_surface_id_allocation( |
| new_local_surface_id_allocation); |
| if (ShouldAllocateLocalSurfaceIdOnResize() && |
| !new_local_surface_id_allocation.IsValid()) { |
| window_->AllocateLocalSurfaceId(); |
| local_surface_id_allocation = window_->GetLocalSurfaceIdAllocation(); |
| } |
| std::unique_ptr<ScopedLocalSurfaceIdValidator> lsi_validator; |
| // With mus |local_surface_id_allocation|, may be applied to Window by way of |
| // Compositor::SetScaleAndSize() so that we can't check here. |
| if (window()->env()->mode() == Env::Mode::LOCAL) |
| lsi_validator = std::make_unique<ScopedLocalSurfaceIdValidator>(window()); |
| compositor_->SetScaleAndSize(device_scale_factor_, new_size_in_pixels, |
| local_surface_id_allocation); |
| |
| 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_->SetDisplayColorSpace(display.color_space()); |
| } |
| |
| 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) { |
| display::Screen* screen = display::Screen::GetScreen(); |
| if (compositor_ && |
| display.id() == screen->GetDisplayNearestView(window()).id()) { |
| compositor_->SetDisplayColorSpace(display.color_space()); |
| } |
| } |
| } |
| |
| gfx::Rect WindowTreeHost::GetTransformedRootWindowBoundsInPixels( |
| const gfx::Size& size_in_pixels) const { |
| gfx::Rect bounds(size_in_pixels); |
| gfx::RectF new_bounds = |
| gfx::ScaleRect(gfx::RectF(bounds), 1.0f / device_scale_factor_); |
| window()->layer()->transform().TransformRect(&new_bounds); |
| return gfx::ToEnclosingRect(new_bounds); |
| } |
| |
| bool WindowTreeHost::ShouldAllocateLocalSurfaceIdOnResize() { |
| return true; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // WindowTreeHost, private: |
| |
| 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; |
| DCHECK(!synchronization_start_time_.is_null()); |
| UMA_HISTOGRAM_TIMES("UI.WindowTreeHost.SurfaceSynchronizationDuration", |
| base::TimeTicks::Now() - synchronization_start_time_); |
| } |
| |
| void WindowTreeHost::OnCompositingChildResizing(ui::Compositor* compositor) { |
| if (!window()->env()->throttle_input_on_resize() || holding_pointer_moves_) |
| return; |
| synchronization_start_time_ = base::TimeTicks::Now(); |
| dispatcher_->HoldPointerMoves(); |
| holding_pointer_moves_ = true; |
| } |
| |
| void WindowTreeHost::OnCompositingShuttingDown(ui::Compositor* compositor) { |
| compositor->RemoveObserver(this); |
| } |
| |
| } // namespace aura |