Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / tabs / tab_unittest.cc
blob23d564d07c08569f8d79f152fce868cedcd93685
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.h"
6 #include "chrome/browser/ui/views/tabs/tab_controller.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "ui/base/models/list_selection_model.h"
11 #include "ui/views/controls/button/image_button.h"
12 #include "ui/views/test/views_test_base.h"
13 #include "ui/views/widget/widget.h"
15 using views::Widget;
17 class FakeTabController : public TabController {
18 public:
19 FakeTabController() : immersive_style_(false), active_tab_(false) {
21 virtual ~FakeTabController() {}
23 void set_immersive_style(bool value) { immersive_style_ = value; }
24 void set_active_tab(bool value) { active_tab_ = value; }
26 virtual const ui::ListSelectionModel& GetSelectionModel() OVERRIDE {
27 return selection_model_;
29 virtual bool SupportsMultipleSelection() OVERRIDE { return false; }
30 virtual void SelectTab(Tab* tab) OVERRIDE {}
31 virtual void ExtendSelectionTo(Tab* tab) OVERRIDE {}
32 virtual void ToggleSelected(Tab* tab) OVERRIDE {}
33 virtual void AddSelectionFromAnchorTo(Tab* tab) OVERRIDE {}
34 virtual void CloseTab(Tab* tab, CloseTabSource source) OVERRIDE {}
35 virtual void ShowContextMenuForTab(Tab* tab,
36 const gfx::Point& p,
37 ui::MenuSourceType source_type) OVERRIDE {}
38 virtual bool IsActiveTab(const Tab* tab) const OVERRIDE {
39 return active_tab_;
41 virtual bool IsTabSelected(const Tab* tab) const OVERRIDE {
42 return false;
44 virtual bool IsTabPinned(const Tab* tab) const OVERRIDE { return false; }
45 virtual void MaybeStartDrag(
46 Tab* tab,
47 const ui::LocatedEvent& event,
48 const ui::ListSelectionModel& original_selection) OVERRIDE {}
49 virtual void ContinueDrag(views::View* view,
50 const ui::LocatedEvent& event) OVERRIDE {}
51 virtual bool EndDrag(EndDragReason reason) OVERRIDE { return false; }
52 virtual Tab* GetTabAt(Tab* tab,
53 const gfx::Point& tab_in_tab_coordinates) OVERRIDE {
54 return NULL;
56 virtual void OnMouseEventInTab(views::View* source,
57 const ui::MouseEvent& event) OVERRIDE {}
58 virtual bool ShouldPaintTab(const Tab* tab, gfx::Rect* clip) OVERRIDE {
59 return true;
61 virtual bool IsImmersiveStyle() const OVERRIDE { return immersive_style_; }
63 private:
64 ui::ListSelectionModel selection_model_;
65 bool immersive_style_;
66 bool active_tab_;
68 DISALLOW_COPY_AND_ASSIGN(FakeTabController);
71 class TabTest : public views::ViewsTestBase {
72 public:
73 TabTest() {}
74 virtual ~TabTest() {}
76 static void DisableMediaIndicatorAnimation(Tab* tab) {
77 tab->media_indicator_animation_.reset();
78 tab->animating_media_state_ = tab->data_.media_state;
81 static void CheckForExpectedLayoutAndVisibilityOfElements(const Tab& tab) {
82 // Check whether elements are visible when they are supposed to be, given
83 // Tab size and TabRendererData state.
84 if (tab.data_.mini) {
85 EXPECT_EQ(1, tab.IconCapacity());
86 if (tab.data_.media_state != TAB_MEDIA_STATE_NONE) {
87 EXPECT_FALSE(tab.ShouldShowIcon());
88 EXPECT_TRUE(tab.ShouldShowMediaIndicator());
89 } else {
90 EXPECT_TRUE(tab.ShouldShowIcon());
91 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
93 EXPECT_FALSE(tab.ShouldShowCloseBox());
94 } else if (tab.IsActive()) {
95 EXPECT_TRUE(tab.ShouldShowCloseBox());
96 switch (tab.IconCapacity()) {
97 case 0:
98 case 1:
99 EXPECT_FALSE(tab.ShouldShowIcon());
100 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
101 break;
102 case 2:
103 if (tab.data_.media_state != TAB_MEDIA_STATE_NONE) {
104 EXPECT_FALSE(tab.ShouldShowIcon());
105 EXPECT_TRUE(tab.ShouldShowMediaIndicator());
106 } else {
107 EXPECT_TRUE(tab.ShouldShowIcon());
108 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
110 break;
111 default:
112 EXPECT_LE(3, tab.IconCapacity());
113 EXPECT_TRUE(tab.ShouldShowIcon());
114 if (tab.data_.media_state != TAB_MEDIA_STATE_NONE)
115 EXPECT_TRUE(tab.ShouldShowMediaIndicator());
116 else
117 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
118 break;
120 } else { // Tab not active and not mini tab.
121 switch (tab.IconCapacity()) {
122 case 0:
123 EXPECT_FALSE(tab.ShouldShowCloseBox());
124 EXPECT_FALSE(tab.ShouldShowIcon());
125 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
126 break;
127 case 1:
128 EXPECT_FALSE(tab.ShouldShowCloseBox());
129 if (tab.data_.media_state != TAB_MEDIA_STATE_NONE) {
130 EXPECT_FALSE(tab.ShouldShowIcon());
131 EXPECT_TRUE(tab.ShouldShowMediaIndicator());
132 } else {
133 EXPECT_TRUE(tab.ShouldShowIcon());
134 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
136 break;
137 default:
138 EXPECT_LE(2, tab.IconCapacity());
139 EXPECT_TRUE(tab.ShouldShowIcon());
140 if (tab.data_.media_state != TAB_MEDIA_STATE_NONE)
141 EXPECT_TRUE(tab.ShouldShowMediaIndicator());
142 else
143 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
144 break;
148 // Check positioning of elements with respect to each other, and that they
149 // are fully within the contents bounds.
150 const gfx::Rect contents_bounds = tab.GetContentsBounds();
151 if (tab.ShouldShowIcon()) {
152 EXPECT_LE(contents_bounds.x(), tab.favicon_bounds_.x());
153 if (tab.title_bounds_.width() > 0)
154 EXPECT_LE(tab.favicon_bounds_.right(), tab.title_bounds_.x());
155 EXPECT_LE(contents_bounds.y(), tab.favicon_bounds_.y());
156 EXPECT_LE(tab.favicon_bounds_.bottom(), contents_bounds.bottom());
158 if (tab.ShouldShowIcon() && tab.ShouldShowMediaIndicator())
159 EXPECT_LE(tab.favicon_bounds_.right(), tab.media_indicator_bounds_.x());
160 if (tab.ShouldShowMediaIndicator()) {
161 if (tab.title_bounds_.width() > 0)
162 EXPECT_LE(tab.title_bounds_.right(), tab.media_indicator_bounds_.x());
163 EXPECT_LE(tab.media_indicator_bounds_.right(), contents_bounds.right());
164 EXPECT_LE(contents_bounds.y(), tab.media_indicator_bounds_.y());
165 EXPECT_LE(tab.media_indicator_bounds_.bottom(), contents_bounds.bottom());
167 if (tab.ShouldShowMediaIndicator() && tab.ShouldShowCloseBox()) {
168 // Note: The media indicator can overlap the left-insets of the close box,
169 // but should otherwise be to the left of the close button.
170 EXPECT_LE(tab.media_indicator_bounds_.right(),
171 tab.close_button_->bounds().x() +
172 tab.close_button_->GetInsets().left());
174 if (tab.ShouldShowCloseBox()) {
175 // Note: The title bounds can overlap the left-insets of the close box,
176 // but should otherwise be to the left of the close button.
177 if (tab.title_bounds_.width() > 0) {
178 EXPECT_LE(tab.title_bounds_.right(),
179 tab.close_button_->bounds().x() +
180 tab.close_button_->GetInsets().left());
182 EXPECT_LE(tab.close_button_->bounds().right(), contents_bounds.right());
183 EXPECT_LE(contents_bounds.y(), tab.close_button_->bounds().y());
184 EXPECT_LE(tab.close_button_->bounds().bottom(), contents_bounds.bottom());
189 TEST_F(TabTest, HitTestTopPixel) {
190 Widget widget;
191 Widget::InitParams params(CreateParams(Widget::InitParams::TYPE_WINDOW));
192 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
193 params.bounds.SetRect(10, 20, 300, 400);
194 widget.Init(params);
196 FakeTabController tab_controller;
197 Tab tab(&tab_controller);
198 widget.GetContentsView()->AddChildView(&tab);
199 tab.SetBoundsRect(gfx::Rect(gfx::Point(0, 0), Tab::GetStandardSize()));
201 // Tabs have some shadow in the top, so by default we don't hit the tab there.
202 int middle_x = tab.width() / 2;
203 EXPECT_FALSE(tab.HitTestPoint(gfx::Point(middle_x, 0)));
205 // Tabs are slanted, so a click halfway down the left edge won't hit it.
206 int middle_y = tab.height() / 2;
207 EXPECT_FALSE(tab.HitTestPoint(gfx::Point(0, middle_y)));
209 // If the window is maximized, however, we want clicks in the top edge to
210 // select the tab.
211 widget.Maximize();
212 EXPECT_TRUE(tab.HitTestPoint(gfx::Point(middle_x, 0)));
214 // But clicks in the area above the slanted sides should still miss.
215 EXPECT_FALSE(tab.HitTestPoint(gfx::Point(0, 0)));
216 EXPECT_FALSE(tab.HitTestPoint(gfx::Point(tab.width() - 1, 0)));
219 TEST_F(TabTest, LayoutAndVisibilityOfElements) {
220 static const TabMediaState kMediaStatesToTest[] = {
221 TAB_MEDIA_STATE_NONE, TAB_MEDIA_STATE_CAPTURING,
222 TAB_MEDIA_STATE_AUDIO_PLAYING
225 FakeTabController controller;
226 Tab tab(&controller);
228 SkBitmap bitmap;
229 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
230 bitmap.allocPixels();
231 TabRendererData data;
232 data.favicon = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
234 // Perform layout over all possible combinations, checking for correct
235 // results.
236 for (int is_mini_tab = 0; is_mini_tab < 2; ++is_mini_tab) {
237 for (int is_active_tab = 0; is_active_tab < 2; ++is_active_tab) {
238 for (size_t media_state_index = 0;
239 media_state_index < arraysize(kMediaStatesToTest);
240 ++media_state_index) {
241 const TabMediaState media_state = kMediaStatesToTest[media_state_index];
242 SCOPED_TRACE(::testing::Message()
243 << (is_active_tab ? "Active" : "Inactive") << ' '
244 << (is_mini_tab ? "Mini " : "")
245 << "Tab with media indicator state " << media_state);
247 data.mini = !!is_mini_tab;
248 controller.set_active_tab(!!is_active_tab);
249 data.media_state = media_state;
250 tab.SetData(data);
252 // Disable the media indicator animation so that the layout/visibility
253 // logic can be tested effectively. If the animation was left enabled,
254 // the ShouldShowMediaIndicator() method would return true during
255 // fade-out transitions.
256 DisableMediaIndicatorAnimation(&tab);
258 // Test layout for every width from standard to minimum.
259 gfx::Rect bounds(gfx::Point(0, 0), Tab::GetStandardSize());
260 int min_width;
261 if (is_mini_tab) {
262 bounds.set_width(Tab::GetMiniWidth());
263 min_width = Tab::GetMiniWidth();
264 } else {
265 min_width = is_active_tab ? Tab::GetMinimumSelectedSize().width() :
266 Tab::GetMinimumUnselectedSize().width();
268 while (bounds.width() >= min_width) {
269 SCOPED_TRACE(::testing::Message() << "bounds=" << bounds.ToString());
270 tab.SetBoundsRect(bounds); // Invokes Tab::Layout().
271 CheckForExpectedLayoutAndVisibilityOfElements(tab);
272 bounds.set_width(bounds.width() - 1);
279 // Regression test for http://crbug.com/226253. Calling Layout() more than once
280 // shouldn't change the insets of the close button.
281 TEST_F(TabTest, CloseButtonLayout) {
282 FakeTabController tab_controller;
283 Tab tab(&tab_controller);
284 tab.SetBounds(0, 0, 100, 50);
285 tab.Layout();
286 gfx::Insets close_button_insets = tab.close_button_->GetInsets();
287 tab.Layout();
288 gfx::Insets close_button_insets_2 = tab.close_button_->GetInsets();
289 EXPECT_EQ(close_button_insets.top(), close_button_insets_2.top());
290 EXPECT_EQ(close_button_insets.left(), close_button_insets_2.left());
291 EXPECT_EQ(close_button_insets.bottom(), close_button_insets_2.bottom());
292 EXPECT_EQ(close_button_insets.right(), close_button_insets_2.right());
294 // Also make sure the close button is sized as large as the tab.
295 EXPECT_EQ(50, tab.close_button_->bounds().height());