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.
5 #include "chrome/browser/ui/views/tabs/tab_strip.h"
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/canvas.h"
16 #include "ui/gfx/geometry/rect_conversions.h"
17 #include "ui/gfx/path.h"
18 #include "ui/gfx/skia_util.h"
19 #include "ui/views/test/views_test_base.h"
20 #include "ui/views/view.h"
21 #include "ui/views/view_targeter.h"
22 #include "ui/views/widget/widget.h"
26 // Walks up the views hierarchy until it finds a tab view. It returns the
27 // found tab view, on NULL if none is found.
28 views::View
* FindTabView(views::View
* view
) {
29 views::View
* current
= view
;
30 while (current
&& strcmp(current
->GetClassName(), Tab::kViewClassName
)) {
31 current
= current
->parent();
38 class TestTabStripObserver
: public TabStripObserver
{
40 explicit TestTabStripObserver(TabStrip
* tab_strip
)
41 : tab_strip_(tab_strip
),
43 last_tab_removed_(-1),
44 last_tab_moved_from_(-1),
45 last_tab_moved_to_(-1),
46 tabstrip_deleted_(false) {
47 tab_strip_
->AddObserver(this);
50 ~TestTabStripObserver() override
{
52 tab_strip_
->RemoveObserver(this);
55 int last_tab_added() const { return last_tab_added_
; }
56 int last_tab_removed() const { return last_tab_removed_
; }
57 int last_tab_moved_from() const { return last_tab_moved_from_
; }
58 int last_tab_moved_to() const { return last_tab_moved_to_
; }
59 bool tabstrip_deleted() const { return tabstrip_deleted_
; }
62 // TabStripObserver overrides.
63 void TabStripAddedTabAt(TabStrip
* tab_strip
, int index
) override
{
64 last_tab_added_
= index
;
67 void TabStripMovedTab(TabStrip
* tab_strip
,
69 int to_index
) override
{
70 last_tab_moved_from_
= from_index
;
71 last_tab_moved_to_
= to_index
;
74 void TabStripRemovedTabAt(TabStrip
* tab_strip
, int index
) override
{
75 last_tab_removed_
= index
;
78 void TabStripDeleted(TabStrip
* tab_strip
) override
{
79 tabstrip_deleted_
= true;
85 int last_tab_removed_
;
86 int last_tab_moved_from_
;
87 int last_tab_moved_to_
;
88 bool tabstrip_deleted_
;
90 DISALLOW_COPY_AND_ASSIGN(TestTabStripObserver
);
93 class TabStripTest
: public views::ViewsTestBase
{
100 ~TabStripTest() override
{}
102 void SetUp() override
{
103 views::ViewsTestBase::SetUp();
105 controller_
= new FakeBaseTabStripController
;
106 tab_strip_
= new TabStrip(controller_
);
107 controller_
->set_tab_strip(tab_strip_
);
108 // Do this to force TabStrip to create the buttons.
109 parent_
.AddChildView(tab_strip_
);
110 parent_
.set_owned_by_client();
112 widget_
.reset(new views::Widget
);
113 views::Widget::InitParams init_params
=
114 CreateParams(views::Widget::InitParams::TYPE_POPUP
);
115 init_params
.ownership
=
116 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
117 init_params
.bounds
= gfx::Rect(0, 0, 200, 200);
118 widget_
->Init(init_params
);
119 widget_
->SetContentsView(&parent_
);
122 void TearDown() override
{
124 views::ViewsTestBase::TearDown();
127 // Forces a call to OnPaint() for each tab in |tab_strip_| in order to
128 // trigger a layout, which is needed to update the visibility of tab
129 // close buttons after a tab switch or close. Note that painting does
130 // not occur in unit tests, which is why this helper is used.
131 void TriggerPaintOfAllTabs() {
133 for (int i
= 0; i
< tab_strip_
->tab_count(); ++i
)
134 tab_strip_
->tab_at(i
)->OnPaint(&canvas
);
138 // Returns the rectangular hit test region of |tab| in |tab|'s local
140 gfx::Rect
GetTabHitTestMask(Tab
* tab
) {
141 views::ViewTargeter
* targeter
= tab
->targeter();
143 views::MaskedTargeterDelegate
* delegate
=
144 static_cast<views::MaskedTargeterDelegate
*>(tab
);
147 bool valid_mask
= delegate
->GetHitTestMask(&mask
);
150 return gfx::ToEnclosingRect((gfx::SkRectToRectF(mask
.getBounds())));
153 // Returns the rectangular hit test region of the tab close button of
154 // |tab| in |tab|'s coordinate space (including padding if |padding|
156 gfx::Rect
GetTabCloseHitTestMask(Tab
* tab
, bool padding
) {
157 gfx::RectF bounds_f
= tab
->close_button_
->GetContentsBounds();
159 bounds_f
= tab
->close_button_
->GetLocalBounds();
160 views::View::ConvertRectToTarget(tab
->close_button_
, tab
, &bounds_f
);
161 return gfx::ToEnclosingRect(bounds_f
);
164 // Checks whether |tab| contains |point_in_tabstrip_coords|, where the point
165 // is in |tab_strip_| coordinates.
166 bool IsPointInTab(Tab
* tab
, const gfx::Point
& point_in_tabstrip_coords
) {
167 gfx::Point
point_in_tab_coords(point_in_tabstrip_coords
);
168 views::View::ConvertPointToTarget(tab_strip_
, tab
, &point_in_tab_coords
);
169 return tab
->HitTestPoint(point_in_tab_coords
);
172 // Owned by TabStrip.
173 FakeBaseTabStripController
* controller_
;
174 // Owns |tab_strip_|.
176 TabStrip
* tab_strip_
;
177 scoped_ptr
<views::Widget
> widget_
;
180 DISALLOW_COPY_AND_ASSIGN(TabStripTest
);
183 TEST_F(TabStripTest
, GetModelCount
) {
184 EXPECT_EQ(0, tab_strip_
->GetModelCount());
187 TEST_F(TabStripTest
, IsValidModelIndex
) {
188 EXPECT_FALSE(tab_strip_
->IsValidModelIndex(0));
191 TEST_F(TabStripTest
, tab_count
) {
192 EXPECT_EQ(0, tab_strip_
->tab_count());
195 TEST_F(TabStripTest
, AddTabAt
) {
196 TestTabStripObserver
observer(tab_strip_
);
197 tab_strip_
->AddTabAt(0, TabRendererData(), false);
198 ASSERT_EQ(1, tab_strip_
->tab_count());
199 EXPECT_EQ(0, observer
.last_tab_added());
200 Tab
* tab
= tab_strip_
->tab_at(0);
201 EXPECT_FALSE(tab
== NULL
);
204 // Confirms that TabStripObserver::TabStripDeleted() is sent.
205 TEST_F(TabStripTest
, TabStripDeleted
) {
206 FakeBaseTabStripController
* controller
= new FakeBaseTabStripController
;
207 TabStrip
* tab_strip
= new TabStrip(controller
);
208 controller
->set_tab_strip(tab_strip
);
209 TestTabStripObserver
observer(tab_strip
);
211 EXPECT_TRUE(observer
.tabstrip_deleted());
214 TEST_F(TabStripTest
, MoveTab
) {
215 TestTabStripObserver
observer(tab_strip_
);
216 tab_strip_
->AddTabAt(0, TabRendererData(), false);
217 tab_strip_
->AddTabAt(1, TabRendererData(), false);
218 tab_strip_
->AddTabAt(2, TabRendererData(), false);
219 ASSERT_EQ(3, tab_strip_
->tab_count());
220 EXPECT_EQ(2, observer
.last_tab_added());
221 Tab
* tab
= tab_strip_
->tab_at(0);
222 tab_strip_
->MoveTab(0, 1, TabRendererData());
223 EXPECT_EQ(0, observer
.last_tab_moved_from());
224 EXPECT_EQ(1, observer
.last_tab_moved_to());
225 EXPECT_EQ(tab
, tab_strip_
->tab_at(1));
228 // Verifies child views are deleted after an animation completes.
229 TEST_F(TabStripTest
, RemoveTab
) {
230 TestTabStripObserver
observer(tab_strip_
);
231 controller_
->AddTab(0, false);
232 controller_
->AddTab(1, false);
233 const int child_view_count
= tab_strip_
->child_count();
234 EXPECT_EQ(2, tab_strip_
->tab_count());
235 controller_
->RemoveTab(0);
236 EXPECT_EQ(0, observer
.last_tab_removed());
237 // When removing a tab the tabcount should immediately decrement.
238 EXPECT_EQ(1, tab_strip_
->tab_count());
239 // But the number of views should remain the same (it's animatining closed).
240 EXPECT_EQ(child_view_count
, tab_strip_
->child_count());
241 tab_strip_
->SetBounds(0, 0, 200, 20);
242 // Layout at a different size should force the animation to end and delete
243 // the tab that was removed.
244 tab_strip_
->Layout();
245 EXPECT_EQ(child_view_count
- 1, tab_strip_
->child_count());
247 // Remove the last tab to make sure things are cleaned up correctly when
248 // the TabStrip is destroyed and an animation is ongoing.
249 controller_
->RemoveTab(0);
250 EXPECT_EQ(0, observer
.last_tab_removed());
253 TEST_F(TabStripTest
, VisibilityInOverflow
) {
254 tab_strip_
->SetBounds(0, 0, 200, 20);
256 // The first tab added to a reasonable-width strip should be visible. If we
257 // add enough additional tabs, eventually one should be invisible due to
259 int invisible_tab_index
= 0;
260 for (; invisible_tab_index
< 100; ++invisible_tab_index
) {
261 controller_
->AddTab(invisible_tab_index
, false);
262 if (!tab_strip_
->tab_at(invisible_tab_index
)->visible())
265 EXPECT_GT(invisible_tab_index
, 0);
266 EXPECT_LT(invisible_tab_index
, 100);
268 // The tabs before the invisible tab should still be visible.
269 for (int i
= 0; i
< invisible_tab_index
; ++i
)
270 EXPECT_TRUE(tab_strip_
->tab_at(i
)->visible());
272 // Enlarging the strip should result in the last tab becoming visible.
273 tab_strip_
->SetBounds(0, 0, 400, 20);
274 EXPECT_TRUE(tab_strip_
->tab_at(invisible_tab_index
)->visible());
276 // Shrinking it again should re-hide the last tab.
277 tab_strip_
->SetBounds(0, 0, 200, 20);
278 EXPECT_FALSE(tab_strip_
->tab_at(invisible_tab_index
)->visible());
280 // Shrinking it still more should make more tabs invisible, though not all.
281 // All the invisible tabs should be at the end of the strip.
282 tab_strip_
->SetBounds(0, 0, 100, 20);
284 for (; i
< invisible_tab_index
; ++i
) {
285 if (!tab_strip_
->tab_at(i
)->visible())
289 EXPECT_LT(i
, invisible_tab_index
);
290 invisible_tab_index
= i
;
291 for (int i
= invisible_tab_index
+ 1; i
< tab_strip_
->tab_count(); ++i
)
292 EXPECT_FALSE(tab_strip_
->tab_at(i
)->visible());
294 // When we're already in overflow, adding tabs at the beginning or end of
295 // the strip should not change how many tabs are visible.
296 controller_
->AddTab(tab_strip_
->tab_count(), false);
297 EXPECT_TRUE(tab_strip_
->tab_at(invisible_tab_index
- 1)->visible());
298 EXPECT_FALSE(tab_strip_
->tab_at(invisible_tab_index
)->visible());
299 controller_
->AddTab(0, false);
300 EXPECT_TRUE(tab_strip_
->tab_at(invisible_tab_index
- 1)->visible());
301 EXPECT_FALSE(tab_strip_
->tab_at(invisible_tab_index
)->visible());
303 // If we remove enough tabs, all the tabs should be visible.
304 for (int i
= tab_strip_
->tab_count() - 1; i
>= invisible_tab_index
; --i
)
305 controller_
->RemoveTab(i
);
306 EXPECT_TRUE(tab_strip_
->tab_at(tab_strip_
->tab_count() - 1)->visible());
309 TEST_F(TabStripTest
, ImmersiveMode
) {
310 // Immersive mode defaults to off.
311 EXPECT_FALSE(tab_strip_
->IsImmersiveStyle());
313 // Tab strip defaults to normal tab height.
314 int normal_height
= Tab::GetMinimumUnselectedSize().height();
315 EXPECT_EQ(normal_height
, tab_strip_
->GetPreferredSize().height());
317 // Tab strip can toggle immersive mode.
318 tab_strip_
->SetImmersiveStyle(true);
319 EXPECT_TRUE(tab_strip_
->IsImmersiveStyle());
321 // Now tabs have the immersive height.
322 int immersive_height
= Tab::GetImmersiveHeight();
323 EXPECT_EQ(immersive_height
, tab_strip_
->GetPreferredSize().height());
325 // Sanity-check immersive tabs are shorter than normal tabs.
326 EXPECT_LT(immersive_height
, normal_height
);
329 // Creates a tab strip in stacked layout mode and verifies the correctness
330 // of hit tests against the visible/occluded regions of a tab and the tab
331 // close button of the active tab.
332 TEST_F(TabStripTest
, TabHitTestMaskWhenStacked
) {
333 tab_strip_
->SetBounds(0, 0, 300, 20);
335 controller_
->AddTab(0, false);
336 controller_
->AddTab(1, true);
337 controller_
->AddTab(2, false);
338 controller_
->AddTab(3, false);
339 ASSERT_EQ(4, tab_strip_
->tab_count());
341 Tab
* left_tab
= tab_strip_
->tab_at(0);
342 left_tab
->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
344 Tab
* active_tab
= tab_strip_
->tab_at(1);
345 active_tab
->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
346 ASSERT_TRUE(active_tab
->IsActive());
348 Tab
* right_tab
= tab_strip_
->tab_at(2);
349 right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
351 Tab
* most_right_tab
= tab_strip_
->tab_at(3);
352 most_right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
353 gfx::Size(200, 20)));
355 // Switch to stacked layout mode and force a layout to ensure tabs stack.
356 tab_strip_
->SetStackedLayout(true);
357 tab_strip_
->DoLayout();
360 // Tests involving |left_tab|, which has part of its bounds occluded by
363 // Bounds of the tab's hit test mask.
364 gfx::Rect tab_bounds
= GetTabHitTestMask(left_tab
);
365 EXPECT_EQ(gfx::Rect(6, 2, 61, 27).ToString(), tab_bounds
.ToString());
367 // Hit tests in the non-occuluded region of the tab.
368 EXPECT_TRUE(left_tab
->HitTestRect(gfx::Rect(6, 2, 2, 2)));
369 EXPECT_TRUE(left_tab
->HitTestRect(gfx::Rect(6, 2, 1, 1)));
370 EXPECT_TRUE(left_tab
->HitTestRect(gfx::Rect(30, 15, 1, 1)));
371 EXPECT_TRUE(left_tab
->HitTestRect(gfx::Rect(30, 15, 25, 35)));
372 EXPECT_TRUE(left_tab
->HitTestRect(gfx::Rect(-10, -5, 20, 30)));
374 // Hit tests in the occluded region of the tab.
375 EXPECT_FALSE(left_tab
->HitTestRect(gfx::Rect(70, 15, 2, 2)));
376 EXPECT_FALSE(left_tab
->HitTestRect(gfx::Rect(70, -15, 30, 40)));
377 EXPECT_FALSE(left_tab
->HitTestRect(gfx::Rect(87, 20, 5, 3)));
379 // Hit tests completely outside of the tab.
380 EXPECT_FALSE(left_tab
->HitTestRect(gfx::Rect(-20, -25, 1, 1)));
381 EXPECT_FALSE(left_tab
->HitTestRect(gfx::Rect(-20, -25, 3, 19)));
384 // Tests involving |active_tab|, which is completely visible.
386 tab_bounds
= GetTabHitTestMask(active_tab
);
387 EXPECT_EQ(gfx::Rect(6, 2, 108, 27).ToString(), tab_bounds
.ToString());
388 gfx::Rect contents_bounds
= GetTabCloseHitTestMask(active_tab
, false);
389 // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
390 // EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
392 // Verify that the tab close button is not occluded.
393 EXPECT_TRUE(tab_bounds
.Contains(contents_bounds
));
395 // Bounds of the tab close button (without padding) in the tab's
397 gfx::Rect local_bounds
= GetTabCloseHitTestMask(active_tab
, true);
398 EXPECT_EQ(gfx::Rect(81, 0, 39, 29).ToString(), local_bounds
.ToString());
400 // Hit tests within the tab.
401 EXPECT_TRUE(active_tab
->HitTestRect(gfx::Rect(30, 15, 1, 1)));
402 EXPECT_TRUE(active_tab
->HitTestRect(gfx::Rect(30, 15, 2, 2)));
404 // Hit tests against the tab close button. Note that hit tests from either
405 // mouse or touch should both fail if they are strictly contained within
406 // the button's padding.
407 views::ImageButton
* active_close
= active_tab
->close_button_
;
408 EXPECT_FALSE(active_close
->HitTestRect(gfx::Rect(1, 1, 1, 1)));
409 EXPECT_FALSE(active_close
->HitTestRect(gfx::Rect(1, 1, 2, 2)));
410 EXPECT_TRUE(active_close
->HitTestRect(gfx::Rect(10, 10, 1, 1)));
411 EXPECT_TRUE(active_close
->HitTestRect(gfx::Rect(10, 10, 25, 35)));
414 // Tests involving |most_right_tab|, which has part of its bounds occluded
417 tab_bounds
= GetTabHitTestMask(most_right_tab
);
418 EXPECT_EQ(gfx::Rect(84, 2, 30, 27).ToString(), tab_bounds
.ToString());
420 // Hit tests in the occluded region of the tab.
421 EXPECT_FALSE(most_right_tab
->HitTestRect(gfx::Rect(20, 15, 1, 1)));
422 EXPECT_FALSE(most_right_tab
->HitTestRect(gfx::Rect(20, 15, 5, 6)));
424 // Hit tests in the non-occluded region of the tab.
425 EXPECT_TRUE(most_right_tab
->HitTestRect(gfx::Rect(85, 15, 1, 1)));
426 EXPECT_TRUE(most_right_tab
->HitTestRect(gfx::Rect(85, 15, 2, 2)));
429 // Tests that the tab close buttons of non-active tabs are hidden when
430 // the tabstrip is in stacked tab mode.
431 TEST_F(TabStripTest
, TabCloseButtonVisibilityWhenStacked
) {
432 tab_strip_
->SetBounds(0, 0, 300, 20);
433 controller_
->AddTab(0, false);
434 controller_
->AddTab(1, true);
435 controller_
->AddTab(2, false);
436 ASSERT_EQ(3, tab_strip_
->tab_count());
438 Tab
* tab0
= tab_strip_
->tab_at(0);
439 Tab
* tab1
= tab_strip_
->tab_at(1);
440 ASSERT_TRUE(tab1
->IsActive());
441 Tab
* tab2
= tab_strip_
->tab_at(2);
443 // Ensure that all tab close buttons are initially visible.
444 TriggerPaintOfAllTabs();
445 EXPECT_TRUE(tab0
->showing_close_button_
);
446 EXPECT_TRUE(tab1
->showing_close_button_
);
447 EXPECT_TRUE(tab2
->showing_close_button_
);
449 // Enter stacked layout mode and verify this sets |touch_layout_|.
450 ASSERT_FALSE(tab_strip_
->touch_layout_
.get());
451 tab_strip_
->SetStackedLayout(true);
452 TriggerPaintOfAllTabs();
453 ASSERT_TRUE(tab_strip_
->touch_layout_
.get());
455 // Only the close button of the active tab should be visible in stacked
457 EXPECT_FALSE(tab0
->showing_close_button_
);
458 EXPECT_TRUE(tab1
->showing_close_button_
);
459 EXPECT_FALSE(tab2
->showing_close_button_
);
461 // An inactive tab added to the tabstrip should not show
462 // its tab close button.
463 controller_
->AddTab(3, false);
464 Tab
* tab3
= tab_strip_
->tab_at(3);
465 EXPECT_FALSE(tab0
->showing_close_button_
);
466 EXPECT_TRUE(tab1
->showing_close_button_
);
467 EXPECT_FALSE(tab2
->showing_close_button_
);
468 EXPECT_FALSE(tab3
->showing_close_button_
);
470 // After switching tabs, the previously-active tab should have its
471 // tab close button hidden and the newly-active tab should show
472 // its tab close button.
473 tab_strip_
->SelectTab(tab2
);
474 TriggerPaintOfAllTabs();
475 ASSERT_FALSE(tab1
->IsActive());
476 ASSERT_TRUE(tab2
->IsActive());
477 EXPECT_FALSE(tab0
->showing_close_button_
);
478 EXPECT_FALSE(tab1
->showing_close_button_
);
479 EXPECT_TRUE(tab2
->showing_close_button_
);
480 EXPECT_FALSE(tab3
->showing_close_button_
);
482 // After closing the active tab, the tab which becomes active should
483 // show its tab close button.
484 tab_strip_
->CloseTab(tab1
, CLOSE_TAB_FROM_TOUCH
);
486 ASSERT_TRUE(tab2
->IsActive());
487 TriggerPaintOfAllTabs();
488 EXPECT_FALSE(tab0
->showing_close_button_
);
489 EXPECT_TRUE(tab2
->showing_close_button_
);
490 EXPECT_FALSE(tab3
->showing_close_button_
);
492 // All tab close buttons should be shown when disengaging stacked tab mode.
493 tab_strip_
->SetStackedLayout(false);
494 TriggerPaintOfAllTabs();
495 ASSERT_FALSE(tab_strip_
->touch_layout_
.get());
496 EXPECT_TRUE(tab0
->showing_close_button_
);
497 EXPECT_TRUE(tab2
->showing_close_button_
);
498 EXPECT_TRUE(tab3
->showing_close_button_
);
501 TEST_F(TabStripTest
, GetEventHandlerForOverlappingArea
) {
502 tab_strip_
->SetBounds(0, 0, 1000, 20);
504 controller_
->AddTab(0, false);
505 controller_
->AddTab(1, true);
506 controller_
->AddTab(2, false);
507 controller_
->AddTab(3, false);
508 ASSERT_EQ(4, tab_strip_
->tab_count());
510 // Verify that the active tab will be a tooltip handler for points that hit
512 Tab
* left_tab
= tab_strip_
->tab_at(0);
513 left_tab
->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
515 Tab
* active_tab
= tab_strip_
->tab_at(1);
516 active_tab
->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
517 ASSERT_TRUE(active_tab
->IsActive());
519 Tab
* right_tab
= tab_strip_
->tab_at(2);
520 right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
522 Tab
* most_right_tab
= tab_strip_
->tab_at(3);
523 most_right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
524 gfx::Size(200, 20)));
526 // Test that active tabs gets events from area in which it overlaps with its
528 gfx::Point
left_overlap(
529 (active_tab
->x() + left_tab
->bounds().right() + 1) / 2,
530 active_tab
->bounds().bottom() - 1);
532 // Sanity check that the point is in both active and left tab.
533 ASSERT_TRUE(IsPointInTab(active_tab
, left_overlap
));
534 ASSERT_TRUE(IsPointInTab(left_tab
, left_overlap
));
536 EXPECT_EQ(active_tab
,
537 FindTabView(tab_strip_
->GetEventHandlerForPoint(left_overlap
)));
539 // Test that active tabs gets events from area in which it overlaps with its
541 gfx::Point
right_overlap((active_tab
->bounds().right() + right_tab
->x()) / 2,
542 active_tab
->bounds().bottom() - 1);
544 // Sanity check that the point is in both active and right tab.
545 ASSERT_TRUE(IsPointInTab(active_tab
, right_overlap
));
546 ASSERT_TRUE(IsPointInTab(right_tab
, right_overlap
));
548 EXPECT_EQ(active_tab
,
549 FindTabView(tab_strip_
->GetEventHandlerForPoint(right_overlap
)));
551 // Test that if neither of tabs is active, the left one is selected.
552 gfx::Point
unactive_overlap(
553 (right_tab
->x() + most_right_tab
->bounds().right() + 1) / 2,
554 right_tab
->bounds().bottom() - 1);
556 // Sanity check that the point is in both active and left tab.
557 ASSERT_TRUE(IsPointInTab(right_tab
, unactive_overlap
));
558 ASSERT_TRUE(IsPointInTab(most_right_tab
, unactive_overlap
));
561 FindTabView(tab_strip_
->GetEventHandlerForPoint(unactive_overlap
)));
564 TEST_F(TabStripTest
, GetTooltipHandler
) {
565 tab_strip_
->SetBounds(0, 0, 1000, 20);
567 controller_
->AddTab(0, false);
568 controller_
->AddTab(1, true);
569 controller_
->AddTab(2, false);
570 controller_
->AddTab(3, false);
571 ASSERT_EQ(4, tab_strip_
->tab_count());
573 // Verify that the active tab will be a tooltip handler for points that hit
575 Tab
* left_tab
= tab_strip_
->tab_at(0);
576 left_tab
->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
578 Tab
* active_tab
= tab_strip_
->tab_at(1);
579 active_tab
->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
580 ASSERT_TRUE(active_tab
->IsActive());
582 Tab
* right_tab
= tab_strip_
->tab_at(2);
583 right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
585 Tab
* most_right_tab
= tab_strip_
->tab_at(3);
586 most_right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
587 gfx::Size(200, 20)));
589 // Test that active_tab handles tooltips from area in which it overlaps with
590 // its left neighbour.
591 gfx::Point
left_overlap(
592 (active_tab
->x() + left_tab
->bounds().right() + 1) / 2,
593 active_tab
->bounds().bottom() - 1);
595 // Sanity check that the point is in both active and left tab.
596 ASSERT_TRUE(IsPointInTab(active_tab
, left_overlap
));
597 ASSERT_TRUE(IsPointInTab(left_tab
, left_overlap
));
599 EXPECT_EQ(active_tab
,
600 FindTabView(tab_strip_
->GetTooltipHandlerForPoint(left_overlap
)));
602 // Test that active_tab handles tooltips from area in which it overlaps with
603 // its right neighbour.
604 gfx::Point
right_overlap((active_tab
->bounds().right() + right_tab
->x()) / 2,
605 active_tab
->bounds().bottom() - 1);
607 // Sanity check that the point is in both active and right tab.
608 ASSERT_TRUE(IsPointInTab(active_tab
, right_overlap
));
609 ASSERT_TRUE(IsPointInTab(right_tab
, right_overlap
));
611 EXPECT_EQ(active_tab
,
612 FindTabView(tab_strip_
->GetTooltipHandlerForPoint(right_overlap
)));
614 // Test that if neither of tabs is active, the left one is selected.
615 gfx::Point
unactive_overlap(
616 (right_tab
->x() + most_right_tab
->bounds().right() + 1) / 2,
617 right_tab
->bounds().bottom() - 1);
619 // Sanity check that the point is in both active and left tab.
620 ASSERT_TRUE(IsPointInTab(right_tab
, unactive_overlap
));
621 ASSERT_TRUE(IsPointInTab(most_right_tab
, unactive_overlap
));
625 FindTabView(tab_strip_
->GetTooltipHandlerForPoint(unactive_overlap
)));
627 // Confirm that tab strip doe not return tooltip handler for points that
629 EXPECT_FALSE(tab_strip_
->GetTooltipHandlerForPoint(gfx::Point(-1, 2)));