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/test/views_test_base.h"
19 #include "ui/views/view.h"
20 #include "ui/views/view_targeter.h"
21 #include "ui/views/widget/widget.h"
25 // Walks up the views hierarchy until it finds a tab view. It returns the
26 // found tab view, on NULL if none is found.
27 views::View* FindTabView(views::View* view) {
28 views::View* current = view;
29 while (current && strcmp(current->GetClassName(), Tab::kViewClassName)) {
30 current = current->parent();
37 class TestTabStripObserver : public TabStripObserver {
39 explicit TestTabStripObserver(TabStrip* tab_strip)
40 : tab_strip_(tab_strip),
42 last_tab_removed_(-1),
43 last_tab_moved_from_(-1),
44 last_tab_moved_to_(-1),
45 tabstrip_deleted_(false) {
46 tab_strip_->AddObserver(this);
49 ~TestTabStripObserver() override {
51 tab_strip_->RemoveObserver(this);
54 int last_tab_added() const { return last_tab_added_; }
55 int last_tab_removed() const { return last_tab_removed_; }
56 int last_tab_moved_from() const { return last_tab_moved_from_; }
57 int last_tab_moved_to() const { return last_tab_moved_to_; }
58 bool tabstrip_deleted() const { return tabstrip_deleted_; }
61 // TabStripObserver overrides.
62 void TabStripAddedTabAt(TabStrip* tab_strip, int index) override {
63 last_tab_added_ = index;
66 void TabStripMovedTab(TabStrip* tab_strip,
68 int to_index) override {
69 last_tab_moved_from_ = from_index;
70 last_tab_moved_to_ = to_index;
73 void TabStripRemovedTabAt(TabStrip* tab_strip, int index) override {
74 last_tab_removed_ = index;
77 void TabStripDeleted(TabStrip* tab_strip) override {
78 tabstrip_deleted_ = true;
84 int last_tab_removed_;
85 int last_tab_moved_from_;
86 int last_tab_moved_to_;
87 bool tabstrip_deleted_;
89 DISALLOW_COPY_AND_ASSIGN(TestTabStripObserver);
92 class TabStripTest : public views::ViewsTestBase {
99 ~TabStripTest() override {}
101 void SetUp() override {
102 views::ViewsTestBase::SetUp();
104 controller_ = new FakeBaseTabStripController;
105 tab_strip_ = new TabStrip(controller_);
106 controller_->set_tab_strip(tab_strip_);
107 // Do this to force TabStrip to create the buttons.
108 parent_.AddChildView(tab_strip_);
109 parent_.set_owned_by_client();
111 widget_.reset(new views::Widget);
112 views::Widget::InitParams init_params =
113 CreateParams(views::Widget::InitParams::TYPE_POPUP);
114 init_params.ownership =
115 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
116 init_params.bounds = gfx::Rect(0, 0, 200, 200);
117 widget_->Init(init_params);
118 widget_->SetContentsView(&parent_);
121 void TearDown() override {
123 views::ViewsTestBase::TearDown();
127 // Returns the rectangular hit test region of |tab| in |tab|'s local
129 gfx::Rect GetTabHitTestMask(Tab* tab) {
130 views::ViewTargeter* targeter = tab->targeter();
132 views::MaskedTargeterDelegate* delegate =
133 static_cast<views::MaskedTargeterDelegate*>(tab);
136 bool valid_mask = delegate->GetHitTestMask(&mask);
139 return gfx::ToEnclosingRect((gfx::SkRectToRectF(mask.getBounds())));
142 // Returns the rectangular hit test region of the tab close button of
143 // |tab| in |tab|'s coordinate space (including padding if |padding|
145 gfx::Rect GetTabCloseHitTestMask(Tab* tab, bool padding) {
146 gfx::RectF bounds_f = tab->close_button_->GetContentsBounds();
148 bounds_f = tab->close_button_->GetLocalBounds();
149 views::View::ConvertRectToTarget(tab->close_button_, tab, &bounds_f);
150 return gfx::ToEnclosingRect(bounds_f);
153 // Checks whether |tab| contains |point_in_tabstrip_coords|, where the point
154 // is in |tab_strip_| coordinates.
155 bool IsPointInTab(Tab* tab, const gfx::Point& point_in_tabstrip_coords) {
156 gfx::Point point_in_tab_coords(point_in_tabstrip_coords);
157 views::View::ConvertPointToTarget(tab_strip_, tab, &point_in_tab_coords);
158 return tab->HitTestPoint(point_in_tab_coords);
161 // Owned by TabStrip.
162 FakeBaseTabStripController* controller_;
163 // Owns |tab_strip_|.
165 TabStrip* tab_strip_;
166 scoped_ptr<views::Widget> widget_;
169 DISALLOW_COPY_AND_ASSIGN(TabStripTest);
172 TEST_F(TabStripTest, GetModelCount) {
173 EXPECT_EQ(0, tab_strip_->GetModelCount());
176 TEST_F(TabStripTest, IsValidModelIndex) {
177 EXPECT_FALSE(tab_strip_->IsValidModelIndex(0));
180 TEST_F(TabStripTest, tab_count) {
181 EXPECT_EQ(0, tab_strip_->tab_count());
184 TEST_F(TabStripTest, AddTabAt) {
185 TestTabStripObserver observer(tab_strip_);
186 tab_strip_->AddTabAt(0, TabRendererData(), false);
187 ASSERT_EQ(1, tab_strip_->tab_count());
188 EXPECT_EQ(0, observer.last_tab_added());
189 Tab* tab = tab_strip_->tab_at(0);
190 EXPECT_FALSE(tab == NULL);
193 // Confirms that TabStripObserver::TabStripDeleted() is sent.
194 TEST_F(TabStripTest, TabStripDeleted) {
195 FakeBaseTabStripController* controller = new FakeBaseTabStripController;
196 TabStrip* tab_strip = new TabStrip(controller);
197 controller->set_tab_strip(tab_strip);
198 TestTabStripObserver observer(tab_strip);
200 EXPECT_TRUE(observer.tabstrip_deleted());
203 TEST_F(TabStripTest, MoveTab) {
204 TestTabStripObserver observer(tab_strip_);
205 tab_strip_->AddTabAt(0, TabRendererData(), false);
206 tab_strip_->AddTabAt(1, TabRendererData(), false);
207 tab_strip_->AddTabAt(2, TabRendererData(), false);
208 ASSERT_EQ(3, tab_strip_->tab_count());
209 EXPECT_EQ(2, observer.last_tab_added());
210 Tab* tab = tab_strip_->tab_at(0);
211 tab_strip_->MoveTab(0, 1, TabRendererData());
212 EXPECT_EQ(0, observer.last_tab_moved_from());
213 EXPECT_EQ(1, observer.last_tab_moved_to());
214 EXPECT_EQ(tab, tab_strip_->tab_at(1));
217 // Verifies child views are deleted after an animation completes.
218 TEST_F(TabStripTest, RemoveTab) {
219 TestTabStripObserver observer(tab_strip_);
220 controller_->AddTab(0, false);
221 controller_->AddTab(1, false);
222 const int child_view_count = tab_strip_->child_count();
223 EXPECT_EQ(2, tab_strip_->tab_count());
224 controller_->RemoveTab(0);
225 EXPECT_EQ(0, observer.last_tab_removed());
226 // When removing a tab the tabcount should immediately decrement.
227 EXPECT_EQ(1, tab_strip_->tab_count());
228 // But the number of views should remain the same (it's animatining closed).
229 EXPECT_EQ(child_view_count, tab_strip_->child_count());
230 tab_strip_->SetBounds(0, 0, 200, 20);
231 // Layout at a different size should force the animation to end and delete
232 // the tab that was removed.
233 tab_strip_->Layout();
234 EXPECT_EQ(child_view_count - 1, tab_strip_->child_count());
236 // Remove the last tab to make sure things are cleaned up correctly when
237 // the TabStrip is destroyed and an animation is ongoing.
238 controller_->RemoveTab(0);
239 EXPECT_EQ(0, observer.last_tab_removed());
242 TEST_F(TabStripTest, VisibilityInOverflow) {
243 tab_strip_->SetBounds(0, 0, 200, 20);
245 // The first tab added to a reasonable-width strip should be visible. If we
246 // add enough additional tabs, eventually one should be invisible due to
248 int invisible_tab_index = 0;
249 for (; invisible_tab_index < 100; ++invisible_tab_index) {
250 controller_->AddTab(invisible_tab_index, false);
251 if (!tab_strip_->tab_at(invisible_tab_index)->visible())
254 EXPECT_GT(invisible_tab_index, 0);
255 EXPECT_LT(invisible_tab_index, 100);
257 // The tabs before the invisible tab should still be visible.
258 for (int i = 0; i < invisible_tab_index; ++i)
259 EXPECT_TRUE(tab_strip_->tab_at(i)->visible());
261 // Enlarging the strip should result in the last tab becoming visible.
262 tab_strip_->SetBounds(0, 0, 400, 20);
263 EXPECT_TRUE(tab_strip_->tab_at(invisible_tab_index)->visible());
265 // Shrinking it again should re-hide the last tab.
266 tab_strip_->SetBounds(0, 0, 200, 20);
267 EXPECT_FALSE(tab_strip_->tab_at(invisible_tab_index)->visible());
269 // Shrinking it still more should make more tabs invisible, though not all.
270 // All the invisible tabs should be at the end of the strip.
271 tab_strip_->SetBounds(0, 0, 100, 20);
273 for (; i < invisible_tab_index; ++i) {
274 if (!tab_strip_->tab_at(i)->visible())
278 EXPECT_LT(i, invisible_tab_index);
279 invisible_tab_index = i;
280 for (int i = invisible_tab_index + 1; i < tab_strip_->tab_count(); ++i)
281 EXPECT_FALSE(tab_strip_->tab_at(i)->visible());
283 // When we're already in overflow, adding tabs at the beginning or end of
284 // the strip should not change how many tabs are visible.
285 controller_->AddTab(tab_strip_->tab_count(), false);
286 EXPECT_TRUE(tab_strip_->tab_at(invisible_tab_index - 1)->visible());
287 EXPECT_FALSE(tab_strip_->tab_at(invisible_tab_index)->visible());
288 controller_->AddTab(0, false);
289 EXPECT_TRUE(tab_strip_->tab_at(invisible_tab_index - 1)->visible());
290 EXPECT_FALSE(tab_strip_->tab_at(invisible_tab_index)->visible());
292 // If we remove enough tabs, all the tabs should be visible.
293 for (int i = tab_strip_->tab_count() - 1; i >= invisible_tab_index; --i)
294 controller_->RemoveTab(i);
295 EXPECT_TRUE(tab_strip_->tab_at(tab_strip_->tab_count() - 1)->visible());
298 TEST_F(TabStripTest, ImmersiveMode) {
299 // Immersive mode defaults to off.
300 EXPECT_FALSE(tab_strip_->IsImmersiveStyle());
302 // Tab strip defaults to normal tab height.
303 int normal_height = Tab::GetMinimumUnselectedSize().height();
304 EXPECT_EQ(normal_height, tab_strip_->GetPreferredSize().height());
306 // Tab strip can toggle immersive mode.
307 tab_strip_->SetImmersiveStyle(true);
308 EXPECT_TRUE(tab_strip_->IsImmersiveStyle());
310 // Now tabs have the immersive height.
311 int immersive_height = Tab::GetImmersiveHeight();
312 EXPECT_EQ(immersive_height, tab_strip_->GetPreferredSize().height());
314 // Sanity-check immersive tabs are shorter than normal tabs.
315 EXPECT_LT(immersive_height, normal_height);
318 // Creates a tab strip in stacked layout mode and verifies the correctness
319 // of hit tests against the visible/occluded regions of a tab and
320 // visible/occluded tab close buttons.
321 TEST_F(TabStripTest, TabHitTestMaskWhenStacked) {
322 tab_strip_->SetBounds(0, 0, 300, 20);
324 controller_->AddTab(0, false);
325 controller_->AddTab(1, true);
326 controller_->AddTab(2, false);
327 controller_->AddTab(3, false);
328 ASSERT_EQ(4, tab_strip_->tab_count());
330 Tab* left_tab = tab_strip_->tab_at(0);
331 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
333 Tab* active_tab = tab_strip_->tab_at(1);
334 active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
335 ASSERT_TRUE(active_tab->IsActive());
337 Tab* right_tab = tab_strip_->tab_at(2);
338 right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
340 Tab* most_right_tab = tab_strip_->tab_at(3);
341 most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
342 gfx::Size(200, 20)));
344 // Switch to stacked layout mode and force a layout to ensure tabs stack.
345 tab_strip_->SetStackedLayout(true);
346 tab_strip_->DoLayout();
349 // Tests involving |left_tab|, which has part of its bounds and its tab
350 // close button completely occluded by |active_tab|.
352 // Bounds of the tab's hit test mask.
353 gfx::Rect tab_bounds = GetTabHitTestMask(left_tab);
354 EXPECT_EQ(gfx::Rect(6, 2, 61, 27).ToString(), tab_bounds.ToString());
356 // Bounds of the tab close button (without padding) in the tab's
358 gfx::Rect contents_bounds = GetTabCloseHitTestMask(left_tab, false);
359 // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
360 //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
362 // Verify that the tab close button is completely occluded.
363 EXPECT_FALSE(tab_bounds.Contains(contents_bounds));
365 // Hit tests in the non-occuluded region of the tab.
366 EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(6, 2, 2, 2)));
367 EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(6, 2, 1, 1)));
368 EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(30, 15, 1, 1)));
369 EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(30, 15, 25, 35)));
370 EXPECT_TRUE(left_tab->HitTestRect(gfx::Rect(-10, -5, 20, 30)));
372 // Hit tests in the occluded region of the tab.
373 EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(70, 15, 2, 2)));
374 EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(70, -15, 30, 40)));
375 EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(87, 20, 5, 3)));
377 // Hit tests completely outside of the tab.
378 EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(-20, -25, 1, 1)));
379 EXPECT_FALSE(left_tab->HitTestRect(gfx::Rect(-20, -25, 3, 19)));
381 // All hit tests against the tab close button should fail because
382 // it is occluded by |active_tab|.
383 views::ImageButton* left_close = left_tab->close_button_;
384 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(1, 1, 1, 1)));
385 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(1, 1, 5, 10)));
386 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
387 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(10, 10, 3, 4)));
390 // Tests involving |active_tab|, which is completely visible.
392 tab_bounds = GetTabHitTestMask(active_tab);
393 EXPECT_EQ(gfx::Rect(6, 2, 108, 27).ToString(), tab_bounds.ToString());
394 contents_bounds = GetTabCloseHitTestMask(active_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 not occluded.
399 EXPECT_TRUE(tab_bounds.Contains(contents_bounds));
401 // Bounds of the tab close button (without padding) in the tab's
403 gfx::Rect local_bounds = GetTabCloseHitTestMask(active_tab, true);
404 EXPECT_EQ(gfx::Rect(81, 0, 39, 29).ToString(), local_bounds.ToString());
406 // Hit tests within the tab.
407 EXPECT_TRUE(active_tab->HitTestRect(gfx::Rect(30, 15, 1, 1)));
408 EXPECT_TRUE(active_tab->HitTestRect(gfx::Rect(30, 15, 2, 2)));
410 // Hit tests against the tab close button. Note that hit tests from either
411 // mouse or touch should both fail if they are strictly contained within
412 // the button's padding.
413 views::ImageButton* active_close = active_tab->close_button_;
414 EXPECT_FALSE(active_close->HitTestRect(gfx::Rect(1, 1, 1, 1)));
415 EXPECT_FALSE(active_close->HitTestRect(gfx::Rect(1, 1, 2, 2)));
416 EXPECT_TRUE(active_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
417 EXPECT_TRUE(active_close->HitTestRect(gfx::Rect(10, 10, 25, 35)));
420 // Tests involving |most_right_tab|, which has part of its bounds occluded
421 // by |right_tab| but has its tab close button completely visible.
423 tab_bounds = GetTabHitTestMask(most_right_tab);
424 EXPECT_EQ(gfx::Rect(84, 2, 30, 27).ToString(), tab_bounds.ToString());
425 contents_bounds = GetTabCloseHitTestMask(active_tab, false);
426 // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
427 //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
428 local_bounds = GetTabCloseHitTestMask(active_tab, true);
429 EXPECT_EQ(gfx::Rect(81, 0, 39, 29).ToString(), local_bounds.ToString());
431 // Verify that the tab close button is not occluded.
432 EXPECT_TRUE(tab_bounds.Contains(contents_bounds));
434 // Hit tests in the occluded region of the tab.
435 EXPECT_FALSE(most_right_tab->HitTestRect(gfx::Rect(20, 15, 1, 1)));
436 EXPECT_FALSE(most_right_tab->HitTestRect(gfx::Rect(20, 15, 5, 6)));
438 // Hit tests in the non-occluded region of the tab.
439 EXPECT_TRUE(most_right_tab->HitTestRect(gfx::Rect(85, 15, 1, 1)));
440 EXPECT_TRUE(most_right_tab->HitTestRect(gfx::Rect(85, 15, 2, 2)));
442 // Hit tests against the tab close button. Note that hit tests from either
443 // mouse or touch should both fail if they are strictly contained within
444 // the button's padding.
445 views::ImageButton* most_right_close = most_right_tab->close_button_;
446 EXPECT_FALSE(most_right_close->HitTestRect(gfx::Rect(1, 1, 1, 1)));
447 EXPECT_FALSE(most_right_close->HitTestRect(gfx::Rect(1, 1, 2, 2)));
448 EXPECT_TRUE(most_right_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
449 EXPECT_TRUE(most_right_close->HitTestRect(gfx::Rect(10, 10, 25, 35)));
450 EXPECT_TRUE(most_right_close->HitTestRect(gfx::Rect(-10, 10, 25, 35)));
453 // Creates a tab strip in stacked layout mode and verifies the correctness
454 // of hit tests against the visible/occluded region of a partially-occluded
456 TEST_F(TabStripTest, ClippedTabCloseButton) {
457 tab_strip_->SetBounds(0, 0, 220, 20);
459 controller_->AddTab(0, false);
460 controller_->AddTab(1, true);
461 ASSERT_EQ(2, tab_strip_->tab_count());
463 Tab* left_tab = tab_strip_->tab_at(0);
464 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
466 Tab* active_tab = tab_strip_->tab_at(1);
467 active_tab->SetBoundsRect(gfx::Rect(gfx::Point(180, 0), gfx::Size(200, 20)));
468 ASSERT_TRUE(active_tab->IsActive());
470 // Switch to stacked layout mode and force a layout to ensure tabs stack.
471 tab_strip_->SetStackedLayout(true);
472 tab_strip_->DoLayout();
475 // Tests involving |left_tab|, which has part of its bounds and its tab
476 // close button partially occluded by |active_tab|.
478 // Bounds of the tab's hit test mask.
479 gfx::Rect tab_bounds = GetTabHitTestMask(left_tab);
480 EXPECT_EQ(gfx::Rect(6, 2, 91, 27).ToString(), tab_bounds.ToString());
482 // Bounds of the tab close button (without padding) in the tab's
484 gfx::Rect contents_bounds = GetTabCloseHitTestMask(left_tab, false);
485 // TODO(tdanderson): Uncomment this line once crbug.com/311609 is resolved.
486 //EXPECT_EQ(gfx::Rect(84, 8, 18, 18).ToString(), contents_bounds.ToString());
488 // Verify that the tab close button is only partially occluded.
489 EXPECT_FALSE(tab_bounds.Contains(contents_bounds));
490 EXPECT_TRUE(tab_bounds.Intersects(contents_bounds));
492 views::ImageButton* left_close = left_tab->close_button_;
494 // Hit tests from mouse should return true if and only if the location
495 // is within a visible region.
496 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(2, 15, 1, 1)));
497 EXPECT_TRUE(left_close->HitTestRect(gfx::Rect(3, 15, 1, 1)));
498 EXPECT_TRUE(left_close->HitTestRect(gfx::Rect(10, 10, 1, 1)));
499 EXPECT_TRUE(left_close->HitTestRect(gfx::Rect(15, 12, 1, 1)));
500 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(16, 10, 1, 1)));
502 // All hit tests from touch should return false because the button is
503 // not fully visible.
504 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(2, 15, 2, 2)));
505 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(3, 15, 25, 25)));
506 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(10, 10, 4, 5)));
507 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(15, 12, 2, 2)));
508 EXPECT_FALSE(left_close->HitTestRect(gfx::Rect(16, 10, 20, 20)));
511 TEST_F(TabStripTest, GetEventHandlerForOverlappingArea) {
512 tab_strip_->SetBounds(0, 0, 1000, 20);
514 controller_->AddTab(0, false);
515 controller_->AddTab(1, true);
516 controller_->AddTab(2, false);
517 controller_->AddTab(3, false);
518 ASSERT_EQ(4, tab_strip_->tab_count());
520 // Verify that the active tab will be a tooltip handler for points that hit
522 Tab* left_tab = tab_strip_->tab_at(0);
523 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
525 Tab* active_tab = tab_strip_->tab_at(1);
526 active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
527 ASSERT_TRUE(active_tab->IsActive());
529 Tab* right_tab = tab_strip_->tab_at(2);
530 right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
532 Tab* most_right_tab = tab_strip_->tab_at(3);
533 most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
534 gfx::Size(200, 20)));
536 // Test that active tabs gets events from area in which it overlaps with its
538 gfx::Point left_overlap(
539 (active_tab->x() + left_tab->bounds().right() + 1) / 2,
540 active_tab->bounds().bottom() - 1);
542 // Sanity check that the point is in both active and left tab.
543 ASSERT_TRUE(IsPointInTab(active_tab, left_overlap));
544 ASSERT_TRUE(IsPointInTab(left_tab, left_overlap));
546 EXPECT_EQ(active_tab,
547 FindTabView(tab_strip_->GetEventHandlerForPoint(left_overlap)));
549 // Test that active tabs gets events from area in which it overlaps with its
551 gfx::Point right_overlap((active_tab->bounds().right() + right_tab->x()) / 2,
552 active_tab->bounds().bottom() - 1);
554 // Sanity check that the point is in both active and right tab.
555 ASSERT_TRUE(IsPointInTab(active_tab, right_overlap));
556 ASSERT_TRUE(IsPointInTab(right_tab, right_overlap));
558 EXPECT_EQ(active_tab,
559 FindTabView(tab_strip_->GetEventHandlerForPoint(right_overlap)));
561 // Test that if neither of tabs is active, the left one is selected.
562 gfx::Point unactive_overlap(
563 (right_tab->x() + most_right_tab->bounds().right() + 1) / 2,
564 right_tab->bounds().bottom() - 1);
566 // Sanity check that the point is in both active and left tab.
567 ASSERT_TRUE(IsPointInTab(right_tab, unactive_overlap));
568 ASSERT_TRUE(IsPointInTab(most_right_tab, unactive_overlap));
571 FindTabView(tab_strip_->GetEventHandlerForPoint(unactive_overlap)));
574 TEST_F(TabStripTest, GetTooltipHandler) {
575 tab_strip_->SetBounds(0, 0, 1000, 20);
577 controller_->AddTab(0, false);
578 controller_->AddTab(1, true);
579 controller_->AddTab(2, false);
580 controller_->AddTab(3, false);
581 ASSERT_EQ(4, tab_strip_->tab_count());
583 // Verify that the active tab will be a tooltip handler for points that hit
585 Tab* left_tab = tab_strip_->tab_at(0);
586 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20)));
588 Tab* active_tab = tab_strip_->tab_at(1);
589 active_tab->SetBoundsRect(gfx::Rect(gfx::Point(150, 0), gfx::Size(200, 20)));
590 ASSERT_TRUE(active_tab->IsActive());
592 Tab* right_tab = tab_strip_->tab_at(2);
593 right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20)));
595 Tab* most_right_tab = tab_strip_->tab_at(3);
596 most_right_tab->SetBoundsRect(gfx::Rect(gfx::Point(450, 0),
597 gfx::Size(200, 20)));
599 // Test that active_tab handles tooltips from area in which it overlaps with
600 // its left neighbour.
601 gfx::Point left_overlap(
602 (active_tab->x() + left_tab->bounds().right() + 1) / 2,
603 active_tab->bounds().bottom() - 1);
605 // Sanity check that the point is in both active and left tab.
606 ASSERT_TRUE(IsPointInTab(active_tab, left_overlap));
607 ASSERT_TRUE(IsPointInTab(left_tab, left_overlap));
609 EXPECT_EQ(active_tab,
610 FindTabView(tab_strip_->GetTooltipHandlerForPoint(left_overlap)));
612 // Test that active_tab handles tooltips from area in which it overlaps with
613 // its right neighbour.
614 gfx::Point right_overlap((active_tab->bounds().right() + right_tab->x()) / 2,
615 active_tab->bounds().bottom() - 1);
617 // Sanity check that the point is in both active and right tab.
618 ASSERT_TRUE(IsPointInTab(active_tab, right_overlap));
619 ASSERT_TRUE(IsPointInTab(right_tab, right_overlap));
621 EXPECT_EQ(active_tab,
622 FindTabView(tab_strip_->GetTooltipHandlerForPoint(right_overlap)));
624 // Test that if neither of tabs is active, the left one is selected.
625 gfx::Point unactive_overlap(
626 (right_tab->x() + most_right_tab->bounds().right() + 1) / 2,
627 right_tab->bounds().bottom() - 1);
629 // Sanity check that the point is in both active and left tab.
630 ASSERT_TRUE(IsPointInTab(right_tab, unactive_overlap));
631 ASSERT_TRUE(IsPointInTab(most_right_tab, unactive_overlap));
635 FindTabView(tab_strip_->GetTooltipHandlerForPoint(unactive_overlap)));
637 // Confirm that tab strip doe not return tooltip handler for points that
639 EXPECT_FALSE(tab_strip_->GetTooltipHandlerForPoint(gfx::Point(-1, 2)));