1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/wm/core/base_focus_rules.h"
6
7#include "ui/aura/client/focus_client.h"
8#include "ui/aura/window.h"
9#include "ui/wm/core/window_modality_controller.h"
10#include "ui/wm/core/window_util.h"
11#include "ui/wm/public/activation_delegate.h"
12
13namespace wm {
14namespace {
15
16aura::Window* GetFocusedWindow(aura::Window* context) {
17  aura::client::FocusClient* focus_client =
18      aura::client::GetFocusClient(context);
19  return focus_client ? focus_client->GetFocusedWindow() : NULL;
20}
21
22}  // namespace
23
24////////////////////////////////////////////////////////////////////////////////
25// BaseFocusRules, protected:
26
27BaseFocusRules::BaseFocusRules() {
28}
29
30BaseFocusRules::~BaseFocusRules() {
31}
32
33bool BaseFocusRules::IsWindowConsideredVisibleForActivation(
34    aura::Window* window) const {
35  return window->IsVisible();
36}
37
38////////////////////////////////////////////////////////////////////////////////
39// BaseFocusRules, FocusRules implementation:
40
41bool BaseFocusRules::IsToplevelWindow(aura::Window* window) const {
42  // The window must in a valid hierarchy.
43  if (!window->GetRootWindow())
44    return false;
45
46  // The window must exist within a container that supports activation.
47  // The window cannot be blocked by a modal transient.
48  return SupportsChildActivation(window->parent());
49}
50
51bool BaseFocusRules::CanActivateWindow(aura::Window* window) const {
52  // It is possible to activate a NULL window, it is equivalent to clearing
53  // activation.
54  if (!window)
55    return true;
56
57  // Only toplevel windows can be activated.
58  if (!IsToplevelWindow(window))
59    return false;
60
61  // The window must be visible.
62  if (!IsWindowConsideredVisibleForActivation(window))
63    return false;
64
65  // The window's activation delegate must allow this window to be activated.
66  if (aura::client::GetActivationDelegate(window) &&
67      !aura::client::GetActivationDelegate(window)->ShouldActivate()) {
68    return false;
69  }
70
71  // A window must be focusable to be activatable. We don't call
72  // CanFocusWindow() from here because it will call back to us via
73  // GetActivatableWindow().
74  if (!window->CanFocus())
75    return false;
76
77  // The window cannot be blocked by a modal transient.
78  return !GetModalTransient(window);
79}
80
81bool BaseFocusRules::CanFocusWindow(aura::Window* window) const {
82  // It is possible to focus a NULL window, it is equivalent to clearing focus.
83  if (!window)
84    return true;
85
86  // The focused window is always inside the active window, so windows that
87  // aren't activatable can't contain the focused window.
88  aura::Window* activatable = GetActivatableWindow(window);
89  if (!activatable || !activatable->Contains(window))
90    return false;
91  return window->CanFocus();
92}
93
94aura::Window* BaseFocusRules::GetToplevelWindow(aura::Window* window) const {
95  aura::Window* parent = window->parent();
96  aura::Window* child = window;
97  while (parent) {
98    if (IsToplevelWindow(child))
99      return child;
100
101    parent = parent->parent();
102    child = child->parent();
103  }
104  return NULL;
105}
106
107aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) const {
108  aura::Window* parent = window->parent();
109  aura::Window* child = window;
110  while (parent) {
111    if (CanActivateWindow(child))
112      return child;
113
114    // CanActivateWindow() above will return false if |child| is blocked by a
115    // modal transient. In this case the modal is or contains the activatable
116    // window. We recurse because the modal may itself be blocked by a modal
117    // transient.
118    aura::Window* modal_transient = GetModalTransient(child);
119    if (modal_transient)
120      return GetActivatableWindow(modal_transient);
121
122    if (wm::GetTransientParent(child)) {
123      // To avoid infinite recursion, if |child| has a transient parent
124      // whose own modal transient is |child| itself, just return |child|.
125      aura::Window* parent_modal_transient =
126          GetModalTransient(wm::GetTransientParent(child));
127      if (parent_modal_transient == child)
128        return child;
129
130      return GetActivatableWindow(wm::GetTransientParent(child));
131    }
132
133    parent = parent->parent();
134    child = child->parent();
135  }
136  return NULL;
137}
138
139aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) const {
140  if (CanFocusWindow(window))
141    return window;
142
143  // |window| may be in a hierarchy that is non-activatable, in which case we
144  // need to cut over to the activatable hierarchy.
145  aura::Window* activatable = GetActivatableWindow(window);
146  if (!activatable) {
147    // There may not be a related activatable hierarchy to cut over to, in which
148    // case we try an unrelated one.
149    aura::Window* toplevel = GetToplevelWindow(window);
150    if (toplevel)
151      activatable = GetNextActivatableWindow(toplevel);
152    if (!activatable)
153      return NULL;
154  }
155
156  if (!activatable->Contains(window)) {
157    // If there's already a child window focused in the activatable hierarchy,
158    // just use that (i.e. don't shift focus), otherwise we need to at least cut
159    // over to the activatable hierarchy.
160    aura::Window* focused = GetFocusedWindow(activatable);
161    return activatable->Contains(focused) ? focused : activatable;
162  }
163
164  while (window && !CanFocusWindow(window))
165    window = window->parent();
166  return window;
167}
168
169aura::Window* BaseFocusRules::GetNextActivatableWindow(
170    aura::Window* ignore) const {
171  DCHECK(ignore);
172
173  // Can be called from the RootWindow's destruction, which has a NULL parent.
174  if (!ignore->parent())
175    return NULL;
176
177  // In the basic scenarios handled by BasicFocusRules, the pool of activatable
178  // windows is limited to the |ignore|'s siblings.
179  const aura::Window::Windows& siblings = ignore->parent()->children();
180  DCHECK(!siblings.empty());
181
182  for (aura::Window::Windows::const_reverse_iterator rit = siblings.rbegin();
183       rit != siblings.rend();
184       ++rit) {
185    aura::Window* cur = *rit;
186    if (cur == ignore)
187      continue;
188    if (CanActivateWindow(cur))
189      return cur;
190  }
191  return NULL;
192}
193
194}  // namespace wm
195