browser_view_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright 2013 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/frame/browser_view.h"
6
7#include "chrome/app/chrome_command_ids.h"
8#include "chrome/browser/ui/browser_commands.h"
9#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
10#include "chrome/browser/ui/views/frame/browser_view_layout.h"
11#include "chrome/browser/ui/views/frame/test_with_browser_view.h"
12#include "chrome/browser/ui/views/frame/top_container_view.h"
13#include "chrome/browser/ui/views/infobars/infobar_container_view.h"
14#include "chrome/browser/ui/views/tabs/tab_strip.h"
15#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
16#include "chrome/common/url_constants.h"
17#include "grit/generated_resources.h"
18#include "grit/theme_resources.h"
19#include "ui/base/resource/resource_bundle.h"
20#include "ui/views/controls/single_split_view.h"
21#include "ui/views/controls/webview/webview.h"
22
23namespace {
24
25// Tab strip bounds depend on the window frame sizes.
26gfx::Point ExpectedTabStripOrigin(BrowserView* browser_view) {
27  gfx::Rect tabstrip_bounds(
28      browser_view->frame()->GetBoundsForTabStrip(browser_view->tabstrip()));
29  gfx::Point tabstrip_origin(tabstrip_bounds.origin());
30  views::View::ConvertPointToTarget(browser_view->parent(),
31                                    browser_view,
32                                    &tabstrip_origin);
33  return tabstrip_origin;
34}
35
36}  // namespace
37
38typedef TestWithBrowserView BrowserViewTest;
39
40// Test basic construction and initialization.
41TEST_F(BrowserViewTest, BrowserView) {
42  // The window is owned by the native widget, not the test class.
43  EXPECT_FALSE(window());
44
45  EXPECT_TRUE(browser_view()->browser());
46
47  // Test initial state.
48  EXPECT_TRUE(browser_view()->IsTabStripVisible());
49  EXPECT_FALSE(browser_view()->IsOffTheRecord());
50  EXPECT_EQ(IDR_OTR_ICON, browser_view()->GetOTRIconResourceID());
51  EXPECT_FALSE(browser_view()->IsGuestSession());
52  EXPECT_FALSE(browser_view()->ShouldShowAvatar());
53  EXPECT_TRUE(browser_view()->IsBrowserTypeNormal());
54  EXPECT_FALSE(browser_view()->IsFullscreen());
55  EXPECT_FALSE(browser_view()->IsBookmarkBarVisible());
56  EXPECT_FALSE(browser_view()->IsBookmarkBarAnimating());
57}
58
59// Test layout of the top-of-window UI.
60TEST_F(BrowserViewTest, BrowserViewLayout) {
61  BookmarkBarView::DisableAnimationsForTesting(true);
62
63  // |browser_view_| owns the Browser, not the test class.
64  Browser* browser = browser_view()->browser();
65  TopContainerView* top_container = browser_view()->top_container();
66  TabStrip* tabstrip = browser_view()->tabstrip();
67  ToolbarView* toolbar = browser_view()->toolbar();
68  views::View* contents_container =
69      browser_view()->GetContentsContainerForTest();
70  views::WebView* contents_web_view =
71      browser_view()->GetContentsWebViewForTest();
72  views::WebView* devtools_web_view =
73      browser_view()->GetDevToolsWebViewForTest();
74
75  // Start with a single tab open to a normal page.
76  AddTab(browser, GURL("about:blank"));
77
78  // Verify the view hierarchy.
79  EXPECT_EQ(top_container, browser_view()->tabstrip()->parent());
80  EXPECT_EQ(top_container, browser_view()->toolbar()->parent());
81  EXPECT_EQ(top_container, browser_view()->GetBookmarkBarView()->parent());
82  EXPECT_EQ(browser_view(), browser_view()->infobar_container()->parent());
83
84  // Find bar host is at the front of the view hierarchy, followed by the top
85  // container.
86  EXPECT_EQ(browser_view()->child_count() - 1,
87            browser_view()->GetIndexOf(browser_view()->find_bar_host_view()));
88  EXPECT_EQ(browser_view()->child_count() - 2,
89            browser_view()->GetIndexOf(top_container));
90
91  // Verify basic layout.
92  EXPECT_EQ(0, top_container->x());
93  EXPECT_EQ(0, top_container->y());
94  EXPECT_EQ(browser_view()->width(), top_container->width());
95  // Tabstrip layout varies based on window frame sizes.
96  gfx::Point expected_tabstrip_origin = ExpectedTabStripOrigin(browser_view());
97  EXPECT_EQ(expected_tabstrip_origin.x(), tabstrip->x());
98  EXPECT_EQ(expected_tabstrip_origin.y(), tabstrip->y());
99  EXPECT_EQ(0, toolbar->x());
100  EXPECT_EQ(
101      tabstrip->bounds().bottom() -
102          BrowserViewLayout::kToolbarTabStripVerticalOverlap,
103      toolbar->y());
104  EXPECT_EQ(0, contents_container->x());
105  EXPECT_EQ(toolbar->bounds().bottom(), contents_container->y());
106  EXPECT_EQ(top_container->bounds().bottom(), contents_container->y());
107  EXPECT_EQ(0, devtools_web_view->x());
108  EXPECT_EQ(0, devtools_web_view->y());
109  EXPECT_EQ(0, contents_web_view->x());
110  EXPECT_EQ(0, contents_web_view->y());
111
112  // Verify bookmark bar visibility.
113  BookmarkBarView* bookmark_bar = browser_view()->GetBookmarkBarView();
114  EXPECT_FALSE(bookmark_bar->visible());
115  EXPECT_FALSE(bookmark_bar->IsDetached());
116  chrome::ExecuteCommand(browser, IDC_SHOW_BOOKMARK_BAR);
117  EXPECT_TRUE(bookmark_bar->visible());
118  EXPECT_FALSE(bookmark_bar->IsDetached());
119  chrome::ExecuteCommand(browser, IDC_SHOW_BOOKMARK_BAR);
120  EXPECT_FALSE(bookmark_bar->visible());
121  EXPECT_FALSE(bookmark_bar->IsDetached());
122
123  // Bookmark bar is reparented to BrowserView on NTP.
124  NavigateAndCommitActiveTabWithTitle(browser,
125                                      GURL(chrome::kChromeUINewTabURL),
126                                      base::string16());
127  EXPECT_TRUE(bookmark_bar->visible());
128  EXPECT_TRUE(bookmark_bar->IsDetached());
129  EXPECT_EQ(browser_view(), bookmark_bar->parent());
130  // Find bar host is still at the front of the view hierarchy, followed by
131  // the top container.
132  EXPECT_EQ(browser_view()->child_count() - 1,
133            browser_view()->GetIndexOf(browser_view()->find_bar_host_view()));
134  EXPECT_EQ(browser_view()->child_count() - 2,
135            browser_view()->GetIndexOf(top_container));
136
137  // Bookmark bar layout on NTP.
138  EXPECT_EQ(0, bookmark_bar->x());
139  EXPECT_EQ(
140      tabstrip->bounds().bottom() +
141          toolbar->height() -
142          BrowserViewLayout::kToolbarTabStripVerticalOverlap -
143          views::NonClientFrameView::kClientEdgeThickness,
144      bookmark_bar->y());
145  EXPECT_EQ(toolbar->bounds().bottom(), contents_container->y());
146  // Contents view has a "top margin" pushing it below the bookmark bar.
147  EXPECT_EQ(bookmark_bar->height() -
148                views::NonClientFrameView::kClientEdgeThickness,
149            devtools_web_view->y());
150  EXPECT_EQ(bookmark_bar->height() -
151                views::NonClientFrameView::kClientEdgeThickness,
152            contents_web_view->y());
153
154  // Bookmark bar is parented back to top container on normal page.
155  NavigateAndCommitActiveTabWithTitle(browser,
156                                      GURL("about:blank"),
157                                      base::string16());
158  EXPECT_FALSE(bookmark_bar->visible());
159  EXPECT_FALSE(bookmark_bar->IsDetached());
160  EXPECT_EQ(top_container, bookmark_bar->parent());
161  // Top container is still second from front.
162  EXPECT_EQ(browser_view()->child_count() - 2,
163            browser_view()->GetIndexOf(top_container));
164
165  BookmarkBarView::DisableAnimationsForTesting(false);
166}
167
168class BrowserViewHostedAppTest : public TestWithBrowserView {
169 public:
170  BrowserViewHostedAppTest()
171      : TestWithBrowserView(Browser::TYPE_POPUP,
172                            chrome::HOST_DESKTOP_TYPE_NATIVE,
173                            true) {
174  }
175  virtual ~BrowserViewHostedAppTest() {
176  }
177
178 private:
179  DISALLOW_COPY_AND_ASSIGN(BrowserViewHostedAppTest);
180};
181
182// Test basic layout for hosted apps.
183TEST_F(BrowserViewHostedAppTest, Layout) {
184  // Add a tab because the browser starts out without any tabs at all.
185  AddTab(browser(), GURL("about:blank"));
186
187  views::View* contents_container =
188      browser_view()->GetContentsContainerForTest();
189
190  // The tabstrip, toolbar and bookmark bar should not be visible for hosted
191  // apps.
192  EXPECT_FALSE(browser_view()->tabstrip()->visible());
193  EXPECT_FALSE(browser_view()->toolbar()->visible());
194  EXPECT_FALSE(browser_view()->IsBookmarkBarVisible());
195
196  gfx::Point header_offset;
197  views::View::ConvertPointToTarget(
198      browser_view(),
199      browser_view()->frame()->non_client_view()->frame_view(),
200      &header_offset);
201
202  // The position of the bottom of the header (the bar with the window
203  // controls) in the coordinates of BrowserView.
204  int bottom_of_header = browser_view()->frame()->GetTopInset() -
205      header_offset.y();
206
207  // The web contents should be flush with the bottom of the header.
208  EXPECT_EQ(bottom_of_header, contents_container->y());
209
210  // The find bar should overlap the 1px header/web-contents separator at the
211  // bottom of the header.
212  EXPECT_EQ(browser_view()->frame()->GetTopInset() - 1,
213            browser_view()->GetFindBarBoundingBox().y());
214}
215
216#if defined(OS_WIN)
217
218// This class provides functionality to test the incognito window/normal window
219// switcher button which is added to Windows 8 metro Chrome.
220// We create the BrowserView ourselves in the
221// BrowserWithTestWindowTest::CreateBrowserWindow function override and add the
222// switcher button to the view. We also provide an incognito profile to ensure
223// that the switcher button is visible.
224class BrowserViewIncognitoSwitcherTest : public TestWithBrowserView {
225 public:
226  // Subclass of BrowserView, which overrides the GetRestoreBounds/IsMaximized
227  // functions to return dummy values. This is needed because we create the
228  // BrowserView instance ourselves and initialize it with the created Browser
229  // instance. These functions get called before the underlying Widget is
230  // initialized which causes a crash while dereferencing a null native_widget_
231  // pointer in the Widget class.
232  class TestBrowserView : public BrowserView {
233   public:
234    virtual ~TestBrowserView() {}
235
236    virtual gfx::Rect GetRestoredBounds() const OVERRIDE {
237      return gfx::Rect();
238    }
239    virtual bool IsMaximized() const OVERRIDE {
240      return false;
241    }
242  };
243
244  BrowserViewIncognitoSwitcherTest()
245      : browser_view_(NULL) {}
246
247  virtual void SetUp() OVERRIDE {
248    TestWithBrowserView::SetUp();
249    browser_view_->Init(browser());
250    (new BrowserFrame(browser_view_))->InitBrowserFrame();
251    browser_view_->SetBounds(gfx::Rect(10, 10, 500, 500));
252    browser_view_->Show();
253  }
254
255  virtual void TearDown() OVERRIDE {
256    // ok to release the window_ pointer because BrowserViewTest::TearDown
257    // deletes the BrowserView instance created.
258    release_browser_window();
259    BrowserViewTest::TearDown();
260    browser_view_ = NULL;
261  }
262
263  virtual BrowserWindow* CreateBrowserWindow() OVERRIDE {
264    // We need an incognito profile for the window switcher button to be
265    // visible.
266    // This profile instance is owned by the TestingProfile instance within the
267    // BrowserWithTestWindowTest class.
268    TestingProfile::Builder builder;
269    builder.SetIncognito();
270    GetProfile()->SetOffTheRecordProfile(builder.Build());
271
272    browser_view_ = new TestBrowserView();
273
274    views::ImageButton* switcher_button = new views::ImageButton(NULL);
275    // The button in the incognito window has the hot-cold images inverted
276    // with respect to the regular browser window.
277    switcher_button->SetImage(
278        views::ImageButton::STATE_NORMAL,
279        ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
280            IDR_INCOGNITO_SWITCH_OFF));
281    switcher_button->SetImage(
282        views::ImageButton::STATE_HOVERED,
283        ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
284            IDR_INCOGNITO_SWITCH_ON));
285    switcher_button->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
286                                        views::ImageButton::ALIGN_MIDDLE);
287
288    browser_view_->SetWindowSwitcherButton(switcher_button);
289    return browser_view_;
290  }
291
292 private:
293  BrowserView* browser_view_;
294
295  DISALLOW_COPY_AND_ASSIGN(BrowserViewIncognitoSwitcherTest);
296};
297
298// Test whether the windows incognito/normal browser window switcher button
299// is the event handler for a point within its bounds. The event handler for
300// a point in the View class is dependent on the order in which children are
301// added to it. This test ensures that we don't regress in the window switcher
302// functionality when additional children are added to the BrowserView class.
303TEST_F(BrowserViewIncognitoSwitcherTest,
304       BrowserViewIncognitoSwitcherEventHandlerTest) {
305  // |browser_view_| owns the Browser, not the test class.
306  EXPECT_TRUE(browser_view()->browser());
307  // Test initial state.
308  EXPECT_TRUE(browser_view()->IsTabStripVisible());
309  // Validate whether the window switcher button is the target for the position
310  // passed in.
311  gfx::Point switcher_point(browser_view()->window_switcher_button()->x() + 2,
312                            browser_view()->window_switcher_button()->y());
313  EXPECT_EQ(browser_view()->GetEventHandlerForPoint(switcher_point),
314            browser_view()->window_switcher_button());
315}
316#endif
317