blob: d21e2f52c2ce666b07d10e31d22fb8190f5b17af [file] [log] [blame]
// Copyright 2015 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/ui/ws/focus_controller.h"
#include <stddef.h>
#include <stdint.h>
#include "base/macros.h"
#include "services/ui/ws/focus_controller_delegate.h"
#include "services/ui/ws/focus_controller_observer.h"
#include "services/ui/ws/server_window.h"
#include "services/ui/ws/test_server_window_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ui {
namespace ws {
namespace {
const char kDisallowActiveChildren[] = "disallow-active-children";
class TestFocusControllerObserver : public FocusControllerObserver,
public FocusControllerDelegate {
public:
TestFocusControllerObserver()
: ignore_explicit_(true),
focus_change_count_(0u),
old_focused_window_(nullptr),
new_focused_window_(nullptr),
old_active_window_(nullptr),
new_active_window_(nullptr) {}
void ClearAll() {
focus_change_count_ = 0u;
old_focused_window_ = nullptr;
new_focused_window_ = nullptr;
old_active_window_ = nullptr;
new_active_window_ = nullptr;
}
size_t focus_change_count() const { return focus_change_count_; }
ServerWindow* old_focused_window() { return old_focused_window_; }
ServerWindow* new_focused_window() { return new_focused_window_; }
ServerWindow* old_active_window() { return old_active_window_; }
ServerWindow* new_active_window() { return new_active_window_; }
void set_ignore_explicit(bool ignore) { ignore_explicit_ = ignore; }
private:
// FocusControllerDelegate:
bool CanHaveActiveChildren(ServerWindow* window) const override {
return !window || window->properties().count(kDisallowActiveChildren) == 0;
}
// FocusControllerObserver:
void OnActivationChanged(ServerWindow* old_active_window,
ServerWindow* new_active_window) override {
old_active_window_ = old_active_window;
new_active_window_ = new_active_window;
}
void OnFocusChanged(FocusControllerChangeSource source,
ServerWindow* old_focused_window,
ServerWindow* new_focused_window) override {
if (ignore_explicit_ && source == FocusControllerChangeSource::EXPLICIT)
return;
focus_change_count_++;
old_focused_window_ = old_focused_window;
new_focused_window_ = new_focused_window;
}
bool ignore_explicit_;
size_t focus_change_count_;
ServerWindow* old_focused_window_;
ServerWindow* new_focused_window_;
ServerWindow* old_active_window_;
ServerWindow* new_active_window_;
DISALLOW_COPY_AND_ASSIGN(TestFocusControllerObserver);
};
} // namespace
TEST(FocusControllerTest, Basic) {
TestServerWindowDelegate server_window_delegate;
ServerWindow root(&server_window_delegate, WindowId());
server_window_delegate.set_root_window(&root);
root.SetVisible(true);
ServerWindow child(&server_window_delegate, WindowId());
child.SetVisible(true);
root.Add(&child);
ServerWindow child_child(&server_window_delegate, WindowId());
child_child.SetVisible(true);
child.Add(&child_child);
TestFocusControllerObserver focus_observer;
FocusController focus_controller(&focus_observer, &root);
focus_controller.AddObserver(&focus_observer);
focus_controller.SetFocusedWindow(&child_child);
EXPECT_EQ(0u, focus_observer.focus_change_count());
// Remove the ancestor of the focused window, focus should go to the |root|.
root.Remove(&child);
EXPECT_EQ(1u, focus_observer.focus_change_count());
EXPECT_EQ(&root, focus_observer.new_focused_window());
EXPECT_EQ(&child_child, focus_observer.old_focused_window());
focus_observer.ClearAll();
// Make the focused window invisible. Focus is lost in this case (as no one
// to give focus to).
root.SetVisible(false);
EXPECT_EQ(1u, focus_observer.focus_change_count());
EXPECT_EQ(nullptr, focus_observer.new_focused_window());
EXPECT_EQ(&root, focus_observer.old_focused_window());
focus_observer.ClearAll();
// Go back to initial state and focus |child_child|.
root.SetVisible(true);
root.Add(&child);
focus_controller.SetFocusedWindow(&child_child);
EXPECT_EQ(0u, focus_observer.focus_change_count());
// Hide the focused window, focus should go to parent.
child_child.SetVisible(false);
EXPECT_EQ(1u, focus_observer.focus_change_count());
EXPECT_EQ(&child, focus_observer.new_focused_window());
EXPECT_EQ(&child_child, focus_observer.old_focused_window());
focus_observer.ClearAll();
child_child.SetVisible(true);
focus_controller.SetFocusedWindow(&child_child);
EXPECT_EQ(0u, focus_observer.focus_change_count());
// Hide the parent of the focused window.
child.SetVisible(false);
EXPECT_EQ(1u, focus_observer.focus_change_count());
EXPECT_EQ(&root, focus_observer.new_focused_window());
EXPECT_EQ(&child_child, focus_observer.old_focused_window());
focus_observer.ClearAll();
focus_controller.RemoveObserver(&focus_observer);
}
// Tests that focus shifts correctly if the focused window is destroyed.
TEST(FocusControllerTest, FocusShiftsOnDestroy) {
TestServerWindowDelegate server_window_delegate;
ServerWindow parent(&server_window_delegate, WindowId());
server_window_delegate.set_root_window(&parent);
parent.SetVisible(true);
ServerWindow child_first(&server_window_delegate, WindowId());
child_first.SetVisible(true);
parent.Add(&child_first);
std::unique_ptr<ServerWindow> child_second(
new ServerWindow(&server_window_delegate, WindowId()));
child_second->SetVisible(true);
parent.Add(child_second.get());
std::vector<uint8_t> dummy;
// Allow only |parent| to be activated.
parent.SetProperty(kDisallowActiveChildren, &dummy);
TestFocusControllerObserver focus_observer;
focus_observer.set_ignore_explicit(false);
FocusController focus_controller(&focus_observer, &parent);
focus_controller.AddObserver(&focus_observer);
focus_controller.ActivateNextWindow();
EXPECT_EQ(nullptr, focus_observer.old_active_window());
EXPECT_EQ(&parent, focus_observer.new_active_window());
EXPECT_EQ(nullptr, focus_observer.old_focused_window());
EXPECT_EQ(child_second.get(), focus_observer.new_focused_window());
focus_observer.ClearAll();
// Destroying |child_second| should move focus to |child_first|.
child_second.reset();
EXPECT_NE(nullptr, focus_observer.old_focused_window());
EXPECT_EQ(&child_first, focus_observer.new_focused_window());
focus_controller.RemoveObserver(&focus_observer);
}
TEST(FocusControllerTest, ActivationSkipsOverHiddenWindow) {
TestServerWindowDelegate server_window_delegate;
ServerWindow parent(&server_window_delegate, WindowId());
server_window_delegate.set_root_window(&parent);
parent.SetVisible(true);
ServerWindow child_first(&server_window_delegate, WindowId());
ServerWindow child_second(&server_window_delegate, WindowId());
ServerWindow child_third(&server_window_delegate, WindowId());
parent.Add(&child_first);
parent.Add(&child_second);
parent.Add(&child_third);
child_first.SetVisible(true);
child_second.SetVisible(false);
child_third.SetVisible(true);
TestFocusControllerObserver focus_observer;
focus_observer.set_ignore_explicit(false);
FocusController focus_controller(&focus_observer, &parent);
focus_controller.AddObserver(&focus_observer);
// Since |child_second| is invisible, activation should cycle from
// |child_third|, to |child_first|, to |parent|, back to |child_third|.
focus_controller.ActivateNextWindow();
EXPECT_EQ(nullptr, focus_observer.old_active_window());
EXPECT_EQ(&child_third, focus_observer.new_active_window());
focus_observer.ClearAll();
focus_controller.ActivateNextWindow();
EXPECT_EQ(&child_third, focus_observer.old_active_window());
EXPECT_EQ(&child_first, focus_observer.new_active_window());
focus_observer.ClearAll();
focus_controller.ActivateNextWindow();
EXPECT_EQ(&child_first, focus_observer.old_active_window());
EXPECT_EQ(&parent, focus_observer.new_active_window());
focus_observer.ClearAll();
focus_controller.ActivateNextWindow();
EXPECT_EQ(&parent, focus_observer.old_active_window());
EXPECT_EQ(&child_third, focus_observer.new_active_window());
focus_observer.ClearAll();
// Once |child_second| is made visible, activation should go from
// |child_third| to |child_second|.
child_second.SetVisible(true);
focus_controller.ActivateNextWindow();
EXPECT_EQ(&child_third, focus_observer.old_active_window());
EXPECT_EQ(&child_second, focus_observer.new_active_window());
}
TEST(FocusControllerTest, ActivationSkipsOverHiddenContainers) {
TestServerWindowDelegate server_window_delegate;
ServerWindow parent(&server_window_delegate, WindowId());
server_window_delegate.set_root_window(&parent);
parent.SetVisible(true);
ServerWindow child1(&server_window_delegate, WindowId());
ServerWindow child2(&server_window_delegate, WindowId());
parent.Add(&child1);
parent.Add(&child2);
child1.SetVisible(true);
child2.SetVisible(true);
ServerWindow child11(&server_window_delegate, WindowId());
ServerWindow child12(&server_window_delegate, WindowId());
ServerWindow child21(&server_window_delegate, WindowId());
ServerWindow child22(&server_window_delegate, WindowId());
child1.Add(&child11);
child1.Add(&child12);
child2.Add(&child21);
child2.Add(&child22);
child11.SetVisible(true);
child12.SetVisible(true);
child21.SetVisible(true);
child22.SetVisible(true);
TestFocusControllerObserver focus_observer;
FocusController focus_controller(&focus_observer, &parent);
focus_controller.AddObserver(&focus_observer);
focus_controller.ActivateNextWindow();
EXPECT_EQ(nullptr, focus_observer.old_active_window());
EXPECT_EQ(&child22, focus_observer.new_active_window());
focus_observer.ClearAll();
child2.SetVisible(false);
EXPECT_EQ(&child22, focus_observer.old_active_window());
EXPECT_EQ(&child12, focus_observer.new_active_window());
}
TEST(FocusControllerTest, NonFocusableWindowNotActivated) {
TestServerWindowDelegate server_window_delegate;
ServerWindow parent(&server_window_delegate, WindowId());
server_window_delegate.set_root_window(&parent);
parent.SetVisible(true);
ServerWindow child_first(&server_window_delegate, WindowId());
ServerWindow child_second(&server_window_delegate, WindowId());
parent.Add(&child_first);
parent.Add(&child_second);
child_first.SetVisible(true);
child_second.SetVisible(true);
TestFocusControllerObserver focus_observer;
focus_observer.set_ignore_explicit(false);
FocusController focus_controller(&focus_observer, &parent);
focus_controller.AddObserver(&focus_observer);
child_first.set_can_focus(false);
// |child_second| is activated first, but then activation skips over
// |child_first| and goes to |parent| instead.
focus_controller.ActivateNextWindow();
EXPECT_EQ(nullptr, focus_observer.old_active_window());
EXPECT_EQ(&child_second, focus_observer.new_active_window());
focus_observer.ClearAll();
focus_controller.ActivateNextWindow();
EXPECT_EQ(&child_second, focus_observer.old_active_window());
EXPECT_EQ(&parent, focus_observer.new_active_window());
}
} // namespace ws
} // namespace ui