Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / tabs / tab_strip_unittest.cc
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   ~TestTabStripObserver() override {
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   void TabStripAddedTabAt(TabStrip* tab_strip, int index) override {
63     last_tab_added_ = index;
64   }
65
66   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   void TabStripRemovedTabAt(TabStrip* tab_strip, int index) override {
74     last_tab_removed_ = index;
75   }
76
77   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   ~TabStripTest() override {}
100
101   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   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 }