summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ash/shell.cc3
-rw-r--r--ash/shell.h7
-rw-r--r--ash/wm/ash_focus_rules.cc37
-rw-r--r--ash/wm/ash_focus_rules.h3
-rw-r--r--ash/wm/panel_layout_manager_unittest.cc6
-rw-r--r--ash/wm/toplevel_window_event_handler_unittest.cc35
-rw-r--r--ash/wm/window_manager_unittest.cc11
-rw-r--r--ui/views/bubble/bubble_delegate.cc1
-rw-r--r--ui/views/corewm/base_focus_rules.cc25
-rw-r--r--ui/views/corewm/focus_controller.cc32
-rw-r--r--ui/views/corewm/focus_controller.h2
-rw-r--r--ui/views/corewm/focus_controller_unittest.cc86
-rw-r--r--ui/views/corewm/window_modality_controller.cc18
-rw-r--r--ui/views/corewm/window_modality_controller.h3
14 files changed, 211 insertions, 58 deletions
diff --git a/ash/shell.cc b/ash/shell.cc
index 1651251..0d4461c 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -898,7 +898,8 @@ void Shell::OnEvent(ui::Event* event) {
void Shell::OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) {
- active_root_window_ = gained_active->GetRootWindow();
+ if (gained_active)
+ active_root_window_ = gained_active->GetRootWindow();
}
} // namespace ash
diff --git a/ash/shell.h b/ash/shell.h
index e8156e0..34de721 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -336,6 +336,9 @@ class ASH_EXPORT Shell
PartialMagnificationController* partial_magnification_controller() {
return partial_magnification_controller_.get();
}
+ aura::client::ActivationClient* activation_client() {
+ return activation_client_;
+ }
ScreenAsh* screen() { return screen_; }
@@ -403,10 +406,6 @@ class ASH_EXPORT Shell
browser_context_ = browser_context;
}
- aura::client::ActivationClient* activation_client() {
- return activation_client_;
- }
-
// Initializes the root window to be used for a secondary display.
void InitRootWindowForSecondaryDisplay(aura::RootWindow* root);
diff --git a/ash/wm/ash_focus_rules.cc b/ash/wm/ash_focus_rules.cc
index bb46e63..39459f6 100644
--- a/ash/wm/ash_focus_rules.cc
+++ b/ash/wm/ash_focus_rules.cc
@@ -104,30 +104,30 @@ aura::Window* AshFocusRules::GetNextActivatableWindow(
aura::Window* ignore) const {
DCHECK(ignore);
- size_t current_container_index = 0;
+ int starting_container_index = 0;
// If the container of the window losing focus is in the list, start from that
// container.
aura::RootWindow* root = ignore->GetRootWindow();
if (!root)
root = Shell::GetActiveRootWindow();
- for (size_t i = 0; ignore && i < arraysize(kWindowContainerIds); i++) {
+ int container_count = static_cast<int>(arraysize(kWindowContainerIds));
+ for (int i = 0; ignore && i < container_count; i++) {
aura::Window* container = Shell::GetContainer(root, kWindowContainerIds[i]);
if (container && container->Contains(ignore)) {
- current_container_index = i;
+ starting_container_index = i;
break;
}
}
- // Look for windows to focus in that container and below.
+ // Look for windows to focus in |ignore|'s container. If none are found, we
+ // look in all the containers in front of |ignore|'s container, then all
+ // behind.
aura::Window* window = NULL;
- for (; !window && current_container_index < arraysize(kWindowContainerIds);
- current_container_index++) {
- aura::Window::Windows containers =
- Shell::GetAllContainers(kWindowContainerIds[current_container_index]);
- for (aura::Window::Windows::const_iterator iter = containers.begin();
- iter != containers.end() && !window; ++iter) {
- window = GetTopmostWindowToActivateInContainer((*iter), ignore);
- }
+ for (int i = starting_container_index; !window && i < container_count; i++)
+ window = GetTopmostWindowToActivateForContainerIndex(i, ignore);
+ if (!window && starting_container_index > 0) {
+ for (int i = starting_container_index - 1; !window && i >= 0; i--)
+ window = GetTopmostWindowToActivateForContainerIndex(i, ignore);
}
return window;
}
@@ -135,6 +135,19 @@ aura::Window* AshFocusRules::GetNextActivatableWindow(
////////////////////////////////////////////////////////////////////////////////
// AshFocusRules, private:
+aura::Window* AshFocusRules::GetTopmostWindowToActivateForContainerIndex(
+ int index,
+ aura::Window* ignore) const {
+ aura::Window* window = NULL;
+ aura::Window::Windows containers =
+ Shell::GetAllContainers(kWindowContainerIds[index]);
+ for (aura::Window::Windows::const_iterator iter = containers.begin();
+ iter != containers.end() && !window; ++iter) {
+ window = GetTopmostWindowToActivateInContainer((*iter), ignore);
+ }
+ return window;
+}
+
aura::Window* AshFocusRules::GetTopmostWindowToActivateInContainer(
aura::Window* container,
aura::Window* ignore) const {
diff --git a/ash/wm/ash_focus_rules.h b/ash/wm/ash_focus_rules.h
index bb4cfca..f53a70b 100644
--- a/ash/wm/ash_focus_rules.h
+++ b/ash/wm/ash_focus_rules.h
@@ -27,6 +27,9 @@ class ASH_EXPORT AshFocusRules : public views::corewm::BaseFocusRules {
virtual aura::Window* GetNextActivatableWindow(
aura::Window* ignore) const OVERRIDE;
+ aura::Window* GetTopmostWindowToActivateForContainerIndex(
+ int index,
+ aura::Window* ignore) const;
aura::Window* GetTopmostWindowToActivateInContainer(
aura::Window* container,
aura::Window* ignore) const;
diff --git a/ash/wm/panel_layout_manager_unittest.cc b/ash/wm/panel_layout_manager_unittest.cc
index bbbbc92..a3e5e3c 100644
--- a/ash/wm/panel_layout_manager_unittest.cc
+++ b/ash/wm/panel_layout_manager_unittest.cc
@@ -18,6 +18,7 @@
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
+#include "ui/views/corewm/corewm_switches.h"
#include "ui/views/widget/widget.h"
namespace ash {
@@ -207,7 +208,10 @@ TEST_F(PanelLayoutManagerTest, MultiplePanelCallout) {
wm::ActivateWindow(w3.get());
EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w3.get()));
w3.reset();
- EXPECT_FALSE(IsCalloutVisible());
+ if (views::corewm::UseFocusController())
+ EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w2.get()));
+ else
+ EXPECT_FALSE(IsCalloutVisible());
}
// Tests removing panels.
diff --git a/ash/wm/toplevel_window_event_handler_unittest.cc b/ash/wm/toplevel_window_event_handler_unittest.cc
index f2457de..16703b3 100644
--- a/ash/wm/toplevel_window_event_handler_unittest.cc
+++ b/ash/wm/toplevel_window_event_handler_unittest.cc
@@ -5,6 +5,7 @@
#include "ash/wm/toplevel_window_event_handler.h"
#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/property_util.h"
#include "ash/wm/window_util.h"
@@ -68,34 +69,19 @@ class TestWindowDelegate : public aura::test::TestWindowDelegate {
class ToplevelWindowEventHandlerTest : public AshTestBase {
public:
- ToplevelWindowEventHandlerTest() : parent_(NULL) {}
+ ToplevelWindowEventHandlerTest() {}
virtual ~ToplevelWindowEventHandlerTest() {}
- virtual void SetUp() OVERRIDE {
- AshTestBase::SetUp();
- parent_ = new aura::Window(NULL);
- parent_->Init(ui::LAYER_NOT_DRAWN);
- parent_->Show();
- Shell::GetPrimaryRootWindow()->AddChild(parent_);
- parent_->SetBounds(Shell::GetPrimaryRootWindow()->bounds());
- handler_.reset(new ToplevelWindowEventHandler(parent_));
- parent_->AddPreTargetHandler(handler_.get());
- }
-
- virtual void TearDown() OVERRIDE {
- parent_->RemovePreTargetHandler(handler_.get());
- handler_.reset();
- parent_ = NULL;
- AshTestBase::TearDown();
- }
-
protected:
aura::Window* CreateWindow(int hittest_code) {
TestWindowDelegate* d1 = new TestWindowDelegate(hittest_code);
aura::Window* w1 = new aura::Window(d1);
w1->set_id(1);
w1->Init(ui::LAYER_TEXTURED);
- parent_->AddChild(w1);
+ aura::Window* parent =
+ Shell::GetContainer(Shell::GetPrimaryRootWindow(),
+ internal::kShellWindowId_AlwaysOnTopContainer);
+ parent->AddChild(w1);
w1->SetBounds(gfx::Rect(0, 0, 100, 100));
w1->Show();
return w1;
@@ -114,9 +100,6 @@ class ToplevelWindowEventHandlerTest : public AshTestBase {
scoped_ptr<ToplevelWindowEventHandler> handler_;
private:
- // Window |handler_| is installed on. Owned by RootWindow.
- aura::Window* parent_;
-
DISALLOW_COPY_AND_ASSIGN(ToplevelWindowEventHandlerTest);
};
@@ -513,12 +496,7 @@ TEST_F(ToplevelWindowEventHandlerTest, GestureDragToRestore) {
#define MAYBE_EscapeReverts EscapeReverts
#endif
TEST_F(ToplevelWindowEventHandlerTest, MAYBE_EscapeReverts) {
- aura::RootWindow* root = Shell::GetPrimaryRootWindow();
- aura::client::ActivationClient* original_client =
- aura::client::GetActivationClient(root);
- aura::test::TestActivationClient activation_client(root);
scoped_ptr<aura::Window> target(CreateWindow(HTBOTTOMRIGHT));
- target->Focus();
aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
target.get());
generator.PressLeftButton();
@@ -531,7 +509,6 @@ TEST_F(ToplevelWindowEventHandlerTest, MAYBE_EscapeReverts) {
generator.PressKey(ui::VKEY_ESCAPE, 0);
generator.ReleaseKey(ui::VKEY_ESCAPE, 0);
EXPECT_EQ("0,0 100x100", target->bounds().ToString());
- aura::client::SetActivationClient(root, original_client);
}
// Verifies window minimization/maximization completes drag.
diff --git a/ash/wm/window_manager_unittest.cc b/ash/wm/window_manager_unittest.cc
index dcfabfb..98568e4 100644
--- a/ash/wm/window_manager_unittest.cc
+++ b/ash/wm/window_manager_unittest.cc
@@ -25,6 +25,7 @@
#include "ui/base/hit_test.h"
#include "ui/gfx/screen.h"
#include "ui/views/corewm/compound_event_filter.h"
+#include "ui/views/corewm/corewm_switches.h"
#include "ui/views/corewm/input_method_event_filter.h"
namespace {
@@ -233,7 +234,7 @@ TEST_F(WindowManagerTest, ActivateOnMouse) {
d1.SetWindow(w1.get());
test::TestActivationDelegate d2;
scoped_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
- &wd, -1, gfx::Rect(70, 70, 50, 50)));
+ &wd, -2, gfx::Rect(70, 70, 50, 50)));
d2.SetWindow(w2.get());
aura::client::FocusClient* focus_client =
@@ -289,7 +290,8 @@ TEST_F(WindowManagerTest, ActivateOnMouse) {
d1.set_activate(true);
w2.reset();
EXPECT_EQ(0, d2.activated_count());
- EXPECT_EQ(0, d2.lost_active_count());
+ EXPECT_EQ(views::corewm::UseFocusController() ? 1 : 0,
+ d2.lost_active_count());
EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
EXPECT_EQ(w1.get(), focus_client->GetFocusedWindow());
EXPECT_EQ(1, d1.activated_count());
@@ -299,7 +301,7 @@ TEST_F(WindowManagerTest, ActivateOnMouse) {
// focus from the child.
{
scoped_ptr<aura::Window> w11(CreateTestWindowWithDelegate(
- &wd, -1, gfx::Rect(10, 10, 10, 10), w1.get()));
+ &wd, -11, gfx::Rect(10, 10, 10, 10), w1.get()));
aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
w11.get());
// First set the focus to the child |w11|.
@@ -404,7 +406,8 @@ TEST_F(WindowManagerTest, ActivateOnTouch) {
d1.set_activate(true);
w2.reset();
EXPECT_EQ(0, d2.activated_count());
- EXPECT_EQ(0, d2.lost_active_count());
+ EXPECT_EQ(views::corewm::UseFocusController() ? 1 : 0,
+ d2.lost_active_count());
EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
EXPECT_EQ(w1.get(), focus_client->GetFocusedWindow());
EXPECT_EQ(1, d1.activated_count());
diff --git a/ui/views/bubble/bubble_delegate.cc b/ui/views/bubble/bubble_delegate.cc
index 3e15b85..844598e 100644
--- a/ui/views/bubble/bubble_delegate.cc
+++ b/ui/views/bubble/bubble_delegate.cc
@@ -196,6 +196,7 @@ NonClientFrameView* BubbleDelegateView::CreateNonClientFrameView(
void BubbleDelegateView::OnWidgetClosing(Widget* widget) {
if (anchor_widget() == widget) {
+ anchor_widget_->RemoveObserver(this);
anchor_view_ = NULL;
anchor_widget_ = NULL;
}
diff --git a/ui/views/corewm/base_focus_rules.cc b/ui/views/corewm/base_focus_rules.cc
index d8b7ed1..2ef30f6 100644
--- a/ui/views/corewm/base_focus_rules.cc
+++ b/ui/views/corewm/base_focus_rules.cc
@@ -69,6 +69,12 @@ bool BaseFocusRules::CanActivateWindow(aura::Window* window) const {
return false;
}
+ // A window must be focusable to be activatable. We don't call
+ // CanFocusWindow() from here because it will call back to us via
+ // GetActivatableWindow().
+ if (!window->CanFocus())
+ return false;
+
// The window cannot be blocked by a modal transient.
return !GetModalTransient(window);
}
@@ -106,6 +112,14 @@ aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) const {
if (CanActivateWindow(child))
return child;
+ // CanActivateWindow() above will return false if |child| is blocked by a
+ // modal transient. In this case the modal is or contains the activatable
+ // window. We recurse because the modal may itself be blocked by a modal
+ // transient.
+ aura::Window* modal_transient = GetModalTransient(child);
+ if (modal_transient)
+ return GetActivatableWindow(modal_transient);
+
if (child->transient_parent())
return GetActivatableWindow(child->transient_parent());
@@ -122,8 +136,15 @@ aura::Window* BaseFocusRules::GetFocusableWindow(aura::Window* window) const {
// |window| may be in a hierarchy that is non-activatable, in which case we
// need to cut over to the activatable hierarchy.
aura::Window* activatable = GetActivatableWindow(window);
- if (!activatable)
- return GetFocusedWindow(window);
+ if (!activatable) {
+ // There may not be a related activatable hierarchy to cut over to, in which
+ // case we try an unrelated one.
+ aura::Window* toplevel = GetToplevelWindow(window);
+ if (toplevel)
+ activatable = GetNextActivatableWindow(toplevel);
+ if (!activatable)
+ return NULL;
+ }
if (!activatable->Contains(window)) {
// If there's already a child window focused in the activatable hierarchy,
diff --git a/ui/views/corewm/focus_controller.cc b/ui/views/corewm/focus_controller.cc
index c296385..cb6b278 100644
--- a/ui/views/corewm/focus_controller.cc
+++ b/ui/views/corewm/focus_controller.cc
@@ -38,6 +38,7 @@ void StackTransientParentsBelowModalWindow(aura::Window* window) {
FocusController::FocusController(FocusRules* rules)
: active_window_(NULL),
focused_window_(NULL),
+ updating_focus_(false),
rules_(rules),
ALLOW_THIS_IN_INITIALIZER_LIST(observer_manager_(this)) {
DCHECK(rules);
@@ -107,14 +108,29 @@ void FocusController::RemoveObserver(
void FocusController::FocusWindow(aura::Window* window,
const ui::Event* event) {
+ if (updating_focus_)
+ return;
+
+ if (window &&
+ (window->Contains(focused_window_) || window->Contains(active_window_))) {
+ return;
+ }
+
// Focusing a window also activates its containing activatable window. Note
// that the rules could redirect activation activation and/or focus.
aura::Window* focusable = rules_->GetFocusableWindow(window);
aura::Window* activatable =
focusable ? rules_->GetActivatableWindow(focusable) : NULL;
+
+ // We need valid focusable/activatable windows in the event we're not clearing
+ // focus. "Clearing focus" is inferred by whether or not |window| passed to
+ // this function is non-NULL.
+ if (window && (!focusable || !activatable))
+ return;
+ DCHECK((focusable && activatable) || !window);
SetActiveWindow(activatable);
- if (focusable && activatable)
- DCHECK(GetActiveWindow()->Contains(focusable));
+ if (active_window_)
+ DCHECK(active_window_->Contains(focusable));
SetFocusedWindow(focusable);
}
@@ -195,12 +211,13 @@ void FocusController::OnWindowInitialized(aura::Window* window) {
// FocusController, private:
void FocusController::SetFocusedWindow(aura::Window* window) {
- if (window == focused_window_)
+ if (updating_focus_ || window == focused_window_)
return;
DCHECK(rules_->CanFocusWindow(window));
if (window)
DCHECK_EQ(window, rules_->GetFocusableWindow(window));
+ base::AutoReset<bool> updating_focus(&updating_focus_, true);
aura::Window* lost_focus = focused_window_;
focused_window_ = window;
@@ -217,12 +234,14 @@ void FocusController::SetFocusedWindow(aura::Window* window) {
}
void FocusController::SetActiveWindow(aura::Window* window) {
- if (window == active_window_)
+ if (updating_focus_ || window == active_window_)
return;
+
DCHECK(rules_->CanActivateWindow(window));
if (window)
DCHECK_EQ(window, rules_->GetActivatableWindow(window));
+ base::AutoReset<bool> updating_focus(&updating_focus_, true);
aura::Window* lost_activation = active_window_;
active_window_ = window;
if (active_window_) {
@@ -244,12 +263,15 @@ void FocusController::SetActiveWindow(aura::Window* window) {
void FocusController::WindowLostFocusFromDispositionChange(
aura::Window* window) {
+ // A window's modality state will interfere with focus restoration during its
+ // destruction.
+ window->ClearProperty(aura::client::kModalKey);
// TODO(beng): See if this function can be replaced by a call to
// FocusWindow().
// Activation adjustments are handled first in the event of a disposition
// changed. If an activation change is necessary, focus is reset as part of
// that process so there's no point in updating focus independently.
- if (window->Contains(active_window_)) {
+ if (window == active_window_) {
aura::Window* next_activatable = rules_->GetNextActivatableWindow(window);
SetActiveWindow(next_activatable);
SetFocusedWindow(next_activatable);
diff --git a/ui/views/corewm/focus_controller.h b/ui/views/corewm/focus_controller.h
index df9b51e..a870c7c 100644
--- a/ui/views/corewm/focus_controller.h
+++ b/ui/views/corewm/focus_controller.h
@@ -106,6 +106,8 @@ class VIEWS_EXPORT FocusController : public aura::client::ActivationClient,
aura::Window* active_window_;
aura::Window* focused_window_;
+ bool updating_focus_;
+
scoped_ptr<FocusRules> rules_;
ObserverList<aura::client::ActivationChangeObserver> activation_observers_;
diff --git a/ui/views/corewm/focus_controller_unittest.cc b/ui/views/corewm/focus_controller_unittest.cc
index a74346d..01b1462 100644
--- a/ui/views/corewm/focus_controller_unittest.cc
+++ b/ui/views/corewm/focus_controller_unittest.cc
@@ -73,6 +73,55 @@ class ScopedFocusNotificationObserver : public FocusNotificationObserver {
DISALLOW_COPY_AND_ASSIGN(ScopedFocusNotificationObserver);
};
+// Focus/Activation change observer that attempts to shift focus/activation
+// while processing an update to focus/activation.
+class RecurseFocusObserver : public aura::client::ActivationChangeObserver,
+ public aura::client::FocusChangeObserver {
+ public:
+ explicit RecurseFocusObserver(aura::Window* other) : other_(other) {}
+ virtual ~RecurseFocusObserver() {}
+
+ private:
+ // Overridden from aura::client::ActivationChangeObserver:
+ virtual void OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) OVERRIDE {
+ DCHECK_NE(gained_active, other_);
+ aura::client::GetActivationClient(other_->GetRootWindow())->ActivateWindow(
+ other_);
+ }
+
+ // Overridden from aura::client::FocusChangeObserver:
+ virtual void OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) OVERRIDE {
+ DCHECK_NE(gained_focus, other_);
+ aura::client::GetFocusClient(other_)->FocusWindow(other_, NULL);
+ }
+
+ aura::Window* other_;
+
+ DISALLOW_COPY_AND_ASSIGN(RecurseFocusObserver);
+};
+
+class ScopedRecurseFocusObserver : public RecurseFocusObserver {
+ public:
+ ScopedRecurseFocusObserver(aura::RootWindow* root_window,
+ aura::Window* other)
+ : RecurseFocusObserver(other),
+ root_window_(root_window) {
+ aura::client::GetActivationClient(root_window_)->AddObserver(this);
+ aura::client::GetFocusClient(root_window_)->AddObserver(this);
+ }
+ virtual ~ScopedRecurseFocusObserver() {
+ aura::client::GetActivationClient(root_window_)->RemoveObserver(this);
+ aura::client::GetFocusClient(root_window_)->RemoveObserver(this);
+ }
+
+ private:
+ aura::RootWindow* root_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedRecurseFocusObserver);
+};
+
class ScopedTargetFocusNotificationObserver : public FocusNotificationObserver {
public:
ScopedTargetFocusNotificationObserver(aura::RootWindow* root_window, int id)
@@ -248,8 +297,11 @@ class FocusControllerTestBase : public aura::test::AuraTestBase {
virtual void DuplicateActivationEvents() {}
virtual void ShiftFocusWithinActiveWindow() {}
virtual void ShiftFocusToChildOfInactiveWindow() {}
+ virtual void ShiftFocusToParentOfFocusedWindow() {}
virtual void FocusRulesOverride() = 0;
virtual void ActivationRulesOverride() = 0;
+ virtual void NoRecurseFocus() {}
+ virtual void NoRecurseActivation() {}
private:
scoped_ptr<FocusController> focus_controller_;
@@ -390,6 +442,15 @@ class FocusControllerDirectTestBase : public FocusControllerTestBase {
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);
@@ -421,6 +482,22 @@ class FocusControllerDirectTestBase : public FocusControllerTestBase {
EXPECT_EQ(2, GetActiveWindowId());
EXPECT_EQ(2, GetFocusedWindowId());
}
+ virtual void NoRecurseFocus() OVERRIDE {
+ aura::Window* w2 = root_window()->GetChildById(2);
+ ScopedRecurseFocusObserver observer(root_window(), w2);
+ FocusWindowById(1);
+ // |observer| will try to set active to w2, but the focus system should
+ // prevent recursive updating.
+ EXPECT_EQ(1, GetFocusedWindowId());
+ }
+ virtual void NoRecurseActivation() OVERRIDE {
+ aura::Window* w2 = root_window()->GetChildById(2);
+ ScopedRecurseFocusObserver observer(root_window(), w2);
+ ActivateWindowById(1);
+ // |observer| will try to set active to w2, but the focus system should
+ // prevent recursive updating.
+ EXPECT_EQ(1, GetActiveWindowId());
+ }
private:
DISALLOW_COPY_AND_ASSIGN(FocusControllerDirectTestBase);
@@ -792,11 +869,20 @@ DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusWithinActiveWindow);
// 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(NoRecurseFocus);
+DIRECT_FOCUS_CHANGE_TESTS(NoRecurseActivation);
+
} // namespace corewm
} // namespace views
diff --git a/ui/views/corewm/window_modality_controller.cc b/ui/views/corewm/window_modality_controller.cc
index 57bbe3c..907c823 100644
--- a/ui/views/corewm/window_modality_controller.cc
+++ b/ui/views/corewm/window_modality_controller.cc
@@ -120,11 +120,29 @@ ui::EventResult WindowModalityController::OnTouchEvent(ui::TouchEvent* event) {
ui::ER_UNHANDLED;
}
+////////////////////////////////////////////////////////////////////////////////
+// WindowModalityController, aura::EnvObserver implementation:
+
void WindowModalityController::OnWindowInitialized(aura::Window* window) {
windows_.push_back(window);
window->AddObserver(this);
}
+////////////////////////////////////////////////////////////////////////////////
+// WindowModalityController, aura::WindowObserver implementation:
+
+void WindowModalityController::OnWindowPropertyChanged(aura::Window* window,
+ const void* key,
+ intptr_t old) {
+ // In tests, we sometimes create the modality relationship after a window is
+ // visible.
+ if (key == aura::client::kModalKey &&
+ window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE &&
+ window->IsVisible()) {
+ ActivateWindow(window);
+ }
+}
+
void WindowModalityController::OnWindowVisibilityChanged(
aura::Window* window,
bool visible) {
diff --git a/ui/views/corewm/window_modality_controller.h b/ui/views/corewm/window_modality_controller.h
index 9384197..449a894 100644
--- a/ui/views/corewm/window_modality_controller.h
+++ b/ui/views/corewm/window_modality_controller.h
@@ -46,6 +46,9 @@ class VIEWS_EXPORT WindowModalityController : public ui::EventHandler,
virtual void OnWindowInitialized(aura::Window* window) OVERRIDE;
// Overridden from aura::WindowObserver:
+ virtual void OnWindowPropertyChanged(aura::Window* window,
+ const void* key,
+ intptr_t old) OVERRIDE;
virtual void OnWindowVisibilityChanged(aura::Window* window,
bool visible) OVERRIDE;
virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;