Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / ui / views / tabs / tab_unittest.cc
blob42f30a43c4d2b961a9422dd5f730cb475febe498
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"
7 #include "base/i18n/rtl.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/ui/tabs/tab_utils.h"
10 #include "chrome/browser/ui/views/tabs/media_indicator_button.h"
11 #include "chrome/browser/ui/views/tabs/tab_controller.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "ui/base/models/list_selection_model.h"
14 #include "ui/views/controls/button/image_button.h"
15 #include "ui/views/controls/label.h"
16 #include "ui/views/test/views_test_base.h"
17 #include "ui/views/widget/widget.h"
19 using views::Widget;
21 class FakeTabController : public TabController {
22 public:
23 FakeTabController() : immersive_style_(false), active_tab_(false) {
25 ~FakeTabController() override {}
27 void set_immersive_style(bool value) { immersive_style_ = value; }
28 void set_active_tab(bool value) { active_tab_ = value; }
30 const ui::ListSelectionModel& GetSelectionModel() override {
31 return selection_model_;
33 bool SupportsMultipleSelection() override { return false; }
34 bool ShouldHideCloseButtonForInactiveTabs() override {
35 return false;
37 void SelectTab(Tab* tab) override {}
38 void ExtendSelectionTo(Tab* tab) override {}
39 void ToggleSelected(Tab* tab) override {}
40 void AddSelectionFromAnchorTo(Tab* tab) override {}
41 void CloseTab(Tab* tab, CloseTabSource source) override {}
42 void ToggleTabAudioMute(Tab* tab) override {}
43 void ShowContextMenuForTab(Tab* tab,
44 const gfx::Point& p,
45 ui::MenuSourceType source_type) override {}
46 bool IsActiveTab(const Tab* tab) const override { return active_tab_; }
47 bool IsTabSelected(const Tab* tab) const override { return false; }
48 bool IsTabPinned(const Tab* tab) const override { return false; }
49 void MaybeStartDrag(
50 Tab* tab,
51 const ui::LocatedEvent& event,
52 const ui::ListSelectionModel& original_selection) override {}
53 void ContinueDrag(views::View* view, const ui::LocatedEvent& event) override {
55 bool EndDrag(EndDragReason reason) override { return false; }
56 Tab* GetTabAt(Tab* tab, const gfx::Point& tab_in_tab_coordinates) override {
57 return NULL;
59 void OnMouseEventInTab(views::View* source,
60 const ui::MouseEvent& event) override {}
61 bool ShouldPaintTab(const Tab* tab, gfx::Rect* clip) override { return true; }
62 bool IsImmersiveStyle() const override { return immersive_style_; }
63 void UpdateTabAccessibilityState(const Tab* tab,
64 ui::AXViewState* state) override{};
66 private:
67 ui::ListSelectionModel selection_model_;
68 bool immersive_style_;
69 bool active_tab_;
71 DISALLOW_COPY_AND_ASSIGN(FakeTabController);
74 class TabTest : public views::ViewsTestBase,
75 public ::testing::WithParamInterface<bool> {
76 public:
77 TabTest() {}
78 virtual ~TabTest() {}
80 bool testing_for_rtl_locale() const { return GetParam(); }
82 void SetUp() override {
83 if (testing_for_rtl_locale()) {
84 original_locale_ = base::i18n::GetConfiguredLocale();
85 base::i18n::SetICUDefaultLocale("he");
87 views::ViewsTestBase::SetUp();
90 void TearDown() override {
91 views::ViewsTestBase::TearDown();
92 if (testing_for_rtl_locale())
93 base::i18n::SetICUDefaultLocale(original_locale_);
96 static void CheckForExpectedLayoutAndVisibilityOfElements(const Tab& tab) {
97 // Check whether elements are visible when they are supposed to be, given
98 // Tab size and TabRendererData state.
99 if (tab.data_.pinned) {
100 EXPECT_EQ(1, tab.IconCapacity());
101 if (tab.data_.media_state != TAB_MEDIA_STATE_NONE) {
102 EXPECT_FALSE(tab.ShouldShowIcon());
103 EXPECT_TRUE(tab.ShouldShowMediaIndicator());
104 } else {
105 EXPECT_TRUE(tab.ShouldShowIcon());
106 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
108 EXPECT_FALSE(tab.ShouldShowCloseBox());
109 } else if (tab.IsActive()) {
110 EXPECT_TRUE(tab.ShouldShowCloseBox());
111 switch (tab.IconCapacity()) {
112 case 0:
113 case 1:
114 EXPECT_FALSE(tab.ShouldShowIcon());
115 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
116 break;
117 case 2:
118 if (tab.data_.media_state != TAB_MEDIA_STATE_NONE) {
119 EXPECT_FALSE(tab.ShouldShowIcon());
120 EXPECT_TRUE(tab.ShouldShowMediaIndicator());
121 } else {
122 EXPECT_TRUE(tab.ShouldShowIcon());
123 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
125 break;
126 default:
127 EXPECT_LE(3, tab.IconCapacity());
128 EXPECT_TRUE(tab.ShouldShowIcon());
129 if (tab.data_.media_state != TAB_MEDIA_STATE_NONE)
130 EXPECT_TRUE(tab.ShouldShowMediaIndicator());
131 else
132 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
133 break;
135 } else { // Tab not active and not pinned tab.
136 switch (tab.IconCapacity()) {
137 case 0:
138 EXPECT_FALSE(tab.ShouldShowCloseBox());
139 EXPECT_FALSE(tab.ShouldShowIcon());
140 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
141 break;
142 case 1:
143 EXPECT_FALSE(tab.ShouldShowCloseBox());
144 if (tab.data_.media_state != TAB_MEDIA_STATE_NONE) {
145 EXPECT_FALSE(tab.ShouldShowIcon());
146 EXPECT_TRUE(tab.ShouldShowMediaIndicator());
147 } else {
148 EXPECT_TRUE(tab.ShouldShowIcon());
149 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
151 break;
152 default:
153 EXPECT_LE(2, tab.IconCapacity());
154 EXPECT_TRUE(tab.ShouldShowIcon());
155 if (tab.data_.media_state != TAB_MEDIA_STATE_NONE)
156 EXPECT_TRUE(tab.ShouldShowMediaIndicator());
157 else
158 EXPECT_FALSE(tab.ShouldShowMediaIndicator());
159 break;
163 // Check positioning of elements with respect to each other, and that they
164 // are fully within the contents bounds.
165 const gfx::Rect contents_bounds = tab.GetContentsBounds();
166 if (tab.ShouldShowIcon()) {
167 EXPECT_LE(contents_bounds.x(), tab.favicon_bounds_.x());
168 if (tab.title_->width() > 0)
169 EXPECT_LE(tab.favicon_bounds_.right(), tab.title_->x());
170 EXPECT_LE(contents_bounds.y(), tab.favicon_bounds_.y());
171 EXPECT_LE(tab.favicon_bounds_.bottom(), contents_bounds.bottom());
173 if (tab.ShouldShowIcon() && tab.ShouldShowMediaIndicator())
174 EXPECT_LE(tab.favicon_bounds_.right(), GetMediaIndicatorBounds(tab).x());
175 if (tab.ShouldShowMediaIndicator()) {
176 if (tab.title_->width() > 0) {
177 EXPECT_LE(tab.title_->bounds().right(),
178 GetMediaIndicatorBounds(tab).x());
180 EXPECT_LE(GetMediaIndicatorBounds(tab).right(), contents_bounds.right());
181 EXPECT_LE(contents_bounds.y(), GetMediaIndicatorBounds(tab).y());
182 EXPECT_LE(GetMediaIndicatorBounds(tab).bottom(),
183 contents_bounds.bottom());
185 if (tab.ShouldShowMediaIndicator() && tab.ShouldShowCloseBox()) {
186 // Note: The media indicator can overlap the left-insets of the close box,
187 // but should otherwise be to the left of the close button.
188 EXPECT_LE(GetMediaIndicatorBounds(tab).right(),
189 tab.close_button_->bounds().x() +
190 tab.close_button_->GetInsets().left());
192 if (tab.ShouldShowCloseBox()) {
193 // Note: The title bounds can overlap the left-insets of the close box,
194 // but should otherwise be to the left of the close button.
195 if (tab.title_->width() > 0) {
196 EXPECT_LE(tab.title_->bounds().right(),
197 tab.close_button_->bounds().x() +
198 tab.close_button_->GetInsets().left());
200 EXPECT_LE(tab.close_button_->bounds().right(), contents_bounds.right());
201 EXPECT_LE(contents_bounds.y(), tab.close_button_->bounds().y());
202 EXPECT_LE(tab.close_button_->bounds().bottom(), contents_bounds.bottom());
206 protected:
207 void InitWidget(Widget* widget) {
208 Widget::InitParams params(CreateParams(Widget::InitParams::TYPE_WINDOW));
209 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
210 params.bounds.SetRect(10, 20, 300, 400);
211 widget->Init(params);
214 private:
215 static gfx::Rect GetMediaIndicatorBounds(const Tab& tab) {
216 if (!tab.media_indicator_button_) {
217 ADD_FAILURE();
218 return gfx::Rect();
220 return tab.media_indicator_button_->bounds();
223 std::string original_locale_;
226 TEST_P(TabTest, HitTestTopPixel) {
227 if (testing_for_rtl_locale() && !base::i18n::IsRTL()) {
228 LOG(WARNING) << "Testing of RTL locale not supported on current platform.";
229 return;
232 Widget widget;
233 InitWidget(&widget);
235 FakeTabController tab_controller;
236 Tab tab(&tab_controller);
237 widget.GetContentsView()->AddChildView(&tab);
238 tab.SetBoundsRect(gfx::Rect(gfx::Point(0, 0), Tab::GetStandardSize()));
240 // Tabs have some shadow in the top, so by default we don't hit the tab there.
241 int middle_x = tab.width() / 2;
242 EXPECT_FALSE(tab.HitTestPoint(gfx::Point(middle_x, 0)));
244 // Tabs are slanted, so a click halfway down the left edge won't hit it.
245 int middle_y = tab.height() / 2;
246 EXPECT_FALSE(tab.HitTestPoint(gfx::Point(0, middle_y)));
248 // If the window is maximized, however, we want clicks in the top edge to
249 // select the tab.
250 widget.Maximize();
251 EXPECT_TRUE(tab.HitTestPoint(gfx::Point(middle_x, 0)));
253 // But clicks in the area above the slanted sides should still miss.
254 EXPECT_FALSE(tab.HitTestPoint(gfx::Point(0, 0)));
255 EXPECT_FALSE(tab.HitTestPoint(gfx::Point(tab.width() - 1, 0)));
258 TEST_P(TabTest, LayoutAndVisibilityOfElements) {
259 if (testing_for_rtl_locale() && !base::i18n::IsRTL()) {
260 LOG(WARNING) << "Testing of RTL locale not supported on current platform.";
261 return;
264 static const TabMediaState kMediaStatesToTest[] = {
265 TAB_MEDIA_STATE_NONE, TAB_MEDIA_STATE_CAPTURING,
266 TAB_MEDIA_STATE_AUDIO_PLAYING, TAB_MEDIA_STATE_AUDIO_MUTING
269 Widget widget;
270 InitWidget(&widget);
272 FakeTabController controller;
273 Tab tab(&controller);
274 widget.GetContentsView()->AddChildView(&tab);
276 SkBitmap bitmap;
277 bitmap.allocN32Pixels(16, 16);
278 TabRendererData data;
279 data.favicon = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
281 // Perform layout over all possible combinations, checking for correct
282 // results.
283 for (int is_pinned_tab = 0; is_pinned_tab < 2; ++is_pinned_tab) {
284 for (int is_active_tab = 0; is_active_tab < 2; ++is_active_tab) {
285 for (size_t media_state_index = 0;
286 media_state_index < arraysize(kMediaStatesToTest);
287 ++media_state_index) {
288 const TabMediaState media_state = kMediaStatesToTest[media_state_index];
289 SCOPED_TRACE(::testing::Message()
290 << (is_active_tab ? "Active" : "Inactive") << ' '
291 << (is_pinned_tab ? "Pinned " : "")
292 << "Tab with media indicator state " << media_state);
294 data.pinned = !!is_pinned_tab;
295 controller.set_active_tab(!!is_active_tab);
296 data.media_state = media_state;
297 tab.SetData(data);
299 // Test layout for every width from standard to minimum.
300 gfx::Rect bounds(gfx::Point(0, 0), Tab::GetStandardSize());
301 int min_width;
302 if (is_pinned_tab) {
303 bounds.set_width(Tab::GetPinnedWidth());
304 min_width = Tab::GetPinnedWidth();
305 } else {
306 min_width = is_active_tab ? Tab::GetMinimumSelectedSize().width() :
307 Tab::GetMinimumUnselectedSize().width();
309 while (bounds.width() >= min_width) {
310 SCOPED_TRACE(::testing::Message() << "bounds=" << bounds.ToString());
311 tab.SetBoundsRect(bounds); // Invokes Tab::Layout().
312 CheckForExpectedLayoutAndVisibilityOfElements(tab);
313 bounds.set_width(bounds.width() - 1);
320 // Regression test for http://crbug.com/420313: Confirms that any child Views of
321 // Tab do not attempt to provide their own tooltip behavior/text. It also tests
322 // that Tab provides the expected tooltip text (according to tab_utils).
323 TEST_P(TabTest, TooltipProvidedByTab) {
324 if (testing_for_rtl_locale() && !base::i18n::IsRTL()) {
325 LOG(WARNING) << "Testing of RTL locale not supported on current platform.";
326 return;
329 Widget widget;
330 InitWidget(&widget);
332 FakeTabController controller;
333 Tab tab(&controller);
334 widget.GetContentsView()->AddChildView(&tab);
335 tab.SetBoundsRect(gfx::Rect(Tab::GetStandardSize()));
337 SkBitmap bitmap;
338 bitmap.allocN32Pixels(16, 16);
339 TabRendererData data;
340 data.favicon = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
342 data.title = base::UTF8ToUTF16(
343 "This is a really long tab title that would case views::Label to provide "
344 "its own tooltip; but Tab should disable that feature so it can provide "
345 "the tooltip instead.");
347 // Test both with and without an indicator showing since the tab tooltip text
348 // should include a description of the media state when the indicator is
349 // present.
350 for (int i = 0; i < 2; ++i) {
351 data.media_state =
352 (i == 0 ? TAB_MEDIA_STATE_NONE : TAB_MEDIA_STATE_AUDIO_PLAYING);
353 SCOPED_TRACE(::testing::Message()
354 << "Tab with media indicator state " << data.media_state);
355 tab.SetData(data);
357 for (int j = 0; j < tab.child_count(); ++j) {
358 views::View& child = *tab.child_at(j);
359 if (!strcmp(child.GetClassName(), "TabCloseButton"))
360 continue; // Close button is excepted.
361 if (!child.visible())
362 continue;
363 SCOPED_TRACE(::testing::Message() << "child_at(" << j << "): "
364 << child.GetClassName());
366 const gfx::Point midpoint(child.width() / 2, child.height() / 2);
367 EXPECT_FALSE(child.GetTooltipHandlerForPoint(midpoint));
368 const gfx::Point mouse_hover_point =
369 midpoint + child.GetMirroredPosition().OffsetFromOrigin();
370 base::string16 tooltip;
371 EXPECT_TRUE(static_cast<views::View&>(tab).GetTooltipText(
372 mouse_hover_point, &tooltip));
373 EXPECT_EQ(chrome::AssembleTabTooltipText(data.title, data.media_state),
374 tooltip);
379 // Regression test for http://crbug.com/226253. Calling Layout() more than once
380 // shouldn't change the insets of the close button.
381 TEST_P(TabTest, CloseButtonLayout) {
382 if (testing_for_rtl_locale() && !base::i18n::IsRTL()) {
383 LOG(WARNING) << "Testing of RTL locale not supported on current platform.";
384 return;
387 FakeTabController tab_controller;
388 Tab tab(&tab_controller);
389 tab.SetBounds(0, 0, 100, 50);
390 tab.Layout();
391 gfx::Insets close_button_insets = tab.close_button_->GetInsets();
392 tab.Layout();
393 gfx::Insets close_button_insets_2 = tab.close_button_->GetInsets();
394 EXPECT_EQ(close_button_insets.top(), close_button_insets_2.top());
395 EXPECT_EQ(close_button_insets.left(), close_button_insets_2.left());
396 EXPECT_EQ(close_button_insets.bottom(), close_button_insets_2.bottom());
397 EXPECT_EQ(close_button_insets.right(), close_button_insets_2.right());
399 // Also make sure the close button is sized as large as the tab.
400 EXPECT_EQ(50, tab.close_button_->bounds().height());
403 // Test in both a LTR and a RTL locale. Note: The fact that the UI code is
404 // configured for an RTL locale does *not* change how the coordinates are
405 // examined in the tests above because views::View and friends are supposed to
406 // auto-mirror the widgets when painting. Thus, what we're testing here is that
407 // there's no code in Tab that will erroneously subvert this automatic
408 // coordinate translation. http://crbug.com/384179
409 INSTANTIATE_TEST_CASE_P(, TabTest, ::testing::Values(false, true));