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.
5 #include "chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.h"
7 #include "ash/wm/window_state.h"
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/run_loop.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_commands.h"
16 #include "chrome/browser/ui/browser_iterator.h"
17 #include "chrome/browser/ui/browser_list.h"
18 #include "chrome/browser/ui/host_desktop.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chrome/browser/ui/views/frame/browser_view.h"
21 #include "chrome/browser/ui/views/frame/native_browser_frame_factory.h"
22 #include "chrome/browser/ui/views/tabs/tab.h"
23 #include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
24 #include "chrome/browser/ui/views/tabs/tab_strip.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/test/base/in_process_browser_test.h"
27 #include "chrome/test/base/interactive_test_utils.h"
28 #include "chrome/test/base/ui_test_utils.h"
29 #include "content/public/browser/notification_details.h"
30 #include "content/public/browser/notification_observer.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/notification_source.h"
33 #include "content/public/browser/web_contents.h"
34 #include "ui/base/test/ui_controls.h"
35 #include "ui/gfx/screen.h"
36 #include "ui/views/view.h"
37 #include "ui/views/widget/widget.h"
39 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
40 #include "chrome/browser/ui/views/frame/desktop_browser_frame_aura.h"
41 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
45 #include "ash/ash_switches.h"
46 #include "ash/display/display_controller.h"
47 #include "ash/display/display_manager.h"
48 #include "ash/shell.h"
49 #include "ash/test/cursor_manager_test_api.h"
50 #include "ash/wm/coordinate_conversion.h"
51 #include "ash/wm/window_state.h"
52 #include "ash/wm/window_util.h"
53 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
54 #include "ui/aura/client/screen_position_client.h"
55 #include "ui/aura/root_window.h"
56 #include "ui/aura/test/event_generator.h"
59 using content::WebContents;
65 const char kTabDragControllerInteractiveUITestUserDataKey[] =
66 "TabDragControllerInteractiveUITestUserData";
68 class TabDragControllerInteractiveUITestUserData
69 : public base::SupportsUserData::Data {
71 explicit TabDragControllerInteractiveUITestUserData(int id) : id_(id) {}
72 virtual ~TabDragControllerInteractiveUITestUserData() {}
73 int id() { return id_; }
81 class QuitDraggingObserver : public content::NotificationObserver {
83 QuitDraggingObserver() {
84 registrar_.Add(this, chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE,
85 content::NotificationService::AllSources());
88 virtual void Observe(int type,
89 const content::NotificationSource& source,
90 const content::NotificationDetails& details) OVERRIDE {
91 DCHECK_EQ(chrome::NOTIFICATION_TAB_DRAG_LOOP_DONE, type);
92 base::MessageLoopForUI::current()->Quit();
97 virtual ~QuitDraggingObserver() {}
99 content::NotificationRegistrar registrar_;
101 DISALLOW_COPY_AND_ASSIGN(QuitDraggingObserver);
104 gfx::Point GetCenterInScreenCoordinates(const views::View* view) {
105 gfx::Point center(view->width() / 2, view->height() / 2);
106 views::View::ConvertPointToScreen(view, ¢er);
110 void SetID(WebContents* web_contents, int id) {
111 web_contents->SetUserData(&kTabDragControllerInteractiveUITestUserDataKey,
112 new TabDragControllerInteractiveUITestUserData(id));
115 void ResetIDs(TabStripModel* model, int start) {
116 for (int i = 0; i < model->count(); ++i)
117 SetID(model->GetWebContentsAt(i), start + i);
120 std::string IDString(TabStripModel* model) {
122 for (int i = 0; i < model->count(); ++i) {
125 WebContents* contents = model->GetWebContentsAt(i);
126 TabDragControllerInteractiveUITestUserData* user_data =
127 static_cast<TabDragControllerInteractiveUITestUserData*>(
128 contents->GetUserData(
129 &kTabDragControllerInteractiveUITestUserDataKey));
131 result += base::IntToString(user_data->id());
138 // Creates a listener that quits the message loop when no longer dragging.
139 void QuitWhenNotDraggingImpl() {
140 new QuitDraggingObserver(); // QuitDraggingObserver deletes itself.
143 TabStrip* GetTabStripForBrowser(Browser* browser) {
144 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
145 return static_cast<TabStrip*>(browser_view->tabstrip());
150 using test::GetCenterInScreenCoordinates;
152 using test::ResetIDs;
153 using test::IDString;
154 using test::GetTabStripForBrowser;
156 TabDragControllerTest::TabDragControllerTest()
157 : native_browser_list(BrowserList::GetInstance(
158 chrome::HOST_DESKTOP_TYPE_NATIVE)) {
161 TabDragControllerTest::~TabDragControllerTest() {
164 void TabDragControllerTest::SetUp() {
165 // TODO(danakj): Remove this when the tests are not flaky (crbug.com/270065)
166 // or we use test contexts in the renderer to keep things fast enough to
167 // avoid the flake (crbug.com/270918).
170 InProcessBrowserTest::SetUp();
173 void TabDragControllerTest::StopAnimating(TabStrip* tab_strip) {
174 tab_strip->StopAnimating(true);
177 void TabDragControllerTest::AddTabAndResetBrowser(Browser* browser) {
178 AddBlankTabAndShow(browser);
179 StopAnimating(GetTabStripForBrowser(browser));
180 ResetIDs(browser->tab_strip_model(), 0);
183 Browser* TabDragControllerTest::CreateAnotherWindowBrowserAndRelayout() {
184 // Create another browser.
185 Browser* browser2 = CreateBrowser(browser()->profile());
186 ResetIDs(browser2->tab_strip_model(), 100);
188 // Resize the two windows so they're right next to each other.
189 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
190 browser()->window()->GetNativeWindow()).work_area();
191 gfx::Size half_size =
192 gfx::Size(work_area.width() / 3 - 10, work_area.height() / 2 - 10);
193 browser()->window()->SetBounds(gfx::Rect(work_area.origin(), half_size));
194 browser2->window()->SetBounds(gfx::Rect(
195 work_area.x() + half_size.width(), work_area.y(),
196 half_size.width(), half_size.height()));
203 INPUT_SOURCE_MOUSE = 0,
204 INPUT_SOURCE_TOUCH = 1
207 int GetDetachY(TabStrip* tab_strip) {
208 return std::max(TabDragController::kTouchVerticalDetachMagnetism,
209 TabDragController::kVerticalDetachMagnetism) +
210 tab_strip->height() + 1;
213 bool GetTrackedByWorkspace(Browser* browser) {
214 #if !defined(USE_ASH) || defined(OS_WIN) // TODO(win_ash)
217 return ash::wm::GetWindowState(browser->window()->GetNativeWindow())->
218 tracked_by_workspace();
224 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
225 class ScreenEventGeneratorDelegate : public aura::test::EventGeneratorDelegate {
227 explicit ScreenEventGeneratorDelegate(aura::Window* root_window)
228 : root_window_(root_window) {}
229 virtual ~ScreenEventGeneratorDelegate() {}
231 // EventGeneratorDelegate overrides:
232 virtual aura::RootWindow* GetRootWindowAt(
233 const gfx::Point& point) const OVERRIDE {
234 return root_window_->GetDispatcher();
237 virtual aura::client::ScreenPositionClient* GetScreenPositionClient(
238 const aura::Window* window) const OVERRIDE {
239 return aura::client::GetScreenPositionClient(root_window_);
243 aura::Window* root_window_;
245 DISALLOW_COPY_AND_ASSIGN(ScreenEventGeneratorDelegate);
250 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
252 // Following classes verify a crash scenario. Specifically on Windows when focus
253 // changes it can trigger capture being lost. This was causing a crash in tab
254 // dragging as it wasn't set up to handle this scenario. These classes
255 // synthesize this scenario.
257 // Allows making ClearNativeFocus() invoke ReleaseCapture().
258 class TestDesktopBrowserFrameAura : public DesktopBrowserFrameAura {
260 TestDesktopBrowserFrameAura(
261 BrowserFrame* browser_frame,
262 BrowserView* browser_view)
263 : DesktopBrowserFrameAura(browser_frame, browser_view),
264 release_capture_(false) {}
265 virtual ~TestDesktopBrowserFrameAura() {}
267 void ReleaseCaptureOnNextClear() {
268 release_capture_ = true;
271 virtual void ClearNativeFocus() OVERRIDE {
272 views::DesktopNativeWidgetAura::ClearNativeFocus();
273 if (release_capture_) {
274 release_capture_ = false;
275 GetWidget()->ReleaseCapture();
280 // If true ReleaseCapture() is invoked in ClearNativeFocus().
281 bool release_capture_;
283 DISALLOW_COPY_AND_ASSIGN(TestDesktopBrowserFrameAura);
286 // Factory for creating a TestDesktopBrowserFrameAura.
287 class TestNativeBrowserFrameFactory : public NativeBrowserFrameFactory {
289 TestNativeBrowserFrameFactory() {}
290 virtual ~TestNativeBrowserFrameFactory() {}
292 virtual NativeBrowserFrame* Create(
293 BrowserFrame* browser_frame,
294 BrowserView* browser_view) OVERRIDE {
295 return new TestDesktopBrowserFrameAura(browser_frame, browser_view);
299 DISALLOW_COPY_AND_ASSIGN(TestNativeBrowserFrameFactory);
302 class TabDragCaptureLostTest : public TabDragControllerTest {
304 TabDragCaptureLostTest() {
305 NativeBrowserFrameFactory::Set(new TestNativeBrowserFrameFactory);
309 DISALLOW_COPY_AND_ASSIGN(TabDragCaptureLostTest);
312 // See description above for details.
313 IN_PROC_BROWSER_TEST_F(TabDragCaptureLostTest, ReleaseCaptureOnDrag) {
314 AddTabAndResetBrowser(browser());
316 TabStrip* tab_strip = GetTabStripForBrowser(browser());
317 gfx::Point tab_1_center(GetCenterInScreenCoordinates(tab_strip->tab_at(1)));
318 ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_1_center) &&
319 ui_test_utils::SendMouseEventsSync(
320 ui_controls::LEFT, ui_controls::DOWN));
321 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
322 TestDesktopBrowserFrameAura* frame =
323 static_cast<TestDesktopBrowserFrameAura*>(
324 BrowserView::GetBrowserViewForBrowser(browser())->GetWidget()->
325 native_widget_private());
326 // Invoke ReleaseCaptureOnDrag() so that when the drag happens and focus
327 // changes capture is released and the drag cancels.
328 frame->ReleaseCaptureOnNextClear();
329 ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
330 EXPECT_FALSE(tab_strip->IsDragSessionActive());
335 class DetachToBrowserTabDragControllerTest
336 : public TabDragControllerTest,
337 public ::testing::WithParamInterface<const char*> {
339 DetachToBrowserTabDragControllerTest() {}
341 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
342 command_line->AppendSwitch(switches::kTabBrowserDragging);
343 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
344 if (docked_windows_enabled()) {
345 CommandLine::ForCurrentProcess()->AppendSwitch(
346 ash::switches::kAshEnableDockedWindows);
351 virtual void SetUpOnMainThread() OVERRIDE {
352 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
353 event_generator_.reset(new aura::test::EventGenerator(
354 ash::Shell::GetPrimaryRootWindow()));
358 InputSource input_source() const {
359 return strstr(GetParam(), "mouse") ?
360 INPUT_SOURCE_MOUSE : INPUT_SOURCE_TOUCH;
363 bool docked_windows_enabled() const {
364 return (strstr(GetParam(), "docked") != NULL);
367 // Set root window from a point in screen coordinates
368 void SetEventGeneratorRootWindow(const gfx::Point& point) {
369 if (input_source() == INPUT_SOURCE_MOUSE)
371 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
372 event_generator_.reset(new aura::test::EventGenerator(
373 new ScreenEventGeneratorDelegate(ash::wm::GetRootWindowAt(point))));
377 // The following methods update one of the mouse or touch input depending upon
379 bool PressInput(const gfx::Point& location) {
380 if (input_source() == INPUT_SOURCE_MOUSE) {
381 return ui_test_utils::SendMouseMoveSync(location) &&
382 ui_test_utils::SendMouseEventsSync(
383 ui_controls::LEFT, ui_controls::DOWN);
385 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
386 event_generator_->set_current_location(location);
387 event_generator_->PressTouch();
395 // Second touch input is only used for touch sequence tests.
396 EXPECT_EQ(INPUT_SOURCE_TOUCH, input_source());
397 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
398 event_generator_->set_current_location(
399 event_generator_->current_location());
400 event_generator_->PressTouchId(1);
407 bool DragInputTo(const gfx::Point& location) {
408 if (input_source() == INPUT_SOURCE_MOUSE)
409 return ui_test_utils::SendMouseMoveSync(location);
410 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
411 event_generator_->MoveTouch(location);
418 bool DragInputToAsync(const gfx::Point& location) {
419 if (input_source() == INPUT_SOURCE_MOUSE)
420 return ui_controls::SendMouseMove(location.x(), location.y());
421 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
422 event_generator_->MoveTouch(location);
429 bool DragInputToNotifyWhenDone(int x,
431 const base::Closure& task) {
432 if (input_source() == INPUT_SOURCE_MOUSE)
433 return ui_controls::SendMouseMoveNotifyWhenDone(x, y, task);
434 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
435 base::MessageLoop::current()->PostTask(FROM_HERE, task);
436 event_generator_->MoveTouch(gfx::Point(x, y));
443 bool DragInputToDelayedNotifyWhenDone(int x,
445 const base::Closure& task,
446 base::TimeDelta delay) {
447 if (input_source() == INPUT_SOURCE_MOUSE)
448 return ui_controls::SendMouseMoveNotifyWhenDone(x, y, task);
449 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
450 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, task, delay);
451 event_generator_->MoveTouch(gfx::Point(x, y));
458 bool DragInput2ToNotifyWhenDone(int x,
460 const base::Closure& task) {
461 if (input_source() == INPUT_SOURCE_MOUSE)
462 return ui_controls::SendMouseMoveNotifyWhenDone(x, y, task);
463 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
464 base::MessageLoop::current()->PostTask(FROM_HERE, task);
465 event_generator_->MoveTouchId(gfx::Point(x, y), 1);
472 bool ReleaseInput() {
473 if (input_source() == INPUT_SOURCE_MOUSE) {
474 return ui_test_utils::SendMouseEventsSync(
475 ui_controls::LEFT, ui_controls::UP);
477 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
478 event_generator_->ReleaseTouch();
485 bool ReleaseInput2() {
486 if (input_source() == INPUT_SOURCE_MOUSE) {
487 return ui_test_utils::SendMouseEventsSync(
488 ui_controls::LEFT, ui_controls::UP);
490 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
491 event_generator_->ReleaseTouchId(1);
498 bool ReleaseMouseAsync() {
499 return input_source() == INPUT_SOURCE_MOUSE &&
500 ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::UP);
503 void QuitWhenNotDragging() {
504 if (input_source() == INPUT_SOURCE_MOUSE) {
505 // Schedule observer to quit message loop when done dragging. This has to
506 // be async so the message loop can run.
507 test::QuitWhenNotDraggingImpl();
508 base::MessageLoop::current()->Run();
510 // Touch events are sync, so we know we're not in a drag session. But some
511 // tests rely on the browser fully closing, which is async. So, run all
513 base::RunLoop run_loop;
514 run_loop.RunUntilIdle();
518 void AddBlankTabAndShow(Browser* browser) {
519 InProcessBrowserTest::AddBlankTabAndShow(browser);
522 Browser* browser() const { return InProcessBrowserTest::browser(); }
525 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
526 scoped_ptr<aura::test::EventGenerator> event_generator_;
529 DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTest);
532 // Creates a browser with two tabs, drags the second to the first.
533 // TODO(sky): this won't work with touch as it requires a long press.
534 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
535 DISABLED_DragInSameWindow) {
536 AddTabAndResetBrowser(browser());
538 TabStrip* tab_strip = GetTabStripForBrowser(browser());
539 TabStripModel* model = browser()->tab_strip_model();
541 gfx::Point tab_1_center(GetCenterInScreenCoordinates(tab_strip->tab_at(1)));
542 ASSERT_TRUE(PressInput(tab_1_center));
543 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
544 ASSERT_TRUE(DragInputTo(tab_0_center));
545 ASSERT_TRUE(ReleaseInput());
546 EXPECT_EQ("1 0", IDString(model));
547 EXPECT_FALSE(TabDragController::IsActive());
548 EXPECT_FALSE(tab_strip->IsDragSessionActive());
553 // Invoked from the nested message loop.
554 void DragToSeparateWindowStep2(DetachToBrowserTabDragControllerTest* test,
555 TabStrip* not_attached_tab_strip,
556 TabStrip* target_tab_strip) {
557 ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
558 ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
559 ASSERT_TRUE(TabDragController::IsActive());
561 // Drag to target_tab_strip. This should stop the nested loop from dragging
563 gfx::Point target_point(target_tab_strip->width() -1,
564 target_tab_strip->height() / 2);
565 views::View::ConvertPointToScreen(target_tab_strip, &target_point);
566 ASSERT_TRUE(test->DragInputToAsync(target_point));
571 // Creates two browsers, drags from first into second.
572 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
573 DragToSeparateWindow) {
574 TabStrip* tab_strip = GetTabStripForBrowser(browser());
576 // Add another tab to browser().
577 AddTabAndResetBrowser(browser());
579 // Create another browser.
580 Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
581 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
583 // Move to the first tab and drag it enough so that it detaches, but not
584 // enough that it attaches to browser2.
585 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
586 ASSERT_TRUE(PressInput(tab_0_center));
587 ASSERT_TRUE(DragInputToNotifyWhenDone(
588 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
589 base::Bind(&DragToSeparateWindowStep2,
590 this, tab_strip, tab_strip2)));
591 QuitWhenNotDragging();
593 // Should now be attached to tab_strip2.
594 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
595 ASSERT_FALSE(tab_strip->IsDragSessionActive());
596 ASSERT_TRUE(TabDragController::IsActive());
597 EXPECT_TRUE(GetTrackedByWorkspace(browser()));
599 // Release the mouse, stopping the drag session.
600 ASSERT_TRUE(ReleaseInput());
601 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
602 ASSERT_FALSE(tab_strip->IsDragSessionActive());
603 ASSERT_FALSE(TabDragController::IsActive());
604 EXPECT_EQ("100 0", IDString(browser2->tab_strip_model()));
605 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
606 EXPECT_TRUE(GetTrackedByWorkspace(browser2));
608 // Both windows should not be maximized
609 EXPECT_FALSE(browser()->window()->IsMaximized());
610 EXPECT_FALSE(browser2->window()->IsMaximized());
615 void DetachToOwnWindowStep2(DetachToBrowserTabDragControllerTest* test) {
616 if (test->input_source() == INPUT_SOURCE_TOUCH)
617 ASSERT_TRUE(test->ReleaseInput());
620 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
621 bool IsWindowPositionManaged(aura::Window* window) {
622 return ash::wm::GetWindowState(window)->window_position_managed();
624 bool HasUserChangedWindowPositionOrSize(aura::Window* window) {
625 return ash::wm::GetWindowState(window)->bounds_changed_by_user();
628 bool IsWindowPositionManaged(gfx::NativeWindow window) {
631 bool HasUserChangedWindowPositionOrSize(gfx::NativeWindow window) {
638 // Drags from browser to separate window and releases mouse.
639 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
641 const gfx::Rect initial_bounds(browser()->window()->GetBounds());
643 AddTabAndResetBrowser(browser());
644 TabStrip* tab_strip = GetTabStripForBrowser(browser());
646 // Move to the first tab and drag it enough so that it detaches.
647 gfx::Point tab_0_center(
648 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
649 ASSERT_TRUE(PressInput(tab_0_center));
650 ASSERT_TRUE(DragInputToNotifyWhenDone(
651 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
652 base::Bind(&DetachToOwnWindowStep2, this)));
653 if (input_source() == INPUT_SOURCE_MOUSE) {
654 ASSERT_TRUE(ReleaseMouseAsync());
655 QuitWhenNotDragging();
658 // Should no longer be dragging.
659 ASSERT_FALSE(tab_strip->IsDragSessionActive());
660 ASSERT_FALSE(TabDragController::IsActive());
662 // There should now be another browser.
663 ASSERT_EQ(2u, native_browser_list->size());
664 Browser* new_browser = native_browser_list->get(1);
665 ASSERT_TRUE(new_browser->window()->IsActive());
666 TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
667 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
669 EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
670 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
672 // The bounds of the initial window should not have changed.
673 EXPECT_EQ(initial_bounds.ToString(),
674 browser()->window()->GetBounds().ToString());
676 EXPECT_TRUE(GetTrackedByWorkspace(browser()));
677 EXPECT_TRUE(GetTrackedByWorkspace(new_browser));
678 // After this both windows should still be manageable.
679 EXPECT_TRUE(IsWindowPositionManaged(browser()->window()->GetNativeWindow()));
680 EXPECT_TRUE(IsWindowPositionManaged(
681 new_browser->window()->GetNativeWindow()));
683 // Both windows should not be maximized
684 EXPECT_FALSE(browser()->window()->IsMaximized());
685 EXPECT_FALSE(new_browser->window()->IsMaximized());
688 // Drags from browser to separate window and releases mouse.
689 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
690 DetachToOwnWindowFromMaximizedWindow) {
691 if (!TabDragController::ShouldDetachIntoNewBrowser()) {
693 << "Skipping DetachToOwnWindowFromMaximizedWindow on this platform.";
697 // Maximize the initial browser window.
698 browser()->window()->Maximize();
699 ASSERT_TRUE(browser()->window()->IsMaximized());
702 AddTabAndResetBrowser(browser());
703 TabStrip* tab_strip = GetTabStripForBrowser(browser());
705 // Move to the first tab and drag it enough so that it detaches.
706 gfx::Point tab_0_center(
707 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
708 ASSERT_TRUE(PressInput(tab_0_center));
709 ASSERT_TRUE(DragInputToNotifyWhenDone(
710 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
711 base::Bind(&DetachToOwnWindowStep2, this)));
712 if (input_source() == INPUT_SOURCE_MOUSE) {
713 ASSERT_TRUE(ReleaseMouseAsync());
714 QuitWhenNotDragging();
717 // Should no longer be dragging.
718 ASSERT_FALSE(tab_strip->IsDragSessionActive());
719 ASSERT_FALSE(TabDragController::IsActive());
721 // There should now be another browser.
722 ASSERT_EQ(2u, native_browser_list->size());
723 Browser* new_browser = native_browser_list->get(1);
724 ASSERT_TRUE(new_browser->window()->IsActive());
725 TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
726 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
728 EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
729 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
731 // The bounds of the initial window should not have changed.
732 EXPECT_TRUE(browser()->window()->IsMaximized());
734 EXPECT_TRUE(GetTrackedByWorkspace(browser()));
735 EXPECT_TRUE(GetTrackedByWorkspace(new_browser));
736 // After this both windows should still be manageable.
737 EXPECT_TRUE(IsWindowPositionManaged(browser()->window()->GetNativeWindow()));
738 EXPECT_TRUE(IsWindowPositionManaged(
739 new_browser->window()->GetNativeWindow()));
741 // The new window should be maximized.
742 EXPECT_TRUE(new_browser->window()->IsMaximized());
745 // Deletes a tab being dragged before the user moved enough to start a drag.
746 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
747 DeleteBeforeStartedDragging) {
749 AddTabAndResetBrowser(browser());
750 TabStrip* tab_strip = GetTabStripForBrowser(browser());
752 // Click on the first tab, but don't move it.
753 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
754 ASSERT_TRUE(PressInput(tab_0_center));
756 // Should be dragging.
757 ASSERT_TRUE(tab_strip->IsDragSessionActive());
758 ASSERT_TRUE(TabDragController::IsActive());
760 // Delete the tab being dragged.
761 delete browser()->tab_strip_model()->GetWebContentsAt(0);
763 // Should have canceled dragging.
764 ASSERT_FALSE(tab_strip->IsDragSessionActive());
765 ASSERT_FALSE(TabDragController::IsActive());
767 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
768 EXPECT_TRUE(GetTrackedByWorkspace(browser()));
771 // Deletes a tab being dragged while still attached.
772 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
773 DeleteTabWhileAttached) {
775 AddTabAndResetBrowser(browser());
776 TabStrip* tab_strip = GetTabStripForBrowser(browser());
778 // Click on the first tab and move it enough so that it starts dragging but is
780 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
781 ASSERT_TRUE(PressInput(tab_0_center));
782 ASSERT_TRUE(DragInputTo(
783 gfx::Point(tab_0_center.x() + 20, tab_0_center.y())));
785 // Should be dragging.
786 ASSERT_TRUE(tab_strip->IsDragSessionActive());
787 ASSERT_TRUE(TabDragController::IsActive());
789 // Delete the tab being dragged.
790 delete browser()->tab_strip_model()->GetWebContentsAt(0);
792 // Should have canceled dragging.
793 ASSERT_FALSE(tab_strip->IsDragSessionActive());
794 ASSERT_FALSE(TabDragController::IsActive());
796 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
798 EXPECT_TRUE(GetTrackedByWorkspace(browser()));
803 void DeleteWhileDetachedStep2(WebContents* tab) {
809 // Deletes a tab being dragged after dragging a tab so that a new window is
811 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
812 DeleteTabWhileDetached) {
814 AddTabAndResetBrowser(browser());
815 TabStrip* tab_strip = GetTabStripForBrowser(browser());
817 // Move to the first tab and drag it enough so that it detaches.
818 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
819 WebContents* to_delete =
820 browser()->tab_strip_model()->GetWebContentsAt(0);
821 ASSERT_TRUE(PressInput(tab_0_center));
822 ASSERT_TRUE(DragInputToNotifyWhenDone(
823 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
824 base::Bind(&DeleteWhileDetachedStep2, to_delete)));
825 QuitWhenNotDragging();
827 // Should not be dragging.
828 ASSERT_FALSE(tab_strip->IsDragSessionActive());
829 ASSERT_FALSE(TabDragController::IsActive());
831 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
833 EXPECT_TRUE(GetTrackedByWorkspace(browser()));
838 void DeleteSourceDetachedStep2(WebContents* tab,
839 const BrowserList* browser_list) {
840 ASSERT_EQ(2u, browser_list->size());
841 Browser* new_browser = browser_list->get(1);
842 // This ends up closing the source window.
845 ui_controls::SendKeyPress(new_browser->window()->GetNativeWindow(),
846 ui::VKEY_ESCAPE, false, false, false, false);
851 // Detaches a tab and while detached deletes a tab from the source so that the
852 // source window closes then presses escape to cancel the drag.
853 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
854 DeleteSourceDetached) {
856 AddTabAndResetBrowser(browser());
857 TabStrip* tab_strip = GetTabStripForBrowser(browser());
859 // Move to the first tab and drag it enough so that it detaches.
860 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
861 WebContents* to_delete = browser()->tab_strip_model()->GetWebContentsAt(1);
862 ASSERT_TRUE(PressInput(tab_0_center));
863 ASSERT_TRUE(DragInputToNotifyWhenDone(
864 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
865 base::Bind(&DeleteSourceDetachedStep2, to_delete, native_browser_list)));
866 QuitWhenNotDragging();
868 // Should not be dragging.
869 ASSERT_EQ(1u, native_browser_list->size());
870 Browser* new_browser = native_browser_list->get(0);
871 ASSERT_FALSE(GetTabStripForBrowser(new_browser)->IsDragSessionActive());
872 ASSERT_FALSE(TabDragController::IsActive());
874 EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
876 EXPECT_TRUE(GetTrackedByWorkspace(new_browser));
878 // Remaining browser window should not be maximized
879 EXPECT_FALSE(new_browser->window()->IsMaximized());
884 void PressEscapeWhileDetachedStep2(const BrowserList* browser_list) {
885 ASSERT_EQ(2u, browser_list->size());
886 Browser* new_browser = browser_list->get(1);
887 ui_controls::SendKeyPress(
888 new_browser->window()->GetNativeWindow(), ui::VKEY_ESCAPE, false, false,
894 // This is disabled until NativeViewHost::Detach really detaches.
895 // Detaches a tab and while detached presses escape to revert the drag.
896 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
897 PressEscapeWhileDetached) {
899 AddTabAndResetBrowser(browser());
900 TabStrip* tab_strip = GetTabStripForBrowser(browser());
902 // Move to the first tab and drag it enough so that it detaches.
903 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
904 ASSERT_TRUE(PressInput(tab_0_center));
905 ASSERT_TRUE(DragInputToNotifyWhenDone(
906 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
907 base::Bind(&PressEscapeWhileDetachedStep2, native_browser_list)));
908 QuitWhenNotDragging();
910 // Should not be dragging.
911 ASSERT_FALSE(tab_strip->IsDragSessionActive());
912 ASSERT_FALSE(TabDragController::IsActive());
914 // And there should only be one window.
915 EXPECT_EQ(1u, native_browser_list->size());
917 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
919 // Remaining browser window should not be maximized
920 EXPECT_FALSE(browser()->window()->IsMaximized());
925 void DragAllStep2(DetachToBrowserTabDragControllerTest* test,
926 const BrowserList* browser_list) {
927 // Should only be one window.
928 ASSERT_EQ(1u, browser_list->size());
929 if (test->input_source() == INPUT_SOURCE_TOUCH) {
930 ASSERT_TRUE(test->ReleaseInput());
932 ASSERT_TRUE(test->ReleaseMouseAsync());
938 // Selects multiple tabs and starts dragging the window.
939 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, DragAll) {
941 AddTabAndResetBrowser(browser());
942 TabStrip* tab_strip = GetTabStripForBrowser(browser());
943 browser()->tab_strip_model()->AddTabAtToSelection(0);
944 browser()->tab_strip_model()->AddTabAtToSelection(1);
946 // Move to the first tab and drag it enough so that it would normally
948 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
949 ASSERT_TRUE(PressInput(tab_0_center));
950 ASSERT_TRUE(DragInputToNotifyWhenDone(
951 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
952 base::Bind(&DragAllStep2, this, native_browser_list)));
953 QuitWhenNotDragging();
955 // Should not be dragging.
956 ASSERT_FALSE(tab_strip->IsDragSessionActive());
957 ASSERT_FALSE(TabDragController::IsActive());
959 // And there should only be one window.
960 EXPECT_EQ(1u, native_browser_list->size());
962 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
964 EXPECT_TRUE(GetTrackedByWorkspace(browser()));
966 // Remaining browser window should not be maximized
967 EXPECT_FALSE(browser()->window()->IsMaximized());
972 // Invoked from the nested message loop.
973 void DragAllToSeparateWindowStep2(DetachToBrowserTabDragControllerTest* test,
974 TabStrip* attached_tab_strip,
975 TabStrip* target_tab_strip,
976 const BrowserList* browser_list) {
977 ASSERT_TRUE(attached_tab_strip->IsDragSessionActive());
978 ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
979 ASSERT_TRUE(TabDragController::IsActive());
980 ASSERT_EQ(2u, browser_list->size());
982 // Drag to target_tab_strip. This should stop the nested loop from dragging
984 gfx::Point target_point(target_tab_strip->width() - 1,
985 target_tab_strip->height() / 2);
986 views::View::ConvertPointToScreen(target_tab_strip, &target_point);
987 ASSERT_TRUE(test->DragInputToAsync(target_point));
992 // Creates two browsers, selects all tabs in first and drags into second.
993 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
994 DragAllToSeparateWindow) {
995 TabStrip* tab_strip = GetTabStripForBrowser(browser());
997 // Add another tab to browser().
998 AddTabAndResetBrowser(browser());
1000 // Create another browser.
1001 Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
1002 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1004 browser()->tab_strip_model()->AddTabAtToSelection(0);
1005 browser()->tab_strip_model()->AddTabAtToSelection(1);
1007 // Move to the first tab and drag it enough so that it detaches, but not
1008 // enough that it attaches to browser2.
1009 gfx::Point tab_0_center(
1010 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1011 ASSERT_TRUE(PressInput(tab_0_center));
1012 ASSERT_TRUE(DragInputToNotifyWhenDone(
1013 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1014 base::Bind(&DragAllToSeparateWindowStep2, this, tab_strip, tab_strip2,
1015 native_browser_list)));
1016 QuitWhenNotDragging();
1018 // Should now be attached to tab_strip2.
1019 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1020 ASSERT_TRUE(TabDragController::IsActive());
1021 ASSERT_EQ(1u, native_browser_list->size());
1023 // Release the mouse, stopping the drag session.
1024 ASSERT_TRUE(ReleaseInput());
1025 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1026 ASSERT_FALSE(TabDragController::IsActive());
1027 EXPECT_EQ("100 0 1", IDString(browser2->tab_strip_model()));
1029 EXPECT_TRUE(GetTrackedByWorkspace(browser2));
1031 // Remaining browser window should not be maximized
1032 EXPECT_FALSE(browser2->window()->IsMaximized());
1037 // Invoked from the nested message loop.
1038 void DragAllToSeparateWindowAndCancelStep2(
1039 DetachToBrowserTabDragControllerTest* test,
1040 TabStrip* attached_tab_strip,
1041 TabStrip* target_tab_strip,
1042 const BrowserList* browser_list) {
1043 ASSERT_TRUE(attached_tab_strip->IsDragSessionActive());
1044 ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
1045 ASSERT_TRUE(TabDragController::IsActive());
1046 ASSERT_EQ(2u, browser_list->size());
1048 // Drag to target_tab_strip. This should stop the nested loop from dragging
1050 gfx::Point target_point(target_tab_strip->width() - 1,
1051 target_tab_strip->height() / 2);
1052 views::View::ConvertPointToScreen(target_tab_strip, &target_point);
1053 ASSERT_TRUE(test->DragInputToAsync(target_point));
1058 // Creates two browsers, selects all tabs in first, drags into second, then hits
1060 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1061 DragAllToSeparateWindowAndCancel) {
1062 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1064 // Add another tab to browser().
1065 AddTabAndResetBrowser(browser());
1067 // Create another browser.
1068 Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
1069 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1071 browser()->tab_strip_model()->AddTabAtToSelection(0);
1072 browser()->tab_strip_model()->AddTabAtToSelection(1);
1074 // Move to the first tab and drag it enough so that it detaches, but not
1075 // enough that it attaches to browser2.
1076 gfx::Point tab_0_center(
1077 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1078 ASSERT_TRUE(PressInput(tab_0_center));
1079 ASSERT_TRUE(DragInputToNotifyWhenDone(
1080 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1081 base::Bind(&DragAllToSeparateWindowAndCancelStep2, this,
1082 tab_strip, tab_strip2, native_browser_list)));
1083 QuitWhenNotDragging();
1085 // Should now be attached to tab_strip2.
1086 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1087 ASSERT_TRUE(TabDragController::IsActive());
1088 ASSERT_EQ(1u, native_browser_list->size());
1091 ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
1092 browser2, ui::VKEY_ESCAPE, false, false, false, false));
1094 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1095 ASSERT_FALSE(TabDragController::IsActive());
1096 EXPECT_EQ("100 0 1", IDString(browser2->tab_strip_model()));
1098 // browser() will have been destroyed, but browser2 should remain.
1099 ASSERT_EQ(1u, native_browser_list->size());
1101 EXPECT_TRUE(GetTrackedByWorkspace(browser2));
1103 // Remaining browser window should not be maximized
1104 EXPECT_FALSE(browser2->window()->IsMaximized());
1107 // Creates two browsers, drags from first into the second in such a way that
1108 // no detaching should happen.
1109 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1110 DragDirectlyToSecondWindow) {
1111 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1113 // Add another tab to browser().
1114 AddTabAndResetBrowser(browser());
1116 // Create another browser.
1117 Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
1118 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1120 // Move the tabstrip down enough so that we can detach.
1121 gfx::Rect bounds(browser2->window()->GetBounds());
1122 bounds.Offset(0, 100);
1123 browser2->window()->SetBounds(bounds);
1125 // Move to the first tab and drag it enough so that it detaches, but not
1126 // enough that it attaches to browser2.
1127 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1128 ASSERT_TRUE(PressInput(tab_0_center));
1130 gfx::Point b2_location(5, 0);
1131 views::View::ConvertPointToScreen(tab_strip2, &b2_location);
1132 ASSERT_TRUE(DragInputTo(b2_location));
1134 // Should now be attached to tab_strip2.
1135 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1136 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1137 ASSERT_TRUE(TabDragController::IsActive());
1139 // Release the mouse, stopping the drag session.
1140 ASSERT_TRUE(ReleaseInput());
1141 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1142 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1143 ASSERT_FALSE(TabDragController::IsActive());
1144 EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1145 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1147 EXPECT_TRUE(GetTrackedByWorkspace(browser()));
1148 EXPECT_TRUE(GetTrackedByWorkspace(browser2));
1150 // Both windows should not be maximized
1151 EXPECT_FALSE(browser()->window()->IsMaximized());
1152 EXPECT_FALSE(browser2->window()->IsMaximized());
1155 // Creates two browsers, the first browser has a single tab and drags into the
1157 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1158 DragSingleTabToSeparateWindow) {
1159 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1161 ResetIDs(browser()->tab_strip_model(), 0);
1163 // Create another browser.
1164 Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
1165 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1166 const gfx::Rect initial_bounds(browser2->window()->GetBounds());
1168 // Move to the first tab and drag it enough so that it detaches, but not
1169 // enough that it attaches to browser2.
1170 gfx::Point tab_0_center(
1171 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1172 ASSERT_TRUE(PressInput(tab_0_center));
1173 ASSERT_TRUE(DragInputToNotifyWhenDone(
1174 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1175 base::Bind(&DragAllToSeparateWindowStep2, this, tab_strip, tab_strip2,
1176 native_browser_list)));
1177 QuitWhenNotDragging();
1179 // Should now be attached to tab_strip2.
1180 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1181 ASSERT_TRUE(TabDragController::IsActive());
1182 ASSERT_EQ(1u, native_browser_list->size());
1184 // Release the mouse, stopping the drag session.
1185 ASSERT_TRUE(ReleaseInput());
1186 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1187 ASSERT_FALSE(TabDragController::IsActive());
1188 EXPECT_EQ("100 0", IDString(browser2->tab_strip_model()));
1190 EXPECT_TRUE(GetTrackedByWorkspace(browser2));
1192 // Remaining browser window should not be maximized
1193 EXPECT_FALSE(browser2->window()->IsMaximized());
1195 // Make sure that the window is still managed and not user moved.
1196 EXPECT_TRUE(IsWindowPositionManaged(browser2->window()->GetNativeWindow()));
1197 EXPECT_FALSE(HasUserChangedWindowPositionOrSize(
1198 browser2->window()->GetNativeWindow()));
1199 // Also make sure that the drag to window position has not changed.
1200 EXPECT_EQ(initial_bounds.ToString(),
1201 browser2->window()->GetBounds().ToString());
1206 // Invoked from the nested message loop.
1207 void CancelOnNewTabWhenDraggingStep2(
1208 DetachToBrowserTabDragControllerTest* test,
1209 const BrowserList* browser_list) {
1210 ASSERT_TRUE(TabDragController::IsActive());
1211 ASSERT_EQ(2u, browser_list->size());
1213 // Add another tab. This should trigger exiting the nested loop.
1214 test->AddBlankTabAndShow(browser_list->GetLastActive());
1219 // Adds another tab, detaches into separate window, adds another tab and
1220 // verifies the run loop ends.
1221 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1222 CancelOnNewTabWhenDragging) {
1223 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1225 // Add another tab to browser().
1226 AddTabAndResetBrowser(browser());
1228 // Move to the first tab and drag it enough so that it detaches.
1229 gfx::Point tab_0_center(
1230 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1231 ASSERT_TRUE(PressInput(tab_0_center));
1232 ASSERT_TRUE(DragInputToNotifyWhenDone(
1233 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1234 base::Bind(&CancelOnNewTabWhenDraggingStep2, this, native_browser_list)));
1235 QuitWhenNotDragging();
1237 // Should be two windows and not dragging.
1238 ASSERT_FALSE(TabDragController::IsActive());
1239 ASSERT_EQ(2u, native_browser_list->size());
1240 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1241 EXPECT_TRUE(GetTrackedByWorkspace(*it));
1242 // Should not be maximized
1243 EXPECT_FALSE(it->window()->IsMaximized());
1247 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
1251 void DragInMaximizedWindowStep2(DetachToBrowserTabDragControllerTest* test,
1253 TabStrip* tab_strip,
1254 const BrowserList* browser_list) {
1255 // There should be another browser.
1256 ASSERT_EQ(2u, browser_list->size());
1257 Browser* new_browser = browser_list->get(1);
1258 EXPECT_NE(browser, new_browser);
1259 ASSERT_TRUE(new_browser->window()->IsActive());
1260 TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
1262 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1263 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1265 // Both windows should be visible.
1266 EXPECT_TRUE(tab_strip->GetWidget()->IsVisible());
1267 EXPECT_TRUE(tab_strip2->GetWidget()->IsVisible());
1270 ASSERT_TRUE(test->ReleaseInput());
1275 // Creates a browser with two tabs, maximizes it, drags the tab out.
1276 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1277 DragInMaximizedWindow) {
1278 AddTabAndResetBrowser(browser());
1279 browser()->window()->Maximize();
1281 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1283 // Move to the first tab and drag it enough so that it detaches.
1284 gfx::Point tab_0_center(
1285 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1286 ASSERT_TRUE(PressInput(tab_0_center));
1287 ASSERT_TRUE(DragInputToNotifyWhenDone(
1288 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1289 base::Bind(&DragInMaximizedWindowStep2, this, browser(), tab_strip,
1290 native_browser_list)));
1291 QuitWhenNotDragging();
1293 ASSERT_FALSE(TabDragController::IsActive());
1295 // Should be two browsers.
1296 ASSERT_EQ(2u, native_browser_list->size());
1297 Browser* new_browser = native_browser_list->get(1);
1298 ASSERT_TRUE(new_browser->window()->IsActive());
1300 EXPECT_TRUE(browser()->window()->GetNativeWindow()->IsVisible());
1301 EXPECT_TRUE(new_browser->window()->GetNativeWindow()->IsVisible());
1303 EXPECT_TRUE(GetTrackedByWorkspace(browser()));
1304 EXPECT_TRUE(GetTrackedByWorkspace(new_browser));
1306 // The source window should be maximized.
1307 EXPECT_TRUE(browser()->window()->IsMaximized());
1308 // The new window should be maximized.
1309 EXPECT_TRUE(new_browser->window()->IsMaximized());
1312 // Subclass of DetachToBrowserTabDragControllerTest that
1313 // creates multiple displays.
1314 class DetachToBrowserInSeparateDisplayTabDragControllerTest
1315 : public DetachToBrowserTabDragControllerTest {
1317 DetachToBrowserInSeparateDisplayTabDragControllerTest() {}
1318 virtual ~DetachToBrowserInSeparateDisplayTabDragControllerTest() {}
1320 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1321 DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
1322 // Make screens sufficiently wide to host 2 browsers side by side.
1323 command_line->AppendSwitchASCII("ash-host-window-bounds",
1324 "0+0-600x600,601+0-600x600");
1328 DISALLOW_COPY_AND_ASSIGN(
1329 DetachToBrowserInSeparateDisplayTabDragControllerTest);
1332 // Subclass of DetachToBrowserTabDragControllerTest that runs tests only with
1334 class DetachToBrowserTabDragControllerTestTouch
1335 : public DetachToBrowserTabDragControllerTest {
1337 DetachToBrowserTabDragControllerTestTouch() {}
1338 virtual ~DetachToBrowserTabDragControllerTestTouch() {}
1341 DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTestTouch);
1346 void DragSingleTabToSeparateWindowInSecondDisplayStep3(
1347 DetachToBrowserTabDragControllerTest* test) {
1348 ASSERT_TRUE(test->ReleaseInput());
1351 void DragSingleTabToSeparateWindowInSecondDisplayStep2(
1352 DetachToBrowserTabDragControllerTest* test,
1353 const gfx::Point& target_point) {
1354 ASSERT_TRUE(test->DragInputToNotifyWhenDone(
1355 target_point.x(), target_point.y(),
1356 base::Bind(&DragSingleTabToSeparateWindowInSecondDisplayStep3, test)));
1361 // Drags from browser to a second display and releases input.
1362 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1363 DragSingleTabToSeparateWindowInSecondDisplay) {
1365 AddTabAndResetBrowser(browser());
1366 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1368 // Move to the first tab and drag it enough so that it detaches.
1369 // Then drag it to the final destination on the second screen.
1370 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1371 ASSERT_TRUE(PressInput(tab_0_center));
1372 ASSERT_TRUE(DragInputToNotifyWhenDone(
1373 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1374 base::Bind(&DragSingleTabToSeparateWindowInSecondDisplayStep2,
1375 this, gfx::Point(600 + tab_0_center.x(),
1377 + GetDetachY(tab_strip)))));
1378 QuitWhenNotDragging();
1380 // Should no longer be dragging.
1381 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1382 ASSERT_FALSE(TabDragController::IsActive());
1384 // There should now be another browser.
1385 ASSERT_EQ(2u, native_browser_list->size());
1386 Browser* new_browser = native_browser_list->get(1);
1387 ASSERT_TRUE(new_browser->window()->IsActive());
1388 TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
1389 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1391 // This other browser should be on the second screen (with mouse drag)
1392 // With the touch input the browser cannot be dragged from one screen
1393 // to another and the window stays on the first screen.
1394 if (input_source() == INPUT_SOURCE_MOUSE) {
1395 std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1396 ASSERT_EQ(2u, roots.size());
1397 aura::RootWindow* second_root = roots[1];
1398 EXPECT_EQ(second_root,
1399 new_browser->window()->GetNativeWindow()->GetRootWindow());
1402 EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
1403 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1405 // Both windows should not be maximized
1406 EXPECT_FALSE(browser()->window()->IsMaximized());
1407 EXPECT_FALSE(new_browser->window()->IsMaximized());
1412 // Invoked from the nested message loop.
1413 void DragTabToWindowInSeparateDisplayStep2(
1414 DetachToBrowserTabDragControllerTest* test,
1415 TabStrip* not_attached_tab_strip,
1416 TabStrip* target_tab_strip) {
1417 ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
1418 ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
1419 ASSERT_TRUE(TabDragController::IsActive());
1421 // Drag to target_tab_strip. This should stop the nested loop from dragging
1423 gfx::Point target_point(
1424 GetCenterInScreenCoordinates(target_tab_strip->tab_at(0)));
1426 // Move it close to the beginning of the target tabstrip.
1428 target_point.x() - target_tab_strip->tab_at(0)->width() / 2 + 10);
1429 ASSERT_TRUE(test->DragInputToAsync(target_point));
1434 // Drags from browser to another browser on a second display and releases input.
1435 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1436 DragTabToWindowInSeparateDisplay) {
1438 AddTabAndResetBrowser(browser());
1439 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1441 // Create another browser.
1442 Browser* browser2 = CreateBrowser(browser()->profile());
1443 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1444 ResetIDs(browser2->tab_strip_model(), 100);
1446 // Move the second browser to the second display.
1447 std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1448 ASSERT_EQ(2u, roots.size());
1449 aura::RootWindow* second_root = roots[1];
1450 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1451 second_root).work_area();
1452 browser2->window()->SetBounds(work_area);
1453 EXPECT_EQ(second_root,
1454 browser2->window()->GetNativeWindow()->GetRootWindow());
1456 // Move to the first tab and drag it enough so that it detaches, but not
1457 // enough that it attaches to browser2.
1458 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1459 ASSERT_TRUE(PressInput(tab_0_center));
1460 ASSERT_TRUE(DragInputToNotifyWhenDone(
1461 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1462 base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1463 this, tab_strip, tab_strip2)));
1464 QuitWhenNotDragging();
1466 // Should now be attached to tab_strip2.
1467 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1468 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1469 ASSERT_TRUE(TabDragController::IsActive());
1471 // Release the mouse, stopping the drag session.
1472 ASSERT_TRUE(ReleaseInput());
1473 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1474 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1475 ASSERT_FALSE(TabDragController::IsActive());
1476 EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1477 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1479 // Both windows should not be maximized
1480 EXPECT_FALSE(browser()->window()->IsMaximized());
1481 EXPECT_FALSE(browser2->window()->IsMaximized());
1484 // Drags from browser to another browser on a second display and releases input.
1485 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1486 DragTabToWindowOnSecondDisplay) {
1488 AddTabAndResetBrowser(browser());
1489 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1491 // Create another browser.
1492 Browser* browser2 = CreateBrowser(browser()->profile());
1493 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1494 ResetIDs(browser2->tab_strip_model(), 100);
1496 // Move both browsers to the second display.
1497 std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1498 ASSERT_EQ(2u, roots.size());
1499 aura::RootWindow* second_root = roots[1];
1500 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1501 second_root).work_area();
1502 browser()->window()->SetBounds(work_area);
1504 // position both browser windows side by side on the second screen.
1505 gfx::Rect work_area2(work_area);
1506 work_area.set_width(work_area.width()/2);
1507 browser()->window()->SetBounds(work_area);
1508 work_area2.set_x(work_area2.x() + work_area2.width()/2);
1509 work_area2.set_width(work_area2.width()/2);
1510 browser2->window()->SetBounds(work_area2);
1511 EXPECT_EQ(second_root,
1512 browser()->window()->GetNativeWindow()->GetRootWindow());
1513 EXPECT_EQ(second_root,
1514 browser2->window()->GetNativeWindow()->GetRootWindow());
1516 // Move to the first tab and drag it enough so that it detaches, but not
1517 // enough that it attaches to browser2.
1518 // SetEventGeneratorRootWindow sets correct (second) RootWindow
1519 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1520 SetEventGeneratorRootWindow(tab_0_center);
1521 ASSERT_TRUE(PressInput(tab_0_center));
1522 ASSERT_TRUE(DragInputToNotifyWhenDone(
1523 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1524 base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1525 this, tab_strip, tab_strip2)));
1526 QuitWhenNotDragging();
1528 // Should now be attached to tab_strip2.
1529 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1530 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1531 ASSERT_TRUE(TabDragController::IsActive());
1533 // Release the mouse, stopping the drag session.
1534 ASSERT_TRUE(ReleaseInput());
1535 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1536 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1537 ASSERT_FALSE(TabDragController::IsActive());
1538 EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1539 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1541 // Both windows should not be maximized
1542 EXPECT_FALSE(browser()->window()->IsMaximized());
1543 EXPECT_FALSE(browser2->window()->IsMaximized());
1546 // Drags from a maximized browser to another non-maximized browser on a second
1547 // display and releases input.
1548 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1549 DragMaxTabToNonMaxWindowInSeparateDisplay) {
1551 AddTabAndResetBrowser(browser());
1552 browser()->window()->Maximize();
1553 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1555 // Create another browser on the second display.
1556 std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1557 ASSERT_EQ(2u, roots.size());
1558 aura::RootWindow* first_root = roots[0];
1559 aura::RootWindow* second_root = roots[1];
1560 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1561 second_root).work_area();
1562 work_area.Inset(20,20,20,60);
1563 Browser::CreateParams params(browser()->profile(),
1564 browser()->host_desktop_type());
1565 params.initial_show_state = ui::SHOW_STATE_NORMAL;
1566 params.initial_bounds = work_area;
1567 Browser* browser2 = new Browser(params);
1568 AddBlankTabAndShow(browser2);
1570 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1571 ResetIDs(browser2->tab_strip_model(), 100);
1573 EXPECT_EQ(second_root,
1574 browser2->window()->GetNativeWindow()->GetRootWindow());
1575 EXPECT_EQ(first_root,
1576 browser()->window()->GetNativeWindow()->GetRootWindow());
1577 EXPECT_EQ(2, tab_strip->tab_count());
1578 EXPECT_EQ(1, tab_strip2->tab_count());
1580 // Move to the first tab and drag it enough so that it detaches, but not
1581 // enough that it attaches to browser2.
1582 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1583 ASSERT_TRUE(PressInput(tab_0_center));
1584 ASSERT_TRUE(DragInputToNotifyWhenDone(
1585 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1586 base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1587 this, tab_strip, tab_strip2)));
1588 QuitWhenNotDragging();
1590 // Should now be attached to tab_strip2.
1591 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1592 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1593 ASSERT_TRUE(TabDragController::IsActive());
1595 // Release the mouse, stopping the drag session.
1596 ASSERT_TRUE(ReleaseInput());
1598 // tab should have moved
1599 EXPECT_EQ(1, tab_strip->tab_count());
1600 EXPECT_EQ(2, tab_strip2->tab_count());
1602 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1603 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1604 ASSERT_FALSE(TabDragController::IsActive());
1605 EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1606 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1608 // Source browser should still be maximized, target should not
1609 EXPECT_TRUE(browser()->window()->IsMaximized());
1610 EXPECT_FALSE(browser2->window()->IsMaximized());
1613 // Immersive fullscreen is ChromeOS only.
1614 #if defined(OS_CHROMEOS)
1615 // Drags from a restored browser to an immersive fullscreen browser on a
1616 // second display and releases input.
1617 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1618 DragTabToImmersiveBrowserOnSeparateDisplay) {
1620 AddTabAndResetBrowser(browser());
1621 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1623 // Create another browser.
1624 Browser* browser2 = CreateBrowser(browser()->profile());
1625 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1626 ResetIDs(browser2->tab_strip_model(), 100);
1628 // Move the second browser to the second display.
1629 std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1630 ASSERT_EQ(2u, roots.size());
1631 aura::RootWindow* second_root = roots[1];
1632 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1633 second_root).work_area();
1634 browser2->window()->SetBounds(work_area);
1635 EXPECT_EQ(second_root,
1636 browser2->window()->GetNativeWindow()->GetRootWindow());
1638 // Put the second browser into immersive fullscreen.
1639 BrowserView* browser_view2 = BrowserView::GetBrowserViewForBrowser(browser2);
1640 ImmersiveModeController* immersive_controller2 =
1641 browser_view2->immersive_mode_controller();
1642 immersive_controller2->SetupForTest();
1643 chrome::ToggleFullscreenMode(browser2);
1644 ASSERT_TRUE(immersive_controller2->IsEnabled());
1645 ASSERT_FALSE(immersive_controller2->IsRevealed());
1646 ASSERT_TRUE(tab_strip2->IsImmersiveStyle());
1648 // Move to the first tab and drag it enough so that it detaches, but not
1649 // enough that it attaches to browser2.
1650 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1651 ASSERT_TRUE(PressInput(tab_0_center));
1652 ASSERT_TRUE(DragInputToNotifyWhenDone(
1653 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1654 base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1655 this, tab_strip, tab_strip2)));
1656 QuitWhenNotDragging();
1658 // Should now be attached to tab_strip2.
1659 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1660 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1661 ASSERT_TRUE(TabDragController::IsActive());
1663 // browser2's top chrome should be revealed and the tab strip should be
1664 // at normal height while user is tragging tabs_strip2's tabs.
1665 ASSERT_TRUE(immersive_controller2->IsRevealed());
1666 ASSERT_FALSE(tab_strip2->IsImmersiveStyle());
1668 // Release the mouse, stopping the drag session.
1669 ASSERT_TRUE(ReleaseInput());
1670 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1671 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1672 ASSERT_FALSE(TabDragController::IsActive());
1673 EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1674 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1676 // The first browser window should not be in immersive fullscreen.
1677 // browser2 should still be in immersive fullscreen, but the top chrome should
1678 // no longer be revealed.
1679 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
1680 EXPECT_FALSE(browser_view->immersive_mode_controller()->IsEnabled());
1682 EXPECT_TRUE(immersive_controller2->IsEnabled());
1683 EXPECT_FALSE(immersive_controller2->IsRevealed());
1684 EXPECT_TRUE(tab_strip2->IsImmersiveStyle());
1686 #endif // OS_CHROMEOS
1688 // Subclass of DetachToBrowserTabDragControllerTest that
1689 // creates multiple displays with different device scale factors.
1690 class DifferentDeviceScaleFactorDisplayTabDragControllerTest
1691 : public DetachToBrowserTabDragControllerTest {
1693 DifferentDeviceScaleFactorDisplayTabDragControllerTest() {}
1694 virtual ~DifferentDeviceScaleFactorDisplayTabDragControllerTest() {}
1696 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1697 DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
1698 command_line->AppendSwitchASCII("ash-host-window-bounds",
1699 "400x400,0+400-800x800*2");
1702 float GetCursorDeviceScaleFactor() const {
1703 ash::test::CursorManagerTestApi cursor_test_api(
1704 ash::Shell::GetInstance()->cursor_manager());
1705 return cursor_test_api.GetDisplay().device_scale_factor();
1709 DISALLOW_COPY_AND_ASSIGN(
1710 DifferentDeviceScaleFactorDisplayTabDragControllerTest);
1715 // The points where a tab is dragged in CursorDeviceScaleFactorStep.
1716 const struct DragPoint {
1727 // The expected device scale factors before the cursor is moved to the
1728 // corresponding kDragPoints in CursorDeviceScaleFactorStep.
1729 const float kDeviceScaleFactorExpectations[] = {
1738 arraysize(kDragPoints) == arraysize(kDeviceScaleFactorExpectations),
1739 kDragPoints_and_kDeviceScaleFactorExpectations_must_have_same_size);
1741 // Drags tab to |kDragPoints[index]|, then calls the next step function.
1742 void CursorDeviceScaleFactorStep(
1743 DifferentDeviceScaleFactorDisplayTabDragControllerTest* test,
1744 TabStrip* not_attached_tab_strip,
1746 ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
1747 ASSERT_TRUE(TabDragController::IsActive());
1749 if (index < arraysize(kDragPoints)) {
1750 EXPECT_EQ(kDeviceScaleFactorExpectations[index],
1751 test->GetCursorDeviceScaleFactor());
1752 const DragPoint p = kDragPoints[index];
1753 ASSERT_TRUE(test->DragInputToNotifyWhenDone(
1754 p.x, p.y, base::Bind(&CursorDeviceScaleFactorStep,
1755 test, not_attached_tab_strip, index + 1)));
1757 // Finishes a serise of CursorDeviceScaleFactorStep calls and ends drag.
1758 EXPECT_EQ(1.0f, test->GetCursorDeviceScaleFactor());
1759 ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
1760 ui_controls::LEFT, ui_controls::UP));
1766 // Verifies cursor's device scale factor is updated when a tab is moved across
1767 // displays with different device scale factors (http://crbug.com/154183).
1768 IN_PROC_BROWSER_TEST_P(DifferentDeviceScaleFactorDisplayTabDragControllerTest,
1769 CursorDeviceScaleFactor) {
1771 AddTabAndResetBrowser(browser());
1772 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1774 // Move the second browser to the second display.
1775 std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1776 ASSERT_EQ(2u, roots.size());
1778 // Move to the first tab and drag it enough so that it detaches.
1779 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1780 ASSERT_TRUE(PressInput(tab_0_center));
1781 ASSERT_TRUE(DragInputToNotifyWhenDone(
1782 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1783 base::Bind(&CursorDeviceScaleFactorStep,
1784 this, tab_strip, 0)));
1785 QuitWhenNotDragging();
1790 class DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest
1791 : public TabDragControllerTest {
1793 DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest() {}
1795 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1796 TabDragControllerTest::SetUpCommandLine(command_line);
1797 command_line->AppendSwitchASCII("ash-host-window-bounds",
1798 "0+0-250x250,251+0-250x250");
1801 bool Press(const gfx::Point& position) {
1802 return ui_test_utils::SendMouseMoveSync(position) &&
1803 ui_test_utils::SendMouseEventsSync(ui_controls::LEFT,
1807 bool DragTabAndExecuteTaskWhenDone(const gfx::Point& position,
1808 const base::Closure& task) {
1809 return ui_controls::SendMouseMoveNotifyWhenDone(
1810 position.x(), position.y(), task);
1813 void QuitWhenNotDragging() {
1814 test::QuitWhenNotDraggingImpl();
1815 base::MessageLoop::current()->Run();
1819 DISALLOW_COPY_AND_ASSIGN(
1820 DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest);
1823 // Invoked from the nested message loop.
1824 void CancelDragTabToWindowInSeparateDisplayStep3(
1825 TabStrip* tab_strip,
1826 const BrowserList* browser_list) {
1827 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1828 ASSERT_TRUE(TabDragController::IsActive());
1829 ASSERT_EQ(2u, browser_list->size());
1831 // Switching display mode should cancel the drag operation.
1832 ash::internal::DisplayManager* display_manager =
1833 ash::Shell::GetInstance()->display_manager();
1834 display_manager->AddRemoveDisplay();
1837 // Invoked from the nested message loop.
1838 void CancelDragTabToWindowInSeparateDisplayStep2(
1839 DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest* test,
1840 TabStrip* tab_strip,
1841 aura::RootWindow* current_root,
1842 gfx::Point final_destination,
1843 const BrowserList* browser_list) {
1844 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1845 ASSERT_TRUE(TabDragController::IsActive());
1846 ASSERT_EQ(2u, browser_list->size());
1848 Browser* new_browser = browser_list->get(1);
1849 EXPECT_EQ(current_root,
1850 new_browser->window()->GetNativeWindow()->GetRootWindow());
1852 ASSERT_TRUE(test->DragTabAndExecuteTaskWhenDone(
1854 base::Bind(&CancelDragTabToWindowInSeparateDisplayStep3,
1855 tab_strip, browser_list)));
1860 // Drags from browser to a second display and releases input.
1861 IN_PROC_BROWSER_TEST_F(
1862 DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
1863 CancelDragTabToWindowIn2ndDisplay) {
1865 AddTabAndResetBrowser(browser());
1866 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1868 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1870 // Move the second browser to the second display.
1871 std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1872 ASSERT_EQ(2u, roots.size());
1873 gfx::Point final_destination =
1874 gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1875 roots[1]).work_area().CenterPoint();
1877 // Move to the first tab and drag it enough so that it detaches, but not
1878 // enough to move to another display.
1879 gfx::Point tab_0_dst(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1880 ASSERT_TRUE(Press(tab_0_dst));
1881 tab_0_dst.Offset(0, GetDetachY(tab_strip));
1882 ASSERT_TRUE(DragTabAndExecuteTaskWhenDone(
1883 tab_0_dst, base::Bind(&CancelDragTabToWindowInSeparateDisplayStep2,
1884 this, tab_strip, roots[0], final_destination,
1885 native_browser_list)));
1886 QuitWhenNotDragging();
1888 ASSERT_EQ(1u, native_browser_list->size());
1889 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1890 ASSERT_FALSE(TabDragController::IsActive());
1891 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1893 // Release the mouse
1894 ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
1895 ui_controls::LEFT, ui_controls::UP));
1898 // Drags from browser from a second display to primary and releases input.
1899 IN_PROC_BROWSER_TEST_F(
1900 DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
1901 CancelDragTabToWindowIn1stDisplay) {
1902 std::vector<aura::RootWindow*> roots(ash::Shell::GetAllRootWindows());
1903 ASSERT_EQ(2u, roots.size());
1906 AddTabAndResetBrowser(browser());
1907 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1909 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1910 EXPECT_EQ(roots[0], browser()->window()->GetNativeWindow()->GetRootWindow());
1912 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->
1913 GetDisplayNearestWindow(roots[1]).work_area();
1914 browser()->window()->SetBounds(work_area);
1915 EXPECT_EQ(roots[1], browser()->window()->GetNativeWindow()->GetRootWindow());
1917 // Move the second browser to the display.
1918 gfx::Point final_destination =
1919 gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1920 roots[0]).work_area().CenterPoint();
1922 // Move to the first tab and drag it enough so that it detaches, but not
1923 // enough to move to another display.
1924 gfx::Point tab_0_dst(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1925 ASSERT_TRUE(Press(tab_0_dst));
1926 tab_0_dst.Offset(0, GetDetachY(tab_strip));
1927 ASSERT_TRUE(DragTabAndExecuteTaskWhenDone(
1928 tab_0_dst, base::Bind(&CancelDragTabToWindowInSeparateDisplayStep2,
1929 this, tab_strip, roots[1], final_destination,
1930 native_browser_list)));
1931 QuitWhenNotDragging();
1933 ASSERT_EQ(1u, native_browser_list->size());
1934 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1935 ASSERT_FALSE(TabDragController::IsActive());
1936 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1938 // Release the mouse
1939 ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
1940 ui_controls::LEFT, ui_controls::UP));
1945 void DetachToOwnWindowTwoFingersDragStep5(
1946 DetachToBrowserTabDragControllerTest* test) {
1947 ASSERT_EQ(2u, test->native_browser_list->size());
1948 Browser* new_browser = test->native_browser_list->get(1);
1949 ASSERT_TRUE(new_browser->window()->IsActive());
1951 ASSERT_TRUE(test->ReleaseInput());
1952 ASSERT_TRUE(test->ReleaseInput2());
1953 ASSERT_TRUE(new_browser->window()->IsActive());
1956 void DetachToOwnWindowTwoFingersDragStep4(
1957 DetachToBrowserTabDragControllerTest* test,
1958 const gfx::Point& target_point) {
1959 ASSERT_EQ(2u, test->native_browser_list->size());
1960 Browser* new_browser = test->native_browser_list->get(1);
1961 ASSERT_TRUE(new_browser->window()->IsActive());
1963 ASSERT_TRUE(test->DragInput2ToNotifyWhenDone(
1964 target_point.x(), target_point.y(),
1965 base::Bind(&DetachToOwnWindowTwoFingersDragStep5, test)));
1968 void DetachToOwnWindowTwoFingersDragStep3(
1969 DetachToBrowserTabDragControllerTest* test,
1970 const gfx::Point& target_point) {
1971 ASSERT_TRUE(test->PressInput2());
1973 ASSERT_EQ(2u, test->native_browser_list->size());
1974 Browser* new_browser = test->native_browser_list->get(1);
1975 ASSERT_TRUE(new_browser->window()->IsActive());
1977 ASSERT_TRUE(test->DragInputToDelayedNotifyWhenDone(
1978 target_point.x(), target_point.y(),
1979 base::Bind(&DetachToOwnWindowTwoFingersDragStep4,
1982 base::TimeDelta::FromMilliseconds(60)));
1985 void DetachToOwnWindowTwoFingersDragStep2(
1986 DetachToBrowserTabDragControllerTest* test,
1987 const gfx::Point& target_point) {
1988 ASSERT_EQ(2u, test->native_browser_list->size());
1989 Browser* new_browser = test->native_browser_list->get(1);
1990 ASSERT_TRUE(new_browser->window()->IsActive());
1992 ASSERT_TRUE(test->DragInputToDelayedNotifyWhenDone(
1993 target_point.x(), target_point.y(),
1994 base::Bind(&DetachToOwnWindowTwoFingersDragStep3,
1996 target_point + gfx::Vector2d(-2, 1)),
1997 base::TimeDelta::FromMilliseconds(60)));
2002 // Drags from browser to separate window starting with one finger and
2003 // then continuing with two fingers.
2004 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
2005 DetachToOwnWindowTwoFingers) {
2006 gfx::Rect bounds(browser()->window()->GetBounds());
2008 AddTabAndResetBrowser(browser());
2009 TabStrip* tab_strip = GetTabStripForBrowser(browser());
2011 // Move to the first tab and drag it enough so that it detaches.
2012 gfx::Point tab_0_center(
2013 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
2014 ASSERT_TRUE(PressInput(tab_0_center));
2015 // Drags in this test are very short to avoid fling.
2016 ASSERT_TRUE(DragInputToDelayedNotifyWhenDone(
2017 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
2018 base::Bind(&DetachToOwnWindowTwoFingersDragStep2,
2019 this, gfx::Point(5 + tab_0_center.x(),
2020 1 + tab_0_center.y()
2021 + GetDetachY(tab_strip))),
2022 base::TimeDelta::FromMilliseconds(60)));
2023 // Continue dragging, first with one finger, then with two fingers.
2024 QuitWhenNotDragging();
2026 // There should now be another browser.
2027 ASSERT_EQ(2u, native_browser_list->size());
2028 Browser* new_browser = native_browser_list->get(1);
2029 ASSERT_TRUE(new_browser->window()->IsActive());
2030 // The sequence of drags should successfully move the browser window.
2031 bounds += gfx::Vector2d(5 - 2, 1 + 1 + GetDetachY(tab_strip));
2032 EXPECT_EQ(bounds.ToString(),
2033 new_browser->window()->GetNativeWindow()->bounds().ToString());
2036 // Subclass of DetachToBrowserTabDragControllerTest that runs tests with
2037 // docked windows enabled and disabled.
2038 class DetachToDockedTabDragControllerTest
2039 : public DetachToBrowserTabDragControllerTest {
2041 DetachToDockedTabDragControllerTest() {}
2042 virtual ~DetachToDockedTabDragControllerTest() {}
2045 DISALLOW_COPY_AND_ASSIGN(DetachToDockedTabDragControllerTest);
2050 void DetachToDockedWindowNextStep(
2051 DetachToDockedTabDragControllerTest* test,
2052 const gfx::Point& target_point,
2054 ASSERT_EQ(2u, test->native_browser_list->size());
2055 Browser* new_browser = test->native_browser_list->get(1);
2056 ASSERT_TRUE(new_browser->window()->IsActive());
2059 ASSERT_TRUE(test->ReleaseInput());
2062 ASSERT_TRUE(test->DragInputToNotifyWhenDone(
2063 target_point.x(), target_point.y(),
2064 base::Bind(&DetachToDockedWindowNextStep,
2066 gfx::Point(target_point.x(), 1 + target_point.y()),
2072 // Drags from browser to separate window, docks that window and releases mouse.
2073 IN_PROC_BROWSER_TEST_P(DetachToDockedTabDragControllerTest,
2074 DetachToDockedWindowFromMaximizedWindow) {
2075 if (!TabDragController::ShouldDetachIntoNewBrowser()) {
2077 << "Skipping DetachToDockedWindowFromMaximizedWindow on this platform.";
2081 // Maximize the initial browser window.
2082 browser()->window()->Maximize();
2083 ASSERT_TRUE(browser()->window()->IsMaximized());
2086 AddTabAndResetBrowser(browser());
2087 TabStrip* tab_strip = GetTabStripForBrowser(browser());
2089 // Move to the first tab and drag it enough so that it detaches.
2090 gfx::Point tab_0_center(
2091 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
2092 ASSERT_TRUE(PressInput(tab_0_center));
2094 // The following matches kMovesBeforeAdjust in snap_sizer.cc
2095 const int kNumIterations = 25 * 5 + 10;
2096 ASSERT_TRUE(DragInputToNotifyWhenDone(
2097 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
2098 base::Bind(&DetachToDockedWindowNextStep, this,
2099 gfx::Point(0, tab_0_center.y() + GetDetachY(tab_strip)),
2101 // Continue dragging enough times to go through snapping sequence and dock
2103 QuitWhenNotDragging();
2104 // Should no longer be dragging.
2105 ASSERT_FALSE(tab_strip->IsDragSessionActive());
2106 ASSERT_FALSE(TabDragController::IsActive());
2108 // There should now be another browser.
2109 ASSERT_EQ(2u, native_browser_list->size());
2110 Browser* new_browser = native_browser_list->get(1);
2111 ASSERT_TRUE(new_browser->window()->IsActive());
2112 TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
2113 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
2115 EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
2116 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
2118 // The bounds of the initial window should not have changed.
2119 EXPECT_TRUE(browser()->window()->IsMaximized());
2121 EXPECT_TRUE(GetTrackedByWorkspace(browser()));
2122 EXPECT_TRUE(GetTrackedByWorkspace(new_browser));
2123 // After this both windows should still be manageable.
2124 EXPECT_TRUE(IsWindowPositionManaged(browser()->window()->GetNativeWindow()));
2125 EXPECT_TRUE(IsWindowPositionManaged(
2126 new_browser->window()->GetNativeWindow()));
2128 // The new window should be docked and not maximized if docking is allowed.
2129 ash::wm::WindowState* window_state =
2130 ash::wm::GetWindowState(new_browser->window()->GetNativeWindow());
2131 if (docked_windows_enabled()) {
2132 EXPECT_FALSE(new_browser->window()->IsMaximized());
2133 EXPECT_TRUE(window_state->IsDocked());
2135 EXPECT_TRUE(new_browser->window()->IsMaximized());
2136 EXPECT_FALSE(window_state->IsDocked());
2143 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
2144 INSTANTIATE_TEST_CASE_P(TabDragging,
2145 DetachToBrowserInSeparateDisplayTabDragControllerTest,
2146 ::testing::Values("mouse", "touch"));
2147 INSTANTIATE_TEST_CASE_P(TabDragging,
2148 DifferentDeviceScaleFactorDisplayTabDragControllerTest,
2149 ::testing::Values("mouse"));
2150 INSTANTIATE_TEST_CASE_P(TabDragging,
2151 DetachToBrowserTabDragControllerTest,
2152 ::testing::Values("mouse", "touch"));
2153 INSTANTIATE_TEST_CASE_P(TabDragging,
2154 DetachToDockedTabDragControllerTest,
2155 ::testing::Values("mouse", "mouse docked"));
2156 INSTANTIATE_TEST_CASE_P(TabDragging,
2157 DetachToBrowserTabDragControllerTestTouch,
2158 ::testing::Values("touch", "touch docked"));
2160 INSTANTIATE_TEST_CASE_P(TabDragging,
2161 DetachToBrowserTabDragControllerTest,
2162 ::testing::Values("mouse"));