| // Copyright 2018 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 "services/ws/focus_handler.h" |
| |
| #include "services/ws/client_change.h" |
| #include "services/ws/client_change_tracker.h" |
| #include "services/ws/server_window.h" |
| #include "services/ws/window_properties.h" |
| #include "services/ws/window_service.h" |
| #include "services/ws/window_service_delegate.h" |
| #include "services/ws/window_tree.h" |
| #include "ui/aura/client/focus_client.h" |
| #include "ui/wm/public/activation_client.h" |
| |
| namespace ws { |
| |
| FocusHandler::FocusHandler(WindowTree* window_tree) |
| : window_tree_(window_tree) { |
| window_tree_->window_service_->focus_client()->AddObserver(this); |
| } |
| |
| FocusHandler::~FocusHandler() { |
| window_tree_->window_service_->focus_client()->RemoveObserver(this); |
| } |
| |
| bool FocusHandler::SetFocus(aura::Window* window) { |
| if (window && !IsFocusableWindow(window)) { |
| DVLOG(1) << "SetFocus failed (access denied or invalid window)"; |
| return false; |
| } |
| |
| aura::client::FocusClient* focus_client = |
| window_tree_->window_service_->focus_client(); |
| ServerWindow* server_window = ServerWindow::GetMayBeNull(window); |
| if (window == focus_client->GetFocusedWindow()) { |
| if (!window) |
| return true; |
| |
| if (server_window->focus_owner() != window_tree_) { |
| // The focused window didn't change, but the client that owns focus did |
| // (see |ServerWindow::focus_owner_| for details on this). Notify the |
| // current owner that it lost focus. |
| if (server_window->focus_owner()) { |
| server_window->focus_owner()->window_tree_client_->OnWindowFocused( |
| kInvalidTransportId); |
| } |
| server_window->set_focus_owner(window_tree_); |
| } |
| return true; |
| } |
| |
| // The client is asking to remove focus from a window. This is typically a |
| // side effect of the window becoming, or about to become, an unfocusable |
| // Window (for example, the Window is hiding). Windows becoming unfocusable is |
| // handled locally. Assume the request is for such a scenario and return |
| // true. Returning false means the client will attempt to revert to the |
| // previously focused window, which may cause unexpected activation changes. |
| // |
| // To process null requests conflicts with top-level activation changes. For |
| // example, the typical sequence when a window is hidden is to first remove |
| // focus, and then hide the window. FocusController keys off window hiding to |
| // move activation. If this code were to set focus to null, FocusController |
| // would not see the window hiding (because the active window was set to null) |
| // and not automatically activate the next window. |
| // |
| // Another possibility for this code is to handle null as a signal to move |
| // focus to the active window (if there is one). I'm going with the simpler |
| // approach for now. |
| if (!window) |
| return true; |
| |
| ClientChange change(window_tree_->property_change_tracker_.get(), window, |
| ClientChangeType::kFocus); |
| |
| // FocusController has a special API to reset focus inside the active window, |
| // which happens when a view requests focus (e.g. the find bar). |
| // https://crbug.com/880533 |
| wm::ActivationClient* activation_client = |
| wm::GetActivationClient(window->GetRootWindow()); |
| if (activation_client) { |
| aura::Window* active_window = activation_client->GetActiveWindow(); |
| if (active_window && active_window->Contains(window)) { |
| focus_client->ResetFocusWithinActiveWindow(window); |
| if (focus_client->GetFocusedWindow() != window) { |
| DVLOG(1) << "SetFocus failed (FocusClient::ResetFocusWithinActiveWindow" |
| << " failed for " << window->GetName() << ")"; |
| return false; |
| } |
| if (server_window) |
| server_window->set_focus_owner(window_tree_); |
| return true; |
| } |
| } |
| |
| focus_client->FocusWindow(window); |
| if (focus_client->GetFocusedWindow() != window) { |
| DVLOG(1) << "SetFocus failed (FocusClient::FocusWindow call failed for " |
| << window->GetName() << ")"; |
| return false; |
| } |
| |
| if (server_window) |
| server_window->set_focus_owner(window_tree_); |
| return true; |
| } |
| |
| void FocusHandler::SetCanFocus(aura::Window* window, bool can_focus) { |
| if (window && (window_tree_->IsClientCreatedWindow(window) || |
| window_tree_->IsClientRootWindow(window))) { |
| window->SetProperty(kCanFocus, can_focus); |
| } else { |
| DVLOG(1) << "SetCanFocus failed (invalid or unknown window)"; |
| } |
| } |
| |
| bool FocusHandler::IsFocusableWindow(aura::Window* window) const { |
| if (!window) |
| return true; // Used to clear focus. |
| |
| if (!window->IsVisible() || !window->GetRootWindow()) |
| return false; // The window must be drawn and attached to a root. |
| |
| return (window_tree_->IsClientCreatedWindow(window) || |
| window_tree_->IsClientRootWindow(window)); |
| } |
| |
| bool FocusHandler::IsEmbeddedClient(ServerWindow* server_window) const { |
| return server_window->embedded_window_tree() == window_tree_; |
| } |
| |
| bool FocusHandler::IsOwningClient(ServerWindow* server_window) const { |
| return server_window->owning_window_tree() == window_tree_; |
| } |
| |
| void FocusHandler::OnWindowFocused(aura::Window* gained_focus, |
| aura::Window* lost_focus) { |
| ClientChangeTracker* change_tracker = |
| window_tree_->property_change_tracker_.get(); |
| if (change_tracker->IsProcessingChangeForWindow(lost_focus, |
| ClientChangeType::kFocus) || |
| change_tracker->IsProcessingChangeForWindow(gained_focus, |
| ClientChangeType::kFocus)) { |
| // The client initiated the change, don't notify the client. |
| return; |
| } |
| |
| // The client did not request the focus change. Update state appropriately. |
| // Prefer the embedded client over the owning client. |
| bool notified_gained = false; |
| if (gained_focus) { |
| ServerWindow* server_window = ServerWindow::GetMayBeNull(gained_focus); |
| if (server_window && (IsEmbeddedClient(server_window) || |
| (!server_window->embedded_window_tree() && |
| IsOwningClient(server_window)))) { |
| server_window->set_focus_owner(window_tree_); |
| window_tree_->window_tree_client_->OnWindowFocused( |
| window_tree_->TransportIdForWindow(gained_focus)); |
| notified_gained = true; |
| } |
| } |
| |
| if (lost_focus && !notified_gained) { |
| ServerWindow* server_window = ServerWindow::GetMayBeNull(lost_focus); |
| if (server_window && server_window->focus_owner() == window_tree_) { |
| server_window->set_focus_owner(nullptr); |
| window_tree_->window_tree_client_->OnWindowFocused(kInvalidTransportId); |
| } |
| } |
| } |
| |
| } // namespace ws |