Home | History | Annotate | Download | only in wm
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ash/wm/workspace_controller.h"
      6 
      7 #include <map>
      8 
      9 #include "ash/ash_switches.h"
     10 #include "ash/root_window_controller.h"
     11 #include "ash/screen_ash.h"
     12 #include "ash/shelf/shelf_layout_manager.h"
     13 #include "ash/shelf/shelf_widget.h"
     14 #include "ash/shell.h"
     15 #include "ash/shell_window_ids.h"
     16 #include "ash/system/status_area_widget.h"
     17 #include "ash/test/ash_test_base.h"
     18 #include "ash/test/shell_test_api.h"
     19 #include "ash/wm/activation_controller.h"
     20 #include "ash/wm/property_util.h"
     21 #include "ash/wm/window_properties.h"
     22 #include "ash/wm/window_util.h"
     23 #include "base/command_line.h"
     24 #include "base/strings/string_number_conversions.h"
     25 #include "ui/aura/client/aura_constants.h"
     26 #include "ui/aura/root_window.h"
     27 #include "ui/aura/test/event_generator.h"
     28 #include "ui/aura/test/test_window_delegate.h"
     29 #include "ui/aura/test/test_windows.h"
     30 #include "ui/aura/window.h"
     31 #include "ui/base/hit_test.h"
     32 #include "ui/base/ui_base_types.h"
     33 #include "ui/compositor/layer.h"
     34 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
     35 #include "ui/gfx/screen.h"
     36 #include "ui/views/corewm/window_animations.h"
     37 #include "ui/views/widget/widget.h"
     38 
     39 using aura::Window;
     40 
     41 namespace ash {
     42 namespace internal {
     43 
     44 // Returns a string containing the names of all the children of |window| (in
     45 // order). Each entry is separated by a space.
     46 std::string GetWindowNames(const aura::Window* window) {
     47   std::string result;
     48   for (size_t i = 0; i < window->children().size(); ++i) {
     49     if (i != 0)
     50       result += " ";
     51     result += window->children()[i]->name();
     52   }
     53   return result;
     54 }
     55 
     56 // Returns a string containing the names of windows corresponding to each of the
     57 // child layers of |window|'s layer. Any layers that don't correspond to a child
     58 // Window of |window| are ignored. The result is ordered based on the layer
     59 // ordering.
     60 std::string GetLayerNames(const aura::Window* window) {
     61   typedef std::map<const ui::Layer*, std::string> LayerToWindowNameMap;
     62   LayerToWindowNameMap window_names;
     63   for (size_t i = 0; i < window->children().size(); ++i) {
     64     window_names[window->children()[i]->layer()] =
     65         window->children()[i]->name();
     66   }
     67 
     68   std::string result;
     69   const std::vector<ui::Layer*>& layers(window->layer()->children());
     70   for (size_t i = 0; i < layers.size(); ++i) {
     71     LayerToWindowNameMap::iterator layer_i =
     72         window_names.find(layers[i]);
     73     if (layer_i != window_names.end()) {
     74       if (!result.empty())
     75         result += " ";
     76       result += layer_i->second;
     77     }
     78   }
     79   return result;
     80 }
     81 
     82 class WorkspaceControllerTest : public test::AshTestBase {
     83  public:
     84   WorkspaceControllerTest() {}
     85   virtual ~WorkspaceControllerTest() {}
     86 
     87   aura::Window* CreateTestWindowUnparented() {
     88     aura::Window* window = new aura::Window(NULL);
     89     window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
     90     window->SetType(aura::client::WINDOW_TYPE_NORMAL);
     91     window->Init(ui::LAYER_TEXTURED);
     92     return window;
     93   }
     94 
     95   aura::Window* CreateTestWindow() {
     96     aura::Window* window = new aura::Window(NULL);
     97     window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
     98     window->SetType(aura::client::WINDOW_TYPE_NORMAL);
     99     window->Init(ui::LAYER_TEXTURED);
    100     SetDefaultParentByPrimaryRootWindow(window);
    101     return window;
    102   }
    103 
    104   aura::Window* CreateAppTestWindow(aura::Window* parent) {
    105     aura::Window* window = new aura::Window(NULL);
    106     window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    107     window->SetType(aura::client::WINDOW_TYPE_POPUP);
    108     window->Init(ui::LAYER_TEXTURED);
    109     if (!parent)
    110       SetDefaultParentByPrimaryRootWindow(window);
    111     else
    112       parent->AddChild(window);
    113     return window;
    114   }
    115 
    116   aura::Window* GetDesktop() {
    117     return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
    118                                kShellWindowId_DefaultContainer);
    119   }
    120 
    121   gfx::Rect GetFullscreenBounds(aura::Window* window) {
    122     return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds();
    123   }
    124 
    125   ShelfWidget* shelf_widget() {
    126     return Shell::GetPrimaryRootWindowController()->shelf();
    127   }
    128 
    129   ShelfLayoutManager* shelf_layout_manager() {
    130     return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
    131   }
    132 
    133   bool GetWindowOverlapsShelf() {
    134     return shelf_layout_manager()->window_overlaps_shelf();
    135   }
    136 
    137  private:
    138   scoped_ptr<ActivationController> activation_controller_;
    139 
    140   DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest);
    141 };
    142 
    143 // Assertions around adding a normal window.
    144 TEST_F(WorkspaceControllerTest, AddNormalWindowWhenEmpty) {
    145   scoped_ptr<Window> w1(CreateTestWindow());
    146   w1->SetBounds(gfx::Rect(0, 0, 250, 251));
    147 
    148   EXPECT_TRUE(GetRestoreBoundsInScreen(w1.get()) == NULL);
    149 
    150   w1->Show();
    151 
    152   EXPECT_TRUE(GetRestoreBoundsInScreen(w1.get()) == NULL);
    153 
    154   ASSERT_TRUE(w1->layer() != NULL);
    155   EXPECT_TRUE(w1->layer()->visible());
    156 
    157   EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
    158 
    159   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    160 }
    161 
    162 // Assertions around maximizing/unmaximizing.
    163 TEST_F(WorkspaceControllerTest, SingleMaximizeWindow) {
    164   scoped_ptr<Window> w1(CreateTestWindow());
    165   w1->SetBounds(gfx::Rect(0, 0, 250, 251));
    166 
    167   w1->Show();
    168   wm::ActivateWindow(w1.get());
    169 
    170   EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
    171 
    172   ASSERT_TRUE(w1->layer() != NULL);
    173   EXPECT_TRUE(w1->layer()->visible());
    174 
    175   EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
    176 
    177   // Maximize the window.
    178   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    179 
    180   EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
    181 
    182   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    183   EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()).width(),
    184             w1->bounds().width());
    185   EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()).height(),
    186             w1->bounds().height());
    187 
    188   // Restore the window.
    189   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    190 
    191   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    192   EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
    193 }
    194 
    195 // Assertions around two windows and toggling one to be fullscreen.
    196 TEST_F(WorkspaceControllerTest, FullscreenWithNormalWindow) {
    197   scoped_ptr<Window> w1(CreateTestWindow());
    198   scoped_ptr<Window> w2(CreateTestWindow());
    199   w1->SetBounds(gfx::Rect(0, 0, 250, 251));
    200   w1->Show();
    201 
    202   ASSERT_TRUE(w1->layer() != NULL);
    203   EXPECT_TRUE(w1->layer()->visible());
    204 
    205   w2->SetBounds(gfx::Rect(0, 0, 50, 51));
    206   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    207   w2->Show();
    208   wm::ActivateWindow(w2.get());
    209 
    210   // Both windows should be in the same workspace.
    211   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    212   EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
    213 
    214   gfx::Rect work_area(
    215       ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()));
    216   EXPECT_EQ(work_area.width(), w2->bounds().width());
    217   EXPECT_EQ(work_area.height(), w2->bounds().height());
    218 
    219   // Restore w2, which should then go back to one workspace.
    220   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    221   EXPECT_EQ(50, w2->bounds().width());
    222   EXPECT_EQ(51, w2->bounds().height());
    223   EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
    224 }
    225 
    226 // Makes sure requests to change the bounds of a normal window go through.
    227 TEST_F(WorkspaceControllerTest, ChangeBoundsOfNormalWindow) {
    228   scoped_ptr<Window> w1(CreateTestWindow());
    229   w1->Show();
    230 
    231   // Setting the bounds should go through since the window is in the normal
    232   // workspace.
    233   w1->SetBounds(gfx::Rect(0, 0, 200, 500));
    234   EXPECT_EQ(200, w1->bounds().width());
    235   EXPECT_EQ(500, w1->bounds().height());
    236 }
    237 
    238 // Verifies the bounds is not altered when showing and grid is enabled.
    239 TEST_F(WorkspaceControllerTest, SnapToGrid) {
    240   scoped_ptr<Window> w1(CreateTestWindowUnparented());
    241   w1->SetBounds(gfx::Rect(1, 6, 25, 30));
    242   SetDefaultParentByPrimaryRootWindow(w1.get());
    243   // We are not aligning this anymore this way. When the window gets shown
    244   // the window is expected to be handled differently, but this cannot be
    245   // tested with this test. So the result of this test should be that the
    246   // bounds are exactly as passed in.
    247   EXPECT_EQ("1,6 25x30", w1->bounds().ToString());
    248 }
    249 
    250 // Assertions around a fullscreen window.
    251 TEST_F(WorkspaceControllerTest, SingleFullscreenWindow) {
    252   scoped_ptr<Window> w1(CreateTestWindow());
    253   w1->SetBounds(gfx::Rect(0, 0, 250, 251));
    254   // Make the window fullscreen.
    255   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    256   w1->Show();
    257   wm::ActivateWindow(w1.get());
    258 
    259   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    260   EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
    261   EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
    262 
    263   // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up
    264   // with when using views::Widget.
    265   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT);
    266   EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
    267 
    268   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    269   EXPECT_EQ(250, w1->bounds().width());
    270   EXPECT_EQ(251, w1->bounds().height());
    271 
    272   // Back to fullscreen.
    273   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    274   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    275   EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
    276   EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
    277   ASSERT_TRUE(GetRestoreBoundsInScreen(w1.get()));
    278   EXPECT_EQ("0,0 250x251", GetRestoreBoundsInScreen(w1.get())->ToString());
    279 }
    280 
    281 // Assertions around minimizing a single window.
    282 TEST_F(WorkspaceControllerTest, MinimizeSingleWindow) {
    283   scoped_ptr<Window> w1(CreateTestWindow());
    284 
    285   w1->Show();
    286 
    287   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
    288   EXPECT_FALSE(w1->layer()->IsDrawn());
    289 
    290   // Show the window.
    291   w1->Show();
    292   EXPECT_TRUE(wm::IsWindowNormal(w1.get()));
    293   EXPECT_TRUE(w1->layer()->IsDrawn());
    294 }
    295 
    296 // Assertions around minimizing a fullscreen window.
    297 TEST_F(WorkspaceControllerTest, MinimizeFullscreenWindow) {
    298   // Two windows, w1 normal, w2 fullscreen.
    299   scoped_ptr<Window> w1(CreateTestWindow());
    300   scoped_ptr<Window> w2(CreateTestWindow());
    301   w1->Show();
    302   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    303   w2->Show();
    304   wm::ActivateWindow(w2.get());
    305 
    306   // Minimize w2.
    307   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
    308   EXPECT_TRUE(w1->layer()->IsDrawn());
    309   EXPECT_FALSE(w2->layer()->IsDrawn());
    310 
    311   // Show the window, which should trigger unminimizing.
    312   w2->Show();
    313   wm::ActivateWindow(w2.get());
    314 
    315   EXPECT_TRUE(wm::IsWindowFullscreen(w2.get()));
    316   EXPECT_TRUE(w1->layer()->IsDrawn());
    317   EXPECT_TRUE(w2->layer()->IsDrawn());
    318 
    319   // Minimize the window, which should hide the window.
    320   EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
    321   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
    322   EXPECT_FALSE(wm::IsActiveWindow(w2.get()));
    323   EXPECT_FALSE(w2->layer()->IsDrawn());
    324   EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
    325 
    326   // Make the window normal.
    327   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    328   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    329   EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
    330   EXPECT_TRUE(w2->layer()->IsDrawn());
    331 }
    332 
    333 // Verifies ShelfLayoutManager's visibility/auto-hide state is correctly
    334 // updated.
    335 TEST_F(WorkspaceControllerTest, ShelfStateUpdated) {
    336   // Since ShelfLayoutManager queries for mouse location, move the mouse so
    337   // it isn't over the shelf.
    338   aura::test::EventGenerator generator(
    339       Shell::GetPrimaryRootWindow(), gfx::Point());
    340   generator.MoveMouseTo(0, 0);
    341 
    342   scoped_ptr<Window> w1(CreateTestWindow());
    343   const gfx::Rect w1_bounds(0, 1, 101, 102);
    344   ShelfLayoutManager* shelf = shelf_layout_manager();
    345   shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
    346   const gfx::Rect touches_shelf_bounds(
    347       0, shelf->GetIdealBounds().y() - 10, 101, 102);
    348   // Move |w1| to overlap the shelf.
    349   w1->SetBounds(touches_shelf_bounds);
    350   EXPECT_FALSE(GetWindowOverlapsShelf());
    351 
    352   // A visible ignored window should not trigger the overlap.
    353   scoped_ptr<Window> w_ignored(CreateTestWindow());
    354   w_ignored->SetBounds(touches_shelf_bounds);
    355   SetIgnoredByShelf(&(*w_ignored), true);
    356   w_ignored->Show();
    357   EXPECT_FALSE(GetWindowOverlapsShelf());
    358 
    359   // Make it visible, since visible shelf overlaps should be true.
    360   w1->Show();
    361   EXPECT_TRUE(GetWindowOverlapsShelf());
    362 
    363   wm::ActivateWindow(w1.get());
    364   w1->SetBounds(w1_bounds);
    365   w1->Show();
    366   wm::ActivateWindow(w1.get());
    367 
    368   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    369 
    370   // Maximize the window.
    371   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    372   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    373   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    374 
    375   // Restore.
    376   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    377   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    378   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    379 
    380   // Fullscreen.
    381   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    382   EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
    383 
    384   // Normal.
    385   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    386   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    387   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    388   EXPECT_FALSE(GetWindowOverlapsShelf());
    389 
    390   // Move window so it obscures shelf.
    391   w1->SetBounds(touches_shelf_bounds);
    392   EXPECT_TRUE(GetWindowOverlapsShelf());
    393 
    394   // Move it back.
    395   w1->SetBounds(w1_bounds);
    396   EXPECT_FALSE(GetWindowOverlapsShelf());
    397 
    398   // Maximize again.
    399   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    400   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    401   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    402 
    403   // Minimize.
    404   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
    405   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    406 
    407   // Since the restore from minimize will restore to the pre-minimize
    408   // state (tested elsewhere), we abandon the current size and restore
    409   // rect and set them to the window.
    410   gfx::Rect restore = *GetRestoreBoundsInScreen(w1.get());
    411   EXPECT_EQ("0,0 800x597", w1->bounds().ToString());
    412   EXPECT_EQ("0,1 101x102", restore.ToString());
    413   ClearRestoreBounds(w1.get());
    414   w1->SetBounds(restore);
    415 
    416   // Restore.
    417   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    418   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    419   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    420 
    421   // Create another window, maximized.
    422   scoped_ptr<Window> w2(CreateTestWindow());
    423   w2->SetBounds(gfx::Rect(10, 11, 250, 251));
    424   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    425   w2->Show();
    426   wm::ActivateWindow(w2.get());
    427   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    428   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    429   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    430 
    431   // Switch to w1.
    432   wm::ActivateWindow(w1.get());
    433   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    434   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    435   EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(
    436                 w2->parent()).ToString(),
    437             w2->bounds().ToString());
    438 
    439   // Switch to w2.
    440   wm::ActivateWindow(w2.get());
    441   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    442   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    443   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    444   EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w2.get()).ToString(),
    445             w2->bounds().ToString());
    446 
    447   // Turn off auto-hide, switch back to w2 (maximized) and verify overlap.
    448   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
    449   wm::ActivateWindow(w2.get());
    450   EXPECT_FALSE(GetWindowOverlapsShelf());
    451 
    452   // Move w1 to overlap shelf, it shouldn't change window overlaps shelf since
    453   // the window isn't in the visible workspace.
    454   w1->SetBounds(touches_shelf_bounds);
    455   EXPECT_FALSE(GetWindowOverlapsShelf());
    456 
    457   // Activate w1. Although w1 is visible, the overlap state is still false since
    458   // w2 is maximized.
    459   wm::ActivateWindow(w1.get());
    460   EXPECT_FALSE(GetWindowOverlapsShelf());
    461 
    462   // Restore w2.
    463   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    464   EXPECT_TRUE(GetWindowOverlapsShelf());
    465 }
    466 
    467 // Verifies going from maximized to minimized sets the right state for painting
    468 // the background of the launcher.
    469 TEST_F(WorkspaceControllerTest, MinimizeResetsVisibility) {
    470   scoped_ptr<Window> w1(CreateTestWindow());
    471   w1->Show();
    472   wm::ActivateWindow(w1.get());
    473   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    474   EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, shelf_widget()->GetBackgroundType());
    475 
    476   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
    477   EXPECT_EQ(SHELF_VISIBLE,
    478             shelf_layout_manager()->visibility_state());
    479   EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, shelf_widget()->GetBackgroundType());
    480 }
    481 
    482 // Verifies window visibility during various workspace changes.
    483 TEST_F(WorkspaceControllerTest, VisibilityTests) {
    484   scoped_ptr<Window> w1(CreateTestWindow());
    485   w1->Show();
    486   EXPECT_TRUE(w1->IsVisible());
    487   EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
    488 
    489   // Create another window, activate it and make it fullscreen.
    490   scoped_ptr<Window> w2(CreateTestWindow());
    491   w2->Show();
    492   wm::ActivateWindow(w2.get());
    493   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    494   EXPECT_TRUE(w2->IsVisible());
    495   EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
    496   EXPECT_TRUE(w1->IsVisible());
    497 
    498   // Switch to w1. |w1| should be visible on top of |w2|.
    499   wm::ActivateWindow(w1.get());
    500   EXPECT_TRUE(w1->IsVisible());
    501   EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
    502   EXPECT_TRUE(w2->IsVisible());
    503 
    504   // Switch back to |w2|.
    505   wm::ActivateWindow(w2.get());
    506   EXPECT_TRUE(w2->IsVisible());
    507   EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
    508   EXPECT_TRUE(w1->IsVisible());
    509 
    510   // Restore |w2|, both windows should be visible.
    511   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    512   EXPECT_TRUE(w1->IsVisible());
    513   EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
    514   EXPECT_TRUE(w2->IsVisible());
    515   EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
    516 
    517   // Make |w2| fullscreen again, then close it.
    518   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    519   w2->Hide();
    520   EXPECT_FALSE(w2->IsVisible());
    521   EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
    522   EXPECT_TRUE(w1->IsVisible());
    523 
    524   // Create |w2| and maximize it.
    525   w2.reset(CreateTestWindow());
    526   w2->Show();
    527   wm::ActivateWindow(w2.get());
    528   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    529   EXPECT_TRUE(w2->IsVisible());
    530   EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
    531   EXPECT_TRUE(w1->IsVisible());
    532 
    533   // Close |w2|.
    534   w2.reset();
    535   EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
    536   EXPECT_TRUE(w1->IsVisible());
    537 }
    538 
    539 // Verifies windows that are offscreen don't move when switching workspaces.
    540 TEST_F(WorkspaceControllerTest, DontMoveOnSwitch) {
    541   aura::test::EventGenerator generator(
    542       Shell::GetPrimaryRootWindow(), gfx::Point());
    543   generator.MoveMouseTo(0, 0);
    544 
    545   scoped_ptr<Window> w1(CreateTestWindow());
    546   ShelfLayoutManager* shelf = shelf_layout_manager();
    547   const gfx::Rect touches_shelf_bounds(
    548       0, shelf->GetIdealBounds().y() - 10, 101, 102);
    549   // Move |w1| to overlap the shelf.
    550   w1->SetBounds(touches_shelf_bounds);
    551   w1->Show();
    552   wm::ActivateWindow(w1.get());
    553 
    554   // Create another window and maximize it.
    555   scoped_ptr<Window> w2(CreateTestWindow());
    556   w2->SetBounds(gfx::Rect(10, 11, 250, 251));
    557   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    558   w2->Show();
    559   wm::ActivateWindow(w2.get());
    560 
    561   // Switch to w1.
    562   wm::ActivateWindow(w1.get());
    563   EXPECT_EQ(touches_shelf_bounds.ToString(), w1->bounds().ToString());
    564 }
    565 
    566 // Verifies that windows that are completely offscreen move when switching
    567 // workspaces.
    568 TEST_F(WorkspaceControllerTest, MoveOnSwitch) {
    569   aura::test::EventGenerator generator(
    570       Shell::GetPrimaryRootWindow(), gfx::Point());
    571   generator.MoveMouseTo(0, 0);
    572 
    573   scoped_ptr<Window> w1(CreateTestWindow());
    574   ShelfLayoutManager* shelf = shelf_layout_manager();
    575   const gfx::Rect w1_bounds(0, shelf->GetIdealBounds().y(), 100, 200);
    576   // Move |w1| so that the top edge is the same as the top edge of the shelf.
    577   w1->SetBounds(w1_bounds);
    578   w1->Show();
    579   wm::ActivateWindow(w1.get());
    580   EXPECT_EQ(w1_bounds.ToString(), w1->bounds().ToString());
    581 
    582   // Create another window and maximize it.
    583   scoped_ptr<Window> w2(CreateTestWindow());
    584   w2->SetBounds(gfx::Rect(10, 11, 250, 251));
    585   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    586   w2->Show();
    587   wm::ActivateWindow(w2.get());
    588 
    589   // Increase the size of the WorkAreaInsets. This would make |w1| fall
    590   // completely out of the display work area.
    591   gfx::Insets insets =
    592       Shell::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets();
    593   insets.Set(0, 0, insets.bottom() + 30, 0);
    594   Shell::GetInstance()->SetDisplayWorkAreaInsets(w1.get(), insets);
    595 
    596   // Switch to w1. The window should have moved.
    597   wm::ActivateWindow(w1.get());
    598   EXPECT_NE(w1_bounds.ToString(), w1->bounds().ToString());
    599 }
    600 
    601 namespace {
    602 
    603 // WindowDelegate used by DontCrashOnChangeAndActivate.
    604 class DontCrashOnChangeAndActivateDelegate
    605     : public aura::test::TestWindowDelegate {
    606  public:
    607   DontCrashOnChangeAndActivateDelegate() : window_(NULL) {}
    608 
    609   void set_window(aura::Window* window) { window_ = window; }
    610 
    611   // WindowDelegate overrides:
    612   virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
    613                                const gfx::Rect& new_bounds) OVERRIDE {
    614     if (window_) {
    615       wm::ActivateWindow(window_);
    616       window_ = NULL;
    617     }
    618   }
    619 
    620  private:
    621   aura::Window* window_;
    622 
    623   DISALLOW_COPY_AND_ASSIGN(DontCrashOnChangeAndActivateDelegate);
    624 };
    625 
    626 }  // namespace
    627 
    628 // Exercises possible crash in W2. Here's the sequence:
    629 // . minimize a maximized window.
    630 // . remove the window (which happens when switching displays).
    631 // . add the window back.
    632 // . show the window and during the bounds change activate it.
    633 TEST_F(WorkspaceControllerTest, DontCrashOnChangeAndActivate) {
    634   // Force the shelf
    635   ShelfLayoutManager* shelf = shelf_layout_manager();
    636   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
    637 
    638   DontCrashOnChangeAndActivateDelegate delegate;
    639   scoped_ptr<Window> w1(CreateTestWindowInShellWithDelegate(
    640       &delegate, 1000, gfx::Rect(10, 11, 250, 251)));
    641 
    642   w1->Show();
    643   wm::ActivateWindow(w1.get());
    644   wm::MaximizeWindow(w1.get());
    645   wm::MinimizeWindow(w1.get());
    646 
    647   w1->parent()->RemoveChild(w1.get());
    648 
    649   // Do this so that when we Show() the window a resize occurs and we make the
    650   // window active.
    651   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
    652 
    653   SetDefaultParentByPrimaryRootWindow(w1.get());
    654   delegate.set_window(w1.get());
    655   w1->Show();
    656 }
    657 
    658 // Verifies a window with a transient parent not managed by workspace works.
    659 TEST_F(WorkspaceControllerTest, TransientParent) {
    660   // Normal window with no transient parent.
    661   scoped_ptr<Window> w2(CreateTestWindow());
    662   w2->SetBounds(gfx::Rect(10, 11, 250, 251));
    663   w2->Show();
    664   wm::ActivateWindow(w2.get());
    665 
    666   // Window with a transient parent. We set the transient parent to the root,
    667   // which would never happen but is enough to exercise the bug.
    668   scoped_ptr<Window> w1(CreateTestWindowUnparented());
    669   Shell::GetInstance()->GetPrimaryRootWindow()->AddTransientChild(w1.get());
    670   w1->SetBounds(gfx::Rect(10, 11, 250, 251));
    671   SetDefaultParentByPrimaryRootWindow(w1.get());
    672   w1->Show();
    673   wm::ActivateWindow(w1.get());
    674 
    675   // The window with the transient parent should get added to the same parent as
    676   // the normal window.
    677   EXPECT_EQ(w2->parent(), w1->parent());
    678 }
    679 
    680 // Verifies changing TrackedByWorkspace works.
    681 TEST_F(WorkspaceControllerTest, TrackedByWorkspace) {
    682   // Create a fullscreen window.
    683   scoped_ptr<Window> w1(CreateTestWindow());
    684   w1->Show();
    685   wm::ActivateWindow(w1.get());
    686   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    687   EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
    688   EXPECT_TRUE(w1->IsVisible());
    689 
    690   // Create a second fullscreen window and mark it not tracked by workspace
    691   // manager.
    692   scoped_ptr<Window> w2(CreateTestWindowUnparented());
    693   w2->SetBounds(gfx::Rect(1, 6, 25, 30));
    694   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    695   SetDefaultParentByPrimaryRootWindow(w2.get());
    696   w2->Show();
    697   SetTrackedByWorkspace(w2.get(), false);
    698   wm::ActivateWindow(w2.get());
    699 
    700   // Activating |w2| should force it to have the same parent as |w1|.
    701   EXPECT_EQ(w1->parent(), w2->parent());
    702   EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
    703   EXPECT_TRUE(w1->IsVisible());
    704   EXPECT_TRUE(w2->IsVisible());
    705 
    706   // Because |w2| isn't tracked we should be able to set the bounds of it.
    707   gfx::Rect bounds(w2->bounds());
    708   bounds.Offset(4, 5);
    709   w2->SetBounds(bounds);
    710   EXPECT_EQ(bounds.ToString(), w2->bounds().ToString());
    711 
    712   // Transition it to tracked by worskpace. It should end up in the desktop
    713   // workspace.
    714   SetTrackedByWorkspace(w2.get(), true);
    715   EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
    716   EXPECT_TRUE(w1->IsVisible());
    717   EXPECT_TRUE(w2->IsVisible());
    718   EXPECT_EQ(w1->parent(), w2->parent());
    719 }
    720 
    721 // Test the basic auto placement of one and or two windows in a "simulated
    722 // session" of sequential window operations.
    723 TEST_F(WorkspaceControllerTest, BasicAutoPlacing) {
    724   // Test 1: In case there is no manageable window, no window should shift.
    725 
    726   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    727   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
    728   gfx::Rect desktop_area = window1->parent()->bounds();
    729 
    730   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    731   // Trigger the auto window placement function by making it visible.
    732   // Note that the bounds are getting changed while it is invisible.
    733   window2->Hide();
    734   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
    735   window2->Show();
    736 
    737   // Check the initial position of the windows is unchanged.
    738   EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
    739   EXPECT_EQ("32,48 256x512", window2->bounds().ToString());
    740 
    741   // Remove the second window and make sure that the first window
    742   // does NOT get centered.
    743   window2.reset();
    744   EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
    745 
    746   // Test 2: Set up two managed windows and check their auto positioning.
    747   ash::wm::SetWindowPositionManaged(window1.get(), true);
    748   scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2));
    749   ash::wm::SetWindowPositionManaged(window3.get(), true);
    750   // To avoid any auto window manager changes due to SetBounds, the window
    751   // gets first hidden and then shown again.
    752   window3->Hide();
    753   window3->SetBounds(gfx::Rect(32, 48, 256, 512));
    754   window3->Show();
    755   // |window1| should be flush right and |window3| flush left.
    756   EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
    757   EXPECT_EQ(base::IntToString(
    758                 desktop_area.width() - window3->bounds().width()) +
    759             ",48 256x512", window3->bounds().ToString());
    760 
    761   // After removing |window3|, |window1| should be centered again.
    762   window3.reset();
    763   EXPECT_EQ(
    764       base::IntToString(
    765           (desktop_area.width() - window1->bounds().width()) / 2) +
    766       ",32 640x320", window1->bounds().ToString());
    767 
    768   // Test 3: Set up a manageable and a non manageable window and check
    769   // positioning.
    770   scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(3));
    771   // To avoid any auto window manager changes due to SetBounds, the window
    772   // gets first hidden and then shown again.
    773   window1->Hide();
    774   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
    775   window4->SetBounds(gfx::Rect(32, 48, 256, 512));
    776   window1->Show();
    777   // |window1| should be centered and |window4| untouched.
    778   EXPECT_EQ(
    779       base::IntToString(
    780           (desktop_area.width() - window1->bounds().width()) / 2) +
    781       ",32 640x320", window1->bounds().ToString());
    782   EXPECT_EQ("32,48 256x512", window4->bounds().ToString());
    783 
    784   // Test4: A single manageable window should get centered.
    785   window4.reset();
    786   ash::wm::SetUserHasChangedWindowPositionOrSize(window1.get(), false);
    787   // Trigger the auto window placement function by showing (and hiding) it.
    788   window1->Hide();
    789   window1->Show();
    790   // |window1| should be centered.
    791   EXPECT_EQ(
    792       base::IntToString(
    793           (desktop_area.width() - window1->bounds().width()) / 2) +
    794       ",32 640x320", window1->bounds().ToString());
    795 }
    796 
    797 // Test the proper usage of user window movement interaction.
    798 TEST_F(WorkspaceControllerTest, TestUserMovedWindowRepositioning) {
    799   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    800   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
    801   gfx::Rect desktop_area = window1->parent()->bounds();
    802   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    803   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
    804   window1->Hide();
    805   window2->Hide();
    806   ash::wm::SetWindowPositionManaged(window1.get(), true);
    807   ash::wm::SetWindowPositionManaged(window2.get(), true);
    808   EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
    809   EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window2.get()));
    810 
    811   // Check that the current location gets preserved if the user has
    812   // positioned it previously.
    813   ash::wm::SetUserHasChangedWindowPositionOrSize(window1.get(), true);
    814   window1->Show();
    815   EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
    816   // Flag should be still set.
    817   EXPECT_TRUE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
    818   EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window2.get()));
    819 
    820   // Turn on the second window and make sure that both windows are now
    821   // positionable again (user movement cleared).
    822   window2->Show();
    823 
    824   // |window1| should be flush left and |window3| flush right.
    825   EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
    826   EXPECT_EQ(
    827       base::IntToString(desktop_area.width() - window2->bounds().width()) +
    828       ",48 256x512", window2->bounds().ToString());
    829   // FLag should now be reset.
    830   EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
    831   EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
    832 
    833   // Going back to one shown window should keep the state.
    834   ash::wm::SetUserHasChangedWindowPositionOrSize(window1.get(), true);
    835   window2->Hide();
    836   EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
    837   EXPECT_TRUE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
    838 }
    839 
    840 // Test that user placed windows go back to their user placement after the user
    841 // closes all other windows.
    842 TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) {
    843   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    844   gfx::Rect user_pos = gfx::Rect(16, 42, 640, 320);
    845   window1->SetBounds(user_pos);
    846   ash::wm::SetPreAutoManageWindowBounds(window1.get(), user_pos);
    847   gfx::Rect desktop_area = window1->parent()->bounds();
    848 
    849   // Create a second window to let the auto manager kick in.
    850   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    851   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
    852   window1->Hide();
    853   window2->Hide();
    854   ash::wm::SetWindowPositionManaged(window1.get(), true);
    855   ash::wm::SetWindowPositionManaged(window2.get(), true);
    856   window1->Show();
    857   EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
    858   window2->Show();
    859 
    860   // |window1| should be flush left and |window2| flush right.
    861   EXPECT_EQ("0," + base::IntToString(user_pos.y()) +
    862             " 640x320", window1->bounds().ToString());
    863   EXPECT_EQ(
    864       base::IntToString(desktop_area.width() - window2->bounds().width()) +
    865       ",48 256x512", window2->bounds().ToString());
    866   window2->Hide();
    867 
    868   // After the other window get hidden the window has to move back to the
    869   // previous position and the bounds should still be set and unchanged.
    870   EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
    871   ASSERT_TRUE(ash::wm::GetPreAutoManageWindowBounds(window1.get()));
    872   EXPECT_EQ(user_pos.ToString(),
    873             ash::wm::GetPreAutoManageWindowBounds(window1.get())->ToString());
    874 }
    875 
    876 // Test that a window from normal to minimize will repos the remaining.
    877 TEST_F(WorkspaceControllerTest, ToMinimizeRepositionsRemaining) {
    878   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    879   ash::wm::SetWindowPositionManaged(window1.get(), true);
    880   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
    881   gfx::Rect desktop_area = window1->parent()->bounds();
    882 
    883   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    884   ash::wm::SetWindowPositionManaged(window2.get(), true);
    885   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
    886 
    887   ash::wm::MinimizeWindow(window1.get());
    888 
    889   // |window2| should be centered now.
    890   EXPECT_TRUE(window2->IsVisible());
    891   EXPECT_TRUE(ash::wm::IsWindowNormal(window2.get()));
    892   EXPECT_EQ(base::IntToString(
    893                 (desktop_area.width() - window2->bounds().width()) / 2) +
    894             ",48 256x512", window2->bounds().ToString());
    895 
    896   ash::wm::RestoreWindow(window1.get());
    897   // |window1| should be flush right and |window3| flush left.
    898   EXPECT_EQ(base::IntToString(
    899                 desktop_area.width() - window1->bounds().width()) +
    900             ",32 640x320", window1->bounds().ToString());
    901   EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
    902 }
    903 
    904 // Test that minimizing an initially maximized window will repos the remaining.
    905 TEST_F(WorkspaceControllerTest, MaxToMinRepositionsRemaining) {
    906   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    907   ash::wm::SetWindowPositionManaged(window1.get(), true);
    908   gfx::Rect desktop_area = window1->parent()->bounds();
    909 
    910   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    911   ash::wm::SetWindowPositionManaged(window2.get(), true);
    912   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
    913 
    914   ash::wm::MaximizeWindow(window1.get());
    915   ash::wm::MinimizeWindow(window1.get());
    916 
    917   // |window2| should be centered now.
    918   EXPECT_TRUE(window2->IsVisible());
    919   EXPECT_TRUE(ash::wm::IsWindowNormal(window2.get()));
    920   EXPECT_EQ(base::IntToString(
    921                 (desktop_area.width() - window2->bounds().width()) / 2) +
    922             ",48 256x512", window2->bounds().ToString());
    923 }
    924 
    925 // Test that nomral, maximize, minimizing will repos the remaining.
    926 TEST_F(WorkspaceControllerTest, NormToMaxToMinRepositionsRemaining) {
    927   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    928   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
    929   ash::wm::SetWindowPositionManaged(window1.get(), true);
    930   gfx::Rect desktop_area = window1->parent()->bounds();
    931 
    932   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    933   ash::wm::SetWindowPositionManaged(window2.get(), true);
    934   window2->SetBounds(gfx::Rect(32, 40, 256, 512));
    935 
    936   // Trigger the auto window placement function by showing (and hiding) it.
    937   window1->Hide();
    938   window1->Show();
    939 
    940   // |window1| should be flush right and |window3| flush left.
    941   EXPECT_EQ(base::IntToString(
    942                 desktop_area.width() - window1->bounds().width()) +
    943             ",32 640x320", window1->bounds().ToString());
    944   EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
    945 
    946   ash::wm::MaximizeWindow(window1.get());
    947   ash::wm::MinimizeWindow(window1.get());
    948 
    949   // |window2| should be centered now.
    950   EXPECT_TRUE(window2->IsVisible());
    951   EXPECT_TRUE(ash::wm::IsWindowNormal(window2.get()));
    952   EXPECT_EQ(base::IntToString(
    953                 (desktop_area.width() - window2->bounds().width()) / 2) +
    954             ",40 256x512", window2->bounds().ToString());
    955 }
    956 
    957 // Test that nomral, maximize, normal will repos the remaining.
    958 TEST_F(WorkspaceControllerTest, NormToMaxToNormRepositionsRemaining) {
    959   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    960   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
    961   ash::wm::SetWindowPositionManaged(window1.get(), true);
    962   gfx::Rect desktop_area = window1->parent()->bounds();
    963 
    964   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    965   ash::wm::SetWindowPositionManaged(window2.get(), true);
    966   window2->SetBounds(gfx::Rect(32, 40, 256, 512));
    967 
    968   // Trigger the auto window placement function by showing (and hiding) it.
    969   window1->Hide();
    970   window1->Show();
    971 
    972   // |window1| should be flush right and |window3| flush left.
    973   EXPECT_EQ(base::IntToString(
    974                 desktop_area.width() - window1->bounds().width()) +
    975             ",32 640x320", window1->bounds().ToString());
    976   EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
    977 
    978   ash::wm::MaximizeWindow(window1.get());
    979   ash::wm::RestoreWindow(window1.get());
    980 
    981   // |window1| should be flush right and |window2| flush left.
    982   EXPECT_EQ(base::IntToString(
    983                 desktop_area.width() - window1->bounds().width()) +
    984             ",32 640x320", window1->bounds().ToString());
    985   EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
    986 }
    987 
    988 // Test that animations are triggered.
    989 TEST_F(WorkspaceControllerTest, AnimatedNormToMaxToNormRepositionsRemaining) {
    990   ui::ScopedAnimationDurationScaleMode normal_duration_mode(
    991       ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
    992   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    993   window1->Hide();
    994   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
    995   gfx::Rect desktop_area = window1->parent()->bounds();
    996   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    997   window2->Hide();
    998   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
    999 
   1000   ash::wm::SetWindowPositionManaged(window1.get(), true);
   1001   ash::wm::SetWindowPositionManaged(window2.get(), true);
   1002   // Make sure nothing is animating.
   1003   window1->layer()->GetAnimator()->StopAnimating();
   1004   window2->layer()->GetAnimator()->StopAnimating();
   1005   window2->Show();
   1006 
   1007   // The second window should now animate.
   1008   EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
   1009   EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
   1010   window2->layer()->GetAnimator()->StopAnimating();
   1011 
   1012   window1->Show();
   1013   EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
   1014   EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
   1015 
   1016   window1->layer()->GetAnimator()->StopAnimating();
   1017   window2->layer()->GetAnimator()->StopAnimating();
   1018   // |window1| should be flush right and |window2| flush left.
   1019   EXPECT_EQ(base::IntToString(
   1020                 desktop_area.width() - window1->bounds().width()) +
   1021             ",32 640x320", window1->bounds().ToString());
   1022   EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
   1023 }
   1024 
   1025 // This tests simulates a browser and an app and verifies the ordering of the
   1026 // windows and layers doesn't get out of sync as various operations occur. Its
   1027 // really testing code in FocusController, but easier to simulate here. Just as
   1028 // with a real browser the browser here has a transient child window
   1029 // (corresponds to the status bubble).
   1030 TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) {
   1031   scoped_ptr<Window> browser(
   1032       aura::test::CreateTestWindowWithDelegate(
   1033           NULL,
   1034           aura::client::WINDOW_TYPE_NORMAL,
   1035           gfx::Rect(5, 6, 7, 8),
   1036           NULL));
   1037   browser->SetName("browser");
   1038   SetDefaultParentByPrimaryRootWindow(browser.get());
   1039   browser->Show();
   1040   wm::ActivateWindow(browser.get());
   1041 
   1042   // |status_bubble| is made a transient child of |browser| and as a result
   1043   // owned by |browser|.
   1044   aura::test::TestWindowDelegate* status_bubble_delegate =
   1045       aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate();
   1046   status_bubble_delegate->set_can_focus(false);
   1047   Window* status_bubble =
   1048       aura::test::CreateTestWindowWithDelegate(
   1049           status_bubble_delegate,
   1050           aura::client::WINDOW_TYPE_POPUP,
   1051           gfx::Rect(5, 6, 7, 8),
   1052           NULL);
   1053   browser->AddTransientChild(status_bubble);
   1054   SetDefaultParentByPrimaryRootWindow(status_bubble);
   1055   status_bubble->SetName("status_bubble");
   1056 
   1057   scoped_ptr<Window> app(
   1058       aura::test::CreateTestWindowWithDelegate(
   1059           NULL,
   1060           aura::client::WINDOW_TYPE_NORMAL,
   1061           gfx::Rect(5, 6, 7, 8),
   1062           NULL));
   1063   app->SetName("app");
   1064   SetDefaultParentByPrimaryRootWindow(app.get());
   1065 
   1066   aura::Window* parent = browser->parent();
   1067 
   1068   app->Show();
   1069   wm::ActivateWindow(app.get());
   1070   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1071 
   1072   // Minimize the app, focus should go the browser.
   1073   app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
   1074   EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
   1075   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1076 
   1077   // Minimize the browser (neither windows are focused).
   1078   browser->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
   1079   EXPECT_FALSE(wm::IsActiveWindow(browser.get()));
   1080   EXPECT_FALSE(wm::IsActiveWindow(app.get()));
   1081   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1082 
   1083   // Show the browser (which should restore it).
   1084   browser->Show();
   1085   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1086 
   1087   // Activate the browser.
   1088   ash::wm::ActivateWindow(browser.get());
   1089   EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
   1090   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1091 
   1092   // Restore the app. This differs from above code for |browser| as internally
   1093   // the app code does this. Restoring this way or using Show() should not make
   1094   // a difference.
   1095   app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
   1096   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1097 
   1098   // Activate the app.
   1099   ash::wm::ActivateWindow(app.get());
   1100   EXPECT_TRUE(wm::IsActiveWindow(app.get()));
   1101   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1102 }
   1103 
   1104 namespace {
   1105 
   1106 // Used by DragMaximizedNonTrackedWindow to track how many times the window
   1107 // hierarchy changes.
   1108 class DragMaximizedNonTrackedWindowObserver
   1109     : public aura::WindowObserver {
   1110  public:
   1111   DragMaximizedNonTrackedWindowObserver() : change_count_(0) {
   1112   }
   1113 
   1114   // Number of times OnWindowHierarchyChanged() has been received.
   1115   void clear_change_count() { change_count_ = 0; }
   1116   int change_count() const {
   1117     return change_count_;
   1118   }
   1119 
   1120   // aura::WindowObserver overrides:
   1121   virtual void OnWindowHierarchyChanged(
   1122       const HierarchyChangeParams& params) OVERRIDE {
   1123     change_count_++;
   1124   }
   1125 
   1126  private:
   1127   int change_count_;
   1128 
   1129   DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver);
   1130 };
   1131 
   1132 }  // namespace
   1133 
   1134 // Verifies setting tracked by workspace to false and then dragging a fullscreen
   1135 // window doesn't result in changing the window hierarchy (which typically
   1136 // indicates new workspaces have been created).
   1137 TEST_F(WorkspaceControllerTest, DragFullscreenNonTrackedWindow) {
   1138   aura::test::EventGenerator generator(
   1139       Shell::GetPrimaryRootWindow(), gfx::Point());
   1140   generator.MoveMouseTo(5, 5);
   1141 
   1142   aura::test::TestWindowDelegate delegate;
   1143   delegate.set_window_component(HTCAPTION);
   1144   scoped_ptr<Window> w1(
   1145       aura::test::CreateTestWindowWithDelegate(&delegate,
   1146                                                aura::client::WINDOW_TYPE_NORMAL,
   1147                                                gfx::Rect(5, 6, 7, 8),
   1148                                                NULL));
   1149   SetDefaultParentByPrimaryRootWindow(w1.get());
   1150   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
   1151   w1->Show();
   1152   wm::ActivateWindow(w1.get());
   1153   DragMaximizedNonTrackedWindowObserver observer;
   1154   w1->parent()->parent()->AddObserver(&observer);
   1155   const gfx::Rect max_bounds(w1->bounds());
   1156 
   1157   generator.PressLeftButton();
   1158   generator.MoveMouseTo(100, 100);
   1159   // The bounds shouldn't change (drag should result in nothing happening
   1160   // now.
   1161   EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
   1162 
   1163   generator.ReleaseLeftButton();
   1164   EXPECT_EQ(0, observer.change_count());
   1165 
   1166   // Set tracked to false and repeat, now the window should move.
   1167   SetTrackedByWorkspace(w1.get(), false);
   1168   generator.MoveMouseTo(5, 5);
   1169   generator.PressLeftButton();
   1170   generator.MoveMouseBy(100, 100);
   1171   EXPECT_EQ(gfx::Rect(max_bounds.x() + 100, max_bounds.y() + 100,
   1172                       max_bounds.width(), max_bounds.height()).ToString(),
   1173             w1->bounds().ToString());
   1174 
   1175   generator.ReleaseLeftButton();
   1176   SetTrackedByWorkspace(w1.get(), true);
   1177   // Marking the window tracked again should snap back to origin.
   1178   EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
   1179   EXPECT_EQ(0, observer.change_count());
   1180 
   1181   w1->parent()->parent()->RemoveObserver(&observer);
   1182 }
   1183 
   1184 // Verifies setting tracked by workspace to false and then dragging a maximized
   1185 // window can change the bound.
   1186 TEST_F(WorkspaceControllerTest, DragMaximizedNonTrackedWindow) {
   1187   aura::test::EventGenerator generator(
   1188       Shell::GetPrimaryRootWindow(), gfx::Point());
   1189   generator.MoveMouseTo(5, 5);
   1190 
   1191   aura::test::TestWindowDelegate delegate;
   1192   delegate.set_window_component(HTCAPTION);
   1193   scoped_ptr<Window> w1(
   1194       aura::test::CreateTestWindowWithDelegate(&delegate,
   1195                                                aura::client::WINDOW_TYPE_NORMAL,
   1196                                                gfx::Rect(5, 6, 7, 8),
   1197                                                NULL));
   1198   SetDefaultParentByPrimaryRootWindow(w1.get());
   1199   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
   1200   w1->Show();
   1201   wm::ActivateWindow(w1.get());
   1202   DragMaximizedNonTrackedWindowObserver observer;
   1203   w1->parent()->parent()->AddObserver(&observer);
   1204   const gfx::Rect max_bounds(w1->bounds());
   1205 
   1206   generator.PressLeftButton();
   1207   generator.MoveMouseTo(100, 100);
   1208   // The bounds shouldn't change (drag should result in nothing happening
   1209   // now.
   1210   EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
   1211 
   1212   generator.ReleaseLeftButton();
   1213   EXPECT_EQ(0, observer.change_count());
   1214 
   1215   // Set tracked to false and repeat, now the window should move.
   1216   SetTrackedByWorkspace(w1.get(), false);
   1217   generator.MoveMouseTo(5, 5);
   1218   generator.PressLeftButton();
   1219   generator.MoveMouseBy(100, 100);
   1220   EXPECT_EQ(gfx::Rect(max_bounds.x() + 100, max_bounds.y() + 100,
   1221                       max_bounds.width(), max_bounds.height()).ToString(),
   1222             w1->bounds().ToString());
   1223 
   1224   generator.ReleaseLeftButton();
   1225   SetTrackedByWorkspace(w1.get(), true);
   1226   // Marking the window tracked again should snap back to origin.
   1227   EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
   1228   EXPECT_EQ(0, observer.change_count());
   1229 
   1230   w1->parent()->parent()->RemoveObserver(&observer);
   1231 }
   1232 
   1233 // Verifies that a new maximized window becomes visible after its activation
   1234 // is requested, even though it does not become activated because a system
   1235 // modal window is active.
   1236 TEST_F(WorkspaceControllerTest, SwitchFromModal) {
   1237   scoped_ptr<Window> modal_window(CreateTestWindowUnparented());
   1238   modal_window->SetBounds(gfx::Rect(10, 11, 21, 22));
   1239   modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
   1240   SetDefaultParentByPrimaryRootWindow(modal_window.get());
   1241   modal_window->Show();
   1242   wm::ActivateWindow(modal_window.get());
   1243 
   1244   scoped_ptr<Window> maximized_window(CreateTestWindow());
   1245   maximized_window->SetProperty(
   1246       aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
   1247   maximized_window->Show();
   1248   wm::ActivateWindow(maximized_window.get());
   1249   EXPECT_TRUE(maximized_window->IsVisible());
   1250 }
   1251 
   1252 }  // namespace internal
   1253 }  // namespace ash
   1254