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 "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/command_line.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/win/windows_version.h"
     12 #include "chrome/browser/chrome_notification_types.h"
     13 #include "chrome/browser/ui/browser.h"
     14 #include "chrome/browser/ui/browser_list.h"
     15 #include "chrome/browser/ui/browser_window.h"
     16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     17 #include "chrome/browser/ui/views/frame/browser_view.h"
     18 #include "chrome/browser/ui/views/tabs/tab.h"
     19 #include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
     20 #include "chrome/browser/ui/views/tabs/tab_strip.h"
     21 #include "chrome/common/chrome_switches.h"
     22 #include "chrome/test/base/in_process_browser_test.h"
     23 #include "chrome/test/base/interactive_test_utils.h"
     24 #include "chrome/test/base/ui_test_utils.h"
     25 #include "content/public/browser/notification_details.h"
     26 #include "content/public/browser/notification_observer.h"
     27 #include "content/public/browser/notification_service.h"
     28 #include "content/public/browser/notification_source.h"
     29 #include "content/public/browser/web_contents.h"
     30 #include "ui/base/test/ui_controls.h"
     31 #include "ui/gfx/screen.h"
     32 #include "ui/views/controls/textfield/textfield.h"
     33 #include "ui/views/view.h"
     34 #include "ui/views/widget/widget.h"
     35 
     36 using content::WebContents;
     37 using test::GetCenterInScreenCoordinates;
     38 using test::GetTabStripForBrowser;
     39 using test::IDString;
     40 using test::ResetIDs;
     41 using test::SetID;
     42 
     43 // The tests in this file exercise detaching the dragged tab into a standalone
     44 // window (not a Browser). They are not applicable to aura as aura forces real
     45 // window dragging.
     46 
     47 // Creates a browser with two tabs, drags the second to the first.
     48 IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DragInSameWindow) {
     49   AddTabAndResetBrowser(browser());
     50 
     51   TabStrip* tab_strip = GetTabStripForBrowser(browser());
     52   TabStripModel* model = browser()->tab_strip_model();
     53 
     54   gfx::Point tab_1_center(GetCenterInScreenCoordinates(tab_strip->tab_at(1)));
     55   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_1_center));
     56   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
     57                   ui_controls::LEFT, ui_controls::DOWN));
     58   gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
     59   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
     60   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
     61                   ui_controls::LEFT, ui_controls::UP));
     62   EXPECT_EQ("1 0", IDString(model));
     63   EXPECT_FALSE(TabDragController::IsActive());
     64   EXPECT_FALSE(tab_strip->IsDragSessionActive());
     65 }
     66 
     67 // Creates two browsers, drags from first into second.
     68 // This test often crashes on Vista <http://crbug.com/156787>
     69 #if defined(OS_WIN)
     70 #define MAYBE_DragToSeparateWindow DISABLED_DragToSeparateWindow
     71 #else
     72 #define MAYBE_DragToSeparateWindow DragToSeparateWindow
     73 #endif
     74 IN_PROC_BROWSER_TEST_F(TabDragControllerTest, MAYBE_DragToSeparateWindow) {
     75   TabStrip* tab_strip = GetTabStripForBrowser(browser());
     76 
     77   // Add another tab to browser().
     78   AddTabAndResetBrowser(browser());
     79 
     80   // Create another browser.
     81   Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
     82   TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
     83 
     84   // Move to the first tab and drag it enough so that it detaches, but not
     85   // enough that it attaches to browser2.
     86   gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
     87   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
     88   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
     89                   ui_controls::LEFT, ui_controls::DOWN));
     90   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
     91                   gfx::Point(tab_0_center.x(),
     92                              tab_0_center.y() + tab_strip->height() + 20)));
     93   ASSERT_TRUE(TabDragController::IsActive());
     94 
     95   // Drag into the second browser.
     96   gfx::Point target_point(tab_strip2->width() -1, tab_strip2->height() / 2);
     97   views::View::ConvertPointToScreen(tab_strip2, &target_point);
     98   ASSERT_TRUE(ui_controls::SendMouseMove(target_point.x(), target_point.y()));
     99 
    100   ASSERT_TRUE(TabDragController::IsActive());
    101 
    102   // Release the mouse, ending the drag session.
    103   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    104                   ui_controls::LEFT, ui_controls::UP));
    105   ASSERT_FALSE(tab_strip2->IsDragSessionActive());
    106   ASSERT_FALSE(tab_strip->IsDragSessionActive());
    107   ASSERT_FALSE(TabDragController::IsActive());
    108   EXPECT_EQ("100 0", IDString(browser2->tab_strip_model()));
    109   EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
    110 }
    111 
    112 // Drags from browser to separate window and releases mouse.
    113 IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DetachToOwnWindow) {
    114   // Add another tab.
    115   AddTabAndResetBrowser(browser());
    116   TabStrip* tab_strip = GetTabStripForBrowser(browser());
    117 
    118   // Move to the first tab and drag it enough so that it detaches.
    119   gfx::Point tab_0_center(
    120       GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
    121   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
    122   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    123                   ui_controls::LEFT, ui_controls::DOWN));
    124   ASSERT_TRUE(ui_controls::SendMouseMove(
    125       tab_0_center.x(), tab_0_center.y() + tab_strip->height() + 20));
    126   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    127                   ui_controls::LEFT, ui_controls::UP));
    128 
    129   // Should no longer be dragging.
    130   ASSERT_FALSE(tab_strip->IsDragSessionActive());
    131   ASSERT_FALSE(TabDragController::IsActive());
    132 
    133   // There should now be another browser.
    134   ASSERT_EQ(2u, native_browser_list->size());
    135   Browser* new_browser = native_browser_list->get(1);
    136   ASSERT_TRUE(new_browser->window()->IsActive());
    137   TabStrip* tab_strip2 = GetTabStripForBrowser(new_browser);
    138   ASSERT_FALSE(tab_strip2->IsDragSessionActive());
    139 
    140   EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
    141   EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
    142 }
    143 
    144 // Deletes a tab being dragged before the user moved enough to start a drag.
    145 IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DeleteBeforeStartedDragging) {
    146   // Add another tab.
    147   AddTabAndResetBrowser(browser());
    148   TabStrip* tab_strip = GetTabStripForBrowser(browser());
    149 
    150   // Click on the first tab, but don't move it.
    151   gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
    152   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
    153   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    154                   ui_controls::LEFT, ui_controls::DOWN));
    155 
    156   // Should be dragging.
    157   ASSERT_TRUE(tab_strip->IsDragSessionActive());
    158   ASSERT_TRUE(TabDragController::IsActive());
    159 
    160   // Delete the tab being dragged.
    161   delete browser()->tab_strip_model()->GetWebContentsAt(0);
    162 
    163   // Should have canceled dragging.
    164   ASSERT_FALSE(tab_strip->IsDragSessionActive());
    165   ASSERT_FALSE(TabDragController::IsActive());
    166 
    167   EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
    168 }
    169 
    170 // Deletes a tab being dragged while still attached.
    171 IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DeleteTabWhileAttached) {
    172   // Add another tab.
    173   AddTabAndResetBrowser(browser());
    174   TabStrip* tab_strip = GetTabStripForBrowser(browser());
    175 
    176   // Click on the first tab and move it enough so that it starts dragging but is
    177   // still attached.
    178   gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
    179   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
    180   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    181                   ui_controls::LEFT, ui_controls::DOWN));
    182   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
    183                   gfx::Point(tab_0_center.x() + 20, tab_0_center.y())));
    184 
    185   // Should be dragging.
    186   ASSERT_TRUE(tab_strip->IsDragSessionActive());
    187   ASSERT_TRUE(TabDragController::IsActive());
    188 
    189   // Delete the tab being dragged.
    190   delete browser()->tab_strip_model()->GetWebContentsAt(0);
    191 
    192   // Should have canceled dragging.
    193   ASSERT_FALSE(tab_strip->IsDragSessionActive());
    194   ASSERT_FALSE(TabDragController::IsActive());
    195 
    196   EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
    197 }
    198 
    199 // Deletes a tab being dragged after dragging a tab so that a new window is
    200 // created.
    201 IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DeleteTabWhileDetached) {
    202   // Add another tab.
    203   AddTabAndResetBrowser(browser());
    204   TabStrip* tab_strip = GetTabStripForBrowser(browser());
    205 
    206   // Move to the first tab and drag it enough so that it detaches.
    207   gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
    208   WebContents* to_delete = browser()->tab_strip_model()->GetWebContentsAt(0);
    209   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
    210   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    211                   ui_controls::LEFT, ui_controls::DOWN));
    212   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
    213                   gfx::Point(tab_0_center.x(),
    214                              tab_0_center.y() + tab_strip->height() + 20)));
    215   delete to_delete;
    216 
    217   // Should not be dragging.
    218   ASSERT_FALSE(tab_strip->IsDragSessionActive());
    219   ASSERT_FALSE(TabDragController::IsActive());
    220 
    221   EXPECT_EQ("1", IDString(browser()->tab_strip_model()));
    222 }
    223 
    224 // Detaches a tab and while detached deletes a tab from the source and releases
    225 // the mouse.
    226 IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DeleteSourceDetached) {
    227   // Add another tab.
    228   AddTabAndResetBrowser(browser());
    229   TabStrip* tab_strip = GetTabStripForBrowser(browser());
    230 
    231   // Move to the first tab and drag it enough so that it detaches.
    232   gfx::Point tab_0_center(GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
    233   WebContents* to_delete = browser()->tab_strip_model()->GetWebContentsAt(1);
    234   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
    235   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    236                   ui_controls::LEFT, ui_controls::DOWN));
    237   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
    238                   gfx::Point(tab_0_center.x(),
    239                              tab_0_center.y() + tab_strip->height() + 20)));
    240   delete to_delete;
    241 
    242   // Should still be dragging.
    243   ASSERT_TRUE(tab_strip->IsDragSessionActive());
    244   ASSERT_TRUE(TabDragController::IsActive());
    245 
    246   // Release the mouse.
    247   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    248                   ui_controls::LEFT, ui_controls::UP));
    249 
    250   // Releasing the mouse should destroy the existing browser and create a new
    251   // one.
    252   ASSERT_EQ(1u, native_browser_list->size());
    253   Browser* new_browser = native_browser_list->get(0);
    254   EXPECT_NE(new_browser, browser());
    255 
    256   ASSERT_FALSE(GetTabStripForBrowser(new_browser)->IsDragSessionActive());
    257   ASSERT_FALSE(TabDragController::IsActive());
    258 
    259   EXPECT_EQ("0", IDString(new_browser->tab_strip_model()));
    260 }
    261 
    262 // Creates two browsers, selects all tabs in first and drags into second.
    263 IN_PROC_BROWSER_TEST_F(TabDragControllerTest, DragAllToSeparateWindow) {
    264   TabStrip* tab_strip = GetTabStripForBrowser(browser());
    265 
    266   // Add another tab to browser().
    267   AddTabAndResetBrowser(browser());
    268 
    269   // Create another browser.
    270   Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
    271   TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
    272 
    273   browser()->tab_strip_model()->AddTabAtToSelection(0);
    274   browser()->tab_strip_model()->AddTabAtToSelection(1);
    275 
    276   // Move to the first tab and drag it enough so that it detaches, but not
    277   // enough that it attaches to browser2.
    278   gfx::Point tab_0_center(
    279       GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
    280   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
    281   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    282                   ui_controls::LEFT, ui_controls::DOWN));
    283   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
    284                   gfx::Point(tab_0_center.x(),
    285                              tab_0_center.y() + tab_strip->height() + 20)));
    286 
    287   ASSERT_TRUE(tab_strip->IsDragSessionActive());
    288   ASSERT_FALSE(tab_strip2->IsDragSessionActive());
    289   ASSERT_TRUE(TabDragController::IsActive());
    290   ASSERT_EQ(2u, native_browser_list->size());
    291 
    292   // Drag to tab_strip2.
    293   gfx::Point target_point(tab_strip2->width() - 1,
    294                           tab_strip2->height() / 2);
    295   views::View::ConvertPointToScreen(tab_strip2, &target_point);
    296   ASSERT_TRUE(ui_controls::SendMouseMove(target_point.x(), target_point.y()));
    297 
    298   // Should now be attached to tab_strip2.
    299   ASSERT_TRUE(tab_strip->IsDragSessionActive());
    300   ASSERT_TRUE(TabDragController::IsActive());
    301 
    302   // Release the mouse, stopping the drag session.
    303   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    304                   ui_controls::LEFT, ui_controls::UP));
    305   ASSERT_FALSE(TabDragController::IsActive());
    306   EXPECT_EQ("100 0 1", IDString(browser2->tab_strip_model()));
    307 }
    308 
    309 // Creates two browsers, selects all tabs in first, drags into second, then hits
    310 // escape.
    311 IN_PROC_BROWSER_TEST_F(TabDragControllerTest,
    312                        DragAllToSeparateWindowAndCancel) {
    313   TabStrip* tab_strip = GetTabStripForBrowser(browser());
    314 
    315   // Add another tab to browser().
    316   AddTabAndResetBrowser(browser());
    317 
    318   // Create another browser.
    319   Browser* browser2 = CreateAnotherWindowBrowserAndRelayout();
    320   TabStrip* tab_strip2 = GetTabStripForBrowser(browser2);
    321 
    322   browser()->tab_strip_model()->AddTabAtToSelection(0);
    323   browser()->tab_strip_model()->AddTabAtToSelection(1);
    324 
    325   // Move to the first tab and drag it enough so that it detaches, but not
    326   // enough that it attaches to browser2.
    327   gfx::Point tab_0_center(
    328       GetCenterInScreenCoordinates(tab_strip->tab_at(0)));
    329   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(tab_0_center));
    330   ASSERT_TRUE(ui_test_utils::SendMouseEventsSync(
    331                   ui_controls::LEFT, ui_controls::DOWN));
    332   ASSERT_TRUE(ui_test_utils::SendMouseMoveSync(
    333                   gfx::Point(tab_0_center.x(),
    334                              tab_0_center.y() + tab_strip->height() + 20)));
    335   ASSERT_TRUE(tab_strip->IsDragSessionActive());
    336   ASSERT_FALSE(tab_strip2->IsDragSessionActive());
    337   ASSERT_TRUE(TabDragController::IsActive());
    338   ASSERT_EQ(2u, native_browser_list->size());
    339 
    340   // Drag to tab_strip2.
    341   gfx::Point target_point(tab_strip2->width() - 1,
    342                           tab_strip2->height() / 2);
    343   views::View::ConvertPointToScreen(tab_strip2, &target_point);
    344   ASSERT_TRUE(ui_controls::SendMouseMove(target_point.x(), target_point.y()));
    345 
    346   ASSERT_TRUE(tab_strip->IsDragSessionActive());
    347   ASSERT_TRUE(TabDragController::IsActive());
    348   ASSERT_EQ(2u, native_browser_list->size());
    349 
    350   // Cancel the drag.
    351   // TODO(msw): Fix this on "XP Tests (1)"; see http://crbug.com/227444
    352   if (base::win::GetVersion() == base::win::VERSION_XP &&
    353       views::Textfield::IsViewsTextfieldEnabled()) {
    354     LOG(INFO) << "Try SendKeyPressToWindowSync [esc]; maybe this works???";
    355     ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
    356         browser2->window()->GetNativeWindow(), ui::VKEY_ESCAPE,
    357         false, false, false, false));
    358     LOG(INFO) << "Tab strip 1 drag active (expect 0): "
    359               << tab_strip->IsDragSessionActive();
    360     LOG(INFO) << "Tab strip 2 drag active (expect 0): "
    361               << tab_strip2->IsDragSessionActive();
    362     LOG(INFO) << "Tab drag controller active (expect 0): "
    363               << TabDragController::IsActive();
    364     LOG(INFO) << "Native browser list size (expect 2): "
    365               << native_browser_list->size();
    366     LOG(INFO) << "Tab strip 1 model string (expect '0 1'): "
    367               << IDString(browser()->tab_strip_model());
    368     LOG(INFO) << "Tab strip 2 model string (expect '100'): "
    369               << IDString(browser2->tab_strip_model());
    370 
    371     LOG(INFO) << "Try SendKeyPressSync [esc]; is this needed???";
    372     ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
    373         browser2, ui::VKEY_ESCAPE, false, false, false, false));
    374     LOG(INFO) << "Tab strip 1 drag active (expect 0): "
    375               << tab_strip->IsDragSessionActive();
    376     LOG(INFO) << "Tab strip 2 drag active (expect 0): "
    377               << tab_strip2->IsDragSessionActive();
    378     LOG(INFO) << "Tab drag controller active (expect 0): "
    379               << TabDragController::IsActive();
    380     LOG(INFO) << "Native browser list size (expect 2): "
    381               << native_browser_list->size();
    382     LOG(INFO) << "Tab strip 1 model string (expect '0 1'): "
    383               << IDString(browser()->tab_strip_model());
    384     LOG(INFO) << "Tab strip 2 model string (expect '100'): "
    385               << IDString(browser2->tab_strip_model());
    386   } else {
    387     ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
    388         browser2, ui::VKEY_ESCAPE, false, false, false, false));
    389     ASSERT_FALSE(tab_strip->IsDragSessionActive());
    390     ASSERT_FALSE(tab_strip2->IsDragSessionActive());
    391     ASSERT_FALSE(TabDragController::IsActive());
    392     ASSERT_EQ(2u, native_browser_list->size());
    393     EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
    394     EXPECT_EQ("100", IDString(browser2->tab_strip_model()));
    395   }
    396 }
    397