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