diff options
57 files changed, 1356 insertions, 677 deletions
diff --git a/ash/display/screen_position_controller.cc b/ash/display/screen_position_controller.cc index 624a0cd..5b45b7c 100644 --- a/ash/display/screen_position_controller.cc +++ b/ash/display/screen_position_controller.cc @@ -19,6 +19,7 @@ #include "ui/compositor/dip_util.h" #include "ui/gfx/display.h" #include "ui/gfx/screen.h" +#include "ui/views/corewm/window_util.h" namespace ash { namespace { @@ -37,7 +38,8 @@ void MoveAllTransientChildrenToNewRoot(const gfx::Display& display, aura::Window* window) { aura::Window* dst_root = Shell::GetInstance()->display_controller()-> GetRootWindowForDisplayId(display.id()); - aura::Window::Windows transient_children = window->transient_children(); + aura::Window::Windows transient_children = + views::corewm::GetTransientChildren(window); for (aura::Window::Windows::iterator iter = transient_children.begin(); iter != transient_children.end(); ++iter) { aura::Window* transient_child = *iter; @@ -162,7 +164,7 @@ void ScreenPositionController::SetBounds(aura::Window* window, // b) if the window or its ancestor has kStayInSameRootWindowkey. It's // intentionally kept in the same root window even if the bounds is // outside of the display. - if (!window->transient_parent() && + if (!views::corewm::GetTransientParent(window) && !ShouldStayInSameRootWindow(window)) { aura::Window* dst_root = Shell::GetInstance()->display_controller()->GetRootWindowForDisplayId( diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc index 1af782d..330b8d4 100644 --- a/ash/root_window_controller.cc +++ b/ash/root_window_controller.cc @@ -65,6 +65,7 @@ #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/corewm/capture_controller.h" #include "ui/views/corewm/visibility_controller.h" +#include "ui/views/corewm/window_util.h" #include "ui/views/view_model.h" #include "ui/views/view_model_utils.h" #include "ui/wm/public/easy_resize_window_targeter.h" @@ -589,7 +590,7 @@ const aura::Window* RootWindowController::GetWindowForFullscreenMode() const { while (topmost_window) { if (wm::GetWindowState(topmost_window)->IsFullscreen()) return topmost_window; - topmost_window = topmost_window->transient_parent(); + topmost_window = views::corewm::GetTransientParent(topmost_window); } return NULL; } diff --git a/ash/wm/dock/docked_window_layout_manager.cc b/ash/wm/dock/docked_window_layout_manager.cc index 40088a4..0a648a0 100644 --- a/ash/wm/dock/docked_window_layout_manager.cc +++ b/ash/wm/dock/docked_window_layout_manager.cc @@ -37,6 +37,7 @@ #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/rect.h" #include "ui/views/background.h" +#include "ui/views/corewm/window_util.h" namespace ash { namespace internal { @@ -189,7 +190,7 @@ namespace { // Returns true if a window is a popup or a transient child. bool IsPopupOrTransient(const aura::Window* window) { return (window->type() == ui::wm::WINDOW_TYPE_POPUP || - window->transient_parent()); + views::corewm::GetTransientParent(window)); } // Certain windows (minimized, hidden or popups) do not matter to docking. diff --git a/ash/wm/dock/docked_window_resizer_unittest.cc b/ash/wm/dock/docked_window_resizer_unittest.cc index b15117d..05cb9b3 100644 --- a/ash/wm/dock/docked_window_resizer_unittest.cc +++ b/ash/wm/dock/docked_window_resizer_unittest.cc @@ -32,6 +32,7 @@ #include "ui/aura/test/test_window_delegate.h" #include "ui/base/hit_test.h" #include "ui/base/ui_base_types.h" +#include "ui/views/corewm/window_util.h" #include "ui/views/widget/widget.h" namespace ash { @@ -1295,10 +1296,10 @@ TEST_P(DockedWindowResizerTest, DragWindowWithTransientChild) { scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201))); scoped_ptr<aura::Window> child(CreateTestWindowInShellWithDelegateAndType( NULL, ui::wm::WINDOW_TYPE_NORMAL, 0, gfx::Rect(20, 20, 150, 20))); - window->AddTransientChild(child.get()); + views::corewm::AddTransientChild(window.get(), child.get()); if (window->parent() != child->parent()) window->parent()->AddChild(child.get()); - EXPECT_EQ(window.get(), child->transient_parent()); + EXPECT_EQ(window.get(), views::corewm::GetTransientParent(child.get())); DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, window.get(), 20); @@ -1345,8 +1346,8 @@ TEST_P(DockedWindowResizerTest, DragWindowWithModalTransientChild) { // While still dragging create a modal window and make it a transient child of // the |window|. scoped_ptr<aura::Window> child(CreateModalWindow(gfx::Rect(20, 20, 150, 20))); - window->AddTransientChild(child.get()); - EXPECT_EQ(window.get(), child->transient_parent()); + views::corewm::AddTransientChild(window.get(), child.get()); + EXPECT_EQ(window.get(), views::corewm::GetTransientParent(child.get())); EXPECT_EQ(internal::kShellWindowId_SystemModalContainer, child->parent()->id()); @@ -1366,7 +1367,7 @@ TEST_P(DockedWindowResizerTest, DragWindowWithModalTransientChild) { EXPECT_EQ(gfx::Point(20, 20).ToString(), child->GetBoundsInScreen().origin().ToString()); // The |child| should still be a transient child of |window|. - EXPECT_EQ(window.get(), child->transient_parent()); + EXPECT_EQ(window.get(), views::corewm::GetTransientParent(child.get())); } // Tests that side snapping a window undocks it, closes the dock and then snaps. diff --git a/ash/wm/drag_window_resizer.cc b/ash/wm/drag_window_resizer.cc index 5b74fcb..2a859cf 100644 --- a/ash/wm/drag_window_resizer.cc +++ b/ash/wm/drag_window_resizer.cc @@ -22,6 +22,7 @@ #include "ui/base/hit_test.h" #include "ui/base/ui_base_types.h" #include "ui/gfx/screen.h" +#include "ui/views/corewm/window_util.h" namespace ash { namespace internal { @@ -232,9 +233,9 @@ void DragWindowResizer::UpdateDragWindow(const gfx::Rect& bounds, bool DragWindowResizer::ShouldAllowMouseWarp() { return (details_.window_component == HTCAPTION) && - !GetTarget()->transient_parent() && - (GetTarget()->type() == ui::wm::WINDOW_TYPE_NORMAL || - GetTarget()->type() == ui::wm::WINDOW_TYPE_PANEL); + !views::corewm::GetTransientParent(GetTarget()) && + (GetTarget()->type() == ui::wm::WINDOW_TYPE_NORMAL || + GetTarget()->type() == ui::wm::WINDOW_TYPE_PANEL); } TrayUser* DragWindowResizer::GetTrayUserItemAtPoint( @@ -245,7 +246,7 @@ TrayUser* DragWindowResizer::GetTrayUserItemAtPoint( // Check that this is a drag move operation from a suitable window. if (details_.window_component != HTCAPTION || - GetTarget()->transient_parent() || + views::corewm::GetTransientParent(GetTarget()) || (GetTarget()->type() != ui::wm::WINDOW_TYPE_NORMAL && GetTarget()->type() != ui::wm::WINDOW_TYPE_PANEL && GetTarget()->type() != ui::wm::WINDOW_TYPE_POPUP)) diff --git a/ash/wm/drag_window_resizer_unittest.cc b/ash/wm/drag_window_resizer_unittest.cc index c2bd1df..0514ad5 100644 --- a/ash/wm/drag_window_resizer_unittest.cc +++ b/ash/wm/drag_window_resizer_unittest.cc @@ -22,6 +22,7 @@ #include "ui/base/ui_base_types.h" #include "ui/gfx/insets.h" #include "ui/gfx/screen.h" +#include "ui/views/corewm/window_util.h" #include "ui/views/widget/widget.h" #if defined(OS_CHROMEOS) @@ -84,7 +85,7 @@ class DragWindowResizerTest : public test::AshTestBase { transient_parent_->SetType(ui::wm::WINDOW_TYPE_NORMAL); transient_parent_->Init(ui::LAYER_NOT_DRAWN); ParentWindowInPrimaryRootWindow(transient_parent_.get()); - transient_parent_->AddTransientChild(transient_child_); + views::corewm::AddTransientChild(transient_parent_.get(), transient_child_); transient_parent_->set_id(5); panel_window_.reset(new aura::Window(&delegate6_)); diff --git a/ash/wm/immersive_fullscreen_controller.cc b/ash/wm/immersive_fullscreen_controller.cc index 701096e..14eee9d 100644 --- a/ash/wm/immersive_fullscreen_controller.cc +++ b/ash/wm/immersive_fullscreen_controller.cc @@ -23,6 +23,7 @@ #include "ui/gfx/rect.h" #include "ui/gfx/screen.h" #include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/corewm/window_util.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" @@ -88,7 +89,7 @@ bool IsWindowTransientChildOf(aura::Window* maybe_transient, return false; for (aura::Window* window = maybe_transient; window; - window = window->transient_parent()) { + window = views::corewm::GetTransientParent(window)) { if (window == toplevel) return true; } @@ -472,8 +473,9 @@ void ImmersiveFullscreenController::AnimationProgressed( //////////////////////////////////////////////////////////////////////////////// // aura::WindowObserver overrides: -void ImmersiveFullscreenController::OnAddTransientChild(aura::Window* window, - aura::Window* transient) { +void ImmersiveFullscreenController::OnAddTransientChild( + aura::Window* window, + aura::Window* transient) { views::BubbleDelegateView* bubble_delegate = AsBubbleDelegate(transient); if (bubble_delegate && bubble_delegate->GetAnchorView() && @@ -915,7 +917,7 @@ bool ImmersiveFullscreenController::ShouldHandleGestureEvent( void ImmersiveFullscreenController::RecreateBubbleManager() { bubble_manager_.reset(new BubbleManager(this)); const std::vector<aura::Window*> transient_children = - native_window_->transient_children(); + views::corewm::GetTransientChildren(native_window_); for (size_t i = 0; i < transient_children.size(); ++i) { aura::Window* transient_child = transient_children[i]; views::BubbleDelegateView* bubble_delegate = diff --git a/ash/wm/mru_window_tracker.cc b/ash/wm/mru_window_tracker.cc index 4ed9a1d..af703e9 100644 --- a/ash/wm/mru_window_tracker.cc +++ b/ash/wm/mru_window_tracker.cc @@ -190,7 +190,10 @@ void MruWindowTracker::OnWindowActivated(aura::Window* gained_active, SetActiveWindow(gained_active); } -void MruWindowTracker::OnWindowDestroying(aura::Window* window) { +void MruWindowTracker::OnWindowDestroyed(aura::Window* window) { + // It's possible for OnWindowActivated() to be called after + // OnWindowDestroying(). This means we need to override OnWindowDestroyed() + // else we may end up with a deleted window in |mru_windows_|. mru_windows_.remove(window); window->RemoveObserver(this); } diff --git a/ash/wm/mru_window_tracker.h b/ash/wm/mru_window_tracker.h index 1569a98..3f8740d 100644 --- a/ash/wm/mru_window_tracker.h +++ b/ash/wm/mru_window_tracker.h @@ -71,7 +71,7 @@ class ASH_EXPORT MruWindowTracker aura::Window* lost_active) OVERRIDE; // Overridden from WindowObserver: - virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; // List of windows that have been activated in containers that we cycle // through, sorted by most recently used. diff --git a/ash/wm/overview/scoped_transform_overview_window.cc b/ash/wm/overview/scoped_transform_overview_window.cc index 507df0c..12b7c0c6 100644 --- a/ash/wm/overview/scoped_transform_overview_window.cc +++ b/ash/wm/overview/scoped_transform_overview_window.cc @@ -14,6 +14,7 @@ #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/animation/tween.h" #include "ui/views/corewm/window_animations.h" +#include "ui/views/corewm/window_util.h" #include "ui/views/widget/widget.h" namespace ash { @@ -63,7 +64,8 @@ void SetTransformOnWindowAndAllTransientChildren( bool animate) { SetTransformOnWindow(window, transform, animate); - aura::Window::Windows transient_children = window->transient_children(); + aura::Window::Windows transient_children = + views::corewm::GetTransientChildren(window); for (aura::Window::Windows::iterator iter = transient_children.begin(); iter != transient_children.end(); ++iter) { aura::Window* transient_child = *iter; @@ -78,7 +80,7 @@ void SetTransformOnWindowAndAllTransientChildren( aura::Window* GetModalTransientParent(aura::Window* window) { if (window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW) - return window->transient_parent(); + return views::corewm::GetTransientParent(window); return NULL; } @@ -237,8 +239,8 @@ void ScopedTransformOverviewWindow::SetTransformOnWindowAndTransientChildren( bool animate) { gfx::Point origin(GetBoundsInScreen().origin()); aura::Window* window = window_; - while (window->transient_parent()) - window = window->transient_parent(); + while (views::corewm::GetTransientParent(window)) + window = views::corewm::GetTransientParent(window); for (ScopedVector<ScopedWindowCopy>::const_iterator iter = window_copies_.begin(); iter != window_copies_.end(); ++iter) { SetTransformOnWindow( diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc index 56ea455..aa35185 100644 --- a/ash/wm/overview/window_selector.cc +++ b/ash/wm/overview/window_selector.cc @@ -27,6 +27,7 @@ #include "ui/aura/window_observer.h" #include "ui/events/event.h" #include "ui/events/event_handler.h" +#include "ui/views/corewm/window_util.h" namespace ash { @@ -400,7 +401,7 @@ void WindowSelector::OnWindowAdded(aura::Window* new_window) { for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) { if (new_window->parent()->id() == kSwitchableWindowContainerIds[i] && - !new_window->transient_parent()) { + !views::corewm::GetTransientParent(new_window)) { // The new window is in one of the switchable containers, abort overview. CancelSelection(); return; diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc index 86c0742..52bca15 100644 --- a/ash/wm/overview/window_selector_unittest.cc +++ b/ash/wm/overview/window_selector_unittest.cc @@ -33,6 +33,7 @@ #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/gfx/rect_conversions.h" #include "ui/gfx/transform.h" +#include "ui/views/corewm/window_util.h" namespace ash { namespace internal { @@ -692,7 +693,7 @@ TEST_F(WindowSelectorTest, ModalChild) { scoped_ptr<aura::Window> window1(CreateWindow(bounds)); scoped_ptr<aura::Window> child1(CreateWindow(bounds)); child1->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); - window1->AddTransientChild(child1.get()); + views::corewm::AddTransientChild(window1.get(), child1.get()); EXPECT_EQ(window1->parent(), child1->parent()); ToggleOverview(); EXPECT_TRUE(window1->IsVisible()); @@ -708,7 +709,7 @@ TEST_F(WindowSelectorTest, ClickModalWindowParent) { scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(0, 0, 180, 180))); scoped_ptr<aura::Window> child1(CreateWindow(gfx::Rect(200, 0, 180, 180))); child1->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); - window1->AddTransientChild(child1.get()); + views::corewm::AddTransientChild(window1.get(), child1.get()); EXPECT_FALSE(WindowsOverlapping(window1.get(), child1.get())); EXPECT_EQ(window1->parent(), child1->parent()); ToggleOverview(); @@ -829,7 +830,7 @@ TEST_F(WindowSelectorTest, CycleMultipleDisplaysCopiesWindows) { unmoved2->SetName("unmoved2"); moved1->SetName("moved1"); moved1->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); - moved1_trans_parent->AddTransientChild(moved1.get()); + views::corewm::AddTransientChild(moved1_trans_parent.get(), moved1.get()); moved1_trans_parent->SetName("moved1_trans_parent"); EXPECT_EQ(root_windows[0], moved1->GetRootWindow()); diff --git a/ash/wm/panels/panel_window_resizer_unittest.cc b/ash/wm/panels/panel_window_resizer_unittest.cc index 68477ab..3fdddf1 100644 --- a/ash/wm/panels/panel_window_resizer_unittest.cc +++ b/ash/wm/panels/panel_window_resizer_unittest.cc @@ -26,6 +26,7 @@ #include "ui/base/hit_test.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/ui_base_types.h" +#include "ui/views/corewm/window_util.h" #include "ui/views/widget/widget.h" namespace ash { @@ -470,10 +471,10 @@ TEST_P(PanelWindowResizerTransientTest, PanelWithTransientChild) { scoped_ptr<aura::Window> window(CreatePanelWindow(gfx::Point(0, 0))); scoped_ptr<aura::Window> child(CreateTestWindowInShellWithDelegateAndType( NULL, transient_window_type_, 0, gfx::Rect(20, 20, 150, 40))); - window->AddTransientChild(child.get()); + views::corewm::AddTransientChild(window.get(), child.get()); if (window->parent() != child->parent()) window->parent()->AddChild(child.get()); - EXPECT_EQ(window.get(), child->transient_parent()); + EXPECT_EQ(window.get(), views::corewm::GetTransientParent(child.get())); // Drag the child to the shelf. Its new position should not be overridden. const gfx::Rect attached_bounds(window->GetBoundsInScreen()); diff --git a/ash/wm/stacking_controller.cc b/ash/wm/stacking_controller.cc index 570c8f3..6603181 100644 --- a/ash/wm/stacking_controller.cc +++ b/ash/wm/stacking_controller.cc @@ -15,6 +15,7 @@ #include "ui/aura/root_window.h" #include "ui/aura/window.h" #include "ui/base/ui_base_types.h" +#include "ui/views/corewm/window_util.h" namespace ash { namespace { @@ -37,9 +38,10 @@ bool IsSystemModal(aura::Window* window) { return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM; } -bool HasTransientParentWindow(aura::Window* window) { - return window->transient_parent() && - window->transient_parent()->type() != ui::wm::WINDOW_TYPE_UNKNOWN; +bool HasTransientParentWindow(const aura::Window* window) { + return views::corewm::GetTransientParent(window) && + views::corewm::GetTransientParent(window)->type() != + ui::wm::WINDOW_TYPE_UNKNOWN; } internal::AlwaysOnTopController* @@ -66,9 +68,10 @@ aura::Window* StackingController::GetDefaultParent(aura::Window* context, aura::Window* window, const gfx::Rect& bounds) { aura::Window* target_root = NULL; - if (window->transient_parent()) { + aura::Window* transient_parent = views::corewm::GetTransientParent(window); + if (transient_parent) { // Transient window should use the same root as its transient parent. - target_root = window->transient_parent()->GetRootWindow(); + target_root = transient_parent->GetRootWindow(); } else { target_root = FindContainerRoot(bounds); } @@ -80,7 +83,7 @@ aura::Window* StackingController::GetDefaultParent(aura::Window* context, return GetSystemModalContainer(target_root, window); else if (HasTransientParentWindow(window)) return internal::RootWindowController::GetContainerForWindow( - window->transient_parent()); + views::corewm::GetTransientParent(window)); return GetAlwaysOnTopController(target_root)->GetContainer(window); case ui::wm::WINDOW_TYPE_CONTROL: return GetContainerById( @@ -120,14 +123,15 @@ aura::Window* StackingController::GetSystemModalContainer( SessionStateDelegate* session_state_delegate = Shell::GetInstance()->session_state_delegate(); if (!session_state_delegate->IsUserSessionBlocked() || - !window->transient_parent()) { + !views::corewm::GetTransientParent(window)) { return GetContainerById(root, internal::kShellWindowId_SystemModalContainer); } // Otherwise those that originate from LockScreen container and above are // placed in the screen lock modal container. - int window_container_id = window->transient_parent()->parent()->id(); + int window_container_id = + views::corewm::GetTransientParent(window)->parent()->id(); aura::Window* container = NULL; if (window_container_id < internal::kShellWindowId_LockScreenContainer) { container = GetContainerById( diff --git a/ash/wm/stacking_controller_unittest.cc b/ash/wm/stacking_controller_unittest.cc index 31ec8ff..6f7b35d 100644 --- a/ash/wm/stacking_controller_unittest.cc +++ b/ash/wm/stacking_controller_unittest.cc @@ -10,6 +10,7 @@ #include "ui/aura/client/aura_constants.h" #include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/views/corewm/window_util.h" using aura::Window; @@ -48,7 +49,7 @@ TEST_F(StackingControllerTest, TransientParent) { // Window with a transient parent. scoped_ptr<Window> w1(CreateTestWindow()); - w2->AddTransientChild(w1.get()); + views::corewm::AddTransientChild(w2.get(), w1.get()); w1->SetBounds(gfx::Rect(10, 11, 250, 251)); ParentWindowInPrimaryRootWindow(w1.get()); w1->Show(); diff --git a/ash/wm/system_modal_container_layout_manager_unittest.cc b/ash/wm/system_modal_container_layout_manager_unittest.cc index 6ae3369..e66678a 100644 --- a/ash/wm/system_modal_container_layout_manager_unittest.cc +++ b/ash/wm/system_modal_container_layout_manager_unittest.cc @@ -17,6 +17,7 @@ #include "ui/aura/window.h" #include "ui/compositor/layer.h" #include "ui/gfx/screen.h" +#include "ui/views/corewm/window_util.h" #include "ui/views/test/capture_tracking_view.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -161,7 +162,7 @@ TEST_F(SystemModalContainerLayoutManagerTest, NonModalTransient) { TransientWindowObserver destruction_observer; transient->AddObserver(&destruction_observer); - EXPECT_EQ(parent.get(), transient->transient_parent()); + EXPECT_EQ(parent.get(), views::corewm::GetTransientParent(transient)); EXPECT_EQ(parent->parent(), transient->parent()); // The transient should be destroyed with its parent. @@ -178,7 +179,7 @@ TEST_F(SystemModalContainerLayoutManagerTest, ModalTransient) { TransientWindowObserver do1; t1->AddObserver(&do1); - EXPECT_EQ(parent.get(), t1->transient_parent()); + EXPECT_EQ(parent.get(), views::corewm::GetTransientParent(t1)); EXPECT_EQ(GetModalContainer(), t1->parent()); // t1 should now be active. @@ -196,7 +197,7 @@ TEST_F(SystemModalContainerLayoutManagerTest, ModalTransient) { EXPECT_TRUE(wm::IsActiveWindow(t2)); - EXPECT_EQ(t1, t2->transient_parent()); + EXPECT_EQ(t1, views::corewm::GetTransientParent(t2)); EXPECT_EQ(GetModalContainer(), t2->parent()); // t2 should still be active, even after clicking on t1. @@ -217,7 +218,7 @@ TEST_F(SystemModalContainerLayoutManagerTest, ModalNonTransient) { TransientWindowObserver do1; t1->AddObserver(&do1); - EXPECT_EQ(NULL, t1->transient_parent()); + EXPECT_EQ(NULL, views::corewm::GetTransientParent(t1.get())); EXPECT_EQ(GetModalContainer(), t1->parent()); // t1 should now be active. @@ -236,7 +237,7 @@ TEST_F(SystemModalContainerLayoutManagerTest, ModalNonTransient) { EXPECT_TRUE(wm::IsActiveWindow(t2)); - EXPECT_EQ(t1, t2->transient_parent()); + EXPECT_EQ(t1, views::corewm::GetTransientParent(t2)); EXPECT_EQ(GetModalContainer(), t2->parent()); // t2 should still be active, even after clicking on t1. diff --git a/ash/wm/window_modality_controller_unittest.cc b/ash/wm/window_modality_controller_unittest.cc index 7e36669..388a7cd 100644 --- a/ash/wm/window_modality_controller_unittest.cc +++ b/ash/wm/window_modality_controller_unittest.cc @@ -14,6 +14,7 @@ #include "ui/aura/test/test_windows.h" #include "ui/aura/window.h" #include "ui/base/ui_base_types.h" +#include "ui/views/corewm/window_util.h" #include "ui/views/test/capture_tracking_view.h" #include "ui/views/test/child_modal_window.h" #include "ui/views/widget/widget.h" @@ -50,14 +51,14 @@ TEST_F(WindowModalityControllerTest, BasicActivation) { scoped_ptr<aura::Window> w12( CreateTestWindowInShellWithDelegate(&d, -12, gfx::Rect())); - w1->AddTransientChild(w11.get()); + views::corewm::AddTransientChild(w1.get(), w11.get()); wm::ActivateWindow(w1.get()); EXPECT_TRUE(wm::IsActiveWindow(w1.get())); wm::ActivateWindow(w11.get()); EXPECT_TRUE(wm::IsActiveWindow(w11.get())); w12->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); - w1->AddTransientChild(w12.get()); + views::corewm::AddTransientChild(w1.get(), w12.get()); wm::ActivateWindow(w12.get()); EXPECT_TRUE(wm::IsActiveWindow(w12.get())); @@ -97,8 +98,8 @@ TEST_F(WindowModalityControllerTest, NestedModals) { scoped_ptr<aura::Window> w2( CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect())); - w1->AddTransientChild(w11.get()); - w11->AddTransientChild(w111.get()); + views::corewm::AddTransientChild(w1.get(), w11.get()); + views::corewm::AddTransientChild(w11.get(), w111.get()); wm::ActivateWindow(w1.get()); EXPECT_TRUE(wm::IsActiveWindow(w1.get())); @@ -151,8 +152,8 @@ TEST_F(WindowModalityControllerTest, NestedModalsOuterClosed) { scoped_ptr<aura::Window> w2( CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect())); - w1->AddTransientChild(w11.get()); - w11->AddTransientChild(w111); + views::corewm::AddTransientChild(w1.get(), w11.get()); + views::corewm::AddTransientChild(w11.get(), w111); wm::ActivateWindow(w1.get()); EXPECT_TRUE(wm::IsActiveWindow(w1.get())); @@ -185,7 +186,7 @@ TEST_F(WindowModalityControllerTest, Events) { scoped_ptr<aura::Window> w11(CreateTestWindowInShellWithDelegate(&d, -11, gfx::Rect(20, 20, 50, 50))); - w1->AddTransientChild(w11.get()); + views::corewm::AddTransientChild(w1.get(), w11.get()); { // Clicking a point within w1 should activate that window. @@ -224,7 +225,7 @@ TEST_F(WindowModalityControllerTest, GetModalTransient) { ASSERT_EQ(static_cast<aura::Window*>(NULL), wt); // Parent w2 to w1. It should get parented to the parent of w1. - w1->AddTransientChild(w2.get()); + views::corewm::AddTransientChild(w1.get(), w2.get()); ASSERT_EQ(2U, w1->parent()->children().size()); EXPECT_EQ(-2, w1->parent()->children().at(1)->id()); @@ -326,7 +327,7 @@ TEST_F(WindowModalityControllerTest, TouchEvent) { aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(), gfx::Point(10, 10)); - w1->AddTransientChild(w11.get()); + views::corewm::AddTransientChild(w1.get(), w11.get()); d1.reset(); d11.reset(); @@ -515,7 +516,7 @@ TEST_F(WindowModalityControllerTest, WindowModalAncestor) { scoped_ptr<aura::Window> w4( CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect())); w4->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); - w1->AddTransientChild(w4.get()); + views::corewm::AddTransientChild(w1.get(), w4.get()); wm::ActivateWindow(w1.get()); EXPECT_TRUE(wm::IsActiveWindow(w4.get())); @@ -544,7 +545,7 @@ TEST_F(WindowModalityControllerTest, ChildModalAncestor) { CreateTestWindowInShellWithDelegate(&d, -2, gfx::Rect())); w4->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_CHILD); views::corewm::SetModalParent(w4.get(), w2.get()); - w1->AddTransientChild(w4.get()); + views::corewm::AddTransientChild(w1.get(), w4.get()); wm::ActivateWindow(w1.get()); EXPECT_TRUE(wm::IsActiveWindow(w1.get())); diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc index 548117ea..0c86e28 100644 --- a/ash/wm/window_state.cc +++ b/ash/wm/window_state.cc @@ -133,7 +133,7 @@ bool WindowState::CanActivate() const { bool WindowState::CanSnap() const { if (!CanResize() || window_->type() == ui::wm::WINDOW_TYPE_PANEL || - window_->transient_parent()) + views::corewm::GetTransientParent(window_)) return false; // If a window has a maximum size defined, snapping may make it too big. return window_->delegate() ? window_->delegate()->GetMaximumSize().IsEmpty() : diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc index def4b6d..2985f66 100644 --- a/ash/wm/window_util.cc +++ b/ash/wm/window_util.cc @@ -134,10 +134,13 @@ void ReparentChildWithTransientChildren(aura::Window* child, void ReparentTransientChildrenOfChild(aura::Window* child, aura::Window* old_parent, aura::Window* new_parent) { - for (size_t i = 0; i < child->transient_children().size(); ++i) { - ReparentChildWithTransientChildren(child->transient_children()[i], - old_parent, - new_parent); + for (size_t i = 0; + i < views::corewm::GetTransientChildren(child).size(); + ++i) { + ReparentChildWithTransientChildren( + views::corewm::GetTransientChildren(child)[i], + old_parent, + new_parent); } } diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc index 0040a6c..49aaaa7 100644 --- a/ash/wm/workspace/workspace_layout_manager_unittest.cc +++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc @@ -20,6 +20,7 @@ #include "ui/aura/test/test_windows.h" #include "ui/aura/window.h" #include "ui/gfx/insets.h" +#include "ui/views/corewm/window_util.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -329,7 +330,7 @@ TEST_F(WorkspaceLayoutManagerTest, DontClobberRestoreBounds) { scoped_ptr<aura::Window> window2( CreateTestWindowInShellWithBounds(gfx::Rect(12, 20, 30, 40))); - window->AddTransientChild(window2.get()); + views::corewm::AddTransientChild(window.get(), window2.get()); window2->Show(); window_observer.set_window(window2.get()); diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc index 9d64fb2..eea0811 100644 --- a/ash/wm/workspace/workspace_window_resizer.cc +++ b/ash/wm/workspace/workspace_window_resizer.cc @@ -36,6 +36,7 @@ #include "ui/compositor/layer.h" #include "ui/gfx/screen.h" #include "ui/gfx/transform.h" +#include "ui/views/corewm/window_util.h" #include "ui/wm/public/window_types.h" namespace ash { @@ -97,7 +98,7 @@ scoped_ptr<WindowResizer> CreateWindowResizer( } if (switches::UseDockedWindows() && window_resizer && window->parent() && - !window->transient_parent() && + !views::corewm::GetTransientParent(window) && (window->parent()->id() == internal::kShellWindowId_DefaultContainer || window->parent()->id() == internal::kShellWindowId_DockedContainer || window->parent()->id() == internal::kShellWindowId_PanelContainer)) { diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc index 4b5fd926..e25a3d3 100644 --- a/ash/wm/workspace_controller_unittest.cc +++ b/ash/wm/workspace_controller_unittest.cc @@ -33,6 +33,7 @@ #include "ui/events/event_utils.h" #include "ui/gfx/screen.h" #include "ui/views/corewm/window_animations.h" +#include "ui/views/corewm/window_util.h" #include "ui/views/widget/widget.h" using aura::Window; @@ -677,7 +678,8 @@ TEST_F(WorkspaceControllerTest, TransientParent) { // Window with a transient parent. We set the transient parent to the root, // which would never happen but is enough to exercise the bug. scoped_ptr<Window> w1(CreateTestWindowUnparented()); - Shell::GetInstance()->GetPrimaryRootWindow()->AddTransientChild(w1.get()); + views::corewm::AddTransientChild( + Shell::GetInstance()->GetPrimaryRootWindow(), w1.get()); w1->SetBounds(gfx::Rect(10, 11, 250, 251)); ParentWindowInPrimaryRootWindow(w1.get()); w1->Show(); @@ -1132,7 +1134,7 @@ TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) { ui::wm::WINDOW_TYPE_POPUP, gfx::Rect(5, 6, 7, 8), NULL); - browser->AddTransientChild(status_bubble); + views::corewm::AddTransientChild(browser.get(), status_bubble); ParentWindowInPrimaryRootWindow(status_bubble); status_bubble->SetName("status_bubble"); diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc index 3e3d98c..43c69d3 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc +++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc @@ -36,6 +36,7 @@ #include "ui/aura/window.h" #include "ui/base/ui_base_types.h" #include "ui/events/event.h" +#include "ui/views/corewm/window_util.h" namespace { @@ -562,8 +563,8 @@ void MultiUserWindowManagerChromeOS::SetWindowVisibility( void MultiUserWindowManagerChromeOS::ShowWithTransientChildrenRecursive( aura::Window* window) { aura::Window::Windows::const_iterator it = - window->transient_children().begin(); - for (; it != window->transient_children().end(); ++it) + views::corewm::GetTransientChildren(window).begin(); + for (; it != views::corewm::GetTransientChildren(window).end(); ++it) ShowWithTransientChildrenRecursive(*it); // We show all children which were not explicitly hidden. @@ -577,11 +578,11 @@ aura::Window* MultiUserWindowManagerChromeOS::GetOwningWindowInTransientChain( aura::Window* window) { if (!GetWindowOwner(window).empty()) return NULL; - aura::Window* parent = window->transient_parent(); + aura::Window* parent = views::corewm::GetTransientParent(window); while (parent) { if (!GetWindowOwner(parent).empty()) return parent; - parent = parent->transient_parent(); + parent = views::corewm::GetTransientParent(parent); } return NULL; } @@ -591,8 +592,8 @@ void MultiUserWindowManagerChromeOS::AddTransientOwnerRecursive( aura::Window* owned_parent) { // First add all child windows. aura::Window::Windows::const_iterator it = - window->transient_children().begin(); - for (; it != window->transient_children().end(); ++it) + views::corewm::GetTransientChildren(window).begin(); + for (; it != views::corewm::GetTransientChildren(window).end(); ++it) AddTransientOwnerRecursive(*it, owned_parent); // If this window is the owned window, we do not have to handle it again. @@ -618,8 +619,8 @@ void MultiUserWindowManagerChromeOS::RemoveTransientOwnerRecursive( aura::Window* window) { // First remove all child windows. aura::Window::Windows::const_iterator it = - window->transient_children().begin(); - for (; it != window->transient_children().end(); ++it) + views::corewm::GetTransientChildren(window).begin(); + for (; it != views::corewm::GetTransientChildren(window).end(); ++it) RemoveTransientOwnerRecursive(*it); // Find from transient window storage the visibility for the given window, diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc index 0af57e4..6d0e8e4 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc +++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc @@ -19,6 +19,7 @@ #include "ui/aura/client/aura_constants.h" #include "ui/aura/root_window.h" #include "ui/base/ui_base_types.h" +#include "ui/views/corewm/window_util.h" namespace ash { namespace test { @@ -465,15 +466,15 @@ TEST_F(MultiUserWindowManagerChromeOSTest, TransientWindows) { // 3 - .. multi_user_window_manager()->SetWindowOwner(window(0), "A"); multi_user_window_manager()->SetWindowOwner(window(4), "B"); - window(0)->AddTransientChild(window(1)); + views::corewm::AddTransientChild(window(0), window(1)); // We first attach 2->3 and then 1->2 to see that the ownership gets // properly propagated through the sub tree upon assigning. - window(2)->AddTransientChild(window(3)); - window(1)->AddTransientChild(window(2)); - window(4)->AddTransientChild(window(5)); - window(4)->AddTransientChild(window(6)); - window(7)->AddTransientChild(window(8)); - window(7)->AddTransientChild(window(9)); + views::corewm::AddTransientChild(window(2), window(3)); + views::corewm::AddTransientChild(window(1), window(2)); + views::corewm::AddTransientChild(window(4), window(5)); + views::corewm::AddTransientChild(window(4), window(6)); + views::corewm::AddTransientChild(window(7), window(8)); + views::corewm::AddTransientChild(window(7), window(9)); // By now the hierarchy should have updated itself to show all windows of A // and hide all windows of B. Unowned windows should remain in what ever state @@ -510,15 +511,15 @@ TEST_F(MultiUserWindowManagerChromeOSTest, TransientWindows) { // 1 5 8 // | // 9 - window(2)->RemoveTransientChild(window(3)); - window(4)->RemoveTransientChild(window(6)); + views::corewm::RemoveTransientChild(window(2), window(3)); + views::corewm::RemoveTransientChild(window(4), window(6)); EXPECT_EQ("S[A], S[], H[], H[], H[B], H[], S[], S[], S[], H[]", GetStatus()); // Before we leave we need to reverse all transient window ownerships. - window(0)->RemoveTransientChild(window(1)); - window(1)->RemoveTransientChild(window(2)); - window(4)->RemoveTransientChild(window(5)); - window(7)->RemoveTransientChild(window(8)); - window(7)->RemoveTransientChild(window(9)); + views::corewm::RemoveTransientChild(window(0), window(1)); + views::corewm::RemoveTransientChild(window(1), window(2)); + views::corewm::RemoveTransientChild(window(4), window(5)); + views::corewm::RemoveTransientChild(window(7), window(8)); + views::corewm::RemoveTransientChild(window(7), window(9)); } // Test that the initial visibility state gets remembered. diff --git a/chrome/browser/ui/views/extensions/extension_popup.cc b/chrome/browser/ui/views/extensions/extension_popup.cc index 3afafb1..ea57e3d 100644 --- a/chrome/browser/ui/views/extensions/extension_popup.cc +++ b/chrome/browser/ui/views/extensions/extension_popup.cc @@ -32,6 +32,7 @@ #include "ui/aura/client/activation_client.h" #include "ui/aura/window.h" #include "ui/views/corewm/window_animations.h" +#include "ui/views/corewm/window_util.h" #endif #if defined(OS_WIN) @@ -197,7 +198,7 @@ void ExtensionPopup::OnWindowActivated(aura::Window* gained_active, if (!inspect_with_devtools_ && anchor_window == gained_active && host_desktop_type != chrome::HOST_DESKTOP_TYPE_ASH && this_window->GetRootWindow() == anchor_window->GetRootWindow() && - gained_active->transient_parent() != this_window) + views::corewm::GetTransientParent(gained_active) != this_window) GetWidget()->Close(); } #endif diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index 7558818..9a38126 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -56,6 +56,7 @@ #include "ui/aura/client/scoped_tooltip_disabler.h" #include "ui/aura/client/screen_position_client.h" #include "ui/aura/client/tooltip_client.h" +#include "ui/aura/client/transient_window_client.h" #include "ui/aura/client/window_tree_client.h" #include "ui/aura/env.h" #include "ui/aura/root_window.h" @@ -505,6 +506,9 @@ void RenderWidgetHostViewAura::InitAsPopup( popup_parent_host_view_ = static_cast<RenderWidgetHostViewAura*>(parent_host_view); + // TransientWindowClient may be NULL during tests. + aura::client::TransientWindowClient* transient_window_client = + aura::client::GetTransientWindowClient(); RenderWidgetHostViewAura* old_child = popup_parent_host_view_->popup_child_host_view_; if (old_child) { @@ -512,7 +516,10 @@ void RenderWidgetHostViewAura::InitAsPopup( // similar mechanism to ensure a second popup doesn't cause the first one // to never get a chance to filter events. See crbug.com/160589. DCHECK(old_child->popup_parent_host_view_ == popup_parent_host_view_); - popup_parent_host_view_->window_->RemoveTransientChild(old_child->window_); + if (transient_window_client) { + transient_window_client->RemoveTransientChild( + popup_parent_host_view_->window_, old_child->window_); + } old_child->popup_parent_host_view_ = NULL; } popup_parent_host_view_->popup_child_host_view_ = this; @@ -525,7 +532,10 @@ void RenderWidgetHostViewAura::InitAsPopup( // Setting the transient child allows for the popup to get mouse events when // in a system modal dialog. // This fixes crbug.com/328593. - popup_parent_host_view_->window_->AddTransientChild(window_); + if (transient_window_client) { + transient_window_client->AddTransientChild( + popup_parent_host_view_->window_, window_); + } SetBounds(bounds_in_screen); Show(); 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_; |