blob: cfccd056c978d063df9530e61aa50b789bd4fb4a [file] [log] [blame]
// Copyright (c) 2012 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 "ui/views/corewm/focus_controller.h"
#include <map>
#include "ui/aura/client/activation_change_observer.h"
#include "ui/aura/client/activation_client.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/default_capture_client.h"
#include "ui/aura/client/focus_change_observer.h"
#include "ui/aura/root_window.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/event_generator.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tracker.h"
#include "ui/events/event_handler.h"
#include "ui/views/corewm/base_focus_rules.h"
namespace views {
namespace corewm {
class FocusNotificationObserver : public aura::client::ActivationChangeObserver,
public aura::client::FocusChangeObserver {
public:
FocusNotificationObserver()
: activation_changed_count_(0),
focus_changed_count_(0),
reactivation_count_(0),
reactivation_requested_window_(NULL),
reactivation_actual_window_(NULL) {}
virtual ~FocusNotificationObserver() {}
void ExpectCounts(int activation_changed_count, int focus_changed_count) {
EXPECT_EQ(activation_changed_count, activation_changed_count_);
EXPECT_EQ(focus_changed_count, focus_changed_count_);
}
int reactivation_count() const {
return reactivation_count_;
}
aura::Window* reactivation_requested_window() const {
return reactivation_requested_window_;
}
aura::Window* reactivation_actual_window() const {
return reactivation_actual_window_;
}
private:
// Overridden from aura::client::ActivationChangeObserver:
virtual void OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) OVERRIDE {
++activation_changed_count_;
}
virtual void OnAttemptToReactivateWindow(
aura::Window* request_active,
aura::Window* actual_active) OVERRIDE {
++reactivation_count_;
reactivation_requested_window_ = request_active;
reactivation_actual_window_ = actual_active;
}
// Overridden from aura::client::FocusChangeObserver:
virtual void OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) OVERRIDE {
++focus_changed_count_;
}
int activation_changed_count_;
int focus_changed_count_;
int reactivation_count_;
aura::Window* reactivation_requested_window_;
aura::Window* reactivation_actual_window_;
DISALLOW_COPY_AND_ASSIGN(FocusNotificationObserver);
};
// ActivationChangeObserver that keeps a vector of all the windows that lost
// active.
class RecordingActivationChangeObserver
: public aura::client::ActivationChangeObserver {
public:
explicit RecordingActivationChangeObserver(aura::Window* root)
: root_(root) {
aura::client::GetActivationClient(root_)->AddObserver(this);
}
virtual ~RecordingActivationChangeObserver() {
aura::client::GetActivationClient(root_)->RemoveObserver(this);
}
// Each time we get OnWindowActivated() the |lost_active| parameter is
// added here.
const std::vector<aura::Window*>& lost() const { return lost_; }
// Overridden from aura::client::ActivationChangeObserver:
virtual void OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) OVERRIDE {
lost_.push_back(lost_active);
}
private:
aura::Window* root_;
std::vector<aura::Window*> lost_;
DISALLOW_COPY_AND_ASSIGN(RecordingActivationChangeObserver);
};
// ActivationChangeObserver that deletes the window losing activation.
class DeleteOnLoseActivationChangeObserver
: public aura::client::ActivationChangeObserver {
public:
explicit DeleteOnLoseActivationChangeObserver(aura::Window* window)
: root_(window->GetRootWindow()),
window_(window) {
aura::client::GetActivationClient(root_)->AddObserver(this);
}
virtual ~DeleteOnLoseActivationChangeObserver() {
aura::client::GetActivationClient(root_)->RemoveObserver(this);
}
bool did_delete() const { return window_ == NULL; }
// Overridden from aura::client::ActivationChangeObserver:
virtual void OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) OVERRIDE {
if (window_ && lost_active == window_) {
window_ = NULL;
delete lost_active;
}
}
private:
aura::Window* root_;
aura::Window* window_;
DISALLOW_COPY_AND_ASSIGN(DeleteOnLoseActivationChangeObserver);
};
class ScopedFocusNotificationObserver : public FocusNotificationObserver {
public:
ScopedFocusNotificationObserver(aura::Window* root_window)
: root_window_(root_window) {
aura::client::GetActivationClient(root_window_)->AddObserver(this);
aura::client::GetFocusClient(root_window_)->AddObserver(this);
}
virtual ~ScopedFocusNotificationObserver() {
aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
aura::client::GetFocusClient(root_window_)->RemoveObserver(this);
}
private:
aura::Window* root_window_;
DISALLOW_COPY_AND_ASSIGN(ScopedFocusNotificationObserver);
};
class ScopedTargetFocusNotificationObserver : public FocusNotificationObserver {
public:
ScopedTargetFocusNotificationObserver(aura::Window* root_window, int id)
: target_(root_window->GetChildById(id)) {
aura::client::SetActivationChangeObserver(target_, this);
aura::client::SetFocusChangeObserver(target_, this);
tracker_.Add(target_);
}
virtual ~ScopedTargetFocusNotificationObserver() {
if (tracker_.Contains(target_)) {
aura::client::SetActivationChangeObserver(target_, NULL);
aura::client::SetFocusChangeObserver(target_, NULL);
}
}
private:
aura::Window* target_;
aura::WindowTracker tracker_;
DISALLOW_COPY_AND_ASSIGN(ScopedTargetFocusNotificationObserver);
};
class FocusShiftingActivationObserver
: public aura::client::ActivationChangeObserver {
public:
explicit FocusShiftingActivationObserver(aura::Window* activated_window)
: activated_window_(activated_window),
shift_focus_to_(NULL) {}
virtual ~FocusShiftingActivationObserver() {}
void set_shift_focus_to(aura::Window* shift_focus_to) {
shift_focus_to_ = shift_focus_to;
}
private:
// Overridden from aura::client::ActivationChangeObserver:
virtual void OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) OVERRIDE {
// Shift focus to a child. This should prevent the default focusing from
// occurring in FocusController::FocusWindow().
if (gained_active == activated_window_) {
aura::client::FocusClient* client =
aura::client::GetFocusClient(gained_active);
client->FocusWindow(shift_focus_to_);
}
}
aura::Window* activated_window_;
aura::Window* shift_focus_to_;
DISALLOW_COPY_AND_ASSIGN(FocusShiftingActivationObserver);
};
// BaseFocusRules subclass that allows basic overrides of focus/activation to
// be tested. This is intended more as a test that the override system works at
// all, rather than as an exhaustive set of use cases, those should be covered
// in tests for those FocusRules implementations.
class TestFocusRules : public BaseFocusRules {
public:
TestFocusRules() : focus_restriction_(NULL) {}
// Restricts focus and activation to this window and its child hierarchy.
void set_focus_restriction(aura::Window* focus_restriction) {
focus_restriction_ = focus_restriction;
}
// Overridden from BaseFocusRules:
virtual bool SupportsChildActivation(aura::Window* window) const OVERRIDE {
// In FocusControllerTests, only the RootWindow has activatable children.
return window->GetRootWindow() == window;
}
virtual bool CanActivateWindow(aura::Window* window) const OVERRIDE {
// Restricting focus to a non-activatable child window means the activatable
// parent outside the focus restriction is activatable.
bool can_activate =
CanFocusOrActivate(window) || window->Contains(focus_restriction_);
return can_activate ? BaseFocusRules::CanActivateWindow(window) : false;
}
virtual bool CanFocusWindow(aura::Window* window) const OVERRIDE {
return CanFocusOrActivate(window) ?
BaseFocusRules::CanFocusWindow(window) : false;
}
virtual aura::Window* GetActivatableWindow(
aura::Window* window) const OVERRIDE {
return BaseFocusRules::GetActivatableWindow(
CanFocusOrActivate(window) ? window : focus_restriction_);
}
virtual aura::Window* GetFocusableWindow(
aura::Window* window) const OVERRIDE {
return BaseFocusRules::GetFocusableWindow(
CanFocusOrActivate(window) ? window : focus_restriction_);
}
virtual aura::Window* GetNextActivatableWindow(
aura::Window* ignore) const OVERRIDE {
aura::Window* next_activatable =
BaseFocusRules::GetNextActivatableWindow(ignore);
return CanFocusOrActivate(next_activatable) ?
next_activatable : GetActivatableWindow(focus_restriction_);
}
private:
bool CanFocusOrActivate(aura::Window* window) const {
return !focus_restriction_ || focus_restriction_->Contains(window);
}
aura::Window* focus_restriction_;
DISALLOW_COPY_AND_ASSIGN(TestFocusRules);
};
// Common infrastructure shared by all FocusController test types.
class FocusControllerTestBase : public aura::test::AuraTestBase {
protected:
FocusControllerTestBase() {}
// Overridden from aura::test::AuraTestBase:
virtual void SetUp() OVERRIDE {
// FocusController registers itself as an Env observer so it can catch all
// window initializations, including the root_window()'s, so we create it
// before allowing the base setup.
test_focus_rules_ = new TestFocusRules;
focus_controller_.reset(new FocusController(test_focus_rules_));
aura::test::AuraTestBase::SetUp();
root_window()->AddPreTargetHandler(focus_controller_.get());
aura::client::SetFocusClient(root_window(), focus_controller_.get());
aura::client::SetActivationClient(root_window(), focus_controller_.get());
// Hierarchy used by all tests:
// root_window
// +-- w1
// | +-- w11
// | +-- w12
// +-- w2
// | +-- w21
// | +-- w211
// +-- w3
aura::Window* w1 = aura::test::CreateTestWindowWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 1,
gfx::Rect(0, 0, 50, 50), root_window());
aura::test::CreateTestWindowWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 11,
gfx::Rect(5, 5, 10, 10), w1);
aura::test::CreateTestWindowWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 12,
gfx::Rect(15, 15, 10, 10), w1);
aura::Window* w2 = aura::test::CreateTestWindowWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 2,
gfx::Rect(75, 75, 50, 50), root_window());
aura::Window* w21 = aura::test::CreateTestWindowWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 21,
gfx::Rect(5, 5, 10, 10), w2);
aura::test::CreateTestWindowWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 211,
gfx::Rect(1, 1, 5, 5), w21);
aura::test::CreateTestWindowWithDelegate(
aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate(), 3,
gfx::Rect(125, 125, 50, 50), root_window());
}
virtual void TearDown() OVERRIDE {
root_window()->RemovePreTargetHandler(focus_controller_.get());
aura::test::AuraTestBase::TearDown();
test_focus_rules_ = NULL; // Owned by FocusController.
focus_controller_.reset();
}
void FocusWindow(aura::Window* window) {
aura::client::GetFocusClient(root_window())->FocusWindow(window);
}
aura::Window* GetFocusedWindow() {
return aura::client::GetFocusClient(root_window())->GetFocusedWindow();
}
int GetFocusedWindowId() {
aura::Window* focused_window = GetFocusedWindow();
return focused_window ? focused_window->id() : -1;
}
void ActivateWindow(aura::Window* window) {
aura::client::GetActivationClient(root_window())->ActivateWindow(window);
}
void DeactivateWindow(aura::Window* window) {
aura::client::GetActivationClient(root_window())->DeactivateWindow(window);
}
aura::Window* GetActiveWindow() {
return aura::client::GetActivationClient(root_window())->GetActiveWindow();
}
int GetActiveWindowId() {
aura::Window* active_window = GetActiveWindow();
return active_window ? active_window->id() : -1;
}
TestFocusRules* test_focus_rules() { return test_focus_rules_; }
// Test functions.
virtual void BasicFocus() = 0;
virtual void BasicActivation() = 0;
virtual void FocusEvents() = 0;
virtual void DuplicateFocusEvents() {}
virtual void ActivationEvents() = 0;
virtual void ReactivationEvents() {}
virtual void DuplicateActivationEvents() {}
virtual void ShiftFocusWithinActiveWindow() {}
virtual void ShiftFocusToChildOfInactiveWindow() {}
virtual void ShiftFocusToParentOfFocusedWindow() {}
virtual void FocusRulesOverride() = 0;
virtual void ActivationRulesOverride() = 0;
virtual void ShiftFocusOnActivation() {}
virtual void ShiftFocusOnActivationDueToHide() {}
virtual void NoShiftActiveOnActivation() {}
virtual void NoFocusChangeOnClickOnCaptureWindow() {}
virtual void ChangeFocusWhenNothingFocusedAndCaptured() {}
virtual void DontPassDeletedWindow() {}
private:
scoped_ptr<FocusController> focus_controller_;
TestFocusRules* test_focus_rules_;
DISALLOW_COPY_AND_ASSIGN(FocusControllerTestBase);
};
// Test base for tests where focus is directly set to a target window.
class FocusControllerDirectTestBase : public FocusControllerTestBase {
protected:
FocusControllerDirectTestBase() {}
// Different test types shift focus in different ways.
virtual void FocusWindowDirect(aura::Window* window) = 0;
virtual void ActivateWindowDirect(aura::Window* window) = 0;
virtual void DeactivateWindowDirect(aura::Window* window) = 0;
// Input events do not change focus if the window can not be focused.
virtual bool IsInputEvent() = 0;
void FocusWindowById(int id) {
aura::Window* window = root_window()->GetChildById(id);
DCHECK(window);
FocusWindowDirect(window);
}
void ActivateWindowById(int id) {
aura::Window* window = root_window()->GetChildById(id);
DCHECK(window);
ActivateWindowDirect(window);
}
// Overridden from FocusControllerTestBase:
virtual void BasicFocus() OVERRIDE {
EXPECT_EQ(NULL, GetFocusedWindow());
FocusWindowById(1);
EXPECT_EQ(1, GetFocusedWindowId());
FocusWindowById(2);
EXPECT_EQ(2, GetFocusedWindowId());
}
virtual void BasicActivation() OVERRIDE {
EXPECT_EQ(NULL, GetActiveWindow());
ActivateWindowById(1);
EXPECT_EQ(1, GetActiveWindowId());
ActivateWindowById(2);
EXPECT_EQ(2, GetActiveWindowId());
// Verify that attempting to deactivate NULL does not crash and does not
// change activation.
DeactivateWindow(NULL);
EXPECT_EQ(2, GetActiveWindowId());
DeactivateWindow(GetActiveWindow());
EXPECT_EQ(1, GetActiveWindowId());
}
virtual void FocusEvents() OVERRIDE {
ScopedFocusNotificationObserver root_observer(root_window());
ScopedTargetFocusNotificationObserver observer1(root_window(), 1);
ScopedTargetFocusNotificationObserver observer2(root_window(), 2);
root_observer.ExpectCounts(0, 0);
observer1.ExpectCounts(0, 0);
observer2.ExpectCounts(0, 0);
FocusWindowById(1);
root_observer.ExpectCounts(1, 1);
observer1.ExpectCounts(1, 1);
observer2.ExpectCounts(0, 0);
FocusWindowById(2);
root_observer.ExpectCounts(2, 2);
observer1.ExpectCounts(2, 2);
observer2.ExpectCounts(1, 1);
}
virtual void DuplicateFocusEvents() OVERRIDE {
// Focusing an existing focused window should not resend focus events.
ScopedFocusNotificationObserver root_observer(root_window());
ScopedTargetFocusNotificationObserver observer1(root_window(), 1);
root_observer.ExpectCounts(0, 0);
observer1.ExpectCounts(0, 0);
FocusWindowById(1);
root_observer.ExpectCounts(1, 1);
observer1.ExpectCounts(1, 1);
FocusWindowById(1);
root_observer.ExpectCounts(1, 1);
observer1.ExpectCounts(1, 1);
}
virtual void ActivationEvents() OVERRIDE {
ActivateWindowById(1);
ScopedFocusNotificationObserver root_observer(root_window());
ScopedTargetFocusNotificationObserver observer1(root_window(), 1);
ScopedTargetFocusNotificationObserver observer2(root_window(), 2);
root_observer.ExpectCounts(0, 0);
observer1.ExpectCounts(0, 0);
observer2.ExpectCounts(0, 0);
ActivateWindowById(2);
root_observer.ExpectCounts(1, 1);
observer1.ExpectCounts(1, 1);
observer2.ExpectCounts(1, 1);
}
virtual void ReactivationEvents() OVERRIDE {
ActivateWindowById(1);
ScopedFocusNotificationObserver root_observer(root_window());
EXPECT_EQ(0, root_observer.reactivation_count());
root_window()->GetChildById(2)->Hide();
// When we attempt to activate "2", which cannot be activated because it
// is not visible, "1" will be reactivated.
ActivateWindowById(2);
EXPECT_EQ(1, root_observer.reactivation_count());
EXPECT_EQ(root_window()->GetChildById(2),
root_observer.reactivation_requested_window());
EXPECT_EQ(root_window()->GetChildById(1),
root_observer.reactivation_actual_window());
}
virtual void DuplicateActivationEvents() OVERRIDE {
// Activating an existing active window should not resend activation events.
ActivateWindowById(1);
ScopedFocusNotificationObserver root_observer(root_window());
ScopedTargetFocusNotificationObserver observer1(root_window(), 1);
ScopedTargetFocusNotificationObserver observer2(root_window(), 2);
root_observer.ExpectCounts(0, 0);
observer1.ExpectCounts(0, 0);
observer2.ExpectCounts(0, 0);
ActivateWindowById(2);
root_observer.ExpectCounts(1, 1);
observer1.ExpectCounts(1, 1);
observer2.ExpectCounts(1, 1);
ActivateWindowById(2);
root_observer.ExpectCounts(1, 1);
observer1.ExpectCounts(1, 1);
observer2.ExpectCounts(1, 1);
}
virtual void ShiftFocusWithinActiveWindow() OVERRIDE {
ActivateWindowById(1);
EXPECT_EQ(1, GetActiveWindowId());
EXPECT_EQ(1, GetFocusedWindowId());
FocusWindowById(11);
EXPECT_EQ(11, GetFocusedWindowId());
FocusWindowById(12);
EXPECT_EQ(12, GetFocusedWindowId());
}
virtual void ShiftFocusToChildOfInactiveWindow() OVERRIDE {
ActivateWindowById(2);
EXPECT_EQ(2, GetActiveWindowId());
EXPECT_EQ(2, GetFocusedWindowId());
FocusWindowById(11);
EXPECT_EQ(1, GetActiveWindowId());
EXPECT_EQ(11, GetFocusedWindowId());
}
virtual void ShiftFocusToParentOfFocusedWindow() OVERRIDE {
ActivateWindowById(1);
EXPECT_EQ(1, GetFocusedWindowId());
FocusWindowById(11);
EXPECT_EQ(11, GetFocusedWindowId());
FocusWindowById(1);
// Focus should _not_ shift to the parent of the already-focused window.
EXPECT_EQ(11, GetFocusedWindowId());
}
virtual void FocusRulesOverride() OVERRIDE {
EXPECT_EQ(NULL, GetFocusedWindow());
FocusWindowById(11);
EXPECT_EQ(11, GetFocusedWindowId());
test_focus_rules()->set_focus_restriction(root_window()->GetChildById(211));
FocusWindowById(12);
// Input events leave focus unchanged; direct API calls will change focus
// to the restricted window.
int focused_window = IsInputEvent() ? 11 : 211;
EXPECT_EQ(focused_window, GetFocusedWindowId());
test_focus_rules()->set_focus_restriction(NULL);
FocusWindowById(12);
EXPECT_EQ(12, GetFocusedWindowId());
}
virtual void ActivationRulesOverride() OVERRIDE {
ActivateWindowById(1);
EXPECT_EQ(1, GetActiveWindowId());
EXPECT_EQ(1, GetFocusedWindowId());
aura::Window* w3 = root_window()->GetChildById(3);
test_focus_rules()->set_focus_restriction(w3);
ActivateWindowById(2);
// Input events leave activation unchanged; direct API calls will activate
// the restricted window.
int active_window = IsInputEvent() ? 1 : 3;
EXPECT_EQ(active_window, GetActiveWindowId());
EXPECT_EQ(active_window, GetFocusedWindowId());
test_focus_rules()->set_focus_restriction(NULL);
ActivateWindowById(2);
EXPECT_EQ(2, GetActiveWindowId());
EXPECT_EQ(2, GetFocusedWindowId());
}
virtual void ShiftFocusOnActivation() OVERRIDE {
// When a window is activated, by default that window is also focused.
// An ActivationChangeObserver may shift focus to another window within the
// same activatable window.
ActivateWindowById(2);
EXPECT_EQ(2, GetFocusedWindowId());
ActivateWindowById(1);
EXPECT_EQ(1, GetFocusedWindowId());
ActivateWindowById(2);
aura::Window* target = root_window()->GetChildById(1);
aura::client::ActivationClient* client =
aura::client::GetActivationClient(root_window());
scoped_ptr<FocusShiftingActivationObserver> observer(
new FocusShiftingActivationObserver(target));
observer->set_shift_focus_to(target->GetChildById(11));
client->AddObserver(observer.get());
ActivateWindowById(1);
// w1's ActivationChangeObserver shifted focus to this child, pre-empting
// FocusController's default setting.
EXPECT_EQ(11, GetFocusedWindowId());
ActivateWindowById(2);
EXPECT_EQ(2, GetFocusedWindowId());
// Simulate a focus reset by the ActivationChangeObserver. This should
// trigger the default setting in FocusController.
observer->set_shift_focus_to(NULL);
ActivateWindowById(1);
EXPECT_EQ(1, GetFocusedWindowId());
client->RemoveObserver(observer.get());
ActivateWindowById(2);
EXPECT_EQ(2, GetFocusedWindowId());
ActivateWindowById(1);
EXPECT_EQ(1, GetFocusedWindowId());
}
virtual void ShiftFocusOnActivationDueToHide() OVERRIDE {
// Similar to ShiftFocusOnActivation except the activation change is
// triggered by hiding the active window.
ActivateWindowById(1);
EXPECT_EQ(1, GetFocusedWindowId());
// Removes window 3 as candidate for next activatable window.
root_window()->GetChildById(3)->Hide();
EXPECT_EQ(1, GetFocusedWindowId());
aura::Window* target = root_window()->GetChildById(2);
aura::client::ActivationClient* client =
aura::client::GetActivationClient(root_window());
scoped_ptr<FocusShiftingActivationObserver> observer(
new FocusShiftingActivationObserver(target));
observer->set_shift_focus_to(target->GetChildById(21));
client->AddObserver(observer.get());
// Hide the active window.
root_window()->GetChildById(1)->Hide();
EXPECT_EQ(21, GetFocusedWindowId());
client->RemoveObserver(observer.get());
}
virtual void NoShiftActiveOnActivation() OVERRIDE {
// When a window is activated, we need to prevent any change to activation
// from being made in response to an activation change notification.
}
virtual void NoFocusChangeOnClickOnCaptureWindow() OVERRIDE {
scoped_ptr<aura::client::DefaultCaptureClient> capture_client(
new aura::client::DefaultCaptureClient(root_window()));
// Clicking on a window which has capture should not cause a focus change
// to the window. This test verifies whether that is indeed the case.
ActivateWindowById(1);
EXPECT_EQ(1, GetActiveWindowId());
EXPECT_EQ(1, GetFocusedWindowId());
aura::Window* w2 = root_window()->GetChildById(2);
aura::client::GetCaptureClient(root_window())->SetCapture(w2);
aura::test::EventGenerator generator(root_window(), w2);
generator.ClickLeftButton();
EXPECT_EQ(1, GetActiveWindowId());
EXPECT_EQ(1, GetFocusedWindowId());
aura::client::GetCaptureClient(root_window())->ReleaseCapture(w2);
}
// Verifies focus change is honored while capture held.
virtual void ChangeFocusWhenNothingFocusedAndCaptured() OVERRIDE {
scoped_ptr<aura::client::DefaultCaptureClient> capture_client(
new aura::client::DefaultCaptureClient(root_window()));
aura::Window* w1 = root_window()->GetChildById(1);
aura::client::GetCaptureClient(root_window())->SetCapture(w1);
EXPECT_EQ(-1, GetActiveWindowId());
EXPECT_EQ(-1, GetFocusedWindowId());
FocusWindowById(1);
EXPECT_EQ(1, GetActiveWindowId());
EXPECT_EQ(1, GetFocusedWindowId());
aura::client::GetCaptureClient(root_window())->ReleaseCapture(w1);
}
// Verfies if a window that loses activation is deleted during observer
// notification we don't pass the deleted window to other observers.
virtual void DontPassDeletedWindow() OVERRIDE {
FocusWindowById(1);
EXPECT_EQ(1, GetActiveWindowId());
EXPECT_EQ(1, GetFocusedWindowId());
DeleteOnLoseActivationChangeObserver observer1(
root_window()->GetChildById(1));
RecordingActivationChangeObserver observer2(root_window());
FocusWindowById(2);
EXPECT_EQ(2, GetActiveWindowId());
EXPECT_EQ(2, GetFocusedWindowId());
EXPECT_TRUE(observer1.did_delete());
ASSERT_EQ(1u, observer2.lost().size());
EXPECT_TRUE(observer2.lost()[0] == NULL);
}
private:
DISALLOW_COPY_AND_ASSIGN(FocusControllerDirectTestBase);
};
// Focus and Activation changes via aura::client::ActivationClient API.
class FocusControllerApiTest : public FocusControllerDirectTestBase {
public:
FocusControllerApiTest() {}
private:
// Overridden from FocusControllerTestBase:
virtual void FocusWindowDirect(aura::Window* window) OVERRIDE {
FocusWindow(window);
}
virtual void ActivateWindowDirect(aura::Window* window) OVERRIDE {
ActivateWindow(window);
}
virtual void DeactivateWindowDirect(aura::Window* window) OVERRIDE {
DeactivateWindow(window);
}
virtual bool IsInputEvent() OVERRIDE { return false; }
DISALLOW_COPY_AND_ASSIGN(FocusControllerApiTest);
};
// Focus and Activation changes via input events.
class FocusControllerMouseEventTest : public FocusControllerDirectTestBase {
public:
FocusControllerMouseEventTest() {}
private:
// Overridden from FocusControllerTestBase:
virtual void FocusWindowDirect(aura::Window* window) OVERRIDE {
aura::test::EventGenerator generator(root_window(), window);
generator.ClickLeftButton();
}
virtual void ActivateWindowDirect(aura::Window* window) OVERRIDE {
aura::test::EventGenerator generator(root_window(), window);
generator.ClickLeftButton();
}
virtual void DeactivateWindowDirect(aura::Window* window) OVERRIDE {
aura::Window* next_activatable =
test_focus_rules()->GetNextActivatableWindow(window);
aura::test::EventGenerator generator(root_window(), next_activatable);
generator.ClickLeftButton();
}
virtual bool IsInputEvent() OVERRIDE { return true; }
DISALLOW_COPY_AND_ASSIGN(FocusControllerMouseEventTest);
};
class FocusControllerGestureEventTest : public FocusControllerDirectTestBase {
public:
FocusControllerGestureEventTest() {}
private:
// Overridden from FocusControllerTestBase:
virtual void FocusWindowDirect(aura::Window* window) OVERRIDE {
aura::test::EventGenerator generator(root_window(), window);
generator.GestureTapAt(window->bounds().CenterPoint());
}
virtual void ActivateWindowDirect(aura::Window* window) OVERRIDE {
aura::test::EventGenerator generator(root_window(), window);
generator.GestureTapAt(window->bounds().CenterPoint());
}
virtual void DeactivateWindowDirect(aura::Window* window) OVERRIDE {
aura::Window* next_activatable =
test_focus_rules()->GetNextActivatableWindow(window);
aura::test::EventGenerator generator(root_window(), next_activatable);
generator.GestureTapAt(window->bounds().CenterPoint());
}
virtual bool IsInputEvent() OVERRIDE { return true; }
DISALLOW_COPY_AND_ASSIGN(FocusControllerGestureEventTest);
};
// Test base for tests where focus is implicitly set to a window as the result
// of a disposition change to the focused window or the hierarchy that contains
// it.
class FocusControllerImplicitTestBase : public FocusControllerTestBase {
protected:
explicit FocusControllerImplicitTestBase(bool parent) : parent_(parent) {}
aura::Window* GetDispositionWindow(aura::Window* window) {
return parent_ ? window->parent() : window;
}
// Change the disposition of |window| in such a way as it will lose focus.
virtual void ChangeWindowDisposition(aura::Window* window) = 0;
// Allow each disposition change test to add additional post-disposition
// change expectations.
virtual void PostDispostionChangeExpectations() {}
// Overridden from FocusControllerTestBase:
virtual void BasicFocus() OVERRIDE {
EXPECT_EQ(NULL, GetFocusedWindow());
aura::Window* w211 = root_window()->GetChildById(211);
FocusWindow(w211);
EXPECT_EQ(211, GetFocusedWindowId());
ChangeWindowDisposition(w211);
// BasicFocusRules passes focus to the parent.
EXPECT_EQ(parent_ ? 2 : 21, GetFocusedWindowId());
}
virtual void BasicActivation() OVERRIDE {
DCHECK(!parent_) << "Activation tests don't support parent changes.";
EXPECT_EQ(NULL, GetActiveWindow());
aura::Window* w2 = root_window()->GetChildById(2);
ActivateWindow(w2);
EXPECT_EQ(2, GetActiveWindowId());
ChangeWindowDisposition(w2);
EXPECT_EQ(3, GetActiveWindowId());
PostDispostionChangeExpectations();
}
virtual void FocusEvents() OVERRIDE {
aura::Window* w211 = root_window()->GetChildById(211);
FocusWindow(w211);
ScopedFocusNotificationObserver root_observer(root_window());
ScopedTargetFocusNotificationObserver observer211(root_window(), 211);
root_observer.ExpectCounts(0, 0);
observer211.ExpectCounts(0, 0);
ChangeWindowDisposition(w211);
root_observer.ExpectCounts(0, 1);
observer211.ExpectCounts(0, 1);
}
virtual void ActivationEvents() OVERRIDE {
DCHECK(!parent_) << "Activation tests don't support parent changes.";
aura::Window* w2 = root_window()->GetChildById(2);
ActivateWindow(w2);
ScopedFocusNotificationObserver root_observer(root_window());
ScopedTargetFocusNotificationObserver observer2(root_window(), 2);
ScopedTargetFocusNotificationObserver observer3(root_window(), 3);
root_observer.ExpectCounts(0, 0);
observer2.ExpectCounts(0, 0);
observer3.ExpectCounts(0, 0);
ChangeWindowDisposition(w2);
root_observer.ExpectCounts(1, 1);
observer2.ExpectCounts(1, 1);
observer3.ExpectCounts(1, 1);
}
virtual void FocusRulesOverride() OVERRIDE {
EXPECT_EQ(NULL, GetFocusedWindow());
aura::Window* w211 = root_window()->GetChildById(211);
FocusWindow(w211);
EXPECT_EQ(211, GetFocusedWindowId());
test_focus_rules()->set_focus_restriction(root_window()->GetChildById(11));
ChangeWindowDisposition(w211);
// Normally, focus would shift to the parent (w21) but the override shifts
// it to 11.
EXPECT_EQ(11, GetFocusedWindowId());
test_focus_rules()->set_focus_restriction(NULL);
}
virtual void ActivationRulesOverride() OVERRIDE {
DCHECK(!parent_) << "Activation tests don't support parent changes.";
aura::Window* w1 = root_window()->GetChildById(1);
ActivateWindow(w1);
EXPECT_EQ(1, GetActiveWindowId());
EXPECT_EQ(1, GetFocusedWindowId());
aura::Window* w3 = root_window()->GetChildById(3);
test_focus_rules()->set_focus_restriction(w3);
// Normally, activation/focus would move to w2, but since we have a focus
// restriction, it should move to w3 instead.
ChangeWindowDisposition(w1);
EXPECT_EQ(3, GetActiveWindowId());
EXPECT_EQ(3, GetFocusedWindowId());
test_focus_rules()->set_focus_restriction(NULL);
ActivateWindow(root_window()->GetChildById(2));
EXPECT_EQ(2, GetActiveWindowId());
EXPECT_EQ(2, GetFocusedWindowId());
}
private:
// When true, the disposition change occurs to the parent of the window
// instead of to the window. This verifies that changes occurring in the
// hierarchy that contains the window affect the window's focus.
bool parent_;
DISALLOW_COPY_AND_ASSIGN(FocusControllerImplicitTestBase);
};
// Focus and Activation changes in response to window visibility changes.
class FocusControllerHideTest : public FocusControllerImplicitTestBase {
public:
FocusControllerHideTest() : FocusControllerImplicitTestBase(false) {}
protected:
FocusControllerHideTest(bool parent)
: FocusControllerImplicitTestBase(parent) {}
// Overridden from FocusControllerImplicitTestBase:
virtual void ChangeWindowDisposition(aura::Window* window) OVERRIDE {
GetDispositionWindow(window)->Hide();
}
virtual void PostDispostionChangeExpectations() OVERRIDE {
// BasicActivation() starts with the stacking order: 1, 2, 3 (3 topmost)
// and then activates 2. After 2 is hidden in ChangeWindowDisposition
// above, 3 is activated, but code in
// FocusController::OnWindowVisibilityChanging keeps 2's layer above 3's
// until a hide animation completes (e.g. a fade-out transition).
aura::Window* w2 = root_window()->GetChildById(2);
aura::Window* w3 = root_window()->GetChildById(3);
// W2 was hidden, but its layer should still be stacked above W3's.
typedef std::vector<ui::Layer*> Layers;
const Layers& children = w3->parent()->layer()->children();
Layers::const_iterator w3_iter =
std::find(children.begin(), children.end(), w3->layer());
Layers::const_iterator w2_iter =
std::find(children.begin(), children.end(), w2->layer());
EXPECT_TRUE(w2_iter > w3_iter);
}
private:
DISALLOW_COPY_AND_ASSIGN(FocusControllerHideTest);
};
// Focus and Activation changes in response to window parent visibility
// changes.
class FocusControllerParentHideTest : public FocusControllerHideTest {
public:
FocusControllerParentHideTest() : FocusControllerHideTest(true) {}
private:
DISALLOW_COPY_AND_ASSIGN(FocusControllerParentHideTest);
};
// Focus and Activation changes in response to window destruction.
class FocusControllerDestructionTest : public FocusControllerImplicitTestBase {
public:
FocusControllerDestructionTest() : FocusControllerImplicitTestBase(false) {}
protected:
FocusControllerDestructionTest(bool parent)
: FocusControllerImplicitTestBase(parent) {}
// Overridden from FocusControllerImplicitTestBase:
virtual void ChangeWindowDisposition(aura::Window* window) OVERRIDE {
delete GetDispositionWindow(window);
}
private:
DISALLOW_COPY_AND_ASSIGN(FocusControllerDestructionTest);
};
// Focus and Activation changes in response to window parent destruction.
class FocusControllerParentDestructionTest
: public FocusControllerDestructionTest {
public:
FocusControllerParentDestructionTest()
: FocusControllerDestructionTest(true) {}
private:
DISALLOW_COPY_AND_ASSIGN(FocusControllerParentDestructionTest);
};
// Focus and Activation changes in response to window removal.
class FocusControllerRemovalTest : public FocusControllerImplicitTestBase {
public:
FocusControllerRemovalTest() : FocusControllerImplicitTestBase(false) {}
protected:
FocusControllerRemovalTest(bool parent)
: FocusControllerImplicitTestBase(parent) {}
// Overridden from FocusControllerImplicitTestBase:
virtual void ChangeWindowDisposition(aura::Window* window) OVERRIDE {
aura::Window* disposition_window = GetDispositionWindow(window);
disposition_window->parent()->RemoveChild(disposition_window);
window_owner_.reset(disposition_window);
}
virtual void TearDown() OVERRIDE {
window_owner_.reset();
FocusControllerImplicitTestBase::TearDown();
}
private:
scoped_ptr<aura::Window> window_owner_;
DISALLOW_COPY_AND_ASSIGN(FocusControllerRemovalTest);
};
// Focus and Activation changes in response to window parent removal.
class FocusControllerParentRemovalTest : public FocusControllerRemovalTest {
public:
FocusControllerParentRemovalTest() : FocusControllerRemovalTest(true) {}
private:
DISALLOW_COPY_AND_ASSIGN(FocusControllerParentRemovalTest);
};
#define FOCUS_CONTROLLER_TEST(TESTCLASS, TESTNAME) \
TEST_F(TESTCLASS, TESTNAME) { TESTNAME(); }
// Runs direct focus change tests (input events and API calls).
#define DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
FOCUS_CONTROLLER_TEST(FocusControllerApiTest, TESTNAME) \
FOCUS_CONTROLLER_TEST(FocusControllerMouseEventTest, TESTNAME) \
FOCUS_CONTROLLER_TEST(FocusControllerGestureEventTest, TESTNAME)
// Runs implicit focus change tests for disposition changes to target.
#define IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) \
FOCUS_CONTROLLER_TEST(FocusControllerHideTest, TESTNAME) \
FOCUS_CONTROLLER_TEST(FocusControllerDestructionTest, TESTNAME) \
FOCUS_CONTROLLER_TEST(FocusControllerRemovalTest, TESTNAME)
// Runs implicit focus change tests for disposition changes to target's parent
// hierarchy.
#define IMPLICIT_FOCUS_CHANGE_PARENT_TESTS(TESTNAME) \
/* TODO(beng): parent destruction tests are not supported at
present due to workspace manager issues. \
FOCUS_CONTROLLER_TEST(FocusControllerParentDestructionTest, TESTNAME) */ \
FOCUS_CONTROLLER_TEST(FocusControllerParentHideTest, TESTNAME) \
FOCUS_CONTROLLER_TEST(FocusControllerParentRemovalTest, TESTNAME)
// Runs all implicit focus change tests (changes to the target and target's
// parent hierarchy)
#define IMPLICIT_FOCUS_CHANGE_TESTS(TESTNAME) \
IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) \
IMPLICIT_FOCUS_CHANGE_PARENT_TESTS(TESTNAME)
// Runs all possible focus change tests.
#define ALL_FOCUS_TESTS(TESTNAME) \
DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
IMPLICIT_FOCUS_CHANGE_TESTS(TESTNAME)
// Runs focus change tests that apply only to the target. For example,
// implicit activation changes caused by window disposition changes do not
// occur when changes to the containing hierarchy happen.
#define TARGET_FOCUS_TESTS(TESTNAME) \
DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME)
// - Focuses a window, verifies that focus changed.
ALL_FOCUS_TESTS(BasicFocus);
// - Activates a window, verifies that activation changed.
TARGET_FOCUS_TESTS(BasicActivation);
// - Focuses a window, verifies that focus events were dispatched.
ALL_FOCUS_TESTS(FocusEvents);
// - Focuses or activates a window multiple times, verifies that events are only
// dispatched when focus/activation actually changes.
DIRECT_FOCUS_CHANGE_TESTS(DuplicateFocusEvents);
DIRECT_FOCUS_CHANGE_TESTS(DuplicateActivationEvents);
// - Activates a window, verifies that activation events were dispatched.
TARGET_FOCUS_TESTS(ActivationEvents);
// - Attempts to active a hidden window, verifies that current window is
// attempted to be reactivated and the appropriate event dispatched.
FOCUS_CONTROLLER_TEST(FocusControllerApiTest, ReactivationEvents);
// - Input events/API calls shift focus between focusable windows within the
// active window.
DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusWithinActiveWindow);
// - Input events/API calls to a child window of an inactive window shifts
// activation to the activatable parent and focuses the child.
DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToChildOfInactiveWindow);
// - Input events/API calls to focus the parent of the focused window do not
// shift focus away from the child.
DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToParentOfFocusedWindow);
// - Verifies that FocusRules determine what can be focused.
ALL_FOCUS_TESTS(FocusRulesOverride);
// - Verifies that FocusRules determine what can be activated.
TARGET_FOCUS_TESTS(ActivationRulesOverride);
// - Verifies that attempts to change focus or activation from a focus or
// activation change observer are ignored.
DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusOnActivation);
DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusOnActivationDueToHide);
DIRECT_FOCUS_CHANGE_TESTS(NoShiftActiveOnActivation);
// Clicking on a window which has capture should not result in a focus change.
DIRECT_FOCUS_CHANGE_TESTS(NoFocusChangeOnClickOnCaptureWindow);
FOCUS_CONTROLLER_TEST(FocusControllerApiTest,
ChangeFocusWhenNothingFocusedAndCaptured);
// See description above DontPassDeletedWindow() for details.
FOCUS_CONTROLLER_TEST(FocusControllerApiTest, DontPassDeletedWindow);
} // namespace corewm
} // namespace views