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