1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/ash/tab_scrubber.h"
7 #include "ash/display/event_transformation_handler.h"
9 #include "base/command_line.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "chrome/browser/ui/browser_tabstrip.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
16 #include "chrome/browser/ui/views/frame/browser_view.h"
17 #include "chrome/browser/ui/views/tabs/tab.h"
18 #include "chrome/browser/ui/views/tabs/tab_strip.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/test/base/in_process_browser_test.h"
21 #include "chrome/test/base/ui_test_utils.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/common/url_constants.h"
24 #include "content/public/test/test_utils.h"
25 #include "ui/aura/test/event_generator.h"
26 #include "ui/aura/window.h"
27 #include "ui/events/event_utils.h"
29 #if defined(OS_CHROMEOS)
30 #include "chromeos/chromeos_switches.h"
35 class TabScrubberTest
: public InProcessBrowserTest
,
36 public TabStripModelObserver
{
42 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
43 #if defined(OS_CHROMEOS)
44 command_line
->AppendSwitch(chromeos::switches::kNaturalScrollDefault
);
46 command_line
->AppendSwitch(switches::kOpenAsh
);
49 virtual void SetUpOnMainThread() OVERRIDE
{
50 TabScrubber::GetInstance()->set_activation_delay(0);
52 // Disable external monitor scaling of coordinates.
53 ash::Shell
* shell
= ash::Shell::GetInstance();
54 shell
->event_transformation_handler()->set_transformation_mode(
55 ash::EventTransformationHandler::TRANSFORM_NONE
);
58 virtual void CleanUpOnMainThread() OVERRIDE
{
59 browser()->tab_strip_model()->RemoveObserver(this);
62 TabStrip
* GetTabStrip(Browser
* browser
) {
63 aura::Window
* window
= browser
->window()->GetNativeWindow();
64 return BrowserView::GetBrowserViewForNativeWindow(window
)->tabstrip();
67 int GetStartX(Browser
* browser
,
69 TabScrubber::Direction direction
) {
70 return TabScrubber::GetStartPoint(
71 GetTabStrip(browser
), index
, direction
).x();
74 int GetTabCenter(Browser
* browser
, int index
) {
75 return GetTabStrip(browser
)->tab_at(index
)->bounds().CenterPoint().x();
78 // Sends one scroll event synchronously without initial or final
80 void SendScrubEvent(Browser
* browser
, int index
) {
81 aura::Window
* window
= browser
->window()->GetNativeWindow();
82 aura::Window
* root
= window
->GetRootWindow();
83 aura::test::EventGenerator
event_generator(root
, window
);
84 int active_index
= browser
->tab_strip_model()->active_index();
85 TabScrubber::Direction direction
= index
< active_index
?
86 TabScrubber::LEFT
: TabScrubber::RIGHT
;
87 int offset
= GetTabCenter(browser
, index
) -
88 GetStartX(browser
, active_index
, direction
);
89 ui::ScrollEvent
scroll_event(ui::ET_SCROLL
,
91 ui::EventTimeForNow(),
96 event_generator
.Dispatch(&scroll_event
);
105 // Sends asynchronous events and waits for tab at |index| to become
107 void Scrub(Browser
* browser
, int index
, ScrubType scrub_type
) {
108 aura::Window
* window
= browser
->window()->GetNativeWindow();
109 aura::Window
* root
= window
->GetRootWindow();
110 aura::test::EventGenerator
event_generator(root
, window
);
111 event_generator
.set_async(true);
112 activation_order_
.clear();
113 int active_index
= browser
->tab_strip_model()->active_index();
114 ASSERT_NE(index
, active_index
);
115 ASSERT_TRUE(scrub_type
!= SKIP_TABS
|| ((index
- active_index
) % 2) == 0);
116 TabScrubber::Direction direction
;
118 if (index
< active_index
) {
119 direction
= TabScrubber::LEFT
;
122 direction
= TabScrubber::RIGHT
;
125 if (scrub_type
== SKIP_TABS
)
127 int last
= GetStartX(browser
, active_index
, direction
);
128 std::vector
<gfx::Point
> offsets
;
129 for (int i
= active_index
+ increment
; i
!= (index
+ increment
);
131 int tab_center
= GetTabCenter(browser
, i
);
132 offsets
.push_back(gfx::Point(tab_center
- last
, 0));
133 last
= GetStartX(browser
, i
, direction
);
134 if (scrub_type
== REPEAT_TABS
) {
135 offsets
.push_back(gfx::Point(increment
, 0));
139 event_generator
.ScrollSequence(gfx::Point(0, 0),
140 base::TimeDelta::FromMilliseconds(100),
143 RunUntilTabActive(browser
, index
);
146 // Sends events and waits for tab at |index| to become active
147 // if it's different from the currently active tab.
148 // If the active tab is expected to stay the same, send events
149 // synchronously (as we don't have anything to wait for).
150 void SendScrubSequence(Browser
* browser
, int x_offset
, int index
) {
151 aura::Window
* window
= browser
->window()->GetNativeWindow();
152 aura::Window
* root
= window
->GetRootWindow();
153 aura::test::EventGenerator
event_generator(root
, window
);
154 bool wait_for_active
= false;
155 if (index
!= browser
->tab_strip_model()->active_index()) {
156 wait_for_active
= true;
157 event_generator
.set_async(true);
159 event_generator
.ScrollSequence(gfx::Point(0, 0),
160 ui::EventTimeForNow(),
166 RunUntilTabActive(browser
, index
);
169 void AddTabs(Browser
* browser
, int num_tabs
) {
170 TabStrip
* tab_strip
= GetTabStrip(browser
);
171 for (int i
= 0; i
< num_tabs
; ++i
)
172 AddBlankTabAndShow(browser
);
173 ASSERT_EQ(num_tabs
+ 1, browser
->tab_strip_model()->count());
174 ASSERT_EQ(num_tabs
, browser
->tab_strip_model()->active_index());
175 tab_strip
->StopAnimating(true);
176 ASSERT_FALSE(tab_strip
->IsAnimating());
179 // TabStripModelObserver overrides.
180 virtual void TabInsertedAt(content::WebContents
* contents
,
182 bool foreground
) OVERRIDE
{}
183 virtual void TabClosingAt(TabStripModel
* tab_strip_model
,
184 content::WebContents
* contents
,
185 int index
) OVERRIDE
{}
186 virtual void TabDetachedAt(content::WebContents
* contents
,
187 int index
) OVERRIDE
{}
188 virtual void TabDeactivated(content::WebContents
* contents
) OVERRIDE
{}
189 virtual void ActiveTabChanged(content::WebContents
* old_contents
,
190 content::WebContents
* new_contents
,
192 int reason
) OVERRIDE
{
193 activation_order_
.push_back(index
);
194 if (index
== target_index_
)
198 virtual void TabSelectionChanged(
199 TabStripModel
* tab_strip_model
,
200 const ui::ListSelectionModel
& old_model
) OVERRIDE
{}
201 virtual void TabMoved(content::WebContents
* contents
,
203 int to_index
) OVERRIDE
{}
204 virtual void TabChangedAt(content::WebContents
* contents
,
206 TabChangeType change_type
) OVERRIDE
{}
207 virtual void TabReplacedAt(TabStripModel
* tab_strip_model
,
208 content::WebContents
* old_contents
,
209 content::WebContents
* new_contents
,
210 int index
) OVERRIDE
{}
211 virtual void TabPinnedStateChanged(content::WebContents
* contents
,
212 int index
) OVERRIDE
{}
213 virtual void TabMiniStateChanged(content::WebContents
* contents
,
214 int index
) OVERRIDE
{
216 virtual void TabBlockedStateChanged(content::WebContents
* contents
,
217 int index
) OVERRIDE
{}
218 virtual void TabStripEmpty() OVERRIDE
{}
219 virtual void TabStripModelDeleted() OVERRIDE
{}
221 // History of tab activation. Scrub() resets it.
222 std::vector
<int> activation_order_
;
225 void RunUntilTabActive(Browser
* browser
, int target
) {
226 base::RunLoop run_loop
;
227 quit_closure_
= content::GetQuitTaskForRunLoop(&run_loop
);
228 browser
->tab_strip_model()->AddObserver(this);
229 target_index_
= target
;
230 content::RunThisRunLoop(&run_loop
);
231 browser
->tab_strip_model()->RemoveObserver(this);
235 base::Closure quit_closure_
;
238 DISALLOW_COPY_AND_ASSIGN(TabScrubberTest
);
243 #if defined(OS_CHROMEOS)
244 // Swipe a single tab in each direction.
245 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, Single
) {
246 AddTabs(browser(), 1);
248 Scrub(browser(), 0, EACH_TAB
);
249 EXPECT_EQ(1U, activation_order_
.size());
250 EXPECT_EQ(0, activation_order_
[0]);
251 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
253 Scrub(browser(), 1, EACH_TAB
);
254 EXPECT_EQ(1U, activation_order_
.size());
255 EXPECT_EQ(1, activation_order_
[0]);
256 EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
259 // Swipe 4 tabs in each direction. Each of the tabs should become active.
260 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, Multi
) {
261 AddTabs(browser(), 4);
263 Scrub(browser(), 0, EACH_TAB
);
264 ASSERT_EQ(4U, activation_order_
.size());
265 EXPECT_EQ(3, activation_order_
[0]);
266 EXPECT_EQ(2, activation_order_
[1]);
267 EXPECT_EQ(1, activation_order_
[2]);
268 EXPECT_EQ(0, activation_order_
[3]);
269 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
271 Scrub(browser(), 4, EACH_TAB
);
272 ASSERT_EQ(4U, activation_order_
.size());
273 EXPECT_EQ(1, activation_order_
[0]);
274 EXPECT_EQ(2, activation_order_
[1]);
275 EXPECT_EQ(3, activation_order_
[2]);
276 EXPECT_EQ(4, activation_order_
[3]);
277 EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
280 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, MultiBrowser
) {
281 AddTabs(browser(), 1);
282 Scrub(browser(), 0, EACH_TAB
);
283 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
285 Browser
* browser2
= CreateBrowser(browser()->profile());
286 browser2
->window()->Activate();
287 ASSERT_TRUE(browser2
->window()->IsActive());
288 ASSERT_FALSE(browser()->window()->IsActive());
289 AddTabs(browser2
, 1);
291 Scrub(browser2
, 0, EACH_TAB
);
292 EXPECT_EQ(0, browser2
->tab_strip_model()->active_index());
295 // Swipe 4 tabs in each direction with an extra swipe within each. The same
296 // 4 tabs should become active.
297 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, Repeated
) {
298 AddTabs(browser(), 4);
300 Scrub(browser(), 0, REPEAT_TABS
);
301 ASSERT_EQ(4U, activation_order_
.size());
302 EXPECT_EQ(3, activation_order_
[0]);
303 EXPECT_EQ(2, activation_order_
[1]);
304 EXPECT_EQ(1, activation_order_
[2]);
305 EXPECT_EQ(0, activation_order_
[3]);
306 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
308 Scrub(browser(), 4, REPEAT_TABS
);
309 ASSERT_EQ(4U, activation_order_
.size());
310 EXPECT_EQ(1, activation_order_
[0]);
311 EXPECT_EQ(2, activation_order_
[1]);
312 EXPECT_EQ(3, activation_order_
[2]);
313 EXPECT_EQ(4, activation_order_
[3]);
314 EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
317 // Confirm that we get the last tab made active when we skip tabs.
318 // These tests have 5 total tabs. We will only received scroll events
319 // on tabs 0, 2 and 4.
320 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, Skipped
) {
321 AddTabs(browser(), 4);
323 Scrub(browser(), 0, SKIP_TABS
);
324 EXPECT_EQ(2U, activation_order_
.size());
325 EXPECT_EQ(2, activation_order_
[0]);
326 EXPECT_EQ(0, activation_order_
[1]);
327 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
329 Scrub(browser(), 4, SKIP_TABS
);
330 EXPECT_EQ(2U, activation_order_
.size());
331 EXPECT_EQ(2, activation_order_
[0]);
332 EXPECT_EQ(4, activation_order_
[1]);
333 EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
336 // Confirm that nothing happens when the swipe is small.
337 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, NoChange
) {
338 AddTabs(browser(), 1);
340 SendScrubSequence(browser(), -1, 1);
341 EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
343 SendScrubSequence(browser(), 1, 1);
344 EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
347 // Confirm that very large swipes go to the beginning and and of the tabstrip.
348 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, Bounds
) {
349 AddTabs(browser(), 1);
351 SendScrubSequence(browser(), -10000, 0);
352 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
354 SendScrubSequence(browser(), 10000, 1);
355 EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
358 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, DeleteHighlighted
) {
359 AddTabs(browser(), 1);
361 SendScrubEvent(browser(), 0);
362 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
363 browser()->tab_strip_model()->CloseWebContentsAt(0,
364 TabStripModel::CLOSE_NONE
);
365 EXPECT_FALSE(TabScrubber::GetInstance()->IsActivationPending());
368 // Delete the currently highlighted tab. Make sure the TabScrubber is aware.
369 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, DeleteBeforeHighlighted
) {
370 AddTabs(browser(), 2);
372 SendScrubEvent(browser(), 1);
373 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
374 browser()->tab_strip_model()->CloseWebContentsAt(0,
375 TabStripModel::CLOSE_NONE
);
376 EXPECT_EQ(0, TabScrubber::GetInstance()->highlighted_tab());
379 // Move the currently highlighted tab and confirm it gets tracked.
380 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, MoveHighlighted
) {
381 AddTabs(browser(), 1);
383 SendScrubEvent(browser(), 0);
384 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
385 browser()->tab_strip_model()->ToggleSelectionAt(0);
386 browser()->tab_strip_model()->ToggleSelectionAt(1);
387 browser()->tab_strip_model()->MoveSelectedTabsTo(1);
388 EXPECT_EQ(1, TabScrubber::GetInstance()->highlighted_tab());
391 // Move a tab to before the highlighted one.
392 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, MoveBefore
) {
393 AddTabs(browser(), 2);
395 SendScrubEvent(browser(), 1);
396 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
397 browser()->tab_strip_model()->ToggleSelectionAt(0);
398 browser()->tab_strip_model()->ToggleSelectionAt(2);
399 browser()->tab_strip_model()->MoveSelectedTabsTo(2);
400 EXPECT_EQ(0, TabScrubber::GetInstance()->highlighted_tab());
403 // Move a tab to after the highlighted one.
404 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, MoveAfter
) {
405 AddTabs(browser(), 2);
407 SendScrubEvent(browser(), 1);
408 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
409 browser()->tab_strip_model()->MoveSelectedTabsTo(0);
410 EXPECT_EQ(2, TabScrubber::GetInstance()->highlighted_tab());
413 // Close the browser while an activation is pending.
414 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, CloseBrowser
) {
415 AddTabs(browser(), 1);
417 SendScrubEvent(browser(), 0);
418 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
419 browser()->window()->Close();
420 EXPECT_FALSE(TabScrubber::GetInstance()->IsActivationPending());
423 #endif // defined(OS_CHROMEOS)