#include "ash/focus/ash_focus_rules.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/wm/container_finder.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/float/float_controller.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/window_restore/window_restore_controller.h"
#include "ash/wm/window_state.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "components/app_restore/full_restore_utils.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/events/event.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
bool BelongsToContainerWithEqualOrGreaterId(const aura::Window* window,
int container_id) {
for (; window; window = window->parent()) {
if (window->GetId() >= container_id) {
return true;
}
}
return false;
}
bool BelongsToContainerWithId(const aura::Window* window, int container_id) {
for (; window; window = window->parent()) {
if (window->GetId() == container_id) {
return true;
}
}
return false;
}
bool IsInactiveDeskContainerId(int id) {
return desks_util::IsDeskContainerId(id) &&
id != desks_util::GetActiveDeskContainerId();
}
}
AshFocusRules::AshFocusRules()
: activatable_container_ids_(GetActivatableShellWindowIds()) {}
AshFocusRules::~AshFocusRules() = default;
bool AshFocusRules::IsToplevelWindow(const aura::Window* window) const {
DCHECK(window);
if (!window->GetRootWindow() || !window->parent()) {
return false;
}
return base::Contains(activatable_container_ids_, window->parent()->GetId());
}
bool AshFocusRules::SupportsChildActivation(const aura::Window* window) const {
return base::Contains(activatable_container_ids_, window->GetId());
}
bool AshFocusRules::IsWindowConsideredVisibleForActivation(
const aura::Window* window) const {
DCHECK(window);
Shell* shell = Shell::Get();
if (!shell->shell_delegate()->CanShowWindowForUser(window)) {
return false;
}
if (window->IsVisible()) {
return true;
}
const WindowState* window_state = WindowState::Get(window);
if (window_state->IsMinimized()) {
return true;
}
if (window_state->IsFloated()) {
auto* float_controller = shell->float_controller();
if (float_controller->FindDeskOfFloatedWindow(window) !=
DesksController::Get()->active_desk()) {
return true;
}
if (float_controller->IsFloatedWindowTuckedForTablet(window)) {
return true;
}
}
if (!window->TargetVisibility()) {
return false;
}
const aura::Window* const parent = window->parent();
return desks_util::IsDeskContainer(parent) ||
parent->GetId() == kShellWindowId_LockScreenContainer;
}
bool AshFocusRules::CanActivateWindow(const aura::Window* window) const {
if (!window) {
return true;
}
if (!WindowRestoreController::CanActivateRestoredWindow(window)) {
return false;
}
if (!WindowRestoreController::CanActivateAppList(window)) {
return false;
}
if (!BaseFocusRules::CanActivateWindow(window)) {
return false;
}
if (Shell::Get()->session_controller()->IsUserSessionBlocked() &&
BelongsToContainerWithId(window, kShellWindowId_ShelfContainer)) {
return true;
}
int modal_container_id = Shell::GetOpenSystemModalWindowContainerId();
if (modal_container_id >= 0) {
return BelongsToContainerWithEqualOrGreaterId(window, modal_container_id);
}
return true;
}
bool AshFocusRules::CanFocusWindow(const aura::Window* window,
const ui::Event* event) const {
if (!window) {
return true;
}
if (event && (event->IsMouseEvent() || event->IsGestureEvent()) &&
!window->GetProperty(aura::client::kActivateOnPointerKey)) {
return false;
}
return BaseFocusRules::CanFocusWindow(window, event);
}
aura::Window* AshFocusRules::GetNextActivatableWindow(
aura::Window* ignore) const {
DCHECK(ignore);
if (ignore->GetProperty(kIgnoreWindowActivationKey)) {
return nullptr;
}
aura::Window* starting_window = nullptr;
aura::Window* transient_parent = ::wm::GetTransientParent(ignore);
OverviewController* overview_controller = Shell::Get()->overview_controller();
if (overview_controller && overview_controller->InOverviewSession() &&
overview_controller->overview_session()->IsSavedDeskUiLosingActivation(
ignore)) {
starting_window =
overview_controller->overview_session()->GetOverviewFocusWindow();
} else if (transient_parent) {
starting_window = transient_parent;
} else {
MruWindowTracker* mru = Shell::Get()->mru_window_tracker();
aura::Window::Windows windows = mru->BuildMruWindowList(kActiveDesk);
starting_window = windows.empty() ? ignore : windows[0].get();
}
DCHECK(starting_window);
int starting_container_index = 0;
aura::Window* root = starting_window->GetRootWindow();
if (!root) {
root = Shell::GetRootWindowForNewWindows();
}
const int container_count = activatable_container_ids_.size();
for (int i = 0; i < container_count; i++) {
aura::Window* container =
Shell::GetContainer(root, activatable_container_ids_[i]);
if (container && container->Contains(starting_window)) {
starting_container_index = i;
break;
}
}
aura::Window* window = nullptr;
for (int i = starting_container_index; !window && i < container_count; i++) {
window = GetTopmostWindowToActivateForContainerIndex(i, ignore, root);
}
if (!window && starting_container_index > 0) {
for (int i = starting_container_index - 1; !window && i >= 0; i--) {
window = GetTopmostWindowToActivateForContainerIndex(i, ignore, root);
}
}
if (window) {
DCHECK(!window->GetProperty(kIgnoreWindowActivationKey));
}
return window;
}
aura::Window* AshFocusRules::GetTopmostWindowToActivateForContainerIndex(
int index,
aura::Window* ignore,
aura::Window* priority_root) const {
const int container_id = activatable_container_ids_[index];
if (IsInactiveDeskContainerId(container_id)) {
return nullptr;
}
aura::Window* window = nullptr;
aura::Window::Windows containers =
GetContainersForAllRootWindows(container_id, priority_root);
for (aura::Window* container : containers) {
window = GetTopmostWindowToActivateInContainer(container, ignore);
if (window) {
return window;
}
}
return window;
}
aura::Window* AshFocusRules::GetTopmostWindowToActivateInContainer(
aura::Window* container,
aura::Window* ignore) const {
for (aura::Window* child : base::Reversed(container->children())) {
WindowState* window_state = WindowState::Get(child);
if (child != ignore && window_state->CanActivate() &&
!window_state->IsMinimized() &&
!(window_state->IsFloated() && !child->IsVisible()) &&
!child->GetProperty(kIgnoreWindowActivationKey)) {
return child;
}
}
return nullptr;
}
}