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