| // Copyright 2015 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/mus/window_tree_host_mus.h" |
| |
| #include <limits> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/env.h" |
| #include "ui/aura/mus/input_method_mus.h" |
| #include "ui/aura/mus/mus_types.h" |
| #include "ui/aura/mus/window_port_mus.h" |
| #include "ui/aura/mus/window_tree_client.h" |
| #include "ui/aura/mus/window_tree_host_mus_delegate.h" |
| #include "ui/aura/mus/window_tree_host_mus_init_params.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_delegate.h" |
| #include "ui/aura/window_event_dispatcher.h" |
| #include "ui/aura/window_tracker.h" |
| #include "ui/aura/window_tree_host_observer.h" |
| #include "ui/base/class_property.h" |
| #include "ui/base/hit_test.h" |
| #include "ui/base/layout.h" |
| #include "ui/base/ui_base_features.h" |
| #include "ui/base/ui_base_switches_util.h" |
| #include "ui/display/display.h" |
| #include "ui/display/screen.h" |
| #include "ui/events/event.h" |
| #include "ui/events/gestures/gesture_recognizer.h" |
| #include "ui/events/gestures/gesture_recognizer_observer.h" |
| #include "ui/gfx/geometry/dip_util.h" |
| #include "ui/platform_window/stub/stub_window.h" |
| |
| DEFINE_UI_CLASS_PROPERTY_TYPE(aura::WindowTreeHostMus*) |
| |
| namespace aura { |
| |
| namespace { |
| |
| DEFINE_UI_CLASS_PROPERTY_KEY(WindowTreeHostMus*, kWindowTreeHostMusKey, nullptr) |
| |
| // Start at the max and decrease as in SingleProcessMash these values must not |
| // overlap with values assigned by Ozone's PlatformWindow (which starts at 1 |
| // and increases). |
| uint32_t next_accelerated_widget_id = std::numeric_limits<uint32_t>::max(); |
| |
| // This class handles the gesture events occurring on the root window and sends |
| // them to the content window during the window move. Typically gesture events |
| // will stop arriving once PerformWindowMove is invoked, but sometimes events |
| // are already queued and arrive to the root window. They should be handled |
| // by the content window. See https://crbug.com/943316. |
| class RemainingGestureEventHandler : public ui::EventHandler, WindowObserver { |
| public: |
| RemainingGestureEventHandler(Window* content_window, Window* root) |
| : content_window_({content_window}), root_(root) { |
| root_->AddPostTargetHandler(this); |
| root_->AddObserver(this); |
| } |
| ~RemainingGestureEventHandler() override { |
| if (root_) |
| StopObserving(); |
| } |
| |
| private: |
| void StopObserving() { |
| root_->RemoveObserver(this); |
| root_->RemovePostTargetHandler(this); |
| root_ = nullptr; |
| } |
| |
| // ui::EventHandler: |
| void OnGestureEvent(ui::GestureEvent* event) override { |
| if (!content_window_.windows().empty()) |
| (*content_window_.windows().begin())->delegate()->OnGestureEvent(event); |
| } |
| // WindowObserver: |
| void OnWindowDestroying(Window* window) override { StopObserving(); } |
| |
| WindowTracker content_window_; |
| Window* root_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RemainingGestureEventHandler); |
| }; |
| |
| // ScopedTouchTransferController controls the transfer of touch events for |
| // window move loop. It transfers touches before the window move starts, and |
| // then transfers them back to the original window when the window move ends. |
| // However this transferring back to the original shouldn't happen if the client |
| // wants to continue the dragging on another window (like attaching the dragged |
| // tab to another window). |
| class ScopedTouchTransferController : public ui::GestureRecognizerObserver { |
| public: |
| ScopedTouchTransferController(Window* source, Window* dest) |
| : tracker_({source, dest}), |
| remaining_gesture_event_handler_(source, dest), |
| gesture_recognizer_(source->env()->gesture_recognizer()) { |
| gesture_recognizer_->TransferEventsTo( |
| source, dest, ui::TransferTouchesBehavior::kDontCancel); |
| gesture_recognizer_->AddObserver(this); |
| } |
| ~ScopedTouchTransferController() override { |
| gesture_recognizer_->RemoveObserver(this); |
| if (tracker_.windows().size() == 2) { |
| Window* source = tracker_.Pop(); |
| Window* dest = tracker_.Pop(); |
| gesture_recognizer_->TransferEventsTo( |
| dest, source, ui::TransferTouchesBehavior::kDontCancel); |
| } |
| } |
| |
| private: |
| // ui::GestureRecognizerObserver: |
| void OnActiveTouchesCanceledExcept( |
| ui::GestureConsumer* not_cancelled) override {} |
| void OnEventsTransferred( |
| ui::GestureConsumer* current_consumer, |
| ui::GestureConsumer* new_consumer, |
| ui::TransferTouchesBehavior transfer_touches_behavior) override { |
| if (tracker_.windows().size() <= 1) |
| return; |
| Window* dest = tracker_.windows()[1]; |
| if (current_consumer == dest) |
| tracker_.Remove(dest); |
| } |
| void OnActiveTouchesCanceled(ui::GestureConsumer* consumer) override {} |
| |
| WindowTracker tracker_; |
| RemainingGestureEventHandler remaining_gesture_event_handler_; |
| ui::GestureRecognizer* gesture_recognizer_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedTouchTransferController); |
| }; |
| |
| void OnPerformWindowMoveDone( |
| std::unique_ptr<ScopedTouchTransferController> controller, |
| base::OnceCallback<void(bool)> callback, |
| bool success) { |
| controller.reset(); |
| std::move(callback).Run(success); |
| } |
| |
| void OnDispatchKeyEventComplete( |
| base::OnceCallback<void(ws::mojom::EventResult)> cb, |
| bool result) { |
| std::move(cb).Run(result ? ws::mojom::EventResult::HANDLED |
| : ws::mojom::EventResult::UNHANDLED); |
| } |
| |
| void OnDispatchKeyEventPostIMEComplete( |
| base::OnceCallback<void(ws::mojom::EventResult)> cb, |
| bool handled, |
| bool stopped_propagation) { |
| OnDispatchKeyEventComplete(std::move(cb), handled); |
| } |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // WindowTreeHostMus, public: |
| |
| WindowTreeHostMus::WindowTreeHostMus(WindowTreeHostMusInitParams init_params) |
| : WindowTreeHostPlatform( |
| std::make_unique<Window>(nullptr, |
| std::move(init_params.window_port))), |
| display_id_(init_params.display_id), |
| delegate_(init_params.window_tree_client), |
| show_state_observer_( |
| window(), |
| base::BindRepeating(&WindowTreeHostMus::OnWindowShowStateDidChange, |
| base::Unretained(this))) { |
| gfx::Rect bounds_in_pixels; |
| window()->SetProperty(kWindowTreeHostMusKey, this); |
| // TODO(sky): find a cleaner way to set this! Revisit this now that |
| // constructor takes a Window. |
| WindowPortMus* window_mus = WindowPortMus::Get(window()); |
| window_mus->window_ = window(); |
| // Apply the properties before initializing the window, that way the server |
| // seems them at the time the window is created. |
| for (auto& pair : init_params.properties) |
| window_mus->SetPropertyFromServer(pair.first, &pair.second); |
| // If window-server is hosting viz, then use the FrameSinkId from the server. |
| // In other cases, let a valid FrameSinkId be selected by |
| // context_factory_private(). |
| const bool force_software_compositor = false; |
| ui::ExternalBeginFrameClient* external_begin_frame_client = nullptr; |
| const bool are_events_in_pixels = false; |
| CreateCompositor(window_mus->GenerateFrameSinkIdFromServerId(), |
| force_software_compositor, external_begin_frame_client, |
| are_events_in_pixels, |
| /* trace_environment_name */ nullptr, |
| /* automatically_allocate_surface_ids */ false); |
| gfx::AcceleratedWidget accelerated_widget; |
| // We need accelerated widget numbers to be different for each window and |
| // fit in the smallest sizeof(AcceleratedWidget) uint32_t has this property. |
| #if defined(OS_WIN) || defined(OS_ANDROID) |
| accelerated_widget = |
| reinterpret_cast<gfx::AcceleratedWidget>(next_accelerated_widget_id--); |
| #else |
| accelerated_widget = |
| static_cast<gfx::AcceleratedWidget>(next_accelerated_widget_id--); |
| #endif |
| OnAcceleratedWidgetAvailable(accelerated_widget); |
| |
| delegate_->OnWindowTreeHostCreated(this); |
| |
| // Do not advertise accelerated widget; already set manually. |
| const bool use_default_accelerated_widget = false; |
| SetPlatformWindow(std::make_unique<ui::StubWindow>( |
| this, use_default_accelerated_widget, bounds_in_pixels)); |
| |
| if (!features::IsMojoImfEnabled()) { |
| // NOTE: This creates one InputMethodMus per display, despite the |
| // call to SetSharedInputMethod() below. |
| input_method_mus_ = std::make_unique<InputMethodMus>(this, this); |
| input_method_mus_->Init(init_params.window_tree_client->connector()); |
| SetSharedInputMethod(input_method_mus_.get()); |
| } |
| |
| compositor()->SetBackgroundColor(SK_ColorTRANSPARENT); |
| |
| // Mus windows are assumed hidden. |
| compositor()->SetVisible(false); |
| } |
| |
| WindowTreeHostMus::~WindowTreeHostMus() { |
| DestroyCompositor(); |
| DestroyDispatcher(); |
| } |
| |
| // static |
| WindowTreeHostMus* WindowTreeHostMus::ForWindow(aura::Window* window) { |
| if (!window) |
| return nullptr; |
| |
| aura::Window* root = window->GetRootWindow(); |
| if (!root) { |
| // During initial setup this function is called for the root, before the |
| // WindowTreeHost has been registered so that GetRootWindow() returns null. |
| // Fallback to checking window, in case it really is the root. |
| return window->GetProperty(kWindowTreeHostMusKey); |
| } |
| |
| return root->GetProperty(kWindowTreeHostMusKey); |
| } |
| |
| void WindowTreeHostMus::DispatchKeyEventFromServer( |
| ui::KeyEvent* event, |
| base::OnceCallback<void(ws::mojom::EventResult)> cb) { |
| ui::InputMethod* input_method = GetInputMethod(); |
| if (input_method) { |
| ui::AsyncKeyDispatcher* dispatcher = input_method->GetAsyncKeyDispatcher(); |
| if (dispatcher) { |
| dispatcher->DispatchKeyEventAsync( |
| event, base::BindOnce(&OnDispatchKeyEventComplete, std::move(cb))); |
| return; |
| } |
| } |
| DispatchKeyEventPostIME( |
| event, base::BindOnce(&OnDispatchKeyEventPostIMEComplete, std::move(cb))); |
| } |
| |
| void WindowTreeHostMus::SetBounds( |
| const gfx::Rect& bounds_in_dip, |
| const viz::LocalSurfaceIdAllocation& local_surface_id_allocation) { |
| viz::LocalSurfaceIdAllocation actual_local_surface_id_allocation = |
| local_surface_id_allocation; |
| // This uses GetScaleFactorForNativeView() as it's called at a time when the |
| // scale factor may not have been applied to the Compositor yet (the |
| // call to WindowTreeHostPlatform::SetBoundsInPixels() updates the |
| // Compositor). |
| // Do not use ConvertRectToPixel, enclosing rects cause problems. In |
| // particular, ConvertRectToPixel's result varies based on the location. |
| // This *must* match the conversion used by ClientRoot, otherwise the two will |
| // be out of sync. See // https://crbug.com/952095 for more details. |
| const float dsf = ui::GetScaleFactorForNativeView(window()); |
| const gfx::Rect pixel_bounds( |
| gfx::ScaleToFlooredPoint(bounds_in_dip.origin(), dsf), |
| gfx::ScaleToCeiledSize(bounds_in_dip.size(), dsf)); |
| if (!is_server_setting_bounds_) { |
| // Update the LocalSurfaceIdAllocation here, rather than in WindowTreeHost |
| // as WindowTreeClient (the delegate) needs that information before |
| // OnWindowTreeHostBoundsWillChange(). |
| if (!local_surface_id_allocation.IsValid() && |
| ShouldAllocateLocalSurfaceIdOnResize()) { |
| if (pixel_bounds.size() != compositor()->size()) |
| window()->AllocateLocalSurfaceId(); |
| actual_local_surface_id_allocation = |
| window()->GetLocalSurfaceIdAllocation(); |
| } |
| delegate_->OnWindowTreeHostBoundsWillChange(this, bounds_in_dip); |
| } |
| bounds_in_dip_ = bounds_in_dip; |
| WindowTreeHostPlatform::SetBoundsInPixels(pixel_bounds, |
| actual_local_surface_id_allocation); |
| } |
| |
| void WindowTreeHostMus::SetBoundsFromServer( |
| const gfx::Rect& bounds, |
| ui::WindowShowState state, |
| const viz::LocalSurfaceIdAllocation& local_surface_id_allocation) { |
| base::AutoReset<bool> resetter(&is_server_setting_bounds_, true); |
| // When there's a non-default |state|, we want to set that property and then |
| // the bounds, so that by the time client code observes a bounds change the |
| // show state is already updated, and by the time client code observes a state |
| // change the bounds are already updated as well. To do this, we set the state |
| // here, and as the first WindowObserver on |window()|, update the bounds. |
| if (state != ui::SHOW_STATE_DEFAULT && |
| window()->GetProperty(aura::client::kShowStateKey) != state) { |
| server_bounds_ = &bounds; |
| server_lsia_ = &local_surface_id_allocation; |
| window()->SetProperty(aura::client::kShowStateKey, state); |
| DCHECK(!server_bounds_); |
| DCHECK(!server_lsia_); |
| return; |
| } |
| SetBounds(bounds, local_surface_id_allocation); |
| } |
| |
| void WindowTreeHostMus::SetClientArea( |
| const gfx::Insets& insets, |
| const std::vector<gfx::Rect>& additional_client_area) { |
| delegate_->OnWindowTreeHostClientAreaWillChange(this, insets, |
| additional_client_area); |
| } |
| |
| void WindowTreeHostMus::SetOpacity(float value) { |
| delegate_->OnWindowTreeHostSetOpacity(this, value); |
| } |
| |
| void WindowTreeHostMus::DeactivateWindow() { |
| delegate_->OnWindowTreeHostDeactivateWindow(this); |
| } |
| |
| void WindowTreeHostMus::StackAbove(Window* window) { |
| delegate_->OnWindowTreeHostStackAbove(this, window); |
| } |
| |
| void WindowTreeHostMus::StackAtTop() { |
| delegate_->OnWindowTreeHostStackAtTop(this); |
| } |
| |
| void WindowTreeHostMus::PerformWindowMove( |
| Window* content_window, |
| ws::mojom::MoveLoopSource mus_source, |
| const gfx::Point& cursor_location, |
| int hit_test, |
| base::OnceCallback<void(bool)> callback) { |
| DCHECK(window()->Contains(content_window)); |
| std::unique_ptr<ScopedTouchTransferController> scoped_controller; |
| if (content_window != window()) { |
| scoped_controller = std::make_unique<ScopedTouchTransferController>( |
| content_window, window()); |
| } |
| content_window->ReleaseCapture(); |
| delegate_->OnWindowTreeHostPerformWindowMove( |
| this, mus_source, cursor_location, hit_test, |
| base::BindOnce(&OnPerformWindowMoveDone, std::move(scoped_controller), |
| std::move(callback))); |
| } |
| |
| void WindowTreeHostMus::CancelWindowMove() { |
| delegate_->OnWindowTreeHostCancelWindowMove(this); |
| } |
| |
| display::Display WindowTreeHostMus::GetDisplay() const { |
| display::Display display; |
| display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id_, &display); |
| return display; |
| } |
| |
| void WindowTreeHostMus::SetPendingLocalSurfaceIdFromServer( |
| const viz::LocalSurfaceIdAllocation& id) { |
| DCHECK(id.IsValid()); |
| pending_local_surface_id_from_server_ = id; |
| } |
| |
| base::Optional<viz::LocalSurfaceIdAllocation> |
| WindowTreeHostMus::TakePendingLocalSurfaceIdFromServer() { |
| base::Optional<viz::LocalSurfaceIdAllocation> id; |
| std::swap(id, pending_local_surface_id_from_server_); |
| return id; |
| } |
| |
| void WindowTreeHostMus::HideImpl() { |
| WindowTreeHostPlatform::HideImpl(); |
| window()->Hide(); |
| } |
| |
| void WindowTreeHostMus::DispatchEvent(ui::Event* event) { |
| DCHECK(!event->IsKeyEvent()); |
| WindowTreeHostPlatform::DispatchEvent(event); |
| } |
| |
| void WindowTreeHostMus::OnClosed() { |
| } |
| |
| void WindowTreeHostMus::OnActivationChanged(bool active) { |
| if (active) |
| GetInputMethod()->OnFocus(); |
| else |
| GetInputMethod()->OnBlur(); |
| WindowTreeHostPlatform::OnActivationChanged(active); |
| } |
| |
| void WindowTreeHostMus::OnCloseRequest() { |
| OnHostCloseRequested(); |
| } |
| |
| int64_t WindowTreeHostMus::GetDisplayId() { |
| return display_id_; |
| } |
| |
| bool WindowTreeHostMus::ShouldAllocateLocalSurfaceIdOnResize() { |
| return WindowPortMus::Get(window())->window_mus_type() == |
| WindowMusType::TOP_LEVEL && |
| (window()->GetLocalSurfaceIdAllocation().IsValid() || |
| pending_local_surface_id_from_server_); |
| } |
| |
| gfx::Rect WindowTreeHostMus::GetTransformedRootWindowBoundsInPixels( |
| const gfx::Size& size_in_pixels) const { |
| // Special case asking for the current size to ensure the aura::Window is |
| // given the same size as |bounds_in_dip_|. To do otherwise could result in |
| // rounding errors and the aura::Window given a different size. |
| if (size_in_pixels == GetBoundsInPixels().size() && |
| window()->layer()->transform().IsIdentity()) { |
| return gfx::Rect(bounds_in_dip_.size()); |
| } |
| return WindowTreeHostPlatform::GetTransformedRootWindowBoundsInPixels( |
| size_in_pixels); |
| } |
| |
| void WindowTreeHostMus::SetTextInputState(ui::mojom::TextInputStatePtr state) { |
| WindowPortMus::Get(window())->SetTextInputState(std::move(state)); |
| } |
| |
| void WindowTreeHostMus::SetImeVisibility(bool visible, |
| ui::mojom::TextInputStatePtr state) { |
| WindowPortMus::Get(window())->SetImeVisibility(visible, std::move(state)); |
| } |
| |
| bool WindowTreeHostMus::ConnectToImeEngine( |
| ime::mojom::ImeEngineRequest engine_request, |
| ime::mojom::ImeEngineClientPtr client) { |
| delegate_->ConnectToImeEngine(std::move(engine_request), std::move(client)); |
| return true; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // WindowTreeHostMus, protected: |
| |
| void WindowTreeHostMus::SetBoundsInPixels( |
| const gfx::Rect& bounds, |
| const viz::LocalSurfaceIdAllocation& local_surface_id_allocation) { |
| // As UI code operates in DIPs (as does the window-service APIs), this |
| // function is very seldomly used, and so converts to DIPs. |
| SetBounds( |
| gfx::ConvertRectToDIP(ui::GetScaleFactorForNativeView(window()), bounds), |
| local_surface_id_allocation); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // WindowTreeHostMus, private: |
| |
| void WindowTreeHostMus::OnWindowShowStateDidChange() { |
| if (!server_bounds_) |
| return; |
| |
| DCHECK(is_server_setting_bounds_); |
| DCHECK(server_lsia_); |
| SetBounds(*server_bounds_, *server_lsia_); |
| server_bounds_ = nullptr; |
| server_lsia_ = nullptr; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // WindowTreeHostMus::WindowShowStateChangeObserver, public: |
| |
| WindowTreeHostMus::WindowShowStateChangeObserver::WindowShowStateChangeObserver( |
| aura::Window* window, |
| base::RepeatingClosure show_state_changed_callback) |
| : show_state_changed_callback_(show_state_changed_callback) { |
| window->AddObserver(this); |
| } |
| |
| WindowTreeHostMus::WindowShowStateChangeObserver:: |
| ~WindowShowStateChangeObserver() = default; |
| |
| void WindowTreeHostMus::WindowShowStateChangeObserver::OnWindowPropertyChanged( |
| aura::Window* window, |
| const void* key, |
| intptr_t old) { |
| if (key == client::kShowStateKey) |
| show_state_changed_callback_.Run(); |
| } |
| |
| } // namespace aura |