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