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 "chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h"
6
7#include "ash/wm/property_util.h"
8#include "base/bind.h"
9#include "base/callback.h"
10#include "base/command_line.h"
11#include "base/run_loop.h"
12#include "base/strings/string_number_conversions.h"
13#include "chrome/browser/chrome_notification_types.h"
14#include "chrome/browser/ui/browser.h"
15#include "chrome/browser/ui/browser_commands.h"
16#include "chrome/browser/ui/browser_iterator.h"
17#include "chrome/browser/ui/browser_list.h"
18#include "chrome/browser/ui/host_desktop.h"
19#include "chrome/browser/ui/immersive_fullscreen_configuration.h"
20#include "chrome/browser/ui/tabs/tab_strip_model.h"
21#include "chrome/browser/ui/views/frame/browser_view.h"
22#include "chrome/browser/ui/views/tabs/tab.h"
23#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
24#include "chrome/browser/ui/views/tabs/tab_strip.h"
25#include "chrome/common/chrome_switches.h"
26#include "chrome/test/base/in_process_browser_test.h"
27#include "chrome/test/base/interactive_test_utils.h"
28#include "chrome/test/base/ui_test_utils.h"
29#include "content/public/browser/notification_details.h"
30#include "content/public/browser/notification_observer.h"
31#include "content/public/browser/notification_service.h"
32#include "content/public/browser/notification_source.h"
33#include "content/public/browser/web_contents.h"
34#include "ui/base/test/ui_controls.h"
35#include "ui/gfx/screen.h"
36#include "ui/views/view.h"
37#include "ui/views/widget/widget.h"
38
39#if defined(USE_ASH)
40#include "ash/display/display_controller.h"
41#include "ash/display/display_manager.h"
42#include "ash/shell.h"
43#include "ash/test/cursor_manager_test_api.h"
44#include "ash/wm/coordinate_conversion.h"
45#include "ash/wm/window_util.h"
46#include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
47#include "ui/aura/client/screen_position_client.h"
48#include "ui/aura/root_window.h"
49#include "ui/aura/test/event_generator.h"
50#endif
51
52using content::WebContents;
53
54namespace test {
55
56namespace {
57
58const char kTabDragControllerInteractiveUITestUserDataKey[] =
59    "TabDragControllerInteractiveUITestUserData";
60
61class TabDragControllerInteractiveUITestUserData
62    : public base::SupportsUserData::Data {
63 public:
64  explicit TabDragControllerInteractiveUITestUserData(int id) : id_(id) {}
65  virtual ~TabDragControllerInteractiveUITestUserData() {}
66  int id() { return id_; }
67
68 private:
69  int id_;
70};
71
72}  // namespace
73
74class QuitDraggingObserver : public content::NotificationObserver {
75 public:
76  QuitDraggingObserver() {
77    registrar_.Add(this, chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
78                   content::NotificationService::AllSources());
79  }
80
81  virtual void Observe(int type,
82                       const content::NotificationSource& source,
83                       const content::NotificationDetails& details) OVERRIDE {
84    DCHECK_EQ(chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE, type);
85    base::MessageLoopForUI::current()->Quit();
86    delete this;
87  }
88
89 private:
90  virtual ~QuitDraggingObserver() {}
91
92  content::NotificationRegistrar registrar_;
93
94  DISALLOW_COPY_AND_ASSIGN(QuitDraggingObserver);
95};
96
97gfx::Point GetCenterInScreenCoordinates(const views::View* view) {
98  gfx::Point center(view->width() / 2, view->height() / 2);
99  views::View::ConvertPointToScreen(view, &center);
100  return center;
101}
102
103void SetID(WebContents* web_contents, int id) {
104  web_contents->SetUserData(&kTabDragControllerInteractiveUITestUserDataKey,
105                            new TabDragControllerInteractiveUITestUserData(id));
106}
107
108void ResetIDs(TabStripModel* model, int start) {
109  for (int i = 0; i < model->count(); ++i)
110    SetID(model->GetWebContentsAt(i), start + i);
111}
112
113std::string IDString(TabStripModel* model) {
114  std::string result;
115  for (int i = 0; i < model->count(); ++i) {
116    if (i != 0)
117      result += " ";
118    WebContents* contents = model->GetWebContentsAt(i);
119    TabDragControllerInteractiveUITestUserData* user_data =
120        static_cast<TabDragControllerInteractiveUITestUserData*>(
121            contents->GetUserData(
122                &kTabDragControllerInteractiveUITestUserDataKey));
123    if (user_data)
124      result += base::IntToString(user_data->id());
125    else
126      result += "?";
127  }
128  return result;
129}
130
131// Creates a listener that quits the message loop when no longer dragging.
132void QuitWhenNotDraggingImpl() {
133  new QuitDraggingObserver();  // QuitDraggingObserver deletes itself.
134}
135
136TabStrip* GetTabStripForBrowser(Browser* browser) {
137  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
138  return static_cast<TabStrip*>(browser_view->tabstrip());
139}
140
141}  // namespace test
142
143using test::GetCenterInScreenCoordinates;
144using test::SetID;
145using test::ResetIDs;
146using test::IDString;
147using test::GetTabStripForBrowser;
148
149TabDragControllerTest::TabDragControllerTest()
150    : native_browser_list(BrowserList::GetInstance(
151                              chrome::HOST_DESKTOP_TYPE_NATIVE)) {
152}
153
154TabDragControllerTest::~TabDragControllerTest() {
155}
156
157void TabDragControllerTest::SetUp() {
158  // TODO(danakj): Remove this when the tests are not flaky (crbug.com/270065)
159  // or we use test contexts in the renderer to keep things fast enough to
160  // avoid the flake (crbug.com/270918).
161  UseRealGLBindings();
162
163  InProcessBrowserTest::SetUp();
164}
165
166void TabDragControllerTest::StopAnimating(TabStrip* tab_strip) {
167  tab_strip->StopAnimating(true);
168}
169
170void TabDragControllerTest::AddTabAndResetBrowser(Browser* browser) {
171  AddBlankTabAndShow(browser);
172  StopAnimating(GetTabStripForBrowser(browser));
173  ResetIDs(browser->tab_strip_model(), 0);
174}
175
176Browser* TabDragControllerTest::CreateAnotherWindowBrowserAndRelayout() {
177  // Create another browser.
178  Browser* browser2 = CreateBrowser(browser()->profile());
179  ResetIDs(browser2->tab_strip_model(), 100);
180
181  // Resize the two windows so they're right next to each other.
182  gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
183      browser()->window()->GetNativeWindow()).work_area();
184  gfx::Size half_size =
185      gfx::Size(work_area.width() / 3 - 10, work_area.height() / 2 - 10);
186  browser()->window()->SetBounds(gfx::Rect(work_area.origin(), half_size));
187  browser2->window()->SetBounds(gfx::Rect(
188      work_area.x() + half_size.width(), work_area.y(),
189      half_size.width(), half_size.height()));
190  return browser2;
191}
192
193namespace {
194
195enum InputSource {
196  INPUT_SOURCE_MOUSE = 0,
197  INPUT_SOURCE_TOUCH = 1
198};
199
200int GetDetachY(TabStrip* tab_strip) {
201  return std::max(TabDragController::kTouchVerticalDetachMagnetism,
202                  TabDragController::kVerticalDetachMagnetism) +
203      tab_strip->height() + 1;
204}
205
206bool GetTrackedByWorkspace(Browser* browser) {
207#if !defined(USE_ASH) || defined(OS_WIN)  // TODO(win_ash)
208  return true;
209#else
210  return ash::GetTrackedByWorkspace(browser->window()->GetNativeWindow());
211#endif
212}
213
214}  // namespace
215
216#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
217class ScreenEventGeneratorDelegate : public aura::test::EventGeneratorDelegate {
218 public:
219  explicit ScreenEventGeneratorDelegate(aura::RootWindow* root_window)
220      : root_window_(root_window) {}
221  virtual ~ScreenEventGeneratorDelegate() {}
222
223  // EventGeneratorDelegate overrides:
224  virtual aura::RootWindow* GetRootWindowAt(
225      const gfx::Point& point) const OVERRIDE {
226    return root_window_;
227  }
228
229  virtual aura::client::ScreenPositionClient* GetScreenPositionClient(
230      const aura::Window* window) const OVERRIDE {
231    return aura::client::GetScreenPositionClient(root_window_);
232  }
233
234 private:
235  aura::RootWindow* root_window_;
236
237  DISALLOW_COPY_AND_ASSIGN(ScreenEventGeneratorDelegate);
238};
239
240#endif
241
242class DetachToBrowserTabDragControllerTest
243    : public TabDragControllerTest,
244      public ::testing::WithParamInterface<const char*> {
245 public:
246  DetachToBrowserTabDragControllerTest() {}
247
248  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
249    command_line->AppendSwitch(switches::kTabBrowserDragging);
250  }
251
252  virtual void SetUpOnMainThread() OVERRIDE {
253#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
254    event_generator_.reset(new aura::test::EventGenerator(
255                               ash::Shell::GetPrimaryRootWindow()));
256#endif
257  }
258
259  InputSource input_source() const {
260    return !strcmp(GetParam(), "mouse") ?
261        INPUT_SOURCE_MOUSE : INPUT_SOURCE_TOUCH;
262  }
263
264  // Set root window from a point in screen coordinates
265  void SetEventGeneratorRootWindow(const gfx::Point& point) {
266    if (input_source() == INPUT_SOURCE_MOUSE)
267      return;
268#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
269    event_generator_.reset(new aura::test::EventGenerator(
270        new ScreenEventGeneratorDelegate(ash::wm::GetRootWindowAt(point))));
271#endif
272  }
273
274  // The following methods update one of the mouse or touch input depending upon
275  // the InputSource.
276  bool PressInput(const gfx::Point& location) {
277    if (input_source() == INPUT_SOURCE_MOUSE) {
278      return ui_test_utils::SendMouseMoveSync(location) &&
279          ui_test_utils::SendMouseEventsSync(
280              ui_controls::LEFT, ui_controls::DOWN);
281    }
282#if defined(USE_ASH)  && !defined(OS_WIN)  // TODO(win_ash)
283    event_generator_->set_current_location(location);
284    event_generator_->PressTouch();
285#else
286    NOTREACHED();
287#endif
288    return true;
289  }
290
291  bool DragInputTo(const gfx::Point& location) {
292    if (input_source() == INPUT_SOURCE_MOUSE)
293      return ui_test_utils::SendMouseMoveSync(location);
294#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
295    event_generator_->MoveTouch(location);
296#else
297    NOTREACHED();
298#endif
299    return true;
300  }
301
302  bool DragInputToAsync(const gfx::Point& location) {
303    if (input_source() == INPUT_SOURCE_MOUSE)
304      return ui_controls::SendMouseMove(location.x(), location.y());
305#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
306    event_generator_->MoveTouch(location);
307#else
308    NOTREACHED();
309#endif
310    return true;
311  }
312
313  bool DragInputToNotifyWhenDone(int x,
314                                 int y,
315                                 const base::Closure& task) {
316    if (input_source() == INPUT_SOURCE_MOUSE)
317      return ui_controls::SendMouseMoveNotifyWhenDone(x, y, task);
318#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
319    base::MessageLoop::current()->PostTask(FROM_HERE, task);
320    event_generator_->MoveTouch(gfx::Point(x, y));
321#else
322    NOTREACHED();
323#endif
324    return true;
325  }
326
327  bool ReleaseInput() {
328    if (input_source() == INPUT_SOURCE_MOUSE) {
329      return ui_test_utils::SendMouseEventsSync(
330              ui_controls::LEFT, ui_controls::UP);
331    }
332#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
333    event_generator_->ReleaseTouch();
334#else
335    NOTREACHED();
336#endif
337    return true;
338  }
339
340  bool ReleaseMouseAsync() {
341    return input_source() == INPUT_SOURCE_MOUSE &&
342        ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::UP);
343  }
344
345  void QuitWhenNotDragging() {
346    if (input_source() == INPUT_SOURCE_MOUSE) {
347      // Schedule observer to quit message loop when done dragging. This has to
348      // be async so the message loop can run.
349      test::QuitWhenNotDraggingImpl();
350      base::MessageLoop::current()->Run();
351    } else {
352      // Touch events are sync, so we know we're not in a drag session. But some
353      // tests rely on the browser fully closing, which is async. So, run all
354      // pending tasks.
355      base::RunLoop run_loop;
356      run_loop.RunUntilIdle();
357    }
358  }
359
360  void AddBlankTabAndShow(Browser* browser) {
361    InProcessBrowserTest::AddBlankTabAndShow(browser);
362  }
363
364  Browser* browser() const { return InProcessBrowserTest::browser(); }
365
366 private:
367#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
368  scoped_ptr<aura::test::EventGenerator> event_generator_;
369#endif
370
371  DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTest);
372};
373
374// Creates a browser with two tabs, drags the second to the first.
375// TODO(sky): this won't work with touch as it requires a long press.
376IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
377                       DISABLED_DragInSameWindow) {
378  AddTabAndResetBrowser(browser());
379
380  TabStrip* tab_strip = GetTabStripForBrowser(browser());
381  TabStripModel* model = browser()->tab_strip_model();
382
383  gfx::Point tab_1_center(GetCenterInScreenCoordinates(tab_strip->tab_at(1)));
384  ASSERT_TRUE(PressInput(tab_1_center));
385  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
386  ASSERT_TRUE(DragInputTo(tab_0_center));
387  ASSERT_TRUE(ReleaseInput());
388  EXPECT_EQ("1 0", IDString(model));
389  EXPECT_FALSE(TabDragController::IsActive());
390  EXPECT_FALSE(tab_strip->IsDragSessionActive());
391}
392
393namespace {
394
395// Invoked from the nested message loop.
396void DragToSeparateWindowStep2(DetachToBrowserTabDragControllerTest* test,
397                               TabStrip* not_attached_tab_strip,
398                               TabStrip* target_tab_strip) {
399  ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
400  ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
401  ASSERT_TRUE(TabDragController::IsActive());
402
403  // Drag to target_tab_strip. This should stop the nested loop from dragging
404  // the window.
405  gfx::Point target_point(target_tab_strip->width() -1,
406                          target_tab_strip->height() / 2);
407  views::View::ConvertPointToScreen(target_tab_strip, &target_point);
408  ASSERT_TRUE(test->DragInputToAsync(target_point));
409}
410
411}  // namespace
412
413#if defined(OS_WIN) && defined(USE_AURA)
414#define MAYBE_DragToSeparateWindow DISABLED_DragToSeparateWindow
415#else
416#define MAYBE_DragToSeparateWindow DragToSeparateWindow
417#endif
418
419// Creates two browsers, drags from first into second.
420IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
421                       MAYBE_DragToSeparateWindow) {
422  TabStrip* tab_strip = GetTabStripForBrowser(browser());
423
424  // Add another tab to browser().
425  AddTabAndResetBrowser(browser());
426
427  // Create another browser.
428  Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
429  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
430
431  // Move to the first tab and drag it enough so that it detaches, but not
432  // enough that it attaches to browser2.
433  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
434  ASSERT_TRUE(PressInput(tab_0_center));
435  ASSERT_TRUE(DragInputToNotifyWhenDone(
436                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
437                  base::Bind(&DragToSeparateWindowStep2,
438                             this, tab_strip, tab_strip2)));
439  QuitWhenNotDragging();
440
441  // Should now be attached to tab_strip2.
442  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
443  ASSERT_FALSE(tab_strip->IsDragSessionActive());
444  ASSERT_TRUE(TabDragController::IsActive());
445  EXPECT_TRUE(GetTrackedByWorkspace(browser()));
446
447  // Release the mouse, stopping the drag session.
448  ASSERT_TRUE(ReleaseInput());
449  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
450  ASSERT_FALSE(tab_strip->IsDragSessionActive());
451  ASSERT_FALSE(TabDragController::IsActive());
452  EXPECT_EQ("100 0", IDString(browser2->tab_strip_model()));
453  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
454  EXPECT_TRUE(GetTrackedByWorkspace(browser2));
455
456  // Both windows should not be maximized
457  EXPECT_FALSE(browser()->window()->IsMaximized());
458  EXPECT_FALSE(browser2->window()->IsMaximized());
459}
460
461namespace {
462
463void DetachToOwnWindowStep2(DetachToBrowserTabDragControllerTest* test) {
464  if (test->input_source() == INPUT_SOURCE_TOUCH)
465    ASSERT_TRUE(test->ReleaseInput());
466}
467
468#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
469bool IsWindowPositionManaged(aura::Window* window) {
470  return ash::wm::IsWindowPositionManaged(window);
471}
472bool HasUserChangedWindowPositionOrSize(aura::Window* window) {
473  return ash::wm::HasUserChangedWindowPositionOrSize(window);
474}
475#else
476bool IsWindowPositionManaged(gfx::NativeWindow window) {
477  return true;
478}
479bool HasUserChangedWindowPositionOrSize(gfx::NativeWindow window) {
480  return false;
481}
482#endif
483
484}  // namespace
485
486// Drags from browser to separate window and releases mouse.
487IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
488                       DetachToOwnWindow) {
489  const gfx::Rect initial_bounds(browser()->window()->GetBounds());
490  // Add another tab.
491  AddTabAndResetBrowser(browser());
492  TabStrip* tab_strip = GetTabStripForBrowser(browser());
493
494  // Move to the first tab and drag it enough so that it detaches.
495  gfx::Point tab_0_center(
496      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
497  ASSERT_TRUE(PressInput(tab_0_center));
498  ASSERT_TRUE(DragInputToNotifyWhenDone(
499                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
500                  base::Bind(&DetachToOwnWindowStep2, this)));
501  if (input_source() == INPUT_SOURCE_MOUSE) {
502    ASSERT_TRUE(ReleaseMouseAsync());
503    QuitWhenNotDragging();
504  }
505
506  // Should no longer be dragging.
507  ASSERT_FALSE(tab_strip->IsDragSessionActive());
508  ASSERT_FALSE(TabDragController::IsActive());
509
510  // There should now be another browser.
511  ASSERT_EQ(2u, native_browser_list->size());
512  Browser* new_browser = native_browser_list->get(1);
513  ASSERT_TRUE(new_browser->window()->IsActive());
514  TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
515  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
516
517  EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
518  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
519
520  // The bounds of the initial window should not have changed.
521  EXPECT_EQ(initial_bounds.ToString(),
522            browser()->window()->GetBounds().ToString());
523
524  EXPECT_TRUE(GetTrackedByWorkspace(browser()));
525  EXPECT_TRUE(GetTrackedByWorkspace(new_browser));
526  // After this both windows should still be managable.
527  EXPECT_TRUE(IsWindowPositionManaged(browser()->window()->GetNativeWindow()));
528  EXPECT_TRUE(IsWindowPositionManaged(
529      new_browser->window()->GetNativeWindow()));
530
531  // Both windows should not be maximized
532  EXPECT_FALSE(browser()->window()->IsMaximized());
533  EXPECT_FALSE(new_browser->window()->IsMaximized());
534}
535
536// Drags from browser to separate window and releases mouse.
537IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
538                       DetachToOwnWindowFromMaximizedWindow) {
539  if (!TabDragController::ShouldDetachIntoNewBrowser()) {
540    VLOG(1)
541        << "Skipping DetachToOwnWindowFromMaximizedWindow on this platform.";
542    return;
543  }
544
545  // Maximize the initial browser window.
546  browser()->window()->Maximize();
547  ASSERT_TRUE(browser()->window()->IsMaximized());
548
549  // Add another tab.
550  AddTabAndResetBrowser(browser());
551  TabStrip* tab_strip = GetTabStripForBrowser(browser());
552
553  // Move to the first tab and drag it enough so that it detaches.
554  gfx::Point tab_0_center(
555      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
556  ASSERT_TRUE(PressInput(tab_0_center));
557  ASSERT_TRUE(DragInputToNotifyWhenDone(
558                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
559                  base::Bind(&DetachToOwnWindowStep2, this)));
560  if (input_source() == INPUT_SOURCE_MOUSE) {
561    ASSERT_TRUE(ReleaseMouseAsync());
562    QuitWhenNotDragging();
563  }
564
565  // Should no longer be dragging.
566  ASSERT_FALSE(tab_strip->IsDragSessionActive());
567  ASSERT_FALSE(TabDragController::IsActive());
568
569  // There should now be another browser.
570  ASSERT_EQ(2u, native_browser_list->size());
571  Browser* new_browser = native_browser_list->get(1);
572  ASSERT_TRUE(new_browser->window()->IsActive());
573  TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
574  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
575
576  EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
577  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
578
579  // The bounds of the initial window should not have changed.
580  EXPECT_TRUE(browser()->window()->IsMaximized());
581
582  EXPECT_TRUE(GetTrackedByWorkspace(browser()));
583  EXPECT_TRUE(GetTrackedByWorkspace(new_browser));
584  // After this both windows should still be managable.
585  EXPECT_TRUE(IsWindowPositionManaged(browser()->window()->GetNativeWindow()));
586  EXPECT_TRUE(IsWindowPositionManaged(
587      new_browser->window()->GetNativeWindow()));
588
589  // The new window should not be maximized.
590  EXPECT_FALSE(new_browser->window()->IsMaximized());
591}
592
593// Deletes a tab being dragged before the user moved enough to start a drag.
594IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
595                       DeleteBeforeStartedDragging) {
596  // Add another tab.
597  AddTabAndResetBrowser(browser());
598  TabStrip* tab_strip = GetTabStripForBrowser(browser());
599
600  // Click on the first tab, but don't move it.
601  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
602  ASSERT_TRUE(PressInput(tab_0_center));
603
604  // Should be dragging.
605  ASSERT_TRUE(tab_strip->IsDragSessionActive());
606  ASSERT_TRUE(TabDragController::IsActive());
607
608  // Delete the tab being dragged.
609  delete browser()->tab_strip_model()->GetWebContentsAt(0);
610
611  // Should have canceled dragging.
612  ASSERT_FALSE(tab_strip->IsDragSessionActive());
613  ASSERT_FALSE(TabDragController::IsActive());
614
615  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
616  EXPECT_TRUE(GetTrackedByWorkspace(browser()));
617}
618
619// Deletes a tab being dragged while still attached.
620IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
621                       DeleteTabWhileAttached) {
622  // Add another tab.
623  AddTabAndResetBrowser(browser());
624  TabStrip* tab_strip = GetTabStripForBrowser(browser());
625
626  // Click on the first tab and move it enough so that it starts dragging but is
627  // still attached.
628  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
629  ASSERT_TRUE(PressInput(tab_0_center));
630  ASSERT_TRUE(DragInputTo(
631                  gfx::Point(tab_0_center.x() + 20, tab_0_center.y())));
632
633  // Should be dragging.
634  ASSERT_TRUE(tab_strip->IsDragSessionActive());
635  ASSERT_TRUE(TabDragController::IsActive());
636
637  // Delete the tab being dragged.
638  delete browser()->tab_strip_model()->GetWebContentsAt(0);
639
640  // Should have canceled dragging.
641  ASSERT_FALSE(tab_strip->IsDragSessionActive());
642  ASSERT_FALSE(TabDragController::IsActive());
643
644  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
645
646  EXPECT_TRUE(GetTrackedByWorkspace(browser()));
647}
648
649namespace {
650
651void DeleteWhileDetachedStep2(WebContents* tab) {
652  delete tab;
653}
654
655}  // namespace
656
657// Deletes a tab being dragged after dragging a tab so that a new window is
658// created.
659IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
660                       DeleteTabWhileDetached) {
661  // Add another tab.
662  AddTabAndResetBrowser(browser());
663  TabStrip* tab_strip = GetTabStripForBrowser(browser());
664
665  // Move to the first tab and drag it enough so that it detaches.
666  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
667  WebContents* to_delete =
668      browser()->tab_strip_model()->GetWebContentsAt(0);
669  ASSERT_TRUE(PressInput(tab_0_center));
670  ASSERT_TRUE(DragInputToNotifyWhenDone(
671      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
672      base::Bind(&DeleteWhileDetachedStep2, to_delete)));
673  QuitWhenNotDragging();
674
675  // Should not be dragging.
676  ASSERT_FALSE(tab_strip->IsDragSessionActive());
677  ASSERT_FALSE(TabDragController::IsActive());
678
679  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
680
681  EXPECT_TRUE(GetTrackedByWorkspace(browser()));
682}
683
684namespace {
685
686void DeleteSourceDetachedStep2(WebContents* tab,
687                               const BrowserList* browser_list) {
688  ASSERT_EQ(2u, browser_list->size());
689  Browser* new_browser = browser_list->get(1);
690  // This ends up closing the source window.
691  delete tab;
692  // Cancel the drag.
693  ui_controls::SendKeyPress(new_browser->window()->GetNativeWindow(),
694                            ui::VKEY_ESCAPE, false, false, false, false);
695}
696
697}  // namespace
698
699// Detaches a tab and while detached deletes a tab from the source so that the
700// source window closes then presses escape to cancel the drag.
701IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
702                       DeleteSourceDetached) {
703  // Add another tab.
704  AddTabAndResetBrowser(browser());
705  TabStrip* tab_strip = GetTabStripForBrowser(browser());
706
707  // Move to the first tab and drag it enough so that it detaches.
708  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
709  WebContents* to_delete = browser()->tab_strip_model()->GetWebContentsAt(1);
710  ASSERT_TRUE(PressInput(tab_0_center));
711  ASSERT_TRUE(DragInputToNotifyWhenDone(
712      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
713      base::Bind(&DeleteSourceDetachedStep2, to_delete, native_browser_list)));
714  QuitWhenNotDragging();
715
716  // Should not be dragging.
717  ASSERT_EQ(1u, native_browser_list->size());
718  Browser* new_browser = native_browser_list->get(0);
719  ASSERT_FALSE(GetTabStripForBrowser(new_browser)->IsDragSessionActive());
720  ASSERT_FALSE(TabDragController::IsActive());
721
722  EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
723
724  EXPECT_TRUE(GetTrackedByWorkspace(new_browser));
725
726  // Remaining browser window should not be maximized
727  EXPECT_FALSE(new_browser->window()->IsMaximized());
728}
729
730namespace {
731
732void PressEscapeWhileDetachedStep2(const BrowserList* browser_list) {
733  ASSERT_EQ(2u, browser_list->size());
734  Browser* new_browser = browser_list->get(1);
735  ui_controls::SendKeyPress(
736      new_browser->window()->GetNativeWindow(), ui::VKEY_ESCAPE, false, false,
737      false, false);
738}
739
740}  // namespace
741
742// This is disabled until NativeViewHost::Detach really detaches.
743// Detaches a tab and while detached presses escape to revert the drag.
744IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
745                       PressEscapeWhileDetached) {
746  // Add another tab.
747  AddTabAndResetBrowser(browser());
748  TabStrip* tab_strip = GetTabStripForBrowser(browser());
749
750  // Move to the first tab and drag it enough so that it detaches.
751  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
752  ASSERT_TRUE(PressInput(tab_0_center));
753  ASSERT_TRUE(DragInputToNotifyWhenDone(
754      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
755      base::Bind(&PressEscapeWhileDetachedStep2, native_browser_list)));
756  QuitWhenNotDragging();
757
758  // Should not be dragging.
759  ASSERT_FALSE(tab_strip->IsDragSessionActive());
760  ASSERT_FALSE(TabDragController::IsActive());
761
762  // And there should only be one window.
763  EXPECT_EQ(1u, native_browser_list->size());
764
765  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
766
767  // Remaining browser window should not be maximized
768  EXPECT_FALSE(browser()->window()->IsMaximized());
769}
770
771namespace {
772
773void DragAllStep2(DetachToBrowserTabDragControllerTest* test,
774                  const BrowserList* browser_list) {
775  // Should only be one window.
776  ASSERT_EQ(1u, browser_list->size());
777  if (test->input_source() == INPUT_SOURCE_TOUCH) {
778    ASSERT_TRUE(test->ReleaseInput());
779  } else {
780    ASSERT_TRUE(test->ReleaseMouseAsync());
781  }
782}
783
784}  // namespace
785
786// Selects multiple tabs and starts dragging the window.
787IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, DragAll) {
788  // Add another tab.
789  AddTabAndResetBrowser(browser());
790  TabStrip* tab_strip = GetTabStripForBrowser(browser());
791  browser()->tab_strip_model()->AddTabAtToSelection(0);
792  browser()->tab_strip_model()->AddTabAtToSelection(1);
793
794  // Move to the first tab and drag it enough so that it would normally
795  // detach.
796  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
797  ASSERT_TRUE(PressInput(tab_0_center));
798  ASSERT_TRUE(DragInputToNotifyWhenDone(
799      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
800      base::Bind(&DragAllStep2, this, native_browser_list)));
801  QuitWhenNotDragging();
802
803  // Should not be dragging.
804  ASSERT_FALSE(tab_strip->IsDragSessionActive());
805  ASSERT_FALSE(TabDragController::IsActive());
806
807  // And there should only be one window.
808  EXPECT_EQ(1u, native_browser_list->size());
809
810  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
811
812  EXPECT_TRUE(GetTrackedByWorkspace(browser()));
813
814  // Remaining browser window should not be maximized
815  EXPECT_FALSE(browser()->window()->IsMaximized());
816}
817
818namespace {
819
820// Invoked from the nested message loop.
821void DragAllToSeparateWindowStep2(DetachToBrowserTabDragControllerTest* test,
822                                  TabStrip* attached_tab_strip,
823                                  TabStrip* target_tab_strip,
824                                  const BrowserList* browser_list) {
825  ASSERT_TRUE(attached_tab_strip->IsDragSessionActive());
826  ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
827  ASSERT_TRUE(TabDragController::IsActive());
828  ASSERT_EQ(2u, browser_list->size());
829
830  // Drag to target_tab_strip. This should stop the nested loop from dragging
831  // the window.
832  gfx::Point target_point(target_tab_strip->width() - 1,
833                          target_tab_strip->height() / 2);
834  views::View::ConvertPointToScreen(target_tab_strip, &target_point);
835  ASSERT_TRUE(test->DragInputToAsync(target_point));
836}
837
838}  // namespace
839
840// Creates two browsers, selects all tabs in first and drags into second.
841IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
842                       DragAllToSeparateWindow) {
843  TabStrip* tab_strip = GetTabStripForBrowser(browser());
844
845  // Add another tab to browser().
846  AddTabAndResetBrowser(browser());
847
848  // Create another browser.
849  Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
850  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
851
852  browser()->tab_strip_model()->AddTabAtToSelection(0);
853  browser()->tab_strip_model()->AddTabAtToSelection(1);
854
855  // Move to the first tab and drag it enough so that it detaches, but not
856  // enough that it attaches to browser2.
857  gfx::Point tab_0_center(
858      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
859  ASSERT_TRUE(PressInput(tab_0_center));
860  ASSERT_TRUE(DragInputToNotifyWhenDone(
861      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
862      base::Bind(&DragAllToSeparateWindowStep2, this, tab_strip, tab_strip2,
863                 native_browser_list)));
864  QuitWhenNotDragging();
865
866  // Should now be attached to tab_strip2.
867  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
868  ASSERT_TRUE(TabDragController::IsActive());
869  ASSERT_EQ(1u, native_browser_list->size());
870
871  // Release the mouse, stopping the drag session.
872  ASSERT_TRUE(ReleaseInput());
873  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
874  ASSERT_FALSE(TabDragController::IsActive());
875  EXPECT_EQ("100 0 1", IDString(browser2->tab_strip_model()));
876
877  EXPECT_TRUE(GetTrackedByWorkspace(browser2));
878
879  // Remaining browser window should not be maximized
880  EXPECT_FALSE(browser2->window()->IsMaximized());
881}
882
883namespace {
884
885// Invoked from the nested message loop.
886void DragAllToSeparateWindowAndCancelStep2(
887    DetachToBrowserTabDragControllerTest* test,
888    TabStrip* attached_tab_strip,
889    TabStrip* target_tab_strip,
890    const BrowserList* browser_list) {
891  ASSERT_TRUE(attached_tab_strip->IsDragSessionActive());
892  ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
893  ASSERT_TRUE(TabDragController::IsActive());
894  ASSERT_EQ(2u, browser_list->size());
895
896  // Drag to target_tab_strip. This should stop the nested loop from dragging
897  // the window.
898  gfx::Point target_point(target_tab_strip->width() - 1,
899                          target_tab_strip->height() / 2);
900  views::View::ConvertPointToScreen(target_tab_strip, &target_point);
901  ASSERT_TRUE(test->DragInputToAsync(target_point));
902}
903
904}  // namespace
905
906// Creates two browsers, selects all tabs in first, drags into second, then hits
907// escape.
908IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
909                       DragAllToSeparateWindowAndCancel) {
910  TabStrip* tab_strip = GetTabStripForBrowser(browser());
911
912  // Add another tab to browser().
913  AddTabAndResetBrowser(browser());
914
915  // Create another browser.
916  Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
917  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
918
919  browser()->tab_strip_model()->AddTabAtToSelection(0);
920  browser()->tab_strip_model()->AddTabAtToSelection(1);
921
922  // Move to the first tab and drag it enough so that it detaches, but not
923  // enough that it attaches to browser2.
924  gfx::Point tab_0_center(
925      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
926  ASSERT_TRUE(PressInput(tab_0_center));
927  ASSERT_TRUE(DragInputToNotifyWhenDone(
928                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
929                  base::Bind(&DragAllToSeparateWindowAndCancelStep2, this,
930                             tab_strip, tab_strip2, native_browser_list)));
931  QuitWhenNotDragging();
932
933  // Should now be attached to tab_strip2.
934  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
935  ASSERT_TRUE(TabDragController::IsActive());
936  ASSERT_EQ(1u, native_browser_list->size());
937
938  // Cancel the drag.
939  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
940      browser2, ui::VKEY_ESCAPE, false, false, false, false));
941
942  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
943  ASSERT_FALSE(TabDragController::IsActive());
944  EXPECT_EQ("100 0 1", IDString(browser2->tab_strip_model()));
945
946  // browser() will have been destroyed, but browser2 should remain.
947  ASSERT_EQ(1u, native_browser_list->size());
948
949  EXPECT_TRUE(GetTrackedByWorkspace(browser2));
950
951  // Remaining browser window should not be maximized
952  EXPECT_FALSE(browser2->window()->IsMaximized());
953}
954
955// Creates two browsers, drags from first into the second in such a way that
956// no detaching should happen.
957IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
958                       DragDirectlyToSecondWindow) {
959  TabStrip* tab_strip = GetTabStripForBrowser(browser());
960
961  // Add another tab to browser().
962  AddTabAndResetBrowser(browser());
963
964  // Create another browser.
965  Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
966  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
967
968  // Move the tabstrip down enough so that we can detach.
969  gfx::Rect bounds(browser2->window()->GetBounds());
970  bounds.Offset(0, 100);
971  browser2->window()->SetBounds(bounds);
972
973  // Move to the first tab and drag it enough so that it detaches, but not
974  // enough that it attaches to browser2.
975  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
976  ASSERT_TRUE(PressInput(tab_0_center));
977
978  gfx::Point b2_location(5, 0);
979  views::View::ConvertPointToScreen(tab_strip2, &b2_location);
980  ASSERT_TRUE(DragInputTo(b2_location));
981
982  // Should now be attached to tab_strip2.
983  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
984  ASSERT_FALSE(tab_strip->IsDragSessionActive());
985  ASSERT_TRUE(TabDragController::IsActive());
986
987  // Release the mouse, stopping the drag session.
988  ASSERT_TRUE(ReleaseInput());
989  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
990  ASSERT_FALSE(tab_strip->IsDragSessionActive());
991  ASSERT_FALSE(TabDragController::IsActive());
992  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
993  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
994
995  EXPECT_TRUE(GetTrackedByWorkspace(browser()));
996  EXPECT_TRUE(GetTrackedByWorkspace(browser2));
997
998  // Both windows should not be maximized
999  EXPECT_FALSE(browser()->window()->IsMaximized());
1000  EXPECT_FALSE(browser2->window()->IsMaximized());
1001}
1002
1003// Creates two browsers, the first browser has a single tab and drags into the
1004// second browser.
1005IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1006                       DragSingleTabToSeparateWindow) {
1007  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1008
1009  ResetIDs(browser()->tab_strip_model(), 0);
1010
1011  // Create another browser.
1012  Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
1013  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1014  const gfx::Rect initial_bounds(browser2->window()->GetBounds());
1015
1016  // Move to the first tab and drag it enough so that it detaches, but not
1017  // enough that it attaches to browser2.
1018  gfx::Point tab_0_center(
1019      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1020  ASSERT_TRUE(PressInput(tab_0_center));
1021  ASSERT_TRUE(DragInputToNotifyWhenDone(
1022      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1023      base::Bind(&DragAllToSeparateWindowStep2, this, tab_strip, tab_strip2,
1024                 native_browser_list)));
1025  QuitWhenNotDragging();
1026
1027  // Should now be attached to tab_strip2.
1028  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1029  ASSERT_TRUE(TabDragController::IsActive());
1030  ASSERT_EQ(1u, native_browser_list->size());
1031
1032  // Release the mouse, stopping the drag session.
1033  ASSERT_TRUE(ReleaseInput());
1034  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1035  ASSERT_FALSE(TabDragController::IsActive());
1036  EXPECT_EQ("100 0", IDString(browser2->tab_strip_model()));
1037
1038  EXPECT_TRUE(GetTrackedByWorkspace(browser2));
1039
1040  // Remaining browser window should not be maximized
1041  EXPECT_FALSE(browser2->window()->IsMaximized());
1042
1043  // Make sure that the window is still managed and not user moved.
1044  EXPECT_TRUE(IsWindowPositionManaged(browser2->window()->GetNativeWindow()));
1045  EXPECT_FALSE(HasUserChangedWindowPositionOrSize(
1046      browser2->window()->GetNativeWindow()));
1047  // Also make sure that the drag to window position has not changed.
1048  EXPECT_EQ(initial_bounds.ToString(),
1049            browser2->window()->GetBounds().ToString());
1050}
1051
1052namespace {
1053
1054// Invoked from the nested message loop.
1055void CancelOnNewTabWhenDraggingStep2(
1056    DetachToBrowserTabDragControllerTest* test,
1057    const BrowserList* browser_list) {
1058  ASSERT_TRUE(TabDragController::IsActive());
1059  ASSERT_EQ(2u, browser_list->size());
1060
1061  // Add another tab. This should trigger exiting the nested loop.
1062  test->AddBlankTabAndShow(browser_list->GetLastActive());
1063}
1064
1065}  // namespace
1066
1067// Adds another tab, detaches into separate window, adds another tab and
1068// verifies the run loop ends.
1069IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1070                       CancelOnNewTabWhenDragging) {
1071  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1072
1073  // Add another tab to browser().
1074  AddTabAndResetBrowser(browser());
1075
1076  // Move to the first tab and drag it enough so that it detaches.
1077  gfx::Point tab_0_center(
1078      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1079  ASSERT_TRUE(PressInput(tab_0_center));
1080  ASSERT_TRUE(DragInputToNotifyWhenDone(
1081      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1082      base::Bind(&CancelOnNewTabWhenDraggingStep2, this, native_browser_list)));
1083  QuitWhenNotDragging();
1084
1085  // Should be two windows and not dragging.
1086  ASSERT_FALSE(TabDragController::IsActive());
1087  ASSERT_EQ(2u, native_browser_list->size());
1088  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1089    EXPECT_TRUE(GetTrackedByWorkspace(*it));
1090    // Should not be maximized
1091    EXPECT_FALSE(it->window()->IsMaximized());
1092  }
1093}
1094
1095#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
1096
1097namespace {
1098
1099void DragInMaximizedWindowStep2(DetachToBrowserTabDragControllerTest* test,
1100                                Browser* browser,
1101                                TabStrip* tab_strip,
1102                                const BrowserList* browser_list) {
1103  // There should be another browser.
1104  ASSERT_EQ(2u, browser_list->size());
1105  Browser* new_browser = browser_list->get(1);
1106  EXPECT_NE(browser, new_browser);
1107  ASSERT_TRUE(new_browser->window()->IsActive());
1108  TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
1109
1110  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1111  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1112
1113  // Both windows should be visible.
1114  EXPECT_TRUE(tab_strip->GetWidget()->IsVisible());
1115  EXPECT_TRUE(tab_strip2->GetWidget()->IsVisible());
1116
1117  // Stops dragging.
1118  ASSERT_TRUE(test->ReleaseInput());
1119}
1120
1121}  // namespace
1122
1123// Creates a browser with two tabs, maximizes it, drags the tab out.
1124IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1125                       DragInMaximizedWindow) {
1126  AddTabAndResetBrowser(browser());
1127  browser()->window()->Maximize();
1128
1129  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1130
1131  // Move to the first tab and drag it enough so that it detaches.
1132  gfx::Point tab_0_center(
1133      GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1134  ASSERT_TRUE(PressInput(tab_0_center));
1135  ASSERT_TRUE(DragInputToNotifyWhenDone(
1136      tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1137      base::Bind(&DragInMaximizedWindowStep2, this, browser(), tab_strip,
1138                 native_browser_list)));
1139  QuitWhenNotDragging();
1140
1141  ASSERT_FALSE(TabDragController::IsActive());
1142
1143  // Should be two browsers.
1144  ASSERT_EQ(2u, native_browser_list->size());
1145  Browser* new_browser = native_browser_list->get(1);
1146  ASSERT_TRUE(new_browser->window()->IsActive());
1147
1148  EXPECT_TRUE(browser()->window()->GetNativeWindow()->IsVisible());
1149  EXPECT_TRUE(new_browser->window()->GetNativeWindow()->IsVisible());
1150
1151  EXPECT_TRUE(GetTrackedByWorkspace(browser()));
1152  EXPECT_TRUE(GetTrackedByWorkspace(new_browser));
1153
1154  // The source window should be maximized, but the new window should now
1155  // be restored.
1156  EXPECT_TRUE(browser()->window()->IsMaximized());
1157  EXPECT_FALSE(new_browser->window()->IsMaximized());
1158}
1159
1160// Subclass of DetachToBrowserInSeparateDisplayTabDragControllerTest that
1161// creates multiple displays.
1162class DetachToBrowserInSeparateDisplayTabDragControllerTest
1163    : public DetachToBrowserTabDragControllerTest {
1164 public:
1165  DetachToBrowserInSeparateDisplayTabDragControllerTest() {}
1166
1167  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1168    DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
1169    // Make screens sufficiently wide to host 2 browsers side by side.
1170    command_line->AppendSwitchASCII("ash-host-window-bounds",
1171                                    "0+0-600x600,601+0-600x600");
1172  }
1173
1174 private:
1175  DISALLOW_COPY_AND_ASSIGN(
1176      DetachToBrowserInSeparateDisplayTabDragControllerTest);
1177};
1178
1179namespace {
1180
1181void DragSingleTabToSeparateWindowInSecondDisplayStep3(
1182    DetachToBrowserTabDragControllerTest* test) {
1183  ASSERT_TRUE(test->ReleaseInput());
1184}
1185
1186void DragSingleTabToSeparateWindowInSecondDisplayStep2(
1187    DetachToBrowserTabDragControllerTest* test,
1188    const gfx::Point& target_point) {
1189  ASSERT_TRUE(test->DragInputToNotifyWhenDone(
1190      target_point.x(), target_point.y(),
1191      base::Bind(&DragSingleTabToSeparateWindowInSecondDisplayStep3, test)));
1192}
1193
1194}  // namespace
1195
1196// Drags from browser to a second display and releases input.
1197IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1198                       DragSingleTabToSeparateWindowInSecondDisplay) {
1199  // Add another tab.
1200  AddTabAndResetBrowser(browser());
1201  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1202
1203  // Move to the first tab and drag it enough so that it detaches.
1204  // Then drag it to the final destination on the second screen.
1205  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1206  ASSERT_TRUE(PressInput(tab_0_center));
1207  ASSERT_TRUE(DragInputToNotifyWhenDone(
1208                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1209                  base::Bind(&DragSingleTabToSeparateWindowInSecondDisplayStep2,
1210                             this, gfx::Point(600 + tab_0_center.x(),
1211                                              tab_0_center.y()
1212                                              + GetDetachY(tab_strip)))));
1213  QuitWhenNotDragging();
1214
1215  // Should no longer be dragging.
1216  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1217  ASSERT_FALSE(TabDragController::IsActive());
1218
1219  // There should now be another browser.
1220  ASSERT_EQ(2u, native_browser_list->size());
1221  Browser* new_browser = native_browser_list->get(1);
1222  ASSERT_TRUE(new_browser->window()->IsActive());
1223  TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
1224  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1225
1226  // This other browser should be on the second screen (with mouse drag)
1227  // With the touch input the browser cannot be dragged from one screen
1228  // to another and the window stays on the first screen.
1229  if (input_source() == INPUT_SOURCE_MOUSE) {
1230    std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1231    ASSERT_EQ(2u, roots.size());
1232    aura::RootWindow* second_root = roots[1];
1233    EXPECT_EQ(second_root,
1234              new_browser->window()->GetNativeWindow()->GetRootWindow());
1235  }
1236
1237  EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
1238  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1239
1240  // Both windows should not be maximized
1241  EXPECT_FALSE(browser()->window()->IsMaximized());
1242  EXPECT_FALSE(new_browser->window()->IsMaximized());
1243}
1244
1245namespace {
1246
1247// Invoked from the nested message loop.
1248void DragTabToWindowInSeparateDisplayStep2(
1249    DetachToBrowserTabDragControllerTest* test,
1250    TabStrip* not_attached_tab_strip,
1251    TabStrip* target_tab_strip) {
1252  ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
1253  ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
1254  ASSERT_TRUE(TabDragController::IsActive());
1255
1256  // Drag to target_tab_strip. This should stop the nested loop from dragging
1257  // the window.
1258  gfx::Point target_point(
1259      GetCenterInScreenCoordinates(target_tab_strip->tab_at(0)));
1260
1261  // Move it close to the beginning of the target tabstrip.
1262  target_point.set_x(
1263      target_point.x() - target_tab_strip->tab_at(0)->width() / 2 + 10);
1264  ASSERT_TRUE(test->DragInputToAsync(target_point));
1265}
1266
1267}  // namespace
1268
1269// Drags from browser to another browser on a second display and releases input.
1270IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1271                       DragTabToWindowInSeparateDisplay) {
1272  // Add another tab.
1273  AddTabAndResetBrowser(browser());
1274  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1275
1276  // Create another browser.
1277  Browser* browser2 = CreateBrowser(browser()->profile());
1278  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1279  ResetIDs(browser2->tab_strip_model(), 100);
1280
1281  // Move the second browser to the second display.
1282  std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1283  ASSERT_EQ(2u, roots.size());
1284  aura::RootWindow* second_root = roots[1];
1285  gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1286      second_root).work_area();
1287  browser2->window()->SetBounds(work_area);
1288  EXPECT_EQ(second_root,
1289            browser2->window()->GetNativeWindow()->GetRootWindow());
1290
1291  // Move to the first tab and drag it enough so that it detaches, but not
1292  // enough that it attaches to browser2.
1293  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1294  ASSERT_TRUE(PressInput(tab_0_center));
1295  ASSERT_TRUE(DragInputToNotifyWhenDone(
1296                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1297                  base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1298                             this, tab_strip, tab_strip2)));
1299  QuitWhenNotDragging();
1300
1301  // Should now be attached to tab_strip2.
1302  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1303  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1304  ASSERT_TRUE(TabDragController::IsActive());
1305
1306  // Release the mouse, stopping the drag session.
1307  ASSERT_TRUE(ReleaseInput());
1308  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1309  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1310  ASSERT_FALSE(TabDragController::IsActive());
1311  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1312  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1313
1314  // Both windows should not be maximized
1315  EXPECT_FALSE(browser()->window()->IsMaximized());
1316  EXPECT_FALSE(browser2->window()->IsMaximized());
1317}
1318
1319// Drags from browser to another browser on a second display and releases input.
1320IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1321                       DragTabToWindowOnSecondDisplay) {
1322  // Add another tab.
1323  AddTabAndResetBrowser(browser());
1324  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1325
1326  // Create another browser.
1327  Browser* browser2 = CreateBrowser(browser()->profile());
1328  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1329  ResetIDs(browser2->tab_strip_model(), 100);
1330
1331  // Move both browsers to the second display.
1332  std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1333  ASSERT_EQ(2u, roots.size());
1334  aura::RootWindow* second_root = roots[1];
1335  gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1336      second_root).work_area();
1337  browser()->window()->SetBounds(work_area);
1338
1339  // position both browser windows side by side on the second screen.
1340  gfx::Rect work_area2(work_area);
1341  work_area.set_width(work_area.width()/2);
1342  browser()->window()->SetBounds(work_area);
1343  work_area2.set_x(work_area2.x() + work_area2.width()/2);
1344  work_area2.set_width(work_area2.width()/2);
1345  browser2->window()->SetBounds(work_area2);
1346  EXPECT_EQ(second_root,
1347            browser()->window()->GetNativeWindow()->GetRootWindow());
1348  EXPECT_EQ(second_root,
1349            browser2->window()->GetNativeWindow()->GetRootWindow());
1350
1351  // Move to the first tab and drag it enough so that it detaches, but not
1352  // enough that it attaches to browser2.
1353  // SetEventGeneratorRootWindow sets correct (second) RootWindow
1354  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1355  SetEventGeneratorRootWindow(tab_0_center);
1356  ASSERT_TRUE(PressInput(tab_0_center));
1357  ASSERT_TRUE(DragInputToNotifyWhenDone(
1358                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1359                  base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1360                             this, tab_strip, tab_strip2)));
1361  QuitWhenNotDragging();
1362
1363  // Should now be attached to tab_strip2.
1364  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1365  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1366  ASSERT_TRUE(TabDragController::IsActive());
1367
1368  // Release the mouse, stopping the drag session.
1369  ASSERT_TRUE(ReleaseInput());
1370  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1371  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1372  ASSERT_FALSE(TabDragController::IsActive());
1373  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1374  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1375
1376  // Both windows should not be maximized
1377  EXPECT_FALSE(browser()->window()->IsMaximized());
1378  EXPECT_FALSE(browser2->window()->IsMaximized());
1379}
1380
1381// Drags from a maximized browser to another non-maximized browser on a second
1382// display and releases input.
1383IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1384                       DragMaxTabToNonMaxWindowInSeparateDisplay) {
1385  // Add another tab.
1386  AddTabAndResetBrowser(browser());
1387  browser()->window()->Maximize();
1388  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1389
1390  // Create another browser on the second display.
1391  std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1392  ASSERT_EQ(2u, roots.size());
1393  aura::RootWindow* first_root = roots[0];
1394  aura::RootWindow* second_root = roots[1];
1395  gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1396      second_root).work_area();
1397  work_area.Inset(20,20,20,60);
1398  Browser::CreateParams params(browser()->profile(),
1399                               browser()->host_desktop_type());
1400  params.initial_show_state = ui::SHOW_STATE_NORMAL;
1401  params.initial_bounds = work_area;
1402  Browser* browser2 = new Browser(params);
1403  AddBlankTabAndShow(browser2);
1404
1405  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1406  ResetIDs(browser2->tab_strip_model(), 100);
1407
1408  EXPECT_EQ(second_root,
1409            browser2->window()->GetNativeWindow()->GetRootWindow());
1410  EXPECT_EQ(first_root,
1411            browser()->window()->GetNativeWindow()->GetRootWindow());
1412  EXPECT_EQ(2, tab_strip->tab_count());
1413  EXPECT_EQ(1, tab_strip2->tab_count());
1414
1415  // Move to the first tab and drag it enough so that it detaches, but not
1416  // enough that it attaches to browser2.
1417  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1418  ASSERT_TRUE(PressInput(tab_0_center));
1419  ASSERT_TRUE(DragInputToNotifyWhenDone(
1420                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1421                  base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1422                             this, tab_strip, tab_strip2)));
1423  QuitWhenNotDragging();
1424
1425  // Should now be attached to tab_strip2.
1426  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1427  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1428  ASSERT_TRUE(TabDragController::IsActive());
1429
1430  // Release the mouse, stopping the drag session.
1431  ASSERT_TRUE(ReleaseInput());
1432
1433  // tab should have moved
1434  EXPECT_EQ(1, tab_strip->tab_count());
1435  EXPECT_EQ(2, tab_strip2->tab_count());
1436
1437  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1438  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1439  ASSERT_FALSE(TabDragController::IsActive());
1440  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1441  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1442
1443  // Source browser should still be maximized, target should not
1444  EXPECT_TRUE(browser()->window()->IsMaximized());
1445  EXPECT_FALSE(browser2->window()->IsMaximized());
1446}
1447
1448// Immersive fullscreen is ChromeOS only.
1449#if defined(OS_CHROMEOS)
1450// Drags from a restored browser to an immersive fullscreen browser on a
1451// second display and releases input.
1452IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1453                       DragTabToImmersiveBrowserOnSeparateDisplay) {
1454  ImmersiveFullscreenConfiguration::EnableImmersiveFullscreenForTest();
1455  ASSERT_TRUE(ImmersiveFullscreenConfiguration::UseImmersiveFullscreen());
1456
1457  // Add another tab.
1458  AddTabAndResetBrowser(browser());
1459  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1460
1461  // Create another browser.
1462  Browser* browser2 = CreateBrowser(browser()->profile());
1463  TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1464  ResetIDs(browser2->tab_strip_model(), 100);
1465
1466  // Move the second browser to the second display.
1467  std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1468  ASSERT_EQ(2u, roots.size());
1469  aura::RootWindow* second_root = roots[1];
1470  gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1471      second_root).work_area();
1472  browser2->window()->SetBounds(work_area);
1473  EXPECT_EQ(second_root,
1474            browser2->window()->GetNativeWindow()->GetRootWindow());
1475
1476  // Put the second browser into immersive fullscreen.
1477  BrowserView* browser_view2 = BrowserView::GetBrowserViewForBrowser(browser2);
1478  ImmersiveModeControllerAsh* immersive_controller2 =
1479      static_cast<ImmersiveModeControllerAsh*>(
1480          browser_view2->immersive_mode_controller());
1481  immersive_controller2->DisableAnimationsForTest();
1482  chrome::ToggleFullscreenMode(browser2);
1483  ASSERT_TRUE(immersive_controller2->IsEnabled());
1484  ASSERT_FALSE(immersive_controller2->IsRevealed());
1485  ASSERT_TRUE(tab_strip2->IsImmersiveStyle());
1486
1487  // Move to the first tab and drag it enough so that it detaches, but not
1488  // enough that it attaches to browser2.
1489  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1490  ASSERT_TRUE(PressInput(tab_0_center));
1491  ASSERT_TRUE(DragInputToNotifyWhenDone(
1492                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1493                  base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1494                             this, tab_strip, tab_strip2)));
1495  QuitWhenNotDragging();
1496
1497  // Should now be attached to tab_strip2.
1498  ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1499  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1500  ASSERT_TRUE(TabDragController::IsActive());
1501
1502  // browser2's top chrome should be revealed and the tab strip should be
1503  // at normal height while user is tragging tabs_strip2's tabs.
1504  ASSERT_TRUE(immersive_controller2->IsRevealed());
1505  ASSERT_FALSE(tab_strip2->IsImmersiveStyle());
1506
1507  // Release the mouse, stopping the drag session.
1508  ASSERT_TRUE(ReleaseInput());
1509  ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1510  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1511  ASSERT_FALSE(TabDragController::IsActive());
1512  EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1513  EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1514
1515  // The first browser window should not be in immersive fullscreen.
1516  // browser2 should still be in immersive fullscreen, but the top chrome should
1517  // no longer be revealed.
1518  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
1519  EXPECT_FALSE(browser_view->immersive_mode_controller()->IsEnabled());
1520
1521  EXPECT_TRUE(immersive_controller2->IsEnabled());
1522  EXPECT_FALSE(immersive_controller2->IsRevealed());
1523  EXPECT_TRUE(tab_strip2->IsImmersiveStyle());
1524}
1525#endif  // OS_CHROMEOS
1526
1527class DifferentDeviceScaleFactorDisplayTabDragControllerTest
1528    : public DetachToBrowserTabDragControllerTest {
1529 public:
1530  DifferentDeviceScaleFactorDisplayTabDragControllerTest() {}
1531
1532  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1533    DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
1534    command_line->AppendSwitchASCII("ash-host-window-bounds",
1535                                    "400x400,0+400-800x800*2");
1536  }
1537
1538  float GetCursorDeviceScaleFactor() const {
1539    ash::test::CursorManagerTestApi cursor_test_api(
1540        ash::Shell::GetInstance()->cursor_manager());
1541    return cursor_test_api.GetDisplay().device_scale_factor();
1542  }
1543
1544 private:
1545  DISALLOW_COPY_AND_ASSIGN(
1546      DifferentDeviceScaleFactorDisplayTabDragControllerTest);
1547};
1548
1549namespace {
1550
1551// The points where a tab is dragged in CursorDeviceScaleFactorStep.
1552const struct DragPoint {
1553  int x;
1554  int y;
1555} kDragPoints[] = {
1556  {300, 200},
1557  {399, 200},
1558  {500, 200},
1559  {400, 200},
1560  {300, 200},
1561};
1562
1563// The expected device scale factors before the cursor is moved to the
1564// corresponding kDragPoints in CursorDeviceScaleFactorStep.
1565const float kDeviceScaleFactorExpectations[] = {
1566  1.0f,
1567  1.0f,
1568  2.0f,
1569  2.0f,
1570  1.0f,
1571};
1572
1573COMPILE_ASSERT(
1574    arraysize(kDragPoints) == arraysize(kDeviceScaleFactorExpectations),
1575    kDragPoints_and_kDeviceScaleFactorExpectations_must_have_same_size);
1576
1577// Drags tab to |kDragPoints[index]|, then calls the next step function.
1578void CursorDeviceScaleFactorStep(
1579    DifferentDeviceScaleFactorDisplayTabDragControllerTest* test,
1580    TabStrip* not_attached_tab_strip,
1581    size_t index) {
1582  ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
1583  ASSERT_TRUE(TabDragController::IsActive());
1584
1585  if (index < arraysize(kDragPoints)) {
1586    EXPECT_EQ(kDeviceScaleFactorExpectations[index],
1587              test->GetCursorDeviceScaleFactor());
1588    const DragPoint p = kDragPoints[index];
1589    ASSERT_TRUE(test->DragInputToNotifyWhenDone(
1590        p.x, p.y, base::Bind(&CursorDeviceScaleFactorStep,
1591                             test, not_attached_tab_strip, index + 1)));
1592  } else {
1593    // Finishes a serise of CursorDeviceScaleFactorStep calls and ends drag.
1594    EXPECT_EQ(1.0f, test->GetCursorDeviceScaleFactor());
1595    ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
1596        ui_controls::LEFT, ui_controls::UP));
1597  }
1598}
1599
1600}  // namespace
1601
1602// Verifies cursor's device scale factor is updated when a tab is moved across
1603// displays with different device scale factors (http://crbug.com/154183).
1604IN_PROC_BROWSER_TEST_P(DifferentDeviceScaleFactorDisplayTabDragControllerTest,
1605                       CursorDeviceScaleFactor) {
1606  // Add another tab.
1607  AddTabAndResetBrowser(browser());
1608  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1609
1610  // Move the second browser to the second display.
1611  std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1612  ASSERT_EQ(2u, roots.size());
1613
1614  // Move to the first tab and drag it enough so that it detaches.
1615  gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1616  ASSERT_TRUE(PressInput(tab_0_center));
1617  ASSERT_TRUE(DragInputToNotifyWhenDone(
1618                  tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1619                  base::Bind(&CursorDeviceScaleFactorStep,
1620                             this, tab_strip, 0)));
1621  QuitWhenNotDragging();
1622}
1623
1624namespace {
1625
1626class DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest
1627    : public TabDragControllerTest {
1628 public:
1629  DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest() {}
1630
1631  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1632    TabDragControllerTest::SetUpCommandLine(command_line);
1633    command_line->AppendSwitchASCII("ash-host-window-bounds",
1634                                    "0+0-250x250,251+0-250x250");
1635  }
1636
1637  bool Press(const gfx::Point& position) {
1638    return ui_test_utils::SendMouseMoveSync(position) &&
1639        ui_test_utils::SendMouseEventsSync(ui_controls::LEFT,
1640                                           ui_controls::DOWN);
1641  }
1642
1643  bool DragTabAndExecuteTaskWhenDone(const gfx::Point& position,
1644                                     const base::Closure& task) {
1645    return ui_controls::SendMouseMoveNotifyWhenDone(
1646        position.x(), position.y(), task);
1647  }
1648
1649  void QuitWhenNotDragging() {
1650    test::QuitWhenNotDraggingImpl();
1651    base::MessageLoop::current()->Run();
1652  }
1653
1654 private:
1655  DISALLOW_COPY_AND_ASSIGN(
1656      DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest);
1657};
1658
1659// Invoked from the nested message loop.
1660void CancelDragTabToWindowInSeparateDisplayStep3(
1661    TabStrip* tab_strip,
1662    const BrowserList* browser_list) {
1663  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1664  ASSERT_TRUE(TabDragController::IsActive());
1665  ASSERT_EQ(2u, browser_list->size());
1666
1667  // Switching display mode should cancel the drag operation.
1668  ash::internal::DisplayManager* display_manager =
1669      ash::Shell::GetInstance()->display_manager();
1670  display_manager->AddRemoveDisplay();
1671}
1672
1673// Invoked from the nested message loop.
1674void CancelDragTabToWindowInSeparateDisplayStep2(
1675    DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest* test,
1676    TabStrip* tab_strip,
1677    aura::RootWindow* current_root,
1678    gfx::Point final_destination,
1679    const BrowserList* browser_list) {
1680  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1681  ASSERT_TRUE(TabDragController::IsActive());
1682  ASSERT_EQ(2u, browser_list->size());
1683
1684  Browser* new_browser = browser_list->get(1);
1685  EXPECT_EQ(current_root,
1686            new_browser->window()->GetNativeWindow()->GetRootWindow());
1687
1688  ASSERT_TRUE(test->DragTabAndExecuteTaskWhenDone(
1689      final_destination,
1690      base::Bind(&CancelDragTabToWindowInSeparateDisplayStep3,
1691                 tab_strip, browser_list)));
1692}
1693
1694}  // namespace
1695
1696// Drags from browser to a second display and releases input.
1697IN_PROC_BROWSER_TEST_F(
1698    DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
1699    CancelDragTabToWindowIn2ndDisplay) {
1700  // Add another tab.
1701  AddTabAndResetBrowser(browser());
1702  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1703
1704  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1705
1706  // Move the second browser to the second display.
1707  std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1708  ASSERT_EQ(2u, roots.size());
1709  gfx::Point final_destination =
1710      gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1711          roots[1]).work_area().CenterPoint();
1712
1713  // Move to the first tab and drag it enough so that it detaches, but not
1714  // enough to move to another display.
1715  gfx::Point tab_0_dst(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1716  ASSERT_TRUE(Press(tab_0_dst));
1717  tab_0_dst.Offset(0, GetDetachY(tab_strip));
1718  ASSERT_TRUE(DragTabAndExecuteTaskWhenDone(
1719      tab_0_dst, base::Bind(&CancelDragTabToWindowInSeparateDisplayStep2,
1720                            this, tab_strip, roots[0], final_destination,
1721                            native_browser_list)));
1722  QuitWhenNotDragging();
1723
1724  ASSERT_EQ(1u, native_browser_list->size());
1725  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1726  ASSERT_FALSE(TabDragController::IsActive());
1727  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1728
1729  // Release the mouse
1730  ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
1731      ui_controls::LEFT, ui_controls::UP));
1732}
1733
1734// Drags from browser from a second display to primary and releases input.
1735IN_PROC_BROWSER_TEST_F(
1736    DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
1737    CancelDragTabToWindowIn1stDisplay) {
1738  std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1739  ASSERT_EQ(2u, roots.size());
1740
1741  // Add another tab.
1742  AddTabAndResetBrowser(browser());
1743  TabStrip* tab_strip = GetTabStripForBrowser(browser());
1744
1745  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1746  EXPECT_EQ(roots[0], browser()->window()->GetNativeWindow()->GetRootWindow());
1747
1748  gfx::Rect work_area = gfx::Screen::GetNativeScreen()->
1749      GetDisplayNearestWindow(roots[1]).work_area();
1750  browser()->window()->SetBounds(work_area);
1751  EXPECT_EQ(roots[1], browser()->window()->GetNativeWindow()->GetRootWindow());
1752
1753  // Move the second browser to the display.
1754  gfx::Point final_destination =
1755      gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1756          roots[0]).work_area().CenterPoint();
1757
1758  // Move to the first tab and drag it enough so that it detaches, but not
1759  // enough to move to another display.
1760  gfx::Point tab_0_dst(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1761  ASSERT_TRUE(Press(tab_0_dst));
1762  tab_0_dst.Offset(0, GetDetachY(tab_strip));
1763  ASSERT_TRUE(DragTabAndExecuteTaskWhenDone(
1764      tab_0_dst, base::Bind(&CancelDragTabToWindowInSeparateDisplayStep2,
1765                            this, tab_strip, roots[1], final_destination,
1766                            native_browser_list)));
1767  QuitWhenNotDragging();
1768
1769  ASSERT_EQ(1u, native_browser_list->size());
1770  ASSERT_FALSE(tab_strip->IsDragSessionActive());
1771  ASSERT_FALSE(TabDragController::IsActive());
1772  EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1773
1774  // Release the mouse
1775  ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
1776      ui_controls::LEFT, ui_controls::UP));
1777}
1778
1779#endif
1780
1781#if defined(USE_ASH) && !defined(OS_WIN)  // TODO(win_ash)
1782INSTANTIATE_TEST_CASE_P(TabDragging,
1783                        DetachToBrowserInSeparateDisplayTabDragControllerTest,
1784                        ::testing::Values("mouse", "touch"));
1785INSTANTIATE_TEST_CASE_P(TabDragging,
1786                        DifferentDeviceScaleFactorDisplayTabDragControllerTest,
1787                        ::testing::Values("mouse"));
1788INSTANTIATE_TEST_CASE_P(TabDragging,
1789                        DetachToBrowserTabDragControllerTest,
1790                        ::testing::Values("mouse", "touch"));
1791#else
1792INSTANTIATE_TEST_CASE_P(TabDragging,
1793                        DetachToBrowserTabDragControllerTest,
1794                        ::testing::Values("mouse"));
1795#endif
1796