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