Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1391)

Unified Diff: mojo/services/window_manager/focus_controller_unittest.cc

Issue 1049993002: Get mojo_shell building inside chromium checkout. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix presubmit Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: mojo/services/window_manager/focus_controller_unittest.cc
diff --git a/mojo/services/window_manager/focus_controller_unittest.cc b/mojo/services/window_manager/focus_controller_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ea60ac726a53619205574047a81474d9ac127e82
--- /dev/null
+++ b/mojo/services/window_manager/focus_controller_unittest.cc
@@ -0,0 +1,1197 @@
+// 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 "mojo/services/window_manager/focus_controller.h"
+
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/services/window_manager/basic_focus_rules.h"
+#include "mojo/services/window_manager/capture_controller.h"
+#include "mojo/services/window_manager/focus_controller_observer.h"
+#include "mojo/services/window_manager/view_event_dispatcher.h"
+#include "mojo/services/window_manager/view_targeter.h"
+#include "mojo/services/window_manager/window_manager_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event_utils.h"
+#include "ui/gfx/geometry/rect.h"
+
+using mojo::View;
+
+namespace window_manager {
+
+// Counts the number of events that occur.
+class FocusNotificationObserver : public FocusControllerObserver {
+ public:
+ FocusNotificationObserver()
+ : activation_changed_count_(0),
+ focus_changed_count_(0),
+ reactivation_count_(0),
+ reactivation_requested_view_(NULL),
+ reactivation_actual_view_(NULL) {}
+ ~FocusNotificationObserver() override {}
+
+ 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_; }
+ View* reactivation_requested_view() const {
+ return reactivation_requested_view_;
+ }
+ View* reactivation_actual_view() const {
+ return reactivation_actual_view_;
+ }
+
+ protected:
+ // Overridden from FocusControllerObserver:
+ void OnActivated(View* gained_active) override {
+ ++activation_changed_count_;
+ }
+
+ void OnFocused(View* gained_focus) override { ++focus_changed_count_; }
+
+ void OnAttemptToReactivateView(View* request_active,
+ View* actual_active) override {
+ ++reactivation_count_;
+ reactivation_requested_view_ = request_active;
+ reactivation_actual_view_ = actual_active;
+ }
+
+ private:
+ int activation_changed_count_;
+ int focus_changed_count_;
+ int reactivation_count_;
+ View* reactivation_requested_view_;
+ View* reactivation_actual_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusNotificationObserver);
+};
+
+class ViewDestroyer {
+ public:
+ virtual View* GetDestroyedView() = 0;
+
+ protected:
+ virtual ~ViewDestroyer() {}
+};
+
+// FocusNotificationObserver that keeps track of whether it was notified about
+// activation changes or focus changes with a destroyed view.
+class RecordingFocusNotificationObserver : public FocusNotificationObserver {
+ public:
+ RecordingFocusNotificationObserver(FocusController* focus_controller,
+ ViewDestroyer* destroyer)
+ : focus_controller_(focus_controller),
+ destroyer_(destroyer),
+ active_(nullptr),
+ focus_(nullptr),
+ was_notified_with_destroyed_view_(false) {
+ focus_controller_->AddObserver(this);
+ }
+ ~RecordingFocusNotificationObserver() override {
+ focus_controller_->RemoveObserver(this);
+ }
+
+ bool was_notified_with_destroyed_view() const {
+ return was_notified_with_destroyed_view_;
+ }
+
+ // Overridden from FocusNotificationObserver:
+ void OnActivated(View* gained_active) override {
+ if (active_ && active_ == destroyer_->GetDestroyedView())
+ was_notified_with_destroyed_view_ = true;
+ active_ = gained_active;
+ }
+
+ void OnFocused(View* gained_focus) override {
+ if (focus_ && focus_ == destroyer_->GetDestroyedView())
+ was_notified_with_destroyed_view_ = true;
+ focus_ = gained_focus;
+ }
+
+ private:
+ FocusController* focus_controller_;
+
+ // Not owned.
+ ViewDestroyer* destroyer_;
+ View* active_;
+ View* focus_;
+
+ // Whether the observer was notified about the loss of activation or the
+ // loss of focus with a view already destroyed by |destroyer_| as the
+ // |lost_active| or |lost_focus| parameter.
+ bool was_notified_with_destroyed_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(RecordingFocusNotificationObserver);
+};
+
+class DestroyOnLoseActivationFocusNotificationObserver
+ : public FocusNotificationObserver,
+ public ViewDestroyer {
+ public:
+ DestroyOnLoseActivationFocusNotificationObserver(
+ FocusController* focus_controller,
+ View* view_to_destroy,
+ View* initial_active)
+ : focus_controller_(focus_controller),
+ view_to_destroy_(view_to_destroy),
+ active_(initial_active),
+ did_destroy_(false) {
+ focus_controller_->AddObserver(this);
+ }
+ ~DestroyOnLoseActivationFocusNotificationObserver() override {
+ focus_controller_->RemoveObserver(this);
+ }
+
+ // Overridden from FocusNotificationObserver:
+ void OnActivated(View* gained_active) override {
+ if (view_to_destroy_ && active_ == view_to_destroy_) {
+ view_to_destroy_->Destroy();
+ did_destroy_ = true;
+ }
+ active_ = gained_active;
+ }
+
+ // Overridden from ViewDestroyer:
+ View* GetDestroyedView() override {
+ return did_destroy_ ? view_to_destroy_ : nullptr;
+ }
+
+ private:
+ FocusController* focus_controller_;
+ View* view_to_destroy_;
+ View* active_;
+ bool did_destroy_;
+
+ DISALLOW_COPY_AND_ASSIGN(DestroyOnLoseActivationFocusNotificationObserver);
+};
+
+class ScopedFocusNotificationObserver : public FocusNotificationObserver {
+ public:
+ ScopedFocusNotificationObserver(FocusController* focus_controller)
+ : focus_controller_(focus_controller) {
+ focus_controller_->AddObserver(this);
+ }
+ ~ScopedFocusNotificationObserver() override {
+ focus_controller_->RemoveObserver(this);
+ }
+
+ private:
+ FocusController* focus_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFocusNotificationObserver);
+};
+
+// Only responds to events if a message contains |target| as a parameter.
+class ScopedFilteringFocusNotificationObserver
+ : public FocusNotificationObserver {
+ public:
+ ScopedFilteringFocusNotificationObserver(FocusController* focus_controller,
+ View* target,
+ View* initial_active,
+ View* initial_focus)
+ : focus_controller_(focus_controller),
+ target_(target),
+ active_(initial_active),
+ focus_(initial_focus) {
+ focus_controller_->AddObserver(this);
+ }
+ ~ScopedFilteringFocusNotificationObserver() override {
+ focus_controller_->RemoveObserver(this);
+ }
+
+ private:
+ // Overridden from FocusControllerObserver:
+ void OnActivated(View* gained_active) override {
+ if (gained_active == target_ || active_ == target_)
+ FocusNotificationObserver::OnActivated(gained_active);
+ active_ = gained_active;
+ }
+
+ void OnFocused(View* gained_focus) override {
+ if (gained_focus == target_ || focus_ == target_)
+ FocusNotificationObserver::OnFocused(gained_focus);
+ focus_ = gained_focus;
+ }
+
+ void OnAttemptToReactivateView(View* request_active,
+ View* actual_active) override {
+ if (request_active == target_ || actual_active == target_) {
+ FocusNotificationObserver::OnAttemptToReactivateView(request_active,
+ actual_active);
+ }
+ }
+
+ FocusController* focus_controller_;
+ View* target_;
+ View* active_;
+ View* focus_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFilteringFocusNotificationObserver);
+};
+
+// Used to fake the handling of events in the pre-target phase.
+class SimpleEventHandler : public ui::EventHandler {
+ public:
+ SimpleEventHandler() {}
+ ~SimpleEventHandler() override {}
+
+ // Overridden from ui::EventHandler:
+ void OnMouseEvent(ui::MouseEvent* event) override {
+ event->SetHandled();
+ }
+ void OnGestureEvent(ui::GestureEvent* event) override {
+ event->SetHandled();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SimpleEventHandler);
+};
+
+class FocusShiftingActivationObserver
+ : public FocusControllerObserver {
+ public:
+ explicit FocusShiftingActivationObserver(FocusController* focus_controller,
+ View* activated_view)
+ : focus_controller_(focus_controller),
+ activated_view_(activated_view),
+ shift_focus_to_(NULL) {}
+ ~FocusShiftingActivationObserver() override {}
+
+ void set_shift_focus_to(View* shift_focus_to) {
+ shift_focus_to_ = shift_focus_to;
+ }
+
+ private:
+ // Overridden from FocusControllerObserver:
+ void OnActivated(View* gained_active) override {
+ // Shift focus to a child. This should prevent the default focusing from
+ // occurring in FocusController::FocusView().
+ if (gained_active == activated_view_)
+ focus_controller_->FocusView(shift_focus_to_);
+ }
+
+ void OnFocused(View* gained_focus) override {}
+
+ FocusController* focus_controller_;
+ View* activated_view_;
+ View* shift_focus_to_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusShiftingActivationObserver);
+};
+
+// BasicFocusRules 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 BasicFocusRules {
+ public:
+ TestFocusRules(View* root)
+ : BasicFocusRules(root), focus_restriction_(NULL) {}
+
+ // Restricts focus and activation to this view and its child hierarchy.
+ void set_focus_restriction(View* focus_restriction) {
+ focus_restriction_ = focus_restriction;
+ }
+
+ // Overridden from BasicFocusRules:
+ bool SupportsChildActivation(View* view) const override {
+ // In FocusControllerTests, only the Root has activatable children.
+ return view->GetRoot() == view;
+ }
+ bool CanActivateView(View* view) const override {
+ // Restricting focus to a non-activatable child view means the activatable
+ // parent outside the focus restriction is activatable.
+ bool can_activate =
+ CanFocusOrActivate(view) || view->Contains(focus_restriction_);
+ return can_activate ? BasicFocusRules::CanActivateView(view) : false;
+ }
+ bool CanFocusView(View* view) const override {
+ return CanFocusOrActivate(view) ? BasicFocusRules::CanFocusView(view)
+ : false;
+ }
+ View* GetActivatableView(View* view) const override {
+ return BasicFocusRules::GetActivatableView(
+ CanFocusOrActivate(view) ? view : focus_restriction_);
+ }
+ View* GetFocusableView(View* view) const override {
+ return BasicFocusRules::GetFocusableView(
+ CanFocusOrActivate(view) ? view : focus_restriction_);
+ }
+ View* GetNextActivatableView(View* ignore) const override {
+ View* next_activatable = BasicFocusRules::GetNextActivatableView(ignore);
+ return CanFocusOrActivate(next_activatable)
+ ? next_activatable
+ : GetActivatableView(focus_restriction_);
+ }
+
+ private:
+ bool CanFocusOrActivate(View* view) const {
+ return !focus_restriction_ || focus_restriction_->Contains(view);
+ }
+
+ View* focus_restriction_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFocusRules);
+};
+
+// Common infrastructure shared by all FocusController test types.
+class FocusControllerTestBase : public testing::Test {
+ protected:
+ // Hierarchy used by all tests:
+ // root_view
+ // +-- w1
+ // | +-- w11
+ // | +-- w12
+ // +-- w2
+ // | +-- w21
+ // | +-- w211
+ // +-- w3
+ FocusControllerTestBase()
+ : root_view_(TestView::Build(0, gfx::Rect(0, 0, 800, 600))),
+ v1(TestView::Build(1, gfx::Rect(0, 0, 50, 50), root_view())),
+ v11(TestView::Build(11, gfx::Rect(5, 5, 10, 10), v1)),
+ v12(TestView::Build(12, gfx::Rect(15, 15, 10, 10), v1)),
+ v2(TestView::Build(2, gfx::Rect(75, 75, 50, 50), root_view())),
+ v21(TestView::Build(21, gfx::Rect(5, 5, 10, 10), v2)),
+ v211(TestView::Build(211, gfx::Rect(1, 1, 5, 5), v21)),
+ v3(TestView::Build(3, gfx::Rect(125, 125, 50, 50), root_view())) {}
+
+ // Overridden from testing::Test:
+ void SetUp() override {
+ testing::Test::SetUp();
+
+ test_focus_rules_ = new TestFocusRules(root_view());
+ focus_controller_.reset(
+ new FocusController(scoped_ptr<FocusRules>(test_focus_rules_)));
+ SetFocusController(root_view(), focus_controller_.get());
+
+ capture_controller_.reset(new CaptureController);
+ SetCaptureController(root_view(), capture_controller_.get());
+
+ ViewTarget* root_target = root_view_->target();
+ root_target->SetEventTargeter(scoped_ptr<ViewTargeter>(new ViewTargeter()));
+ view_event_dispatcher_.reset(new ViewEventDispatcher());
+ view_event_dispatcher_->SetRootViewTarget(root_target);
+
+ GetRootViewTarget()->AddPreTargetHandler(focus_controller_.get());
+ }
+
+ void TearDown() override {
+ GetRootViewTarget()->RemovePreTargetHandler(focus_controller_.get());
+ view_event_dispatcher_.reset();
+
+ root_view_->Destroy();
+
+ capture_controller_.reset();
+ test_focus_rules_ = nullptr; // Owned by FocusController.
+ focus_controller_.reset();
+
+ testing::Test::TearDown();
+ }
+
+ void FocusView(View* view) { focus_controller_->FocusView(view); }
+ View* GetFocusedView() { return focus_controller_->GetFocusedView(); }
+ int GetFocusedViewId() {
+ View* focused_view = GetFocusedView();
+ return focused_view ? focused_view->id() : -1;
+ }
+ void ActivateView(View* view) { focus_controller_->ActivateView(view); }
+ void DeactivateView(View* view) { focus_controller_->DeactivateView(view); }
+ View* GetActiveView() { return focus_controller_->GetActiveView(); }
+ int GetActiveViewId() {
+ View* active_view = GetActiveView();
+ return active_view ? active_view->id() : -1;
+ }
+
+ View* GetViewById(int id) { return root_view_->GetChildById(id); }
+
+ void ClickLeftButton(View* view) {
+ // Get the center bounds of |target| in |root_view_| coordinate space.
+ gfx::Point center =
+ gfx::Rect(view->bounds().To<gfx::Rect>().size()).CenterPoint();
+ ViewTarget::ConvertPointToTarget(ViewTarget::TargetFromView(view),
+ root_view_->target(), &center);
+
+ ui::MouseEvent button_down(ui::ET_MOUSE_PRESSED, center, center,
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_NONE);
+ ui::EventDispatchDetails details =
+ view_event_dispatcher_->OnEventFromSource(&button_down);
+ CHECK(!details.dispatcher_destroyed);
+
+ ui::MouseEvent button_up(ui::ET_MOUSE_RELEASED, center, center,
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_NONE);
+ details = view_event_dispatcher_->OnEventFromSource(&button_up);
+ CHECK(!details.dispatcher_destroyed);
+ }
+
+ ViewTarget* GetRootViewTarget() {
+ return ViewTarget::TargetFromView(root_view());
+ }
+
+ View* root_view() { return root_view_; }
+ TestFocusRules* test_focus_rules() { return test_focus_rules_; }
+ FocusController* focus_controller() { return focus_controller_.get(); }
+ CaptureController* capture_controller() { return capture_controller_.get(); }
+
+ // 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 ShiftFocusWithinActiveView() {}
+ virtual void ShiftFocusToChildOfInactiveView() {}
+ virtual void ShiftFocusToParentOfFocusedView() {}
+ virtual void FocusRulesOverride() = 0;
+ virtual void ActivationRulesOverride() = 0;
+ virtual void ShiftFocusOnActivation() {}
+ virtual void ShiftFocusOnActivationDueToHide() {}
+ virtual void NoShiftActiveOnActivation() {}
+ virtual void ChangeFocusWhenNothingFocusedAndCaptured() {}
+ virtual void DontPassDestroyedView() {}
+ // TODO(erg): Also, void FocusedTextInputClient() once we build the IME.
+
+ private:
+ TestView* root_view_;
+ scoped_ptr<FocusController> focus_controller_;
+ TestFocusRules* test_focus_rules_;
+ scoped_ptr<CaptureController> capture_controller_;
+ // TODO(erg): The aura version of this class also keeps track of WMState. Do
+ // we need something analogous here?
+
+ scoped_ptr<ViewEventDispatcher> view_event_dispatcher_;
+
+ TestView* v1;
+ TestView* v11;
+ TestView* v12;
+ TestView* v2;
+ TestView* v21;
+ TestView* v211;
+ TestView* v3;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerTestBase);
+};
+
+// Test base for tests where focus is directly set to a target view.
+class FocusControllerDirectTestBase : public FocusControllerTestBase {
+ protected:
+ FocusControllerDirectTestBase() {}
+
+ // Different test types shift focus in different ways.
+ virtual void FocusViewDirect(View* view) = 0;
+ virtual void ActivateViewDirect(View* view) = 0;
+ virtual void DeactivateViewDirect(View* view) = 0;
+
+ // Input events do not change focus if the view can not be focused.
+ virtual bool IsInputEvent() = 0;
+
+ void FocusViewById(int id) {
+ View* view = root_view()->GetChildById(id);
+ DCHECK(view);
+ FocusViewDirect(view);
+ }
+ void ActivateViewById(int id) {
+ View* view = root_view()->GetChildById(id);
+ DCHECK(view);
+ ActivateViewDirect(view);
+ }
+
+ // Overridden from FocusControllerTestBase:
+ void BasicFocus() override {
+ EXPECT_EQ(nullptr, GetFocusedView());
+ FocusViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+ FocusViewById(2);
+ EXPECT_EQ(2, GetFocusedViewId());
+ }
+ void BasicActivation() override {
+ EXPECT_EQ(nullptr, GetActiveView());
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetActiveViewId());
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetActiveViewId());
+ // Verify that attempting to deactivate NULL does not crash and does not
+ // change activation.
+ DeactivateView(nullptr);
+ EXPECT_EQ(2, GetActiveViewId());
+ DeactivateView(GetActiveView());
+ EXPECT_EQ(1, GetActiveViewId());
+ }
+ void FocusEvents() override {
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer1(
+ focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+ ScopedFilteringFocusNotificationObserver observer2(
+ focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+
+ {
+ SCOPED_TRACE("initial state");
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+ }
+
+ FocusViewById(1);
+ {
+ SCOPED_TRACE("FocusViewById(1)");
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(0, 0);
+ }
+
+ FocusViewById(2);
+ {
+ SCOPED_TRACE("FocusViewById(2)");
+ root_observer.ExpectCounts(2, 2);
+ observer1.ExpectCounts(2, 2);
+ observer2.ExpectCounts(1, 1);
+ }
+ }
+ void DuplicateFocusEvents() override {
+ // Focusing an existing focused view should not resend focus events.
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer1(
+ focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+
+ FocusViewById(1);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+
+ FocusViewById(1);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ }
+ void ActivationEvents() override {
+ ActivateViewById(1);
+
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer1(
+ focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+ ScopedFilteringFocusNotificationObserver observer2(
+ focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+
+ ActivateViewById(2);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+ }
+ void ReactivationEvents() override {
+ ActivateViewById(1);
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ EXPECT_EQ(0, root_observer.reactivation_count());
+ GetViewById(2)->SetVisible(false);
+ // When we attempt to activate "2", which cannot be activated because it
+ // is not visible, "1" will be reactivated.
+ ActivateViewById(2);
+ EXPECT_EQ(1, root_observer.reactivation_count());
+ EXPECT_EQ(GetViewById(2),
+ root_observer.reactivation_requested_view());
+ EXPECT_EQ(GetViewById(1),
+ root_observer.reactivation_actual_view());
+ }
+ void DuplicateActivationEvents() override {
+ ActivateViewById(1);
+
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer1(
+ focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+ ScopedFilteringFocusNotificationObserver observer2(
+ focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+
+ ActivateViewById(2);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+
+ // Activating an existing active view should not resend activation events.
+ ActivateViewById(2);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+ }
+ void ShiftFocusWithinActiveView() override {
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(1, GetFocusedViewId());
+ FocusViewById(11);
+ EXPECT_EQ(11, GetFocusedViewId());
+ FocusViewById(12);
+ EXPECT_EQ(12, GetFocusedViewId());
+ }
+ void ShiftFocusToChildOfInactiveView() override {
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetActiveViewId());
+ EXPECT_EQ(2, GetFocusedViewId());
+ FocusViewById(11);
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(11, GetFocusedViewId());
+ }
+ void ShiftFocusToParentOfFocusedView() override {
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+ FocusViewById(11);
+ EXPECT_EQ(11, GetFocusedViewId());
+ FocusViewById(1);
+ // Focus should _not_ shift to the parent of the already-focused view.
+ EXPECT_EQ(11, GetFocusedViewId());
+ }
+ void FocusRulesOverride() override {
+ EXPECT_EQ(NULL, GetFocusedView());
+ FocusViewById(11);
+ EXPECT_EQ(11, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(GetViewById(211));
+ FocusViewById(12);
+ // Input events leave focus unchanged; direct API calls will change focus
+ // to the restricted view.
+ int focused_view = IsInputEvent() ? 11 : 211;
+ EXPECT_EQ(focused_view, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ FocusViewById(12);
+ EXPECT_EQ(12, GetFocusedViewId());
+ }
+ void ActivationRulesOverride() override {
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ View* v3 = GetViewById(3);
+ test_focus_rules()->set_focus_restriction(v3);
+
+ ActivateViewById(2);
+ // Input events leave activation unchanged; direct API calls will activate
+ // the restricted view.
+ int active_view = IsInputEvent() ? 1 : 3;
+ EXPECT_EQ(active_view, GetActiveViewId());
+ EXPECT_EQ(active_view, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetActiveViewId());
+ EXPECT_EQ(2, GetFocusedViewId());
+ }
+ void ShiftFocusOnActivation() override {
+ // When a view is activated, by default that view is also focused.
+ // An ActivationChangeObserver may shift focus to another view within the
+ // same activatable view.
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetFocusedViewId());
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ ActivateViewById(2);
+
+ View* target = GetViewById(1);
+
+ scoped_ptr<FocusShiftingActivationObserver> observer(
+ new FocusShiftingActivationObserver(focus_controller(), target));
+ observer->set_shift_focus_to(target->GetChildById(11));
+ focus_controller()->AddObserver(observer.get());
+
+ ActivateViewById(1);
+
+ // w1's ActivationChangeObserver shifted focus to this child, pre-empting
+ // FocusController's default setting.
+ EXPECT_EQ(11, GetFocusedViewId());
+
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetFocusedViewId());
+
+ // Simulate a focus reset by the ActivationChangeObserver. This should
+ // trigger the default setting in FocusController.
+ observer->set_shift_focus_to(nullptr);
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ focus_controller()->RemoveObserver(observer.get());
+
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetFocusedViewId());
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+ }
+ void ShiftFocusOnActivationDueToHide() override {
+ // Similar to ShiftFocusOnActivation except the activation change is
+ // triggered by hiding the active view.
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ // Removes view 3 as candidate for next activatable view.
+ root_view()->GetChildById(3)->SetVisible(false);
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ View* target = root_view()->GetChildById(2);
+
+ scoped_ptr<FocusShiftingActivationObserver> observer(
+ new FocusShiftingActivationObserver(focus_controller(), target));
+ observer->set_shift_focus_to(target->GetChildById(21));
+ focus_controller()->AddObserver(observer.get());
+
+ // Hide the active view.
+ root_view()->GetChildById(1)->SetVisible(false);
+
+ EXPECT_EQ(21, GetFocusedViewId());
+
+ focus_controller()->RemoveObserver(observer.get());
+ }
+ void NoShiftActiveOnActivation() override {
+ // When a view is activated, we need to prevent any change to activation
+ // from being made in response to an activation change notification.
+ }
+
+ // Verifies focus change is honored while capture held.
+ void ChangeFocusWhenNothingFocusedAndCaptured() override {
+ View* v1 = root_view()->GetChildById(1);
+ capture_controller()->SetCapture(v1);
+
+ EXPECT_EQ(-1, GetActiveViewId());
+ EXPECT_EQ(-1, GetFocusedViewId());
+
+ FocusViewById(1);
+
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ capture_controller()->ReleaseCapture(v1);
+ }
+
+ // Verifies if a view that loses activation or focus is destroyed during
+ // observer notification we don't pass the destroyed view to other observers.
+ void DontPassDestroyedView() override {
+ FocusViewById(1);
+
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ {
+ View* to_destroy = root_view()->GetChildById(1);
+ DestroyOnLoseActivationFocusNotificationObserver observer1(
+ focus_controller(), to_destroy, GetActiveView());
+ RecordingFocusNotificationObserver observer2(focus_controller(),
+ &observer1);
+
+ FocusViewById(2);
+
+ EXPECT_EQ(2, GetActiveViewId());
+ EXPECT_EQ(2, GetFocusedViewId());
+
+ EXPECT_EQ(to_destroy, observer1.GetDestroyedView());
+ EXPECT_FALSE(observer2.was_notified_with_destroyed_view());
+ }
+
+ {
+ View* to_destroy = root_view()->GetChildById(2);
+ DestroyOnLoseActivationFocusNotificationObserver observer1(
+ focus_controller(), to_destroy, GetActiveView());
+ RecordingFocusNotificationObserver observer2(focus_controller(),
+ &observer1);
+
+ FocusViewById(3);
+
+ EXPECT_EQ(3, GetActiveViewId());
+ EXPECT_EQ(3, GetFocusedViewId());
+
+ EXPECT_EQ(to_destroy, observer1.GetDestroyedView());
+ EXPECT_FALSE(observer2.was_notified_with_destroyed_view());
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerDirectTestBase);
+};
+
+// Focus and Activation changes via the FocusController API.
+class FocusControllerApiTest : public FocusControllerDirectTestBase {
+ public:
+ FocusControllerApiTest() {}
+
+ private:
+ // Overridden from FocusControllerTestBase:
+ void FocusViewDirect(View* view) override { FocusView(view); }
+ void ActivateViewDirect(View* view) override { ActivateView(view); }
+ void DeactivateViewDirect(View* view) override { DeactivateView(view); }
+ bool IsInputEvent() override { return false; }
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerApiTest);
+};
+
+// Focus and Activation changes via input events.
+class FocusControllerMouseEventTest : public FocusControllerDirectTestBase {
+ public:
+ FocusControllerMouseEventTest() {}
+
+ // Tests that a handled mouse event does not trigger a view activation.
+ void IgnoreHandledEvent() {
+ EXPECT_EQ(NULL, GetActiveView());
+ View* v1 = root_view()->GetChildById(1);
+ SimpleEventHandler handler;
+ GetRootViewTarget()->PrependPreTargetHandler(&handler);
+ ClickLeftButton(v1);
+ EXPECT_EQ(NULL, GetActiveView());
+ // TODO(erg): Add gesture testing when we get gestures working.
+ GetRootViewTarget()->RemovePreTargetHandler(&handler);
+ ClickLeftButton(v1);
+ EXPECT_EQ(1, GetActiveViewId());
+ }
+
+ private:
+ // Overridden from FocusControllerTestBase:
+ void FocusViewDirect(View* view) override { ClickLeftButton(view); }
+ void ActivateViewDirect(View* view) override { ClickLeftButton(view); }
+ void DeactivateViewDirect(View* view) override {
+ View* next_activatable = test_focus_rules()->GetNextActivatableView(view);
+ ClickLeftButton(next_activatable);
+ }
+ bool IsInputEvent() override { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerMouseEventTest);
+};
+
+// TODO(erg): Add a FocusControllerGestureEventTest once we have working
+// gesture forwarding and handling.
+
+// 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) {}
+
+ View* GetDispositionView(View* view) {
+ return parent_ ? view->parent() : view;
+ }
+
+ // Change the disposition of |view| in such a way as it will lose focus.
+ virtual void ChangeViewDisposition(View* view) = 0;
+
+ // Allow each disposition change test to add additional post-disposition
+ // change expectations.
+ virtual void PostDispostionChangeExpectations() {}
+
+ // Overridden from FocusControllerTestBase:
+ void BasicFocus() override {
+ EXPECT_EQ(NULL, GetFocusedView());
+
+ View* w211 = root_view()->GetChildById(211);
+ FocusView(w211);
+ EXPECT_EQ(211, GetFocusedViewId());
+
+ ChangeViewDisposition(w211);
+ // BasicFocusRules passes focus to the parent.
+ EXPECT_EQ(parent_ ? 2 : 21, GetFocusedViewId());
+ }
+
+ void BasicActivation() override {
+ DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+ EXPECT_EQ(NULL, GetActiveView());
+
+ View* w2 = root_view()->GetChildById(2);
+ ActivateView(w2);
+ EXPECT_EQ(2, GetActiveViewId());
+
+ ChangeViewDisposition(w2);
+ EXPECT_EQ(3, GetActiveViewId());
+ PostDispostionChangeExpectations();
+ }
+
+ void FocusEvents() override {
+ View* w211 = root_view()->GetChildById(211);
+ FocusView(w211);
+
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer211(
+ focus_controller(), GetViewById(211), GetActiveView(),
+ GetFocusedView());
+
+ {
+ SCOPED_TRACE("first");
+ root_observer.ExpectCounts(0, 0);
+ observer211.ExpectCounts(0, 0);
+ }
+
+ ChangeViewDisposition(w211);
+ {
+ SCOPED_TRACE("second");
+ {
+ SCOPED_TRACE("root_observer");
+ root_observer.ExpectCounts(0, 1);
+ }
+ {
+ SCOPED_TRACE("observer211");
+ observer211.ExpectCounts(0, 1);
+ }
+ }
+ }
+
+ void ActivationEvents() override {
+ DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+ View* w2 = root_view()->GetChildById(2);
+ ActivateView(w2);
+
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer2(
+ focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+ ScopedFilteringFocusNotificationObserver observer3(
+ focus_controller(), GetViewById(3), GetActiveView(), GetFocusedView());
+ root_observer.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+ observer3.ExpectCounts(0, 0);
+
+ ChangeViewDisposition(w2);
+ root_observer.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+ observer3.ExpectCounts(1, 1);
+ }
+
+ void FocusRulesOverride() override {
+ EXPECT_EQ(NULL, GetFocusedView());
+ View* w211 = root_view()->GetChildById(211);
+ FocusView(w211);
+ EXPECT_EQ(211, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(root_view()->GetChildById(11));
+ ChangeViewDisposition(w211);
+ // Normally, focus would shift to the parent (w21) but the override shifts
+ // it to 11.
+ EXPECT_EQ(11, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ }
+
+ void ActivationRulesOverride() override {
+ DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+ View* w1 = root_view()->GetChildById(1);
+ ActivateView(w1);
+
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ View* w3 = root_view()->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.
+ ChangeViewDisposition(w1);
+ EXPECT_EQ(3, GetActiveViewId());
+ EXPECT_EQ(3, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ ActivateView(root_view()->GetChildById(2));
+ EXPECT_EQ(2, GetActiveViewId());
+ EXPECT_EQ(2, GetFocusedViewId());
+ }
+
+ 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:
+ void ChangeViewDisposition(View* view) override {
+ GetDispositionView(view)->SetVisible(false);
+ }
+
+ 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:
+ void ChangeViewDisposition(View* view) override {
+ GetDispositionView(view)->Destroy();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerDestructionTest);
+};
+
+// Focus and Activation changes in response to window removal.
+class FocusControllerRemovalTest : public FocusControllerImplicitTestBase {
+ public:
+ FocusControllerRemovalTest()
+ : FocusControllerImplicitTestBase(false),
+ window_to_destroy_(nullptr) {}
+
+ protected:
+ FocusControllerRemovalTest(bool parent)
+ : FocusControllerImplicitTestBase(parent),
+ window_to_destroy_(nullptr) {}
+
+ // Overridden from FocusControllerImplicitTestBase:
+ void ChangeViewDisposition(View* view) override {
+ View* disposition_view = GetDispositionView(view);
+ disposition_view->parent()->RemoveChild(disposition_view);
+ window_to_destroy_ = disposition_view;
+ }
+ void TearDown() override {
+ if (window_to_destroy_)
+ window_to_destroy_->Destroy();
+
+ FocusControllerImplicitTestBase::TearDown();
+ }
+
+ private:
+ View* window_to_destroy_;
+
+ 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).
+//
+// TODO(erg): Enable gesture events in the future.
+#define DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerApiTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerMouseEventTest, 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) \
+ FOCUS_CONTROLLER_TEST(FocusControllerParentHideTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerParentRemovalTest, TESTNAME)
+// TODO(erg): FocusControllerParentDestructionTest were commented out in the
+// aura version of this file, and don't work when I tried porting things over.
+
+// 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 view, verifies that current view is
+// attempted to be reactivated and the appropriate event dispatched.
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest, ReactivationEvents);
+
+// - Input events/API calls shift focus between focusable views within the
+// active view.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusWithinActiveView);
+
+// - Input events/API calls to a child view of an inactive view shifts
+// activation to the activatable parent and focuses the child.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToChildOfInactiveView);
+
+// - Input events/API calls to focus the parent of the focused view do not
+// shift focus away from the child.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToParentOfFocusedView);
+
+// - 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);
+
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest,
+ ChangeFocusWhenNothingFocusedAndCaptured);
+
+// See description above DontPassDestroyedView() for details.
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest, DontPassDestroyedView);
+
+// TODO(erg): Add the TextInputClient tests here.
+
+// If a mouse event was handled, it should not activate a view.
+FOCUS_CONTROLLER_TEST(FocusControllerMouseEventTest, IgnoreHandledEvent);
+
+} // namespace window_manager
« no previous file with comments | « mojo/services/window_manager/focus_controller_observer.h ('k') | mojo/services/window_manager/focus_rules.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698