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
36using content::WebContents;
37using test::GetCenterInScreenCoordinates;
38using test::GetTabStripForBrowser;
39using test::IDString;
40using test::ResetIDs;
41using 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.
48IN_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
74IN_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.
113IN_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.
145IN_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.
171IN_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.
201IN_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.
226IN_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.
263IN_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.
311IN_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    LOG(INFO) << "Try SendKeyPressToWindowSync [esc]; maybe this works???";
354    ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
355        browser2->window()->GetNativeWindow(), ui::VKEY_ESCAPE,
356        false, false, false, false));
357    LOG(INFO) << "Tab strip 1 drag active (expect 0): "
358              << tab_strip->IsDragSessionActive();
359    LOG(INFO) << "Tab strip 2 drag active (expect 0): "
360              << tab_strip2->IsDragSessionActive();
361    LOG(INFO) << "Tab drag controller active (expect 0): "
362              << TabDragController::IsActive();
363    LOG(INFO) << "Native browser list size (expect 2): "
364              << native_browser_list->size();
365    LOG(INFO) << "Tab strip 1 model string (expect '0 1'): "
366              << IDString(browser()->tab_strip_model());
367    LOG(INFO) << "Tab strip 2 model string (expect '100'): "
368              << IDString(browser2->tab_strip_model());
369
370    LOG(INFO) << "Try SendKeyPressSync [esc]; is this needed???";
371    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
372        browser2, ui::VKEY_ESCAPE, false, false, false, false));
373    LOG(INFO) << "Tab strip 1 drag active (expect 0): "
374              << tab_strip->IsDragSessionActive();
375    LOG(INFO) << "Tab strip 2 drag active (expect 0): "
376              << tab_strip2->IsDragSessionActive();
377    LOG(INFO) << "Tab drag controller active (expect 0): "
378              << TabDragController::IsActive();
379    LOG(INFO) << "Native browser list size (expect 2): "
380              << native_browser_list->size();
381    LOG(INFO) << "Tab strip 1 model string (expect '0 1'): "
382              << IDString(browser()->tab_strip_model());
383    LOG(INFO) << "Tab strip 2 model string (expect '100'): "
384              << IDString(browser2->tab_strip_model());
385  } else {
386    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
387        browser2, ui::VKEY_ESCAPE, false, false, false, false));
388    ASSERT_FALSE(tab_strip->IsDragSessionActive());
389    ASSERT_FALSE(tab_strip2->IsDragSessionActive());
390    ASSERT_FALSE(TabDragController::IsActive());
391    ASSERT_EQ(2u, native_browser_list->size());
392    EXPECT_EQ("0 1", IDString(browser()->tab_strip_model()));
393    EXPECT_EQ("100", IDString(browser2->tab_strip_model()));
394  }
395}
396