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