Home | History | Annotate | Download | only in dock
      1 // Copyright (c) 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/dock/docked_window_layout_manager.h"
      6 
      7 #include "ash/ash_switches.h"
      8 #include "ash/root_window_controller.h"
      9 #include "ash/shelf/shelf.h"
     10 #include "ash/shelf/shelf_layout_manager.h"
     11 #include "ash/shelf/shelf_model.h"
     12 #include "ash/shelf/shelf_types.h"
     13 #include "ash/shelf/shelf_widget.h"
     14 #include "ash/shell.h"
     15 #include "ash/shell_window_ids.h"
     16 #include "ash/test/ash_test_base.h"
     17 #include "ash/test/shelf_test_api.h"
     18 #include "ash/test/shelf_view_test_api.h"
     19 #include "ash/test/shell_test_api.h"
     20 #include "ash/test/test_shelf_delegate.h"
     21 #include "ash/wm/coordinate_conversion.h"
     22 #include "ash/wm/panels/panel_layout_manager.h"
     23 #include "ash/wm/window_resizer.h"
     24 #include "ash/wm/window_state.h"
     25 #include "ash/wm/window_util.h"
     26 #include "base/basictypes.h"
     27 #include "base/command_line.h"
     28 #include "base/strings/string_number_conversions.h"
     29 #include "ui/aura/client/aura_constants.h"
     30 #include "ui/aura/test/test_window_delegate.h"
     31 #include "ui/aura/window.h"
     32 #include "ui/aura/window_event_dispatcher.h"
     33 #include "ui/base/hit_test.h"
     34 #include "ui/gfx/screen.h"
     35 #include "ui/views/widget/widget.h"
     36 #include "ui/wm/core/coordinate_conversion.h"
     37 
     38 namespace ash {
     39 
     40 class DockedWindowLayoutManagerTest
     41     : public test::AshTestBase,
     42       public testing::WithParamInterface<ui::wm::WindowType> {
     43  public:
     44   DockedWindowLayoutManagerTest() : window_type_(GetParam()) {}
     45   virtual ~DockedWindowLayoutManagerTest() {}
     46 
     47   virtual void SetUp() OVERRIDE {
     48     AshTestBase::SetUp();
     49     UpdateDisplay("600x600");
     50     ASSERT_TRUE(test::TestShelfDelegate::instance());
     51 
     52     shelf_view_test_.reset(new test::ShelfViewTestAPI(
     53         test::ShelfTestAPI(Shelf::ForPrimaryDisplay()).shelf_view()));
     54     shelf_view_test_->SetAnimationDuration(1);
     55   }
     56 
     57  protected:
     58   enum DockedEdge {
     59     DOCKED_EDGE_NONE,
     60     DOCKED_EDGE_LEFT,
     61     DOCKED_EDGE_RIGHT,
     62   };
     63 
     64   int min_dock_gap() const { return DockedWindowLayoutManager::kMinDockGap; }
     65   int ideal_width() const { return DockedWindowLayoutManager::kIdealWidth; }
     66   int docked_width(const DockedWindowLayoutManager* layout_manager) const {
     67     return layout_manager->docked_width_;
     68   }
     69 
     70   aura::Window* CreateTestWindow(const gfx::Rect& bounds) {
     71     aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
     72         NULL, window_type_, 0, bounds);
     73     if (window_type_ == ui::wm::WINDOW_TYPE_PANEL) {
     74       test::TestShelfDelegate* shelf_delegate =
     75           test::TestShelfDelegate::instance();
     76       shelf_delegate->AddShelfItem(window);
     77       PanelLayoutManager* manager =
     78           static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
     79               layout_manager());
     80       manager->Relayout();
     81     }
     82     return window;
     83   }
     84 
     85   aura::Window* CreateTestWindowWithDelegate(
     86       const gfx::Rect& bounds,
     87       aura::test::TestWindowDelegate* delegate) {
     88     aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
     89         delegate, window_type_, 0, bounds);
     90     if (window_type_ == ui::wm::WINDOW_TYPE_PANEL) {
     91       test::TestShelfDelegate* shelf_delegate =
     92           test::TestShelfDelegate::instance();
     93       shelf_delegate->AddShelfItem(window);
     94       PanelLayoutManager* manager =
     95           static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
     96               layout_manager());
     97       manager->Relayout();
     98     }
     99     return window;
    100   }
    101 
    102   aura::Window* GetPanelContainer(aura::Window* panel) {
    103     return Shell::GetContainer(panel->GetRootWindow(),
    104                                kShellWindowId_PanelContainer);
    105   }
    106 
    107   static WindowResizer* CreateSomeWindowResizer(
    108       aura::Window* window,
    109       const gfx::Point& point_in_parent,
    110       int window_component) {
    111     return CreateWindowResizer(
    112         window,
    113         point_in_parent,
    114         window_component,
    115         aura::client::WINDOW_MOVE_SOURCE_MOUSE).release();
    116   }
    117 
    118   void DragStart(aura::Window* window) {
    119     DragStartAtOffsetFromwindowOrigin(window, 0, 0);
    120   }
    121 
    122   void DragStartAtOffsetFromwindowOrigin(aura::Window* window,
    123                                          int dx, int dy) {
    124     initial_location_in_parent_ =
    125         window->bounds().origin() + gfx::Vector2d(dx, dy);
    126     resizer_.reset(CreateSomeWindowResizer(window,
    127                                            initial_location_in_parent_,
    128                                            HTCAPTION));
    129     ASSERT_TRUE(resizer_.get());
    130   }
    131 
    132   void DragMove(int dx, int dy) {
    133     resizer_->Drag(initial_location_in_parent_ + gfx::Vector2d(dx, dy), 0);
    134   }
    135 
    136   void DragEnd() {
    137     resizer_->CompleteDrag();
    138     resizer_.reset();
    139   }
    140 
    141   void DragRevert() {
    142     resizer_->RevertDrag();
    143     resizer_.reset();
    144   }
    145 
    146   // Panels are parented by panel container during drags.
    147   // Docked windows are parented by dock container during drags.
    148   // All other windows that we are testing here have default container as a
    149   // parent.
    150   int CorrectContainerIdDuringDrag() {
    151     if (window_type_ == ui::wm::WINDOW_TYPE_PANEL)
    152       return kShellWindowId_PanelContainer;
    153     return kShellWindowId_DockedContainer;
    154   }
    155 
    156   // Test dragging the window vertically (to detach if it is a panel) and then
    157   // horizontally to the edge with an added offset from the edge of |dx|.
    158   void DragRelativeToEdge(DockedEdge edge, aura::Window* window, int dx) {
    159     DragVerticallyAndRelativeToEdge(
    160         edge,
    161         window,
    162         dx,
    163         window_type_ == ui::wm::WINDOW_TYPE_PANEL ? -100 : 20);
    164   }
    165 
    166   void DragToVerticalPositionAndToEdge(DockedEdge edge,
    167                                        aura::Window* window,
    168                                        int y) {
    169     DragToVerticalPositionRelativeToEdge(edge, window, 0, y);
    170   }
    171 
    172   void DragToVerticalPositionRelativeToEdge(DockedEdge edge,
    173                                             aura::Window* window,
    174                                             int dx,
    175                                             int y) {
    176     gfx::Rect initial_bounds = window->GetBoundsInScreen();
    177     DragVerticallyAndRelativeToEdge(edge, window, dx, y - initial_bounds.y());
    178   }
    179 
    180   // Detach if our window is a panel, then drag it vertically by |dy| and
    181   // horizontally to the edge with an added offset from the edge of |dx|.
    182   void DragVerticallyAndRelativeToEdge(DockedEdge edge,
    183                                        aura::Window* window,
    184                                        int dx, int dy) {
    185     gfx::Rect initial_bounds = window->GetBoundsInScreen();
    186     // avoid snap by clicking away from the border
    187     ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(window, 25, 5));
    188 
    189     gfx::Rect work_area =
    190         Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
    191     gfx::Point initial_location_in_screen = initial_location_in_parent_;
    192     ::wm::ConvertPointToScreen(window->parent(), &initial_location_in_screen);
    193     // Drag the window left or right to the edge (or almost to it).
    194     if (edge == DOCKED_EDGE_LEFT)
    195       dx += work_area.x() - initial_location_in_screen.x();
    196     else if (edge == DOCKED_EDGE_RIGHT)
    197       dx += work_area.right() - 1 - initial_location_in_screen.x();
    198     DragMove(dx, dy);
    199     EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id());
    200     // Release the mouse and the panel should be attached to the dock.
    201     DragEnd();
    202 
    203     // x-coordinate can get adjusted by snapping or sticking.
    204     // y-coordinate could be changed by possible automatic layout if docked.
    205     if (window->parent()->id() != kShellWindowId_DockedContainer &&
    206         !wm::GetWindowState(window)->HasRestoreBounds()) {
    207       EXPECT_EQ(initial_bounds.y() + dy, window->GetBoundsInScreen().y());
    208     }
    209   }
    210 
    211  private:
    212   scoped_ptr<WindowResizer> resizer_;
    213   scoped_ptr<test::ShelfViewTestAPI> shelf_view_test_;
    214   ui::wm::WindowType window_type_;
    215 
    216   // Location at start of the drag in |window->parent()|'s coordinates.
    217   gfx::Point initial_location_in_parent_;
    218 
    219   DISALLOW_COPY_AND_ASSIGN(DockedWindowLayoutManagerTest);
    220 };
    221 
    222 // Tests that a created window is successfully added to the dock
    223 // layout manager.
    224 TEST_P(DockedWindowLayoutManagerTest, AddOneWindow) {
    225   if (!SupportsHostWindowResize())
    226     return;
    227 
    228   gfx::Rect bounds(0, 0, 201, 201);
    229   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
    230   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
    231 
    232   // The window should be attached and docked at the right edge.
    233   // Its width should shrink or grow to ideal width.
    234   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
    235             window->GetBoundsInScreen().right());
    236   EXPECT_EQ(ideal_width(), window->bounds().width());
    237   EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
    238 }
    239 
    240 // Tests that with a window docked on the left the auto-placing logic in
    241 // RearrangeVisibleWindowOnShow places windows flush with work area edges.
    242 TEST_P(DockedWindowLayoutManagerTest, AutoPlacingLeft) {
    243   if (!SupportsHostWindowResize())
    244     return;
    245 
    246   gfx::Rect bounds(0, 0, 201, 201);
    247   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
    248   DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), 0);
    249 
    250   // The window should be attached and snapped to the right side of the screen.
    251   EXPECT_EQ(window->GetRootWindow()->bounds().x(),
    252             window->GetBoundsInScreen().x());
    253   EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
    254 
    255   DockedWindowLayoutManager* manager = static_cast<DockedWindowLayoutManager*>(
    256       window->parent()->layout_manager());
    257 
    258   // Create two additional windows and test their auto-placement
    259   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(1));
    260   gfx::Rect desktop_area = window1->parent()->bounds();
    261   wm::GetWindowState(window1.get())->set_window_position_managed(true);
    262   window1->Hide();
    263   window1->SetBounds(gfx::Rect(250, 32, 231, 320));
    264   window1->Show();
    265   // |window1| should be centered in work area.
    266   EXPECT_EQ(base::IntToString(
    267       docked_width(manager) + min_dock_gap() +
    268       (desktop_area.width() - docked_width(manager) -
    269        min_dock_gap() - window1->bounds().width()) / 2) +
    270       ",32 231x320", window1->bounds().ToString());
    271 
    272   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(2));
    273   wm::GetWindowState(window2.get())->set_window_position_managed(true);
    274   // To avoid any auto window manager changes due to SetBounds, the window
    275   // gets first hidden and then shown again.
    276   window2->Hide();
    277   window2->SetBounds(gfx::Rect(250, 48, 150, 300));
    278   window2->Show();
    279 
    280   // |window1| should be flush left and |window2| flush right.
    281   EXPECT_EQ(
    282       base::IntToString(docked_width(manager) + min_dock_gap()) +
    283       ",32 231x320", window1->bounds().ToString());
    284   EXPECT_EQ(
    285       base::IntToString(
    286           desktop_area.width() - window2->bounds().width()) +
    287       ",48 150x300", window2->bounds().ToString());
    288 }
    289 
    290 // Tests that with a window docked on the right the auto-placing logic in
    291 // RearrangeVisibleWindowOnShow places windows flush with work area edges.
    292 TEST_P(DockedWindowLayoutManagerTest, AutoPlacingRight) {
    293   if (!SupportsHostWindowResize())
    294     return;
    295 
    296   gfx::Rect bounds(0, 0, 201, 201);
    297   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
    298   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
    299 
    300   // The window should be attached and snapped to the right side of the screen.
    301   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
    302             window->GetBoundsInScreen().right());
    303   EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
    304 
    305   DockedWindowLayoutManager* manager = static_cast<DockedWindowLayoutManager*>(
    306       window->parent()->layout_manager());
    307 
    308   // Create two additional windows and test their auto-placement
    309   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(1));
    310   gfx::Rect desktop_area = window1->parent()->bounds();
    311   wm::GetWindowState(window1.get())->set_window_position_managed(true);
    312   window1->Hide();
    313   window1->SetBounds(gfx::Rect(16, 32, 231, 320));
    314   window1->Show();
    315 
    316   // |window1| should be centered in work area.
    317   EXPECT_EQ(base::IntToString(
    318       (desktop_area.width() - docked_width(manager) -
    319        min_dock_gap() - window1->bounds().width()) / 2) +
    320       ",32 231x320", window1->bounds().ToString());
    321 
    322   scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(2));
    323   wm::GetWindowState(window2.get())->set_window_position_managed(true);
    324   // To avoid any auto window manager changes due to SetBounds, the window
    325   // gets first hidden and then shown again.
    326   window2->Hide();
    327   window2->SetBounds(gfx::Rect(32, 48, 256, 512));
    328   window2->Show();
    329 
    330   // |window1| should be flush left and |window2| flush right.
    331   EXPECT_EQ("0,32 231x320", window1->bounds().ToString());
    332   EXPECT_EQ(
    333       base::IntToString(
    334           desktop_area.width() - window2->bounds().width() -
    335           docked_width(manager) - min_dock_gap()) +
    336       ",48 256x512", window2->bounds().ToString());
    337 }
    338 
    339 // Tests that with a window docked on the right the auto-placing logic in
    340 // RearrangeVisibleWindowOnShow places windows flush with work area edges.
    341 // Test case for the secondary screen.
    342 TEST_P(DockedWindowLayoutManagerTest, AutoPlacingRightSecondScreen) {
    343   if (!SupportsMultipleDisplays() || !SupportsHostWindowResize())
    344     return;
    345 
    346   // Create a dual screen layout.
    347   UpdateDisplay("600x600,600x600");
    348 
    349   gfx::Rect bounds(600, 0, 201, 201);
    350   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
    351   // Drag pointer to the right edge of the second screen.
    352   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
    353 
    354   // The window should be attached and snapped to the right side of the screen.
    355   EXPECT_EQ(window->GetRootWindow()->GetBoundsInScreen().right(),
    356             window->GetBoundsInScreen().right());
    357   EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
    358 
    359   DockedWindowLayoutManager* manager = static_cast<DockedWindowLayoutManager*>(
    360       window->parent()->layout_manager());
    361 
    362   // Create two additional windows and test their auto-placement
    363   bounds = gfx::Rect(616, 32, 231, 320);
    364   scoped_ptr<aura::Window> window1(
    365       CreateTestWindowInShellWithDelegate(NULL, 1, bounds));
    366   gfx::Rect desktop_area = window1->parent()->bounds();
    367   wm::GetWindowState(window1.get())->set_window_position_managed(true);
    368   window1->Hide();
    369   window1->Show();
    370 
    371   // |window1| should be centered in work area.
    372   EXPECT_EQ(base::IntToString(
    373       600 + (desktop_area.width() - docked_width(manager) -
    374           min_dock_gap() - window1->bounds().width()) / 2) +
    375       ",32 231x320", window1->GetBoundsInScreen().ToString());
    376 
    377   bounds = gfx::Rect(632, 48, 256, 512);
    378   scoped_ptr<aura::Window> window2(
    379       CreateTestWindowInShellWithDelegate(NULL, 2, bounds));
    380   wm::GetWindowState(window2.get())->set_window_position_managed(true);
    381   // To avoid any auto window manager changes due to SetBounds, the window
    382   // gets first hidden and then shown again.
    383   window2->Hide();
    384   window2->Show();
    385 
    386   // |window1| should be flush left and |window2| flush right.
    387   EXPECT_EQ("600,32 231x320", window1->GetBoundsInScreen().ToString());
    388   EXPECT_EQ(
    389       base::IntToString(
    390           600 + desktop_area.width() - window2->bounds().width() -
    391           docked_width(manager) - min_dock_gap()) +
    392       ",48 256x512", window2->GetBoundsInScreen().ToString());
    393 }
    394 
    395 // Adds two windows and tests that the gaps are evenly distributed.
    396 TEST_P(DockedWindowLayoutManagerTest, AddTwoWindows) {
    397   if (!SupportsHostWindowResize())
    398     return;
    399 
    400   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
    401   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
    402   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    403   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
    404 
    405   // The windows should be attached and snapped to the right side of the screen.
    406   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
    407             w1->GetBoundsInScreen().right());
    408   EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
    409   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
    410             w2->GetBoundsInScreen().right());
    411   EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
    412 
    413   // Test that the gaps differ at most by a single pixel.
    414   gfx::Rect work_area =
    415       Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
    416   int gap1 = w1->GetBoundsInScreen().y();
    417   int gap2 = w2->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
    418   int gap3 = work_area.bottom() - w2->GetBoundsInScreen().bottom();
    419   EXPECT_EQ(0, gap1);
    420   EXPECT_NEAR(gap2, min_dock_gap(), 1);
    421   EXPECT_EQ(0, gap3);
    422 }
    423 
    424 // Adds two non-overlapping windows and tests layout after a drag.
    425 TEST_P(DockedWindowLayoutManagerTest, TwoWindowsDragging) {
    426   if (!SupportsHostWindowResize())
    427     return;
    428 
    429   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
    430   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
    431   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    432   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
    433 
    434   // The windows should be attached and snapped to the right side of the screen.
    435   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
    436             w1->GetBoundsInScreen().right());
    437   EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
    438   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
    439             w2->GetBoundsInScreen().right());
    440   EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
    441 
    442   // Drag w2 above w1.
    443   ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(w2.get(), 0, 20));
    444   DragMove(0, -w2->bounds().height() / 2 - min_dock_gap() - 1);
    445   DragEnd();
    446 
    447   // Test the new windows order and that the gaps differ at most by a pixel.
    448   gfx::Rect work_area =
    449       Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
    450   int gap1 = w2->GetBoundsInScreen().y() - work_area.y();
    451   int gap2 = w1->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
    452   int gap3 = work_area.bottom() - w1->GetBoundsInScreen().bottom();
    453   EXPECT_EQ(0, gap1);
    454   EXPECT_NEAR(gap2, min_dock_gap(), 1);
    455   EXPECT_EQ(0, gap3);
    456 }
    457 
    458 // Adds three overlapping windows and tests layout after a drag.
    459 TEST_P(DockedWindowLayoutManagerTest, ThreeWindowsDragging) {
    460   if (!SupportsHostWindowResize())
    461     return;
    462   UpdateDisplay("600x1000");
    463 
    464   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 310)));
    465   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    466   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 310)));
    467   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 500);
    468   scoped_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 0, 220, 310)));
    469   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w3.get(), 600);
    470 
    471   // All windows should be attached and snapped to the right side of the screen.
    472   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
    473             w1->GetBoundsInScreen().right());
    474   EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
    475   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
    476             w2->GetBoundsInScreen().right());
    477   EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
    478   EXPECT_EQ(w3->GetRootWindow()->bounds().right(),
    479             w3->GetBoundsInScreen().right());
    480   EXPECT_EQ(kShellWindowId_DockedContainer, w3->parent()->id());
    481 
    482   // Test that the top and bottom windows are clamped in work area and
    483   // that the gaps between the windows differ at most by a pixel.
    484   gfx::Rect work_area =
    485       Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
    486   int gap1 = w1->GetBoundsInScreen().y() - work_area.y();
    487   int gap2 = w2->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
    488   int gap3 = w3->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
    489   int gap4 = work_area.bottom() - w3->GetBoundsInScreen().bottom();
    490   EXPECT_EQ(0, gap1);
    491   EXPECT_NEAR(gap2, min_dock_gap(), 1);
    492   EXPECT_NEAR(gap3, min_dock_gap(), 1);
    493   EXPECT_EQ(0, gap4);
    494 
    495   // Drag w1 below the point where w1 and w2 would swap places. This point is
    496   // half way between the tops of those two windows.
    497   // A bit more vertical drag is needed to account for a window bounds changing
    498   // to its restore bounds during the drag.
    499   ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(w1.get(), 0, 20));
    500   DragMove(0, min_dock_gap() + w2->bounds().height() / 2 + 10);
    501 
    502   // During the drag the windows get rearranged and the top and the bottom
    503   // should be limited by the work area.
    504   EXPECT_EQ(work_area.y(), w2->GetBoundsInScreen().y());
    505   EXPECT_GT(w1->GetBoundsInScreen().y(), w2->GetBoundsInScreen().y());
    506   EXPECT_EQ(work_area.bottom(), w3->GetBoundsInScreen().bottom());
    507   DragEnd();
    508 
    509   // Test the new windows order and that the gaps differ at most by a pixel.
    510   gap1 = w2->GetBoundsInScreen().y() - work_area.y();
    511   gap2 = w1->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
    512   gap3 = w3->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
    513   gap4 = work_area.bottom() - w3->GetBoundsInScreen().bottom();
    514   EXPECT_EQ(0, gap1);
    515   EXPECT_NEAR(gap2, min_dock_gap(), 1);
    516   EXPECT_NEAR(gap3, min_dock_gap(), 1);
    517   EXPECT_EQ(0, gap4);
    518 }
    519 
    520 // Adds three windows in bottom display and tests layout after a drag.
    521 TEST_P(DockedWindowLayoutManagerTest, ThreeWindowsDraggingSecondScreen) {
    522   if (!SupportsMultipleDisplays() || !SupportsHostWindowResize())
    523     return;
    524 
    525   // Create two screen vertical layout.
    526   UpdateDisplay("600x1000,600x1000");
    527   // Layout the secondary display to the bottom of the primary.
    528   DisplayLayout layout(DisplayLayout::BOTTOM, 0);
    529   ASSERT_GT(Shell::GetScreen()->GetNumDisplays(), 1);
    530   Shell::GetInstance()->display_manager()->
    531       SetLayoutForCurrentDisplays(layout);
    532 
    533   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 1000, 201, 310)));
    534   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 1000 + 20);
    535   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 1000, 210, 310)));
    536   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 1000 + 500);
    537   scoped_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 1000, 220, 310)));
    538   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w3.get(), 1000 + 600);
    539 
    540   // All windows should be attached and snapped to the right side of the screen.
    541   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
    542             w1->GetBoundsInScreen().right());
    543   EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
    544   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
    545             w2->GetBoundsInScreen().right());
    546   EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
    547   EXPECT_EQ(w3->GetRootWindow()->bounds().right(),
    548             w3->GetBoundsInScreen().right());
    549   EXPECT_EQ(kShellWindowId_DockedContainer, w3->parent()->id());
    550 
    551   gfx::Rect work_area =
    552       Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
    553   // Test that the top and bottom windows are clamped in work area and
    554   // that the overlaps between the windows differ at most by a pixel.
    555   int gap1 = w1->GetBoundsInScreen().y() - work_area.y();
    556   int gap2 = w2->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
    557   int gap3 = w3->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
    558   int gap4 = work_area.bottom() - w3->GetBoundsInScreen().bottom();
    559   EXPECT_EQ(0, gap1);
    560   EXPECT_NEAR(gap2, min_dock_gap(), 1);
    561   EXPECT_NEAR(gap3, min_dock_gap(), 1);
    562   EXPECT_EQ(0, gap4);
    563 
    564   // Drag w1 below the point where w1 and w2 would swap places. This point is
    565   // half way between the tops of those two windows.
    566   // A bit more vertical drag is needed to account for a window bounds changing
    567   // to its restore bounds during the drag.
    568   ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(w1.get(), 0, 20));
    569   DragMove(0, min_dock_gap() + w2->bounds().height() / 2 + 10);
    570 
    571   // During the drag the windows get rearranged and the top and the bottom
    572   // should be limited by the work area.
    573   EXPECT_EQ(work_area.y(), w2->GetBoundsInScreen().y());
    574   EXPECT_GT(w1->GetBoundsInScreen().y(), w2->GetBoundsInScreen().y());
    575   EXPECT_EQ(work_area.bottom(), w3->GetBoundsInScreen().bottom());
    576   DragEnd();
    577 
    578   // Test the new windows order and that the overlaps differ at most by a pixel.
    579   gap1 = w2->GetBoundsInScreen().y() - work_area.y();
    580   gap2 = w1->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
    581   gap3 = w3->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
    582   gap4 = work_area.bottom() - w3->GetBoundsInScreen().bottom();
    583   EXPECT_EQ(0, gap1);
    584   EXPECT_NEAR(gap2, min_dock_gap(), 1);
    585   EXPECT_NEAR(gap3, min_dock_gap(), 1);
    586   EXPECT_EQ(0, gap4);
    587 }
    588 
    589 // Tests that a second window added to the dock is resized to match.
    590 TEST_P(DockedWindowLayoutManagerTest, TwoWindowsWidthNew) {
    591   if (!SupportsHostWindowResize())
    592     return;
    593 
    594   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
    595   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 280, 202)));
    596   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    597   // The first window should get resized to ideal width.
    598   EXPECT_EQ(ideal_width(), w1->bounds().width());
    599 
    600   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
    601   // The second window should get resized to the existing dock.
    602   EXPECT_EQ(ideal_width(), w2->bounds().width());
    603 }
    604 
    605 // Tests that a first non-resizable window added to the dock is not resized.
    606 TEST_P(DockedWindowLayoutManagerTest, TwoWindowsWidthNonResizableFirst) {
    607   if (!SupportsHostWindowResize())
    608     return;
    609 
    610   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
    611   w1->SetProperty(aura::client::kCanResizeKey, false);
    612   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 280, 202)));
    613   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    614   // The first window should not get resized.
    615   EXPECT_EQ(201, w1->bounds().width());
    616 
    617   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
    618   // The second window should get resized to the first window's width.
    619   EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
    620 }
    621 
    622 // Tests that a second non-resizable window added to the dock is not resized.
    623 TEST_P(DockedWindowLayoutManagerTest, TwoWindowsWidthNonResizableSecond) {
    624   if (!SupportsHostWindowResize())
    625     return;
    626 
    627   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
    628   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 280, 202)));
    629   w2->SetProperty(aura::client::kCanResizeKey, false);
    630   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    631   // The first window should get resized to ideal width.
    632   EXPECT_EQ(ideal_width(), w1->bounds().width());
    633 
    634   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
    635   // The second window should not get resized.
    636   EXPECT_EQ(280, w2->bounds().width());
    637 
    638   // The first window should get resized again - to match the second window.
    639   EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
    640 }
    641 
    642 // Test that restrictions on minimum and maximum width of windows are honored.
    643 TEST_P(DockedWindowLayoutManagerTest, TwoWindowsWidthRestrictions) {
    644   if (!SupportsHostWindowResize())
    645     return;
    646 
    647   aura::test::TestWindowDelegate delegate1;
    648   delegate1.set_maximum_size(gfx::Size(240, 0));
    649   scoped_ptr<aura::Window> w1(CreateTestWindowWithDelegate(
    650       gfx::Rect(0, 0, 201, 201), &delegate1));
    651   aura::test::TestWindowDelegate delegate2;
    652   delegate2.set_minimum_size(gfx::Size(260, 0));
    653   scoped_ptr<aura::Window> w2(CreateTestWindowWithDelegate(
    654       gfx::Rect(0, 0, 280, 202), &delegate2));
    655   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    656   // The first window should get resized to its maximum width.
    657   EXPECT_EQ(240, w1->bounds().width());
    658 
    659   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
    660   // The second window should get resized to its minimum width.
    661   EXPECT_EQ(260, w2->bounds().width());
    662 
    663   // The first window should be centered relative to the second.
    664   EXPECT_EQ(w1->bounds().CenterPoint().x(), w2->bounds().CenterPoint().x());
    665 }
    666 
    667 // Test that restrictions on minimum width of windows are honored.
    668 TEST_P(DockedWindowLayoutManagerTest, WidthMoreThanMax) {
    669   if (!SupportsHostWindowResize())
    670     return;
    671 
    672   aura::test::TestWindowDelegate delegate;
    673   delegate.set_minimum_size(gfx::Size(400, 0));
    674   scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
    675       gfx::Rect(0, 0, 400, 201), &delegate));
    676   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, window.get(), 20);
    677 
    678   // Secondary drag ensures that we are testing the minimum size restriction
    679   // and not just failure to get past the tiling step in SnapSizer.
    680   ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(window.get(),
    681                                                             25, 5));
    682   DragMove(150,0);
    683   DragEnd();
    684 
    685   // The window should not get docked even though it is dragged past the edge.
    686   EXPECT_NE(window->GetRootWindow()->bounds().right(),
    687             window->GetBoundsInScreen().right());
    688   EXPECT_NE(kShellWindowId_DockedContainer, window->parent()->id());
    689 }
    690 
    691 // Docks three windows and tests that the very first window gets minimized.
    692 TEST_P(DockedWindowLayoutManagerTest, ThreeWindowsMinimize) {
    693   if (!SupportsHostWindowResize())
    694     return;
    695 
    696   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
    697   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    698   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
    699   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 200);
    700   scoped_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 0, 220, 204)));
    701   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w3.get(), 300);
    702 
    703   // The last two windows should be attached and snapped to the right edge.
    704   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
    705             w2->GetBoundsInScreen().right());
    706   EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
    707   EXPECT_EQ(w3->GetRootWindow()->bounds().right(),
    708             w3->GetBoundsInScreen().right());
    709   EXPECT_EQ(kShellWindowId_DockedContainer, w3->parent()->id());
    710 
    711   // The first window should get minimized but parented by the dock container.
    712   EXPECT_TRUE(wm::GetWindowState(w1.get())->IsMinimized());
    713   EXPECT_TRUE(wm::GetWindowState(w2.get())->IsNormalStateType());
    714   EXPECT_TRUE(wm::GetWindowState(w3.get())->IsNormalStateType());
    715   EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
    716 }
    717 
    718 // Docks up to three windows and tests that they split vertical space.
    719 TEST_P(DockedWindowLayoutManagerTest, ThreeWindowsSplitHeightEvenly) {
    720   if (!SupportsHostWindowResize())
    721     return;
    722 
    723   UpdateDisplay("600x1000");
    724   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
    725   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    726   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
    727   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 200);
    728 
    729   // The two windows should be attached and snapped to the right edge.
    730   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
    731             w1->GetBoundsInScreen().right());
    732   EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
    733   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
    734             w2->GetBoundsInScreen().right());
    735   EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
    736 
    737   // The two windows should be same size vertically and almost 1/2 of work area.
    738   gfx::Rect work_area =
    739       Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
    740   EXPECT_NEAR(w1->GetBoundsInScreen().height(),
    741               w2->GetBoundsInScreen().height(),
    742               1);
    743   EXPECT_NEAR(work_area.height() / 2, w1->GetBoundsInScreen().height(),
    744               min_dock_gap() * 2);
    745 
    746   // Create and dock the third window.
    747   scoped_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 0, 220, 204)));
    748   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w3.get(), 300);
    749 
    750   // All three windows should be docked and snapped to the right edge.
    751   EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
    752   EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
    753   EXPECT_EQ(kShellWindowId_DockedContainer, w3->parent()->id());
    754 
    755   // All windows should be near same size vertically and about 1/3 of work area.
    756   EXPECT_NEAR(w1->GetBoundsInScreen().height(),
    757               w2->GetBoundsInScreen().height(),
    758               1);
    759   EXPECT_NEAR(w2->GetBoundsInScreen().height(),
    760               w3->GetBoundsInScreen().height(),
    761               1);
    762   EXPECT_NEAR(work_area.height() / 3, w1->GetBoundsInScreen().height(),
    763               min_dock_gap() * 2);
    764 }
    765 
    766 // Docks two windows and tests that restrictions on vertical size are honored.
    767 TEST_P(DockedWindowLayoutManagerTest, TwoWindowsHeightRestrictions) {
    768   if (!SupportsHostWindowResize())
    769     return;
    770 
    771   // The first window is fixed height.
    772   aura::test::TestWindowDelegate delegate1;
    773   delegate1.set_minimum_size(gfx::Size(0, 300));
    774   delegate1.set_maximum_size(gfx::Size(0, 300));
    775   scoped_ptr<aura::Window> w1(CreateTestWindowWithDelegate(
    776       gfx::Rect(0, 0, 201, 300), &delegate1));
    777   // The second window has maximum height.
    778   aura::test::TestWindowDelegate delegate2;
    779   delegate2.set_maximum_size(gfx::Size(0, 100));
    780   scoped_ptr<aura::Window> w2(CreateTestWindowWithDelegate(
    781       gfx::Rect(0, 0, 280, 90), &delegate2));
    782 
    783   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    784   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 200);
    785 
    786   // The two windows should be attached and snapped to the right edge.
    787   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
    788             w1->GetBoundsInScreen().right());
    789   EXPECT_EQ(kShellWindowId_DockedContainer, w1->parent()->id());
    790   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
    791             w2->GetBoundsInScreen().right());
    792   EXPECT_EQ(kShellWindowId_DockedContainer, w2->parent()->id());
    793 
    794   // The two windows should have their heights restricted.
    795   EXPECT_EQ(300, w1->GetBoundsInScreen().height());
    796   EXPECT_EQ(100, w2->GetBoundsInScreen().height());
    797 
    798   // w1 should be more than half of the work area height (even with a margin).
    799   // w2 should be less than half of the work area height (even with a margin).
    800   gfx::Rect work_area =
    801       Shell::GetScreen()->GetDisplayNearestWindow(w1.get()).work_area();
    802   EXPECT_GT(w1->GetBoundsInScreen().height(), work_area.height() / 2 + 10);
    803   EXPECT_LT(w2->GetBoundsInScreen().height(), work_area.height() / 2 - 10);
    804 }
    805 
    806 // Tests that a docked window is moved to primary display when secondary display
    807 // is disconnected and that it stays docked and properly positioned.
    808 TEST_P(DockedWindowLayoutManagerTest, DisplayDisconnectionMovesDocked) {
    809   if (!SupportsMultipleDisplays() || !SupportsHostWindowResize())
    810     return;
    811 
    812   // Create a dual screen layout.
    813   UpdateDisplay("600x700,800x600");
    814 
    815   gfx::Rect bounds(600, 0, 201, 201);
    816   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
    817   // Drag pointer to the right edge of the second screen.
    818   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
    819 
    820   // Simulate disconnection of the secondary display.
    821   UpdateDisplay("600x700");
    822 
    823   // The window should be still docked at the right edge.
    824   // Its height should grow to match the new work area.
    825   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
    826             window->GetBoundsInScreen().right());
    827   EXPECT_EQ(kShellWindowId_DockedContainer, window->parent()->id());
    828   EXPECT_EQ(ideal_width(), window->bounds().width());
    829   gfx::Rect work_area =
    830       Shell::GetScreen()->GetDisplayNearestWindow(window.get()).work_area();
    831   EXPECT_EQ(work_area.height(), window->GetBoundsInScreen().height());
    832 }
    833 
    834 // Tests run twice - on both panels and normal windows
    835 INSTANTIATE_TEST_CASE_P(NormalOrPanel,
    836                         DockedWindowLayoutManagerTest,
    837                         testing::Values(ui::wm::WINDOW_TYPE_NORMAL,
    838                                         ui::wm::WINDOW_TYPE_PANEL));
    839 
    840 }  // namespace ash
    841