tab_scrubber_browsertest.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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/ash/tab_scrubber.h"
6
7#include "ash/display/event_transformation_handler.h"
8#include "ash/shell.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/browser_thread.h"
23#include "content/public/browser/notification_service.h"
24#include "content/public/common/url_constants.h"
25#include "content/public/test/test_utils.h"
26#include "ui/aura/test/event_generator.h"
27#include "ui/aura/window.h"
28#include "ui/events/event_utils.h"
29
30#if defined(OS_CHROMEOS)
31#include "chromeos/chromeos_switches.h"
32#endif
33
34namespace {
35
36class TabScrubberTest : public InProcessBrowserTest,
37                        public TabStripModelObserver {
38 public:
39  TabScrubberTest()
40      : target_index_(-1) {
41  }
42
43  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
44#if defined(OS_CHROMEOS)
45    command_line->AppendSwitch(chromeos::switches::kNaturalScrollDefault);
46#endif
47    command_line->AppendSwitch(switches::kOpenAsh);
48  }
49
50  virtual void SetUpOnMainThread() OVERRIDE {
51    TabScrubber::GetInstance()->set_activation_delay(0);
52
53    // Disable external monitor scaling of coordinates.
54    ash::Shell* shell = ash::Shell::GetInstance();
55    shell->event_transformation_handler()->set_transformation_mode(
56        ash::internal::EventTransformationHandler::TRANSFORM_NONE);
57  }
58
59  virtual void CleanUpOnMainThread() OVERRIDE {
60    browser()->tab_strip_model()->RemoveObserver(this);
61  }
62
63  TabStrip* GetTabStrip(Browser* browser) {
64    aura::Window* window = browser->window()->GetNativeWindow();
65    return BrowserView::GetBrowserViewForNativeWindow(window)->tabstrip();
66  }
67
68  int GetStartX(Browser* browser,
69                int index,
70                TabScrubber::Direction direction) {
71    return TabScrubber::GetStartPoint(
72        GetTabStrip(browser), index, direction).x();
73  }
74
75  int GetTabCenter(Browser* browser, int index) {
76    return GetTabStrip(browser)->tab_at(index)->bounds().CenterPoint().x();
77  }
78
79  // Sends one scroll event synchronously without initial or final
80  // fling events.
81  void SendScrubEvent(Browser* browser, int index) {
82    aura::Window* window = browser->window()->GetNativeWindow();
83    aura::Window* root = window->GetRootWindow();
84    aura::test::EventGenerator event_generator(root, window);
85    int active_index = browser->tab_strip_model()->active_index();
86    TabScrubber::Direction direction = index < active_index ?
87        TabScrubber::LEFT : TabScrubber::RIGHT;
88    int offset = GetTabCenter(browser, index) -
89        GetStartX(browser, active_index, direction);
90    ui::ScrollEvent scroll_event(ui::ET_SCROLL,
91                                 gfx::Point(0, 0),
92                                 ui::EventTimeForNow(),
93                                 0,
94                                 offset, 0,
95                                 offset, 0,
96                                 3);
97    event_generator.Dispatch(&scroll_event);
98  }
99
100  enum ScrubType {
101    EACH_TAB,
102    SKIP_TABS,
103    REPEAT_TABS,
104  };
105
106  // Sends asynchronous events and waits for tab at |index| to become
107  // active.
108  void Scrub(Browser* browser, int index, ScrubType scrub_type) {
109    aura::Window* window = browser->window()->GetNativeWindow();
110    aura::Window* root = window->GetRootWindow();
111    aura::test::EventGenerator event_generator(root, window);
112    event_generator.set_async(true);
113    activation_order_.clear();
114    int active_index = browser->tab_strip_model()->active_index();
115    ASSERT_NE(index, active_index);
116    ASSERT_TRUE(scrub_type != SKIP_TABS || ((index - active_index) % 2) == 0);
117    TabScrubber::Direction direction;
118    int increment;
119    if (index < active_index) {
120      direction = TabScrubber::LEFT;
121      increment = -1;
122    } else {
123      direction = TabScrubber::RIGHT;
124      increment = 1;
125    }
126    if (scrub_type == SKIP_TABS)
127      increment *= 2;
128    int last = GetStartX(browser, active_index, direction);
129    std::vector<gfx::Point> offsets;
130    for (int i = active_index + increment; i != (index + increment);
131        i += increment) {
132      int tab_center = GetTabCenter(browser, i);
133      offsets.push_back(gfx::Point(tab_center - last, 0));
134      last = GetStartX(browser, i, direction);
135      if (scrub_type == REPEAT_TABS) {
136        offsets.push_back(gfx::Point(increment, 0));
137        last += increment;
138      }
139    }
140    event_generator.ScrollSequence(gfx::Point(0, 0),
141                                   base::TimeDelta::FromMilliseconds(100),
142                                   offsets,
143                                   3);
144    RunUntilTabActive(browser, index);
145  }
146
147  // Sends events and waits for tab at |index| to become active
148  // if it's different from the currently active tab.
149  // If the active tab is expected to stay the same, send events
150  // synchronously (as we don't have anything to wait for).
151  void SendScrubSequence(Browser* browser, int x_offset, int index) {
152    aura::Window* window = browser->window()->GetNativeWindow();
153    aura::Window* root = window->GetRootWindow();
154    aura::test::EventGenerator event_generator(root, window);
155    bool wait_for_active = false;
156    if (index != browser->tab_strip_model()->active_index()) {
157      wait_for_active = true;
158      event_generator.set_async(true);
159    }
160    event_generator.ScrollSequence(gfx::Point(0, 0),
161                                   ui::EventTimeForNow(),
162                                   x_offset,
163                                   0,
164                                   1,
165                                   3);
166    if (wait_for_active)
167      RunUntilTabActive(browser, index);
168  }
169
170  void AddTabs(Browser* browser, int num_tabs) {
171    TabStrip* tab_strip = GetTabStrip(browser);
172    for (int i = 0; i < num_tabs; ++i)
173      AddBlankTabAndShow(browser);
174    ASSERT_EQ(num_tabs + 1, browser->tab_strip_model()->count());
175    ASSERT_EQ(num_tabs, browser->tab_strip_model()->active_index());
176    tab_strip->StopAnimating(true);
177    ASSERT_FALSE(tab_strip->IsAnimating());
178  }
179
180  // TabStripModelObserver overrides.
181  virtual void TabInsertedAt(content::WebContents* contents,
182                             int index,
183                             bool foreground) OVERRIDE {}
184  virtual void TabClosingAt(TabStripModel* tab_strip_model,
185                            content::WebContents* contents,
186                            int index) OVERRIDE {}
187  virtual void TabDetachedAt(content::WebContents* contents,
188                             int index) OVERRIDE {}
189  virtual void TabDeactivated(content::WebContents* contents) OVERRIDE {}
190  virtual void ActiveTabChanged(content::WebContents* old_contents,
191                                content::WebContents* new_contents,
192                                int index,
193                                int reason) OVERRIDE {
194    activation_order_.push_back(index);
195    if (index == target_index_)
196      quit_closure_.Run();
197  }
198
199  virtual void TabSelectionChanged(
200      TabStripModel* tab_strip_model,
201      const ui::ListSelectionModel& old_model) OVERRIDE {}
202  virtual void TabMoved(content::WebContents* contents,
203                        int from_index,
204                        int to_index) OVERRIDE {}
205  virtual void TabChangedAt(content::WebContents* contents,
206                            int index,
207                            TabChangeType change_type) OVERRIDE {}
208  virtual void TabReplacedAt(TabStripModel* tab_strip_model,
209                             content::WebContents* old_contents,
210                             content::WebContents* new_contents,
211                             int index) OVERRIDE {}
212  virtual void TabPinnedStateChanged(content::WebContents* contents,
213                                     int index) OVERRIDE {}
214  virtual void TabMiniStateChanged(content::WebContents* contents,
215                                   int index) OVERRIDE {
216  }
217  virtual void TabBlockedStateChanged(content::WebContents* contents,
218                                      int index) OVERRIDE {}
219  virtual void TabStripEmpty() OVERRIDE {}
220  virtual void TabStripModelDeleted() OVERRIDE {}
221
222  // History of tab activation. Scrub() resets it.
223  std::vector<int> activation_order_;
224
225 private:
226  void RunUntilTabActive(Browser* browser, int target) {
227    base::RunLoop run_loop;
228    quit_closure_ = content::GetQuitTaskForRunLoop(&run_loop);
229    browser->tab_strip_model()->AddObserver(this);
230    target_index_ = target;
231    content::RunThisRunLoop(&run_loop);
232    browser->tab_strip_model()->RemoveObserver(this);
233    target_index_ = -1;
234  }
235
236  base::Closure quit_closure_;
237  int target_index_;
238
239  DISALLOW_COPY_AND_ASSIGN(TabScrubberTest);
240};
241
242}  // namespace
243
244#if defined(OS_CHROMEOS)
245// Swipe a single tab in each direction.
246IN_PROC_BROWSER_TEST_F(TabScrubberTest, Single) {
247  AddTabs(browser(), 1);
248
249  Scrub(browser(), 0, EACH_TAB);
250  EXPECT_EQ(1U, activation_order_.size());
251  EXPECT_EQ(0, activation_order_[0]);
252  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
253
254  Scrub(browser(), 1, EACH_TAB);
255  EXPECT_EQ(1U, activation_order_.size());
256  EXPECT_EQ(1, activation_order_[0]);
257  EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
258}
259
260// Swipe 4 tabs in each direction. Each of the tabs should become active.
261IN_PROC_BROWSER_TEST_F(TabScrubberTest, Multi) {
262  AddTabs(browser(), 4);
263
264  Scrub(browser(), 0, EACH_TAB);
265  ASSERT_EQ(4U, activation_order_.size());
266  EXPECT_EQ(3, activation_order_[0]);
267  EXPECT_EQ(2, activation_order_[1]);
268  EXPECT_EQ(1, activation_order_[2]);
269  EXPECT_EQ(0, activation_order_[3]);
270  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
271
272  Scrub(browser(), 4, EACH_TAB);
273  ASSERT_EQ(4U, activation_order_.size());
274  EXPECT_EQ(1, activation_order_[0]);
275  EXPECT_EQ(2, activation_order_[1]);
276  EXPECT_EQ(3, activation_order_[2]);
277  EXPECT_EQ(4, activation_order_[3]);
278  EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
279}
280
281IN_PROC_BROWSER_TEST_F(TabScrubberTest, MultiBrowser) {
282  AddTabs(browser(), 1);
283  Scrub(browser(), 0, EACH_TAB);
284  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
285
286  Browser* browser2 = CreateBrowser(browser()->profile());
287  browser2->window()->Activate();
288  ASSERT_TRUE(browser2->window()->IsActive());
289  ASSERT_FALSE(browser()->window()->IsActive());
290  AddTabs(browser2, 1);
291
292  Scrub(browser2, 0, EACH_TAB);
293  EXPECT_EQ(0, browser2->tab_strip_model()->active_index());
294}
295
296// Swipe 4 tabs in each direction with an extra swipe within each. The same
297// 4 tabs should become active.
298IN_PROC_BROWSER_TEST_F(TabScrubberTest, Repeated) {
299  AddTabs(browser(), 4);
300
301  Scrub(browser(), 0, REPEAT_TABS);
302  ASSERT_EQ(4U, activation_order_.size());
303  EXPECT_EQ(3, activation_order_[0]);
304  EXPECT_EQ(2, activation_order_[1]);
305  EXPECT_EQ(1, activation_order_[2]);
306  EXPECT_EQ(0, activation_order_[3]);
307  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
308
309  Scrub(browser(), 4, REPEAT_TABS);
310  ASSERT_EQ(4U, activation_order_.size());
311  EXPECT_EQ(1, activation_order_[0]);
312  EXPECT_EQ(2, activation_order_[1]);
313  EXPECT_EQ(3, activation_order_[2]);
314  EXPECT_EQ(4, activation_order_[3]);
315  EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
316}
317
318// Confirm that we get the last tab made active when we skip tabs.
319// These tests have 5 total tabs. We will only received scroll events
320// on tabs 0, 2 and 4.
321IN_PROC_BROWSER_TEST_F(TabScrubberTest, Skipped) {
322  AddTabs(browser(), 4);
323
324  Scrub(browser(), 0, SKIP_TABS);
325  EXPECT_EQ(2U, activation_order_.size());
326  EXPECT_EQ(2, activation_order_[0]);
327  EXPECT_EQ(0, activation_order_[1]);
328  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
329
330  Scrub(browser(), 4, SKIP_TABS);
331  EXPECT_EQ(2U, activation_order_.size());
332  EXPECT_EQ(2, activation_order_[0]);
333  EXPECT_EQ(4, activation_order_[1]);
334  EXPECT_EQ(4, browser()->tab_strip_model()->active_index());
335}
336
337// Confirm that nothing happens when the swipe is small.
338IN_PROC_BROWSER_TEST_F(TabScrubberTest, NoChange) {
339  AddTabs(browser(), 1);
340
341  SendScrubSequence(browser(), -1, 1);
342  EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
343
344  SendScrubSequence(browser(), 1, 1);
345  EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
346}
347
348// Confirm that very large swipes go to the beginning and and of the tabstrip.
349IN_PROC_BROWSER_TEST_F(TabScrubberTest, Bounds) {
350  AddTabs(browser(), 1);
351
352  SendScrubSequence(browser(), -10000, 0);
353  EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
354
355  SendScrubSequence(browser(), 10000, 1);
356  EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
357}
358
359IN_PROC_BROWSER_TEST_F(TabScrubberTest, DeleteHighlighted) {
360  AddTabs(browser(), 1);
361
362  SendScrubEvent(browser(), 0);
363  EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
364  browser()->tab_strip_model()->CloseWebContentsAt(0,
365                                                   TabStripModel::CLOSE_NONE);
366  EXPECT_FALSE(TabScrubber::GetInstance()->IsActivationPending());
367}
368
369// Delete the currently highlighted tab. Make sure the TabScrubber is aware.
370IN_PROC_BROWSER_TEST_F(TabScrubberTest, DeleteBeforeHighlighted) {
371  AddTabs(browser(), 2);
372
373  SendScrubEvent(browser(), 1);
374  EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
375  browser()->tab_strip_model()->CloseWebContentsAt(0,
376                                                   TabStripModel::CLOSE_NONE);
377  EXPECT_EQ(0, TabScrubber::GetInstance()->highlighted_tab());
378}
379
380// Move the currently highlighted tab and confirm it gets tracked.
381IN_PROC_BROWSER_TEST_F(TabScrubberTest, MoveHighlighted) {
382  AddTabs(browser(), 1);
383
384  SendScrubEvent(browser(), 0);
385  EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
386  browser()->tab_strip_model()->ToggleSelectionAt(0);
387  browser()->tab_strip_model()->ToggleSelectionAt(1);
388  browser()->tab_strip_model()->MoveSelectedTabsTo(1);
389  EXPECT_EQ(1, TabScrubber::GetInstance()->highlighted_tab());
390}
391
392// Move a tab to before  the highlighted one.
393IN_PROC_BROWSER_TEST_F(TabScrubberTest, MoveBefore) {
394  AddTabs(browser(), 2);
395
396  SendScrubEvent(browser(), 1);
397  EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
398  browser()->tab_strip_model()->ToggleSelectionAt(0);
399  browser()->tab_strip_model()->ToggleSelectionAt(2);
400  browser()->tab_strip_model()->MoveSelectedTabsTo(2);
401  EXPECT_EQ(0, TabScrubber::GetInstance()->highlighted_tab());
402}
403
404// Move a tab to after the highlighted one.
405IN_PROC_BROWSER_TEST_F(TabScrubberTest, MoveAfter) {
406  AddTabs(browser(), 2);
407
408  SendScrubEvent(browser(), 1);
409  EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
410  browser()->tab_strip_model()->MoveSelectedTabsTo(0);
411  EXPECT_EQ(2, TabScrubber::GetInstance()->highlighted_tab());
412}
413
414// Close the browser while an activation is pending.
415IN_PROC_BROWSER_TEST_F(TabScrubberTest, CloseBrowser) {
416  AddTabs(browser(), 1);
417
418  SendScrubEvent(browser(), 0);
419  EXPECT_TRUE(TabScrubber::GetInstance()->IsActivationPending());
420  browser()->window()->Close();
421  EXPECT_FALSE(TabScrubber::GetInstance()->IsActivationPending());
422}
423
424#endif // OS_CHROMEOS
425