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/run_loop.h"
12 #include "chrome/browser/ui/browser_tabstrip.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
15 #include "chrome/browser/ui/views/frame/browser_view.h"
16 #include "chrome/browser/ui/views/tabs/tab.h"
17 #include "chrome/browser/ui/views/tabs/tab_strip.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/test/base/in_process_browser_test.h"
20 #include "content/public/browser/notification_service.h"
21 #include "content/public/common/url_constants.h"
22 #include "content/public/test/test_utils.h"
23 #include "ui/aura/window.h"
24 #include "ui/events/event_utils.h"
25 #include "ui/events/test/event_generator.h"
27 #if defined(OS_CHROMEOS)
28 #include "chromeos/chromeos_switches.h"
33 class TabScrubberTest
: public InProcessBrowserTest
,
34 public TabStripModelObserver
{
40 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
41 #if defined(OS_CHROMEOS)
42 command_line
->AppendSwitch(chromeos::switches::kNaturalScrollDefault
);
44 command_line
->AppendSwitch(switches::kOpenAsh
);
47 void SetUpOnMainThread() override
{
48 TabScrubber::GetInstance()->set_activation_delay(0);
50 // Disable external monitor scaling of coordinates.
51 ash::Shell
* shell
= ash::Shell::GetInstance();
52 shell
->event_transformation_handler()->set_transformation_mode(
53 ash::EventTransformationHandler::TRANSFORM_NONE
);
56 void TearDownOnMainThread() override
{
57 browser()->tab_strip_model()->RemoveObserver(this);
60 TabStrip
* GetTabStrip(Browser
* browser
) {
61 aura::Window
* window
= browser
->window()->GetNativeWindow();
62 return BrowserView::GetBrowserViewForNativeWindow(window
)->tabstrip();
65 float GetStartX(Browser
* browser
,
67 TabScrubber::Direction direction
) {
68 return static_cast<float>(TabScrubber::GetStartPoint(
69 GetTabStrip(browser
), index
, direction
).x());
72 float GetTabCenter(Browser
* browser
, int index
) {
73 return static_cast<float>(GetTabStrip(browser
)
80 // The simulated scroll event's offsets are calculated in the tests rather
81 // than generated by the real event system. For the offsets calculation to be
82 // correct in an RTL layout, we must invert the direction since it is
83 // calculated here in the tests based on the indices and they're inverted in
85 // Tab indices in an English layout : 0 - 1 - 2 - 3 - 4.
86 // Tab indices in an Arabic layout : 4 - 3 - 2 - 1 - 0.
87 TabScrubber::Direction
InvertDirectionIfNeeded(
88 TabScrubber::Direction direction
) {
89 if (base::i18n::IsRTL()) {
90 return direction
== TabScrubber::LEFT
? TabScrubber::RIGHT
97 // Sends one scroll event synchronously without initial or final
99 void SendScrubEvent(Browser
* browser
, int index
) {
100 aura::Window
* window
= browser
->window()->GetNativeWindow();
101 aura::Window
* root
= window
->GetRootWindow();
102 ui::test::EventGenerator
event_generator(root
, window
);
103 int active_index
= browser
->tab_strip_model()->active_index();
104 TabScrubber::Direction direction
= index
< active_index
?
105 TabScrubber::LEFT
: TabScrubber::RIGHT
;
107 direction
= InvertDirectionIfNeeded(direction
);
109 float offset
= GetTabCenter(browser
, index
) -
110 GetStartX(browser
, active_index
, direction
);
111 ui::ScrollEvent
scroll_event(ui::ET_SCROLL
,
113 ui::EventTimeForNow(),
118 event_generator
.Dispatch(&scroll_event
);
127 // Sends asynchronous events and waits for tab at |index| to become
129 void Scrub(Browser
* browser
, int index
, ScrubType scrub_type
) {
130 aura::Window
* window
= browser
->window()->GetNativeWindow();
131 aura::Window
* root
= window
->GetRootWindow();
132 ui::test::EventGenerator
event_generator(root
, window
);
133 event_generator
.set_async(true);
134 activation_order_
.clear();
135 int active_index
= browser
->tab_strip_model()->active_index();
136 ASSERT_NE(index
, active_index
);
137 ASSERT_TRUE(scrub_type
!= SKIP_TABS
|| ((index
- active_index
) % 2) == 0);
138 TabScrubber::Direction direction
;
140 if (index
< active_index
) {
141 direction
= TabScrubber::LEFT
;
144 direction
= TabScrubber::RIGHT
;
148 direction
= InvertDirectionIfNeeded(direction
);
150 if (scrub_type
== SKIP_TABS
)
152 float last
= GetStartX(browser
, active_index
, direction
);
153 std::vector
<gfx::PointF
> offsets
;
154 for (int i
= active_index
+ increment
; i
!= (index
+ increment
);
156 float tab_center
= GetTabCenter(browser
, i
);
157 offsets
.push_back(gfx::PointF(tab_center
- last
, 0));
158 last
= GetStartX(browser
, i
, direction
);
159 if (scrub_type
== REPEAT_TABS
) {
160 offsets
.push_back(gfx::PointF(static_cast<float>(increment
), 0));
164 event_generator
.ScrollSequence(gfx::Point(0, 0),
165 base::TimeDelta::FromMilliseconds(100),
168 RunUntilTabActive(browser
, index
);
171 // Sends events and waits for tab at |index| to become active
172 // if it's different from the currently active tab.
173 // If the active tab is expected to stay the same, send events
174 // synchronously (as we don't have anything to wait for).
175 void SendScrubSequence(Browser
* browser
, float x_offset
, int index
) {
176 aura::Window
* window
= browser
->window()->GetNativeWindow();
177 aura::Window
* root
= window
->GetRootWindow();
178 ui::test::EventGenerator
event_generator(root
, window
);
179 bool wait_for_active
= false;
180 if (index
!= browser
->tab_strip_model()->active_index()) {
181 wait_for_active
= true;
182 event_generator
.set_async(true);
184 event_generator
.ScrollSequence(gfx::Point(0, 0),
185 ui::EventTimeForNow(),
191 RunUntilTabActive(browser
, index
);
194 void AddTabs(Browser
* browser
, int num_tabs
) {
195 TabStrip
* tab_strip
= GetTabStrip(browser
);
196 for (int i
= 0; i
< num_tabs
; ++i
)
197 AddBlankTabAndShow(browser
);
198 ASSERT_EQ(num_tabs
+ 1, browser
->tab_strip_model()->count());
199 ASSERT_EQ(num_tabs
, browser
->tab_strip_model()->active_index());
200 tab_strip
->StopAnimating(true);
201 ASSERT_FALSE(tab_strip
->IsAnimating());
204 // TabStripModelObserver overrides.
205 void TabInsertedAt(content::WebContents
* contents
,
207 bool foreground
) override
{}
208 void TabClosingAt(TabStripModel
* tab_strip_model
,
209 content::WebContents
* contents
,
210 int index
) override
{}
211 void TabDetachedAt(content::WebContents
* contents
, int index
) override
{}
212 void TabDeactivated(content::WebContents
* contents
) override
{}
213 void ActiveTabChanged(content::WebContents
* old_contents
,
214 content::WebContents
* new_contents
,
216 int reason
) override
{
217 activation_order_
.push_back(index
);
218 if (index
== target_index_
)
222 void TabSelectionChanged(TabStripModel
* tab_strip_model
,
223 const ui::ListSelectionModel
& old_model
) override
{}
224 void TabMoved(content::WebContents
* contents
,
226 int to_index
) override
{}
227 void TabChangedAt(content::WebContents
* contents
,
229 TabChangeType change_type
) override
{}
230 void TabReplacedAt(TabStripModel
* tab_strip_model
,
231 content::WebContents
* old_contents
,
232 content::WebContents
* new_contents
,
233 int index
) override
{}
234 void TabPinnedStateChanged(content::WebContents
* contents
,
235 int index
) override
{}
236 void TabBlockedStateChanged(content::WebContents
* contents
,
237 int index
) override
{}
238 void TabStripEmpty() override
{}
239 void TabStripModelDeleted() override
{}
241 // History of tab activation. Scrub() resets it.
242 std::vector
<int> activation_order_
;
245 void RunUntilTabActive(Browser
* browser
, int target
) {
246 base::RunLoop run_loop
;
247 quit_closure_
= content::GetQuitTaskForRunLoop(&run_loop
);
248 browser
->tab_strip_model()->AddObserver(this);
249 target_index_
= target
;
250 content::RunThisRunLoop(&run_loop
);
251 browser
->tab_strip_model()->RemoveObserver(this);
255 base::Closure quit_closure_
;
258 DISALLOW_COPY_AND_ASSIGN(TabScrubberTest
);
263 #if defined(OS_CHROMEOS)
264 // Swipe a single tab in each direction.
265 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, Single
) {
266 AddTabs(browser(), 1);
268 Scrub(browser(), 0, EACH_TAB
);
269 EXPECT_EQ(1U, activation_order_
.size());
270 EXPECT_EQ(0, activation_order_
[0]);
271 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
273 Scrub(browser(), 1, EACH_TAB
);
274 EXPECT_EQ(1U, activation_order_
.size());
275 EXPECT_EQ(1, activation_order_
[0]);
276 EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
279 // Swipe 4 tabs in each direction. Each of the tabs should become active.
280 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, Multi
) {
281 AddTabs(browser(), 4);
283 Scrub(browser(), 0, EACH_TAB
);
284 ASSERT_EQ(4U, activation_order_
.size());
285 EXPECT_EQ(3, activation_order_
[0]);
286 EXPECT_EQ(2, activation_order_
[1]);
287 EXPECT_EQ(1, activation_order_
[2]);
288 EXPECT_EQ(0, activation_order_
[3]);
289 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
291 Scrub(browser(), 4, EACH_TAB
);
292 ASSERT_EQ(4U, activation_order_
.size());
293 EXPECT_EQ(1, activation_order_
[0]);
294 EXPECT_EQ(2, activation_order_
[1]);
295 EXPECT_EQ(3, activation_order_
[2]);
296 EXPECT_EQ(4, activation_order_
[3]);
297 EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
300 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, MultiBrowser
) {
301 AddTabs(browser(), 1);
302 Scrub(browser(), 0, EACH_TAB
);
303 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
305 Browser
* browser2
= CreateBrowser(browser()->profile());
306 browser2
->window()->Activate();
307 ASSERT_TRUE(browser2
->window()->IsActive());
308 ASSERT_FALSE(browser()->window()->IsActive());
309 AddTabs(browser2
, 1);
311 Scrub(browser2
, 0, EACH_TAB
);
312 EXPECT_EQ(0, browser2
->tab_strip_model()->active_index());
315 // Swipe 4 tabs in each direction with an extra swipe within each. The same
316 // 4 tabs should become active.
317 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, Repeated
) {
318 AddTabs(browser(), 4);
320 Scrub(browser(), 0, REPEAT_TABS
);
321 ASSERT_EQ(4U, activation_order_
.size());
322 EXPECT_EQ(3, activation_order_
[0]);
323 EXPECT_EQ(2, activation_order_
[1]);
324 EXPECT_EQ(1, activation_order_
[2]);
325 EXPECT_EQ(0, activation_order_
[3]);
326 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
328 Scrub(browser(), 4, REPEAT_TABS
);
329 ASSERT_EQ(4U, activation_order_
.size());
330 EXPECT_EQ(1, activation_order_
[0]);
331 EXPECT_EQ(2, activation_order_
[1]);
332 EXPECT_EQ(3, activation_order_
[2]);
333 EXPECT_EQ(4, activation_order_
[3]);
334 EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
337 // Confirm that we get the last tab made active when we skip tabs.
338 // These tests have 5 total tabs. We will only received scroll events
339 // on tabs 0, 2 and 4.
340 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, Skipped
) {
341 AddTabs(browser(), 4);
343 Scrub(browser(), 0, SKIP_TABS
);
344 EXPECT_EQ(2U, activation_order_
.size());
345 EXPECT_EQ(2, activation_order_
[0]);
346 EXPECT_EQ(0, activation_order_
[1]);
347 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
349 Scrub(browser(), 4, SKIP_TABS
);
350 EXPECT_EQ(2U, activation_order_
.size());
351 EXPECT_EQ(2, activation_order_
[0]);
352 EXPECT_EQ(4, activation_order_
[1]);
353 EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
356 // Confirm that nothing happens when the swipe is small.
357 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, NoChange
) {
358 AddTabs(browser(), 1);
360 SendScrubSequence(browser(), -1, 1);
361 EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
363 SendScrubSequence(browser(), 1, 1);
364 EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
367 // Confirm that very large swipes go to the beginning and and of the tabstrip.
368 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, Bounds
) {
369 AddTabs(browser(), 1);
371 SendScrubSequence(browser(), -10000, 0);
372 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
374 SendScrubSequence(browser(), 10000, 1);
375 EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
378 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, DeleteHighlighted
) {
379 AddTabs(browser(), 1);
381 SendScrubEvent(browser(), 0);
382 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
383 browser()->tab_strip_model()->CloseWebContentsAt(0,
384 TabStripModel::CLOSE_NONE
);
385 EXPECT_FALSE(TabScrubber::GetInstance()->IsActivationPending());
388 // Delete the currently highlighted tab. Make sure the TabScrubber is aware.
389 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, DeleteBeforeHighlighted
) {
390 AddTabs(browser(), 2);
392 SendScrubEvent(browser(), 1);
393 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
394 browser()->tab_strip_model()->CloseWebContentsAt(0,
395 TabStripModel::CLOSE_NONE
);
396 EXPECT_EQ(0, TabScrubber::GetInstance()->highlighted_tab());
399 // Move the currently highlighted tab and confirm it gets tracked.
400 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, MoveHighlighted
) {
401 AddTabs(browser(), 1);
403 SendScrubEvent(browser(), 0);
404 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
405 browser()->tab_strip_model()->ToggleSelectionAt(0);
406 browser()->tab_strip_model()->ToggleSelectionAt(1);
407 browser()->tab_strip_model()->MoveSelectedTabsTo(1);
408 EXPECT_EQ(1, TabScrubber::GetInstance()->highlighted_tab());
411 // Move a tab to before the highlighted one. Make sure that the highlighted tab
412 // index is updated correctly.
413 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, MoveBefore
) {
414 AddTabs(browser(), 2);
416 SendScrubEvent(browser(), 1);
417 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
418 browser()->tab_strip_model()->ToggleSelectionAt(0);
419 browser()->tab_strip_model()->ToggleSelectionAt(2);
420 browser()->tab_strip_model()->MoveSelectedTabsTo(2);
421 EXPECT_EQ(0, TabScrubber::GetInstance()->highlighted_tab());
424 // Move a tab to after the highlighted one. Make sure that the highlighted tab
425 // index is updated correctly.
426 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, MoveAfter
) {
427 AddTabs(browser(), 2);
429 SendScrubEvent(browser(), 1);
430 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
431 browser()->tab_strip_model()->MoveSelectedTabsTo(0);
432 EXPECT_EQ(2, TabScrubber::GetInstance()->highlighted_tab());
435 // Close the browser while an activation is pending.
436 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, CloseBrowser
) {
437 AddTabs(browser(), 1);
439 SendScrubEvent(browser(), 0);
440 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
441 browser()->window()->Close();
442 EXPECT_FALSE(TabScrubber::GetInstance()->IsActivationPending());
445 // In an RTL layout, swipe 4 tabs in each direction. Each of the tabs should
447 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, RTLMulti
) {
448 base::i18n::SetICUDefaultLocale("ar");
449 ASSERT_TRUE(base::i18n::IsRTL());
451 AddTabs(browser(), 4);
453 Scrub(browser(), 0, EACH_TAB
);
454 ASSERT_EQ(4U, activation_order_
.size());
455 EXPECT_EQ(3, activation_order_
[0]);
456 EXPECT_EQ(2, activation_order_
[1]);
457 EXPECT_EQ(1, activation_order_
[2]);
458 EXPECT_EQ(0, activation_order_
[3]);
459 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
461 Scrub(browser(), 4, EACH_TAB
);
462 ASSERT_EQ(4U, activation_order_
.size());
463 EXPECT_EQ(1, activation_order_
[0]);
464 EXPECT_EQ(2, activation_order_
[1]);
465 EXPECT_EQ(3, activation_order_
[2]);
466 EXPECT_EQ(4, activation_order_
[3]);
467 EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
470 // In an RTL layout, confirm that we get the last tab made active when we skip
471 // tabs. These tests have 5 total tabs. We will only received scroll events
472 // on tabs 0, 2 and 4.
473 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, RTLSkipped
) {
474 base::i18n::SetICUDefaultLocale("ar");
475 ASSERT_TRUE(base::i18n::IsRTL());
477 AddTabs(browser(), 4);
479 Scrub(browser(), 0, SKIP_TABS
);
480 EXPECT_EQ(2U, activation_order_
.size());
481 EXPECT_EQ(2, activation_order_
[0]);
482 EXPECT_EQ(0, activation_order_
[1]);
483 EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
485 Scrub(browser(), 4, SKIP_TABS
);
486 EXPECT_EQ(2U, activation_order_
.size());
487 EXPECT_EQ(2, activation_order_
[0]);
488 EXPECT_EQ(4, activation_order_
[1]);
489 EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
492 // In an RTL layout, move a tab to before the highlighted one. Make sure that
493 // the highlighted tab index is updated correctly.
494 IN_PROC_BROWSER_TEST_F(TabScrubberTest
, RTLMoveBefore
) {
495 base::i18n::SetICUDefaultLocale("ar");
496 ASSERT_TRUE(base::i18n::IsRTL());
498 AddTabs(browser(), 2);
500 SendScrubEvent(browser(), 1);
501 EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
502 browser()->tab_strip_model()->ToggleSelectionAt(0);
503 browser()->tab_strip_model()->ToggleSelectionAt(2);
504 browser()->tab_strip_model()->MoveSelectedTabsTo(2);
505 EXPECT_EQ(0, TabScrubber::GetInstance()->highlighted_tab());
508 #endif // defined(OS_CHROMEOS)