Home | History | Annotate | Download | only in ash
      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/base/events/event_utils.h"
     29 
     30 #if defined(OS_CHROMEOS)
     31 #include "chromeos/chromeos_switches.h"
     32 #endif
     33 
     34 namespace {
     35 
     36 class 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::RootWindow* 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::RootWindow* 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::RootWindow* 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.
    246 IN_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.
    261 IN_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 
    281 IN_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.
    298 IN_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.
    321 IN_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.
    338 IN_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.
    349 IN_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 
    359 IN_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.
    370 IN_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.
    381 IN_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.
    393 IN_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.
    405 IN_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.
    415 IN_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