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