summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-07 19:52:52 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-07 19:52:52 +0000
commit751b13df3b768295e4b215aca45b9f1ea8bd373c (patch)
tree50660265c2fe6a4e81d834117c2e1f5a03d50a0e /ui
parent691b2001018e28625ceeb0d4710472ff66ef9857 (diff)
downloadchromium_src-751b13df3b768295e4b215aca45b9f1ea8bd373c.zip
chromium_src-751b13df3b768295e4b215aca45b9f1ea8bd373c.tar.gz
chromium_src-751b13df3b768295e4b215aca45b9f1ea8bd373c.tar.bz2
Moves management of transients out of Window
And into TransientWindowManager. Additionally moves handling of NULL layer stacking into TransientStackingWindowClient. The tests that were exercising these code paths have all been moved to ui/views/corewm. Also wires up TransientStackingWindowClient in a couple of places that didn't have it. The order of removing from transient parent as well as destroying of transient children is slightly differently than before. To get the order exactly as it was would require some new specific observer functions. I'm hoping we don't need those. Hopefully this doesn't cause issues, if it does I'll revisit. I ended up exposing convenience functions. That's because typing something like: views::corewm::TransientWindowManager::Get(window)->AddTransientChild() was too much for me. There is also some subtlety in so far as the Get() function that takes a const Window* may return NULL, where as non-const never returns NULL. Lastly I had to make Window friend TransientWindowManager. This is temporary until I create a specific TransientWindowManagerObserver that contains the two transient related observer functions in WindowObserver. BUG=none TEST=none R=ben@chromium.org Review URL: https://codereview.chromium.org/115453004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@243368 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/aura/aura.gyp2
-rw-r--r--ui/aura/client/transient_window_client.cc25
-rw-r--r--ui/aura/client/transient_window_client.h47
-rw-r--r--ui/aura/test/aura_test_base.cc10
-rw-r--r--ui/aura/test/aura_test_base.h3
-rw-r--r--ui/aura/window.cc110
-rw-r--r--ui/aura/window.h47
-rw-r--r--ui/aura/window_unittest.cc374
-rw-r--r--ui/oak/oak_aura_window_display.cc8
-rw-r--r--ui/views/corewm/base_focus_rules.cc7
-rw-r--r--ui/views/corewm/focus_controller.cc7
-rw-r--r--ui/views/corewm/shadow_controller.cc7
-rw-r--r--ui/views/corewm/shadow_controller_unittest.cc3
-rw-r--r--ui/views/corewm/transient_window_controller.cc42
-rw-r--r--ui/views/corewm/transient_window_controller.h38
-rw-r--r--ui/views/corewm/transient_window_manager.cc147
-rw-r--r--ui/views/corewm/transient_window_manager.h96
-rw-r--r--ui/views/corewm/transient_window_manager_unittest.cc577
-rw-r--r--ui/views/corewm/transient_window_stacking_client.cc56
-rw-r--r--ui/views/corewm/transient_window_stacking_client.h7
-rw-r--r--ui/views/corewm/transient_window_stacking_client_unittest.cc64
-rw-r--r--ui/views/corewm/window_modality_controller.cc16
-rw-r--r--ui/views/corewm/window_util.cc38
-rw-r--r--ui/views/corewm/window_util.h18
-rw-r--r--ui/views/corewm/wm_state.cc10
-rw-r--r--ui/views/corewm/wm_state.h2
-rw-r--r--ui/views/views.gyp5
-rw-r--r--ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc10
-rw-r--r--ui/views/widget/native_widget_aura.cc11
-rw-r--r--ui/wm/core/easy_resize_window_targeter.cc20
-rw-r--r--ui/wm/public/easy_resize_window_targeter.h4
31 files changed, 1223 insertions, 588 deletions
diff --git a/ui/aura/aura.gyp b/ui/aura/aura.gyp
index c10a3ed..ded1112 100644
--- a/ui/aura/aura.gyp
+++ b/ui/aura/aura.gyp
@@ -67,6 +67,8 @@
'client/screen_position_client.h',
'client/tooltip_client.cc',
'client/tooltip_client.h',
+ 'client/transient_window_client.cc',
+ 'client/transient_window_client.h',
'client/user_action_client.cc',
'client/user_action_client.h',
'client/visibility_client.cc',
diff --git a/ui/aura/client/transient_window_client.cc b/ui/aura/client/transient_window_client.cc
new file mode 100644
index 0000000..47b50b9
--- /dev/null
+++ b/ui/aura/client/transient_window_client.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 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/aura/client/transient_window_client.h"
+
+namespace aura {
+namespace client {
+
+namespace {
+
+TransientWindowClient* instance = NULL;
+
+} // namespace
+
+void SetTransientWindowClient(TransientWindowClient* client) {
+ instance = client;
+}
+
+TransientWindowClient* GetTransientWindowClient() {
+ return instance;
+}
+
+} // namespace client
+} // namespace aura
diff --git a/ui/aura/client/transient_window_client.h b/ui/aura/client/transient_window_client.h
new file mode 100644
index 0000000..96434d7
--- /dev/null
+++ b/ui/aura/client/transient_window_client.h
@@ -0,0 +1,47 @@
+// Copyright 2014 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.
+
+#ifndef UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_H_
+#define UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_H_
+
+#include "base/basictypes.h"
+#include "ui/aura/aura_export.h"
+
+namespace aura {
+
+class Window;
+
+namespace client {
+
+// TransientWindowClient is used to add or remove transient windows. Transient
+// children get the following behavior:
+// . The transient parent destroys any transient children when it is
+// destroyed. This means a transient child is destroyed if either its parent
+// or transient parent is destroyed.
+// . If a transient child and its transient parent share the same parent, then
+// transient children are always ordered above the transient parent.
+// Transient windows are typically used for popups and menus.
+// TODO(sky): nuke this class and replace with calls to TransientWindowManager.
+// This is temporary until we start moving to ui/wm.
+class AURA_EXPORT TransientWindowClient {
+ public:
+ virtual void AddTransientChild(Window* parent, Window* child) = 0;
+ virtual void RemoveTransientChild(Window* parent, Window* child) = 0;
+ virtual Window* GetTransientParent(Window* window) = 0;
+ virtual const Window* GetTransientParent(const Window* window) = 0;
+
+ protected:
+ virtual ~TransientWindowClient() {}
+};
+
+// Sets/gets the TransientWindowClient. This does *not* take ownership of
+// |client|. It is assumed the caller will invoke SetTransientWindowClient(NULL)
+// before deleting |client|.
+AURA_EXPORT void SetTransientWindowClient(TransientWindowClient* client);
+AURA_EXPORT TransientWindowClient* GetTransientWindowClient();
+
+} // namespace client
+} // namespace aura
+
+#endif // UI_AURA_CLIENT_TRANSIENT_WINDOW_CLIENT_H_
diff --git a/ui/aura/test/aura_test_base.cc b/ui/aura/test/aura_test_base.cc
index 4522c98..3b2d1b0 100644
--- a/ui/aura/test/aura_test_base.cc
+++ b/ui/aura/test/aura_test_base.cc
@@ -97,16 +97,6 @@ Window* AuraTestBase::CreateNormalWindow(int id, Window* parent,
return window;
}
-Window* AuraTestBase::CreateTransientChild(int id, Window* parent) {
- Window* window = new Window(NULL);
- window->set_id(id);
- window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
- window->Init(ui::LAYER_TEXTURED);
- aura::client::ParentWindowWithContext(window, root_window(), gfx::Rect());
- parent->AddTransientChild(window);
- return window;
-}
-
void AuraTestBase::RunAllPendingInMessageLoop() {
helper_->RunAllPendingInMessageLoop();
}
diff --git a/ui/aura/test/aura_test_base.h b/ui/aura/test/aura_test_base.h
index 52cccf0..971fdba 100644
--- a/ui/aura/test/aura_test_base.h
+++ b/ui/aura/test/aura_test_base.h
@@ -32,9 +32,6 @@ class AuraTestBase : public testing::Test {
aura::Window* CreateNormalWindow(int id, Window* parent,
aura::WindowDelegate* delegate);
- // Creates a transient window that is transient to |parent|.
- aura::Window* CreateTransientChild(int id, aura::Window* parent);
-
protected:
void RunAllPendingInMessageLoop();
diff --git a/ui/aura/window.cc b/ui/aura/window.cc
index 404ad15..1936089 100644
--- a/ui/aura/window.cc
+++ b/ui/aura/window.cc
@@ -10,7 +10,6 @@
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/logging.h"
-#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
@@ -218,7 +217,6 @@ Window::Window(WindowDelegate* delegate)
owned_by_parent_(true),
delegate_(delegate),
parent_(NULL),
- transient_parent_(NULL),
visible_(false),
id_(-1),
transparent_(false),
@@ -249,24 +247,11 @@ Window::~Window() {
// Then destroy the children.
RemoveOrDestroyChildren();
- // Removes ourselves from our transient parent (if it hasn't been done by the
- // RootWindow).
- if (transient_parent_)
- transient_parent_->RemoveTransientChild(this);
-
// The window needs to be removed from the parent before calling the
// WindowDestroyed callbacks of delegate and the observers.
if (parent_)
parent_->RemoveChild(this);
- // Destroy transient children, only after we've removed ourselves from our
- // parent, as destroying an active transient child may otherwise attempt to
- // refocus us.
- Windows transient_children(transient_children_);
- STLDeleteElements(&transient_children);
- DCHECK(transient_children_.empty());
-
- // Delegate and observers need to be notified after transients are deleted.
if (delegate_)
delegate_->OnWindowDestroyed();
ObserverListBase<WindowObserver>::Iterator iter(observers_);
@@ -391,10 +376,6 @@ void Window::Show() {
}
void Window::Hide() {
- for (Windows::iterator it = transient_children_.begin();
- it != transient_children_.end(); ++it) {
- (*it)->Hide();
- }
SetVisible(false);
ReleaseCapture();
}
@@ -605,28 +586,6 @@ bool Window::Contains(const Window* other) const {
return false;
}
-void Window::AddTransientChild(Window* child) {
- if (child->transient_parent_)
- child->transient_parent_->RemoveTransientChild(child);
- DCHECK(std::find(transient_children_.begin(), transient_children_.end(),
- child) == transient_children_.end());
- transient_children_.push_back(child);
- child->transient_parent_ = this;
- FOR_EACH_OBSERVER(WindowObserver, observers_,
- OnAddTransientChild(this, child));
-}
-
-void Window::RemoveTransientChild(Window* child) {
- Windows::iterator i =
- std::find(transient_children_.begin(), transient_children_.end(), child);
- DCHECK(i != transient_children_.end());
- transient_children_.erase(i);
- if (child->transient_parent_ == this)
- child->transient_parent_ = NULL;
- FOR_EACH_OBSERVER(WindowObserver, observers_,
- OnRemoveTransientChild(this, child));
-}
-
Window* Window::GetChildById(int id) {
return const_cast<Window*>(const_cast<const Window*>(this)->GetChildById(id));
}
@@ -1160,34 +1119,6 @@ void Window::OnParentChanged() {
WindowObserver, observers_, OnWindowParentChanged(this, parent_));
}
-bool Window::HasTransientAncestor(const Window* ancestor) const {
- if (transient_parent_ == ancestor)
- return true;
- return transient_parent_ ?
- transient_parent_->HasTransientAncestor(ancestor) : false;
-}
-
-void Window::SkipNullDelegatesForStacking(StackDirection direction,
- Window** target) const {
- DCHECK_EQ(this, (*target)->parent());
- size_t target_i =
- std::find(children_.begin(), children_.end(), *target) -
- children_.begin();
-
- // By convention we don't stack on top of windows with layers with NULL
- // delegates. Walk backward to find a valid target window.
- // See tests WindowTest.StackingMadrigal and StackOverClosingTransient
- // for an explanation of this.
- while (target_i > 0) {
- const size_t index = direction == STACK_ABOVE ? target_i : target_i - 1;
- if (!children_[index]->layer_ ||
- children_[index]->layer_->delegate() != NULL)
- break;
- --target_i;
- }
- *target = children_[target_i];
-}
-
void Window::StackChildRelativeTo(Window* child,
Window* target,
StackDirection direction) {
@@ -1199,47 +1130,10 @@ void Window::StackChildRelativeTo(Window* child,
client::WindowStackingClient* stacking_client =
client::GetWindowStackingClient();
- if (stacking_client)
- stacking_client->AdjustStacking(&child, &target, &direction);
-
- SkipNullDelegatesForStacking(direction, &target);
-
- // If we couldn't find a valid target position, don't move anything.
- if (direction == STACK_ABOVE &&
- (target->layer_ && target->layer_->delegate() == NULL))
- return;
-
- // Don't try to stack a child above itself.
- if (child == target)
+ if (stacking_client &&
+ !stacking_client->AdjustStacking(&child, &target, &direction))
return;
- // Move the child.
- StackChildRelativeToImpl(child, target, direction);
-
- // Stack any transient children that share the same parent to be in front of
- // 'child'. Preserve the existing stacking order by iterating in the order
- // those children appear in children_ array.
- Window* last_transient = child;
- Windows children(children_);
- for (Windows::iterator it = children.begin(); it != children.end(); ++it) {
- Window* transient_child = *it;
- if (transient_child != last_transient &&
- transient_child->HasTransientAncestor(child)) {
- StackChildRelativeToImpl(transient_child, last_transient, STACK_ABOVE);
- last_transient = transient_child;
- }
- }
-}
-
-void Window::StackChildRelativeToImpl(Window* child,
- Window* target,
- StackDirection direction) {
- DCHECK_NE(child, target);
- DCHECK(child);
- DCHECK(target);
- DCHECK_EQ(this, child->parent());
- DCHECK_EQ(this, target->parent());
-
const size_t child_i =
std::find(children_.begin(), children_.end(), child) - children_.begin();
const size_t target_i =
diff --git a/ui/aura/window.h b/ui/aura/window.h
index 6b65be4..6f71f09 100644
--- a/ui/aura/window.h
+++ b/ui/aura/window.h
@@ -42,6 +42,12 @@ class Layer;
class Texture;
}
+// TODO(sky): nuke. Temporary while moving transients out of Window.
+namespace views {
+namespace corewm {
+class TransientWindowManager;
+}
+}
namespace aura {
class LayoutManager;
@@ -211,22 +217,6 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// Returns true if this Window contains |other| somewhere in its children.
bool Contains(const Window* other) const;
- // Adds or removes |child| as a transient child of this window. Transient
- // children get the following behavior:
- // . The transient parent destroys any transient children when it is
- // destroyed. This means a transient child is destroyed if either its parent
- // or transient parent is destroyed.
- // . If a transient child and its transient parent share the same parent, then
- // transient children are always ordered above the transient parent.
- // Transient windows are typically used for popups and menus.
- void AddTransientChild(Window* child);
- void RemoveTransientChild(Window* child);
-
- const Windows& transient_children() const { return transient_children_; }
-
- Window* transient_parent() { return transient_parent_; }
- const Window* transient_parent() const { return transient_parent_; }
-
// Retrieves the first-level child with the specified id, or NULL if no first-
// level child is found matching |id|.
Window* GetChildById(int id);
@@ -383,6 +373,8 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
private:
friend class test::WindowTestApi;
+ // TODO(sky): temporary until TransientWindowManager gets its own observer.
+ friend class views::corewm::TransientWindowManager;
friend class LayoutManager;
friend class RootWindow;
friend class WindowTargeter;
@@ -445,26 +437,12 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// Called when this window's parent has changed.
void OnParentChanged();
- // Returns true when |ancestor| is a transient ancestor of |this|.
- bool HasTransientAncestor(const Window* ancestor) const;
-
- // Adjusts |target| so that we don't attempt to stack on top of a window with
- // a NULL delegate. See implementation for details.
- void SkipNullDelegatesForStacking(StackDirection direction,
- Window** target) const;
-
- // Determines the real location for stacking |child| and invokes
- // StackChildRelativeToImpl().
+ // The various stacking functions call into this to do the actual stacking.
void StackChildRelativeTo(Window* child,
Window* target,
StackDirection direction);
- // Implementation of StackChildRelativeTo().
- void StackChildRelativeToImpl(Window* child,
- Window* target,
- StackDirection direction);
-
- // Invoked from StackChildRelativeToImpl() to stack the layers appropriately
+ // Invoked from StackChildRelativeTo() to stack the layers appropriately
// when stacking |child| relative to |target|.
void StackChildLayerRelativeTo(Window* child,
Window* target,
@@ -562,11 +540,6 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// Child windows. Topmost is last.
Windows children_;
- // Transient windows.
- Windows transient_children_;
-
- Window* transient_parent_;
-
// The visibility state of the window as set by Show()/Hide(). This may differ
// from the visibility of the underlying layer, which may remain visible after
// the window is hidden (e.g. to animate its disappearance).
diff --git a/ui/aura/window_unittest.cc b/ui/aura/window_unittest.cc
index ce1021e..6117489 100644
--- a/ui/aura/window_unittest.cc
+++ b/ui/aura/window_unittest.cc
@@ -18,7 +18,6 @@
#include "ui/aura/client/focus_change_observer.h"
#include "ui/aura/client/visibility_client.h"
#include "ui/aura/client/window_tree_client.h"
-#include "ui/aura/layout_manager.h"
#include "ui/aura/root_window.h"
#include "ui/aura/root_window_observer.h"
#include "ui/aura/test/aura_test_base.h"
@@ -1583,41 +1582,6 @@ TEST_F(WindowTest, TransformGesture) {
EXPECT_EQ(gfx::Point(10, 10).ToString(), delegate->position().ToString());
}
-// Various assertions for transient children.
-TEST_F(WindowTest, TransientChildren) {
- scoped_ptr<Window> parent(CreateTestWindowWithId(0, root_window()));
- scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
- scoped_ptr<Window> w3(CreateTestWindowWithId(3, parent.get()));
- Window* w2 = CreateTestWindowWithId(2, parent.get());
- w1->AddTransientChild(w2); // w2 is now owned by w1.
- // Stack w1 at the top (end), this should force w2 to be last (on top of w1).
- parent->StackChildAtTop(w1.get());
- ASSERT_EQ(3u, parent->children().size());
- EXPECT_EQ(w2, parent->children().back());
-
- // Destroy w1, which should also destroy w3 (since it's a transient child).
- w1.reset();
- w2 = NULL;
- ASSERT_EQ(1u, parent->children().size());
- EXPECT_EQ(w3.get(), parent->children()[0]);
-
- w1.reset(CreateTestWindowWithId(4, parent.get()));
- w2 = CreateTestWindowWithId(5, w3.get());
- w1->AddTransientChild(w2);
- parent->StackChildAtTop(w3.get());
- // Stack w1 at the top (end), this shouldn't affect w2 since it has a
- // different parent.
- parent->StackChildAtTop(w1.get());
- ASSERT_EQ(2u, parent->children().size());
- EXPECT_EQ(w3.get(), parent->children()[0]);
- EXPECT_EQ(w1.get(), parent->children()[1]);
-
- // Hiding parent should hide transient children.
- EXPECT_TRUE(w2->IsVisible());
- w1->Hide();
- EXPECT_FALSE(w2->IsVisible());
-}
-
namespace {
DEFINE_WINDOW_PROPERTY_KEY(int, kIntKey, -2);
DEFINE_WINDOW_PROPERTY_KEY(const char*, kStringKey, "squeamish");
@@ -2056,66 +2020,6 @@ TEST_F(WindowTest, StackWindowAtBottomBelowWindowWhoseLayerHasNoDelegate) {
ui::test::ChildLayerNamesAsString(*root_window()->layer()));
}
-TEST_F(WindowTest, StackWindowsWhoseLayersHaveNoDelegate) {
- scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
- window1->layer()->set_name("1");
- scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window()));
- window2->layer()->set_name("2");
- scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window()));
- window3->layer()->set_name("3");
-
- // This brings |window1| (and its layer) to the front.
- root_window()->StackChildAbove(window1.get(), window3.get());
- EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
- EXPECT_EQ("2 3 1",
- ui::test::ChildLayerNamesAsString(*root_window()->layer()));
-
- // Since |window1| does not have a delegate, |window2| should not move in
- // front of it, nor should its layer.
- window1->layer()->set_delegate(NULL);
- root_window()->StackChildAbove(window2.get(), window1.get());
- EXPECT_EQ("3 2 1", ChildWindowIDsAsString(root_window()));
- EXPECT_EQ("3 2 1",
- ui::test::ChildLayerNamesAsString(*root_window()->layer()));
-
- // It should still be possible to stack |window3| immediately below |window1|.
- root_window()->StackChildBelow(window3.get(), window1.get());
- EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
- EXPECT_EQ("2 3 1",
- ui::test::ChildLayerNamesAsString(*root_window()->layer()));
-
- // Since neither |window3| nor |window1| have a delegate, |window2| should
- // not move in front of either.
- window3->layer()->set_delegate(NULL);
- root_window()->StackChildBelow(window2.get(), window1.get());
- EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
- EXPECT_EQ("2 3 1",
- ui::test::ChildLayerNamesAsString(*root_window()->layer()));
-}
-
-TEST_F(WindowTest, StackTransientsWhoseLayersHaveNoDelegate) {
- // Create a window with several transients, then a couple windows on top.
- scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
- scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
- scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
- scoped_ptr<Window> window13(CreateTransientChild(13, window1.get()));
- scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window()));
- scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window()));
-
- EXPECT_EQ("1 11 12 13 2 3", ChildWindowIDsAsString(root_window()));
-
- // Remove the delegates of a couple of transients, as if they are closing
- // and animating out.
- window11->layer()->set_delegate(NULL);
- window13->layer()->set_delegate(NULL);
-
- // Move window1 to the front. All transients should move with it, and their
- // order should be preserved.
- root_window()->StackChildAtTop(window1.get());
-
- EXPECT_EQ("2 3 1 11 12 13", ChildWindowIDsAsString(root_window()));
-}
-
class TestVisibilityClient : public client::VisibilityClient {
public:
explicit TestVisibilityClient(Window* root_window)
@@ -2253,240 +2157,6 @@ TEST_F(WindowTest, MouseEventsOnWindowChange) {
EXPECT_EQ("0 0 0", d11.GetMouseMotionCountsAndReset());
}
-class StackingMadrigalLayoutManager : public LayoutManager {
- public:
- explicit StackingMadrigalLayoutManager(Window* root_window)
- : root_window_(root_window) {
- root_window_->SetLayoutManager(this);
- }
- virtual ~StackingMadrigalLayoutManager() {
- }
-
- private:
- // Overridden from LayoutManager:
- virtual void OnWindowResized() OVERRIDE {}
- virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {}
- virtual void OnWillRemoveWindowFromLayout(Window* child) OVERRIDE {}
- virtual void OnWindowRemovedFromLayout(Window* child) OVERRIDE {}
- virtual void OnChildWindowVisibilityChanged(Window* child,
- bool visible) OVERRIDE {
- Window::Windows::const_iterator it = root_window_->children().begin();
- Window* last_window = NULL;
- for (; it != root_window_->children().end(); ++it) {
- if (*it == child && last_window) {
- if (!visible)
- root_window_->StackChildAbove(last_window, *it);
- else
- root_window_->StackChildAbove(*it, last_window);
- break;
- }
- last_window = *it;
- }
- }
- virtual void SetChildBounds(Window* child,
- const gfx::Rect& requested_bounds) OVERRIDE {
- SetChildBoundsDirect(child, requested_bounds);
- }
-
- Window* root_window_;
-
- DISALLOW_COPY_AND_ASSIGN(StackingMadrigalLayoutManager);
-};
-
-class StackingMadrigalVisibilityClient : public client::VisibilityClient {
- public:
- explicit StackingMadrigalVisibilityClient(Window* root_window)
- : ignored_window_(NULL) {
- client::SetVisibilityClient(root_window, this);
- }
- virtual ~StackingMadrigalVisibilityClient() {
- }
-
- void set_ignored_window(Window* ignored_window) {
- ignored_window_ = ignored_window;
- }
-
- private:
- // Overridden from client::VisibilityClient:
- virtual void UpdateLayerVisibility(Window* window, bool visible) OVERRIDE {
- if (!visible) {
- if (window == ignored_window_)
- window->layer()->set_delegate(NULL);
- else
- window->layer()->SetVisible(visible);
- } else {
- window->layer()->SetVisible(visible);
- }
- }
-
- Window* ignored_window_;
-
- DISALLOW_COPY_AND_ASSIGN(StackingMadrigalVisibilityClient);
-};
-
-// This test attempts to reconstruct a circumstance that can happen when the
-// aura client attempts to manipulate the visibility and delegate of a layer
-// independent of window visibility.
-// A use case is where the client attempts to keep a window visible onscreen
-// even after code has called Hide() on the window. The use case for this would
-// be that window hides are animated (e.g. the window fades out). To prevent
-// spurious updating the client code may also clear window's layer's delegate,
-// so that the window cannot attempt to paint or update it further. The window
-// uses the presence of a NULL layer delegate as a signal in stacking to note
-// that the window is being manipulated by such a use case and its stacking
-// should not be adjusted.
-// One issue that can arise when a window opens two transient children, and the
-// first is hidden. Subsequent attempts to activate the transient parent can
-// result in the transient parent being stacked above the second transient
-// child. A fix is made to Window::StackAbove to prevent this, and this test
-// verifies this fix.
-TEST_F(WindowTest, StackingMadrigal) {
- new StackingMadrigalLayoutManager(root_window());
- StackingMadrigalVisibilityClient visibility_client(root_window());
-
- scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
- scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
-
- visibility_client.set_ignored_window(window11.get());
-
- window11->Show();
- window11->Hide();
-
- // As a transient, window11 should still be stacked above window1, even when
- // hidden.
- EXPECT_TRUE(WindowIsAbove(window11.get(), window1.get()));
- EXPECT_TRUE(LayerIsAbove(window11.get(), window1.get()));
-
- // A new transient should still be above window1. It will appear behind
- // window11 because we don't stack windows on top of targets with NULL
- // delegates.
- scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
- window12->Show();
-
- EXPECT_TRUE(WindowIsAbove(window12.get(), window1.get()));
- EXPECT_TRUE(LayerIsAbove(window12.get(), window1.get()));
-
- // In earlier versions of the StackChildAbove() method, attempting to stack
- // window1 above window12 at this point would actually restack the layers
- // resulting in window12's layer being below window1's layer (though the
- // windows themselves would still be correctly stacked, so events would pass
- // through.)
- root_window()->StackChildAbove(window1.get(), window12.get());
-
- // Both window12 and its layer should be stacked above window1.
- EXPECT_TRUE(WindowIsAbove(window12.get(), window1.get()));
- EXPECT_TRUE(LayerIsAbove(window12.get(), window1.get()));
-}
-
-// Test for an issue where attempting to stack a primary window on top of a
-// transient with a NULL layer delegate causes that primary window to be moved,
-// but the layer order not changed to match. http://crbug.com/112562
-TEST_F(WindowTest, StackOverClosingTransient) {
- scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
- scoped_ptr<Window> transient1(CreateTransientChild(11, window1.get()));
- scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window()));
- scoped_ptr<Window> transient2(CreateTransientChild(21, window2.get()));
-
- // Both windows and layers are stacked in creation order.
- Window* root = root_window();
- ASSERT_EQ(4u, root->children().size());
- EXPECT_EQ(root->children()[0], window1.get());
- EXPECT_EQ(root->children()[1], transient1.get());
- EXPECT_EQ(root->children()[2], window2.get());
- EXPECT_EQ(root->children()[3], transient2.get());
- ASSERT_EQ(4u, root->layer()->children().size());
- EXPECT_EQ(root->layer()->children()[0], window1->layer());
- EXPECT_EQ(root->layer()->children()[1], transient1->layer());
- EXPECT_EQ(root->layer()->children()[2], window2->layer());
- EXPECT_EQ(root->layer()->children()[3], transient2->layer());
- EXPECT_EQ("1 11 2 21", ChildWindowIDsAsString(root_window()));
-
- // This brings window1 and its transient to the front.
- root->StackChildAtTop(window1.get());
- EXPECT_EQ("2 21 1 11", ChildWindowIDsAsString(root_window()));
-
- EXPECT_EQ(root->children()[0], window2.get());
- EXPECT_EQ(root->children()[1], transient2.get());
- EXPECT_EQ(root->children()[2], window1.get());
- EXPECT_EQ(root->children()[3], transient1.get());
- EXPECT_EQ(root->layer()->children()[0], window2->layer());
- EXPECT_EQ(root->layer()->children()[1], transient2->layer());
- EXPECT_EQ(root->layer()->children()[2], window1->layer());
- EXPECT_EQ(root->layer()->children()[3], transient1->layer());
-
- // Pretend we're closing the top-most transient, then bring window2 to the
- // front. This mimics activating a browser window while the status bubble
- // is fading out. The transient should stay topmost.
- transient1->layer()->set_delegate(NULL);
- root->StackChildAtTop(window2.get());
-
- EXPECT_EQ(root->children()[0], window1.get());
- EXPECT_EQ(root->children()[1], window2.get());
- EXPECT_EQ(root->children()[2], transient2.get());
- EXPECT_EQ(root->children()[3], transient1.get());
- EXPECT_EQ(root->layer()->children()[0], window1->layer());
- EXPECT_EQ(root->layer()->children()[1], window2->layer());
- EXPECT_EQ(root->layer()->children()[2], transient2->layer());
- EXPECT_EQ(root->layer()->children()[3], transient1->layer());
-
- // Close the transient. Remaining windows are stable.
- transient1.reset();
-
- ASSERT_EQ(3u, root->children().size());
- EXPECT_EQ(root->children()[0], window1.get());
- EXPECT_EQ(root->children()[1], window2.get());
- EXPECT_EQ(root->children()[2], transient2.get());
- ASSERT_EQ(3u, root->layer()->children().size());
- EXPECT_EQ(root->layer()->children()[0], window1->layer());
- EXPECT_EQ(root->layer()->children()[1], window2->layer());
- EXPECT_EQ(root->layer()->children()[2], transient2->layer());
-
- // Open another window on top.
- scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window()));
-
- ASSERT_EQ(4u, root->children().size());
- EXPECT_EQ(root->children()[0], window1.get());
- EXPECT_EQ(root->children()[1], window2.get());
- EXPECT_EQ(root->children()[2], transient2.get());
- EXPECT_EQ(root->children()[3], window3.get());
- ASSERT_EQ(4u, root->layer()->children().size());
- EXPECT_EQ(root->layer()->children()[0], window1->layer());
- EXPECT_EQ(root->layer()->children()[1], window2->layer());
- EXPECT_EQ(root->layer()->children()[2], transient2->layer());
- EXPECT_EQ(root->layer()->children()[3], window3->layer());
-
- // Pretend we're closing the topmost non-transient window, then bring
- // window2 to the top. It should not move.
- window3->layer()->set_delegate(NULL);
- root->StackChildAtTop(window2.get());
-
- ASSERT_EQ(4u, root->children().size());
- EXPECT_EQ(root->children()[0], window1.get());
- EXPECT_EQ(root->children()[1], window2.get());
- EXPECT_EQ(root->children()[2], transient2.get());
- EXPECT_EQ(root->children()[3], window3.get());
- ASSERT_EQ(4u, root->layer()->children().size());
- EXPECT_EQ(root->layer()->children()[0], window1->layer());
- EXPECT_EQ(root->layer()->children()[1], window2->layer());
- EXPECT_EQ(root->layer()->children()[2], transient2->layer());
- EXPECT_EQ(root->layer()->children()[3], window3->layer());
-
- // Bring window1 to the top. It should move ahead of window2, but not
- // ahead of window3 (with NULL delegate).
- root->StackChildAtTop(window1.get());
-
- ASSERT_EQ(4u, root->children().size());
- EXPECT_EQ(root->children()[0], window2.get());
- EXPECT_EQ(root->children()[1], transient2.get());
- EXPECT_EQ(root->children()[2], window1.get());
- EXPECT_EQ(root->children()[3], window3.get());
- ASSERT_EQ(4u, root->layer()->children().size());
- EXPECT_EQ(root->layer()->children()[0], window2->layer());
- EXPECT_EQ(root->layer()->children()[1], transient2->layer());
- EXPECT_EQ(root->layer()->children()[2], window1->layer());
- EXPECT_EQ(root->layer()->children()[3], window3->layer());
-}
-
class RootWindowAttachmentObserver : public WindowObserver {
public:
RootWindowAttachmentObserver() : added_count_(0), removed_count_(0) {}
@@ -3007,50 +2677,6 @@ TEST_F(WindowTest, OnWindowHierarchyChange) {
}
-namespace {
-
-// Used by NotifyDelegateAfterDeletingTransients. Adds a string to a vector when
-// OnWindowDestroyed() is invoked so that destruction order can be verified.
-class DestroyedTrackingDelegate : public TestWindowDelegate {
- public:
- explicit DestroyedTrackingDelegate(const std::string& name,
- std::vector<std::string>* results)
- : name_(name),
- results_(results) {}
-
- virtual void OnWindowDestroyed() OVERRIDE {
- results_->push_back(name_);
- }
-
- private:
- const std::string name_;
- std::vector<std::string>* results_;
-
- DISALLOW_COPY_AND_ASSIGN(DestroyedTrackingDelegate);
-};
-
-} // namespace
-
-// Verifies the delegate is notified of destruction after transients are
-// destroyed.
-TEST_F(WindowTest, NotifyDelegateAfterDeletingTransients) {
- std::vector<std::string> destruction_order;
-
- DestroyedTrackingDelegate parent_delegate("parent", &destruction_order);
- scoped_ptr<Window> parent(new Window(&parent_delegate));
- parent->Init(ui::LAYER_NOT_DRAWN);
-
- DestroyedTrackingDelegate transient_delegate("transient", &destruction_order);
- Window* transient = new Window(&transient_delegate); // Owned by |parent|.
- transient->Init(ui::LAYER_NOT_DRAWN);
- parent->AddTransientChild(transient);
- parent.reset();
-
- ASSERT_EQ(2u, destruction_order.size());
- EXPECT_EQ("transient", destruction_order[0]);
- EXPECT_EQ("parent", destruction_order[1]);
-}
-
// Verifies SchedulePaint() on a layerless window results in damaging the right
// thing.
TEST_F(WindowTest, LayerlessWindowSchedulePaint) {
diff --git a/ui/oak/oak_aura_window_display.cc b/ui/oak/oak_aura_window_display.cc
index 51e83c4..41ee3d3 100644
--- a/ui/oak/oak_aura_window_display.cc
+++ b/ui/oak/oak_aura_window_display.cc
@@ -11,6 +11,7 @@
#include "ui/aura/window.h"
#include "ui/base/models/table_model_observer.h"
#include "ui/oak/oak_pretty_print.h"
+#include "ui/views/corewm/window_util.h"
namespace oak {
namespace internal {
@@ -131,11 +132,12 @@ base::string16 OakAuraWindowDisplay::GetText(int row, int column_id) {
case ROW_ROOTWINDOW:
return PropertyWithVoidStar("Root Window: ", window_->GetRootWindow());
case ROW_TRANSIENTCHILDREN:
- return PropertyWithInteger("Transient Children: ",
- window_->transient_children().size());
+ return PropertyWithInteger(
+ "Transient Children: ",
+ views::corewm::GetTransientChildren(window_).size());
case ROW_TRANSIENTPARENT:
return PropertyWithVoidStar("Transient Parent: ",
- window_->transient_parent());
+ views::corewm::GetTransientParent(window_));
case ROW_USERDATA:
return PropertyWithVoidStar("User Data: ", window_->user_data());
case ROW_IGNOREEVENTS:
diff --git a/ui/views/corewm/base_focus_rules.cc b/ui/views/corewm/base_focus_rules.cc
index 9520c74..066627d 100644
--- a/ui/views/corewm/base_focus_rules.cc
+++ b/ui/views/corewm/base_focus_rules.cc
@@ -9,6 +9,7 @@
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/views/corewm/window_modality_controller.h"
+#include "ui/views/corewm/window_util.h"
namespace views {
namespace corewm {
@@ -120,15 +121,15 @@ aura::Window* BaseFocusRules::GetActivatableWindow(aura::Window* window) const {
if (modal_transient)
return GetActivatableWindow(modal_transient);
- if (child->transient_parent()) {
+ if (views::corewm::GetTransientParent(child)) {
// To avoid infinite recursion, if |child| has a transient parent
// whose own modal transient is |child| itself, just return |child|.
aura::Window* parent_modal_transient =
- GetModalTransient(child->transient_parent());
+ GetModalTransient(views::corewm::GetTransientParent(child));
if (parent_modal_transient == child)
return child;
- return GetActivatableWindow(child->transient_parent());
+ return GetActivatableWindow(views::corewm::GetTransientParent(child));
}
parent = parent->parent();
diff --git a/ui/views/corewm/focus_controller.cc b/ui/views/corewm/focus_controller.cc
index bf77b03..a3107a1 100644
--- a/ui/views/corewm/focus_controller.cc
+++ b/ui/views/corewm/focus_controller.cc
@@ -13,6 +13,7 @@
#include "ui/aura/window_tracker.h"
#include "ui/events/event.h"
#include "ui/views/corewm/focus_rules.h"
+#include "ui/views/corewm/window_util.h"
namespace views {
namespace corewm {
@@ -25,10 +26,10 @@ void StackTransientParentsBelowModalWindow(aura::Window* window) {
if (window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_WINDOW)
return;
- aura::Window* transient_parent = window->transient_parent();
+ aura::Window* transient_parent = views::corewm::GetTransientParent(window);
while (transient_parent) {
transient_parent->parent()->StackChildAtTop(transient_parent);
- transient_parent = transient_parent->transient_parent();
+ transient_parent = views::corewm::GetTransientParent(transient_parent);
}
}
@@ -37,7 +38,7 @@ void StackWindowLayerAbove(aura::Window* window, aura::Window* relative_to) {
// Stack |window| above the last transient child of |relative_to| that shares
// the same parent.
const aura::Window::Windows& window_transients(
- relative_to->transient_children());
+ GetTransientChildren(relative_to));
for (aura::Window::Windows::const_iterator i = window_transients.begin();
i != window_transients.end(); ++i) {
aura::Window* transient = *i;
diff --git a/ui/views/corewm/shadow_controller.cc b/ui/views/corewm/shadow_controller.cc
index eb09616..7327f92 100644
--- a/ui/views/corewm/shadow_controller.cc
+++ b/ui/views/corewm/shadow_controller.cc
@@ -19,6 +19,7 @@
#include "ui/compositor/layer.h"
#include "ui/views/corewm/shadow.h"
#include "ui/views/corewm/shadow_types.h"
+#include "ui/views/corewm/window_util.h"
using std::make_pair;
@@ -59,10 +60,10 @@ Shadow::Style GetShadowStyleForWindowLosingActive(
aura::Window* gaining_active) {
if (gaining_active && aura::client::GetHideOnDeactivate(gaining_active)) {
aura::Window::Windows::const_iterator it =
- std::find(losing_active->transient_children().begin(),
- losing_active->transient_children().end(),
+ std::find(GetTransientChildren(losing_active).begin(),
+ GetTransientChildren(losing_active).end(),
gaining_active);
- if (it != losing_active->transient_children().end())
+ if (it != GetTransientChildren(losing_active).end())
return Shadow::STYLE_ACTIVE;
}
return Shadow::STYLE_INACTIVE;
diff --git a/ui/views/corewm/shadow_controller_unittest.cc b/ui/views/corewm/shadow_controller_unittest.cc
index fb2c1d9..a01237a 100644
--- a/ui/views/corewm/shadow_controller_unittest.cc
+++ b/ui/views/corewm/shadow_controller_unittest.cc
@@ -16,6 +16,7 @@
#include "ui/compositor/layer.h"
#include "ui/views/corewm/shadow.h"
#include "ui/views/corewm/shadow_types.h"
+#include "ui/views/corewm/window_util.h"
#include "ui/views/corewm/wm_state.h"
namespace views {
@@ -205,7 +206,7 @@ TEST_F(ShadowControllerTest, TransientParentKeepsActiveShadow) {
window2->Init(ui::LAYER_TEXTURED);
ParentWindow(window2.get());
window2->SetBounds(gfx::Rect(11, 21, 301, 401));
- window1->AddTransientChild(window2.get());
+ AddTransientChild(window1.get(), window2.get());
aura::client::SetHideOnDeactivate(window2.get(), true);
window2->Show();
ActivateWindow(window2.get());
diff --git a/ui/views/corewm/transient_window_controller.cc b/ui/views/corewm/transient_window_controller.cc
new file mode 100644
index 0000000..13a5b1f
--- /dev/null
+++ b/ui/views/corewm/transient_window_controller.cc
@@ -0,0 +1,42 @@
+// Copyright 2014 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/transient_window_controller.h"
+
+#include "ui/views/corewm/transient_window_manager.h"
+
+namespace views {
+namespace corewm {
+
+TransientWindowController::TransientWindowController() {
+}
+
+TransientWindowController::~TransientWindowController() {
+}
+
+void TransientWindowController::AddTransientChild(aura::Window* parent,
+ aura::Window* child) {
+ TransientWindowManager::Get(parent)->AddTransientChild(child);
+}
+
+void TransientWindowController::RemoveTransientChild(aura::Window* parent,
+ aura::Window* child) {
+ TransientWindowManager::Get(parent)->RemoveTransientChild(child);
+}
+
+aura::Window* TransientWindowController::GetTransientParent(
+ aura::Window* window) {
+ return const_cast<aura::Window*>(GetTransientParent(
+ const_cast<const aura::Window*>(window)));
+}
+
+const aura::Window* TransientWindowController::GetTransientParent(
+ const aura::Window* window) {
+ const TransientWindowManager* window_manager =
+ TransientWindowManager::Get(window);
+ return window_manager ? window_manager->transient_parent() : NULL;
+}
+
+} // namespace corewm
+} // namespace views
diff --git a/ui/views/corewm/transient_window_controller.h b/ui/views/corewm/transient_window_controller.h
new file mode 100644
index 0000000..88d473d
--- /dev/null
+++ b/ui/views/corewm/transient_window_controller.h
@@ -0,0 +1,38 @@
+// Copyright 2014 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.
+
+#ifndef UI_VIEWS_COREWM_TRANSIENT_WINDOW_CONTROLLER_H_
+#define UI_VIEWS_COREWM_TRANSIENT_WINDOW_CONTROLLER_H_
+
+#include "ui/aura/client/transient_window_client.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+namespace corewm {
+
+// TransientWindowClient implementation. Uses TransientWindowManager to handle
+// tracking transient per window.
+class VIEWS_EXPORT TransientWindowController
+ : public aura::client::TransientWindowClient {
+ public:
+ TransientWindowController();
+ virtual ~TransientWindowController();
+
+ // TransientWindowClient:
+ virtual void AddTransientChild(aura::Window* parent,
+ aura::Window* child) OVERRIDE;
+ virtual void RemoveTransientChild(aura::Window* parent,
+ aura::Window* child) OVERRIDE;
+ virtual aura::Window* GetTransientParent(aura::Window* window) OVERRIDE;
+ virtual const aura::Window* GetTransientParent(
+ const aura::Window* window) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowController);
+};
+
+} // namespace corewm
+} // namespace views
+
+#endif // UI_VIEWS_COREWM_TRANSIENT_WINDOW_CONTROLLER_H_
diff --git a/ui/views/corewm/transient_window_manager.cc b/ui/views/corewm/transient_window_manager.cc
new file mode 100644
index 0000000..35ee95d
--- /dev/null
+++ b/ui/views/corewm/transient_window_manager.cc
@@ -0,0 +1,147 @@
+// Copyright 2014 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/transient_window_manager.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "base/auto_reset.h"
+#include "base/stl_util.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+#include "ui/views/corewm/transient_window_stacking_client.h"
+#include "ui/views/corewm/window_util.h"
+
+using aura::Window;
+
+namespace views {
+namespace corewm {
+
+DEFINE_OWNED_WINDOW_PROPERTY_KEY(TransientWindowManager, kPropertyKey, NULL);
+
+TransientWindowManager::~TransientWindowManager() {
+}
+
+// static
+TransientWindowManager* TransientWindowManager::Get(Window* window) {
+ TransientWindowManager* manager = window->GetProperty(kPropertyKey);
+ if (!manager) {
+ manager = new TransientWindowManager(window);
+ window->SetProperty(kPropertyKey, manager);
+ }
+ return manager;
+}
+
+// static
+const TransientWindowManager* TransientWindowManager::Get(
+ const Window* window) {
+ return window->GetProperty(kPropertyKey);
+}
+
+void TransientWindowManager::AddTransientChild(Window* child) {
+ // TransientWindowStackingClient does the stacking of transient windows. If it
+ // isn't installed stacking is going to be wrong.
+ DCHECK(TransientWindowStackingClient::instance_);
+
+ TransientWindowManager* child_manager = Get(child);
+ if (child_manager->transient_parent_)
+ Get(child_manager->transient_parent_)->RemoveTransientChild(child);
+ DCHECK(std::find(transient_children_.begin(), transient_children_.end(),
+ child) == transient_children_.end());
+ transient_children_.push_back(child);
+ child_manager->transient_parent_ = window_;
+ FOR_EACH_OBSERVER(WindowObserver, window_->observers_,
+ OnAddTransientChild(window_, child));
+}
+
+void TransientWindowManager::RemoveTransientChild(Window* child) {
+ Windows::iterator i =
+ std::find(transient_children_.begin(), transient_children_.end(), child);
+ DCHECK(i != transient_children_.end());
+ transient_children_.erase(i);
+ TransientWindowManager* child_manager = Get(child);
+ DCHECK_EQ(window_, child_manager->transient_parent_);
+ child_manager->transient_parent_ = NULL;
+ FOR_EACH_OBSERVER(WindowObserver, window_->observers_,
+ OnRemoveTransientChild(window_, child));
+}
+
+bool TransientWindowManager::IsStackingTransient(
+ const aura::Window* child,
+ const aura::Window* target) const {
+ return stacking_pair_ && stacking_pair_->child == child &&
+ stacking_pair_->target == target;
+}
+
+TransientWindowManager::TransientWindowManager(Window* window)
+ : window_(window),
+ transient_parent_(NULL),
+ stacking_pair_(NULL) {
+ window_->AddObserver(this);
+}
+
+void TransientWindowManager::OnChildStackingChanged(aura::Window* child) {
+ // Do nothing if we initiated the stacking change.
+ // TODO(sky): change args to OnWindowStackingChanged() so that this lookup
+ // can be simpler.
+ if (stacking_pair_ && stacking_pair_->child == child) {
+ Windows::const_iterator child_i = std::find(
+ window_->children().begin(), window_->children().end(), child);
+ DCHECK(child_i != window_->children().end());
+ if (child_i != window_->children().begin() &&
+ (*(child_i - 1) == stacking_pair_->target))
+ return;
+ }
+
+ // Stack any transient children that share the same parent to be in front of
+ // |child|. The existing stacking order is preserved by iterating backwards
+ // and always stacking on top.
+ Window::Windows children(window_->children());
+ for (Window::Windows::reverse_iterator it = children.rbegin();
+ it != children.rend(); ++it) {
+ if ((*it) != child && HasTransientAncestor(*it, child)) {
+ StackingPair pair(*it, child);
+ base::AutoReset<StackingPair*> resetter(&stacking_pair_, &pair);
+ window_->StackChildAbove((*it), child);
+ }
+ }
+}
+
+void TransientWindowManager::OnWindowVisibilityChanging(Window* window,
+ bool visible) {
+ // TODO(sky): move handling of becoming visible here.
+ if (!visible) {
+ std::for_each(transient_children_.begin(), transient_children_.end(),
+ std::mem_fun(&Window::Hide));
+ }
+}
+
+void TransientWindowManager::OnWindowStackingChanged(Window* window) {
+ TransientWindowManager* parent_manager = Get(window->parent());
+ parent_manager->OnChildStackingChanged(window);
+}
+
+void TransientWindowManager::OnWindowDestroying(Window* window) {
+ // TODO(sky): remove notes after safely landing and baking.
+
+ // Removes ourselves from our transient parent (if it hasn't been done by the
+ // RootWindow).
+ // NOTE: This use to be done after children where removed, now it is before.
+ if (transient_parent_) {
+ TransientWindowManager::Get(transient_parent_)->RemoveTransientChild(
+ window_);
+ }
+
+ // Destroy transient children, only after we've removed ourselves from our
+ // parent, as destroying an active transient child may otherwise attempt to
+ // refocus us.
+ // NOTE: this use to be after removed from parent, now its before.
+ Windows transient_children(transient_children_);
+ STLDeleteElements(&transient_children);
+ DCHECK(transient_children_.empty());
+}
+
+} // namespace corewm
+} // namespace views
diff --git a/ui/views/corewm/transient_window_manager.h b/ui/views/corewm/transient_window_manager.h
new file mode 100644
index 0000000..72c9f99
--- /dev/null
+++ b/ui/views/corewm/transient_window_manager.h
@@ -0,0 +1,96 @@
+// Copyright 2014 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.
+
+#ifndef UI_VIEWS_COREWM_TRANSIENT_WINDOW_MANAGER_H_
+#define UI_VIEWS_COREWM_TRANSIENT_WINDOW_MANAGER_H_
+
+#include <vector>
+
+#include "ui/aura/window_observer.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+namespace corewm {
+
+// TransientWindowManager manages the set of transient children for a window
+// along with the transient parent. Transient children get the following
+// behavior:
+// . The transient parent destroys any transient children when it is
+// destroyed. This means a transient child is destroyed if either its parent
+// or transient parent is destroyed.
+// . If a transient child and its transient parent share the same parent, then
+// transient children are always ordered above the transient parent.
+// Transient windows are typically used for popups and menus.
+// TODO(sky): when we nuke TransientWindowClient rename this to
+// TransientWindowController.
+class VIEWS_EXPORT TransientWindowManager : public aura::WindowObserver {
+ public:
+ typedef std::vector<aura::Window*> Windows;
+
+ virtual ~TransientWindowManager();
+
+ // Returns the TransientWindowManager for |window|. This never returns NULL.
+ static TransientWindowManager* Get(aura::Window* window);
+
+ // Returns the TransientWindowManager for |window| only if it already exists.
+ // WARNING: this may return NULL.
+ static const TransientWindowManager* Get(const aura::Window* window);
+
+ // Adds or removes a transient child.
+ void AddTransientChild(aura::Window* child);
+ void RemoveTransientChild(aura::Window* child);
+
+ const Windows& transient_children() const { return transient_children_; }
+
+ aura::Window* transient_parent() { return transient_parent_; }
+ const aura::Window* transient_parent() const { return transient_parent_; }
+
+ // Returns true if in the process of stacking |child| on top of |target|. That
+ // is, when the stacking order of a window changes (OnWindowStackingChanged())
+ // the transients may get restacked as well. This function can be used to
+ // detect if TransientWindowManager is in the process of stacking a transient
+ // as the result of window stacking changing.
+ bool IsStackingTransient(const aura::Window* child,
+ const aura::Window* target) const;
+
+ private:
+ // Used to identify when a stacking change needs to restack transients.
+ struct StackingPair {
+ StackingPair(const aura::Window* child, const aura::Window* target)
+ : child(child),
+ target(target) {}
+
+ // The window being moved.
+ const aura::Window* child;
+
+ // |child| is being stacked on top of this.
+ const aura::Window* target;
+ };
+
+ explicit TransientWindowManager(aura::Window* window);
+
+ // Invoked whne |child|'s stacking order changes.
+ void OnChildStackingChanged(aura::Window* child);
+
+ // WindowObserver:
+ virtual void OnWindowVisibilityChanging(aura::Window* window,
+ bool visible) OVERRIDE;
+ virtual void OnWindowStackingChanged(aura::Window* window) OVERRIDE;
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+
+ aura::Window* window_;
+ aura::Window* transient_parent_;
+ Windows transient_children_;
+
+ // If non-null we're actively restacking transient as the result of a
+ // transient ancestor changing. This is a pointer to a value on the stack.
+ StackingPair* stacking_pair_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowManager);
+};
+
+} // namespace corewm
+} // namespace views
+
+#endif // UI_VIEWS_COREWM_TRANSIENT_WINDOW_MANAGER_H_
diff --git a/ui/views/corewm/transient_window_manager_unittest.cc b/ui/views/corewm/transient_window_manager_unittest.cc
new file mode 100644
index 0000000..b942eb1
--- /dev/null
+++ b/ui/views/corewm/transient_window_manager_unittest.cc
@@ -0,0 +1,577 @@
+// Copyright 2014 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/transient_window_manager.h"
+
+#include "ui/aura/client/visibility_client.h"
+#include "ui/aura/client/window_tree_client.h"
+#include "ui/aura/layout_manager.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
+#include "ui/views/corewm/window_util.h"
+#include "ui/views/test/views_test_base.h"
+
+using aura::Window;
+
+using aura::test::ChildWindowIDsAsString;
+using aura::test::CreateTestWindowWithId;
+
+namespace views {
+namespace corewm {
+
+class TransientWindowManagerTest : public views::ViewsTestBase {
+ public:
+ TransientWindowManagerTest() {}
+ virtual ~TransientWindowManagerTest() {}
+
+ protected:
+ // Creates a transient window that is transient to |parent|.
+ Window* CreateTransientChild(int id, Window* parent) {
+ Window* window = new Window(NULL);
+ window->set_id(id);
+ window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
+ window->Init(ui::LAYER_TEXTURED);
+ aura::client::ParentWindowWithContext(window, GetContext(), gfx::Rect());
+ AddTransientChild(parent, window);
+ return window;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TransientWindowManagerTest);
+};
+
+// Various assertions for transient children.
+TEST_F(TransientWindowManagerTest, TransientChildren) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, GetContext()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ scoped_ptr<Window> w3(CreateTestWindowWithId(3, parent.get()));
+ Window* w2 = CreateTestWindowWithId(2, parent.get());
+ // w2 is now owned by w1.
+ AddTransientChild(w1.get(), w2);
+ // Stack w1 at the top (end), this should force w2 to be last (on top of w1).
+ parent->StackChildAtTop(w1.get());
+ ASSERT_EQ(3u, parent->children().size());
+ EXPECT_EQ(w2, parent->children().back());
+
+ // Destroy w1, which should also destroy w3 (since it's a transient child).
+ w1.reset();
+ w2 = NULL;
+ ASSERT_EQ(1u, parent->children().size());
+ EXPECT_EQ(w3.get(), parent->children()[0]);
+
+ w1.reset(CreateTestWindowWithId(4, parent.get()));
+ w2 = CreateTestWindowWithId(5, w3.get());
+ AddTransientChild(w1.get(), w2);
+ parent->StackChildAtTop(w3.get());
+ // Stack w1 at the top (end), this shouldn't affect w2 since it has a
+ // different parent.
+ parent->StackChildAtTop(w1.get());
+ ASSERT_EQ(2u, parent->children().size());
+ EXPECT_EQ(w3.get(), parent->children()[0]);
+ EXPECT_EQ(w1.get(), parent->children()[1]);
+
+ // Hiding parent should hide transient children.
+ EXPECT_TRUE(w2->IsVisible());
+ w1->Hide();
+ EXPECT_FALSE(w2->IsVisible());
+}
+
+// Tests that transient children are stacked as a unit when using stack above.
+TEST_F(TransientWindowManagerTest, TransientChildrenGroupAbove) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, GetContext()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ Window* w11 = CreateTestWindowWithId(11, parent.get());
+ scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get()));
+ Window* w21 = CreateTestWindowWithId(21, parent.get());
+ Window* w211 = CreateTestWindowWithId(211, parent.get());
+ Window* w212 = CreateTestWindowWithId(212, parent.get());
+ Window* w213 = CreateTestWindowWithId(213, parent.get());
+ Window* w22 = CreateTestWindowWithId(22, parent.get());
+ ASSERT_EQ(8u, parent->children().size());
+
+ // w11 is now owned by w1.
+ AddTransientChild(w1.get(), w11);
+ // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w21);
+ // w22 is now owned by w2.
+ AddTransientChild(w2.get(), w22);
+ // w211 is now owned by w21.
+ AddTransientChild(w21, w211);
+ // w212 is now owned by w21.
+ AddTransientChild(w21, w212);
+ // w213 is now owned by w21.
+ AddTransientChild(w21, w213);
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ // Stack w1 at the top (end), this should force w11 to be last (on top of w1).
+ parent->StackChildAtTop(w1.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // This tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAtTop(w2.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w2.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w21, w1.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w21, w22);
+ EXPECT_EQ(w213, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w21);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w213, w21);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // No change when stacking a transient parent above its transient child.
+ parent->StackChildAbove(w21, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // This tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAbove(w2.get(), w1.get());
+ EXPECT_EQ(w212, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAbove(w11, w213);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+}
+
+// Tests that transient children are stacked as a unit when using stack below.
+TEST_F(TransientWindowManagerTest, TransientChildrenGroupBelow) {
+ scoped_ptr<Window> parent(CreateTestWindowWithId(0, GetContext()));
+ scoped_ptr<Window> w1(CreateTestWindowWithId(1, parent.get()));
+ Window* w11 = CreateTestWindowWithId(11, parent.get());
+ scoped_ptr<Window> w2(CreateTestWindowWithId(2, parent.get()));
+ Window* w21 = CreateTestWindowWithId(21, parent.get());
+ Window* w211 = CreateTestWindowWithId(211, parent.get());
+ Window* w212 = CreateTestWindowWithId(212, parent.get());
+ Window* w213 = CreateTestWindowWithId(213, parent.get());
+ Window* w22 = CreateTestWindowWithId(22, parent.get());
+ ASSERT_EQ(8u, parent->children().size());
+
+ // w11 is now owned by w1.
+ AddTransientChild(w1.get(), w11);
+ // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w21);
+ // w22 is now owned by w2.
+ AddTransientChild(w2.get(), w22);
+ // w211 is now owned by w21.
+ AddTransientChild(w21, w211);
+ // w212 is now owned by w21.
+ AddTransientChild(w21, w212);
+ // w213 is now owned by w21.
+ AddTransientChild(w21, w213);
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ // Stack w2 at the bottom, this should force w11 to be last (on top of w1).
+ // This also tests that the order in children_ array rather than in
+ // transient_children_ array is used when reinserting transient children.
+ // If transient_children_ array was used '22' would be following '21'.
+ parent->StackChildAtBottom(w2.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildAtBottom(w1.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w21, w1.get());
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 21 211 212 213 22 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w11, w2.get());
+ EXPECT_EQ(w22, parent->children().back());
+ EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w22, w21);
+ EXPECT_EQ(w213, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 211 212 213", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w21, w11);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 211 212 213 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w213, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ // No change when stacking a transient parent below its transient child.
+ parent->StackChildBelow(w21, w211);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w1.get(), w2.get());
+ EXPECT_EQ(w212, parent->children().back());
+ EXPECT_EQ("1 11 2 22 21 213 211 212", ChildWindowIDsAsString(parent.get()));
+
+ parent->StackChildBelow(w213, w11);
+ EXPECT_EQ(w11, parent->children().back());
+ EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
+}
+
+namespace {
+
+// Used by NotifyDelegateAfterDeletingTransients. Adds a string to a vector when
+// OnWindowDestroyed() is invoked so that destruction order can be verified.
+class DestroyedTrackingDelegate : public aura::test::TestWindowDelegate {
+ public:
+ explicit DestroyedTrackingDelegate(const std::string& name,
+ std::vector<std::string>* results)
+ : name_(name),
+ results_(results) {}
+
+ virtual void OnWindowDestroyed() OVERRIDE {
+ results_->push_back(name_);
+ }
+
+ private:
+ const std::string name_;
+ std::vector<std::string>* results_;
+
+ DISALLOW_COPY_AND_ASSIGN(DestroyedTrackingDelegate);
+};
+
+} // namespace
+
+// Verifies the delegate is notified of destruction after transients are
+// destroyed.
+TEST_F(TransientWindowManagerTest, NotifyDelegateAfterDeletingTransients) {
+ std::vector<std::string> destruction_order;
+
+ DestroyedTrackingDelegate parent_delegate("parent", &destruction_order);
+ scoped_ptr<Window> parent(new Window(&parent_delegate));
+ parent->Init(ui::LAYER_NOT_DRAWN);
+
+ DestroyedTrackingDelegate transient_delegate("transient", &destruction_order);
+ Window* transient = new Window(&transient_delegate); // Owned by |parent|.
+ transient->Init(ui::LAYER_NOT_DRAWN);
+ AddTransientChild(parent.get(), transient);
+ parent.reset();
+
+ ASSERT_EQ(2u, destruction_order.size());
+ EXPECT_EQ("transient", destruction_order[0]);
+ EXPECT_EQ("parent", destruction_order[1]);
+}
+
+TEST_F(TransientWindowManagerTest, StackTransientsWhoseLayersHaveNoDelegate) {
+ // Create a window with several transients, then a couple windows on top.
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, GetContext()));
+ scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
+ scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
+ scoped_ptr<Window> window13(CreateTransientChild(13, window1.get()));
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, GetContext()));
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, GetContext()));
+
+ EXPECT_EQ("1 11 12 13 2 3", ChildWindowIDsAsString(GetContext()));
+
+ // Remove the delegates of a couple of transients, as if they are closing
+ // and animating out.
+ window11->layer()->set_delegate(NULL);
+ window13->layer()->set_delegate(NULL);
+
+ // Move window1 to the front. All transients should move with it, and their
+ // order should be preserved.
+ GetContext()->StackChildAtTop(window1.get());
+
+ EXPECT_EQ("2 3 1 11 12 13", ChildWindowIDsAsString(GetContext()));
+}
+
+TEST_F(TransientWindowManagerTest,
+ StackTransientsLayersRelativeToOtherTransients) {
+ // Create a window with several transients, then a couple windows on top.
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, GetContext()));
+ scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
+ scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
+ scoped_ptr<Window> window13(CreateTransientChild(13, window1.get()));
+
+ EXPECT_EQ("1 11 12 13", ChildWindowIDsAsString(GetContext()));
+
+ // Stack 11 above 12.
+ GetContext()->StackChildAbove(window11.get(), window12.get());
+ EXPECT_EQ("1 12 11 13", ChildWindowIDsAsString(GetContext()));
+
+ // Stack 13 below 12.
+ GetContext()->StackChildBelow(window13.get(), window12.get());
+ EXPECT_EQ("1 13 12 11", ChildWindowIDsAsString(GetContext()));
+
+ // Stack 11 above 1.
+ GetContext()->StackChildAbove(window11.get(), window1.get());
+ EXPECT_EQ("1 11 13 12", ChildWindowIDsAsString(GetContext()));
+
+ // Stack 12 below 13.
+ GetContext()->StackChildBelow(window12.get(), window13.get());
+ EXPECT_EQ("1 11 12 13", ChildWindowIDsAsString(GetContext()));
+}
+
+TEST_F(TransientWindowManagerTest,
+ StackTransientsLayersRelativeToOtherTransientsNoLayerDelegate) {
+ // Create a window with several transients, then a couple windows on top.
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, GetContext()));
+ scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
+ scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
+ scoped_ptr<Window> window13(CreateTransientChild(13, window1.get()));
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, GetContext()));
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, GetContext()));
+
+ EXPECT_EQ("1 11 12 13 2 3", ChildWindowIDsAsString(GetContext()));
+
+ window1->layer()->set_delegate(NULL);
+
+ // Stack 1 at top.
+ GetContext()->StackChildAtTop(window1.get());
+ EXPECT_EQ("2 3 1 11 12 13", ChildWindowIDsAsString(GetContext()));
+}
+
+class StackingMadrigalLayoutManager : public aura::LayoutManager {
+ public:
+ explicit StackingMadrigalLayoutManager(Window* root_window)
+ : root_window_(root_window) {
+ root_window_->SetLayoutManager(this);
+ }
+ virtual ~StackingMadrigalLayoutManager() {
+ }
+
+ private:
+ // Overridden from LayoutManager:
+ virtual void OnWindowResized() OVERRIDE {}
+ virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {}
+ virtual void OnWillRemoveWindowFromLayout(Window* child) OVERRIDE {}
+ virtual void OnWindowRemovedFromLayout(Window* child) OVERRIDE {}
+ virtual void OnChildWindowVisibilityChanged(Window* child,
+ bool visible) OVERRIDE {
+ Window::Windows::const_iterator it = root_window_->children().begin();
+ Window* last_window = NULL;
+ for (; it != root_window_->children().end(); ++it) {
+ if (*it == child && last_window) {
+ if (!visible)
+ root_window_->StackChildAbove(last_window, *it);
+ else
+ root_window_->StackChildAbove(*it, last_window);
+ break;
+ }
+ last_window = *it;
+ }
+ }
+ virtual void SetChildBounds(Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE {
+ SetChildBoundsDirect(child, requested_bounds);
+ }
+
+ Window* root_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackingMadrigalLayoutManager);
+};
+
+class StackingMadrigalVisibilityClient : public aura::client::VisibilityClient {
+ public:
+ explicit StackingMadrigalVisibilityClient(Window* root_window)
+ : ignored_window_(NULL) {
+ aura::client::SetVisibilityClient(root_window, this);
+ }
+ virtual ~StackingMadrigalVisibilityClient() {
+ }
+
+ void set_ignored_window(Window* ignored_window) {
+ ignored_window_ = ignored_window;
+ }
+
+ private:
+ // Overridden from client::VisibilityClient:
+ virtual void UpdateLayerVisibility(Window* window, bool visible) OVERRIDE {
+ if (!visible) {
+ if (window == ignored_window_)
+ window->layer()->set_delegate(NULL);
+ else
+ window->layer()->SetVisible(visible);
+ } else {
+ window->layer()->SetVisible(visible);
+ }
+ }
+
+ Window* ignored_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackingMadrigalVisibilityClient);
+};
+
+// This test attempts to reconstruct a circumstance that can happen when the
+// aura client attempts to manipulate the visibility and delegate of a layer
+// independent of window visibility.
+// A use case is where the client attempts to keep a window visible onscreen
+// even after code has called Hide() on the window. The use case for this would
+// be that window hides are animated (e.g. the window fades out). To prevent
+// spurious updating the client code may also clear window's layer's delegate,
+// so that the window cannot attempt to paint or update it further. The window
+// uses the presence of a NULL layer delegate as a signal in stacking to note
+// that the window is being manipulated by such a use case and its stacking
+// should not be adjusted.
+// One issue that can arise when a window opens two transient children, and the
+// first is hidden. Subsequent attempts to activate the transient parent can
+// result in the transient parent being stacked above the second transient
+// child. A fix is made to Window::StackAbove to prevent this, and this test
+// verifies this fix.
+TEST_F(TransientWindowManagerTest, StackingMadrigal) {
+ new StackingMadrigalLayoutManager(GetContext());
+ StackingMadrigalVisibilityClient visibility_client(GetContext());
+
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, GetContext()));
+ scoped_ptr<Window> window11(CreateTransientChild(11, window1.get()));
+
+ visibility_client.set_ignored_window(window11.get());
+
+ window11->Show();
+ window11->Hide();
+
+ // As a transient, window11 should still be stacked above window1, even when
+ // hidden.
+ EXPECT_TRUE(aura::test::WindowIsAbove(window11.get(), window1.get()));
+ EXPECT_TRUE(aura::test::LayerIsAbove(window11.get(), window1.get()));
+
+ // A new transient should still be above window1. It will appear behind
+ // window11 because we don't stack windows on top of targets with NULL
+ // delegates.
+ scoped_ptr<Window> window12(CreateTransientChild(12, window1.get()));
+ window12->Show();
+
+ EXPECT_TRUE(aura::test::WindowIsAbove(window12.get(), window1.get()));
+ EXPECT_TRUE(aura::test::LayerIsAbove(window12.get(), window1.get()));
+
+ // In earlier versions of the StackChildAbove() method, attempting to stack
+ // window1 above window12 at this point would actually restack the layers
+ // resulting in window12's layer being below window1's layer (though the
+ // windows themselves would still be correctly stacked, so events would pass
+ // through.)
+ GetContext()->StackChildAbove(window1.get(), window12.get());
+
+ // Both window12 and its layer should be stacked above window1.
+ EXPECT_TRUE(aura::test::WindowIsAbove(window12.get(), window1.get()));
+ EXPECT_TRUE(aura::test::LayerIsAbove(window12.get(), window1.get()));
+}
+
+// Test for an issue where attempting to stack a primary window on top of a
+// transient with a NULL layer delegate causes that primary window to be moved,
+// but the layer order not changed to match. http://crbug.com/112562
+TEST_F(TransientWindowManagerTest, StackOverClosingTransient) {
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, GetContext()));
+ scoped_ptr<Window> transient1(CreateTransientChild(11, window1.get()));
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, GetContext()));
+ scoped_ptr<Window> transient2(CreateTransientChild(21, window2.get()));
+
+ // Both windows and layers are stacked in creation order.
+ Window* root = GetContext();
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], transient1.get());
+ EXPECT_EQ(root->children()[2], window2.get());
+ EXPECT_EQ(root->children()[3], transient2.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], transient1->layer());
+ EXPECT_EQ(root->layer()->children()[2], window2->layer());
+ EXPECT_EQ(root->layer()->children()[3], transient2->layer());
+ EXPECT_EQ("1 11 2 21", ChildWindowIDsAsString(GetContext()));
+
+ // This brings window1 and its transient to the front.
+ root->StackChildAtTop(window1.get());
+ EXPECT_EQ("2 21 1 11", ChildWindowIDsAsString(GetContext()));
+
+ EXPECT_EQ(root->children()[0], window2.get());
+ EXPECT_EQ(root->children()[1], transient2.get());
+ EXPECT_EQ(root->children()[2], window1.get());
+ EXPECT_EQ(root->children()[3], transient1.get());
+ EXPECT_EQ(root->layer()->children()[0], window2->layer());
+ EXPECT_EQ(root->layer()->children()[1], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[2], window1->layer());
+ EXPECT_EQ(root->layer()->children()[3], transient1->layer());
+
+ // Pretend we're closing the top-most transient, then bring window2 to the
+ // front. This mimics activating a browser window while the status bubble
+ // is fading out. The transient should stay topmost.
+ transient1->layer()->set_delegate(NULL);
+ root->StackChildAtTop(window2.get());
+
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ EXPECT_EQ(root->children()[3], transient1.get());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[3], transient1->layer());
+
+ // Close the transient. Remaining windows are stable.
+ transient1.reset();
+
+ ASSERT_EQ(3u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ ASSERT_EQ(3u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+
+ // Open another window on top.
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, GetContext()));
+
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ EXPECT_EQ(root->children()[3], window3.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[3], window3->layer());
+
+ // Pretend we're closing the topmost non-transient window, then bring
+ // window2 to the top. It should not move.
+ window3->layer()->set_delegate(NULL);
+ root->StackChildAtTop(window2.get());
+
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window1.get());
+ EXPECT_EQ(root->children()[1], window2.get());
+ EXPECT_EQ(root->children()[2], transient2.get());
+ EXPECT_EQ(root->children()[3], window3.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window1->layer());
+ EXPECT_EQ(root->layer()->children()[1], window2->layer());
+ EXPECT_EQ(root->layer()->children()[2], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[3], window3->layer());
+
+ // Bring window1 to the top. It should move ahead of window2, but not
+ // ahead of window3 (with NULL delegate).
+ root->StackChildAtTop(window1.get());
+
+ ASSERT_EQ(4u, root->children().size());
+ EXPECT_EQ(root->children()[0], window2.get());
+ EXPECT_EQ(root->children()[1], transient2.get());
+ EXPECT_EQ(root->children()[2], window1.get());
+ EXPECT_EQ(root->children()[3], window3.get());
+ ASSERT_EQ(4u, root->layer()->children().size());
+ EXPECT_EQ(root->layer()->children()[0], window2->layer());
+ EXPECT_EQ(root->layer()->children()[1], transient2->layer());
+ EXPECT_EQ(root->layer()->children()[2], window1->layer());
+ EXPECT_EQ(root->layer()->children()[3], window3->layer());
+}
+
+} // namespace corewm
+} // namespace views
diff --git a/ui/views/corewm/transient_window_stacking_client.cc b/ui/views/corewm/transient_window_stacking_client.cc
index e3373ff..ef08cc0 100644
--- a/ui/views/corewm/transient_window_stacking_client.cc
+++ b/ui/views/corewm/transient_window_stacking_client.cc
@@ -6,6 +6,9 @@
#include <algorithm>
+#include "ui/views/corewm/transient_window_manager.h"
+#include "ui/views/corewm/window_util.h"
+
using aura::Window;
namespace views {
@@ -17,7 +20,7 @@ namespace {
// siblings of |window|. Returns true if any ancestors were found, false if not.
bool GetAllTransientAncestors(Window* window, Window::Windows* ancestors) {
Window* parent = window->parent();
- for (; window; window = window->transient_parent()) {
+ for (; window; window = GetTransientParent(window)) {
if (window->parent() == parent)
ancestors->push_back(window);
}
@@ -51,27 +54,53 @@ void FindCommonTransientAncestor(Window** window1, Window** window2) {
}
}
-// Returns true if |window| has |ancestor| as a transient ancestor. A transient
-// ancestor is found by following the transient parent chain of the window.
-bool HasTransientAncestor(const Window* window, const Window* ancestor) {
- if (window->transient_parent() == ancestor)
- return true;
- return window->transient_parent() ?
- HasTransientAncestor(window->transient_parent(), ancestor) : false;
+// Adjusts |target| so that we don't attempt to stack on top of a window with a
+// NULL delegate.
+void SkipNullDelegates(Window::StackDirection direction, Window** target) {
+ const Window::Windows& children((*target)->parent()->children());
+ size_t target_i =
+ std::find(children.begin(), children.end(), *target) -
+ children.begin();
+
+ // By convention we don't stack on top of windows with layers with NULL
+ // delegates. Walk backward to find a valid target window. See tests
+ // TransientWindowManagerTest.StackingMadrigal and StackOverClosingTransient
+ // for an explanation of this.
+ while (target_i > 0) {
+ const size_t index = direction == Window::STACK_ABOVE ?
+ target_i : target_i - 1;
+ if (!children[index]->layer() ||
+ children[index]->layer()->delegate() != NULL)
+ break;
+ --target_i;
+ }
+ *target = children[target_i];
}
} // namespace
+// static
+TransientWindowStackingClient* TransientWindowStackingClient::instance_ = NULL;
+
TransientWindowStackingClient::TransientWindowStackingClient() {
+ instance_ = this;
}
TransientWindowStackingClient::~TransientWindowStackingClient() {
+ if (instance_ == this)
+ instance_ = NULL;
}
bool TransientWindowStackingClient::AdjustStacking(
Window** child,
Window** target,
Window::StackDirection* direction) {
+ const TransientWindowManager* transient_manager =
+ TransientWindowManager::Get((*child)->parent());
+ if (transient_manager &&
+ transient_manager->IsStackingTransient(*child, *target))
+ return true;
+
// For windows that have transient children stack the transient ancestors that
// are siblings. This prevents one transient group from being inserted in the
// middle of another.
@@ -89,7 +118,16 @@ bool TransientWindowStackingClient::AdjustStacking(
}
*target = siblings[target_i];
}
- return true;
+
+ SkipNullDelegates(*direction, target);
+
+ // If we couldn't find a valid target position, don't move anything.
+ if (*direction == Window::STACK_ABOVE &&
+ ((*target)->layer() && (*target)->layer()->delegate() == NULL)) {
+ return false;
+ }
+
+ return *child != *target;
}
} // namespace corewm
diff --git a/ui/views/corewm/transient_window_stacking_client.h b/ui/views/corewm/transient_window_stacking_client.h
index f8c1982..04e0eaf 100644
--- a/ui/views/corewm/transient_window_stacking_client.h
+++ b/ui/views/corewm/transient_window_stacking_client.h
@@ -11,6 +11,8 @@
namespace views {
namespace corewm {
+class TransientWindowManager;
+
class VIEWS_EXPORT TransientWindowStackingClient
: public aura::client::WindowStackingClient {
public:
@@ -23,6 +25,11 @@ class VIEWS_EXPORT TransientWindowStackingClient
aura::Window::StackDirection* direction) OVERRIDE;
private:
+ // Purely for DCHECKs.
+ friend class TransientWindowManager;
+
+ static TransientWindowStackingClient* instance_;
+
DISALLOW_COPY_AND_ASSIGN(TransientWindowStackingClient);
};
diff --git a/ui/views/corewm/transient_window_stacking_client_unittest.cc b/ui/views/corewm/transient_window_stacking_client_unittest.cc
index 05d7b3e..c4570b7 100644
--- a/ui/views/corewm/transient_window_stacking_client_unittest.cc
+++ b/ui/views/corewm/transient_window_stacking_client_unittest.cc
@@ -7,6 +7,8 @@
#include "base/memory/scoped_ptr.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/test_windows.h"
+#include "ui/compositor/test/test_layers.h"
+#include "ui/views/corewm/window_util.h"
using aura::test::ChildWindowIDsAsString;
using aura::test::CreateTestWindowWithId;
@@ -49,12 +51,12 @@ TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupAbove) {
Window* w22 = CreateTestWindowWithId(22, parent.get());
ASSERT_EQ(8u, parent->children().size());
- w1->AddTransientChild(w11); // w11 is now owned by w1.
- w2->AddTransientChild(w21); // w21 is now owned by w2.
- w2->AddTransientChild(w22); // w22 is now owned by w2.
- w21->AddTransientChild(w211); // w211 is now owned by w21.
- w21->AddTransientChild(w212); // w212 is now owned by w21.
- w21->AddTransientChild(w213); // w213 is now owned by w21.
+ AddTransientChild(w1.get(), w11); // w11 is now owned by w1.
+ AddTransientChild(w2.get(), w21); // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w22); // w22 is now owned by w2.
+ AddTransientChild(w21, w211); // w211 is now owned by w21.
+ AddTransientChild(w21, w212); // w212 is now owned by w21.
+ AddTransientChild(w21, w213); // w213 is now owned by w21.
EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
// Stack w1 at the top (end), this should force w11 to be last (on top of w1).
@@ -119,12 +121,12 @@ TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupBelow) {
Window* w22 = CreateTestWindowWithId(22, parent.get());
ASSERT_EQ(8u, parent->children().size());
- w1->AddTransientChild(w11); // w11 is now owned by w1.
- w2->AddTransientChild(w21); // w21 is now owned by w2.
- w2->AddTransientChild(w22); // w22 is now owned by w2.
- w21->AddTransientChild(w211); // w211 is now owned by w21.
- w21->AddTransientChild(w212); // w212 is now owned by w21.
- w21->AddTransientChild(w213); // w213 is now owned by w21.
+ AddTransientChild(w1.get(), w11); // w11 is now owned by w1.
+ AddTransientChild(w2.get(), w21); // w21 is now owned by w2.
+ AddTransientChild(w2.get(), w22); // w22 is now owned by w2.
+ AddTransientChild(w21, w211); // w211 is now owned by w21.
+ AddTransientChild(w21, w212); // w212 is now owned by w21.
+ AddTransientChild(w21, w213); // w213 is now owned by w21.
EXPECT_EQ("1 11 2 21 211 212 213 22", ChildWindowIDsAsString(parent.get()));
// Stack w2 at the bottom, this should force w11 to be last (on top of w1).
@@ -173,5 +175,43 @@ TEST_F(TransientWindowStackingClientTest, TransientChildrenGroupBelow) {
EXPECT_EQ("2 22 21 213 211 212 1 11", ChildWindowIDsAsString(parent.get()));
}
+TEST_F(TransientWindowStackingClientTest,
+ StackWindowsWhoseLayersHaveNoDelegate) {
+ scoped_ptr<Window> window1(CreateTestWindowWithId(1, root_window()));
+ window1->layer()->set_name("1");
+ scoped_ptr<Window> window2(CreateTestWindowWithId(2, root_window()));
+ window2->layer()->set_name("2");
+ scoped_ptr<Window> window3(CreateTestWindowWithId(3, root_window()));
+ window3->layer()->set_name("3");
+
+ // This brings |window1| (and its layer) to the front.
+ root_window()->StackChildAbove(window1.get(), window3.get());
+ EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("2 3 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+
+ // Since |window1| does not have a delegate, |window2| should not move in
+ // front of it, nor should its layer.
+ window1->layer()->set_delegate(NULL);
+ root_window()->StackChildAbove(window2.get(), window1.get());
+ EXPECT_EQ("3 2 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("3 2 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+
+ // It should still be possible to stack |window3| immediately below |window1|.
+ root_window()->StackChildBelow(window3.get(), window1.get());
+ EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("2 3 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+
+ // Since neither |window3| nor |window1| have a delegate, |window2| should
+ // not move in front of either.
+ window3->layer()->set_delegate(NULL);
+ root_window()->StackChildBelow(window2.get(), window1.get());
+ EXPECT_EQ("2 3 1", ChildWindowIDsAsString(root_window()));
+ EXPECT_EQ("2 3 1",
+ ui::test::ChildLayerNamesAsString(*root_window()->layer()));
+}
+
} // namespace corewm
} // namespace views
diff --git a/ui/views/corewm/window_modality_controller.cc b/ui/views/corewm/window_modality_controller.cc
index b52a9d6..d87f088 100644
--- a/ui/views/corewm/window_modality_controller.cc
+++ b/ui/views/corewm/window_modality_controller.cc
@@ -63,13 +63,13 @@ bool IsModalTransientChild(aura::Window* transient, aura::Window* original) {
aura::Window* GetModalTransientChild(
aura::Window* activatable,
aura::Window* original) {
- aura::Window::Windows::const_iterator it;
- for (it = activatable->transient_children().begin();
- it != activatable->transient_children().end();
+ for (aura::Window::Windows::const_iterator it =
+ GetTransientChildren(activatable).begin();
+ it != GetTransientChildren(activatable).end();
++it) {
aura::Window* transient = *it;
if (IsModalTransientChild(transient, original)) {
- return transient->transient_children().empty() ?
+ return GetTransientChildren(transient).empty() ?
transient : GetModalTransientChild(transient, original);
}
}
@@ -153,8 +153,8 @@ void WindowModalityController::OnWindowPropertyChanged(aura::Window* window,
window->GetProperty(aura::client::kModalKey) != ui::MODAL_TYPE_NONE &&
window->IsVisible()) {
ActivateWindow(window);
- ui::GestureRecognizer::Get()->TransferEventsTo(
- window->transient_parent(), NULL);
+ ui::GestureRecognizer::Get()->TransferEventsTo(GetTransientParent(window),
+ NULL);
}
}
@@ -163,8 +163,8 @@ void WindowModalityController::OnWindowVisibilityChanged(
bool visible) {
if (visible && window->GetProperty(aura::client::kModalKey) !=
ui::MODAL_TYPE_NONE) {
- ui::GestureRecognizer::Get()->TransferEventsTo(
- window->transient_parent(), NULL);
+ ui::GestureRecognizer::Get()->TransferEventsTo(GetTransientParent(window),
+ NULL);
// Make sure no other window has capture, otherwise |window| won't get mouse
// events.
aura::Window* capture_window = aura::client::GetCaptureWindow(window);
diff --git a/ui/views/corewm/window_util.cc b/ui/views/corewm/window_util.cc
index eaee174..64a23d1 100644
--- a/ui/views/corewm/window_util.cc
+++ b/ui/views/corewm/window_util.cc
@@ -8,6 +8,7 @@
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer.h"
+#include "ui/views/corewm/transient_window_manager.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
@@ -137,5 +138,42 @@ ui::Layer* RecreateWindowLayers(aura::Window* window, bool set_bounds) {
return old_layer;
}
+aura::Window* GetTransientParent(aura::Window* window) {
+ return const_cast<aura::Window*>(GetTransientParent(
+ const_cast<const aura::Window*>(window)));
+}
+
+const aura::Window* GetTransientParent(const aura::Window* window) {
+ const TransientWindowManager* manager = TransientWindowManager::Get(window);
+ return manager ? manager->transient_parent() : NULL;
+}
+
+const std::vector<aura::Window*>& GetTransientChildren(
+ const aura::Window* window) {
+ const TransientWindowManager* manager = TransientWindowManager::Get(window);
+ if (manager)
+ return manager->transient_children();
+
+ static std::vector<aura::Window*>* shared = new std::vector<aura::Window*>;
+ return *shared;
+}
+
+void AddTransientChild(aura::Window* parent, aura::Window* child) {
+ TransientWindowManager::Get(parent)->AddTransientChild(child);
+}
+
+void RemoveTransientChild(aura::Window* parent, aura::Window* child) {
+ TransientWindowManager::Get(parent)->RemoveTransientChild(child);
+}
+
+bool HasTransientAncestor(const aura::Window* window,
+ const aura::Window* ancestor) {
+ const aura::Window* transient_parent = GetTransientParent(window);
+ if (transient_parent == ancestor)
+ return true;
+ return transient_parent ?
+ HasTransientAncestor(transient_parent, ancestor) : false;
+}
+
} // namespace corewm
} // namespace views
diff --git a/ui/views/corewm/window_util.h b/ui/views/corewm/window_util.h
index 01a3ea4..c3b552d 100644
--- a/ui/views/corewm/window_util.h
+++ b/ui/views/corewm/window_util.h
@@ -5,6 +5,8 @@
#ifndef UI_VIEWS_COREWM_WINDOW_UTIL_H_
#define UI_VIEWS_COREWM_WINDOW_UTIL_H_
+#include <vector>
+
#include "base/compiler_specific.h"
#include "ui/views/views_export.h"
@@ -47,6 +49,22 @@ VIEWS_EXPORT void DeepDeleteLayers(ui::Layer* layer);
VIEWS_EXPORT ui::Layer* RecreateWindowLayers(aura::Window* window,
bool set_bounds) WARN_UNUSED_RESULT;
+// Convenience functions that get the TransientWindowManager for the window and
+// redirect appropriately. These are preferable to calling functions on
+// TransientWindowManager as they handle the appropriate NULL checks.
+VIEWS_EXPORT aura::Window* GetTransientParent(aura::Window* window);
+VIEWS_EXPORT const aura::Window* GetTransientParent(const aura::Window* window);
+VIEWS_EXPORT const std::vector<aura::Window*>& GetTransientChildren(
+ const aura::Window* window);
+VIEWS_EXPORT void AddTransientChild(aura::Window* parent, aura::Window* child);
+VIEWS_EXPORT void RemoveTransientChild(aura::Window* parent,
+ aura::Window* child);
+
+// Returns true if |window| has |ancestor| as a transient ancestor. A transient
+// ancestor is found by following the transient parent chain of the window.
+VIEWS_EXPORT bool HasTransientAncestor(const aura::Window* window,
+ const aura::Window* ancestor);
+
} // namespace corewm
} // namespace views
diff --git a/ui/views/corewm/wm_state.cc b/ui/views/corewm/wm_state.cc
index 0799247..16d9e7a 100644
--- a/ui/views/corewm/wm_state.cc
+++ b/ui/views/corewm/wm_state.cc
@@ -4,19 +4,27 @@
#include "ui/views/corewm/wm_state.h"
+#include "ui/views/corewm/transient_window_controller.h"
#include "ui/views/corewm/transient_window_stacking_client.h"
namespace views {
namespace corewm {
WMState::WMState()
- : window_stacking_client_(new TransientWindowStackingClient) {
+ : window_stacking_client_(new TransientWindowStackingClient),
+ transient_window_client_(new TransientWindowController) {
aura::client::SetWindowStackingClient(window_stacking_client_.get());
+ aura::client::SetTransientWindowClient(transient_window_client_.get());
}
WMState::~WMState() {
if (aura::client::GetWindowStackingClient() == window_stacking_client_.get())
aura::client::SetWindowStackingClient(NULL);
+
+ if (aura::client::GetTransientWindowClient() ==
+ transient_window_client_.get()) {
+ aura::client::SetTransientWindowClient(NULL);
+ }
}
} // namespace corewm
diff --git a/ui/views/corewm/wm_state.h b/ui/views/corewm/wm_state.h
index 1e0ee4f..e8a09f2 100644
--- a/ui/views/corewm/wm_state.h
+++ b/ui/views/corewm/wm_state.h
@@ -11,6 +11,7 @@
namespace views {
namespace corewm {
+class TransientWindowController;
class TransientWindowStackingClient;
// Installs state needed by the window manager.
@@ -22,6 +23,7 @@ class VIEWS_EXPORT WMState {
// WindowStackingClient:
private:
scoped_ptr<TransientWindowStackingClient> window_stacking_client_;
+ scoped_ptr<TransientWindowController> transient_window_client_;
DISALLOW_COPY_AND_ASSIGN(WMState);
};
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index ce8e690..3cfd592 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -263,6 +263,10 @@
'corewm/tooltip_controller.h',
'corewm/tooltip_win.cc',
'corewm/tooltip_win.h',
+ 'corewm/transient_window_controller.cc',
+ 'corewm/transient_window_controller.h',
+ 'corewm/transient_window_manager.cc',
+ 'corewm/transient_window_manager.h',
'corewm/transient_window_stacking_client.cc',
'corewm/transient_window_stacking_client.h',
'corewm/visibility_controller.cc',
@@ -768,6 +772,7 @@
'corewm/shadow_controller_unittest.cc',
'corewm/tooltip_aura_unittest.cc',
'corewm/tooltip_controller_unittest.cc',
+ 'corewm/transient_window_manager_unittest.cc',
'corewm/transient_window_stacking_client_unittest.cc',
'corewm/visibility_controller_unittest.cc',
'corewm/window_animations_unittest.cc',
diff --git a/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc b/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc
index b4541d0..00e3f6d 100644
--- a/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc
@@ -37,6 +37,7 @@
#include "ui/views/corewm/compound_event_filter.h"
#include "ui/views/corewm/corewm_switches.h"
#include "ui/views/corewm/tooltip_aura.h"
+#include "ui/views/corewm/window_util.h"
#include "ui/views/ime/input_method.h"
#include "ui/views/linux_ui/linux_ui.h"
#include "ui/views/views_delegate.h"
@@ -241,8 +242,9 @@ void DesktopRootWindowHostX11::OnRootWindowCreated(
// If we're given a parent, we need to mark ourselves as transient to another
// window. Otherwise activation gets screwy.
gfx::NativeView parent = params.parent;
- if (!params.child && params.parent)
- parent->AddTransientChild(content_window_);
+ if (!params.child && params.parent) {
+ corewm::AddTransientChild(parent, content_window_);
+ }
// Ensure that the X11DesktopHandler exists so that it dispatches activation
// messages to us.
@@ -366,9 +368,9 @@ void DesktopRootWindowHostX11::CenterWindow(const gfx::Size& size) {
// If |window_|'s transient parent bounds are big enough to contain |size|,
// use them instead.
- if (content_window_->transient_parent()) {
+ if (corewm::GetTransientParent(content_window_)) {
gfx::Rect transient_parent_rect =
- content_window_->transient_parent()->GetBoundsInScreen();
+ corewm::GetTransientParent(content_window_)->GetBoundsInScreen();
if (transient_parent_rect.height() >= size.height() &&
transient_parent_rect.width() >= size.width()) {
parent_bounds = transient_parent_rect;
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index 1f97ac1..d2ec6cf 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -27,6 +27,7 @@
#include "ui/gfx/font.h"
#include "ui/gfx/screen.h"
#include "ui/native_theme/native_theme_aura.h"
+#include "ui/views/corewm/window_util.h"
#include "ui/views/drag_utils.h"
#include "ui/views/ime/input_method_bridge.h"
#include "ui/views/views_delegate.h"
@@ -131,7 +132,7 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) {
// Set up the transient child before the window is added. This way the
// LayoutManager knows the window has a transient parent.
if (parent && parent->type() != ui::wm::WINDOW_TYPE_UNKNOWN) {
- parent->AddTransientChild(window_);
+ corewm::AddTransientChild(parent, window_);
if (!context)
context = parent;
parent = NULL;
@@ -310,9 +311,9 @@ void NativeWidgetAura::CenterWindow(const gfx::Size& size) {
// If |window_|'s transient parent's bounds are big enough to fit it, then we
// center it with respect to the transient parent.
- if (window_->transient_parent()) {
- gfx::Rect transient_parent_rect = window_->transient_parent()->
- GetBoundsInRootWindow();
+ if (views::corewm::GetTransientParent(window_)) {
+ gfx::Rect transient_parent_rect =
+ views::corewm::GetTransientParent(window_)->GetBoundsInRootWindow();
transient_parent_rect.Intersect(work_area);
if (transient_parent_rect.height() >= size.height() &&
transient_parent_rect.width() >= size.width())
@@ -1082,7 +1083,7 @@ void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view,
Widget::Widgets* owned) {
const aura::Window::Windows& transient_children =
- native_view->transient_children();
+ views::corewm::GetTransientChildren(native_view);
for (aura::Window::Windows::const_iterator i = transient_children.begin();
i != transient_children.end(); ++i) {
NativeWidgetPrivate* native_widget = static_cast<NativeWidgetPrivate*>(
diff --git a/ui/wm/core/easy_resize_window_targeter.cc b/ui/wm/core/easy_resize_window_targeter.cc
index f3a2b0a..1f0e90a 100644
--- a/ui/wm/core/easy_resize_window_targeter.cc
+++ b/ui/wm/core/easy_resize_window_targeter.cc
@@ -4,6 +4,7 @@
#include "ui/wm/public/easy_resize_window_targeter.h"
+#include "ui/aura/client/transient_window_client.h"
#include "ui/aura/window.h"
#include "ui/gfx/geometry/insets_f.h"
#include "ui/gfx/geometry/rect.h"
@@ -25,10 +26,7 @@ EasyResizeWindowTargeter::~EasyResizeWindowTargeter() {
bool EasyResizeWindowTargeter::EventLocationInsideBounds(
aura::Window* window,
const ui::LocatedEvent& event) const {
- // Use the extended bounds only for immediate child windows of |container_|.
- // Use the default targetter otherwise.
- if (window->parent() == container_ && (!window->transient_parent() ||
- window->transient_parent() == container_)) {
+ if (ShouldUseExtendedBounds(window)) {
gfx::RectF bounds(window->bounds());
gfx::Transform transform = window->layer()->transform();
transform.TransformRect(&bounds);
@@ -45,4 +43,18 @@ bool EasyResizeWindowTargeter::EventLocationInsideBounds(
return WindowTargeter::EventLocationInsideBounds(window, event);
}
+bool EasyResizeWindowTargeter::ShouldUseExtendedBounds(
+ const aura::Window* window) const {
+ // Use the extended bounds only for immediate child windows of |container_|.
+ // Use the default targetter otherwise.
+ if (window->parent() != container_)
+ return false;
+
+ aura::client::TransientWindowClient* transient_window_client =
+ aura::client::GetTransientWindowClient();
+ return !transient_window_client ||
+ !transient_window_client->GetTransientParent(window) ||
+ transient_window_client->GetTransientParent(window) == container_;
+}
+
} // namespace wm
diff --git a/ui/wm/public/easy_resize_window_targeter.h b/ui/wm/public/easy_resize_window_targeter.h
index c4c7137..f873509 100644
--- a/ui/wm/public/easy_resize_window_targeter.h
+++ b/ui/wm/public/easy_resize_window_targeter.h
@@ -36,6 +36,10 @@ class EasyResizeWindowTargeter : public aura::WindowTargeter {
const ui::LocatedEvent& event) const OVERRIDE;
private:
+ // Returns true if the hit testing (EventLocationInsideBounds()) should use
+ // the extended bounds.
+ bool ShouldUseExtendedBounds(const aura::Window* window) const;
+
aura::Window* container_;
gfx::Insets mouse_extend_;
gfx::Insets touch_extend_;