Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / tabs / tab_drag_controller_interactive_uitest.cc
blob18262cd597795882c5cd2810813b41d60f148312
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"
8 #include "base/bind.h"
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/run_loop.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_commands.h"
16 #include "chrome/browser/ui/browser_iterator.h"
17 #include "chrome/browser/ui/browser_list.h"
18 #include "chrome/browser/ui/host_desktop.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chrome/browser/ui/views/frame/browser_view.h"
21 #include "chrome/browser/ui/views/frame/native_browser_frame_factory.h"
22 #include "chrome/browser/ui/views/tabs/tab.h"
23 #include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
24 #include "chrome/browser/ui/views/tabs/tab_strip.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/test/base/in_process_browser_test.h"
27 #include "chrome/test/base/interactive_test_utils.h"
28 #include "chrome/test/base/ui_test_utils.h"
29 #include "content/public/browser/notification_details.h"
30 #include "content/public/browser/notification_observer.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/notification_source.h"
33 #include "content/public/browser/web_contents.h"
34 #include "ui/base/test/ui_controls.h"
35 #include "ui/gfx/screen.h"
36 #include "ui/views/view.h"
37 #include "ui/views/widget/widget.h"
39 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
40 #include "chrome/browser/ui/views/frame/desktop_browser_frame_aura.h"
41 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
42 #endif
44 #if defined(USE_ASH)
45 #include "ash/ash_switches.h"
46 #include "ash/display/display_controller.h"
47 #include "ash/display/display_manager.h"
48 #include "ash/shell.h"
49 #include "ash/test/cursor_manager_test_api.h"
50 #include "ash/wm/coordinate_conversion.h"
51 #include "ash/wm/window_state.h"
52 #include "ash/wm/window_util.h"
53 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
54 #include "ui/aura/client/screen_position_client.h"
55 #include "ui/aura/root_window.h"
56 #include "ui/aura/test/event_generator.h"
57 #endif
59 using content::WebContents;
61 namespace test {
63 namespace {
65 const char kTabDragControllerInteractiveUITestUserDataKey[] =
66 "TabDragControllerInteractiveUITestUserData";
68 class TabDragControllerInteractiveUITestUserData
69 : public base::SupportsUserData::Data {
70 public:
71 explicit TabDragControllerInteractiveUITestUserData(int id) : id_(id) {}
72 virtual ~TabDragControllerInteractiveUITestUserData() {}
73 int id() { return id_; }
75 private:
76 int id_;
79 } // namespace
81 class QuitDraggingObserver : public content::NotificationObserver {
82 public:
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();
93 delete this;
96 private:
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, &center);
107 return center;
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) {
121 std::string result;
122 for (int i = 0; i < model->count(); ++i) {
123 if (i != 0)
124 result += " ";
125 WebContents* contents = model->GetWebContentsAt(i);
126 TabDragControllerInteractiveUITestUserData* user_data =
127 static_cast<TabDragControllerInteractiveUITestUserData*>(
128 contents->GetUserData(
129 &kTabDragControllerInteractiveUITestUserDataKey));
130 if (user_data)
131 result += base::IntToString(user_data->id());
132 else
133 result += "?";
135 return result;
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());
148 } // namespace test
150 using test::GetCenterInScreenCoordinates;
151 using test::SetID;
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).
168 UseRealGLBindings();
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()));
197 return browser2;
200 namespace {
202 enum InputSource {
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 GetIsDragged(Browser* browser) {
214 #if !defined(USE_ASH) || defined(OS_WIN) // TODO(win_ash)
215 return false;
216 #else
217 return ash::wm::GetWindowState(browser->window()->GetNativeWindow())->
218 is_dragged();
219 #endif
222 } // namespace
224 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
225 class ScreenEventGeneratorDelegate : public aura::test::EventGeneratorDelegate {
226 public:
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_);
242 private:
243 aura::Window* root_window_;
245 DISALLOW_COPY_AND_ASSIGN(ScreenEventGeneratorDelegate);
248 #endif
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 {
259 public:
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();
279 private:
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 {
288 public:
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);
298 private:
299 DISALLOW_COPY_AND_ASSIGN(TestNativeBrowserFrameFactory);
302 class TabDragCaptureLostTest : public TabDragControllerTest {
303 public:
304 TabDragCaptureLostTest() {
305 NativeBrowserFrameFactory::Set(new TestNativeBrowserFrameFactory);
308 private:
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());
333 IN_PROC_BROWSER_TEST_F(TabDragControllerTest, GestureEndShouldEndDragTest) {
334 AddTabAndResetBrowser(browser());
336 TabStrip* tab_strip = GetTabStripForBrowser(browser());
337 TabStripModel* model = browser()->tab_strip_model();
339 Tab* tab1 = tab_strip->tab_at(1);
340 gfx::Point tab_1_center(tab1->width() / 2, tab1->height() / 2);
342 ui::GestureEvent gesture_begin(ui::ET_GESTURE_BEGIN, tab_1_center.x(),
343 tab_1_center.x(), 0, base::TimeDelta(),
344 ui::GestureEventDetails(ui::ET_GESTURE_BEGIN, 0.0f, 0.0f), 0);
345 tab_strip->MaybeStartDrag(tab1, gesture_begin,
346 tab_strip->GetSelectionModel());
347 //tab_strip->tab_at(1)->OnGestureEvent(&gesture_begin);
348 EXPECT_TRUE(TabDragController::IsActive());
350 ui::GestureEvent gesture_end(ui::ET_GESTURE_END, tab_1_center.x(),
351 tab_1_center.x(), 0, base::TimeDelta(),
352 ui::GestureEventDetails(ui::ET_GESTURE_END, 0.0f, 0.0f), 0);
353 tab_strip->OnGestureEvent(&gesture_end);
354 EXPECT_FALSE(TabDragController::IsActive());
355 EXPECT_FALSE(tab_strip->IsDragSessionActive());
358 #endif
360 class DetachToBrowserTabDragControllerTest
361 : public TabDragControllerTest,
362 public ::testing::WithParamInterface<const char*> {
363 public:
364 DetachToBrowserTabDragControllerTest() {}
366 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
367 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
368 if (!docked_windows_enabled()) {
369 CommandLine::ForCurrentProcess()->AppendSwitch(
370 ash::switches::kAshDisableDockedWindows);
372 #endif
375 virtual void SetUpOnMainThread() OVERRIDE {
376 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
377 event_generator_.reset(new aura::test::EventGenerator(
378 ash::Shell::GetPrimaryRootWindow()));
379 #endif
382 InputSource input_source() const {
383 return strstr(GetParam(), "mouse") ?
384 INPUT_SOURCE_MOUSE : INPUT_SOURCE_TOUCH;
387 bool docked_windows_enabled() const {
388 return (strstr(GetParam(), "docked") != NULL);
391 // Set root window from a point in screen coordinates
392 void SetEventGeneratorRootWindow(const gfx::Point& point) {
393 if (input_source() == INPUT_SOURCE_MOUSE)
394 return;
395 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
396 event_generator_.reset(new aura::test::EventGenerator(
397 new ScreenEventGeneratorDelegate(ash::wm::GetRootWindowAt(point))));
398 #endif
401 // The following methods update one of the mouse or touch input depending upon
402 // the InputSource.
403 bool PressInput(const gfx::Point& location) {
404 if (input_source() == INPUT_SOURCE_MOUSE) {
405 return ui_test_utils::SendMouseMoveSync(location) &&
406 ui_test_utils::SendMouseEventsSync(
407 ui_controls::LEFT, ui_controls::DOWN);
409 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
410 event_generator_->set_current_location(location);
411 event_generator_->PressTouch();
412 #else
413 NOTREACHED();
414 #endif
415 return true;
418 bool PressInput2() {
419 // Second touch input is only used for touch sequence tests.
420 EXPECT_EQ(INPUT_SOURCE_TOUCH, input_source());
421 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
422 event_generator_->set_current_location(
423 event_generator_->current_location());
424 event_generator_->PressTouchId(1);
425 #else
426 NOTREACHED();
427 #endif
428 return true;
431 bool DragInputTo(const gfx::Point& location) {
432 if (input_source() == INPUT_SOURCE_MOUSE)
433 return ui_test_utils::SendMouseMoveSync(location);
434 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
435 event_generator_->MoveTouch(location);
436 #else
437 NOTREACHED();
438 #endif
439 return true;
442 bool DragInputToAsync(const gfx::Point& location) {
443 if (input_source() == INPUT_SOURCE_MOUSE)
444 return ui_controls::SendMouseMove(location.x(), location.y());
445 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
446 event_generator_->MoveTouch(location);
447 #else
448 NOTREACHED();
449 #endif
450 return true;
453 bool DragInputToNotifyWhenDone(int x,
454 int y,
455 const base::Closure& task) {
456 if (input_source() == INPUT_SOURCE_MOUSE)
457 return ui_controls::SendMouseMoveNotifyWhenDone(x, y, task);
458 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
459 base::MessageLoop::current()->PostTask(FROM_HERE, task);
460 event_generator_->MoveTouch(gfx::Point(x, y));
461 #else
462 NOTREACHED();
463 #endif
464 return true;
467 bool DragInputToDelayedNotifyWhenDone(int x,
468 int y,
469 const base::Closure& task,
470 base::TimeDelta delay) {
471 if (input_source() == INPUT_SOURCE_MOUSE)
472 return ui_controls::SendMouseMoveNotifyWhenDone(x, y, task);
473 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
474 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, task, delay);
475 event_generator_->MoveTouch(gfx::Point(x, y));
476 #else
477 NOTREACHED();
478 #endif
479 return true;
482 bool DragInput2ToNotifyWhenDone(int x,
483 int y,
484 const base::Closure& task) {
485 if (input_source() == INPUT_SOURCE_MOUSE)
486 return ui_controls::SendMouseMoveNotifyWhenDone(x, y, task);
487 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
488 base::MessageLoop::current()->PostTask(FROM_HERE, task);
489 event_generator_->MoveTouchId(gfx::Point(x, y), 1);
490 #else
491 NOTREACHED();
492 #endif
493 return true;
496 bool ReleaseInput() {
497 if (input_source() == INPUT_SOURCE_MOUSE) {
498 return ui_test_utils::SendMouseEventsSync(
499 ui_controls::LEFT, ui_controls::UP);
501 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
502 event_generator_->ReleaseTouch();
503 #else
504 NOTREACHED();
505 #endif
506 return true;
509 bool ReleaseInput2() {
510 if (input_source() == INPUT_SOURCE_MOUSE) {
511 return ui_test_utils::SendMouseEventsSync(
512 ui_controls::LEFT, ui_controls::UP);
514 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
515 event_generator_->ReleaseTouchId(1);
516 #else
517 NOTREACHED();
518 #endif
519 return true;
522 bool ReleaseMouseAsync() {
523 return input_source() == INPUT_SOURCE_MOUSE &&
524 ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::UP);
527 void QuitWhenNotDragging() {
528 if (input_source() == INPUT_SOURCE_MOUSE) {
529 // Schedule observer to quit message loop when done dragging. This has to
530 // be async so the message loop can run.
531 test::QuitWhenNotDraggingImpl();
532 base::MessageLoop::current()->Run();
533 } else {
534 // Touch events are sync, so we know we're not in a drag session. But some
535 // tests rely on the browser fully closing, which is async. So, run all
536 // pending tasks.
537 base::RunLoop run_loop;
538 run_loop.RunUntilIdle();
542 void AddBlankTabAndShow(Browser* browser) {
543 InProcessBrowserTest::AddBlankTabAndShow(browser);
546 Browser* browser() const { return InProcessBrowserTest::browser(); }
548 private:
549 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
550 scoped_ptr<aura::test::EventGenerator> event_generator_;
551 #endif
553 DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTest);
556 // Creates a browser with two tabs, drags the second to the first.
557 // TODO(sky): this won't work with touch as it requires a long press.
558 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
559 DISABLED_DragInSameWindow) {
560 AddTabAndResetBrowser(browser());
562 TabStrip* tab_strip = GetTabStripForBrowser(browser());
563 TabStripModel* model = browser()->tab_strip_model();
565 gfx::Point tab_1_center(GetCenterInScreenCoordinates(tab_strip->tab_at(1)));
566 ASSERT_TRUE(PressInput(tab_1_center));
567 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
568 ASSERT_TRUE(DragInputTo(tab_0_center));
569 ASSERT_TRUE(ReleaseInput());
570 EXPECT_EQ("1 0", IDString(model));
571 EXPECT_FALSE(TabDragController::IsActive());
572 EXPECT_FALSE(tab_strip->IsDragSessionActive());
575 namespace {
577 // Invoked from the nested message loop.
578 void DragToSeparateWindowStep2(DetachToBrowserTabDragControllerTest* test,
579 TabStrip* not_attached_tab_strip,
580 TabStrip* target_tab_strip) {
581 ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
582 ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
583 ASSERT_TRUE(TabDragController::IsActive());
585 // Drag to target_tab_strip. This should stop the nested loop from dragging
586 // the window.
587 gfx::Point target_point(target_tab_strip->width() -1,
588 target_tab_strip->height() / 2);
589 views::View::ConvertPointToScreen(target_tab_strip, &target_point);
590 ASSERT_TRUE(test->DragInputToAsync(target_point));
593 } // namespace
595 // Creates two browsers, drags from first into second.
596 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
597 DragToSeparateWindow) {
598 TabStrip* tab_strip = GetTabStripForBrowser(browser());
600 // Add another tab to browser().
601 AddTabAndResetBrowser(browser());
603 // Create another browser.
604 Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
605 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
607 // Move to the first tab and drag it enough so that it detaches, but not
608 // enough that it attaches to browser2.
609 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
610 ASSERT_TRUE(PressInput(tab_0_center));
611 ASSERT_TRUE(DragInputToNotifyWhenDone(
612 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
613 base::Bind(&DragToSeparateWindowStep2,
614 this, tab_strip, tab_strip2)));
615 QuitWhenNotDragging();
617 // Should now be attached to tab_strip2.
618 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
619 ASSERT_FALSE(tab_strip->IsDragSessionActive());
620 ASSERT_TRUE(TabDragController::IsActive());
621 EXPECT_FALSE(GetIsDragged(browser()));
623 // Release the mouse, stopping the drag session.
624 ASSERT_TRUE(ReleaseInput());
625 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
626 ASSERT_FALSE(tab_strip->IsDragSessionActive());
627 ASSERT_FALSE(TabDragController::IsActive());
628 EXPECT_EQ("100 0", IDString(browser2->tab_strip_model()));
629 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
630 EXPECT_FALSE(GetIsDragged(browser2));
632 // Both windows should not be maximized
633 EXPECT_FALSE(browser()->window()->IsMaximized());
634 EXPECT_FALSE(browser2->window()->IsMaximized());
637 namespace {
639 void DetachToOwnWindowStep2(DetachToBrowserTabDragControllerTest* test) {
640 if (test->input_source() == INPUT_SOURCE_TOUCH)
641 ASSERT_TRUE(test->ReleaseInput());
644 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
645 bool IsWindowPositionManaged(aura::Window* window) {
646 return ash::wm::GetWindowState(window)->window_position_managed();
648 bool HasUserChangedWindowPositionOrSize(aura::Window* window) {
649 return ash::wm::GetWindowState(window)->bounds_changed_by_user();
651 #else
652 bool IsWindowPositionManaged(gfx::NativeWindow window) {
653 return true;
655 bool HasUserChangedWindowPositionOrSize(gfx::NativeWindow window) {
656 return false;
658 #endif
660 } // namespace
662 // Drags from browser to separate window and releases mouse.
663 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
664 DetachToOwnWindow) {
665 const gfx::Rect initial_bounds(browser()->window()->GetBounds());
666 // Add another tab.
667 AddTabAndResetBrowser(browser());
668 TabStrip* tab_strip = GetTabStripForBrowser(browser());
670 // Move to the first tab and drag it enough so that it detaches.
671 gfx::Point tab_0_center(
672 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
673 ASSERT_TRUE(PressInput(tab_0_center));
674 ASSERT_TRUE(DragInputToNotifyWhenDone(
675 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
676 base::Bind(&DetachToOwnWindowStep2, this)));
677 if (input_source() == INPUT_SOURCE_MOUSE) {
678 ASSERT_TRUE(ReleaseMouseAsync());
679 QuitWhenNotDragging();
682 // Should no longer be dragging.
683 ASSERT_FALSE(tab_strip->IsDragSessionActive());
684 ASSERT_FALSE(TabDragController::IsActive());
686 // There should now be another browser.
687 ASSERT_EQ(2u, native_browser_list->size());
688 Browser* new_browser = native_browser_list->get(1);
689 ASSERT_TRUE(new_browser->window()->IsActive());
690 TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
691 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
693 EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
694 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
696 // The bounds of the initial window should not have changed.
697 EXPECT_EQ(initial_bounds.ToString(),
698 browser()->window()->GetBounds().ToString());
700 EXPECT_FALSE(GetIsDragged(browser()));
701 EXPECT_FALSE(GetIsDragged(new_browser));
702 // After this both windows should still be manageable.
703 EXPECT_TRUE(IsWindowPositionManaged(browser()->window()->GetNativeWindow()));
704 EXPECT_TRUE(IsWindowPositionManaged(
705 new_browser->window()->GetNativeWindow()));
707 // Both windows should not be maximized
708 EXPECT_FALSE(browser()->window()->IsMaximized());
709 EXPECT_FALSE(new_browser->window()->IsMaximized());
712 // Drags from browser to separate window and releases mouse.
713 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
714 DetachToOwnWindowFromMaximizedWindow) {
715 // Maximize the initial browser window.
716 browser()->window()->Maximize();
717 ASSERT_TRUE(browser()->window()->IsMaximized());
719 // Add another tab.
720 AddTabAndResetBrowser(browser());
721 TabStrip* tab_strip = GetTabStripForBrowser(browser());
723 // Move to the first tab and drag it enough so that it detaches.
724 gfx::Point tab_0_center(
725 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
726 ASSERT_TRUE(PressInput(tab_0_center));
727 ASSERT_TRUE(DragInputToNotifyWhenDone(
728 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
729 base::Bind(&DetachToOwnWindowStep2, this)));
730 if (input_source() == INPUT_SOURCE_MOUSE) {
731 ASSERT_TRUE(ReleaseMouseAsync());
732 QuitWhenNotDragging();
735 // Should no longer be dragging.
736 ASSERT_FALSE(tab_strip->IsDragSessionActive());
737 ASSERT_FALSE(TabDragController::IsActive());
739 // There should now be another browser.
740 ASSERT_EQ(2u, native_browser_list->size());
741 Browser* new_browser = native_browser_list->get(1);
742 ASSERT_TRUE(new_browser->window()->IsActive());
743 TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
744 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
746 EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
747 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
749 // The bounds of the initial window should not have changed.
750 EXPECT_TRUE(browser()->window()->IsMaximized());
752 EXPECT_FALSE(GetIsDragged(browser()));
753 EXPECT_FALSE(GetIsDragged(new_browser));
754 // After this both windows should still be manageable.
755 EXPECT_TRUE(IsWindowPositionManaged(browser()->window()->GetNativeWindow()));
756 EXPECT_TRUE(IsWindowPositionManaged(
757 new_browser->window()->GetNativeWindow()));
759 // The new window should be maximized.
760 EXPECT_TRUE(new_browser->window()->IsMaximized());
763 // Deletes a tab being dragged before the user moved enough to start a drag.
764 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
765 DeleteBeforeStartedDragging) {
766 // Add another tab.
767 AddTabAndResetBrowser(browser());
768 TabStrip* tab_strip = GetTabStripForBrowser(browser());
770 // Click on the first tab, but don't move it.
771 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
772 ASSERT_TRUE(PressInput(tab_0_center));
774 // Should be dragging.
775 ASSERT_TRUE(tab_strip->IsDragSessionActive());
776 ASSERT_TRUE(TabDragController::IsActive());
778 // Delete the tab being dragged.
779 delete browser()->tab_strip_model()->GetWebContentsAt(0);
781 // Should have canceled dragging.
782 ASSERT_FALSE(tab_strip->IsDragSessionActive());
783 ASSERT_FALSE(TabDragController::IsActive());
785 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
786 EXPECT_FALSE(GetIsDragged(browser()));
789 // Deletes a tab being dragged while still attached.
790 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
791 DeleteTabWhileAttached) {
792 // Add another tab.
793 AddTabAndResetBrowser(browser());
794 TabStrip* tab_strip = GetTabStripForBrowser(browser());
796 // Click on the first tab and move it enough so that it starts dragging but is
797 // still attached.
798 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
799 ASSERT_TRUE(PressInput(tab_0_center));
800 ASSERT_TRUE(DragInputTo(
801 gfx::Point(tab_0_center.x() + 20, tab_0_center.y())));
803 // Should be dragging.
804 ASSERT_TRUE(tab_strip->IsDragSessionActive());
805 ASSERT_TRUE(TabDragController::IsActive());
807 // Delete the tab being dragged.
808 delete browser()->tab_strip_model()->GetWebContentsAt(0);
810 // Should have canceled dragging.
811 ASSERT_FALSE(tab_strip->IsDragSessionActive());
812 ASSERT_FALSE(TabDragController::IsActive());
814 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
816 EXPECT_FALSE(GetIsDragged(browser()));
819 namespace {
821 void DeleteWhileDetachedStep2(WebContents* tab) {
822 delete tab;
825 } // namespace
827 // Deletes a tab being dragged after dragging a tab so that a new window is
828 // created.
829 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
830 DeleteTabWhileDetached) {
831 // Add another tab.
832 AddTabAndResetBrowser(browser());
833 TabStrip* tab_strip = GetTabStripForBrowser(browser());
835 // Move to the first tab and drag it enough so that it detaches.
836 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
837 WebContents* to_delete =
838 browser()->tab_strip_model()->GetWebContentsAt(0);
839 ASSERT_TRUE(PressInput(tab_0_center));
840 ASSERT_TRUE(DragInputToNotifyWhenDone(
841 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
842 base::Bind(&DeleteWhileDetachedStep2, to_delete)));
843 QuitWhenNotDragging();
845 // Should not be dragging.
846 ASSERT_FALSE(tab_strip->IsDragSessionActive());
847 ASSERT_FALSE(TabDragController::IsActive());
849 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
851 EXPECT_FALSE(GetIsDragged(browser()));
854 namespace {
856 void DeleteSourceDetachedStep2(WebContents* tab,
857 const BrowserList* browser_list) {
858 ASSERT_EQ(2u, browser_list->size());
859 Browser* new_browser = browser_list->get(1);
860 // This ends up closing the source window.
861 delete tab;
862 // Cancel the drag.
863 ui_controls::SendKeyPress(new_browser->window()->GetNativeWindow(),
864 ui::VKEY_ESCAPE, false, false, false, false);
867 } // namespace
869 // Detaches a tab and while detached deletes a tab from the source so that the
870 // source window closes then presses escape to cancel the drag.
871 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
872 DeleteSourceDetached) {
873 // Add another tab.
874 AddTabAndResetBrowser(browser());
875 TabStrip* tab_strip = GetTabStripForBrowser(browser());
877 // Move to the first tab and drag it enough so that it detaches.
878 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
879 WebContents* to_delete = browser()->tab_strip_model()->GetWebContentsAt(1);
880 ASSERT_TRUE(PressInput(tab_0_center));
881 ASSERT_TRUE(DragInputToNotifyWhenDone(
882 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
883 base::Bind(&DeleteSourceDetachedStep2, to_delete, native_browser_list)));
884 QuitWhenNotDragging();
886 // Should not be dragging.
887 ASSERT_EQ(1u, native_browser_list->size());
888 Browser* new_browser = native_browser_list->get(0);
889 ASSERT_FALSE(GetTabStripForBrowser(new_browser)->IsDragSessionActive());
890 ASSERT_FALSE(TabDragController::IsActive());
892 EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
894 EXPECT_FALSE(GetIsDragged(new_browser));
896 // Remaining browser window should not be maximized
897 EXPECT_FALSE(new_browser->window()->IsMaximized());
900 namespace {
902 void PressEscapeWhileDetachedStep2(const BrowserList* browser_list) {
903 ASSERT_EQ(2u, browser_list->size());
904 Browser* new_browser = browser_list->get(1);
905 ui_controls::SendKeyPress(
906 new_browser->window()->GetNativeWindow(), ui::VKEY_ESCAPE, false, false,
907 false, false);
910 } // namespace
912 // This is disabled until NativeViewHost::Detach really detaches.
913 // Detaches a tab and while detached presses escape to revert the drag.
914 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
915 PressEscapeWhileDetached) {
916 // Add another tab.
917 AddTabAndResetBrowser(browser());
918 TabStrip* tab_strip = GetTabStripForBrowser(browser());
920 // Move to the first tab and drag it enough so that it detaches.
921 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
922 ASSERT_TRUE(PressInput(tab_0_center));
923 ASSERT_TRUE(DragInputToNotifyWhenDone(
924 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
925 base::Bind(&PressEscapeWhileDetachedStep2, native_browser_list)));
926 QuitWhenNotDragging();
928 // Should not be dragging.
929 ASSERT_FALSE(tab_strip->IsDragSessionActive());
930 ASSERT_FALSE(TabDragController::IsActive());
932 // And there should only be one window.
933 EXPECT_EQ(1u, native_browser_list->size());
935 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
937 // Remaining browser window should not be maximized
938 EXPECT_FALSE(browser()->window()->IsMaximized());
941 namespace {
943 void DragAllStep2(DetachToBrowserTabDragControllerTest* test,
944 const BrowserList* browser_list) {
945 // Should only be one window.
946 ASSERT_EQ(1u, browser_list->size());
947 if (test->input_source() == INPUT_SOURCE_TOUCH) {
948 ASSERT_TRUE(test->ReleaseInput());
949 } else {
950 ASSERT_TRUE(test->ReleaseMouseAsync());
954 } // namespace
956 // Selects multiple tabs and starts dragging the window.
957 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, DragAll) {
958 // Add another tab.
959 AddTabAndResetBrowser(browser());
960 TabStrip* tab_strip = GetTabStripForBrowser(browser());
961 browser()->tab_strip_model()->AddTabAtToSelection(0);
962 browser()->tab_strip_model()->AddTabAtToSelection(1);
964 // Move to the first tab and drag it enough so that it would normally
965 // detach.
966 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
967 ASSERT_TRUE(PressInput(tab_0_center));
968 ASSERT_TRUE(DragInputToNotifyWhenDone(
969 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
970 base::Bind(&DragAllStep2, this, native_browser_list)));
971 QuitWhenNotDragging();
973 // Should not be dragging.
974 ASSERT_FALSE(tab_strip->IsDragSessionActive());
975 ASSERT_FALSE(TabDragController::IsActive());
977 // And there should only be one window.
978 EXPECT_EQ(1u, native_browser_list->size());
980 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
982 EXPECT_FALSE(GetIsDragged(browser()));
984 // Remaining browser window should not be maximized
985 EXPECT_FALSE(browser()->window()->IsMaximized());
988 namespace {
990 // Invoked from the nested message loop.
991 void DragAllToSeparateWindowStep2(DetachToBrowserTabDragControllerTest* test,
992 TabStrip* attached_tab_strip,
993 TabStrip* target_tab_strip,
994 const BrowserList* browser_list) {
995 ASSERT_TRUE(attached_tab_strip->IsDragSessionActive());
996 ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
997 ASSERT_TRUE(TabDragController::IsActive());
998 ASSERT_EQ(2u, browser_list->size());
1000 // Drag to target_tab_strip. This should stop the nested loop from dragging
1001 // the window.
1002 gfx::Point target_point(target_tab_strip->width() - 1,
1003 target_tab_strip->height() / 2);
1004 views::View::ConvertPointToScreen(target_tab_strip, &target_point);
1005 ASSERT_TRUE(test->DragInputToAsync(target_point));
1008 } // namespace
1010 // Creates two browsers, selects all tabs in first and drags into second.
1011 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1012 DragAllToSeparateWindow) {
1013 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1015 // Add another tab to browser().
1016 AddTabAndResetBrowser(browser());
1018 // Create another browser.
1019 Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
1020 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1022 browser()->tab_strip_model()->AddTabAtToSelection(0);
1023 browser()->tab_strip_model()->AddTabAtToSelection(1);
1025 // Move to the first tab and drag it enough so that it detaches, but not
1026 // enough that it attaches to browser2.
1027 gfx::Point tab_0_center(
1028 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(&DragAllToSeparateWindowStep2, this, tab_strip, tab_strip2,
1033 native_browser_list)));
1034 QuitWhenNotDragging();
1036 // Should now be attached to tab_strip2.
1037 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1038 ASSERT_TRUE(TabDragController::IsActive());
1039 ASSERT_EQ(1u, native_browser_list->size());
1041 // Release the mouse, stopping the drag session.
1042 ASSERT_TRUE(ReleaseInput());
1043 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1044 ASSERT_FALSE(TabDragController::IsActive());
1045 EXPECT_EQ("100 0 1", IDString(browser2->tab_strip_model()));
1047 EXPECT_FALSE(GetIsDragged(browser2));
1049 // Remaining browser window should not be maximized
1050 EXPECT_FALSE(browser2->window()->IsMaximized());
1053 namespace {
1055 // Invoked from the nested message loop.
1056 void DragAllToSeparateWindowAndCancelStep2(
1057 DetachToBrowserTabDragControllerTest* test,
1058 TabStrip* attached_tab_strip,
1059 TabStrip* target_tab_strip,
1060 const BrowserList* browser_list) {
1061 ASSERT_TRUE(attached_tab_strip->IsDragSessionActive());
1062 ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
1063 ASSERT_TRUE(TabDragController::IsActive());
1064 ASSERT_EQ(2u, browser_list->size());
1066 // Drag to target_tab_strip. This should stop the nested loop from dragging
1067 // the window.
1068 gfx::Point target_point(target_tab_strip->width() - 1,
1069 target_tab_strip->height() / 2);
1070 views::View::ConvertPointToScreen(target_tab_strip, &target_point);
1071 ASSERT_TRUE(test->DragInputToAsync(target_point));
1074 } // namespace
1076 // Creates two browsers, selects all tabs in first, drags into second, then hits
1077 // escape.
1078 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1079 DragAllToSeparateWindowAndCancel) {
1080 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1082 // Add another tab to browser().
1083 AddTabAndResetBrowser(browser());
1085 // Create another browser.
1086 Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
1087 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1089 browser()->tab_strip_model()->AddTabAtToSelection(0);
1090 browser()->tab_strip_model()->AddTabAtToSelection(1);
1092 // Move to the first tab and drag it enough so that it detaches, but not
1093 // enough that it attaches to browser2.
1094 gfx::Point tab_0_center(
1095 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1096 ASSERT_TRUE(PressInput(tab_0_center));
1097 ASSERT_TRUE(DragInputToNotifyWhenDone(
1098 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1099 base::Bind(&DragAllToSeparateWindowAndCancelStep2, this,
1100 tab_strip, tab_strip2, native_browser_list)));
1101 QuitWhenNotDragging();
1103 // Should now be attached to tab_strip2.
1104 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1105 ASSERT_TRUE(TabDragController::IsActive());
1106 ASSERT_EQ(1u, native_browser_list->size());
1108 // Cancel the drag.
1109 ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
1110 browser2, ui::VKEY_ESCAPE, false, false, false, false));
1112 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1113 ASSERT_FALSE(TabDragController::IsActive());
1114 EXPECT_EQ("100 0 1", IDString(browser2->tab_strip_model()));
1116 // browser() will have been destroyed, but browser2 should remain.
1117 ASSERT_EQ(1u, native_browser_list->size());
1119 EXPECT_FALSE(GetIsDragged(browser2));
1121 // Remaining browser window should not be maximized
1122 EXPECT_FALSE(browser2->window()->IsMaximized());
1125 // Creates two browsers, drags from first into the second in such a way that
1126 // no detaching should happen.
1127 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1128 DragDirectlyToSecondWindow) {
1129 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1131 // Add another tab to browser().
1132 AddTabAndResetBrowser(browser());
1134 // Create another browser.
1135 Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
1136 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1138 // Move the tabstrip down enough so that we can detach.
1139 gfx::Rect bounds(browser2->window()->GetBounds());
1140 bounds.Offset(0, 100);
1141 browser2->window()->SetBounds(bounds);
1143 // Move to the first tab and drag it enough so that it detaches, but not
1144 // enough that it attaches to browser2.
1145 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1146 ASSERT_TRUE(PressInput(tab_0_center));
1148 gfx::Point b2_location(5, 0);
1149 views::View::ConvertPointToScreen(tab_strip2, &b2_location);
1150 ASSERT_TRUE(DragInputTo(b2_location));
1152 // Should now be attached to tab_strip2.
1153 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1154 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1155 ASSERT_TRUE(TabDragController::IsActive());
1157 // Release the mouse, stopping the drag session.
1158 ASSERT_TRUE(ReleaseInput());
1159 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1160 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1161 ASSERT_FALSE(TabDragController::IsActive());
1162 EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1163 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1165 EXPECT_FALSE(GetIsDragged(browser()));
1166 EXPECT_FALSE(GetIsDragged(browser2));
1168 // Both windows should not be maximized
1169 EXPECT_FALSE(browser()->window()->IsMaximized());
1170 EXPECT_FALSE(browser2->window()->IsMaximized());
1173 // Creates two browsers, the first browser has a single tab and drags into the
1174 // second browser.
1175 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1176 DragSingleTabToSeparateWindow) {
1177 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1179 ResetIDs(browser()->tab_strip_model(), 0);
1181 // Create another browser.
1182 Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
1183 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1184 const gfx::Rect initial_bounds(browser2->window()->GetBounds());
1186 // Move to the first tab and drag it enough so that it detaches, but not
1187 // enough that it attaches to browser2.
1188 gfx::Point tab_0_center(
1189 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1190 ASSERT_TRUE(PressInput(tab_0_center));
1191 ASSERT_TRUE(DragInputToNotifyWhenDone(
1192 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1193 base::Bind(&DragAllToSeparateWindowStep2, this, tab_strip, tab_strip2,
1194 native_browser_list)));
1195 QuitWhenNotDragging();
1197 // Should now be attached to tab_strip2.
1198 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1199 ASSERT_TRUE(TabDragController::IsActive());
1200 ASSERT_EQ(1u, native_browser_list->size());
1202 // Release the mouse, stopping the drag session.
1203 ASSERT_TRUE(ReleaseInput());
1204 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1205 ASSERT_FALSE(TabDragController::IsActive());
1206 EXPECT_EQ("100 0", IDString(browser2->tab_strip_model()));
1208 EXPECT_FALSE(GetIsDragged(browser2));
1210 // Remaining browser window should not be maximized
1211 EXPECT_FALSE(browser2->window()->IsMaximized());
1213 // Make sure that the window is still managed and not user moved.
1214 EXPECT_TRUE(IsWindowPositionManaged(browser2->window()->GetNativeWindow()));
1215 EXPECT_FALSE(HasUserChangedWindowPositionOrSize(
1216 browser2->window()->GetNativeWindow()));
1217 // Also make sure that the drag to window position has not changed.
1218 EXPECT_EQ(initial_bounds.ToString(),
1219 browser2->window()->GetBounds().ToString());
1222 namespace {
1224 // Invoked from the nested message loop.
1225 void CancelOnNewTabWhenDraggingStep2(
1226 DetachToBrowserTabDragControllerTest* test,
1227 const BrowserList* browser_list) {
1228 ASSERT_TRUE(TabDragController::IsActive());
1229 ASSERT_EQ(2u, browser_list->size());
1231 // Add another tab. This should trigger exiting the nested loop.
1232 test->AddBlankTabAndShow(browser_list->GetLastActive());
1235 } // namespace
1237 // Adds another tab, detaches into separate window, adds another tab and
1238 // verifies the run loop ends.
1239 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1240 CancelOnNewTabWhenDragging) {
1241 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1243 // Add another tab to browser().
1244 AddTabAndResetBrowser(browser());
1246 // Move to the first tab and drag it enough so that it detaches.
1247 gfx::Point tab_0_center(
1248 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1249 ASSERT_TRUE(PressInput(tab_0_center));
1250 ASSERT_TRUE(DragInputToNotifyWhenDone(
1251 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1252 base::Bind(&CancelOnNewTabWhenDraggingStep2, this, native_browser_list)));
1253 QuitWhenNotDragging();
1255 // Should be two windows and not dragging.
1256 ASSERT_FALSE(TabDragController::IsActive());
1257 ASSERT_EQ(2u, native_browser_list->size());
1258 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1259 EXPECT_FALSE(GetIsDragged(*it));
1260 // Should not be maximized
1261 EXPECT_FALSE(it->window()->IsMaximized());
1265 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
1267 namespace {
1269 void DragInMaximizedWindowStep2(DetachToBrowserTabDragControllerTest* test,
1270 Browser* browser,
1271 TabStrip* tab_strip,
1272 const BrowserList* browser_list) {
1273 // There should be another browser.
1274 ASSERT_EQ(2u, browser_list->size());
1275 Browser* new_browser = browser_list->get(1);
1276 EXPECT_NE(browser, new_browser);
1277 ASSERT_TRUE(new_browser->window()->IsActive());
1278 TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
1280 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1281 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1283 // Both windows should be visible.
1284 EXPECT_TRUE(tab_strip->GetWidget()->IsVisible());
1285 EXPECT_TRUE(tab_strip2->GetWidget()->IsVisible());
1287 // Stops dragging.
1288 ASSERT_TRUE(test->ReleaseInput());
1291 } // namespace
1293 // Creates a browser with two tabs, maximizes it, drags the tab out.
1294 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
1295 DragInMaximizedWindow) {
1296 AddTabAndResetBrowser(browser());
1297 browser()->window()->Maximize();
1299 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1301 // Move to the first tab and drag it enough so that it detaches.
1302 gfx::Point tab_0_center(
1303 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1304 ASSERT_TRUE(PressInput(tab_0_center));
1305 ASSERT_TRUE(DragInputToNotifyWhenDone(
1306 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1307 base::Bind(&DragInMaximizedWindowStep2, this, browser(), tab_strip,
1308 native_browser_list)));
1309 QuitWhenNotDragging();
1311 ASSERT_FALSE(TabDragController::IsActive());
1313 // Should be two browsers.
1314 ASSERT_EQ(2u, native_browser_list->size());
1315 Browser* new_browser = native_browser_list->get(1);
1316 ASSERT_TRUE(new_browser->window()->IsActive());
1318 EXPECT_TRUE(browser()->window()->GetNativeWindow()->IsVisible());
1319 EXPECT_TRUE(new_browser->window()->GetNativeWindow()->IsVisible());
1321 EXPECT_FALSE(GetIsDragged(browser()));
1322 EXPECT_FALSE(GetIsDragged(new_browser));
1324 // The source window should be maximized.
1325 EXPECT_TRUE(browser()->window()->IsMaximized());
1326 // The new window should be maximized.
1327 EXPECT_TRUE(new_browser->window()->IsMaximized());
1330 // Subclass of DetachToBrowserTabDragControllerTest that
1331 // creates multiple displays.
1332 class DetachToBrowserInSeparateDisplayTabDragControllerTest
1333 : public DetachToBrowserTabDragControllerTest {
1334 public:
1335 DetachToBrowserInSeparateDisplayTabDragControllerTest() {}
1336 virtual ~DetachToBrowserInSeparateDisplayTabDragControllerTest() {}
1338 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1339 DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
1340 // Make screens sufficiently wide to host 2 browsers side by side.
1341 command_line->AppendSwitchASCII("ash-host-window-bounds",
1342 "0+0-600x600,601+0-600x600");
1345 private:
1346 DISALLOW_COPY_AND_ASSIGN(
1347 DetachToBrowserInSeparateDisplayTabDragControllerTest);
1350 // Subclass of DetachToBrowserTabDragControllerTest that runs tests only with
1351 // touch input.
1352 class DetachToBrowserTabDragControllerTestTouch
1353 : public DetachToBrowserTabDragControllerTest {
1354 public:
1355 DetachToBrowserTabDragControllerTestTouch() {}
1356 virtual ~DetachToBrowserTabDragControllerTestTouch() {}
1358 private:
1359 DISALLOW_COPY_AND_ASSIGN(DetachToBrowserTabDragControllerTestTouch);
1362 namespace {
1364 void DragSingleTabToSeparateWindowInSecondDisplayStep3(
1365 DetachToBrowserTabDragControllerTest* test) {
1366 ASSERT_TRUE(test->ReleaseInput());
1369 void DragSingleTabToSeparateWindowInSecondDisplayStep2(
1370 DetachToBrowserTabDragControllerTest* test,
1371 const gfx::Point& target_point) {
1372 ASSERT_TRUE(test->DragInputToNotifyWhenDone(
1373 target_point.x(), target_point.y(),
1374 base::Bind(&DragSingleTabToSeparateWindowInSecondDisplayStep3, test)));
1377 } // namespace
1379 // Drags from browser to a second display and releases input.
1380 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1381 DragSingleTabToSeparateWindowInSecondDisplay) {
1382 // Add another tab.
1383 AddTabAndResetBrowser(browser());
1384 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1386 // Move to the first tab and drag it enough so that it detaches.
1387 // Then drag it to the final destination on the second screen.
1388 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1389 ASSERT_TRUE(PressInput(tab_0_center));
1390 ASSERT_TRUE(DragInputToNotifyWhenDone(
1391 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1392 base::Bind(&DragSingleTabToSeparateWindowInSecondDisplayStep2,
1393 this, gfx::Point(600 + tab_0_center.x(),
1394 tab_0_center.y()
1395 + GetDetachY(tab_strip)))));
1396 QuitWhenNotDragging();
1398 // Should no longer be dragging.
1399 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1400 ASSERT_FALSE(TabDragController::IsActive());
1402 // There should now be another browser.
1403 ASSERT_EQ(2u, native_browser_list->size());
1404 Browser* new_browser = native_browser_list->get(1);
1405 ASSERT_TRUE(new_browser->window()->IsActive());
1406 TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
1407 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1409 // This other browser should be on the second screen (with mouse drag)
1410 // With the touch input the browser cannot be dragged from one screen
1411 // to another and the window stays on the first screen.
1412 if (input_source() == INPUT_SOURCE_MOUSE) {
1413 aura::Window::Windows roots = ash::Shell::GetAllRootWindows();
1414 ASSERT_EQ(2u, roots.size());
1415 aura::Window* second_root = roots[1];
1416 EXPECT_EQ(second_root,
1417 new_browser->window()->GetNativeWindow()->GetRootWindow());
1420 EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
1421 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1423 // Both windows should not be maximized
1424 EXPECT_FALSE(browser()->window()->IsMaximized());
1425 EXPECT_FALSE(new_browser->window()->IsMaximized());
1428 namespace {
1430 // Invoked from the nested message loop.
1431 void DragTabToWindowInSeparateDisplayStep2(
1432 DetachToBrowserTabDragControllerTest* test,
1433 TabStrip* not_attached_tab_strip,
1434 TabStrip* target_tab_strip) {
1435 ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
1436 ASSERT_FALSE(target_tab_strip->IsDragSessionActive());
1437 ASSERT_TRUE(TabDragController::IsActive());
1439 // Drag to target_tab_strip. This should stop the nested loop from dragging
1440 // the window.
1441 gfx::Point target_point(
1442 GetCenterInScreenCoordinates(target_tab_strip->tab_at(0)));
1444 // Move it close to the beginning of the target tabstrip.
1445 target_point.set_x(
1446 target_point.x() - target_tab_strip->tab_at(0)->width() / 2 + 10);
1447 ASSERT_TRUE(test->DragInputToAsync(target_point));
1450 } // namespace
1452 // Drags from browser to another browser on a second display and releases input.
1453 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1454 DragTabToWindowInSeparateDisplay) {
1455 // Add another tab.
1456 AddTabAndResetBrowser(browser());
1457 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1459 // Create another browser.
1460 Browser* browser2 = CreateBrowser(browser()->profile());
1461 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1462 ResetIDs(browser2->tab_strip_model(), 100);
1464 // Move the second browser to the second display.
1465 aura::Window::Windows roots = ash::Shell::GetAllRootWindows();
1466 ASSERT_EQ(2u, roots.size());
1467 aura::Window* second_root = roots[1];
1468 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1469 second_root).work_area();
1470 browser2->window()->SetBounds(work_area);
1471 EXPECT_EQ(second_root,
1472 browser2->window()->GetNativeWindow()->GetRootWindow());
1474 // Move to the first tab and drag it enough so that it detaches, but not
1475 // enough that it attaches to browser2.
1476 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1477 ASSERT_TRUE(PressInput(tab_0_center));
1478 ASSERT_TRUE(DragInputToNotifyWhenDone(
1479 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1480 base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1481 this, tab_strip, tab_strip2)));
1482 QuitWhenNotDragging();
1484 // Should now be attached to tab_strip2.
1485 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1486 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1487 ASSERT_TRUE(TabDragController::IsActive());
1489 // Release the mouse, stopping the drag session.
1490 ASSERT_TRUE(ReleaseInput());
1491 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1492 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1493 ASSERT_FALSE(TabDragController::IsActive());
1494 EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1495 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1497 // Both windows should not be maximized
1498 EXPECT_FALSE(browser()->window()->IsMaximized());
1499 EXPECT_FALSE(browser2->window()->IsMaximized());
1502 // Drags from browser to another browser on a second display and releases input.
1503 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1504 DragTabToWindowOnSecondDisplay) {
1505 // Add another tab.
1506 AddTabAndResetBrowser(browser());
1507 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1509 // Create another browser.
1510 Browser* browser2 = CreateBrowser(browser()->profile());
1511 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1512 ResetIDs(browser2->tab_strip_model(), 100);
1514 // Move both browsers to the second display.
1515 aura::Window::Windows roots = ash::Shell::GetAllRootWindows();
1516 ASSERT_EQ(2u, roots.size());
1517 aura::Window* second_root = roots[1];
1518 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1519 second_root).work_area();
1520 browser()->window()->SetBounds(work_area);
1522 // position both browser windows side by side on the second screen.
1523 gfx::Rect work_area2(work_area);
1524 work_area.set_width(work_area.width()/2);
1525 browser()->window()->SetBounds(work_area);
1526 work_area2.set_x(work_area2.x() + work_area2.width()/2);
1527 work_area2.set_width(work_area2.width()/2);
1528 browser2->window()->SetBounds(work_area2);
1529 EXPECT_EQ(second_root,
1530 browser()->window()->GetNativeWindow()->GetRootWindow());
1531 EXPECT_EQ(second_root,
1532 browser2->window()->GetNativeWindow()->GetRootWindow());
1534 // Move to the first tab and drag it enough so that it detaches, but not
1535 // enough that it attaches to browser2.
1536 // SetEventGeneratorRootWindow sets correct (second) RootWindow
1537 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1538 SetEventGeneratorRootWindow(tab_0_center);
1539 ASSERT_TRUE(PressInput(tab_0_center));
1540 ASSERT_TRUE(DragInputToNotifyWhenDone(
1541 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1542 base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1543 this, tab_strip, tab_strip2)));
1544 QuitWhenNotDragging();
1546 // Should now be attached to tab_strip2.
1547 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1548 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1549 ASSERT_TRUE(TabDragController::IsActive());
1551 // Release the mouse, stopping the drag session.
1552 ASSERT_TRUE(ReleaseInput());
1553 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1554 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1555 ASSERT_FALSE(TabDragController::IsActive());
1556 EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1557 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1559 // Both windows should not be maximized
1560 EXPECT_FALSE(browser()->window()->IsMaximized());
1561 EXPECT_FALSE(browser2->window()->IsMaximized());
1564 // Drags from a maximized browser to another non-maximized browser on a second
1565 // display and releases input.
1566 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1567 DragMaxTabToNonMaxWindowInSeparateDisplay) {
1568 // Add another tab.
1569 AddTabAndResetBrowser(browser());
1570 browser()->window()->Maximize();
1571 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1573 // Create another browser on the second display.
1574 aura::Window::Windows roots = ash::Shell::GetAllRootWindows();
1575 ASSERT_EQ(2u, roots.size());
1576 aura::Window* first_root = roots[0];
1577 aura::Window* second_root = roots[1];
1578 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1579 second_root).work_area();
1580 work_area.Inset(20,20,20,60);
1581 Browser::CreateParams params(browser()->profile(),
1582 browser()->host_desktop_type());
1583 params.initial_show_state = ui::SHOW_STATE_NORMAL;
1584 params.initial_bounds = work_area;
1585 Browser* browser2 = new Browser(params);
1586 AddBlankTabAndShow(browser2);
1588 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1589 ResetIDs(browser2->tab_strip_model(), 100);
1591 EXPECT_EQ(second_root,
1592 browser2->window()->GetNativeWindow()->GetRootWindow());
1593 EXPECT_EQ(first_root,
1594 browser()->window()->GetNativeWindow()->GetRootWindow());
1595 EXPECT_EQ(2, tab_strip->tab_count());
1596 EXPECT_EQ(1, tab_strip2->tab_count());
1598 // Move to the first tab and drag it enough so that it detaches, but not
1599 // enough that it attaches to browser2.
1600 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1601 ASSERT_TRUE(PressInput(tab_0_center));
1602 ASSERT_TRUE(DragInputToNotifyWhenDone(
1603 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1604 base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1605 this, tab_strip, tab_strip2)));
1606 QuitWhenNotDragging();
1608 // Should now be attached to tab_strip2.
1609 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1610 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1611 ASSERT_TRUE(TabDragController::IsActive());
1613 // Release the mouse, stopping the drag session.
1614 ASSERT_TRUE(ReleaseInput());
1616 // tab should have moved
1617 EXPECT_EQ(1, tab_strip->tab_count());
1618 EXPECT_EQ(2, tab_strip2->tab_count());
1620 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1621 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1622 ASSERT_FALSE(TabDragController::IsActive());
1623 EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1624 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1626 // Source browser should still be maximized, target should not
1627 EXPECT_TRUE(browser()->window()->IsMaximized());
1628 EXPECT_FALSE(browser2->window()->IsMaximized());
1631 // Immersive fullscreen is ChromeOS only.
1632 #if defined(OS_CHROMEOS)
1633 // Drags from a restored browser to an immersive fullscreen browser on a
1634 // second display and releases input.
1635 IN_PROC_BROWSER_TEST_P(DetachToBrowserInSeparateDisplayTabDragControllerTest,
1636 DragTabToImmersiveBrowserOnSeparateDisplay) {
1637 // Add another tab.
1638 AddTabAndResetBrowser(browser());
1639 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1641 // Create another browser.
1642 Browser* browser2 = CreateBrowser(browser()->profile());
1643 TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
1644 ResetIDs(browser2->tab_strip_model(), 100);
1646 // Move the second browser to the second display.
1647 aura::Window::Windows roots = ash::Shell::GetAllRootWindows();
1648 ASSERT_EQ(2u, roots.size());
1649 aura::Window* second_root = roots[1];
1650 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1651 second_root).work_area();
1652 browser2->window()->SetBounds(work_area);
1653 EXPECT_EQ(second_root,
1654 browser2->window()->GetNativeWindow()->GetRootWindow());
1656 // Put the second browser into immersive fullscreen.
1657 BrowserView* browser_view2 = BrowserView::GetBrowserViewForBrowser(browser2);
1658 ImmersiveModeController* immersive_controller2 =
1659 browser_view2->immersive_mode_controller();
1660 immersive_controller2->SetupForTest();
1661 chrome::ToggleFullscreenMode(browser2);
1662 ASSERT_TRUE(immersive_controller2->IsEnabled());
1663 ASSERT_FALSE(immersive_controller2->IsRevealed());
1664 ASSERT_TRUE(tab_strip2->IsImmersiveStyle());
1666 // Move to the first tab and drag it enough so that it detaches, but not
1667 // enough that it attaches to browser2.
1668 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1669 ASSERT_TRUE(PressInput(tab_0_center));
1670 ASSERT_TRUE(DragInputToNotifyWhenDone(
1671 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1672 base::Bind(&DragTabToWindowInSeparateDisplayStep2,
1673 this, tab_strip, tab_strip2)));
1674 QuitWhenNotDragging();
1676 // Should now be attached to tab_strip2.
1677 ASSERT_TRUE(tab_strip2->IsDragSessionActive());
1678 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1679 ASSERT_TRUE(TabDragController::IsActive());
1681 // browser2's top chrome should be revealed and the tab strip should be
1682 // at normal height while user is tragging tabs_strip2's tabs.
1683 ASSERT_TRUE(immersive_controller2->IsRevealed());
1684 ASSERT_FALSE(tab_strip2->IsImmersiveStyle());
1686 // Release the mouse, stopping the drag session.
1687 ASSERT_TRUE(ReleaseInput());
1688 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
1689 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1690 ASSERT_FALSE(TabDragController::IsActive());
1691 EXPECT_EQ("0 100", IDString(browser2->tab_strip_model()));
1692 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
1694 // Move the mouse off of browser2's top chrome.
1695 aura::Window* primary_root = roots[0];
1696 ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
1697 primary_root->GetBoundsInScreen().CenterPoint()));
1699 // The first browser window should not be in immersive fullscreen.
1700 // browser2 should still be in immersive fullscreen, but the top chrome should
1701 // no longer be revealed.
1702 BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
1703 EXPECT_FALSE(browser_view->immersive_mode_controller()->IsEnabled());
1705 EXPECT_TRUE(immersive_controller2->IsEnabled());
1706 EXPECT_FALSE(immersive_controller2->IsRevealed());
1707 EXPECT_TRUE(tab_strip2->IsImmersiveStyle());
1709 #endif // OS_CHROMEOS
1711 // Subclass of DetachToBrowserTabDragControllerTest that
1712 // creates multiple displays with different device scale factors.
1713 class DifferentDeviceScaleFactorDisplayTabDragControllerTest
1714 : public DetachToBrowserTabDragControllerTest {
1715 public:
1716 DifferentDeviceScaleFactorDisplayTabDragControllerTest() {}
1717 virtual ~DifferentDeviceScaleFactorDisplayTabDragControllerTest() {}
1719 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1720 DetachToBrowserTabDragControllerTest::SetUpCommandLine(command_line);
1721 command_line->AppendSwitchASCII("ash-host-window-bounds",
1722 "400x400,0+400-800x800*2");
1725 float GetCursorDeviceScaleFactor() const {
1726 ash::test::CursorManagerTestApi cursor_test_api(
1727 ash::Shell::GetInstance()->cursor_manager());
1728 return cursor_test_api.GetDisplay().device_scale_factor();
1731 private:
1732 DISALLOW_COPY_AND_ASSIGN(
1733 DifferentDeviceScaleFactorDisplayTabDragControllerTest);
1736 namespace {
1738 // The points where a tab is dragged in CursorDeviceScaleFactorStep.
1739 const struct DragPoint {
1740 int x;
1741 int y;
1742 } kDragPoints[] = {
1743 {300, 200},
1744 {399, 200},
1745 {500, 200},
1746 {400, 200},
1747 {300, 200},
1750 // The expected device scale factors before the cursor is moved to the
1751 // corresponding kDragPoints in CursorDeviceScaleFactorStep.
1752 const float kDeviceScaleFactorExpectations[] = {
1753 1.0f,
1754 1.0f,
1755 2.0f,
1756 2.0f,
1757 1.0f,
1760 COMPILE_ASSERT(
1761 arraysize(kDragPoints) == arraysize(kDeviceScaleFactorExpectations),
1762 kDragPoints_and_kDeviceScaleFactorExpectations_must_have_same_size);
1764 // Drags tab to |kDragPoints[index]|, then calls the next step function.
1765 void CursorDeviceScaleFactorStep(
1766 DifferentDeviceScaleFactorDisplayTabDragControllerTest* test,
1767 TabStrip* not_attached_tab_strip,
1768 size_t index) {
1769 ASSERT_FALSE(not_attached_tab_strip->IsDragSessionActive());
1770 ASSERT_TRUE(TabDragController::IsActive());
1772 if (index < arraysize(kDragPoints)) {
1773 EXPECT_EQ(kDeviceScaleFactorExpectations[index],
1774 test->GetCursorDeviceScaleFactor());
1775 const DragPoint p = kDragPoints[index];
1776 ASSERT_TRUE(test->DragInputToNotifyWhenDone(
1777 p.x, p.y, base::Bind(&CursorDeviceScaleFactorStep,
1778 test, not_attached_tab_strip, index + 1)));
1779 } else {
1780 // Finishes a serise of CursorDeviceScaleFactorStep calls and ends drag.
1781 EXPECT_EQ(1.0f, test->GetCursorDeviceScaleFactor());
1782 ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
1783 ui_controls::LEFT, ui_controls::UP));
1787 } // namespace
1789 // Verifies cursor's device scale factor is updated when a tab is moved across
1790 // displays with different device scale factors (http://crbug.com/154183).
1791 IN_PROC_BROWSER_TEST_P(DifferentDeviceScaleFactorDisplayTabDragControllerTest,
1792 CursorDeviceScaleFactor) {
1793 // Add another tab.
1794 AddTabAndResetBrowser(browser());
1795 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1797 // Move the second browser to the second display.
1798 aura::Window::Windows roots = ash::Shell::GetAllRootWindows();
1799 ASSERT_EQ(2u, roots.size());
1801 // Move to the first tab and drag it enough so that it detaches.
1802 gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1803 ASSERT_TRUE(PressInput(tab_0_center));
1804 ASSERT_TRUE(DragInputToNotifyWhenDone(
1805 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
1806 base::Bind(&CursorDeviceScaleFactorStep,
1807 this, tab_strip, 0)));
1808 QuitWhenNotDragging();
1811 namespace {
1813 class DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest
1814 : public TabDragControllerTest {
1815 public:
1816 DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest() {}
1818 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1819 TabDragControllerTest::SetUpCommandLine(command_line);
1820 command_line->AppendSwitchASCII("ash-host-window-bounds",
1821 "0+0-250x250,251+0-250x250");
1824 bool Press(const gfx::Point& position) {
1825 return ui_test_utils::SendMouseMoveSync(position) &&
1826 ui_test_utils::SendMouseEventsSync(ui_controls::LEFT,
1827 ui_controls::DOWN);
1830 bool DragTabAndExecuteTaskWhenDone(const gfx::Point& position,
1831 const base::Closure& task) {
1832 return ui_controls::SendMouseMoveNotifyWhenDone(
1833 position.x(), position.y(), task);
1836 void QuitWhenNotDragging() {
1837 test::QuitWhenNotDraggingImpl();
1838 base::MessageLoop::current()->Run();
1841 private:
1842 DISALLOW_COPY_AND_ASSIGN(
1843 DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest);
1846 // Invoked from the nested message loop.
1847 void CancelDragTabToWindowInSeparateDisplayStep3(
1848 TabStrip* tab_strip,
1849 const BrowserList* browser_list) {
1850 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1851 ASSERT_TRUE(TabDragController::IsActive());
1852 ASSERT_EQ(2u, browser_list->size());
1854 // Switching display mode should cancel the drag operation.
1855 ash::internal::DisplayManager* display_manager =
1856 ash::Shell::GetInstance()->display_manager();
1857 display_manager->AddRemoveDisplay();
1860 // Invoked from the nested message loop.
1861 void CancelDragTabToWindowInSeparateDisplayStep2(
1862 DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest* test,
1863 TabStrip* tab_strip,
1864 aura::Window* current_root,
1865 gfx::Point final_destination,
1866 const BrowserList* browser_list) {
1867 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1868 ASSERT_TRUE(TabDragController::IsActive());
1869 ASSERT_EQ(2u, browser_list->size());
1871 Browser* new_browser = browser_list->get(1);
1872 EXPECT_EQ(current_root,
1873 new_browser->window()->GetNativeWindow()->GetRootWindow());
1875 ASSERT_TRUE(test->DragTabAndExecuteTaskWhenDone(
1876 final_destination,
1877 base::Bind(&CancelDragTabToWindowInSeparateDisplayStep3,
1878 tab_strip, browser_list)));
1881 } // namespace
1883 // Drags from browser to a second display and releases input.
1884 IN_PROC_BROWSER_TEST_F(
1885 DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
1886 CancelDragTabToWindowIn2ndDisplay) {
1887 // Add another tab.
1888 AddTabAndResetBrowser(browser());
1889 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1891 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1893 // Move the second browser to the second display.
1894 aura::Window::Windows roots = ash::Shell::GetAllRootWindows();
1895 ASSERT_EQ(2u, roots.size());
1896 gfx::Point final_destination =
1897 gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1898 roots[1]).work_area().CenterPoint();
1900 // Move to the first tab and drag it enough so that it detaches, but not
1901 // enough to move to another display.
1902 gfx::Point tab_0_dst(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1903 ASSERT_TRUE(Press(tab_0_dst));
1904 tab_0_dst.Offset(0, GetDetachY(tab_strip));
1905 ASSERT_TRUE(DragTabAndExecuteTaskWhenDone(
1906 tab_0_dst, base::Bind(&CancelDragTabToWindowInSeparateDisplayStep2,
1907 this, tab_strip, roots[0], final_destination,
1908 native_browser_list)));
1909 QuitWhenNotDragging();
1911 ASSERT_EQ(1u, native_browser_list->size());
1912 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1913 ASSERT_FALSE(TabDragController::IsActive());
1914 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1916 // Release the mouse
1917 ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
1918 ui_controls::LEFT, ui_controls::UP));
1921 // Drags from browser from a second display to primary and releases input.
1922 IN_PROC_BROWSER_TEST_F(
1923 DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest,
1924 CancelDragTabToWindowIn1stDisplay) {
1925 aura::Window::Windows roots = ash::Shell::GetAllRootWindows();
1926 ASSERT_EQ(2u, roots.size());
1928 // Add another tab.
1929 AddTabAndResetBrowser(browser());
1930 TabStrip* tab_strip = GetTabStripForBrowser(browser());
1932 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1933 EXPECT_EQ(roots[0], browser()->window()->GetNativeWindow()->GetRootWindow());
1935 gfx::Rect work_area = gfx::Screen::GetNativeScreen()->
1936 GetDisplayNearestWindow(roots[1]).work_area();
1937 browser()->window()->SetBounds(work_area);
1938 EXPECT_EQ(roots[1], browser()->window()->GetNativeWindow()->GetRootWindow());
1940 // Move the second browser to the display.
1941 gfx::Point final_destination =
1942 gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(
1943 roots[0]).work_area().CenterPoint();
1945 // Move to the first tab and drag it enough so that it detaches, but not
1946 // enough to move to another display.
1947 gfx::Point tab_0_dst(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
1948 ASSERT_TRUE(Press(tab_0_dst));
1949 tab_0_dst.Offset(0, GetDetachY(tab_strip));
1950 ASSERT_TRUE(DragTabAndExecuteTaskWhenDone(
1951 tab_0_dst, base::Bind(&CancelDragTabToWindowInSeparateDisplayStep2,
1952 this, tab_strip, roots[1], final_destination,
1953 native_browser_list)));
1954 QuitWhenNotDragging();
1956 ASSERT_EQ(1u, native_browser_list->size());
1957 ASSERT_FALSE(tab_strip->IsDragSessionActive());
1958 ASSERT_FALSE(TabDragController::IsActive());
1959 EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
1961 // Release the mouse
1962 ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
1963 ui_controls::LEFT, ui_controls::UP));
1966 namespace {
1968 void DetachToOwnWindowTwoFingersDragStep5(
1969 DetachToBrowserTabDragControllerTest* test) {
1970 ASSERT_EQ(2u, test->native_browser_list->size());
1971 Browser* new_browser = test->native_browser_list->get(1);
1972 ASSERT_TRUE(new_browser->window()->IsActive());
1974 ASSERT_TRUE(test->ReleaseInput());
1975 ASSERT_TRUE(test->ReleaseInput2());
1976 ASSERT_TRUE(new_browser->window()->IsActive());
1979 void DetachToOwnWindowTwoFingersDragStep4(
1980 DetachToBrowserTabDragControllerTest* test,
1981 const gfx::Point& target_point) {
1982 ASSERT_EQ(2u, test->native_browser_list->size());
1983 Browser* new_browser = test->native_browser_list->get(1);
1984 ASSERT_TRUE(new_browser->window()->IsActive());
1986 ASSERT_TRUE(test->DragInput2ToNotifyWhenDone(
1987 target_point.x(), target_point.y(),
1988 base::Bind(&DetachToOwnWindowTwoFingersDragStep5, test)));
1991 void DetachToOwnWindowTwoFingersDragStep3(
1992 DetachToBrowserTabDragControllerTest* test,
1993 const gfx::Point& target_point) {
1994 ASSERT_TRUE(test->PressInput2());
1996 ASSERT_EQ(2u, test->native_browser_list->size());
1997 Browser* new_browser = test->native_browser_list->get(1);
1998 ASSERT_TRUE(new_browser->window()->IsActive());
2000 ASSERT_TRUE(test->DragInputToDelayedNotifyWhenDone(
2001 target_point.x(), target_point.y(),
2002 base::Bind(&DetachToOwnWindowTwoFingersDragStep4,
2003 test,
2004 target_point),
2005 base::TimeDelta::FromMilliseconds(60)));
2008 void DetachToOwnWindowTwoFingersDragStep2(
2009 DetachToBrowserTabDragControllerTest* test,
2010 const gfx::Point& target_point) {
2011 ASSERT_EQ(2u, test->native_browser_list->size());
2012 Browser* new_browser = test->native_browser_list->get(1);
2013 ASSERT_TRUE(new_browser->window()->IsActive());
2015 ASSERT_TRUE(test->DragInputToDelayedNotifyWhenDone(
2016 target_point.x(), target_point.y(),
2017 base::Bind(&DetachToOwnWindowTwoFingersDragStep3,
2018 test,
2019 target_point + gfx::Vector2d(-2, 1)),
2020 base::TimeDelta::FromMilliseconds(60)));
2023 } // namespace
2025 // Drags from browser to separate window starting with one finger and
2026 // then continuing with two fingers.
2027 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTestTouch,
2028 DetachToOwnWindowTwoFingers) {
2029 gfx::Rect bounds(browser()->window()->GetBounds());
2030 // Add another tab.
2031 AddTabAndResetBrowser(browser());
2032 TabStrip* tab_strip = GetTabStripForBrowser(browser());
2034 // Move to the first tab and drag it enough so that it detaches.
2035 gfx::Point tab_0_center(
2036 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
2037 ASSERT_TRUE(PressInput(tab_0_center));
2038 // Drags in this test are very short to avoid fling.
2039 ASSERT_TRUE(DragInputToDelayedNotifyWhenDone(
2040 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
2041 base::Bind(&DetachToOwnWindowTwoFingersDragStep2,
2042 this, gfx::Point(5 + tab_0_center.x(),
2043 1 + tab_0_center.y()
2044 + GetDetachY(tab_strip))),
2045 base::TimeDelta::FromMilliseconds(60)));
2046 // Continue dragging, first with one finger, then with two fingers.
2047 QuitWhenNotDragging();
2049 // There should now be another browser.
2050 ASSERT_EQ(2u, native_browser_list->size());
2051 Browser* new_browser = native_browser_list->get(1);
2052 ASSERT_TRUE(new_browser->window()->IsActive());
2053 // The sequence of drags should successfully move the browser window.
2054 bounds += gfx::Vector2d(5 - 2, 1 + 1 + GetDetachY(tab_strip));
2055 EXPECT_EQ(bounds.ToString(),
2056 new_browser->window()->GetNativeWindow()->bounds().ToString());
2059 // Subclass of DetachToBrowserTabDragControllerTest that runs tests with
2060 // docked windows enabled and disabled.
2061 class DetachToDockedTabDragControllerTest
2062 : public DetachToBrowserTabDragControllerTest {
2063 public:
2064 DetachToDockedTabDragControllerTest() {}
2065 virtual ~DetachToDockedTabDragControllerTest() {}
2067 private:
2068 DISALLOW_COPY_AND_ASSIGN(DetachToDockedTabDragControllerTest);
2071 namespace {
2073 void DetachToDockedWindowNextStep(
2074 DetachToDockedTabDragControllerTest* test,
2075 const gfx::Point& target_point,
2076 int iteration) {
2077 ASSERT_EQ(2u, test->native_browser_list->size());
2078 Browser* new_browser = test->native_browser_list->get(1);
2079 ASSERT_TRUE(new_browser->window()->IsActive());
2081 if (!iteration) {
2082 ASSERT_TRUE(test->ReleaseInput());
2083 return;
2085 ASSERT_TRUE(test->DragInputToNotifyWhenDone(
2086 target_point.x(), target_point.y(),
2087 base::Bind(&DetachToDockedWindowNextStep,
2088 test,
2089 gfx::Point(target_point.x(), 1 + target_point.y()),
2090 iteration - 1)));
2093 } // namespace
2095 // Drags from browser to separate window, docks that window and releases mouse.
2096 IN_PROC_BROWSER_TEST_P(DetachToDockedTabDragControllerTest,
2097 DetachToDockedWindowFromMaximizedWindow) {
2098 // Maximize the initial browser window.
2099 browser()->window()->Maximize();
2100 ASSERT_TRUE(browser()->window()->IsMaximized());
2102 // Add another tab.
2103 AddTabAndResetBrowser(browser());
2104 TabStrip* tab_strip = GetTabStripForBrowser(browser());
2106 // Move to the first tab and drag it enough so that it detaches.
2107 gfx::Point tab_0_center(
2108 GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
2109 ASSERT_TRUE(PressInput(tab_0_center));
2111 // The following matches kMovesBeforeAdjust in snap_sizer.cc
2112 const int kNumIterations = 25 * 5 + 10;
2113 ASSERT_TRUE(DragInputToNotifyWhenDone(
2114 tab_0_center.x(), tab_0_center.y() + GetDetachY(tab_strip),
2115 base::Bind(&DetachToDockedWindowNextStep, this,
2116 gfx::Point(0, tab_0_center.y() + GetDetachY(tab_strip)),
2117 kNumIterations)));
2118 // Continue dragging enough times to go through snapping sequence and dock
2119 // the window.
2120 QuitWhenNotDragging();
2121 // Should no longer be dragging.
2122 ASSERT_FALSE(tab_strip->IsDragSessionActive());
2123 ASSERT_FALSE(TabDragController::IsActive());
2125 // There should now be another browser.
2126 ASSERT_EQ(2u, native_browser_list->size());
2127 Browser* new_browser = native_browser_list->get(1);
2128 ASSERT_TRUE(new_browser->window()->IsActive());
2129 TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
2130 ASSERT_FALSE(tab_strip2->IsDragSessionActive());
2132 EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
2133 EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
2135 // The bounds of the initial window should not have changed.
2136 EXPECT_TRUE(browser()->window()->IsMaximized());
2138 EXPECT_FALSE(GetIsDragged(browser()));
2139 EXPECT_FALSE(GetIsDragged(new_browser));
2140 // After this both windows should still be manageable.
2141 EXPECT_TRUE(IsWindowPositionManaged(browser()->window()->GetNativeWindow()));
2142 EXPECT_TRUE(IsWindowPositionManaged(
2143 new_browser->window()->GetNativeWindow()));
2145 ash::wm::WindowState* window_state =
2146 ash::wm::GetWindowState(new_browser->window()->GetNativeWindow());
2147 // The new window should not be maximized because it gets docked or snapped.
2148 EXPECT_FALSE(new_browser->window()->IsMaximized());
2149 if (docked_windows_enabled()) {
2150 // The new window should be docked and not snapped if docking is allowed.
2151 EXPECT_TRUE(window_state->IsDocked());
2152 EXPECT_FALSE(window_state->IsSnapped());
2153 } else {
2154 // The new window should be snapped and not docked if docking is disabled.
2155 EXPECT_FALSE(window_state->IsDocked());
2156 EXPECT_TRUE(window_state->IsSnapped());
2161 #endif
2163 #if defined(USE_ASH) && !defined(OS_WIN) // TODO(win_ash)
2164 INSTANTIATE_TEST_CASE_P(TabDragging,
2165 DetachToBrowserInSeparateDisplayTabDragControllerTest,
2166 ::testing::Values("mouse", "touch"));
2167 INSTANTIATE_TEST_CASE_P(TabDragging,
2168 DifferentDeviceScaleFactorDisplayTabDragControllerTest,
2169 ::testing::Values("mouse"));
2170 INSTANTIATE_TEST_CASE_P(TabDragging,
2171 DetachToBrowserTabDragControllerTest,
2172 ::testing::Values("mouse", "touch"));
2173 INSTANTIATE_TEST_CASE_P(TabDragging,
2174 DetachToDockedTabDragControllerTest,
2175 ::testing::Values("mouse", "mouse docked"));
2176 INSTANTIATE_TEST_CASE_P(TabDragging,
2177 DetachToBrowserTabDragControllerTestTouch,
2178 ::testing::Values("touch", "touch docked"));
2179 #else
2180 INSTANTIATE_TEST_CASE_P(TabDragging,
2181 DetachToBrowserTabDragControllerTest,
2182 ::testing::Values("mouse"));
2183 #endif