blob: c7a2eda6c1fbcde7ff6e18bdde79841f0f2382a7 [file] [log] [blame]
// 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) {
FocusHandler::~FocusHandler() {
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 =
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()) {
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,
// FocusController has a special API to reset focus inside the active window,
// which happens when a view requests focus (e.g. the find bar).
wm::ActivationClient* activation_client =
if (activation_client) {
aura::Window* active_window = activation_client->GetActiveWindow();
if (active_window && active_window->Contains(window)) {
if (focus_client->GetFocusedWindow() != window) {
DVLOG(1) << "SetFocus failed (FocusClient::ResetFocusWithinActiveWindow"
<< " failed for " << window->GetName() << ")";
return false;
if (server_window)
return true;
if (focus_client->GetFocusedWindow() != window) {
DVLOG(1) << "SetFocus failed (FocusClient::FocusWindow call failed for "
<< window->GetName() << ")";
return false;
if (server_window)
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) ||
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 =
if (change_tracker->IsProcessingChangeForWindow(lost_focus,
ClientChangeType::kFocus) ||
ClientChangeType::kFocus)) {
// The client initiated the change, don't notify the client.
// 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)))) {
notified_gained = true;
if (lost_focus && !notified_gained) {
ServerWindow* server_window = ServerWindow::GetMayBeNull(lost_focus);
if (server_window && server_window->focus_owner() == window_tree_) {
} // namespace ws