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/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" 22 23 namespace { 24 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(); 31 } 32 return current; 33 } 34 35 } // namespace 36 37 class TestTabStripObserver : public TabStripObserver { 38 public: 39 explicit TestTabStripObserver(TabStrip* tab_strip) 40 : tab_strip_(tab_strip), 41 last_tab_added_(-1), 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); 47 } 48 49 virtual ~TestTabStripObserver() { 50 if (tab_strip_) 51 tab_strip_->RemoveObserver(this); 52 } 53 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_; } 59 60 private: 61 // TabStripObserver overrides. 62 virtual void TabStripAddedTabAt(TabStrip* tab_strip, int index) OVERRIDE { 63 last_tab_added_ = index; 64 } 65 66 virtual void TabStripMovedTab(TabStrip* tab_strip, 67 int from_index, 68 int to_index) OVERRIDE { 69 last_tab_moved_from_ = from_index; 70 last_tab_moved_to_ = to_index; 71 } 72 73 virtual void TabStripRemovedTabAt(TabStrip* tab_strip, int index) OVERRIDE { 74 last_tab_removed_ = index; 75 } 76 77 virtual void TabStripDeleted(TabStrip* tab_strip) OVERRIDE { 78 tabstrip_deleted_ = true; 79 tab_strip_ = NULL; 80 } 81 82 TabStrip* tab_strip_; 83 int last_tab_added_; 84 int last_tab_removed_; 85 int last_tab_moved_from_; 86 int last_tab_moved_to_; 87 bool tabstrip_deleted_; 88 89 DISALLOW_COPY_AND_ASSIGN(TestTabStripObserver); 90 }; 91 92 class TabStripTest : public views::ViewsTestBase { 93 public: 94 TabStripTest() 95 : controller_(NULL), 96 tab_strip_(NULL) { 97 } 98 99 virtual ~TabStripTest() {} 100 101 virtual void SetUp() OVERRIDE { 102 views::ViewsTestBase::SetUp(); 103 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(); 110 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_); 119 } 120 121 virtual void TearDown() OVERRIDE { 122 widget_.reset(); 123 views::ViewsTestBase::TearDown(); 124 } 125 126 protected: 127 // Returns the rectangular hit test region of |tab| in |tab|'s local 128 // coordinate space. 129 gfx::Rect GetTabHitTestMask(Tab* tab) { 130 views::ViewTargeter* targeter = tab->targeter(); 131 DCHECK(targeter); 132 views::MaskedTargeterDelegate* delegate = 133 static_cast<views::MaskedTargeterDelegate*>(tab); 134 135 gfx::Path mask; 136 bool valid_mask = delegate->GetHitTestMask(&mask); 137 DCHECK(valid_mask); 138 139 return gfx::ToEnclosingRect((gfx::SkRectToRectF(mask.getBounds()))); 140 } 141 142 // Returns the rectangular hit test region of the tab close button of 143 // |tab| in |tab|'s coordinate space (including padding if |padding| 144 // is true). 145 gfx::Rect GetTabCloseHitTestMask(Tab* tab, bool padding) { 146 gfx::RectF bounds_f = tab->close_button_->GetContentsBounds(); 147 if (padding) 148 bounds_f = tab->close_button_->GetLocalBounds(); 149 views::View::ConvertRectToTarget(tab->close_button_, tab, &bounds_f); 150 return gfx::ToEnclosingRect(bounds_f); 151 } 152 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); 159 } 160 161 // Owned by TabStrip. 162 FakeBaseTabStripController* controller_; 163 // Owns |tab_strip_|. 164 views::View parent_; 165 TabStrip* tab_strip_; 166 scoped_ptr<views::Widget> widget_; 167 168 private: 169 DISALLOW_COPY_AND_ASSIGN(TabStripTest); 170 }; 171 172 TEST_F(TabStripTest, GetModelCount) { 173 EXPECT_EQ(0, tab_strip_->GetModelCount()); 174 } 175 176 TEST_F(TabStripTest, IsValidModelIndex) { 177 EXPECT_FALSE(tab_strip_->IsValidModelIndex(0)); 178 } 179 180 TEST_F(TabStripTest, tab_count) { 181 EXPECT_EQ(0, tab_strip_->tab_count()); 182 } 183 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); 191 } 192 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); 199 delete tab_strip; 200 EXPECT_TRUE(observer.tabstrip_deleted()); 201 } 202 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)); 215 } 216 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()); 235 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()); 240 } 241 242 TEST_F(TabStripTest, VisibilityInOverflow) { 243 tab_strip_->SetBounds(0, 0, 200, 20); 244 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 247 // overflow. 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()) 252 break; 253 } 254 EXPECT_GT(invisible_tab_index, 0); 255 EXPECT_LT(invisible_tab_index, 100); 256 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()); 260 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()); 264 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()); 268 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); 272 int i = 0; 273 for (; i < invisible_tab_index; ++i) { 274 if (!tab_strip_->tab_at(i)->visible()) 275 break; 276 } 277 ASSERT_GT(i, 0); 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()); 282 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()); 291 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()); 296 } 297 298 TEST_F(TabStripTest, ImmersiveMode) { 299 // Immersive mode defaults to off. 300 EXPECT_FALSE(tab_strip_->IsImmersiveStyle()); 301 302 // Tab strip defaults to normal tab height. 303 int normal_height = Tab::GetMinimumUnselectedSize().height(); 304 EXPECT_EQ(normal_height, tab_strip_->GetPreferredSize().height()); 305 306 // Tab strip can toggle immersive mode. 307 tab_strip_->SetImmersiveStyle(true); 308 EXPECT_TRUE(tab_strip_->IsImmersiveStyle()); 309 310 // Now tabs have the immersive height. 311 int immersive_height = Tab::GetImmersiveHeight(); 312 EXPECT_EQ(immersive_height, tab_strip_->GetPreferredSize().height()); 313 314 // Sanity-check immersive tabs are shorter than normal tabs. 315 EXPECT_LT(immersive_height, normal_height); 316 } 317 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); 323 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()); 329 330 Tab* left_tab = tab_strip_->tab_at(0); 331 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20))); 332 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()); 336 337 Tab* right_tab = tab_strip_->tab_at(2); 338 right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20))); 339 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))); 343 344 // Switch to stacked layout mode and force a layout to ensure tabs stack. 345 tab_strip_->SetStackedLayout(true); 346 tab_strip_->DoLayout(); 347 348 349 // Tests involving |left_tab|, which has part of its bounds and its tab 350 // close button completely occluded by |active_tab|. 351 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()); 355 356 // Bounds of the tab close button (without padding) in the tab's 357 // coordinate space. 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()); 361 362 // Verify that the tab close button is completely occluded. 363 EXPECT_FALSE(tab_bounds.Contains(contents_bounds)); 364 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))); 371 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))); 376 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))); 380 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))); 388 389 390 // Tests involving |active_tab|, which is completely visible. 391 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()); 397 398 // Verify that the tab close button is not occluded. 399 EXPECT_TRUE(tab_bounds.Contains(contents_bounds)); 400 401 // Bounds of the tab close button (without padding) in the tab's 402 // coordinate space. 403 gfx::Rect local_bounds = GetTabCloseHitTestMask(active_tab, true); 404 EXPECT_EQ(gfx::Rect(81, 0, 39, 29).ToString(), local_bounds.ToString()); 405 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))); 409 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))); 418 419 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. 422 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()); 430 431 // Verify that the tab close button is not occluded. 432 EXPECT_TRUE(tab_bounds.Contains(contents_bounds)); 433 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))); 437 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))); 441 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))); 451 } 452 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 455 // tab close button. 456 TEST_F(TabStripTest, ClippedTabCloseButton) { 457 tab_strip_->SetBounds(0, 0, 220, 20); 458 459 controller_->AddTab(0, false); 460 controller_->AddTab(1, true); 461 ASSERT_EQ(2, tab_strip_->tab_count()); 462 463 Tab* left_tab = tab_strip_->tab_at(0); 464 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20))); 465 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()); 469 470 // Switch to stacked layout mode and force a layout to ensure tabs stack. 471 tab_strip_->SetStackedLayout(true); 472 tab_strip_->DoLayout(); 473 474 475 // Tests involving |left_tab|, which has part of its bounds and its tab 476 // close button partially occluded by |active_tab|. 477 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()); 481 482 // Bounds of the tab close button (without padding) in the tab's 483 // coordinate space. 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()); 487 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)); 491 492 views::ImageButton* left_close = left_tab->close_button_; 493 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))); 501 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))); 509 } 510 511 TEST_F(TabStripTest, GetEventHandlerForOverlappingArea) { 512 tab_strip_->SetBounds(0, 0, 1000, 20); 513 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()); 519 520 // Verify that the active tab will be a tooltip handler for points that hit 521 // it. 522 Tab* left_tab = tab_strip_->tab_at(0); 523 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20))); 524 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()); 528 529 Tab* right_tab = tab_strip_->tab_at(2); 530 right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20))); 531 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))); 535 536 // Test that active tabs gets events from area in which it overlaps with its 537 // left neighbour. 538 gfx::Point left_overlap( 539 (active_tab->x() + left_tab->bounds().right() + 1) / 2, 540 active_tab->bounds().bottom() - 1); 541 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)); 545 546 EXPECT_EQ(active_tab, 547 FindTabView(tab_strip_->GetEventHandlerForPoint(left_overlap))); 548 549 // Test that active tabs gets events from area in which it overlaps with its 550 // right neighbour. 551 gfx::Point right_overlap((active_tab->bounds().right() + right_tab->x()) / 2, 552 active_tab->bounds().bottom() - 1); 553 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)); 557 558 EXPECT_EQ(active_tab, 559 FindTabView(tab_strip_->GetEventHandlerForPoint(right_overlap))); 560 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); 565 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)); 569 570 EXPECT_EQ(right_tab, 571 FindTabView(tab_strip_->GetEventHandlerForPoint(unactive_overlap))); 572 } 573 574 TEST_F(TabStripTest, GetTooltipHandler) { 575 tab_strip_->SetBounds(0, 0, 1000, 20); 576 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()); 582 583 // Verify that the active tab will be a tooltip handler for points that hit 584 // it. 585 Tab* left_tab = tab_strip_->tab_at(0); 586 left_tab->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(200, 20))); 587 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()); 591 592 Tab* right_tab = tab_strip_->tab_at(2); 593 right_tab->SetBoundsRect(gfx::Rect(gfx::Point(300, 0), gfx::Size(200, 20))); 594 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))); 598 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); 604 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)); 608 609 EXPECT_EQ(active_tab, 610 FindTabView(tab_strip_->GetTooltipHandlerForPoint(left_overlap))); 611 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); 616 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)); 620 621 EXPECT_EQ(active_tab, 622 FindTabView(tab_strip_->GetTooltipHandlerForPoint(right_overlap))); 623 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); 628 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)); 632 633 EXPECT_EQ( 634 right_tab, 635 FindTabView(tab_strip_->GetTooltipHandlerForPoint(unactive_overlap))); 636 637 // Confirm that tab strip doe not return tooltip handler for points that 638 // don't hit it. 639 EXPECT_FALSE(tab_strip_->GetTooltipHandlerForPoint(gfx::Point(-1, 2))); 640 } 641