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