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