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/root_window_controller.h"
     10 #include "ash/screen_util.h"
     11 #include "ash/shelf/shelf_layout_manager.h"
     12 #include "ash/shelf/shelf_widget.h"
     13 #include "ash/shell.h"
     14 #include "ash/shell_window_ids.h"
     15 #include "ash/system/status_area_widget.h"
     16 #include "ash/test/ash_test_base.h"
     17 #include "ash/test/shell_test_api.h"
     18 #include "ash/test/test_shelf_delegate.h"
     19 #include "ash/wm/panels/panel_layout_manager.h"
     20 #include "ash/wm/window_state.h"
     21 #include "ash/wm/window_util.h"
     22 #include "ash/wm/workspace/workspace_window_resizer.h"
     23 #include "base/strings/string_number_conversions.h"
     24 #include "ui/aura/client/aura_constants.h"
     25 #include "ui/aura/test/event_generator.h"
     26 #include "ui/aura/test/test_window_delegate.h"
     27 #include "ui/aura/test/test_windows.h"
     28 #include "ui/aura/window.h"
     29 #include "ui/aura/window_event_dispatcher.h"
     30 #include "ui/base/hit_test.h"
     31 #include "ui/base/ui_base_types.h"
     32 #include "ui/compositor/layer.h"
     33 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
     34 #include "ui/events/event_utils.h"
     35 #include "ui/gfx/screen.h"
     36 #include "ui/views/widget/widget.h"
     37 #include "ui/wm/core/window_animations.h"
     38 #include "ui/wm/core/window_util.h"
     39 
     40 using aura::Window;
     41 
     42 namespace ash {
     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(ui::wm::WINDOW_TYPE_NORMAL);
     91     window->Init(aura::WINDOW_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(ui::wm::WINDOW_TYPE_NORMAL);
     99     window->Init(aura::WINDOW_LAYER_TEXTURED);
    100     ParentWindowInPrimaryRootWindow(window);
    101     return window;
    102   }
    103 
    104   aura::Window* CreateBrowserLikeWindow(const gfx::Rect& bounds) {
    105     aura::Window* window = CreateTestWindow();
    106     window->SetBounds(bounds);
    107     wm::WindowState* window_state = wm::GetWindowState(window);
    108     window_state->set_window_position_managed(true);
    109     window->Show();
    110     return window;
    111   }
    112 
    113   aura::Window* CreatePopupLikeWindow(const gfx::Rect& bounds) {
    114     aura::Window* window = CreateTestWindowInShellWithBounds(bounds);
    115     window->Show();
    116     return window;
    117   }
    118 
    119   aura::Window* CreateTestPanel(aura::WindowDelegate* delegate,
    120                                 const gfx::Rect& bounds) {
    121     aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
    122         delegate,
    123         ui::wm::WINDOW_TYPE_PANEL,
    124         0,
    125         bounds);
    126     test::TestShelfDelegate* shelf_delegate =
    127         test::TestShelfDelegate::instance();
    128     shelf_delegate->AddShelfItem(window);
    129     PanelLayoutManager* manager = static_cast<PanelLayoutManager*>(
    130         Shell::GetContainer(window->GetRootWindow(),
    131                             kShellWindowId_PanelContainer)->layout_manager());
    132     manager->Relayout();
    133     return window;
    134   }
    135 
    136   aura::Window* GetDesktop() {
    137     return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
    138                                kShellWindowId_DefaultContainer);
    139   }
    140 
    141   gfx::Rect GetFullscreenBounds(aura::Window* window) {
    142     return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds();
    143   }
    144 
    145   ShelfWidget* shelf_widget() {
    146     return Shell::GetPrimaryRootWindowController()->shelf();
    147   }
    148 
    149   ShelfLayoutManager* shelf_layout_manager() {
    150     return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
    151   }
    152 
    153   bool GetWindowOverlapsShelf() {
    154     return shelf_layout_manager()->window_overlaps_shelf();
    155   }
    156 
    157  private:
    158   DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest);
    159 };
    160 
    161 // Assertions around adding a normal window.
    162 TEST_F(WorkspaceControllerTest, AddNormalWindowWhenEmpty) {
    163   scoped_ptr<Window> w1(CreateTestWindow());
    164   w1->SetBounds(gfx::Rect(0, 0, 250, 251));
    165 
    166   wm::WindowState* window_state = wm::GetWindowState(w1.get());
    167 
    168   EXPECT_FALSE(window_state->HasRestoreBounds());
    169 
    170   w1->Show();
    171 
    172   EXPECT_FALSE(window_state->HasRestoreBounds());
    173 
    174   ASSERT_TRUE(w1->layer() != NULL);
    175   EXPECT_TRUE(w1->layer()->visible());
    176 
    177   EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
    178 
    179   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    180 }
    181 
    182 // Assertions around maximizing/unmaximizing.
    183 TEST_F(WorkspaceControllerTest, SingleMaximizeWindow) {
    184   scoped_ptr<Window> w1(CreateTestWindow());
    185   w1->SetBounds(gfx::Rect(0, 0, 250, 251));
    186 
    187   w1->Show();
    188   wm::ActivateWindow(w1.get());
    189 
    190   EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
    191 
    192   ASSERT_TRUE(w1->layer() != NULL);
    193   EXPECT_TRUE(w1->layer()->visible());
    194 
    195   EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
    196 
    197   // Maximize the window.
    198   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    199 
    200   EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
    201 
    202   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    203   EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).width(),
    204             w1->bounds().width());
    205   EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).height(),
    206             w1->bounds().height());
    207 
    208   // Restore the window.
    209   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    210 
    211   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    212   EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
    213 }
    214 
    215 // Assertions around two windows and toggling one to be fullscreen.
    216 TEST_F(WorkspaceControllerTest, FullscreenWithNormalWindow) {
    217   scoped_ptr<Window> w1(CreateTestWindow());
    218   scoped_ptr<Window> w2(CreateTestWindow());
    219   w1->SetBounds(gfx::Rect(0, 0, 250, 251));
    220   w1->Show();
    221 
    222   ASSERT_TRUE(w1->layer() != NULL);
    223   EXPECT_TRUE(w1->layer()->visible());
    224 
    225   w2->SetBounds(gfx::Rect(0, 0, 50, 51));
    226   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    227   w2->Show();
    228   wm::ActivateWindow(w2.get());
    229 
    230   // Both windows should be in the same workspace.
    231   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    232   EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
    233 
    234   gfx::Rect work_area(
    235       ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()));
    236   EXPECT_EQ(work_area.width(), w2->bounds().width());
    237   EXPECT_EQ(work_area.height(), w2->bounds().height());
    238 
    239   // Restore w2, which should then go back to one workspace.
    240   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    241   EXPECT_EQ(50, w2->bounds().width());
    242   EXPECT_EQ(51, w2->bounds().height());
    243   EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
    244 }
    245 
    246 // Makes sure requests to change the bounds of a normal window go through.
    247 TEST_F(WorkspaceControllerTest, ChangeBoundsOfNormalWindow) {
    248   scoped_ptr<Window> w1(CreateTestWindow());
    249   w1->Show();
    250 
    251   // Setting the bounds should go through since the window is in the normal
    252   // workspace.
    253   w1->SetBounds(gfx::Rect(0, 0, 200, 500));
    254   EXPECT_EQ(200, w1->bounds().width());
    255   EXPECT_EQ(500, w1->bounds().height());
    256 }
    257 
    258 // Verifies the bounds is not altered when showing and grid is enabled.
    259 TEST_F(WorkspaceControllerTest, SnapToGrid) {
    260   scoped_ptr<Window> w1(CreateTestWindowUnparented());
    261   w1->SetBounds(gfx::Rect(1, 6, 25, 30));
    262   ParentWindowInPrimaryRootWindow(w1.get());
    263   // We are not aligning this anymore this way. When the window gets shown
    264   // the window is expected to be handled differently, but this cannot be
    265   // tested with this test. So the result of this test should be that the
    266   // bounds are exactly as passed in.
    267   EXPECT_EQ("1,6 25x30", w1->bounds().ToString());
    268 }
    269 
    270 // Assertions around a fullscreen window.
    271 TEST_F(WorkspaceControllerTest, SingleFullscreenWindow) {
    272   scoped_ptr<Window> w1(CreateTestWindow());
    273   w1->SetBounds(gfx::Rect(0, 0, 250, 251));
    274   // Make the window fullscreen.
    275   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    276   w1->Show();
    277   wm::ActivateWindow(w1.get());
    278 
    279   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    280   EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
    281   EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
    282 
    283   // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up
    284   // with when using views::Widget.
    285   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT);
    286   EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
    287 
    288   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    289   EXPECT_EQ(250, w1->bounds().width());
    290   EXPECT_EQ(251, w1->bounds().height());
    291 
    292   // Back to fullscreen.
    293   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    294   EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
    295   EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
    296   EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
    297   wm::WindowState* window_state = wm::GetWindowState(w1.get());
    298 
    299   ASSERT_TRUE(window_state->HasRestoreBounds());
    300   EXPECT_EQ("0,0 250x251", window_state->GetRestoreBoundsInScreen().ToString());
    301 }
    302 
    303 // Assertions around minimizing a single window.
    304 TEST_F(WorkspaceControllerTest, MinimizeSingleWindow) {
    305   scoped_ptr<Window> w1(CreateTestWindow());
    306 
    307   w1->Show();
    308 
    309   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
    310   EXPECT_FALSE(w1->layer()->IsDrawn());
    311   EXPECT_TRUE(w1->layer()->GetTargetTransform().IsIdentity());
    312 
    313   // Show the window.
    314   w1->Show();
    315   EXPECT_TRUE(wm::GetWindowState(w1.get())->IsNormalStateType());
    316   EXPECT_TRUE(w1->layer()->IsDrawn());
    317 }
    318 
    319 // Assertions around minimizing a fullscreen window.
    320 TEST_F(WorkspaceControllerTest, MinimizeFullscreenWindow) {
    321   // Two windows, w1 normal, w2 fullscreen.
    322   scoped_ptr<Window> w1(CreateTestWindow());
    323   scoped_ptr<Window> w2(CreateTestWindow());
    324   w1->Show();
    325   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    326   w2->Show();
    327 
    328   wm::WindowState* w1_state = wm::GetWindowState(w1.get());
    329   wm::WindowState* w2_state = wm::GetWindowState(w2.get());
    330 
    331   w2_state->Activate();
    332 
    333   // Minimize w2.
    334   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
    335   EXPECT_TRUE(w1->layer()->IsDrawn());
    336   EXPECT_FALSE(w2->layer()->IsDrawn());
    337 
    338   // Show the window, which should trigger unminimizing.
    339   w2->Show();
    340   w2_state->Activate();
    341 
    342   EXPECT_TRUE(w2_state->IsFullscreen());
    343   EXPECT_TRUE(w1->layer()->IsDrawn());
    344   EXPECT_TRUE(w2->layer()->IsDrawn());
    345 
    346   // Minimize the window, which should hide the window.
    347   EXPECT_TRUE(w2_state->IsActive());
    348   w2_state->Minimize();
    349   EXPECT_FALSE(w2_state->IsActive());
    350   EXPECT_FALSE(w2->layer()->IsDrawn());
    351   EXPECT_TRUE(w1_state->IsActive());
    352   EXPECT_EQ(w2.get(), GetDesktop()->children()[0]);
    353   EXPECT_EQ(w1.get(), GetDesktop()->children()[1]);
    354 
    355   // Make the window normal.
    356   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    357   // Setting back to normal doesn't change the activation.
    358   EXPECT_FALSE(w2_state->IsActive());
    359   EXPECT_TRUE(w1_state->IsActive());
    360   EXPECT_EQ(w2.get(), GetDesktop()->children()[0]);
    361   EXPECT_EQ(w1.get(), GetDesktop()->children()[1]);
    362   EXPECT_TRUE(w2->layer()->IsDrawn());
    363 }
    364 
    365 // Verifies ShelfLayoutManager's visibility/auto-hide state is correctly
    366 // updated.
    367 TEST_F(WorkspaceControllerTest, ShelfStateUpdated) {
    368   // Since ShelfLayoutManager queries for mouse location, move the mouse so
    369   // it isn't over the shelf.
    370   aura::test::EventGenerator generator(
    371       Shell::GetPrimaryRootWindow(), gfx::Point());
    372   generator.MoveMouseTo(0, 0);
    373 
    374   scoped_ptr<Window> w1(CreateTestWindow());
    375   const gfx::Rect w1_bounds(0, 1, 101, 102);
    376   ShelfLayoutManager* shelf = shelf_layout_manager();
    377   shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
    378   const gfx::Rect touches_shelf_bounds(
    379       0, shelf->GetIdealBounds().y() - 10, 101, 102);
    380   // Move |w1| to overlap the shelf.
    381   w1->SetBounds(touches_shelf_bounds);
    382   EXPECT_FALSE(GetWindowOverlapsShelf());
    383 
    384   // A visible ignored window should not trigger the overlap.
    385   scoped_ptr<Window> w_ignored(CreateTestWindow());
    386   w_ignored->SetBounds(touches_shelf_bounds);
    387   wm::GetWindowState(&(*w_ignored))->set_ignored_by_shelf(true);
    388   w_ignored->Show();
    389   EXPECT_FALSE(GetWindowOverlapsShelf());
    390 
    391   // Make it visible, since visible shelf overlaps should be true.
    392   w1->Show();
    393   EXPECT_TRUE(GetWindowOverlapsShelf());
    394 
    395   wm::ActivateWindow(w1.get());
    396   w1->SetBounds(w1_bounds);
    397   w1->Show();
    398   wm::ActivateWindow(w1.get());
    399 
    400   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    401 
    402   // Maximize the window.
    403   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    404   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    405   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    406 
    407   // Restore.
    408   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    409   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    410   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    411 
    412   // Fullscreen.
    413   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    414   EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
    415 
    416   // Normal.
    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   EXPECT_FALSE(GetWindowOverlapsShelf());
    421 
    422   // Move window so it obscures shelf.
    423   w1->SetBounds(touches_shelf_bounds);
    424   EXPECT_TRUE(GetWindowOverlapsShelf());
    425 
    426   // Move it back.
    427   w1->SetBounds(w1_bounds);
    428   EXPECT_FALSE(GetWindowOverlapsShelf());
    429 
    430   // Maximize again.
    431   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    432   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    433   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    434 
    435   // Minimize.
    436   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
    437   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    438 
    439   // Since the restore from minimize will restore to the pre-minimize
    440   // state (tested elsewhere), we abandon the current size and restore
    441   // rect and set them to the window.
    442   wm::WindowState* window_state = wm::GetWindowState(w1.get());
    443 
    444   gfx::Rect restore = window_state->GetRestoreBoundsInScreen();
    445   EXPECT_EQ("0,0 800x597", w1->bounds().ToString());
    446   EXPECT_EQ("0,1 101x102", restore.ToString());
    447   window_state->ClearRestoreBounds();
    448   w1->SetBounds(restore);
    449 
    450   // Restore.
    451   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    452   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    453   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    454 
    455   // Create another window, maximized.
    456   scoped_ptr<Window> w2(CreateTestWindow());
    457   w2->SetBounds(gfx::Rect(10, 11, 250, 251));
    458   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    459   w2->Show();
    460   wm::ActivateWindow(w2.get());
    461   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    462   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    463   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    464   EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(
    465                 w2->parent()).ToString(),
    466             w2->bounds().ToString());
    467 
    468   // Switch to w1.
    469   wm::ActivateWindow(w1.get());
    470   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    471   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    472   EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(
    473                 w2->parent()).ToString(),
    474             w2->bounds().ToString());
    475 
    476   // Switch to w2.
    477   wm::ActivateWindow(w2.get());
    478   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
    479   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    480   EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
    481   EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w2.get()).ToString(),
    482             w2->bounds().ToString());
    483 
    484   // Turn off auto-hide, switch back to w2 (maximized) and verify overlap.
    485   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
    486   wm::ActivateWindow(w2.get());
    487   EXPECT_FALSE(GetWindowOverlapsShelf());
    488 
    489   // Move w1 to overlap shelf, it shouldn't change window overlaps shelf since
    490   // the window isn't in the visible workspace.
    491   w1->SetBounds(touches_shelf_bounds);
    492   EXPECT_FALSE(GetWindowOverlapsShelf());
    493 
    494   // Activate w1. Although w1 is visible, the overlap state is still false since
    495   // w2 is maximized.
    496   wm::ActivateWindow(w1.get());
    497   EXPECT_FALSE(GetWindowOverlapsShelf());
    498 
    499   // Restore w2.
    500   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    501   EXPECT_TRUE(GetWindowOverlapsShelf());
    502 }
    503 
    504 // Verifies going from maximized to minimized sets the right state for painting
    505 // the background of the launcher.
    506 TEST_F(WorkspaceControllerTest, MinimizeResetsVisibility) {
    507   scoped_ptr<Window> w1(CreateTestWindow());
    508   w1->Show();
    509   wm::ActivateWindow(w1.get());
    510   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    511   EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, shelf_widget()->GetBackgroundType());
    512 
    513   w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
    514   EXPECT_EQ(SHELF_VISIBLE,
    515             shelf_layout_manager()->visibility_state());
    516   EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, shelf_widget()->GetBackgroundType());
    517 }
    518 
    519 // Verifies window visibility during various workspace changes.
    520 TEST_F(WorkspaceControllerTest, VisibilityTests) {
    521   scoped_ptr<Window> w1(CreateTestWindow());
    522   w1->Show();
    523   EXPECT_TRUE(w1->IsVisible());
    524   EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
    525 
    526   // Create another window, activate it and make it fullscreen.
    527   scoped_ptr<Window> w2(CreateTestWindow());
    528   w2->Show();
    529   wm::ActivateWindow(w2.get());
    530   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    531   EXPECT_TRUE(w2->IsVisible());
    532   EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
    533   EXPECT_TRUE(w1->IsVisible());
    534 
    535   // Switch to w1. |w1| should be visible on top of |w2|.
    536   wm::ActivateWindow(w1.get());
    537   EXPECT_TRUE(w1->IsVisible());
    538   EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
    539   EXPECT_TRUE(w2->IsVisible());
    540 
    541   // Switch back to |w2|.
    542   wm::ActivateWindow(w2.get());
    543   EXPECT_TRUE(w2->IsVisible());
    544   EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
    545   EXPECT_TRUE(w1->IsVisible());
    546 
    547   // Restore |w2|, both windows should be visible.
    548   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
    549   EXPECT_TRUE(w1->IsVisible());
    550   EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
    551   EXPECT_TRUE(w2->IsVisible());
    552   EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
    553 
    554   // Make |w2| fullscreen again, then close it.
    555   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    556   w2->Hide();
    557   EXPECT_FALSE(w2->IsVisible());
    558   EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
    559   EXPECT_TRUE(w1->IsVisible());
    560 
    561   // Create |w2| and maximize it.
    562   w2.reset(CreateTestWindow());
    563   w2->Show();
    564   wm::ActivateWindow(w2.get());
    565   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    566   EXPECT_TRUE(w2->IsVisible());
    567   EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
    568   EXPECT_TRUE(w1->IsVisible());
    569 
    570   // Close |w2|.
    571   w2.reset();
    572   EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
    573   EXPECT_TRUE(w1->IsVisible());
    574 }
    575 
    576 // Verifies windows that are offscreen don't move when switching workspaces.
    577 TEST_F(WorkspaceControllerTest, DontMoveOnSwitch) {
    578   aura::test::EventGenerator generator(
    579       Shell::GetPrimaryRootWindow(), gfx::Point());
    580   generator.MoveMouseTo(0, 0);
    581 
    582   scoped_ptr<Window> w1(CreateTestWindow());
    583   ShelfLayoutManager* shelf = shelf_layout_manager();
    584   const gfx::Rect touches_shelf_bounds(
    585       0, shelf->GetIdealBounds().y() - 10, 101, 102);
    586   // Move |w1| to overlap the shelf.
    587   w1->SetBounds(touches_shelf_bounds);
    588   w1->Show();
    589   wm::ActivateWindow(w1.get());
    590 
    591   // Create another window and maximize it.
    592   scoped_ptr<Window> w2(CreateTestWindow());
    593   w2->SetBounds(gfx::Rect(10, 11, 250, 251));
    594   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    595   w2->Show();
    596   wm::ActivateWindow(w2.get());
    597 
    598   // Switch to w1.
    599   wm::ActivateWindow(w1.get());
    600   EXPECT_EQ(touches_shelf_bounds.ToString(), w1->bounds().ToString());
    601 }
    602 
    603 // Verifies that windows that are completely offscreen move when switching
    604 // workspaces.
    605 TEST_F(WorkspaceControllerTest, MoveOnSwitch) {
    606   aura::test::EventGenerator generator(
    607       Shell::GetPrimaryRootWindow(), gfx::Point());
    608   generator.MoveMouseTo(0, 0);
    609 
    610   scoped_ptr<Window> w1(CreateTestWindow());
    611   ShelfLayoutManager* shelf = shelf_layout_manager();
    612   const gfx::Rect w1_bounds(0, shelf->GetIdealBounds().y(), 100, 200);
    613   // Move |w1| so that the top edge is the same as the top edge of the shelf.
    614   w1->SetBounds(w1_bounds);
    615   w1->Show();
    616   wm::ActivateWindow(w1.get());
    617   EXPECT_EQ(w1_bounds.ToString(), w1->bounds().ToString());
    618 
    619   // Create another window and maximize it.
    620   scoped_ptr<Window> w2(CreateTestWindow());
    621   w2->SetBounds(gfx::Rect(10, 11, 250, 251));
    622   w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
    623   w2->Show();
    624   wm::ActivateWindow(w2.get());
    625 
    626   // Increase the size of the WorkAreaInsets. This would make |w1| fall
    627   // completely out of the display work area.
    628   gfx::Insets insets =
    629       Shell::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets();
    630   insets.Set(0, 0, insets.bottom() + 30, 0);
    631   Shell::GetInstance()->SetDisplayWorkAreaInsets(w1.get(), insets);
    632 
    633   // Switch to w1. The window should have moved.
    634   wm::ActivateWindow(w1.get());
    635   EXPECT_NE(w1_bounds.ToString(), w1->bounds().ToString());
    636 }
    637 
    638 namespace {
    639 
    640 // WindowDelegate used by DontCrashOnChangeAndActivate.
    641 class DontCrashOnChangeAndActivateDelegate
    642     : public aura::test::TestWindowDelegate {
    643  public:
    644   DontCrashOnChangeAndActivateDelegate() : window_(NULL) {}
    645 
    646   void set_window(aura::Window* window) { window_ = window; }
    647 
    648   // WindowDelegate overrides:
    649   virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
    650                                const gfx::Rect& new_bounds) OVERRIDE {
    651     if (window_) {
    652       wm::ActivateWindow(window_);
    653       window_ = NULL;
    654     }
    655   }
    656 
    657  private:
    658   aura::Window* window_;
    659 
    660   DISALLOW_COPY_AND_ASSIGN(DontCrashOnChangeAndActivateDelegate);
    661 };
    662 
    663 }  // namespace
    664 
    665 // Exercises possible crash in W2. Here's the sequence:
    666 // . minimize a maximized window.
    667 // . remove the window (which happens when switching displays).
    668 // . add the window back.
    669 // . show the window and during the bounds change activate it.
    670 TEST_F(WorkspaceControllerTest, DontCrashOnChangeAndActivate) {
    671   // Force the shelf
    672   ShelfLayoutManager* shelf = shelf_layout_manager();
    673   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
    674 
    675   DontCrashOnChangeAndActivateDelegate delegate;
    676   scoped_ptr<Window> w1(CreateTestWindowInShellWithDelegate(
    677       &delegate, 1000, gfx::Rect(10, 11, 250, 251)));
    678 
    679   w1->Show();
    680   wm::WindowState* w1_state = wm::GetWindowState(w1.get());
    681   w1_state->Activate();
    682   w1_state->Maximize();
    683   w1_state->Minimize();
    684 
    685   w1->parent()->RemoveChild(w1.get());
    686 
    687   // Do this so that when we Show() the window a resize occurs and we make the
    688   // window active.
    689   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
    690 
    691   ParentWindowInPrimaryRootWindow(w1.get());
    692   delegate.set_window(w1.get());
    693   w1->Show();
    694 }
    695 
    696 // Verifies a window with a transient parent not managed by workspace works.
    697 TEST_F(WorkspaceControllerTest, TransientParent) {
    698   // Normal window with no transient parent.
    699   scoped_ptr<Window> w2(CreateTestWindow());
    700   w2->SetBounds(gfx::Rect(10, 11, 250, 251));
    701   w2->Show();
    702   wm::ActivateWindow(w2.get());
    703 
    704   // Window with a transient parent. We set the transient parent to the root,
    705   // which would never happen but is enough to exercise the bug.
    706   scoped_ptr<Window> w1(CreateTestWindowUnparented());
    707   ::wm::AddTransientChild(
    708       Shell::GetInstance()->GetPrimaryRootWindow(), w1.get());
    709   w1->SetBounds(gfx::Rect(10, 11, 250, 251));
    710   ParentWindowInPrimaryRootWindow(w1.get());
    711   w1->Show();
    712   wm::ActivateWindow(w1.get());
    713 
    714   // The window with the transient parent should get added to the same parent as
    715   // the normal window.
    716   EXPECT_EQ(w2->parent(), w1->parent());
    717 }
    718 
    719 // Test the placement of newly created windows.
    720 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) {
    721   if (!SupportsHostWindowResize())
    722     return;
    723   UpdateDisplay("1600x1200");
    724   // Creating a popup handler here to make sure it does not interfere with the
    725   // existing windows.
    726   gfx::Rect source_browser_bounds(16, 32, 640, 320);
    727   scoped_ptr<aura::Window> browser_window(CreateBrowserLikeWindow(
    728       source_browser_bounds));
    729 
    730   // Creating a popup to make sure it does not interfere with the positioning.
    731   scoped_ptr<aura::Window> browser_popup(CreatePopupLikeWindow(
    732       gfx::Rect(16, 32, 128, 256)));
    733 
    734   browser_window->Show();
    735   browser_popup->Show();
    736 
    737   { // With a shown window it's size should get returned.
    738     scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
    739         source_browser_bounds));
    740     // The position should be right flush.
    741     EXPECT_EQ("960,32 640x320", new_browser_window->bounds().ToString());
    742   }
    743 
    744   { // With the window shown - but more on the right side then on the left
    745     // side (and partially out of the screen), it should default to the other
    746     // side and inside the screen.
    747     gfx::Rect source_browser_bounds(gfx::Rect(1000, 600, 640, 320));
    748     browser_window->SetBounds(source_browser_bounds);
    749 
    750     scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
    751         source_browser_bounds));
    752     // The position should be left & bottom flush.
    753     EXPECT_EQ("0,600 640x320", new_browser_window->bounds().ToString());
    754 
    755     // If the other window was already beyond the point to get right flush
    756     // it will remain where it is.
    757     EXPECT_EQ("1000,600 640x320", browser_window->bounds().ToString());
    758   }
    759 
    760   { // Make sure that popups do not get changed.
    761     scoped_ptr<aura::Window> new_popup_window(CreatePopupLikeWindow(
    762         gfx::Rect(50, 100, 300, 150)));
    763     EXPECT_EQ("50,100 300x150", new_popup_window->bounds().ToString());
    764   }
    765 
    766   browser_window->Hide();
    767   { // If a window is there but not shown the default should be centered.
    768     scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
    769         gfx::Rect(50, 100, 300, 150)));
    770     EXPECT_EQ("650,100 300x150", new_browser_window->bounds().ToString());
    771   }
    772 }
    773 
    774 // Test that adding a second window shifts both the first window and its
    775 // transient child.
    776 TEST_F(WorkspaceControllerTest, AutoPlacingMovesTransientChild) {
    777   // Create an auto-positioned window.
    778   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    779   gfx::Rect desktop_area = window1->parent()->bounds();
    780   wm::GetWindowState(window1.get())->set_window_position_managed(true);
    781   // Hide and then show |window1| to trigger auto-positioning logic.
    782   window1->Hide();
    783   window1->SetBounds(gfx::Rect(16, 32, 300, 300));
    784   window1->Show();
    785 
    786   // |window1| should be horizontally centered.
    787   int x_window1 = (desktop_area.width() - 300) / 2;
    788   EXPECT_EQ(base::IntToString(x_window1) + ",32 300x300",
    789             window1->bounds().ToString());
    790 
    791   // Create a |child| window and make it a transient child of |window1|.
    792   scoped_ptr<Window> child(CreateTestWindowUnparented());
    793   ::wm::AddTransientChild(window1.get(), child.get());
    794   const int x_child = x_window1 + 50;
    795   child->SetBounds(gfx::Rect(x_child, 20, 200, 200));
    796   ParentWindowInPrimaryRootWindow(child.get());
    797   child->Show();
    798   wm::ActivateWindow(child.get());
    799 
    800   // The |child| should be where it was created.
    801   EXPECT_EQ(base::IntToString(x_child) + ",20 200x200",
    802             child->bounds().ToString());
    803 
    804   // Create and show a second window forcing the first window and its child to
    805   // move.
    806   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    807   wm::GetWindowState(window2.get())->set_window_position_managed(true);
    808   // Hide and then show |window2| to trigger auto-positioning logic.
    809   window2->Hide();
    810   window2->SetBounds(gfx::Rect(32, 48, 250, 250));
    811   window2->Show();
    812 
    813   // Check that both |window1| and |child| have moved left.
    814   EXPECT_EQ("0,32 300x300", window1->bounds().ToString());
    815   int x = x_child - x_window1;
    816   EXPECT_EQ(base::IntToString(x) + ",20 200x200", child->bounds().ToString());
    817   // Check that |window2| has moved right.
    818   x = desktop_area.width() - window2->bounds().width();
    819   EXPECT_EQ(base::IntToString(x) + ",48 250x250", window2->bounds().ToString());
    820 }
    821 
    822 // Test the basic auto placement of one and or two windows in a "simulated
    823 // session" of sequential window operations.
    824 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnShowHide) {
    825   // Test 1: In case there is no manageable window, no window should shift.
    826 
    827   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    828   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
    829   gfx::Rect desktop_area = window1->parent()->bounds();
    830 
    831   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    832   // Trigger the auto window placement function by making it visible.
    833   // Note that the bounds are getting changed while it is invisible.
    834   window2->Hide();
    835   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
    836   window2->Show();
    837 
    838   // Check the initial position of the windows is unchanged.
    839   EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
    840   EXPECT_EQ("32,48 256x512", window2->bounds().ToString());
    841 
    842   // Remove the second window and make sure that the first window
    843   // does NOT get centered.
    844   window2.reset();
    845   EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
    846 
    847   wm::WindowState* window1_state = wm::GetWindowState(window1.get());
    848   // Test 2: Set up two managed windows and check their auto positioning.
    849   window1_state->set_window_position_managed(true);
    850 
    851   scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2));
    852   wm::GetWindowState(window3.get())->set_window_position_managed(true);
    853   // To avoid any auto window manager changes due to SetBounds, the window
    854   // gets first hidden and then shown again.
    855   window3->Hide();
    856   window3->SetBounds(gfx::Rect(32, 48, 256, 512));
    857   window3->Show();
    858   // |window1| should be flush left and |window3| flush right.
    859   EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
    860   EXPECT_EQ(base::IntToString(
    861                 desktop_area.width() - window3->bounds().width()) +
    862             ",48 256x512", window3->bounds().ToString());
    863 
    864   // After removing |window3|, |window1| should be centered again.
    865   window3.reset();
    866   EXPECT_EQ(
    867       base::IntToString(
    868           (desktop_area.width() - window1->bounds().width()) / 2) +
    869       ",32 640x320", window1->bounds().ToString());
    870 
    871   // Test 3: Set up a manageable and a non manageable window and check
    872   // positioning.
    873   scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(3));
    874   // To avoid any auto window manager changes due to SetBounds, the window
    875   // gets first hidden and then shown again.
    876   window1->Hide();
    877   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
    878   window4->SetBounds(gfx::Rect(32, 48, 256, 512));
    879   window1->Show();
    880   // |window1| should be centered and |window4| untouched.
    881   EXPECT_EQ(
    882       base::IntToString(
    883           (desktop_area.width() - window1->bounds().width()) / 2) +
    884       ",32 640x320", window1->bounds().ToString());
    885   EXPECT_EQ("32,48 256x512", window4->bounds().ToString());
    886 
    887   // Test4: A single manageable window should get centered.
    888   window4.reset();
    889   window1_state->set_bounds_changed_by_user(false);
    890   // Trigger the auto window placement function by showing (and hiding) it.
    891   window1->Hide();
    892   window1->Show();
    893   // |window1| should be centered.
    894   EXPECT_EQ(
    895       base::IntToString(
    896           (desktop_area.width() - window1->bounds().width()) / 2) +
    897       ",32 640x320", window1->bounds().ToString());
    898 }
    899 
    900 // Test the proper usage of user window movement interaction.
    901 TEST_F(WorkspaceControllerTest, TestUserMovedWindowRepositioning) {
    902   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    903   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
    904   gfx::Rect desktop_area = window1->parent()->bounds();
    905   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
    906   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
    907   window1->Hide();
    908   window2->Hide();
    909   wm::WindowState* window1_state = wm::GetWindowState(window1.get());
    910   wm::WindowState* window2_state = wm::GetWindowState(window2.get());
    911 
    912   window1_state->set_window_position_managed(true);
    913   window2_state->set_window_position_managed(true);
    914   EXPECT_FALSE(window1_state->bounds_changed_by_user());
    915   EXPECT_FALSE(window2_state->bounds_changed_by_user());
    916 
    917   // Check that the current location gets preserved if the user has
    918   // positioned it previously.
    919   window1_state->set_bounds_changed_by_user(true);
    920   window1->Show();
    921   EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
    922   // Flag should be still set.
    923   EXPECT_TRUE(window1_state->bounds_changed_by_user());
    924   EXPECT_FALSE(window2_state->bounds_changed_by_user());
    925 
    926   // Turn on the second window and make sure that both windows are now
    927   // positionable again (user movement cleared).
    928   window2->Show();
    929 
    930   // |window1| should be flush left and |window2| flush right.
    931   EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
    932   EXPECT_EQ(
    933       base::IntToString(desktop_area.width() - window2->bounds().width()) +
    934       ",48 256x512", window2->bounds().ToString());
    935   // FLag should now be reset.
    936   EXPECT_FALSE(window1_state->bounds_changed_by_user());
    937   EXPECT_FALSE(window2_state->bounds_changed_by_user());
    938 
    939   // Going back to one shown window should keep the state.
    940   window1_state->set_bounds_changed_by_user(true);
    941   window2->Hide();
    942   EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
    943   EXPECT_TRUE(window1_state->bounds_changed_by_user());
    944 }
    945 
    946 // Test if the single window will be restored at original position.
    947 TEST_F(WorkspaceControllerTest, TestSingleWindowsRestoredBounds) {
    948   scoped_ptr<aura::Window> window1(
    949       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
    950   scoped_ptr<aura::Window> window2(
    951       CreateTestWindowInShellWithBounds(gfx::Rect(110, 110, 100, 100)));
    952   scoped_ptr<aura::Window> window3(
    953       CreateTestWindowInShellWithBounds(gfx::Rect(120, 120, 100, 100)));
    954   window1->Hide();
    955   window2->Hide();
    956   window3->Hide();
    957   wm::GetWindowState(window1.get())->set_window_position_managed(true);
    958   wm::GetWindowState(window2.get())->set_window_position_managed(true);
    959   wm::GetWindowState(window3.get())->set_window_position_managed(true);
    960 
    961   window1->Show();
    962   wm::ActivateWindow(window1.get());
    963   window2->Show();
    964   wm::ActivateWindow(window2.get());
    965   window3->Show();
    966   wm::ActivateWindow(window3.get());
    967   EXPECT_EQ(0, window1->bounds().x());
    968   EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
    969             window2->bounds().right());
    970   EXPECT_EQ(0, window3->bounds().x());
    971 
    972   window1->Hide();
    973   EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
    974             window2->bounds().right());
    975   EXPECT_EQ(0, window3->bounds().x());
    976 
    977   // Being a single window will retore the original location.
    978   window3->Hide();
    979   wm::ActivateWindow(window2.get());
    980   EXPECT_EQ("110,110 100x100", window2->bounds().ToString());
    981 
    982   // Showing the 3rd will push the 2nd window left.
    983   window3->Show();
    984   wm::ActivateWindow(window3.get());
    985   EXPECT_EQ(0, window2->bounds().x());
    986   EXPECT_EQ(window3->GetRootWindow()->bounds().right(),
    987             window3->bounds().right());
    988 
    989   // Being a single window will retore the original location.
    990   window2->Hide();
    991   EXPECT_EQ("120,120 100x100", window3->bounds().ToString());
    992 }
    993 
    994 // Test that user placed windows go back to their user placement after the user
    995 // closes all other windows.
    996 TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) {
    997   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
    998   gfx::Rect user_pos = gfx::Rect(16, 42, 640, 320);
    999   window1->SetBounds(user_pos);
   1000   wm::WindowState* window1_state = wm::GetWindowState(window1.get());
   1001 
   1002   window1_state->SetPreAutoManageWindowBounds(user_pos);
   1003   gfx::Rect desktop_area = window1->parent()->bounds();
   1004 
   1005   // Create a second window to let the auto manager kick in.
   1006   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
   1007   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
   1008   window1->Hide();
   1009   window2->Hide();
   1010   wm::GetWindowState(window1.get())->set_window_position_managed(true);
   1011   wm::GetWindowState(window2.get())->set_window_position_managed(true);
   1012   window1->Show();
   1013   EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
   1014   window2->Show();
   1015 
   1016   // |window1| should be flush left and |window2| flush right.
   1017   EXPECT_EQ("0," + base::IntToString(user_pos.y()) +
   1018             " 640x320", window1->bounds().ToString());
   1019   EXPECT_EQ(
   1020       base::IntToString(desktop_area.width() - window2->bounds().width()) +
   1021       ",48 256x512", window2->bounds().ToString());
   1022   window2->Hide();
   1023 
   1024   // After the other window get hidden the window has to move back to the
   1025   // previous position and the bounds should still be set and unchanged.
   1026   EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
   1027   ASSERT_TRUE(window1_state->pre_auto_manage_window_bounds());
   1028   EXPECT_EQ(user_pos.ToString(),
   1029             window1_state->pre_auto_manage_window_bounds()->ToString());
   1030 }
   1031 
   1032 // Solo window should be restored to the bounds where a user moved to.
   1033 TEST_F(WorkspaceControllerTest, TestRestoreToUserModifiedBounds) {
   1034   if (!SupportsHostWindowResize())
   1035     return;
   1036 
   1037   UpdateDisplay("400x300");
   1038   gfx::Rect default_bounds(10, 0, 100, 100);
   1039   scoped_ptr<aura::Window> window1(
   1040       CreateTestWindowInShellWithBounds(default_bounds));
   1041   wm::WindowState* window1_state = wm::GetWindowState(window1.get());
   1042   window1->Hide();
   1043   window1_state->set_window_position_managed(true);
   1044   window1->Show();
   1045   // First window is centered.
   1046   EXPECT_EQ("150,0 100x100", window1->bounds().ToString());
   1047   scoped_ptr<aura::Window> window2(
   1048       CreateTestWindowInShellWithBounds(default_bounds));
   1049   wm::WindowState* window2_state = wm::GetWindowState(window2.get());
   1050   window2->Hide();
   1051   window2_state->set_window_position_managed(true);
   1052   window2->Show();
   1053 
   1054   // Auto positioning pushes windows to each sides.
   1055   EXPECT_EQ("0,0 100x100", window1->bounds().ToString());
   1056   EXPECT_EQ("300,0 100x100", window2->bounds().ToString());
   1057 
   1058   window2->Hide();
   1059   // Restores to the center.
   1060   EXPECT_EQ("150,0 100x100", window1->bounds().ToString());
   1061 
   1062   // A user moved the window.
   1063   scoped_ptr<WindowResizer> resizer(CreateWindowResizer(
   1064       window1.get(),
   1065       gfx::Point(),
   1066       HTCAPTION,
   1067       aura::client::WINDOW_MOVE_SOURCE_MOUSE).release());
   1068   gfx::Point location = resizer->GetInitialLocation();
   1069   location.Offset(-50, 0);
   1070   resizer->Drag(location, 0);
   1071   resizer->CompleteDrag();
   1072 
   1073   window1_state->set_bounds_changed_by_user(true);
   1074   window1->SetBounds(gfx::Rect(100, 0, 100, 100));
   1075 
   1076   window2->Show();
   1077   EXPECT_EQ("0,0 100x100", window1->bounds().ToString());
   1078   EXPECT_EQ("300,0 100x100", window2->bounds().ToString());
   1079 
   1080   // Window 1 should be restored to the user modified bounds.
   1081   window2->Hide();
   1082   EXPECT_EQ("100,0 100x100", window1->bounds().ToString());
   1083 }
   1084 
   1085 // Test that a window from normal to minimize will repos the remaining.
   1086 TEST_F(WorkspaceControllerTest, ToMinimizeRepositionsRemaining) {
   1087   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
   1088   wm::WindowState* window1_state = wm::GetWindowState(window1.get());
   1089   window1_state->set_window_position_managed(true);
   1090   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
   1091   gfx::Rect desktop_area = window1->parent()->bounds();
   1092 
   1093   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
   1094   wm::WindowState* window2_state = wm::GetWindowState(window2.get());
   1095   window2_state->set_window_position_managed(true);
   1096   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
   1097 
   1098   window1_state->Minimize();
   1099 
   1100   // |window2| should be centered now.
   1101   EXPECT_TRUE(window2->IsVisible());
   1102   EXPECT_TRUE(window2_state->IsNormalStateType());
   1103   EXPECT_EQ(base::IntToString(
   1104                 (desktop_area.width() - window2->bounds().width()) / 2) +
   1105             ",48 256x512", window2->bounds().ToString());
   1106 
   1107   window1_state->Restore();
   1108   // |window1| should be flush right and |window3| flush left.
   1109   EXPECT_EQ(base::IntToString(
   1110                 desktop_area.width() - window1->bounds().width()) +
   1111             ",32 640x320", window1->bounds().ToString());
   1112   EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
   1113 }
   1114 
   1115 // Test that minimizing an initially maximized window will repos the remaining.
   1116 TEST_F(WorkspaceControllerTest, MaxToMinRepositionsRemaining) {
   1117   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
   1118   wm::WindowState* window1_state = wm::GetWindowState(window1.get());
   1119   window1_state->set_window_position_managed(true);
   1120   gfx::Rect desktop_area = window1->parent()->bounds();
   1121 
   1122   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
   1123   wm::WindowState* window2_state = wm::GetWindowState(window2.get());
   1124   window2_state->set_window_position_managed(true);
   1125   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
   1126 
   1127   window1_state->Maximize();
   1128   window1_state->Minimize();
   1129 
   1130   // |window2| should be centered now.
   1131   EXPECT_TRUE(window2->IsVisible());
   1132   EXPECT_TRUE(window2_state->IsNormalStateType());
   1133   EXPECT_EQ(base::IntToString(
   1134                 (desktop_area.width() - window2->bounds().width()) / 2) +
   1135             ",48 256x512", window2->bounds().ToString());
   1136 }
   1137 
   1138 // Test that nomral, maximize, minimizing will repos the remaining.
   1139 TEST_F(WorkspaceControllerTest, NormToMaxToMinRepositionsRemaining) {
   1140   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
   1141   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
   1142   wm::WindowState* window1_state = wm::GetWindowState(window1.get());
   1143   window1_state->set_window_position_managed(true);
   1144   gfx::Rect desktop_area = window1->parent()->bounds();
   1145 
   1146   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
   1147   wm::WindowState* window2_state = wm::GetWindowState(window2.get());
   1148   window2_state->set_window_position_managed(true);
   1149   window2->SetBounds(gfx::Rect(32, 40, 256, 512));
   1150 
   1151   // Trigger the auto window placement function by showing (and hiding) it.
   1152   window1->Hide();
   1153   window1->Show();
   1154 
   1155   // |window1| should be flush right and |window3| flush left.
   1156   EXPECT_EQ(base::IntToString(
   1157                 desktop_area.width() - window1->bounds().width()) +
   1158             ",32 640x320", window1->bounds().ToString());
   1159   EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
   1160 
   1161   window1_state->Maximize();
   1162   window1_state->Minimize();
   1163 
   1164   // |window2| should be centered now.
   1165   EXPECT_TRUE(window2->IsVisible());
   1166   EXPECT_TRUE(window2_state->IsNormalStateType());
   1167   EXPECT_EQ(base::IntToString(
   1168                 (desktop_area.width() - window2->bounds().width()) / 2) +
   1169             ",40 256x512", window2->bounds().ToString());
   1170 }
   1171 
   1172 // Test that nomral, maximize, normal will repos the remaining.
   1173 TEST_F(WorkspaceControllerTest, NormToMaxToNormRepositionsRemaining) {
   1174   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
   1175   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
   1176   wm::WindowState* window1_state = wm::GetWindowState(window1.get());
   1177   window1_state->set_window_position_managed(true);
   1178   gfx::Rect desktop_area = window1->parent()->bounds();
   1179 
   1180   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
   1181   wm::GetWindowState(window2.get())->set_window_position_managed(true);
   1182   window2->SetBounds(gfx::Rect(32, 40, 256, 512));
   1183 
   1184   // Trigger the auto window placement function by showing (and hiding) it.
   1185   window1->Hide();
   1186   window1->Show();
   1187 
   1188   // |window1| should be flush right and |window3| flush left.
   1189   EXPECT_EQ(base::IntToString(
   1190                 desktop_area.width() - window1->bounds().width()) +
   1191             ",32 640x320", window1->bounds().ToString());
   1192   EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
   1193 
   1194   window1_state->Maximize();
   1195   window1_state->Restore();
   1196 
   1197   // |window1| should be flush right and |window2| flush left.
   1198   EXPECT_EQ(base::IntToString(
   1199                 desktop_area.width() - window1->bounds().width()) +
   1200             ",32 640x320", window1->bounds().ToString());
   1201   EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
   1202 }
   1203 
   1204 // Test that animations are triggered.
   1205 TEST_F(WorkspaceControllerTest, AnimatedNormToMaxToNormRepositionsRemaining) {
   1206   ui::ScopedAnimationDurationScaleMode normal_duration_mode(
   1207       ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
   1208   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
   1209   window1->Hide();
   1210   window1->SetBounds(gfx::Rect(16, 32, 640, 320));
   1211   gfx::Rect desktop_area = window1->parent()->bounds();
   1212   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
   1213   window2->Hide();
   1214   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
   1215 
   1216   wm::GetWindowState(window1.get())->set_window_position_managed(true);
   1217   wm::GetWindowState(window2.get())->set_window_position_managed(true);
   1218   // Make sure nothing is animating.
   1219   window1->layer()->GetAnimator()->StopAnimating();
   1220   window2->layer()->GetAnimator()->StopAnimating();
   1221   window2->Show();
   1222 
   1223   // The second window should now animate.
   1224   EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
   1225   EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
   1226   window2->layer()->GetAnimator()->StopAnimating();
   1227 
   1228   window1->Show();
   1229   EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
   1230   EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
   1231 
   1232   window1->layer()->GetAnimator()->StopAnimating();
   1233   window2->layer()->GetAnimator()->StopAnimating();
   1234   // |window1| should be flush right and |window2| flush left.
   1235   EXPECT_EQ(base::IntToString(
   1236                 desktop_area.width() - window1->bounds().width()) +
   1237             ",32 640x320", window1->bounds().ToString());
   1238   EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
   1239 }
   1240 
   1241 // This tests simulates a browser and an app and verifies the ordering of the
   1242 // windows and layers doesn't get out of sync as various operations occur. Its
   1243 // really testing code in FocusController, but easier to simulate here. Just as
   1244 // with a real browser the browser here has a transient child window
   1245 // (corresponds to the status bubble).
   1246 TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) {
   1247   scoped_ptr<Window> browser(aura::test::CreateTestWindowWithDelegate(
   1248       NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL));
   1249   browser->SetName("browser");
   1250   ParentWindowInPrimaryRootWindow(browser.get());
   1251   browser->Show();
   1252   wm::ActivateWindow(browser.get());
   1253 
   1254   // |status_bubble| is made a transient child of |browser| and as a result
   1255   // owned by |browser|.
   1256   aura::test::TestWindowDelegate* status_bubble_delegate =
   1257       aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate();
   1258   status_bubble_delegate->set_can_focus(false);
   1259   Window* status_bubble =
   1260       aura::test::CreateTestWindowWithDelegate(status_bubble_delegate,
   1261                                                ui::wm::WINDOW_TYPE_POPUP,
   1262                                                gfx::Rect(5, 6, 7, 8),
   1263                                                NULL);
   1264   ::wm::AddTransientChild(browser.get(), status_bubble);
   1265   ParentWindowInPrimaryRootWindow(status_bubble);
   1266   status_bubble->SetName("status_bubble");
   1267 
   1268   scoped_ptr<Window> app(aura::test::CreateTestWindowWithDelegate(
   1269       NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL));
   1270   app->SetName("app");
   1271   ParentWindowInPrimaryRootWindow(app.get());
   1272 
   1273   aura::Window* parent = browser->parent();
   1274 
   1275   app->Show();
   1276   wm::ActivateWindow(app.get());
   1277   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1278 
   1279   // Minimize the app, focus should go the browser.
   1280   app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
   1281   EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
   1282   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1283 
   1284   // Minimize the browser (neither windows are focused).
   1285   browser->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
   1286   EXPECT_FALSE(wm::IsActiveWindow(browser.get()));
   1287   EXPECT_FALSE(wm::IsActiveWindow(app.get()));
   1288   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1289 
   1290   // Show the browser (which should restore it).
   1291   browser->Show();
   1292   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1293 
   1294   // Activate the browser.
   1295   ash::wm::ActivateWindow(browser.get());
   1296   EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
   1297   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1298 
   1299   // Restore the app. This differs from above code for |browser| as internally
   1300   // the app code does this. Restoring this way or using Show() should not make
   1301   // a difference.
   1302   app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
   1303   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1304 
   1305   // Activate the app.
   1306   ash::wm::ActivateWindow(app.get());
   1307   EXPECT_TRUE(wm::IsActiveWindow(app.get()));
   1308   EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
   1309 }
   1310 
   1311 namespace {
   1312 
   1313 // Used by DragMaximizedNonTrackedWindow to track how many times the window
   1314 // hierarchy changes affecting the specified window.
   1315 class DragMaximizedNonTrackedWindowObserver
   1316     : public aura::WindowObserver {
   1317  public:
   1318   DragMaximizedNonTrackedWindowObserver(aura::Window* window)
   1319   : change_count_(0),
   1320     window_(window) {
   1321   }
   1322 
   1323   // Number of times OnWindowHierarchyChanged() has been received.
   1324   void clear_change_count() { change_count_ = 0; }
   1325   int change_count() const {
   1326     return change_count_;
   1327   }
   1328 
   1329   // aura::WindowObserver overrides:
   1330   // Counts number of times a window is reparented. Ignores reparenting into and
   1331   // from a docked container which is expected when a tab is dragged.
   1332   virtual void OnWindowHierarchyChanged(
   1333       const HierarchyChangeParams& params) OVERRIDE {
   1334     if (params.target != window_ ||
   1335         (params.old_parent->id() == kShellWindowId_DefaultContainer &&
   1336          params.new_parent->id() == kShellWindowId_DockedContainer) ||
   1337         (params.old_parent->id() == kShellWindowId_DockedContainer &&
   1338          params.new_parent->id() == kShellWindowId_DefaultContainer)) {
   1339       return;
   1340     }
   1341     change_count_++;
   1342   }
   1343 
   1344  private:
   1345   int change_count_;
   1346   aura::Window* window_;
   1347 
   1348   DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver);
   1349 };
   1350 
   1351 }  // namespace
   1352 
   1353 // Verifies that a new maximized window becomes visible after its activation
   1354 // is requested, even though it does not become activated because a system
   1355 // modal window is active.
   1356 TEST_F(WorkspaceControllerTest, SwitchFromModal) {
   1357   scoped_ptr<Window> modal_window(CreateTestWindowUnparented());
   1358   modal_window->SetBounds(gfx::Rect(10, 11, 21, 22));
   1359   modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
   1360   ParentWindowInPrimaryRootWindow(modal_window.get());
   1361   modal_window->Show();
   1362   wm::ActivateWindow(modal_window.get());
   1363 
   1364   scoped_ptr<Window> maximized_window(CreateTestWindow());
   1365   maximized_window->SetProperty(
   1366       aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
   1367   maximized_window->Show();
   1368   wm::ActivateWindow(maximized_window.get());
   1369   EXPECT_TRUE(maximized_window->IsVisible());
   1370 }
   1371 
   1372 namespace {
   1373 
   1374 // Subclass of WorkspaceControllerTest that runs tests with docked windows
   1375 // enabled and disabled.
   1376 class WorkspaceControllerTestDragging : public WorkspaceControllerTest {
   1377  public:
   1378   WorkspaceControllerTestDragging() {}
   1379   virtual ~WorkspaceControllerTestDragging() {}
   1380 
   1381  private:
   1382   DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging);
   1383 };
   1384 
   1385 }  // namespace
   1386 
   1387 // Verifies that when dragging a window over the shelf overlap is detected
   1388 // during and after the drag.
   1389 TEST_F(WorkspaceControllerTestDragging, DragWindowOverlapShelf) {
   1390   aura::test::TestWindowDelegate delegate;
   1391   delegate.set_window_component(HTCAPTION);
   1392   scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate(
   1393       &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL));
   1394   ParentWindowInPrimaryRootWindow(w1.get());
   1395 
   1396   ShelfLayoutManager* shelf = shelf_layout_manager();
   1397   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
   1398 
   1399   // Drag near the shelf.
   1400   aura::test::EventGenerator generator(
   1401       Shell::GetPrimaryRootWindow(), gfx::Point());
   1402   generator.MoveMouseTo(10, 10);
   1403   generator.PressLeftButton();
   1404   generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 70);
   1405 
   1406   // Shelf should not be in overlapped state.
   1407   EXPECT_FALSE(GetWindowOverlapsShelf());
   1408 
   1409   generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20);
   1410 
   1411   // Shelf should detect overlap. Overlap state stays after mouse is released.
   1412   EXPECT_TRUE(GetWindowOverlapsShelf());
   1413   generator.ReleaseLeftButton();
   1414   EXPECT_TRUE(GetWindowOverlapsShelf());
   1415 }
   1416 
   1417 // Verifies that when dragging a window autohidden shelf stays hidden during
   1418 // and after the drag.
   1419 TEST_F(WorkspaceControllerTestDragging, DragWindowKeepsShelfAutohidden) {
   1420   aura::test::TestWindowDelegate delegate;
   1421   delegate.set_window_component(HTCAPTION);
   1422   scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate(
   1423       &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL));
   1424   ParentWindowInPrimaryRootWindow(w1.get());
   1425 
   1426   ShelfLayoutManager* shelf = shelf_layout_manager();
   1427   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
   1428   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
   1429 
   1430   // Drag very little.
   1431   aura::test::EventGenerator generator(
   1432       Shell::GetPrimaryRootWindow(), gfx::Point());
   1433   generator.MoveMouseTo(10, 10);
   1434   generator.PressLeftButton();
   1435   generator.MoveMouseTo(12, 12);
   1436 
   1437   // Shelf should be hidden during and after the drag.
   1438   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
   1439   generator.ReleaseLeftButton();
   1440   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
   1441 }
   1442 
   1443 // Verifies that events are targeted properly just outside the window edges.
   1444 TEST_F(WorkspaceControllerTest, WindowEdgeHitTest) {
   1445   aura::test::TestWindowDelegate d_first, d_second;
   1446   scoped_ptr<Window> first(aura::test::CreateTestWindowWithDelegate(&d_first,
   1447       123, gfx::Rect(20, 10, 100, 50), NULL));
   1448   ParentWindowInPrimaryRootWindow(first.get());
   1449   first->Show();
   1450 
   1451   scoped_ptr<Window> second(aura::test::CreateTestWindowWithDelegate(&d_second,
   1452       234, gfx::Rect(30, 40, 40, 10), NULL));
   1453   ParentWindowInPrimaryRootWindow(second.get());
   1454   second->Show();
   1455 
   1456   ui::EventTarget* root = first->GetRootWindow();
   1457   ui::EventTargeter* targeter = root->GetEventTargeter();
   1458 
   1459   // The windows overlap, and |second| is on top of |first|. Events targeted
   1460   // slightly outside the edges of the |second| window should still be targeted
   1461   // to |second| to allow resizing the windows easily.
   1462 
   1463   const int kNumPoints = 4;
   1464   struct {
   1465     const char* direction;
   1466     gfx::Point location;
   1467   } points[kNumPoints] = {
   1468     { "left", gfx::Point(28, 45) },  // outside the left edge.
   1469     { "top", gfx::Point(50, 38) },  // outside the top edge.
   1470     { "right", gfx::Point(72, 45) },  // outside the right edge.
   1471     { "bottom", gfx::Point(50, 52) },  // outside the bottom edge.
   1472   };
   1473   // Do two iterations, first without any transform on |second|, and the second
   1474   // time after applying some transform on |second| so that it doesn't get
   1475   // targeted.
   1476   for (int times = 0; times < 2; ++times) {
   1477     SCOPED_TRACE(times == 0 ? "Without transform" : "With transform");
   1478     aura::Window* expected_target = times == 0 ? second.get() : first.get();
   1479     for (int i = 0; i < kNumPoints; ++i) {
   1480       SCOPED_TRACE(points[i].direction);
   1481       const gfx::Point& location = points[i].location;
   1482       ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
   1483                            ui::EF_NONE);
   1484       ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
   1485       EXPECT_EQ(expected_target, target);
   1486 
   1487       ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
   1488                            ui::EventTimeForNow());
   1489       target = targeter->FindTargetForEvent(root, &touch);
   1490       EXPECT_EQ(expected_target, target);
   1491     }
   1492     // Apply a transform on |second|. After the transform is applied, the window
   1493     // should no longer be targeted.
   1494     gfx::Transform transform;
   1495     transform.Translate(70, 40);
   1496     second->SetTransform(transform);
   1497   }
   1498 }
   1499 
   1500 // Verifies mouse event targeting just outside the window edges for panels.
   1501 TEST_F(WorkspaceControllerTest, WindowEdgeMouseHitTestPanel) {
   1502   aura::test::TestWindowDelegate delegate;
   1503   scoped_ptr<Window> window(CreateTestPanel(&delegate,
   1504                                            gfx::Rect(20, 10, 100, 50)));
   1505   ui::EventTarget* root = window->GetRootWindow();
   1506   ui::EventTargeter* targeter = root->GetEventTargeter();
   1507   const gfx::Rect bounds = window->bounds();
   1508   const int kNumPoints = 5;
   1509   struct {
   1510     const char* direction;
   1511     gfx::Point location;
   1512     bool is_target_hit;
   1513   } points[kNumPoints] = {
   1514     { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
   1515     { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
   1516     { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
   1517     { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true },
   1518     { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
   1519   };
   1520   for (int i = 0; i < kNumPoints; ++i) {
   1521     SCOPED_TRACE(points[i].direction);
   1522     const gfx::Point& location = points[i].location;
   1523     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
   1524                          ui::EF_NONE);
   1525     ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
   1526     if (points[i].is_target_hit)
   1527       EXPECT_EQ(window.get(), target);
   1528     else
   1529       EXPECT_NE(window.get(), target);
   1530   }
   1531 }
   1532 
   1533 // Verifies touch event targeting just outside the window edges for panels.
   1534 // The shelf is aligned to the bottom by default, and so touches just below
   1535 // the bottom edge of the panel should not target the panel itself because
   1536 // an AttachedPanelWindowTargeter is installed on the panel container.
   1537 TEST_F(WorkspaceControllerTest, WindowEdgeTouchHitTestPanel) {
   1538   aura::test::TestWindowDelegate delegate;
   1539   scoped_ptr<Window> window(CreateTestPanel(&delegate,
   1540                                             gfx::Rect(20, 10, 100, 50)));
   1541   ui::EventTarget* root = window->GetRootWindow();
   1542   ui::EventTargeter* targeter = root->GetEventTargeter();
   1543   const gfx::Rect bounds = window->bounds();
   1544   const int kNumPoints = 5;
   1545   struct {
   1546     const char* direction;
   1547     gfx::Point location;
   1548     bool is_target_hit;
   1549   } points[kNumPoints] = {
   1550     { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
   1551     { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
   1552     { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
   1553     { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), false },
   1554     { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
   1555   };
   1556   for (int i = 0; i < kNumPoints; ++i) {
   1557     SCOPED_TRACE(points[i].direction);
   1558     const gfx::Point& location = points[i].location;
   1559     ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
   1560                          ui::EventTimeForNow());
   1561     ui::EventTarget* target = targeter->FindTargetForEvent(root, &touch);
   1562     if (points[i].is_target_hit)
   1563       EXPECT_EQ(window.get(), target);
   1564     else
   1565       EXPECT_NE(window.get(), target);
   1566   }
   1567 }
   1568 
   1569 // Verifies events targeting just outside the window edges for docked windows.
   1570 TEST_F(WorkspaceControllerTest, WindowEdgeHitTestDocked) {
   1571   aura::test::TestWindowDelegate delegate;
   1572   // Make window smaller than the minimum docked area so that the window edges
   1573   // are exposed.
   1574   delegate.set_maximum_size(gfx::Size(180, 200));
   1575   scoped_ptr<Window> window(aura::test::CreateTestWindowWithDelegate(&delegate,
   1576       123, gfx::Rect(20, 10, 100, 50), NULL));
   1577   ParentWindowInPrimaryRootWindow(window.get());
   1578   aura::Window* docked_container = Shell::GetContainer(
   1579       window->GetRootWindow(), kShellWindowId_DockedContainer);
   1580   docked_container->AddChild(window.get());
   1581   window->Show();
   1582   ui::EventTarget* root = window->GetRootWindow();
   1583   ui::EventTargeter* targeter = root->GetEventTargeter();
   1584   const gfx::Rect bounds = window->bounds();
   1585   const int kNumPoints = 5;
   1586   struct {
   1587     const char* direction;
   1588     gfx::Point location;
   1589     bool is_target_hit;
   1590   } points[kNumPoints] = {
   1591     { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
   1592     { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
   1593     { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
   1594     { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true },
   1595     { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
   1596   };
   1597   for (int i = 0; i < kNumPoints; ++i) {
   1598     SCOPED_TRACE(points[i].direction);
   1599     const gfx::Point& location = points[i].location;
   1600     ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
   1601                          ui::EF_NONE);
   1602     ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
   1603     if (points[i].is_target_hit)
   1604       EXPECT_EQ(window.get(), target);
   1605     else
   1606       EXPECT_NE(window.get(), target);
   1607 
   1608     ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
   1609                          ui::EventTimeForNow());
   1610     target = targeter->FindTargetForEvent(root, &touch);
   1611     if (points[i].is_target_hit)
   1612       EXPECT_EQ(window.get(), target);
   1613     else
   1614       EXPECT_NE(window.get(), target);
   1615   }
   1616 }
   1617 
   1618 }  // namespace ash
   1619