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, CreateTabForDragging) {
    151   // Any result is good, as long as it doesn't crash.
    152   scoped_ptr<Tab> tab(tab_strip_->CreateTabForDragging());
    153 }
    154 
    155 TEST_F(TabStripTest, AddTabAt) {
    156   TestTabStripObserver observer(tab_strip_);
    157   tab_strip_->AddTabAt(0, TabRendererData(), false);
    158   ASSERT_EQ(1, tab_strip_->tab_count());
    159   EXPECT_EQ(0, observer.last_tab_added());
    160   Tab* tab = tab_strip_->tab_at(0);
    161   EXPECT_FALSE(tab == NULL);
    162 }
    163 
    164 // Confirms that TabStripObserver::TabStripDeleted() is sent.
    165 TEST_F(TabStripTest, TabStripDeleted) {
    166   FakeBaseTabStripController* controller = new FakeBaseTabStripController;
    167   TabStrip* tab_strip = new TabStrip(controller);
    168   controller->set_tab_strip(tab_strip);
    169   TestTabStripObserver observer(tab_strip);
    170   delete tab_strip;
    171   EXPECT_TRUE(observer.tabstrip_deleted());
    172 }
    173 
    174 TEST_F(TabStripTest, MoveTab) {
    175   TestTabStripObserver observer(tab_strip_);
    176   tab_strip_->AddTabAt(0, TabRendererData(), false);
    177   tab_strip_->AddTabAt(1, TabRendererData(), false);
    178   tab_strip_->AddTabAt(2, TabRendererData(), false);
    179   ASSERT_EQ(3, tab_strip_->tab_count());
    180   EXPECT_EQ(2, observer.last_tab_added());
    181   Tab* tab = tab_strip_->tab_at(0);
    182   tab_strip_->MoveTab(0, 1, TabRendererData());
    183   EXPECT_EQ(0, observer.last_tab_moved_from());
    184   EXPECT_EQ(1, observer.last_tab_moved_to());
    185   EXPECT_EQ(tab, tab_strip_->tab_at(1));
    186 }
    187 
    188 // Verifies child views are deleted after an animation completes.
    189 TEST_F(TabStripTest, RemoveTab) {
    190   TestTabStripObserver observer(tab_strip_);
    191   controller_->AddTab(0, false);
    192   controller_->AddTab(1, false);
    193   const int child_view_count = tab_strip_->child_count();
    194   EXPECT_EQ(2, tab_strip_->tab_count());
    195   controller_->RemoveTab(0);
    196   EXPECT_EQ(0, observer.last_tab_removed());
    197   // When removing a tab the tabcount should immediately decrement.
    198   EXPECT_EQ(1, tab_strip_->tab_count());
    199   // But the number of views should remain the same (it's animatining closed).
    200   EXPECT_EQ(child_view_count, tab_strip_->child_count());
    201   tab_strip_->SetBounds(0, 0, 200, 20);
    202   // Layout at a different size should force the animation to end and delete
    203   // the tab that was removed.
    204   tab_strip_->Layout();
    205   EXPECT_EQ(child_view_count - 1, tab_strip_->child_count());
    206 
    207   // Remove the last tab to make sure things are cleaned up correctly when
    208   // the TabStrip is destroyed and an animation is ongoing.
    209   controller_->RemoveTab(0);
    210   EXPECT_EQ(0, observer.last_tab_removed());
    211 }
    212 
    213 TEST_F(TabStripTest, ImmersiveMode) {
    214   // Immersive mode defaults to off.
    215   EXPECT_FALSE(tab_strip_->IsImmersiveStyle());
    216 
    217   // Tab strip defaults to normal tab height.
    218   int normal_height = Tab::GetMinimumUnselectedSize().height();
    219   EXPECT_EQ(normal_height, tab_strip_->GetPreferredSize().height());
    220 
    221   // Tab strip can toggle immersive mode.
    222   tab_strip_->SetImmersiveStyle(true);
    223   EXPECT_TRUE(tab_strip_->IsImmersiveStyle());
    224 
    225   // Now tabs have the immersive height.
    226   int immersive_height = Tab::GetImmersiveHeight();
    227   EXPECT_EQ(immersive_height, tab_strip_->GetPreferredSize().height());
    228 
    229   // Sanity-check immersive tabs are shorter than normal tabs.
    230   EXPECT_LT(immersive_height, normal_height);
    231 }
    232 
    233 // Creates a tab strip in stacked layout mode and verifies the correctness
    234 // of hit tests against the visible/occluded regions of a tab and
    235 // visible/occluded tab close buttons.
    236 TEST_F(TabStripTest, TabHitTestMaskWhenStacked) {
    237   tab_strip_->SetBounds(0, 0, 300, 20);
    238 
    239   controller_->AddTab(0, false);
    240   controller_->AddTab(1, true);
    241   controller_->AddTab(2, false);
    242   controller_->AddTab(3, false);
    243   ASSERT_EQ(4, tab_strip_->tab_count());
    244 
    245   Tab* left_tab = tab_strip_->tab_at(0);
    246   left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
    247 
    248   Tab* active_tab = tab_strip_->tab_at(1);
    249   active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
    250   ASSERT_TRUE(active_tab->IsActive());
    251 
    252   Tab* right_tab = tab_strip_->tab_at(2);
    253   right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
    254 
    255   Tab* most_right_tab = tab_strip_->tab_at(3);
    256   most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
    257                                           gfx::Size(200, 20)));
    258 
    259   // Switch to stacked layout mode and force a layout to ensure tabs stack.
    260   tab_strip_->SetLayoutType(TAB_STRIP_LAYOUT_STACKED, false);
    261   tab_strip_->DoLayout();
    262 
    263 
    264   // Tests involving |left_tab|, which has part of its bounds and its tab
    265   // close button completely occluded by |active_tab|.
    266 
    267   // Bounds of the tab's hit test mask.
    268   gfx::Rect tab_bounds = GetTabHitTestMask(left_tab);
    269   EXPECT_EQ(gfx::Rect(6, 2, 61, 27).ToString(), tab_bounds.ToString());
    270 
    271   // Bounds of the tab close button (without padding) in the tab's
    272   // coordinate space.
    273   gfx::Rect contents_bounds = GetTabCloseHitTestMask(left_tab, false);
    274   // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
    275   //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
    276 
    277   // Verify that the tab close button is completely occluded.
    278   EXPECT_FALSE(tab_bounds.Contains(contents_bounds));
    279 
    280   // Hit tests in the non-occuluded region of the tab.
    281   EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(6, 2, 2, 2)));
    282   EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(6, 2, 1, 1)));
    283   EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(30, 15, 1, 1)));
    284   EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(30, 15, 25, 35)));
    285   EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(-10, -5, 20, 30)));
    286 
    287   // Hit tests in the occluded region of the tab.
    288   EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(70, 15, 2, 2)));
    289   EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(70, -15, 30, 40)));
    290   EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(87, 20, 5, 3)));
    291 
    292   // Hit tests completely outside of the tab.
    293   EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(-20, -25, 1, 1)));
    294   EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(-20, -25, 3, 19)));
    295 
    296   // All hit tests against the tab close button should fail because
    297   // it is occluded by |active_tab|.
    298   views::ImageButton* left_close = left_tab->close_button_;
    299   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(1, 1, 1, 1)));
    300   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(1, 1, 5, 10)));
    301   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
    302   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(10, 10, 3, 4)));
    303 
    304 
    305   // Tests involving |active_tab|, which is completely visible.
    306 
    307   tab_bounds = GetTabHitTestMask(active_tab);
    308   EXPECT_EQ(gfx::Rect(6, 2, 108, 27).ToString(), tab_bounds.ToString());
    309   contents_bounds = GetTabCloseHitTestMask(active_tab, false);
    310   // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
    311   //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
    312 
    313   // Verify that the tab close button is not occluded.
    314   EXPECT_TRUE(tab_bounds.Contains(contents_bounds));
    315 
    316   // Bounds of the tab close button (without padding) in the tab's
    317   // coordinate space.
    318   gfx::Rect local_bounds = GetTabCloseHitTestMask(active_tab, true);
    319   EXPECT_EQ(gfx::Rect(81, 0, 39, 29).ToString(), local_bounds.ToString());
    320 
    321   // Hit tests within the tab.
    322   EXPECT_TRUE(active_tab->HitTestRect(gfx::Rect(30, 15, 1, 1)));
    323   EXPECT_TRUE(active_tab->HitTestRect(gfx::Rect(30, 15, 2, 2)));
    324 
    325   // Hit tests against the tab close button. Note that hit tests from either
    326   // mouse or touch should both fail if they are strictly contained within
    327   // the button's padding.
    328   views::ImageButton* active_close = active_tab->close_button_;
    329   EXPECT_FALSE(active_close->HitTestRect(gfx::Rect(1, 1, 1, 1)));
    330   EXPECT_FALSE(active_close->HitTestRect(gfx::Rect(1, 1, 2, 2)));
    331   EXPECT_TRUE(active_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
    332   EXPECT_TRUE(active_close->HitTestRect(gfx::Rect(10, 10, 25, 35)));
    333 
    334 
    335   // Tests involving |most_right_tab|, which has part of its bounds occluded
    336   // by |right_tab| but has its tab close button completely visible.
    337 
    338   tab_bounds = GetTabHitTestMask(most_right_tab);
    339   EXPECT_EQ(gfx::Rect(84, 2, 30, 27).ToString(), tab_bounds.ToString());
    340   contents_bounds = GetTabCloseHitTestMask(active_tab, false);
    341   // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
    342   //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
    343   local_bounds = GetTabCloseHitTestMask(active_tab, true);
    344   EXPECT_EQ(gfx::Rect(81, 0, 39, 29).ToString(), local_bounds.ToString());
    345 
    346   // Verify that the tab close button is not occluded.
    347   EXPECT_TRUE(tab_bounds.Contains(contents_bounds));
    348 
    349   // Hit tests in the occluded region of the tab.
    350   EXPECT_FALSE(most_right_tab->HitTestRect(gfx::Rect(20, 15, 1, 1)));
    351   EXPECT_FALSE(most_right_tab->HitTestRect(gfx::Rect(20, 15, 5, 6)));
    352 
    353   // Hit tests in the non-occluded region of the tab.
    354   EXPECT_TRUE(most_right_tab->HitTestRect(gfx::Rect(85, 15, 1, 1)));
    355   EXPECT_TRUE(most_right_tab->HitTestRect(gfx::Rect(85, 15, 2, 2)));
    356 
    357   // Hit tests against the tab close button. Note that hit tests from either
    358   // mouse or touch should both fail if they are strictly contained within
    359   // the button's padding.
    360   views::ImageButton* most_right_close = most_right_tab->close_button_;
    361   EXPECT_FALSE(most_right_close->HitTestRect(gfx::Rect(1, 1, 1, 1)));
    362   EXPECT_FALSE(most_right_close->HitTestRect(gfx::Rect(1, 1, 2, 2)));
    363   EXPECT_TRUE(most_right_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
    364   EXPECT_TRUE(most_right_close->HitTestRect(gfx::Rect(10, 10, 25, 35)));
    365   EXPECT_TRUE(most_right_close->HitTestRect(gfx::Rect(-10, 10, 25, 35)));
    366 }
    367 
    368 // Creates a tab strip in stacked layout mode and verifies the correctness
    369 // of hit tests against the visible/occluded region of a partially-occluded
    370 // tab close button.
    371 TEST_F(TabStripTest, ClippedTabCloseButton) {
    372   tab_strip_->SetBounds(0, 0, 220, 20);
    373 
    374   controller_->AddTab(0, false);
    375   controller_->AddTab(1, true);
    376   ASSERT_EQ(2, tab_strip_->tab_count());
    377 
    378   Tab* left_tab = tab_strip_->tab_at(0);
    379   left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
    380 
    381   Tab* active_tab = tab_strip_->tab_at(1);
    382   active_tab->SetBoundsRect(gfx::Rect(gfx::Point(180, 0), gfx::Size(200, 20)));
    383   ASSERT_TRUE(active_tab->IsActive());
    384 
    385   // Switch to stacked layout mode and force a layout to ensure tabs stack.
    386   tab_strip_->SetLayoutType(TAB_STRIP_LAYOUT_STACKED, false);
    387   tab_strip_->DoLayout();
    388 
    389 
    390   // Tests involving |left_tab|, which has part of its bounds and its tab
    391   // close button partially occluded by |active_tab|.
    392 
    393   // Bounds of the tab's hit test mask.
    394   gfx::Rect tab_bounds = GetTabHitTestMask(left_tab);
    395   EXPECT_EQ(gfx::Rect(6, 2, 91, 27).ToString(), tab_bounds.ToString());
    396 
    397   // Bounds of the tab close button (without padding) in the tab's
    398   // coordinate space.
    399   gfx::Rect contents_bounds = GetTabCloseHitTestMask(left_tab, false);
    400   // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
    401   //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
    402 
    403   // Verify that the tab close button is only partially occluded.
    404   EXPECT_FALSE(tab_bounds.Contains(contents_bounds));
    405   EXPECT_TRUE(tab_bounds.Intersects(contents_bounds));
    406 
    407   views::ImageButton* left_close = left_tab->close_button_;
    408 
    409   // Hit tests from mouse should return true if and only if the location
    410   // is within a visible region.
    411   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(2, 15, 1, 1)));
    412   EXPECT_TRUE(left_close->HitTestRect(gfx::Rect(3, 15, 1, 1)));
    413   EXPECT_TRUE(left_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
    414   EXPECT_TRUE(left_close->HitTestRect(gfx::Rect(15, 12, 1, 1)));
    415   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(16, 10, 1, 1)));
    416 
    417   // All hit tests from touch should return false because the button is
    418   // not fully visible.
    419   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(2, 15, 2, 2)));
    420   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(3, 15, 25, 25)));
    421   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(10, 10, 4, 5)));
    422   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(15, 12, 2, 2)));
    423   EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(16, 10, 20, 20)));
    424 }
    425 
    426 TEST_F(TabStripTest, GetEventHandlerForOverlappingArea) {
    427   tab_strip_->SetBounds(0, 0, 1000, 20);
    428 
    429   controller_->AddTab(0, false);
    430   controller_->AddTab(1, true);
    431   controller_->AddTab(2, false);
    432   controller_->AddTab(3, false);
    433   ASSERT_EQ(4, tab_strip_->tab_count());
    434 
    435   // Verify that the active tab will be a tooltip handler for points that hit
    436   // it.
    437   Tab* left_tab = tab_strip_->tab_at(0);
    438   left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
    439 
    440   Tab* active_tab = tab_strip_->tab_at(1);
    441   active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
    442   ASSERT_TRUE(active_tab->IsActive());
    443 
    444   Tab* right_tab = tab_strip_->tab_at(2);
    445   right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
    446 
    447   Tab* most_right_tab = tab_strip_->tab_at(3);
    448   most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
    449                                           gfx::Size(200, 20)));
    450 
    451   // Test that active tabs gets events from area in which it overlaps with its
    452   // left neighbour.
    453   gfx::Point left_overlap(
    454       (active_tab->x() + left_tab->bounds().right() + 1) / 2,
    455       active_tab->bounds().bottom() - 1);
    456 
    457   // Sanity check that the point is in both active and left tab.
    458   ASSERT_TRUE(IsPointInTab(active_tab, left_overlap));
    459   ASSERT_TRUE(IsPointInTab(left_tab, left_overlap));
    460 
    461   EXPECT_EQ(active_tab,
    462             FindTabView(tab_strip_->GetEventHandlerForPoint(left_overlap)));
    463 
    464   // Test that active tabs gets events from area in which it overlaps with its
    465   // right neighbour.
    466   gfx::Point right_overlap((active_tab->bounds().right() + right_tab->x()) / 2,
    467                            active_tab->bounds().bottom() - 1);
    468 
    469   // Sanity check that the point is in both active and right tab.
    470   ASSERT_TRUE(IsPointInTab(active_tab, right_overlap));
    471   ASSERT_TRUE(IsPointInTab(right_tab, right_overlap));
    472 
    473   EXPECT_EQ(active_tab,
    474             FindTabView(tab_strip_->GetEventHandlerForPoint(right_overlap)));
    475 
    476   // Test that if neither of tabs is active, the left one is selected.
    477   gfx::Point unactive_overlap(
    478       (right_tab->x() + most_right_tab->bounds().right() + 1) / 2,
    479       right_tab->bounds().bottom() - 1);
    480 
    481   // Sanity check that the point is in both active and left tab.
    482   ASSERT_TRUE(IsPointInTab(right_tab, unactive_overlap));
    483   ASSERT_TRUE(IsPointInTab(most_right_tab, unactive_overlap));
    484 
    485   EXPECT_EQ(right_tab,
    486             FindTabView(tab_strip_->GetEventHandlerForPoint(unactive_overlap)));
    487 }
    488 
    489 TEST_F(TabStripTest, GetTooltipHandler) {
    490   tab_strip_->SetBounds(0, 0, 1000, 20);
    491 
    492   controller_->AddTab(0, false);
    493   controller_->AddTab(1, true);
    494   controller_->AddTab(2, false);
    495   controller_->AddTab(3, false);
    496   ASSERT_EQ(4, tab_strip_->tab_count());
    497 
    498   // Verify that the active tab will be a tooltip handler for points that hit
    499   // it.
    500   Tab* left_tab = tab_strip_->tab_at(0);
    501   left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
    502 
    503   Tab* active_tab = tab_strip_->tab_at(1);
    504   active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
    505   ASSERT_TRUE(active_tab->IsActive());
    506 
    507   Tab* right_tab = tab_strip_->tab_at(2);
    508   right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
    509 
    510   Tab* most_right_tab = tab_strip_->tab_at(3);
    511   most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
    512                                           gfx::Size(200, 20)));
    513 
    514   // Test that active_tab handles tooltips from area in which it overlaps with
    515   // its left neighbour.
    516   gfx::Point left_overlap(
    517       (active_tab->x() + left_tab->bounds().right() + 1) / 2,
    518       active_tab->bounds().bottom() - 1);
    519 
    520   // Sanity check that the point is in both active and left tab.
    521   ASSERT_TRUE(IsPointInTab(active_tab, left_overlap));
    522   ASSERT_TRUE(IsPointInTab(left_tab, left_overlap));
    523 
    524   EXPECT_EQ(active_tab,
    525             FindTabView(tab_strip_->GetTooltipHandlerForPoint(left_overlap)));
    526 
    527   // Test that active_tab handles tooltips from area in which it overlaps with
    528   // its right neighbour.
    529   gfx::Point right_overlap((active_tab->bounds().right() + right_tab->x()) / 2,
    530                            active_tab->bounds().bottom() - 1);
    531 
    532   // Sanity check that the point is in both active and right tab.
    533   ASSERT_TRUE(IsPointInTab(active_tab, right_overlap));
    534   ASSERT_TRUE(IsPointInTab(right_tab, right_overlap));
    535 
    536   EXPECT_EQ(active_tab,
    537             FindTabView(tab_strip_->GetTooltipHandlerForPoint(right_overlap)));
    538 
    539   // Test that if neither of tabs is active, the left one is selected.
    540   gfx::Point unactive_overlap(
    541       (right_tab->x() + most_right_tab->bounds().right() + 1) / 2,
    542       right_tab->bounds().bottom() - 1);
    543 
    544   // Sanity check that the point is in both active and left tab.
    545   ASSERT_TRUE(IsPointInTab(right_tab, unactive_overlap));
    546   ASSERT_TRUE(IsPointInTab(most_right_tab, unactive_overlap));
    547 
    548   EXPECT_EQ(
    549       right_tab,
    550       FindTabView(tab_strip_->GetTooltipHandlerForPoint(unactive_overlap)));
    551 
    552   // Confirm that tab strip doe not return tooltip handler for points that
    553   // don't hit it.
    554   EXPECT_FALSE(tab_strip_->GetTooltipHandlerForPoint(gfx::Point(-1, 2)));
    555 }
    556