1// Copyright (c) 2012 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/drag_window_resizer.h"
6
7#include "ash/display/mouse_cursor_event_filter.h"
8#include "ash/root_window_controller.h"
9#include "ash/shelf/shelf_layout_manager.h"
10#include "ash/shell.h"
11#include "ash/shell_window_ids.h"
12#include "ash/test/ash_test_base.h"
13#include "ash/test/cursor_manager_test_api.h"
14#include "ash/wm/drag_window_controller.h"
15#include "ash/wm/window_util.h"
16#include "base/strings/string_number_conversions.h"
17#include "base/strings/stringprintf.h"
18#include "ui/aura/client/aura_constants.h"
19#include "ui/aura/test/test_window_delegate.h"
20#include "ui/aura/window_event_dispatcher.h"
21#include "ui/base/hit_test.h"
22#include "ui/base/ui_base_types.h"
23#include "ui/compositor/layer_tree_owner.h"
24#include "ui/gfx/insets.h"
25#include "ui/gfx/screen.h"
26#include "ui/views/widget/widget.h"
27#include "ui/wm/core/window_util.h"
28
29namespace ash {
30namespace {
31
32const int kRootHeight = 600;
33
34}  // namespace
35
36class DragWindowResizerTest : public test::AshTestBase {
37 public:
38  DragWindowResizerTest() {}
39  virtual ~DragWindowResizerTest() {}
40
41  virtual void SetUp() OVERRIDE {
42    AshTestBase::SetUp();
43    UpdateDisplay(base::StringPrintf("800x%d", kRootHeight));
44
45    aura::Window* root = Shell::GetPrimaryRootWindow();
46    gfx::Rect root_bounds(root->bounds());
47    EXPECT_EQ(kRootHeight, root_bounds.height());
48    EXPECT_EQ(800, root_bounds.width());
49    Shell::GetInstance()->SetDisplayWorkAreaInsets(root, gfx::Insets());
50    window_.reset(new aura::Window(&delegate_));
51    window_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
52    window_->Init(aura::WINDOW_LAYER_NOT_DRAWN);
53    ParentWindowInPrimaryRootWindow(window_.get());
54    window_->set_id(1);
55
56    always_on_top_window_.reset(new aura::Window(&delegate2_));
57    always_on_top_window_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
58    always_on_top_window_->SetProperty(aura::client::kAlwaysOnTopKey, true);
59    always_on_top_window_->Init(aura::WINDOW_LAYER_NOT_DRAWN);
60    ParentWindowInPrimaryRootWindow(always_on_top_window_.get());
61    always_on_top_window_->set_id(2);
62
63    system_modal_window_.reset(new aura::Window(&delegate3_));
64    system_modal_window_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
65    system_modal_window_->SetProperty(aura::client::kModalKey,
66                                      ui::MODAL_TYPE_SYSTEM);
67    system_modal_window_->Init(aura::WINDOW_LAYER_NOT_DRAWN);
68    ParentWindowInPrimaryRootWindow(system_modal_window_.get());
69    system_modal_window_->set_id(3);
70
71    transient_child_ = new aura::Window(&delegate4_);
72    transient_child_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
73    transient_child_->Init(aura::WINDOW_LAYER_NOT_DRAWN);
74    ParentWindowInPrimaryRootWindow(transient_child_);
75    transient_child_->set_id(4);
76
77    transient_parent_.reset(new aura::Window(&delegate5_));
78    transient_parent_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
79    transient_parent_->Init(aura::WINDOW_LAYER_NOT_DRAWN);
80    ParentWindowInPrimaryRootWindow(transient_parent_.get());
81    ::wm::AddTransientChild(transient_parent_.get(), transient_child_);
82    transient_parent_->set_id(5);
83
84    panel_window_.reset(new aura::Window(&delegate6_));
85    panel_window_->SetType(ui::wm::WINDOW_TYPE_PANEL);
86    panel_window_->Init(aura::WINDOW_LAYER_NOT_DRAWN);
87    ParentWindowInPrimaryRootWindow(panel_window_.get());
88  }
89
90  virtual void TearDown() OVERRIDE {
91    window_.reset();
92    always_on_top_window_.reset();
93    system_modal_window_.reset();
94    transient_parent_.reset();
95    panel_window_.reset();
96    AshTestBase::TearDown();
97  }
98
99 protected:
100  gfx::Point CalculateDragPoint(const WindowResizer& resizer,
101                                int delta_x,
102                                int delta_y) const {
103    gfx::Point location = resizer.GetInitialLocation();
104    location.set_x(location.x() + delta_x);
105    location.set_y(location.y() + delta_y);
106    return location;
107  }
108
109  ShelfLayoutManager* shelf_layout_manager() {
110    return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
111  }
112
113  static WindowResizer* CreateDragWindowResizer(
114      aura::Window* window,
115      const gfx::Point& point_in_parent,
116      int window_component) {
117    return CreateWindowResizer(
118        window,
119        point_in_parent,
120        window_component,
121        aura::client::WINDOW_MOVE_SOURCE_MOUSE).release();
122  }
123
124  bool WarpMouseCursorIfNecessary(aura::Window* target_root,
125                                  const gfx::Point& point_in_screen) {
126    MouseCursorEventFilter* event_filter =
127        Shell::GetInstance()->mouse_cursor_filter();
128    bool is_warped = event_filter->WarpMouseCursorIfNecessaryForTest(
129        target_root, point_in_screen);
130    event_filter->reset_was_mouse_warped_for_test();
131    return is_warped;
132  }
133
134  aura::test::TestWindowDelegate delegate_;
135  aura::test::TestWindowDelegate delegate2_;
136  aura::test::TestWindowDelegate delegate3_;
137  aura::test::TestWindowDelegate delegate4_;
138  aura::test::TestWindowDelegate delegate5_;
139  aura::test::TestWindowDelegate delegate6_;
140
141  scoped_ptr<aura::Window> window_;
142  scoped_ptr<aura::Window> always_on_top_window_;
143  scoped_ptr<aura::Window> system_modal_window_;
144  scoped_ptr<aura::Window> panel_window_;
145  aura::Window* transient_child_;
146  scoped_ptr<aura::Window> transient_parent_;
147
148 private:
149  DISALLOW_COPY_AND_ASSIGN(DragWindowResizerTest);
150};
151
152// Verifies a window can be moved from the primary display to another.
153TEST_F(DragWindowResizerTest, WindowDragWithMultiDisplays) {
154  if (!SupportsMultipleDisplays())
155    return;
156
157  // The secondary display is logically on the right, but on the system (e.g. X)
158  // layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
159  UpdateDisplay("800x600,400x300");
160  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
161  ASSERT_EQ(2U, root_windows.size());
162
163  window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
164                             Shell::GetScreen()->GetPrimaryDisplay());
165  EXPECT_EQ(root_windows[0], window_->GetRootWindow());
166  {
167    // Grab (0, 0) of the window.
168    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
169        window_.get(), gfx::Point(), HTCAPTION));
170    ASSERT_TRUE(resizer.get());
171    // Drag the pointer to the right. Once it reaches the right edge of the
172    // primary display, it warps to the secondary.
173    resizer->Drag(CalculateDragPoint(*resizer, 800, 10), 0);
174    resizer->CompleteDrag();
175    // The whole window is on the secondary display now. The parent should be
176    // changed.
177    EXPECT_EQ(root_windows[1], window_->GetRootWindow());
178    EXPECT_EQ("0,10 50x60", window_->bounds().ToString());
179  }
180
181  window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
182                             Shell::GetScreen()->GetPrimaryDisplay());
183  EXPECT_EQ(root_windows[0], window_->GetRootWindow());
184  {
185    // Grab (0, 0) of the window and move the pointer to (790, 10).
186    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
187        window_.get(), gfx::Point(), HTCAPTION));
188    ASSERT_TRUE(resizer.get());
189    resizer->Drag(CalculateDragPoint(*resizer, 795, 10), 0);
190    // Window should be adjusted for minimum visibility (10px) during the drag.
191    EXPECT_EQ("790,10 50x60", window_->bounds().ToString());
192    resizer->CompleteDrag();
193    // Since the pointer is still on the primary root window, the parent should
194    // not be changed.
195    // Window origin should be adjusted for minimum visibility (10px).
196    EXPECT_EQ(root_windows[0], window_->GetRootWindow());
197    EXPECT_EQ("790,10 50x60", window_->bounds().ToString());
198  }
199
200  window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
201                             Shell::GetScreen()->GetPrimaryDisplay());
202  EXPECT_EQ(root_windows[0], window_->GetRootWindow());
203  {
204    // Grab the top-right edge of the window and move the pointer to (0, 10)
205    // in the secondary root window's coordinates.
206    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
207        window_.get(), gfx::Point(49, 0), HTCAPTION));
208    ASSERT_TRUE(resizer.get());
209    resizer->Drag(CalculateDragPoint(*resizer, 751, 10), ui::EF_CONTROL_DOWN);
210    resizer->CompleteDrag();
211    // Since the pointer is on the secondary, the parent should be changed
212    // even though only small fraction of the window is within the secondary
213    // root window's bounds.
214    EXPECT_EQ(root_windows[1], window_->GetRootWindow());
215    // Window origin should be adjusted for minimum visibility (10px).
216    int expected_x = -50 + 10;
217    EXPECT_EQ(base::IntToString(expected_x) + ",10 50x60",
218              window_->bounds().ToString());
219  }
220  // Dropping a window that is larger than the destination work area
221  // will shrink to fit to the work area.
222  window_->SetBoundsInScreen(gfx::Rect(0, 0, 700, 500),
223                             Shell::GetScreen()->GetPrimaryDisplay());
224  EXPECT_EQ(root_windows[0], window_->GetRootWindow());
225  {
226    // Grab the top-right edge of the window and move the pointer to (0, 10)
227    // in the secondary root window's coordinates.
228    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
229        window_.get(), gfx::Point(699, 0), HTCAPTION));
230    ASSERT_TRUE(resizer.get());
231    resizer->Drag(CalculateDragPoint(*resizer, 101, 10), ui::EF_CONTROL_DOWN);
232    resizer->CompleteDrag();
233    EXPECT_EQ(root_windows[1], window_->GetRootWindow());
234    // Window size should be adjusted to fit to the work area
235    EXPECT_EQ("400x253", window_->bounds().size().ToString());
236    gfx::Rect window_bounds_in_screen = window_->GetBoundsInScreen();
237    gfx::Rect intersect(window_->GetRootWindow()->GetBoundsInScreen());
238    intersect.Intersect(window_bounds_in_screen);
239
240    EXPECT_LE(10, intersect.width());
241    EXPECT_LE(10, intersect.height());
242    EXPECT_TRUE(window_bounds_in_screen.Contains(gfx::Point(800, 10)));
243  }
244
245  // Dropping a window that is larger than the destination work area
246  // will shrink to fit to the work area.
247  window_->SetBoundsInScreen(gfx::Rect(0, 0, 700, 500),
248                             Shell::GetScreen()->GetPrimaryDisplay());
249  EXPECT_EQ(root_windows[0], window_->GetRootWindow());
250  {
251    // Grab the top-left edge of the window and move the pointer to (150, 10)
252    // in the secondary root window's coordinates. Make sure the window is
253    // shrink in such a way that it keeps the cursor within.
254    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
255        window_.get(), gfx::Point(0, 0), HTCAPTION));
256    ASSERT_TRUE(resizer.get());
257    resizer->Drag(CalculateDragPoint(*resizer, 799, 10), ui::EF_CONTROL_DOWN);
258    resizer->Drag(CalculateDragPoint(*resizer, 850, 10), ui::EF_CONTROL_DOWN);
259    resizer->CompleteDrag();
260    EXPECT_EQ(root_windows[1], window_->GetRootWindow());
261    // Window size should be adjusted to fit to the work area
262    EXPECT_EQ("400x253", window_->bounds().size().ToString());
263    gfx::Rect window_bounds_in_screen = window_->GetBoundsInScreen();
264    gfx::Rect intersect(window_->GetRootWindow()->GetBoundsInScreen());
265    intersect.Intersect(window_bounds_in_screen);
266    EXPECT_LE(10, intersect.width());
267    EXPECT_LE(10, intersect.height());
268    EXPECT_TRUE(window_bounds_in_screen.Contains(gfx::Point(850, 10)));
269  }
270}
271
272// Verifies that dragging the active window to another display makes the new
273// root window the active root window.
274TEST_F(DragWindowResizerTest, WindowDragWithMultiDisplaysActiveRoot) {
275  if (!SupportsMultipleDisplays())
276    return;
277
278  // The secondary display is logically on the right, but on the system (e.g. X)
279  // layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
280  UpdateDisplay("800x600,800x600");
281  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
282  ASSERT_EQ(2U, root_windows.size());
283
284  aura::test::TestWindowDelegate delegate;
285  scoped_ptr<aura::Window> window(new aura::Window(&delegate));
286  window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
287  window->Init(aura::WINDOW_LAYER_TEXTURED);
288  ParentWindowInPrimaryRootWindow(window.get());
289  window->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
290                            Shell::GetScreen()->GetPrimaryDisplay());
291  window->Show();
292  EXPECT_TRUE(ash::wm::CanActivateWindow(window.get()));
293  ash::wm::ActivateWindow(window.get());
294  EXPECT_EQ(root_windows[0], window->GetRootWindow());
295  EXPECT_EQ(root_windows[0], ash::Shell::GetTargetRootWindow());
296  {
297    // Grab (0, 0) of the window.
298    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
299        window.get(), gfx::Point(), HTCAPTION));
300    ASSERT_TRUE(resizer.get());
301    // Drag the pointer to the right. Once it reaches the right edge of the
302    // primary display, it warps to the secondary.
303    resizer->Drag(CalculateDragPoint(*resizer, 800, 10), 0);
304    resizer->CompleteDrag();
305    // The whole window is on the secondary display now. The parent should be
306    // changed.
307    EXPECT_EQ(root_windows[1], window->GetRootWindow());
308    EXPECT_EQ(root_windows[1], ash::Shell::GetTargetRootWindow());
309  }
310}
311
312// Verifies a window can be moved from the secondary display to primary.
313TEST_F(DragWindowResizerTest, WindowDragWithMultiDisplaysRightToLeft) {
314  if (!SupportsMultipleDisplays())
315    return;
316
317  UpdateDisplay("800x600,800x600");
318  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
319  ASSERT_EQ(2U, root_windows.size());
320
321  window_->SetBoundsInScreen(
322      gfx::Rect(800, 00, 50, 60),
323      Shell::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
324  EXPECT_EQ(root_windows[1], window_->GetRootWindow());
325  {
326    // Grab (0, 0) of the window.
327    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
328        window_.get(), gfx::Point(), HTCAPTION));
329    ASSERT_TRUE(resizer.get());
330    // Move the mouse near the right edge, (798, 0), of the primary display.
331    resizer->Drag(CalculateDragPoint(*resizer, -2, 0), ui::EF_CONTROL_DOWN);
332    resizer->CompleteDrag();
333    EXPECT_EQ(root_windows[0], window_->GetRootWindow());
334    // Window origin should be adjusted for minimum visibility (10px).
335    int expected_x = 800 - 10;
336    EXPECT_EQ(base::IntToString(expected_x) + ",0 50x60",
337              window_->bounds().ToString());
338  }
339}
340
341// Verifies the drag window is shown correctly.
342TEST_F(DragWindowResizerTest, DragWindowController) {
343  if (!SupportsMultipleDisplays())
344    return;
345
346  UpdateDisplay("800x600,800x600");
347  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
348  ASSERT_EQ(2U, root_windows.size());
349
350  window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
351                             Shell::GetScreen()->GetPrimaryDisplay());
352  EXPECT_EQ(root_windows[0], window_->GetRootWindow());
353  EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
354  {
355    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
356        window_.get(), gfx::Point(), HTCAPTION));
357    ASSERT_TRUE(resizer.get());
358    DragWindowResizer* drag_resizer = DragWindowResizer::instance_;
359    ASSERT_TRUE(drag_resizer);
360    EXPECT_FALSE(drag_resizer->drag_window_controller_.get());
361
362    // The pointer is inside the primary root. The drag window controller
363    // should be NULL.
364    resizer->Drag(CalculateDragPoint(*resizer, 10, 10), 0);
365    EXPECT_FALSE(drag_resizer->drag_window_controller_.get());
366
367    // The window spans both root windows.
368    resizer->Drag(CalculateDragPoint(*resizer, 798, 10), 0);
369    DragWindowController* controller =
370        drag_resizer->drag_window_controller_.get();
371    ASSERT_TRUE(controller);
372
373    ASSERT_TRUE(controller->drag_widget_);
374    ui::Layer* drag_layer =
375        controller->drag_widget_->GetNativeWindow()->layer();
376    ASSERT_TRUE(drag_layer);
377    // Check if |resizer->layer_| is properly set to the drag widget.
378    const std::vector<ui::Layer*>& layers = drag_layer->children();
379    EXPECT_FALSE(layers.empty());
380    EXPECT_EQ(controller->layer_owner_->root(), layers.back());
381
382    // |window_| should be opaque since the pointer is still on the primary
383    // root window. The drag window should be semi-transparent.
384    EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
385    ASSERT_TRUE(controller->drag_widget_);
386    EXPECT_GT(1.0f, drag_layer->opacity());
387
388    // Enter the pointer to the secondary display.
389    resizer->Drag(CalculateDragPoint(*resizer, 800, 10), 0);
390    controller = drag_resizer->drag_window_controller_.get();
391    ASSERT_TRUE(controller);
392    // |window_| should be transparent, and the drag window should be opaque.
393    EXPECT_GT(1.0f, window_->layer()->opacity());
394    EXPECT_FLOAT_EQ(1.0f, drag_layer->opacity());
395
396    resizer->CompleteDrag();
397    EXPECT_EQ(root_windows[1], window_->GetRootWindow());
398    EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
399  }
400
401  // Do the same test with RevertDrag().
402  window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
403                             Shell::GetScreen()->GetPrimaryDisplay());
404  EXPECT_EQ(root_windows[0], window_->GetRootWindow());
405  EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
406  {
407    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
408        window_.get(), gfx::Point(), HTCAPTION));
409    ASSERT_TRUE(resizer.get());
410    DragWindowResizer* drag_resizer = DragWindowResizer::instance_;
411    ASSERT_TRUE(drag_resizer);
412    EXPECT_FALSE(drag_resizer->drag_window_controller_.get());
413
414    resizer->Drag(CalculateDragPoint(*resizer, 0, 610), 0);
415    resizer->RevertDrag();
416    EXPECT_EQ(root_windows[0], window_->GetRootWindow());
417    EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
418  }
419}
420
421// Verifies if the resizer sets and resets
422// MouseCursorEventFilter::mouse_warp_mode_ as expected.
423TEST_F(DragWindowResizerTest, WarpMousePointer) {
424  MouseCursorEventFilter* event_filter =
425      Shell::GetInstance()->mouse_cursor_filter();
426  ASSERT_TRUE(event_filter);
427  window_->SetBounds(gfx::Rect(0, 0, 50, 60));
428
429  EXPECT_EQ(MouseCursorEventFilter::WARP_ALWAYS,
430            event_filter->mouse_warp_mode_);
431  {
432    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
433        window_.get(), gfx::Point(), HTCAPTION));
434    // While dragging a window, warp should be allowed.
435    EXPECT_EQ(MouseCursorEventFilter::WARP_DRAG,
436              event_filter->mouse_warp_mode_);
437    resizer->CompleteDrag();
438  }
439  EXPECT_EQ(MouseCursorEventFilter::WARP_ALWAYS,
440            event_filter->mouse_warp_mode_);
441
442  {
443    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
444        window_.get(), gfx::Point(), HTCAPTION));
445    EXPECT_EQ(MouseCursorEventFilter::WARP_DRAG,
446              event_filter->mouse_warp_mode_);
447    resizer->RevertDrag();
448  }
449  EXPECT_EQ(MouseCursorEventFilter::WARP_ALWAYS,
450            event_filter->mouse_warp_mode_);
451
452  {
453    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
454        window_.get(), gfx::Point(), HTRIGHT));
455    // While resizing a window, warp should NOT be allowed.
456    EXPECT_EQ(MouseCursorEventFilter::WARP_NONE,
457              event_filter->mouse_warp_mode_);
458    resizer->CompleteDrag();
459  }
460  EXPECT_EQ(MouseCursorEventFilter::WARP_ALWAYS,
461            event_filter->mouse_warp_mode_);
462
463  {
464    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
465        window_.get(), gfx::Point(), HTRIGHT));
466    EXPECT_EQ(MouseCursorEventFilter::WARP_NONE,
467              event_filter->mouse_warp_mode_);
468    resizer->RevertDrag();
469  }
470  EXPECT_EQ(MouseCursorEventFilter::WARP_ALWAYS,
471            event_filter->mouse_warp_mode_);
472}
473
474// Verifies cursor's device scale factor is updated whe a window is moved across
475// root windows with different device scale factors (http://crbug.com/154183).
476TEST_F(DragWindowResizerTest, CursorDeviceScaleFactor) {
477  if (!SupportsMultipleDisplays())
478    return;
479
480  // The secondary display is logically on the right, but on the system (e.g. X)
481  // layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
482  UpdateDisplay("400x400,800x800*2");
483  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
484  ASSERT_EQ(2U, root_windows.size());
485
486  test::CursorManagerTestApi cursor_test_api(
487      Shell::GetInstance()->cursor_manager());
488  // Move window from the root window with 1.0 device scale factor to the root
489  // window with 2.0 device scale factor.
490  {
491    window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
492                               Shell::GetScreen()->GetPrimaryDisplay());
493    EXPECT_EQ(root_windows[0], window_->GetRootWindow());
494    // Grab (0, 0) of the window.
495    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
496        window_.get(), gfx::Point(), HTCAPTION));
497    EXPECT_EQ(1.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
498    ASSERT_TRUE(resizer.get());
499    resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
500    WarpMouseCursorIfNecessary(root_windows[0], gfx::Point(399, 200));
501    EXPECT_EQ(2.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
502    resizer->CompleteDrag();
503    EXPECT_EQ(2.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
504  }
505
506  // Move window from the root window with 2.0 device scale factor to the root
507  // window with 1.0 device scale factor.
508  {
509    // Make sure the window is on the default container first.
510    aura::Window* default_container =
511        GetRootWindowController(root_windows[1])
512            ->GetContainer(kShellWindowId_DefaultContainer);
513    default_container->AddChild(window_.get());
514    window_->SetBoundsInScreen(
515        gfx::Rect(600, 0, 50, 60),
516        Shell::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
517    EXPECT_EQ(root_windows[1], window_->GetRootWindow());
518    // Grab (0, 0) of the window.
519    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
520        window_.get(), gfx::Point(), HTCAPTION));
521    EXPECT_EQ(2.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
522    ASSERT_TRUE(resizer.get());
523    resizer->Drag(CalculateDragPoint(*resizer, -200, 200), 0);
524    WarpMouseCursorIfNecessary(root_windows[1], gfx::Point(400, 200));
525    EXPECT_EQ(1.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
526    resizer->CompleteDrag();
527    EXPECT_EQ(1.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
528  }
529}
530
531// Verifies several kinds of windows can be moved across displays.
532TEST_F(DragWindowResizerTest, MoveWindowAcrossDisplays) {
533  if (!SupportsMultipleDisplays())
534    return;
535
536  // The secondary display is logically on the right, but on the system (e.g. X)
537  // layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
538  UpdateDisplay("400x400,400x400");
539
540  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
541  ASSERT_EQ(2U, root_windows.size());
542
543  // Normal window can be moved across display.
544  {
545    aura::Window* window = window_.get();
546    window->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
547                              Shell::GetScreen()->GetPrimaryDisplay());
548    // Grab (0, 0) of the window.
549    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
550        window, gfx::Point(), HTCAPTION));
551    ASSERT_TRUE(resizer.get());
552    resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
553    EXPECT_TRUE(WarpMouseCursorIfNecessary(root_windows[0],
554                                           gfx::Point(399, 200)));
555    resizer->CompleteDrag();
556  }
557
558  // Always on top window can be moved across display.
559  {
560    aura::Window* window = always_on_top_window_.get();
561    window->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
562                              Shell::GetScreen()->GetPrimaryDisplay());
563    // Grab (0, 0) of the window.
564    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
565        window, gfx::Point(), HTCAPTION));
566    ASSERT_TRUE(resizer.get());
567    resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
568    EXPECT_TRUE(WarpMouseCursorIfNecessary(root_windows[0],
569                                           gfx::Point(399, 200)));
570    resizer->CompleteDrag();
571  }
572
573  // System modal window can be moved across display.
574  {
575    aura::Window* window = system_modal_window_.get();
576    window->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
577                              Shell::GetScreen()->GetPrimaryDisplay());
578    // Grab (0, 0) of the window.
579    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
580        window, gfx::Point(), HTCAPTION));
581    ASSERT_TRUE(resizer.get());
582    resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
583    EXPECT_TRUE(WarpMouseCursorIfNecessary(root_windows[0],
584                                           gfx::Point(399, 200)));
585    resizer->CompleteDrag();
586  }
587
588  // Transient window cannot be moved across display.
589  {
590    aura::Window* window = transient_child_;
591    window->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
592                              Shell::GetScreen()->GetPrimaryDisplay());
593    // Grab (0, 0) of the window.
594    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
595        window, gfx::Point(), HTCAPTION));
596    ASSERT_TRUE(resizer.get());
597    resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
598    EXPECT_FALSE(WarpMouseCursorIfNecessary(
599        root_windows[0],
600        gfx::Point(399, 200)));
601    resizer->CompleteDrag();
602  }
603
604  // The parent of transient window can be moved across display.
605  {
606    aura::Window* window = transient_parent_.get();
607    window->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
608                              Shell::GetScreen()->GetPrimaryDisplay());
609    // Grab (0, 0) of the window.
610    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
611        window, gfx::Point(), HTCAPTION));
612    ASSERT_TRUE(resizer.get());
613    resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
614    EXPECT_TRUE(WarpMouseCursorIfNecessary(root_windows[0],
615                                           gfx::Point(399, 200)));
616    resizer->CompleteDrag();
617  }
618
619  // Panel window can be moved across display.
620  {
621    aura::Window* window = panel_window_.get();
622    window->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
623                              Shell::GetScreen()->GetPrimaryDisplay());
624    // Grab (0, 0) of the window.
625    scoped_ptr<WindowResizer> resizer(CreateDragWindowResizer(
626        window, gfx::Point(), HTCAPTION));
627    ASSERT_TRUE(resizer.get());
628    resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
629    EXPECT_TRUE(WarpMouseCursorIfNecessary(root_windows[0],
630                                           gfx::Point(399, 200)));
631    resizer->CompleteDrag();
632  }
633}
634
635}  // namespace ash
636