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/path.h"
16 #include "ui/gfx/rect_conversions.h"
17 #include "ui/gfx/skia_util.h"
18 #include "ui/views/view.h"
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();
34 class TestTabStripObserver
: public TabStripObserver
{
36 explicit TestTabStripObserver(TabStrip
* tab_strip
)
37 : tab_strip_(tab_strip
),
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);
46 virtual ~TestTabStripObserver() {
48 tab_strip_
->RemoveObserver(this);
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_
; }
58 // TabStripObserver overrides.
59 virtual void TabStripAddedTabAt(TabStrip
* tab_strip
, int index
) OVERRIDE
{
60 last_tab_added_
= index
;
63 virtual void TabStripMovedTab(TabStrip
* tab_strip
,
65 int to_index
) OVERRIDE
{
66 last_tab_moved_from_
= from_index
;
67 last_tab_moved_to_
= to_index
;
70 virtual void TabStripRemovedTabAt(TabStrip
* tab_strip
, int index
) OVERRIDE
{
71 last_tab_removed_
= index
;
74 virtual void TabStripDeleted(TabStrip
* tab_strip
) OVERRIDE
{
75 tabstrip_deleted_
= true;
81 int last_tab_removed_
;
82 int last_tab_moved_from_
;
83 int last_tab_moved_to_
;
84 bool tabstrip_deleted_
;
86 DISALLOW_COPY_AND_ASSIGN(TestTabStripObserver
);
89 class TabStripTest
: public testing::Test
{
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_
);
100 // Returns the rectangular hit test region of |tab| in |tab|'s local
102 gfx::Rect
GetTabHitTestMask(Tab
* tab
) {
104 tab
->GetHitTestMask(views::View::HIT_TEST_SOURCE_TOUCH
, &mask
);
105 return gfx::ToEnclosingRect((gfx::SkRectToRectF(mask
.getBounds())));
108 // Returns the rectangular hit test region of the tab close button of
109 // |tab| in |tab|'s coordinate space (including padding if |padding|
111 gfx::Rect
GetTabCloseHitTestMask(Tab
* tab
, bool padding
) {
112 gfx::RectF bounds_f
= tab
->close_button_
->GetContentsBounds();
114 bounds_f
= tab
->close_button_
->GetLocalBounds();
115 views::View::ConvertRectToTarget(tab
->close_button_
, tab
, &bounds_f
);
116 return gfx::ToEnclosingRect(bounds_f
);
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
);
127 base::MessageLoopForUI ui_loop_
;
128 // Owned by TabStrip.
129 FakeBaseTabStripController
* controller_
;
130 // Owns |tab_strip_|.
132 TabStrip
* tab_strip_
;
135 DISALLOW_COPY_AND_ASSIGN(TabStripTest
);
138 TEST_F(TabStripTest
, GetModelCount
) {
139 EXPECT_EQ(0, tab_strip_
->GetModelCount());
142 TEST_F(TabStripTest
, IsValidModelIndex
) {
143 EXPECT_FALSE(tab_strip_
->IsValidModelIndex(0));
146 TEST_F(TabStripTest
, tab_count
) {
147 EXPECT_EQ(0, tab_strip_
->tab_count());
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
);
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
);
166 EXPECT_TRUE(observer
.tabstrip_deleted());
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));
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());
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());
208 TEST_F(TabStripTest
, ImmersiveMode
) {
209 // Immersive mode defaults to off.
210 EXPECT_FALSE(tab_strip_
->IsImmersiveStyle());
212 // Tab strip defaults to normal tab height.
213 int normal_height
= Tab::GetMinimumUnselectedSize().height();
214 EXPECT_EQ(normal_height
, tab_strip_
->GetPreferredSize().height());
216 // Tab strip can toggle immersive mode.
217 tab_strip_
->SetImmersiveStyle(true);
218 EXPECT_TRUE(tab_strip_
->IsImmersiveStyle());
220 // Now tabs have the immersive height.
221 int immersive_height
= Tab::GetImmersiveHeight();
222 EXPECT_EQ(immersive_height
, tab_strip_
->GetPreferredSize().height());
224 // Sanity-check immersive tabs are shorter than normal tabs.
225 EXPECT_LT(immersive_height
, normal_height
);
228 // Creates a tab strip in stacked layout mode and verifies the correctness
229 // of hit tests against the visible/occluded regions of a tab and
230 // visible/occluded tab close buttons.
231 TEST_F(TabStripTest
, TabHitTestMaskWhenStacked
) {
232 tab_strip_
->SetBounds(0, 0, 300, 20);
234 controller_
->AddTab(0, false);
235 controller_
->AddTab(1, true);
236 controller_
->AddTab(2, false);
237 controller_
->AddTab(3, false);
238 ASSERT_EQ(4, tab_strip_
->tab_count());
240 Tab
* left_tab
= tab_strip_
->tab_at(0);
241 left_tab
->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
243 Tab
* active_tab
= tab_strip_
->tab_at(1);
244 active_tab
->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
245 ASSERT_TRUE(active_tab
->IsActive());
247 Tab
* right_tab
= tab_strip_
->tab_at(2);
248 right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
250 Tab
* most_right_tab
= tab_strip_
->tab_at(3);
251 most_right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
252 gfx::Size(200, 20)));
254 // Switch to stacked layout mode and force a layout to ensure tabs stack.
255 tab_strip_
->SetLayoutType(TAB_STRIP_LAYOUT_STACKED
, false);
256 tab_strip_
->DoLayout();
259 // Tests involving |left_tab|, which has part of its bounds and its tab
260 // close button completely occluded by |active_tab|.
262 // Bounds of the tab's hit test mask.
263 gfx::Rect tab_bounds
= GetTabHitTestMask(left_tab
);
264 EXPECT_EQ(gfx::Rect(6, 2, 61, 27).ToString(), tab_bounds
.ToString());
266 // Bounds of the tab close button (without padding) in the tab's
268 gfx::Rect contents_bounds
= GetTabCloseHitTestMask(left_tab
, false);
269 // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
270 //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
272 // Verify that the tab close button is completely occluded.
273 EXPECT_FALSE(tab_bounds
.Contains(contents_bounds
));
275 // Hit tests in the non-occuluded region of the tab.
276 EXPECT_TRUE(left_tab
->HitTestRect(gfx::Rect(6, 2, 2, 2)));
277 EXPECT_TRUE(left_tab
->HitTestRect(gfx::Rect(6, 2, 1, 1)));
278 EXPECT_TRUE(left_tab
->HitTestRect(gfx::Rect(30, 15, 1, 1)));
279 EXPECT_TRUE(left_tab
->HitTestRect(gfx::Rect(30, 15, 25, 35)));
280 EXPECT_TRUE(left_tab
->HitTestRect(gfx::Rect(-10, -5, 20, 30)));
282 // Hit tests in the occluded region of the tab.
283 EXPECT_FALSE(left_tab
->HitTestRect(gfx::Rect(70, 15, 2, 2)));
284 EXPECT_FALSE(left_tab
->HitTestRect(gfx::Rect(70, -15, 30, 40)));
285 EXPECT_FALSE(left_tab
->HitTestRect(gfx::Rect(87, 20, 5, 3)));
287 // Hit tests completely outside of the tab.
288 EXPECT_FALSE(left_tab
->HitTestRect(gfx::Rect(-20, -25, 1, 1)));
289 EXPECT_FALSE(left_tab
->HitTestRect(gfx::Rect(-20, -25, 3, 19)));
291 // All hit tests against the tab close button should fail because
292 // it is occluded by |active_tab|.
293 views::ImageButton
* left_close
= left_tab
->close_button_
;
294 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(1, 1, 1, 1)));
295 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(1, 1, 5, 10)));
296 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(10, 10, 1, 1)));
297 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(10, 10, 3, 4)));
300 // Tests involving |active_tab|, which is completely visible.
302 tab_bounds
= GetTabHitTestMask(active_tab
);
303 EXPECT_EQ(gfx::Rect(6, 2, 108, 27).ToString(), tab_bounds
.ToString());
304 contents_bounds
= GetTabCloseHitTestMask(active_tab
, false);
305 // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
306 //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
308 // Verify that the tab close button is not occluded.
309 EXPECT_TRUE(tab_bounds
.Contains(contents_bounds
));
311 // Bounds of the tab close button (without padding) in the tab's
313 gfx::Rect local_bounds
= GetTabCloseHitTestMask(active_tab
, true);
314 EXPECT_EQ(gfx::Rect(81, 0, 39, 29).ToString(), local_bounds
.ToString());
316 // Hit tests within the tab.
317 EXPECT_TRUE(active_tab
->HitTestRect(gfx::Rect(30, 15, 1, 1)));
318 EXPECT_TRUE(active_tab
->HitTestRect(gfx::Rect(30, 15, 2, 2)));
320 // Hit tests against the tab close button. Note that hit tests from either
321 // mouse or touch should both fail if they are strictly contained within
322 // the button's padding.
323 views::ImageButton
* active_close
= active_tab
->close_button_
;
324 EXPECT_FALSE(active_close
->HitTestRect(gfx::Rect(1, 1, 1, 1)));
325 EXPECT_FALSE(active_close
->HitTestRect(gfx::Rect(1, 1, 2, 2)));
326 EXPECT_TRUE(active_close
->HitTestRect(gfx::Rect(10, 10, 1, 1)));
327 EXPECT_TRUE(active_close
->HitTestRect(gfx::Rect(10, 10, 25, 35)));
330 // Tests involving |most_right_tab|, which has part of its bounds occluded
331 // by |right_tab| but has its tab close button completely visible.
333 tab_bounds
= GetTabHitTestMask(most_right_tab
);
334 EXPECT_EQ(gfx::Rect(84, 2, 30, 27).ToString(), tab_bounds
.ToString());
335 contents_bounds
= GetTabCloseHitTestMask(active_tab
, false);
336 // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
337 //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
338 local_bounds
= GetTabCloseHitTestMask(active_tab
, true);
339 EXPECT_EQ(gfx::Rect(81, 0, 39, 29).ToString(), local_bounds
.ToString());
341 // Verify that the tab close button is not occluded.
342 EXPECT_TRUE(tab_bounds
.Contains(contents_bounds
));
344 // Hit tests in the occluded region of the tab.
345 EXPECT_FALSE(most_right_tab
->HitTestRect(gfx::Rect(20, 15, 1, 1)));
346 EXPECT_FALSE(most_right_tab
->HitTestRect(gfx::Rect(20, 15, 5, 6)));
348 // Hit tests in the non-occluded region of the tab.
349 EXPECT_TRUE(most_right_tab
->HitTestRect(gfx::Rect(85, 15, 1, 1)));
350 EXPECT_TRUE(most_right_tab
->HitTestRect(gfx::Rect(85, 15, 2, 2)));
352 // Hit tests against the tab close button. Note that hit tests from either
353 // mouse or touch should both fail if they are strictly contained within
354 // the button's padding.
355 views::ImageButton
* most_right_close
= most_right_tab
->close_button_
;
356 EXPECT_FALSE(most_right_close
->HitTestRect(gfx::Rect(1, 1, 1, 1)));
357 EXPECT_FALSE(most_right_close
->HitTestRect(gfx::Rect(1, 1, 2, 2)));
358 EXPECT_TRUE(most_right_close
->HitTestRect(gfx::Rect(10, 10, 1, 1)));
359 EXPECT_TRUE(most_right_close
->HitTestRect(gfx::Rect(10, 10, 25, 35)));
360 EXPECT_TRUE(most_right_close
->HitTestRect(gfx::Rect(-10, 10, 25, 35)));
363 // Creates a tab strip in stacked layout mode and verifies the correctness
364 // of hit tests against the visible/occluded region of a partially-occluded
366 TEST_F(TabStripTest
, ClippedTabCloseButton
) {
367 tab_strip_
->SetBounds(0, 0, 220, 20);
369 controller_
->AddTab(0, false);
370 controller_
->AddTab(1, true);
371 ASSERT_EQ(2, tab_strip_
->tab_count());
373 Tab
* left_tab
= tab_strip_
->tab_at(0);
374 left_tab
->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
376 Tab
* active_tab
= tab_strip_
->tab_at(1);
377 active_tab
->SetBoundsRect(gfx::Rect(gfx::Point(180, 0), gfx::Size(200, 20)));
378 ASSERT_TRUE(active_tab
->IsActive());
380 // Switch to stacked layout mode and force a layout to ensure tabs stack.
381 tab_strip_
->SetLayoutType(TAB_STRIP_LAYOUT_STACKED
, false);
382 tab_strip_
->DoLayout();
385 // Tests involving |left_tab|, which has part of its bounds and its tab
386 // close button partially occluded by |active_tab|.
388 // Bounds of the tab's hit test mask.
389 gfx::Rect tab_bounds
= GetTabHitTestMask(left_tab
);
390 EXPECT_EQ(gfx::Rect(6, 2, 91, 27).ToString(), tab_bounds
.ToString());
392 // Bounds of the tab close button (without padding) in the tab's
394 gfx::Rect contents_bounds
= GetTabCloseHitTestMask(left_tab
, false);
395 // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
396 //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
398 // Verify that the tab close button is only partially occluded.
399 EXPECT_FALSE(tab_bounds
.Contains(contents_bounds
));
400 EXPECT_TRUE(tab_bounds
.Intersects(contents_bounds
));
402 views::ImageButton
* left_close
= left_tab
->close_button_
;
404 // Hit tests from mouse should return true if and only if the location
405 // is within a visible region.
406 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(2, 15, 1, 1)));
407 EXPECT_TRUE(left_close
->HitTestRect(gfx::Rect(3, 15, 1, 1)));
408 EXPECT_TRUE(left_close
->HitTestRect(gfx::Rect(10, 10, 1, 1)));
409 EXPECT_TRUE(left_close
->HitTestRect(gfx::Rect(15, 12, 1, 1)));
410 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(16, 10, 1, 1)));
412 // All hit tests from touch should return false because the button is
413 // not fully visible.
414 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(2, 15, 2, 2)));
415 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(3, 15, 25, 25)));
416 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(10, 10, 4, 5)));
417 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(15, 12, 2, 2)));
418 EXPECT_FALSE(left_close
->HitTestRect(gfx::Rect(16, 10, 20, 20)));
421 TEST_F(TabStripTest
, GetEventHandlerForOverlappingArea
) {
422 tab_strip_
->SetBounds(0, 0, 1000, 20);
424 controller_
->AddTab(0, false);
425 controller_
->AddTab(1, true);
426 controller_
->AddTab(2, false);
427 controller_
->AddTab(3, false);
428 ASSERT_EQ(4, tab_strip_
->tab_count());
430 // Verify that the active tab will be a tooltip handler for points that hit
432 Tab
* left_tab
= tab_strip_
->tab_at(0);
433 left_tab
->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
435 Tab
* active_tab
= tab_strip_
->tab_at(1);
436 active_tab
->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
437 ASSERT_TRUE(active_tab
->IsActive());
439 Tab
* right_tab
= tab_strip_
->tab_at(2);
440 right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
442 Tab
* most_right_tab
= tab_strip_
->tab_at(3);
443 most_right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
444 gfx::Size(200, 20)));
446 // Test that active tabs gets events from area in which it overlaps with its
448 gfx::Point
left_overlap(
449 (active_tab
->x() + left_tab
->bounds().right() + 1) / 2,
450 active_tab
->bounds().bottom() - 1);
452 // Sanity check that the point is in both active and left tab.
453 ASSERT_TRUE(IsPointInTab(active_tab
, left_overlap
));
454 ASSERT_TRUE(IsPointInTab(left_tab
, left_overlap
));
456 EXPECT_EQ(active_tab
,
457 FindTabView(tab_strip_
->GetEventHandlerForPoint(left_overlap
)));
459 // Test that active tabs gets events from area in which it overlaps with its
461 gfx::Point
right_overlap((active_tab
->bounds().right() + right_tab
->x()) / 2,
462 active_tab
->bounds().bottom() - 1);
464 // Sanity check that the point is in both active and right tab.
465 ASSERT_TRUE(IsPointInTab(active_tab
, right_overlap
));
466 ASSERT_TRUE(IsPointInTab(right_tab
, right_overlap
));
468 EXPECT_EQ(active_tab
,
469 FindTabView(tab_strip_
->GetEventHandlerForPoint(right_overlap
)));
471 // Test that if neither of tabs is active, the left one is selected.
472 gfx::Point
unactive_overlap(
473 (right_tab
->x() + most_right_tab
->bounds().right() + 1) / 2,
474 right_tab
->bounds().bottom() - 1);
476 // Sanity check that the point is in both active and left tab.
477 ASSERT_TRUE(IsPointInTab(right_tab
, unactive_overlap
));
478 ASSERT_TRUE(IsPointInTab(most_right_tab
, unactive_overlap
));
481 FindTabView(tab_strip_
->GetEventHandlerForPoint(unactive_overlap
)));
484 TEST_F(TabStripTest
, GetTooltipHandler
) {
485 tab_strip_
->SetBounds(0, 0, 1000, 20);
487 controller_
->AddTab(0, false);
488 controller_
->AddTab(1, true);
489 controller_
->AddTab(2, false);
490 controller_
->AddTab(3, false);
491 ASSERT_EQ(4, tab_strip_
->tab_count());
493 // Verify that the active tab will be a tooltip handler for points that hit
495 Tab
* left_tab
= tab_strip_
->tab_at(0);
496 left_tab
->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
498 Tab
* active_tab
= tab_strip_
->tab_at(1);
499 active_tab
->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
500 ASSERT_TRUE(active_tab
->IsActive());
502 Tab
* right_tab
= tab_strip_
->tab_at(2);
503 right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
505 Tab
* most_right_tab
= tab_strip_
->tab_at(3);
506 most_right_tab
->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
507 gfx::Size(200, 20)));
509 // Test that active_tab handles tooltips from area in which it overlaps with
510 // its left neighbour.
511 gfx::Point
left_overlap(
512 (active_tab
->x() + left_tab
->bounds().right() + 1) / 2,
513 active_tab
->bounds().bottom() - 1);
515 // Sanity check that the point is in both active and left tab.
516 ASSERT_TRUE(IsPointInTab(active_tab
, left_overlap
));
517 ASSERT_TRUE(IsPointInTab(left_tab
, left_overlap
));
519 EXPECT_EQ(active_tab
,
520 FindTabView(tab_strip_
->GetTooltipHandlerForPoint(left_overlap
)));
522 // Test that active_tab handles tooltips from area in which it overlaps with
523 // its right neighbour.
524 gfx::Point
right_overlap((active_tab
->bounds().right() + right_tab
->x()) / 2,
525 active_tab
->bounds().bottom() - 1);
527 // Sanity check that the point is in both active and right tab.
528 ASSERT_TRUE(IsPointInTab(active_tab
, right_overlap
));
529 ASSERT_TRUE(IsPointInTab(right_tab
, right_overlap
));
531 EXPECT_EQ(active_tab
,
532 FindTabView(tab_strip_
->GetTooltipHandlerForPoint(right_overlap
)));
534 // Test that if neither of tabs is active, the left one is selected.
535 gfx::Point
unactive_overlap(
536 (right_tab
->x() + most_right_tab
->bounds().right() + 1) / 2,
537 right_tab
->bounds().bottom() - 1);
539 // Sanity check that the point is in both active and left tab.
540 ASSERT_TRUE(IsPointInTab(right_tab
, unactive_overlap
));
541 ASSERT_TRUE(IsPointInTab(most_right_tab
, unactive_overlap
));
545 FindTabView(tab_strip_
->GetTooltipHandlerForPoint(unactive_overlap
)));
547 // Confirm that tab strip doe not return tooltip handler for points that
549 EXPECT_FALSE(tab_strip_
->GetTooltipHandlerForPoint(gfx::Point(-1, 2)));