Home | History | Annotate | Download | only in tabs
      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_strip.h"
      6 
      7 #include "base/message_loop/message_loop.h"
      8 #include "chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h"
      9 #include "chrome/browser/ui/views/tabs/tab.h"
     10 #include "chrome/browser/ui/views/tabs/tab_strip.h"
     11 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
     12 #include "chrome/browser/ui/views/tabs/tab_strip_observer.h"
     13 #include "chrome/test/base/testing_profile.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 #include "ui/gfx/path.h"
     16 #include "ui/gfx/rect_conversions.h"
     17 #include "ui/gfx/skia_util.h"
     18 #include "ui/views/view.h"
     19 
     20 namespace {
     21 
     22 // Walks up the views hierarchy until it finds a tab view. It returns the
     23 // found tab view, on NULL if none is found.
     24 views::View* FindTabView(views::View* view) {
     25   views::View* current = view;
     26   while (current && strcmp(current->GetClassName(), Tab::kViewClassName)) {
     27     current = current->parent();
     28   }
     29   return current;
     30 }
     31 
     32 }  // namespace
     33 
     34 class TestTabStripObserver : public TabStripObserver {
     35  public:
     36   explicit TestTabStripObserver(TabStrip* tab_strip)
     37       : tab_strip_(tab_strip),
     38         last_tab_added_(-1),
     39         last_tab_removed_(-1),
     40         last_tab_moved_from_(-1),
     41         last_tab_moved_to_(-1),
     42         tabstrip_deleted_(false) {
     43     tab_strip_->AddObserver(this);
     44   }
     45 
     46   virtual ~TestTabStripObserver() {
     47     if (tab_strip_)
     48       tab_strip_->RemoveObserver(this);
     49   }
     50 
     51   int last_tab_added() const { return last_tab_added_; }
     52   int last_tab_removed() const { return last_tab_removed_; }
     53   int last_tab_moved_from() const { return last_tab_moved_from_; }
     54   int last_tab_moved_to() const { return last_tab_moved_to_; }
     55   bool tabstrip_deleted() const { return tabstrip_deleted_; }
     56 
     57  private:
     58   // TabStripObserver overrides.
     59   virtual void TabStripAddedTabAt(TabStrip* tab_strip, int index) OVERRIDE {
     60     last_tab_added_ = index;
     61   }
     62 
     63   virtual void TabStripMovedTab(TabStrip* tab_strip,
     64                                 int from_index,
     65                                 int to_index) OVERRIDE {
     66     last_tab_moved_from_ = from_index;
     67     last_tab_moved_to_ = to_index;
     68   }
     69 
     70   virtual void TabStripRemovedTabAt(TabStrip* tab_strip, int index) OVERRIDE {
     71     last_tab_removed_ = index;
     72   }
     73 
     74   virtual void TabStripDeleted(TabStrip* tab_strip) OVERRIDE {
     75     tabstrip_deleted_ = true;
     76     tab_strip_ = NULL;
     77   }
     78 
     79   TabStrip* tab_strip_;
     80   int last_tab_added_;
     81   int last_tab_removed_;
     82   int last_tab_moved_from_;
     83   int last_tab_moved_to_;
     84   bool tabstrip_deleted_;
     85 
     86   DISALLOW_COPY_AND_ASSIGN(TestTabStripObserver);
     87 };
     88 
     89 class TabStripTest : public testing::Test {
     90  public:
     91   TabStripTest()
     92       : controller_(new FakeBaseTabStripController) {
     93     tab_strip_ = new TabStrip(controller_);
     94     controller_->set_tab_strip(tab_strip_);
     95     // Do this to force TabStrip to create the buttons.
     96     parent_.AddChildView(tab_strip_);
     97   }
     98 
     99  protected:
    100   // Returns the rectangular hit test region of |tab| in |tab|'s local
    101   // coordinate space.
    102   gfx::Rect GetTabHitTestMask(Tab* tab) {
    103     gfx::Path mask;
    104     tab->GetHitTestMask(views::View::HIT_TEST_SOURCE_TOUCH, &mask);
    105     return gfx::ToEnclosingRect((gfx::SkRectToRectF(mask.getBounds())));
    106   }
    107 
    108   // Returns the rectangular hit test region of the tab close button of
    109   // |tab| in |tab|'s coordinate space (including padding if |padding|
    110   // is true).
    111   gfx::Rect GetTabCloseHitTestMask(Tab* tab, bool padding) {
    112     gfx::RectF bounds_f = tab->close_button_->GetContentsBounds();
    113     if (padding)
    114       bounds_f = tab->close_button_->GetLocalBounds();
    115     views::View::ConvertRectToTarget(tab->close_button_, tab, &bounds_f);
    116     return gfx::ToEnclosingRect(bounds_f);
    117   }
    118 
    119   // Checks whether |tab| contains |point_in_tabstrip_coords|, where the point
    120   // is in |tab_strip_| coordinates.
    121   bool IsPointInTab(Tab* tab, const gfx::Point& point_in_tabstrip_coords) {
    122     gfx::Point point_in_tab_coords(point_in_tabstrip_coords);
    123     views::View::ConvertPointToTarget(tab_strip_, tab, &point_in_tab_coords);
    124     return tab->HitTestPoint(point_in_tab_coords);
    125   }
    126 
    127   base::MessageLoopForUI ui_loop_;
    128   // Owned by TabStrip.
    129   FakeBaseTabStripController* controller_;
    130   // Owns |tab_strip_|.
    131   views::View parent_;
    132   TabStrip* tab_strip_;
    133 
    134  private:
    135   DISALLOW_COPY_AND_ASSIGN(TabStripTest);
    136 };
    137 
    138 TEST_F(TabStripTest, GetModelCount) {
    139   EXPECT_EQ(0, tab_strip_->GetModelCount());
    140 }
    141 
    142 TEST_F(TabStripTest, IsValidModelIndex) {
    143   EXPECT_FALSE(tab_strip_->IsValidModelIndex(0));
    144 }
    145 
    146 TEST_F(TabStripTest, tab_count) {
    147   EXPECT_EQ(0, tab_strip_->tab_count());
    148 }
    149 
    150 TEST_F(TabStripTest, AddTabAt) {
    151   TestTabStripObserver observer(tab_strip_);
    152   tab_strip_->AddTabAt(0, TabRendererData(), false);
    153   ASSERT_EQ(1, tab_strip_->tab_count());
    154   EXPECT_EQ(0, observer.last_tab_added());
    155   Tab* tab = tab_strip_->tab_at(0);
    156   EXPECT_FALSE(tab == NULL);
    157 }
    158 
    159 // Confirms that TabStripObserver::TabStripDeleted() is sent.
    160 TEST_F(TabStripTest, TabStripDeleted) {
    161   FakeBaseTabStripController* controller = new FakeBaseTabStripController;
    162   TabStrip* tab_strip = new TabStrip(controller);
    163   controller->set_tab_strip(tab_strip);
    164   TestTabStripObserver observer(tab_strip);
    165   delete tab_strip;
    166   EXPECT_TRUE(observer.tabstrip_deleted());
    167 }
    168 
    169 TEST_F(TabStripTest, MoveTab) {
    170   TestTabStripObserver observer(tab_strip_);
    171   tab_strip_->AddTabAt(0, TabRendererData(), false);
    172   tab_strip_->AddTabAt(1, TabRendererData(), false);
    173   tab_strip_->AddTabAt(2, TabRendererData(), false);
    174   ASSERT_EQ(3, tab_strip_->tab_count());
    175   EXPECT_EQ(2, observer.last_tab_added());
    176   Tab* tab = tab_strip_->tab_at(0);
    177   tab_strip_->MoveTab(0, 1, TabRendererData());
    178   EXPECT_EQ(0, observer.last_tab_moved_from());
    179   EXPECT_EQ(1, observer.last_tab_moved_to());
    180   EXPECT_EQ(tab, tab_strip_->tab_at(1));
    181 }
    182 
    183 // Verifies child views are deleted after an animation completes.
    184 TEST_F(TabStripTest, RemoveTab) {
    185   TestTabStripObserver observer(tab_strip_);
    186   controller_->AddTab(0, false);
    187   controller_->AddTab(1, false);
    188   const int child_view_count = tab_strip_->child_count();
    189   EXPECT_EQ(2, tab_strip_->tab_count());
    190   controller_->RemoveTab(0);
    191   EXPECT_EQ(0, observer.last_tab_removed());
    192   // When removing a tab the tabcount should immediately decrement.
    193   EXPECT_EQ(1, tab_strip_->tab_count());
    194   // But the number of views should remain the same (it's animatining closed).
    195   EXPECT_EQ(child_view_count, tab_strip_->child_count());
    196   tab_strip_->SetBounds(0, 0, 200, 20);
    197   // Layout at a different size should force the animation to end and delete
    198   // the tab that was removed.
    199   tab_strip_->Layout();
    200   EXPECT_EQ(child_view_count - 1, tab_strip_->child_count());
    201 
    202   // Remove the last tab to make sure things are cleaned up correctly when
    203   // the TabStrip is destroyed and an animation is ongoing.
    204   controller_->RemoveTab(0);
    205   EXPECT_EQ(0, observer.last_tab_removed());
    206 }
    207 
    208 TEST_F(TabStripTest, VisibilityInOverflow) {
    209   tab_strip_->SetBounds(0, 0, 200, 20);
    210 
    211   // The first tab added to a reasonable-width strip should be visible.  If we
    212   // add enough additional tabs, eventually one should be invisible due to
    213   // overflow.
    214   int invisible_tab_index = 0;
    215   for (; invisible_tab_index < 100; ++invisible_tab_index) {
    216     controller_->AddTab(invisible_tab_index, false);
    217     if (!tab_strip_->tab_at(invisible_tab_index)->visible())
    218       break;
    219   }
    220   EXPECT_GT(invisible_tab_index, 0);
    221   EXPECT_LT(invisible_tab_index, 100);
    222 
    223   // The tabs before the invisible tab should still be visible.
    224   for (int i = 0; i < invisible_tab_index; ++i)
    225     EXPECT_TRUE(tab_strip_->tab_at(i)->visible());
    226 
    227   // Enlarging the strip should result in the last tab becoming visible.
    228   tab_strip_->SetBounds(0, 0, 400, 20);
    229   EXPECT_TRUE(tab_strip_->tab_at(invisible_tab_index)->visible());
    230 
    231   // Shrinking it again should re-hide the last tab.
    232   tab_strip_->SetBounds(0, 0, 200, 20);
    233   EXPECT_FALSE(tab_strip_->tab_at(invisible_tab_index)->visible());
    234 
    235   // Shrinking it still more should make more tabs invisible, though not all.
    236   // All the invisible tabs should be at the end of the strip.
    237   tab_strip_->SetBounds(0, 0, 100, 20);
    238   int i = 0;
    239   for (; i < invisible_tab_index; ++i) {
    240     if (!tab_strip_->tab_at(i)->visible())
    241       break;
    242   }
    243   ASSERT_GT(i, 0);
    244   EXPECT_LT(i, invisible_tab_index);
    245   invisible_tab_index = i;
    246   for (int i = invisible_tab_index + 1; i < tab_strip_->tab_count(); ++i)
    247     EXPECT_FALSE(tab_strip_->tab_at(i)->visible());
    248 
    249   // When we're already in overflow, adding tabs at the beginning or end of
    250   // the strip should not change how many tabs are visible.
    251   controller_->AddTab(tab_strip_->tab_count(), false);
    252   EXPECT_TRUE(tab_strip_->tab_at(invisible_tab_index - 1)->visible());
    253   EXPECT_FALSE(tab_strip_->tab_at(invisible_tab_index)->visible());
    254   controller_->AddTab(0, false);
    255   EXPECT_TRUE(tab_strip_->tab_at(invisible_tab_index - 1)->visible());
    256   EXPECT_FALSE(tab_strip_->tab_at(invisible_tab_index)->visible());
    257 
    258   // If we remove enough tabs, all the tabs should be visible.
    259   for (int i = tab_strip_->tab_count() - 1; i >= invisible_tab_index; --i)
    260     controller_->RemoveTab(i);
    261   EXPECT_TRUE(tab_strip_->tab_at(tab_strip_->tab_count() - 1)->visible());
    262 }
    263 
    264 TEST_F(TabStripTest, ImmersiveMode) {
    265   // Immersive mode defaults to off.
    266   EXPECT_FALSE(tab_strip_->IsImmersiveStyle());
    267 
    268   // Tab strip defaults to normal tab height.
    269   int normal_height = Tab::GetMinimumUnselectedSize().height();
    270   EXPECT_EQ(normal_height, tab_strip_->GetPreferredSize().height());
    271 
    272   // Tab strip can toggle immersive mode.
    273   tab_strip_->SetImmersiveStyle(true);
    274   EXPECT_TRUE(tab_strip_->IsImmersiveStyle());
    275 
    276   // Now tabs have the immersive height.
    277   int immersive_height = Tab::GetImmersiveHeight();
    278   EXPECT_EQ(immersive_height, tab_strip_->GetPreferredSize().height());
    279 
    280   // Sanity-check immersive tabs are shorter than normal tabs.
    281   EXPECT_LT(immersive_height, normal_height);
    282 }
    283 
    284 // Creates a tab strip in stacked layout mode and verifies the correctness
    285 // of hit tests against the visible/occluded regions of a tab and
    286 // visible/occluded tab close buttons.
    287 TEST_F(TabStripTest, TabHitTestMaskWhenStacked) {
    288   tab_strip_->SetBounds(0, 0, 300, 20);
    289 
    290   controller_->AddTab(0, false);
    291   controller_->AddTab(1, true);
    292   controller_->AddTab(2, false);
    293   controller_->AddTab(3, false);
    294   ASSERT_EQ(4, tab_strip_->tab_count());
    295 
    296   Tab* left_tab = tab_strip_->tab_at(0);
    297   left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
    298 
    299   Tab* active_tab = tab_strip_->tab_at(1);
    300   active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
    301   ASSERT_TRUE(active_tab->IsActive());
    302 
    303   Tab* right_tab = tab_strip_->tab_at(2);
    304   right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
    305 
    306   Tab* most_right_tab = tab_strip_->tab_at(3);
    307   most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
    308                                           gfx::Size(200, 20)));
    309 
    310   // Switch to stacked layout mode and force a layout to ensure tabs stack.
    311   tab_strip_->SetStackedLayout(true);
    312   tab_strip_->DoLayout();
    313 
    314 
    315   // Tests involving |left_tab|, which has part of its bounds and its tab
    316   // close button completely occluded by |active_tab|.
    317 
    318   // Bounds of the tab's hit test mask.
    319   gfx::Rect tab_bounds = GetTabHitTestMask(left_tab);
    320   EXPECT_EQ(gfx::Rect(6, 2, 61, 27).ToString(), tab_bounds.ToString());
    321 
    322   // Bounds of the tab close button (without padding) in the tab's
    323   // coordinate space.
    324   gfx::Rect contents_bounds = GetTabCloseHitTestMask(left_tab, false);
    325   // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
    326   //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
    327 
    328   // Verify that the tab close button is completely occluded.
    329   EXPECT_FALSE(tab_bounds.Contains(contents_bounds));
    330 
    331   // Hit tests in the non-occuluded region of the tab.
    332   EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(6, 2, 2, 2)));
    333   EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(6, 2, 1, 1)));
    334   EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(30, 15, 1, 1)));
    335   EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(30, 15, 25, 35)));
    336   EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(-10, -5, 20, 30)));
    337 
    338   // Hit tests in the occluded region of the tab.
    339   EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(70, 15, 2, 2)));
    340   EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(70, -15, 30, 40)));
    341   EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(87, 20, 5, 3)));
    342 
    343   // Hit tests completely outside of the tab.
    344   EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(-20, -25, 1, 1)));
    345   EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(-20, -25, 3, 19)));
    346 
    347   // All hit tests against the tab close button should fail because
    348   // it is occluded by |active_tab|.
    349   views::ImageButton* left_close = left_tab->close_button_;
    350   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(1, 1, 1, 1)));
    351   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(1, 1, 5, 10)));
    352   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
    353   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(10, 10, 3, 4)));
    354 
    355 
    356   // Tests involving |active_tab|, which is completely visible.
    357 
    358   tab_bounds = GetTabHitTestMask(active_tab);
    359   EXPECT_EQ(gfx::Rect(6, 2, 108, 27).ToString(), tab_bounds.ToString());
    360   contents_bounds = GetTabCloseHitTestMask(active_tab, false);
    361   // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
    362   //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
    363 
    364   // Verify that the tab close button is not occluded.
    365   EXPECT_TRUE(tab_bounds.Contains(contents_bounds));
    366 
    367   // Bounds of the tab close button (without padding) in the tab's
    368   // coordinate space.
    369   gfx::Rect local_bounds = GetTabCloseHitTestMask(active_tab, true);
    370   EXPECT_EQ(gfx::Rect(81, 0, 39, 29).ToString(), local_bounds.ToString());
    371 
    372   // Hit tests within the tab.
    373   EXPECT_TRUE(active_tab->HitTestRect(gfx::Rect(30, 15, 1, 1)));
    374   EXPECT_TRUE(active_tab->HitTestRect(gfx::Rect(30, 15, 2, 2)));
    375 
    376   // Hit tests against the tab close button. Note that hit tests from either
    377   // mouse or touch should both fail if they are strictly contained within
    378   // the button's padding.
    379   views::ImageButton* active_close = active_tab->close_button_;
    380   EXPECT_FALSE(active_close->HitTestRect(gfx::Rect(1, 1, 1, 1)));
    381   EXPECT_FALSE(active_close->HitTestRect(gfx::Rect(1, 1, 2, 2)));
    382   EXPECT_TRUE(active_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
    383   EXPECT_TRUE(active_close->HitTestRect(gfx::Rect(10, 10, 25, 35)));
    384 
    385 
    386   // Tests involving |most_right_tab|, which has part of its bounds occluded
    387   // by |right_tab| but has its tab close button completely visible.
    388 
    389   tab_bounds = GetTabHitTestMask(most_right_tab);
    390   EXPECT_EQ(gfx::Rect(84, 2, 30, 27).ToString(), tab_bounds.ToString());
    391   contents_bounds = GetTabCloseHitTestMask(active_tab, false);
    392   // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
    393   //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
    394   local_bounds = GetTabCloseHitTestMask(active_tab, true);
    395   EXPECT_EQ(gfx::Rect(81, 0, 39, 29).ToString(), local_bounds.ToString());
    396 
    397   // Verify that the tab close button is not occluded.
    398   EXPECT_TRUE(tab_bounds.Contains(contents_bounds));
    399 
    400   // Hit tests in the occluded region of the tab.
    401   EXPECT_FALSE(most_right_tab->HitTestRect(gfx::Rect(20, 15, 1, 1)));
    402   EXPECT_FALSE(most_right_tab->HitTestRect(gfx::Rect(20, 15, 5, 6)));
    403 
    404   // Hit tests in the non-occluded region of the tab.
    405   EXPECT_TRUE(most_right_tab->HitTestRect(gfx::Rect(85, 15, 1, 1)));
    406   EXPECT_TRUE(most_right_tab->HitTestRect(gfx::Rect(85, 15, 2, 2)));
    407 
    408   // Hit tests against the tab close button. Note that hit tests from either
    409   // mouse or touch should both fail if they are strictly contained within
    410   // the button's padding.
    411   views::ImageButton* most_right_close = most_right_tab->close_button_;
    412   EXPECT_FALSE(most_right_close->HitTestRect(gfx::Rect(1, 1, 1, 1)));
    413   EXPECT_FALSE(most_right_close->HitTestRect(gfx::Rect(1, 1, 2, 2)));
    414   EXPECT_TRUE(most_right_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
    415   EXPECT_TRUE(most_right_close->HitTestRect(gfx::Rect(10, 10, 25, 35)));
    416   EXPECT_TRUE(most_right_close->HitTestRect(gfx::Rect(-10, 10, 25, 35)));
    417 }
    418 
    419 // Creates a tab strip in stacked layout mode and verifies the correctness
    420 // of hit tests against the visible/occluded region of a partially-occluded
    421 // tab close button.
    422 TEST_F(TabStripTest, ClippedTabCloseButton) {
    423   tab_strip_->SetBounds(0, 0, 220, 20);
    424 
    425   controller_->AddTab(0, false);
    426   controller_->AddTab(1, true);
    427   ASSERT_EQ(2, tab_strip_->tab_count());
    428 
    429   Tab* left_tab = tab_strip_->tab_at(0);
    430   left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
    431 
    432   Tab* active_tab = tab_strip_->tab_at(1);
    433   active_tab->SetBoundsRect(gfx::Rect(gfx::Point(180, 0), gfx::Size(200, 20)));
    434   ASSERT_TRUE(active_tab->IsActive());
    435 
    436   // Switch to stacked layout mode and force a layout to ensure tabs stack.
    437   tab_strip_->SetStackedLayout(true);
    438   tab_strip_->DoLayout();
    439 
    440 
    441   // Tests involving |left_tab|, which has part of its bounds and its tab
    442   // close button partially occluded by |active_tab|.
    443 
    444   // Bounds of the tab's hit test mask.
    445   gfx::Rect tab_bounds = GetTabHitTestMask(left_tab);
    446   EXPECT_EQ(gfx::Rect(6, 2, 91, 27).ToString(), tab_bounds.ToString());
    447 
    448   // Bounds of the tab close button (without padding) in the tab's
    449   // coordinate space.
    450   gfx::Rect contents_bounds = GetTabCloseHitTestMask(left_tab, false);
    451   // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
    452   //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
    453 
    454   // Verify that the tab close button is only partially occluded.
    455   EXPECT_FALSE(tab_bounds.Contains(contents_bounds));
    456   EXPECT_TRUE(tab_bounds.Intersects(contents_bounds));
    457 
    458   views::ImageButton* left_close = left_tab->close_button_;
    459 
    460   // Hit tests from mouse should return true if and only if the location
    461   // is within a visible region.
    462   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(2, 15, 1, 1)));
    463   EXPECT_TRUE(left_close->HitTestRect(gfx::Rect(3, 15, 1, 1)));
    464   EXPECT_TRUE(left_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
    465   EXPECT_TRUE(left_close->HitTestRect(gfx::Rect(15, 12, 1, 1)));
    466   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(16, 10, 1, 1)));
    467 
    468   // All hit tests from touch should return false because the button is
    469   // not fully visible.
    470   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(2, 15, 2, 2)));
    471   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(3, 15, 25, 25)));
    472   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(10, 10, 4, 5)));
    473   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(15, 12, 2, 2)));
    474   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(16, 10, 20, 20)));
    475 }
    476 
    477 TEST_F(TabStripTest, GetEventHandlerForOverlappingArea) {
    478   tab_strip_->SetBounds(0, 0, 1000, 20);
    479 
    480   controller_->AddTab(0, false);
    481   controller_->AddTab(1, true);
    482   controller_->AddTab(2, false);
    483   controller_->AddTab(3, false);
    484   ASSERT_EQ(4, tab_strip_->tab_count());
    485 
    486   // Verify that the active tab will be a tooltip handler for points that hit
    487   // it.
    488   Tab* left_tab = tab_strip_->tab_at(0);
    489   left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
    490 
    491   Tab* active_tab = tab_strip_->tab_at(1);
    492   active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
    493   ASSERT_TRUE(active_tab->IsActive());
    494 
    495   Tab* right_tab = tab_strip_->tab_at(2);
    496   right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
    497 
    498   Tab* most_right_tab = tab_strip_->tab_at(3);
    499   most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
    500                                           gfx::Size(200, 20)));
    501 
    502   // Test that active tabs gets events from area in which it overlaps with its
    503   // left neighbour.
    504   gfx::Point left_overlap(
    505       (active_tab->x() + left_tab->bounds().right() + 1) / 2,
    506       active_tab->bounds().bottom() - 1);
    507 
    508   // Sanity check that the point is in both active and left tab.
    509   ASSERT_TRUE(IsPointInTab(active_tab, left_overlap));
    510   ASSERT_TRUE(IsPointInTab(left_tab, left_overlap));
    511 
    512   EXPECT_EQ(active_tab,
    513             FindTabView(tab_strip_->GetEventHandlerForPoint(left_overlap)));
    514 
    515   // Test that active tabs gets events from area in which it overlaps with its
    516   // right neighbour.
    517   gfx::Point right_overlap((active_tab->bounds().right() + right_tab->x()) / 2,
    518                            active_tab->bounds().bottom() - 1);
    519 
    520   // Sanity check that the point is in both active and right tab.
    521   ASSERT_TRUE(IsPointInTab(active_tab, right_overlap));
    522   ASSERT_TRUE(IsPointInTab(right_tab, right_overlap));
    523 
    524   EXPECT_EQ(active_tab,
    525             FindTabView(tab_strip_->GetEventHandlerForPoint(right_overlap)));
    526 
    527   // Test that if neither of tabs is active, the left one is selected.
    528   gfx::Point unactive_overlap(
    529       (right_tab->x() + most_right_tab->bounds().right() + 1) / 2,
    530       right_tab->bounds().bottom() - 1);
    531 
    532   // Sanity check that the point is in both active and left tab.
    533   ASSERT_TRUE(IsPointInTab(right_tab, unactive_overlap));
    534   ASSERT_TRUE(IsPointInTab(most_right_tab, unactive_overlap));
    535 
    536   EXPECT_EQ(right_tab,
    537             FindTabView(tab_strip_->GetEventHandlerForPoint(unactive_overlap)));
    538 }
    539 
    540 TEST_F(TabStripTest, GetTooltipHandler) {
    541   tab_strip_->SetBounds(0, 0, 1000, 20);
    542 
    543   controller_->AddTab(0, false);
    544   controller_->AddTab(1, true);
    545   controller_->AddTab(2, false);
    546   controller_->AddTab(3, false);
    547   ASSERT_EQ(4, tab_strip_->tab_count());
    548 
    549   // Verify that the active tab will be a tooltip handler for points that hit
    550   // it.
    551   Tab* left_tab = tab_strip_->tab_at(0);
    552   left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
    553 
    554   Tab* active_tab = tab_strip_->tab_at(1);
    555   active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
    556   ASSERT_TRUE(active_tab->IsActive());
    557 
    558   Tab* right_tab = tab_strip_->tab_at(2);
    559   right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
    560 
    561   Tab* most_right_tab = tab_strip_->tab_at(3);
    562   most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
    563                                           gfx::Size(200, 20)));
    564 
    565   // Test that active_tab handles tooltips from area in which it overlaps with
    566   // its left neighbour.
    567   gfx::Point left_overlap(
    568       (active_tab->x() + left_tab->bounds().right() + 1) / 2,
    569       active_tab->bounds().bottom() - 1);
    570 
    571   // Sanity check that the point is in both active and left tab.
    572   ASSERT_TRUE(IsPointInTab(active_tab, left_overlap));
    573   ASSERT_TRUE(IsPointInTab(left_tab, left_overlap));
    574 
    575   EXPECT_EQ(active_tab,
    576             FindTabView(tab_strip_->GetTooltipHandlerForPoint(left_overlap)));
    577 
    578   // Test that active_tab handles tooltips from area in which it overlaps with
    579   // its right neighbour.
    580   gfx::Point right_overlap((active_tab->bounds().right() + right_tab->x()) / 2,
    581                            active_tab->bounds().bottom() - 1);
    582 
    583   // Sanity check that the point is in both active and right tab.
    584   ASSERT_TRUE(IsPointInTab(active_tab, right_overlap));
    585   ASSERT_TRUE(IsPointInTab(right_tab, right_overlap));
    586 
    587   EXPECT_EQ(active_tab,
    588             FindTabView(tab_strip_->GetTooltipHandlerForPoint(right_overlap)));
    589 
    590   // Test that if neither of tabs is active, the left one is selected.
    591   gfx::Point unactive_overlap(
    592       (right_tab->x() + most_right_tab->bounds().right() + 1) / 2,
    593       right_tab->bounds().bottom() - 1);
    594 
    595   // Sanity check that the point is in both active and left tab.
    596   ASSERT_TRUE(IsPointInTab(right_tab, unactive_overlap));
    597   ASSERT_TRUE(IsPointInTab(most_right_tab, unactive_overlap));
    598 
    599   EXPECT_EQ(
    600       right_tab,
    601       FindTabView(tab_strip_->GetTooltipHandlerForPoint(unactive_overlap)));
    602 
    603   // Confirm that tab strip doe not return tooltip handler for points that
    604   // don't hit it.
    605   EXPECT_FALSE(tab_strip_->GetTooltipHandlerForPoint(gfx::Point(-1, 2)));
    606 }
    607