• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
FindTabView(views::View * view)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:
TestTabStripObserver(TabStrip * tab_strip)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 
~TestTabStripObserver()46   virtual ~TestTabStripObserver() {
47     if (tab_strip_)
48       tab_strip_->RemoveObserver(this);
49   }
50 
last_tab_added() const51   int last_tab_added() const { return last_tab_added_; }
last_tab_removed() const52   int last_tab_removed() const { return last_tab_removed_; }
last_tab_moved_from() const53   int last_tab_moved_from() const { return last_tab_moved_from_; }
last_tab_moved_to() const54   int last_tab_moved_to() const { return last_tab_moved_to_; }
tabstrip_deleted() const55   bool tabstrip_deleted() const { return tabstrip_deleted_; }
56 
57  private:
58   // TabStripObserver overrides.
TabStripAddedTabAt(TabStrip * tab_strip,int index)59   virtual void TabStripAddedTabAt(TabStrip* tab_strip, int index) OVERRIDE {
60     last_tab_added_ = index;
61   }
62 
TabStripMovedTab(TabStrip * tab_strip,int from_index,int to_index)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 
TabStripRemovedTabAt(TabStrip * tab_strip,int index)70   virtual void TabStripRemovedTabAt(TabStrip* tab_strip, int index) OVERRIDE {
71     last_tab_removed_ = index;
72   }
73 
TabStripDeleted(TabStrip * tab_strip)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:
TabStripTest()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.
GetTabHitTestMask(Tab * tab)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).
GetTabCloseHitTestMask(Tab * tab,bool padding)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.
IsPointInTab(Tab * tab,const gfx::Point & point_in_tabstrip_coords)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 
TEST_F(TabStripTest,GetModelCount)138 TEST_F(TabStripTest, GetModelCount) {
139   EXPECT_EQ(0, tab_strip_->GetModelCount());
140 }
141 
TEST_F(TabStripTest,IsValidModelIndex)142 TEST_F(TabStripTest, IsValidModelIndex) {
143   EXPECT_FALSE(tab_strip_->IsValidModelIndex(0));
144 }
145 
TEST_F(TabStripTest,tab_count)146 TEST_F(TabStripTest, tab_count) {
147   EXPECT_EQ(0, tab_strip_->tab_count());
148 }
149 
TEST_F(TabStripTest,AddTabAt)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.
TEST_F(TabStripTest,TabStripDeleted)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 
TEST_F(TabStripTest,MoveTab)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.
TEST_F(TabStripTest,RemoveTab)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 
TEST_F(TabStripTest,VisibilityInOverflow)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 
TEST_F(TabStripTest,ImmersiveMode)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.
TEST_F(TabStripTest,TabHitTestMaskWhenStacked)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.
TEST_F(TabStripTest,ClippedTabCloseButton)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 
TEST_F(TabStripTest,GetEventHandlerForOverlappingArea)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 
TEST_F(TabStripTest,GetTooltipHandler)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