1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/views/tabs/tab_strip.h" 6 7 #include "base/message_loop/message_loop.h" 8 #include "chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h" 9 #include "chrome/browser/ui/views/tabs/tab.h" 10 #include "chrome/browser/ui/views/tabs/tab_strip.h" 11 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h" 12 #include "chrome/browser/ui/views/tabs/tab_strip_observer.h" 13 #include "chrome/test/base/testing_profile.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 #include "ui/gfx/path.h" 16 #include "ui/gfx/rect_conversions.h" 17 #include "ui/gfx/skia_util.h" 18 #include "ui/views/view.h" 19 20 namespace { 21 22 // Walks up the views hierarchy until it finds a tab view. It returns the 23 // found tab view, on NULL if none is found. 24 views::View* FindTabView(views::View* view) { 25 views::View* current = view; 26 while (current && strcmp(current->GetClassName(), Tab::kViewClassName)) { 27 current = current->parent(); 28 } 29 return current; 30 } 31 32 } // namespace 33 34 class TestTabStripObserver : public TabStripObserver { 35 public: 36 explicit TestTabStripObserver(TabStrip* tab_strip) 37 : tab_strip_(tab_strip), 38 last_tab_added_(-1), 39 last_tab_removed_(-1), 40 last_tab_moved_from_(-1), 41 last_tab_moved_to_(-1), 42 tabstrip_deleted_(false) { 43 tab_strip_->AddObserver(this); 44 } 45 46 virtual ~TestTabStripObserver() { 47 if (tab_strip_) 48 tab_strip_->RemoveObserver(this); 49 } 50 51 int last_tab_added() const { return last_tab_added_; } 52 int last_tab_removed() const { return last_tab_removed_; } 53 int last_tab_moved_from() const { return last_tab_moved_from_; } 54 int last_tab_moved_to() const { return last_tab_moved_to_; } 55 bool tabstrip_deleted() const { return tabstrip_deleted_; } 56 57 private: 58 // TabStripObserver overrides. 59 virtual void TabStripAddedTabAt(TabStrip* tab_strip, int index) OVERRIDE { 60 last_tab_added_ = index; 61 } 62 63 virtual void TabStripMovedTab(TabStrip* tab_strip, 64 int from_index, 65 int to_index) OVERRIDE { 66 last_tab_moved_from_ = from_index; 67 last_tab_moved_to_ = to_index; 68 } 69 70 virtual void TabStripRemovedTabAt(TabStrip* tab_strip, int index) OVERRIDE { 71 last_tab_removed_ = index; 72 } 73 74 virtual void TabStripDeleted(TabStrip* tab_strip) OVERRIDE { 75 tabstrip_deleted_ = true; 76 tab_strip_ = NULL; 77 } 78 79 TabStrip* tab_strip_; 80 int last_tab_added_; 81 int last_tab_removed_; 82 int last_tab_moved_from_; 83 int last_tab_moved_to_; 84 bool tabstrip_deleted_; 85 86 DISALLOW_COPY_AND_ASSIGN(TestTabStripObserver); 87 }; 88 89 class TabStripTest : public testing::Test { 90 public: 91 TabStripTest() 92 : controller_(new FakeBaseTabStripController) { 93 tab_strip_ = new TabStrip(controller_); 94 controller_->set_tab_strip(tab_strip_); 95 // Do this to force TabStrip to create the buttons. 96 parent_.AddChildView(tab_strip_); 97 } 98 99 protected: 100 // Returns the rectangular hit test region of |tab| in |tab|'s local 101 // coordinate space. 102 gfx::Rect GetTabHitTestMask(Tab* tab) { 103 gfx::Path mask; 104 tab->GetHitTestMask(views::View::HIT_TEST_SOURCE_TOUCH, &mask); 105 return gfx::ToEnclosingRect((gfx::SkRectToRectF(mask.getBounds()))); 106 } 107 108 // Returns the rectangular hit test region of the tab close button of 109 // |tab| in |tab|'s coordinate space (including padding if |padding| 110 // is true). 111 gfx::Rect GetTabCloseHitTestMask(Tab* tab, bool padding) { 112 gfx::RectF bounds_f = tab->close_button_->GetContentsBounds(); 113 if (padding) 114 bounds_f = tab->close_button_->GetLocalBounds(); 115 views::View::ConvertRectToTarget(tab->close_button_, tab, &bounds_f); 116 return gfx::ToEnclosingRect(bounds_f); 117 } 118 119 // Checks whether |tab| contains |point_in_tabstrip_coords|, where the point 120 // is in |tab_strip_| coordinates. 121 bool IsPointInTab(Tab* tab, const gfx::Point& point_in_tabstrip_coords) { 122 gfx::Point point_in_tab_coords(point_in_tabstrip_coords); 123 views::View::ConvertPointToTarget(tab_strip_, tab, &point_in_tab_coords); 124 return tab->HitTestPoint(point_in_tab_coords); 125 } 126 127 base::MessageLoopForUI ui_loop_; 128 // Owned by TabStrip. 129 FakeBaseTabStripController* controller_; 130 // Owns |tab_strip_|. 131 views::View parent_; 132 TabStrip* tab_strip_; 133 134 private: 135 DISALLOW_COPY_AND_ASSIGN(TabStripTest); 136 }; 137 138 TEST_F(TabStripTest, GetModelCount) { 139 EXPECT_EQ(0, tab_strip_->GetModelCount()); 140 } 141 142 TEST_F(TabStripTest, IsValidModelIndex) { 143 EXPECT_FALSE(tab_strip_->IsValidModelIndex(0)); 144 } 145 146 TEST_F(TabStripTest, tab_count) { 147 EXPECT_EQ(0, tab_strip_->tab_count()); 148 } 149 150 TEST_F(TabStripTest, 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. 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 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. 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 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 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. 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. 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 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 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