// 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