Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / tabs / tab_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.h"
6
7 #include "base/i18n/rtl.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/ui/views/tabs/media_indicator_button.h"
10 #include "chrome/browser/ui/views/tabs/tab_controller.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "ui/base/models/list_selection_model.h"
13 #include "ui/views/controls/button/image_button.h"
14 #include "ui/views/controls/label.h"
15 #include "ui/views/test/views_test_base.h"
16 #include "ui/views/widget/widget.h"
17
18 using views::Widget;
19
20 class FakeTabController : public TabController {
21  public:
22   FakeTabController() : immersive_style_(false), active_tab_(false) {
23   }
24   virtual ~FakeTabController() {}
25
26   void set_immersive_style(bool value) { immersive_style_ = value; }
27   void set_active_tab(bool value) { active_tab_ = value; }
28
29   virtual const ui::ListSelectionModel& GetSelectionModel() OVERRIDE {
30     return selection_model_;
31   }
32   virtual bool SupportsMultipleSelection() OVERRIDE { return false; }
33   virtual void SelectTab(Tab* tab) OVERRIDE {}
34   virtual void ExtendSelectionTo(Tab* tab) OVERRIDE {}
35   virtual void ToggleSelected(Tab* tab) OVERRIDE {}
36   virtual void AddSelectionFromAnchorTo(Tab* tab) OVERRIDE {}
37   virtual void CloseTab(Tab* tab, CloseTabSource source) OVERRIDE {}
38   virtual void ToggleTabAudioMute(Tab* tab) OVERRIDE {}
39   virtual void ShowContextMenuForTab(Tab* tab,
40                                      const gfx::Point& p,
41                                      ui::MenuSourceType source_type) OVERRIDE {}
42   virtual bool IsActiveTab(const Tab* tab) const OVERRIDE {
43     return active_tab_;
44   }
45   virtual bool IsTabSelected(const Tab* tab) const OVERRIDE {
46     return false;
47   }
48   virtual bool IsTabPinned(const Tab* tab) const OVERRIDE { return false; }
49   virtual void MaybeStartDrag(
50       Tab* tab,
51       const ui::LocatedEvent& event,
52       const ui::ListSelectionModel& original_selection) OVERRIDE {}
53   virtual void ContinueDrag(views::View* view,
54                             const ui::LocatedEvent& event) OVERRIDE {}
55   virtual bool EndDrag(EndDragReason reason) OVERRIDE { return false; }
56   virtual Tab* GetTabAt(Tab* tab,
57                         const gfx::Point& tab_in_tab_coordinates) OVERRIDE {
58     return NULL;
59   }
60   virtual void OnMouseEventInTab(views::View* source,
61                                  const ui::MouseEvent& event) OVERRIDE {}
62   virtual bool ShouldPaintTab(const Tab* tab, gfx::Rect* clip) OVERRIDE {
63     return true;
64   }
65   virtual bool IsImmersiveStyle() const OVERRIDE { return immersive_style_; }
66   virtual void UpdateTabAccessibilityState(const Tab* tab,
67                                            ui::AXViewState* state) OVERRIDE{};
68
69  private:
70   ui::ListSelectionModel selection_model_;
71   bool immersive_style_;
72   bool active_tab_;
73
74   DISALLOW_COPY_AND_ASSIGN(FakeTabController);
75 };
76
77 class TabTest : public views::ViewsTestBase,
78                 public ::testing::WithParamInterface<bool> {
79  public:
80   TabTest() {}
81   virtual ~TabTest() {}
82
83   bool testing_for_rtl_locale() const { return GetParam(); }
84
85   virtual void SetUp() OVERRIDE {
86     if (testing_for_rtl_locale()) {
87       original_locale_ = base::i18n::GetConfiguredLocale();
88       base::i18n::SetICUDefaultLocale("he");
89     }
90     views::ViewsTestBase::SetUp();
91   }
92
93   virtual void TearDown() OVERRIDE {
94     views::ViewsTestBase::TearDown();
95     if (testing_for_rtl_locale())
96       base::i18n::SetICUDefaultLocale(original_locale_);
97   }
98
99   static void CheckForExpectedLayoutAndVisibilityOfElements(const Tab& tab) {
100     // Check whether elements are visible when they are supposed to be, given
101     // Tab size and TabRendererData state.
102     if (tab.data_.mini) {
103       EXPECT_EQ(1, tab.IconCapacity());
104       if (tab.data_.media_state != TAB_MEDIA_STATE_NONE) {
105         EXPECT_FALSE(tab.ShouldShowIcon());
106         EXPECT_TRUE(tab.ShouldShowMediaIndicator());
107       } else {
108         EXPECT_TRUE(tab.ShouldShowIcon());
109         EXPECT_FALSE(tab.ShouldShowMediaIndicator());
110       }
111       EXPECT_FALSE(tab.ShouldShowCloseBox());
112     } else if (tab.IsActive()) {
113       EXPECT_TRUE(tab.ShouldShowCloseBox());
114       switch (tab.IconCapacity()) {
115         case 0:
116         case 1:
117           EXPECT_FALSE(tab.ShouldShowIcon());
118           EXPECT_FALSE(tab.ShouldShowMediaIndicator());
119           break;
120         case 2:
121           if (tab.data_.media_state != TAB_MEDIA_STATE_NONE) {
122             EXPECT_FALSE(tab.ShouldShowIcon());
123             EXPECT_TRUE(tab.ShouldShowMediaIndicator());
124           } else {
125             EXPECT_TRUE(tab.ShouldShowIcon());
126             EXPECT_FALSE(tab.ShouldShowMediaIndicator());
127           }
128           break;
129         default:
130           EXPECT_LE(3, tab.IconCapacity());
131           EXPECT_TRUE(tab.ShouldShowIcon());
132           if (tab.data_.media_state != TAB_MEDIA_STATE_NONE)
133             EXPECT_TRUE(tab.ShouldShowMediaIndicator());
134           else
135             EXPECT_FALSE(tab.ShouldShowMediaIndicator());
136           break;
137       }
138     } else {  // Tab not active and not mini tab.
139       switch (tab.IconCapacity()) {
140         case 0:
141           EXPECT_FALSE(tab.ShouldShowCloseBox());
142           EXPECT_FALSE(tab.ShouldShowIcon());
143           EXPECT_FALSE(tab.ShouldShowMediaIndicator());
144           break;
145         case 1:
146           EXPECT_FALSE(tab.ShouldShowCloseBox());
147           if (tab.data_.media_state != TAB_MEDIA_STATE_NONE) {
148             EXPECT_FALSE(tab.ShouldShowIcon());
149             EXPECT_TRUE(tab.ShouldShowMediaIndicator());
150           } else {
151             EXPECT_TRUE(tab.ShouldShowIcon());
152             EXPECT_FALSE(tab.ShouldShowMediaIndicator());
153           }
154           break;
155         default:
156           EXPECT_LE(2, tab.IconCapacity());
157           EXPECT_TRUE(tab.ShouldShowIcon());
158           if (tab.data_.media_state != TAB_MEDIA_STATE_NONE)
159             EXPECT_TRUE(tab.ShouldShowMediaIndicator());
160           else
161             EXPECT_FALSE(tab.ShouldShowMediaIndicator());
162           break;
163       }
164     }
165
166     // Check positioning of elements with respect to each other, and that they
167     // are fully within the contents bounds.
168     const gfx::Rect contents_bounds = tab.GetContentsBounds();
169     if (tab.ShouldShowIcon()) {
170       EXPECT_LE(contents_bounds.x(), tab.favicon_bounds_.x());
171       if (tab.title_->width() > 0)
172         EXPECT_LE(tab.favicon_bounds_.right(), tab.title_->x());
173       EXPECT_LE(contents_bounds.y(), tab.favicon_bounds_.y());
174       EXPECT_LE(tab.favicon_bounds_.bottom(), contents_bounds.bottom());
175     }
176     if (tab.ShouldShowIcon() && tab.ShouldShowMediaIndicator())
177       EXPECT_LE(tab.favicon_bounds_.right(), GetMediaIndicatorBounds(tab).x());
178     if (tab.ShouldShowMediaIndicator()) {
179       if (tab.title_->width() > 0) {
180         EXPECT_LE(tab.title_->bounds().right(),
181                   GetMediaIndicatorBounds(tab).x());
182       }
183       EXPECT_LE(GetMediaIndicatorBounds(tab).right(), contents_bounds.right());
184       EXPECT_LE(contents_bounds.y(), GetMediaIndicatorBounds(tab).y());
185       EXPECT_LE(GetMediaIndicatorBounds(tab).bottom(),
186                 contents_bounds.bottom());
187     }
188     if (tab.ShouldShowMediaIndicator() && tab.ShouldShowCloseBox()) {
189       // Note: The media indicator can overlap the left-insets of the close box,
190       // but should otherwise be to the left of the close button.
191       EXPECT_LE(GetMediaIndicatorBounds(tab).right(),
192                 tab.close_button_->bounds().x() +
193                     tab.close_button_->GetInsets().left());
194     }
195     if (tab.ShouldShowCloseBox()) {
196       // Note: The title bounds can overlap the left-insets of the close box,
197       // but should otherwise be to the left of the close button.
198       if (tab.title_->width() > 0) {
199         EXPECT_LE(tab.title_->bounds().right(),
200                   tab.close_button_->bounds().x() +
201                       tab.close_button_->GetInsets().left());
202       }
203       EXPECT_LE(tab.close_button_->bounds().right(), contents_bounds.right());
204       EXPECT_LE(contents_bounds.y(), tab.close_button_->bounds().y());
205       EXPECT_LE(tab.close_button_->bounds().bottom(), contents_bounds.bottom());
206     }
207   }
208
209  private:
210   static gfx::Rect GetMediaIndicatorBounds(const Tab& tab) {
211     if (!tab.media_indicator_button_) {
212       ADD_FAILURE();
213       return gfx::Rect();
214     }
215     return tab.media_indicator_button_->bounds();
216   }
217
218   std::string original_locale_;
219 };
220
221 TEST_P(TabTest, HitTestTopPixel) {
222   if (testing_for_rtl_locale() && !base::i18n::IsRTL()) {
223     LOG(WARNING) << "Testing of RTL locale not supported on current platform.";
224     return;
225   }
226
227   Widget widget;
228   Widget::InitParams params(CreateParams(Widget::InitParams::TYPE_WINDOW));
229   params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
230   params.bounds.SetRect(10, 20, 300, 400);
231   widget.Init(params);
232
233   FakeTabController tab_controller;
234   Tab tab(&tab_controller);
235   widget.GetContentsView()->AddChildView(&tab);
236   tab.SetBoundsRect(gfx::Rect(gfx::Point(0, 0), Tab::GetStandardSize()));
237
238   // Tabs have some shadow in the top, so by default we don't hit the tab there.
239   int middle_x = tab.width() / 2;
240   EXPECT_FALSE(tab.HitTestPoint(gfx::Point(middle_x, 0)));
241
242   // Tabs are slanted, so a click halfway down the left edge won't hit it.
243   int middle_y = tab.height() / 2;
244   EXPECT_FALSE(tab.HitTestPoint(gfx::Point(0, middle_y)));
245
246   // If the window is maximized, however, we want clicks in the top edge to
247   // select the tab.
248   widget.Maximize();
249   EXPECT_TRUE(tab.HitTestPoint(gfx::Point(middle_x, 0)));
250
251   // But clicks in the area above the slanted sides should still miss.
252   EXPECT_FALSE(tab.HitTestPoint(gfx::Point(0, 0)));
253   EXPECT_FALSE(tab.HitTestPoint(gfx::Point(tab.width() - 1, 0)));
254 }
255
256 TEST_P(TabTest, LayoutAndVisibilityOfElements) {
257   if (testing_for_rtl_locale() && !base::i18n::IsRTL()) {
258     LOG(WARNING) << "Testing of RTL locale not supported on current platform.";
259     return;
260   }
261
262   static const TabMediaState kMediaStatesToTest[] = {
263     TAB_MEDIA_STATE_NONE, TAB_MEDIA_STATE_CAPTURING,
264     TAB_MEDIA_STATE_AUDIO_PLAYING, TAB_MEDIA_STATE_AUDIO_MUTING
265   };
266
267   FakeTabController controller;
268   Tab tab(&controller);
269
270   SkBitmap bitmap;
271   bitmap.allocN32Pixels(16, 16);
272   TabRendererData data;
273   data.favicon = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
274
275   // Perform layout over all possible combinations, checking for correct
276   // results.
277   for (int is_mini_tab = 0; is_mini_tab < 2; ++is_mini_tab) {
278     for (int is_active_tab = 0; is_active_tab < 2; ++is_active_tab) {
279       for (size_t media_state_index = 0;
280            media_state_index < arraysize(kMediaStatesToTest);
281            ++media_state_index) {
282         const TabMediaState media_state = kMediaStatesToTest[media_state_index];
283         SCOPED_TRACE(::testing::Message()
284                      << (is_active_tab ? "Active" : "Inactive") << ' '
285                      << (is_mini_tab ? "Mini " : "")
286                      << "Tab with media indicator state " << media_state);
287
288         data.mini = !!is_mini_tab;
289         controller.set_active_tab(!!is_active_tab);
290         data.media_state = media_state;
291         tab.SetData(data);
292
293         // Test layout for every width from standard to minimum.
294         gfx::Rect bounds(gfx::Point(0, 0), Tab::GetStandardSize());
295         int min_width;
296         if (is_mini_tab) {
297           bounds.set_width(Tab::GetMiniWidth());
298           min_width = Tab::GetMiniWidth();
299         } else {
300           min_width = is_active_tab ? Tab::GetMinimumSelectedSize().width() :
301               Tab::GetMinimumUnselectedSize().width();
302         }
303         while (bounds.width() >= min_width) {
304           SCOPED_TRACE(::testing::Message() << "bounds=" << bounds.ToString());
305           tab.SetBoundsRect(bounds);  // Invokes Tab::Layout().
306           CheckForExpectedLayoutAndVisibilityOfElements(tab);
307           bounds.set_width(bounds.width() - 1);
308         }
309       }
310     }
311   }
312 }
313
314 // Regression test for http://crbug.com/226253. Calling Layout() more than once
315 // shouldn't change the insets of the close button.
316 TEST_P(TabTest, CloseButtonLayout) {
317   if (testing_for_rtl_locale() && !base::i18n::IsRTL()) {
318     LOG(WARNING) << "Testing of RTL locale not supported on current platform.";
319     return;
320   }
321
322   FakeTabController tab_controller;
323   Tab tab(&tab_controller);
324   tab.SetBounds(0, 0, 100, 50);
325   tab.Layout();
326   gfx::Insets close_button_insets = tab.close_button_->GetInsets();
327   tab.Layout();
328   gfx::Insets close_button_insets_2 = tab.close_button_->GetInsets();
329   EXPECT_EQ(close_button_insets.top(), close_button_insets_2.top());
330   EXPECT_EQ(close_button_insets.left(), close_button_insets_2.left());
331   EXPECT_EQ(close_button_insets.bottom(), close_button_insets_2.bottom());
332   EXPECT_EQ(close_button_insets.right(), close_button_insets_2.right());
333
334   // Also make sure the close button is sized as large as the tab.
335   EXPECT_EQ(50, tab.close_button_->bounds().height());
336 }
337
338 // Test in both a LTR and a RTL locale.  Note: The fact that the UI code is
339 // configured for an RTL locale does *not* change how the coordinates are
340 // examined in the tests above because views::View and friends are supposed to
341 // auto-mirror the widgets when painting.  Thus, what we're testing here is that
342 // there's no code in Tab that will erroneously subvert this automatic
343 // coordinate translation.  http://crbug.com/384179
344 INSTANTIATE_TEST_CASE_P(, TabTest, ::testing::Values(false, true));