browser_view_unittest.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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 "base/memory/scoped_ptr.h"
8
9#include "chrome/app/chrome_command_ids.h"
10#include "chrome/browser/autocomplete/autocomplete_classifier.h"
11#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
12#include "chrome/browser/search_engines/template_url_service.h"
13#include "chrome/browser/search_engines/template_url_service_factory.h"
14#include "chrome/browser/ui/browser_commands.h"
15#include "chrome/browser/ui/tabs/tab_strip_model.h"
16#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
17#include "chrome/browser/ui/views/frame/browser_view_layout.h"
18#include "chrome/browser/ui/views/frame/overlay_container.h"
19#include "chrome/browser/ui/views/frame/top_container_view.h"
20#include "chrome/browser/ui/views/infobars/infobar_container_view.h"
21#include "chrome/browser/ui/views/tabs/tab_strip.h"
22#include "chrome/browser/ui/views/toolbar_view.h"
23#include "chrome/common/url_constants.h"
24#include "chrome/test/base/browser_with_test_window_test.h"
25#include "chrome/test/base/scoped_testing_local_state.h"
26#include "chrome/test/base/testing_browser_process.h"
27#include "grit/theme_resources.h"
28#include "testing/gtest/include/gtest/gtest.h"
29#include "ui/views/controls/single_split_view.h"
30#include "ui/views/controls/webview/webview.h"
31
32#if defined(OS_CHROMEOS)
33#include "chrome/browser/chromeos/input_method/input_method_configuration.h"
34#include "chrome/browser/chromeos/input_method/mock_input_method_manager.h"
35#endif
36
37#if defined(OS_WIN)
38#include "chrome/browser/ui/views/frame/browser_frame_win.h"
39#endif
40
41namespace {
42
43// Tab strip bounds depend on the window frame sizes.
44gfx::Point ExpectedTabStripOrigin(BrowserView* browser_view) {
45  gfx::Rect tabstrip_bounds(
46      browser_view->frame()->GetBoundsForTabStrip(browser_view->tabstrip()));
47  gfx::Point tabstrip_origin(tabstrip_bounds.origin());
48  views::View::ConvertPointToTarget(browser_view->parent(),
49                                    browser_view,
50                                    &tabstrip_origin);
51  return tabstrip_origin;
52}
53
54// Caller owns the returned service.
55BrowserContextKeyedService* CreateTemplateURLService(
56    content::BrowserContext* profile) {
57  return new TemplateURLService(static_cast<Profile*>(profile));
58}
59
60BrowserContextKeyedService* CreateAutocompleteClassifier(
61    content::BrowserContext* profile) {
62  return new AutocompleteClassifier(static_cast<Profile*>(profile));
63}
64
65}  // namespace
66
67class BrowserViewTest : public BrowserWithTestWindowTest {
68 public:
69  BrowserViewTest();
70  virtual ~BrowserViewTest() {}
71
72  // BrowserWithTestWindowTest overrides:
73  virtual void SetUp() OVERRIDE;
74  virtual void TearDown() OVERRIDE;
75  virtual TestingProfile* CreateProfile() OVERRIDE;
76  virtual BrowserWindow* CreateBrowserWindow() OVERRIDE;
77
78  void Init();
79  BrowserView* browser_view() { return browser_view_; }
80
81 private:
82  BrowserView* browser_view_;  // Not owned.
83  scoped_ptr<ScopedTestingLocalState> local_state_;
84  DISALLOW_COPY_AND_ASSIGN(BrowserViewTest);
85};
86
87BrowserViewTest::BrowserViewTest()
88    : browser_view_(NULL) {
89}
90
91void BrowserViewTest::SetUp() {
92  Init();
93  // Memory ownership is tricky here. BrowserView has taken ownership of
94  // |browser|, so BrowserWithTestWindowTest cannot continue to own it.
95  ASSERT_TRUE(release_browser());
96}
97
98void BrowserViewTest::TearDown() {
99  // Clean up any tabs we opened, otherwise Browser crashes in destruction.
100  browser_view_->browser()->tab_strip_model()->CloseAllTabs();
101  // Ensure the Browser is reset before BrowserWithTestWindowTest cleans up
102  // the Profile.
103  browser_view_->GetWidget()->CloseNow();
104  browser_view_ = NULL;
105  BrowserWithTestWindowTest::TearDown();
106#if defined(OS_CHROMEOS)
107  chromeos::input_method::Shutdown();
108#endif
109  local_state_.reset(NULL);
110}
111
112TestingProfile* BrowserViewTest::CreateProfile() {
113  TestingProfile* profile = BrowserWithTestWindowTest::CreateProfile();
114  // TemplateURLService is normally NULL during testing. Instant extended
115  // needs this service so set a custom factory function.
116  TemplateURLServiceFactory::GetInstance()->SetTestingFactory(
117      profile, &CreateTemplateURLService);
118  // TODO(jamescook): Eliminate this by introducing a mock toolbar or mock
119  // location bar.
120  AutocompleteClassifierFactory::GetInstance()->SetTestingFactory(
121      profile, &CreateAutocompleteClassifier);
122  return profile;
123}
124
125BrowserWindow* BrowserViewTest::CreateBrowserWindow() {
126  // Allow BrowserWithTestWindowTest to use Browser to create the default
127  // BrowserView and BrowserFrame.
128  return NULL;
129}
130
131void BrowserViewTest::Init() {
132  local_state_.reset(
133      new ScopedTestingLocalState(TestingBrowserProcess::GetGlobal()));
134#if defined(OS_CHROMEOS)
135  chromeos::input_method::InitializeForTesting(
136      new chromeos::input_method::MockInputMethodManager);
137#endif
138  BrowserWithTestWindowTest::SetUp();
139  browser_view_ = static_cast<BrowserView*>(browser()->window());
140}
141
142// Test basic construction and initialization.
143TEST_F(BrowserViewTest, BrowserView) {
144  // The window is owned by the native widget, not the test class.
145  EXPECT_FALSE(window());
146  // |browser_view_| owns the Browser, not the test class.
147  EXPECT_FALSE(browser());
148  EXPECT_TRUE(browser_view()->browser());
149
150  // Test initial state.
151  EXPECT_TRUE(browser_view()->IsTabStripVisible());
152  EXPECT_FALSE(browser_view()->IsOffTheRecord());
153  EXPECT_EQ(IDR_OTR_ICON, browser_view()->GetOTRIconResourceID());
154  EXPECT_FALSE(browser_view()->IsGuestSession());
155  EXPECT_FALSE(browser_view()->ShouldShowAvatar());
156  EXPECT_TRUE(browser_view()->IsBrowserTypeNormal());
157  EXPECT_FALSE(browser_view()->IsFullscreen());
158  EXPECT_FALSE(browser_view()->IsBookmarkBarVisible());
159  EXPECT_FALSE(browser_view()->IsBookmarkBarAnimating());
160}
161
162// Test layout of the top-of-window UI.
163TEST_F(BrowserViewTest, BrowserViewLayout) {
164  BookmarkBarView::DisableAnimationsForTesting(true);
165
166  // |browser_view_| owns the Browser, not the test class.
167  Browser* browser = browser_view()->browser();
168  TopContainerView* top_container = browser_view()->top_container();
169  TabStrip* tabstrip = browser_view()->tabstrip();
170  ToolbarView* toolbar = browser_view()->toolbar();
171  views::SingleSplitView* contents_split =
172      browser_view()->GetContentsSplitForTest();
173  views::WebView* contents_web_view =
174      browser_view()->GetContentsWebViewForTest();
175  OverlayContainer* overlay_container =
176      browser_view()->GetOverlayContainerForTest();
177
178  // Start with a single tab open to a normal page.
179  AddTab(browser, GURL("about:blank"));
180
181  // Verify the view hierarchy.
182  EXPECT_EQ(top_container, browser_view()->tabstrip()->parent());
183  EXPECT_EQ(top_container, browser_view()->toolbar()->parent());
184  EXPECT_EQ(top_container, browser_view()->GetBookmarkBarView()->parent());
185  EXPECT_EQ(browser_view(), browser_view()->infobar_container()->parent());
186  EXPECT_EQ(browser_view(), overlay_container->parent());
187
188  // Overlay container is at the front of the view hierarchy, followed by the
189  // find bar host and the top container.
190  EXPECT_EQ(browser_view()->child_count() - 1,
191            browser_view()->GetIndexOf(overlay_container));
192  EXPECT_EQ(browser_view()->child_count() - 2,
193            browser_view()->GetIndexOf(browser_view()->find_bar_host_view()));
194  EXPECT_EQ(browser_view()->child_count() - 3,
195            browser_view()->GetIndexOf(top_container));
196
197  // Verify basic layout.
198  EXPECT_EQ(0, top_container->x());
199  EXPECT_EQ(0, top_container->y());
200  EXPECT_EQ(browser_view()->width(), top_container->width());
201  // Tabstrip layout varies based on window frame sizes.
202  gfx::Point expected_tabstrip_origin = ExpectedTabStripOrigin(browser_view());
203  EXPECT_EQ(expected_tabstrip_origin.x(), tabstrip->x());
204  EXPECT_EQ(expected_tabstrip_origin.y(), tabstrip->y());
205  EXPECT_EQ(0, toolbar->x());
206  EXPECT_EQ(
207      tabstrip->bounds().bottom() -
208          BrowserViewLayout::kToolbarTabStripVerticalOverlap,
209      toolbar->y());
210  EXPECT_EQ(0, contents_split->x());
211  EXPECT_EQ(toolbar->bounds().bottom(), contents_split->y());
212  EXPECT_EQ(0, contents_web_view->x());
213  EXPECT_EQ(0, contents_web_view->y());
214
215  // Verify bookmark bar visibility.
216  BookmarkBarView* bookmark_bar = browser_view()->GetBookmarkBarView();
217  EXPECT_FALSE(bookmark_bar->visible());
218  EXPECT_FALSE(bookmark_bar->IsDetached());
219  chrome::ExecuteCommand(browser, IDC_SHOW_BOOKMARK_BAR);
220  EXPECT_TRUE(bookmark_bar->visible());
221  EXPECT_FALSE(bookmark_bar->IsDetached());
222  chrome::ExecuteCommand(browser, IDC_SHOW_BOOKMARK_BAR);
223  EXPECT_FALSE(bookmark_bar->visible());
224  EXPECT_FALSE(bookmark_bar->IsDetached());
225
226  // Bookmark bar is reparented to BrowserView on NTP.
227  NavigateAndCommitActiveTabWithTitle(browser,
228                                      GURL(chrome::kChromeUINewTabURL),
229                                      string16());
230  EXPECT_TRUE(bookmark_bar->visible());
231  EXPECT_TRUE(bookmark_bar->IsDetached());
232  EXPECT_EQ(browser_view(), bookmark_bar->parent());
233  // Overlay container is still at the front of the view hierarchy, followed by
234  // the find bar host and the top container.
235  EXPECT_EQ(browser_view()->child_count() - 1,
236            browser_view()->GetIndexOf(overlay_container));
237  EXPECT_EQ(browser_view()->child_count() - 2,
238            browser_view()->GetIndexOf(browser_view()->find_bar_host_view()));
239  EXPECT_EQ(browser_view()->child_count() - 3,
240            browser_view()->GetIndexOf(top_container));
241
242  // Bookmark bar layout on NTP.
243  EXPECT_EQ(0, bookmark_bar->x());
244  EXPECT_EQ(
245      tabstrip->bounds().bottom() +
246          toolbar->height() -
247          BrowserViewLayout::kToolbarTabStripVerticalOverlap -
248          views::NonClientFrameView::kClientEdgeThickness,
249      bookmark_bar->y());
250  EXPECT_EQ(toolbar->bounds().bottom(), contents_split->y());
251  // Contents view has a "top margin" pushing it below the bookmark bar.
252  EXPECT_EQ(bookmark_bar->height() -
253                views::NonClientFrameView::kClientEdgeThickness,
254            contents_web_view->y());
255
256  // Bookmark bar is parented back to top container on normal page.
257  NavigateAndCommitActiveTabWithTitle(browser,
258                                      GURL("about:blank"),
259                                      string16());
260  EXPECT_FALSE(bookmark_bar->visible());
261  EXPECT_FALSE(bookmark_bar->IsDetached());
262  EXPECT_EQ(top_container, bookmark_bar->parent());
263  // Top container is still third from front.
264  EXPECT_EQ(browser_view()->child_count() - 3,
265            browser_view()->GetIndexOf(top_container));
266
267  BookmarkBarView::DisableAnimationsForTesting(false);
268}
269
270#if defined(OS_WIN) && !defined(USE_AURA)
271
272// This class provides functionality to test the incognito window/normal window
273// switcher button which is added to Windows 8 metro Chrome.
274// We create the BrowserView ourselves in the
275// BrowserWithTestWindowTest::CreateBrowserWindow function override and add the
276// switcher button to the view. We also provide an incognito profile to ensure
277// that the switcher button is visible.
278class BrowserViewIncognitoSwitcherTest : public BrowserViewTest {
279 public:
280  // Subclass of BrowserView, which overrides the GetRestoreBounds/IsMaximized
281  // functions to return dummy values. This is needed because we create the
282  // BrowserView instance ourselves and initialize it with the created Browser
283  // instance. These functions get called before the underlying Widget is
284  // initialized which causes a crash while dereferencing a null native_widget_
285  // pointer in the Widget class.
286  class TestBrowserView : public BrowserView {
287   public:
288    virtual ~TestBrowserView() {}
289
290    virtual gfx::Rect GetRestoredBounds() const OVERRIDE {
291      return gfx::Rect();
292    }
293    virtual bool IsMaximized() const OVERRIDE {
294      return false;
295    }
296  };
297
298  BrowserViewIncognitoSwitcherTest()
299      : browser_view_(NULL) {}
300  virtual ~BrowserViewIncognitoSwitcherTest() {}
301
302  virtual void SetUp() OVERRIDE {
303    Init();
304    browser_view_->Init(browser());
305    (new BrowserFrame(browser_view_))->InitBrowserFrame();
306    browser_view_->SetBounds(gfx::Rect(10, 10, 500, 500));
307    browser_view_->Show();
308    // Memory ownership is tricky here. BrowserView has taken ownership of
309    // |browser|, so BrowserWithTestWindowTest cannot continue to own it.
310    ASSERT_TRUE(release_browser());
311  }
312
313  virtual void TearDown() OVERRIDE {
314    // ok to release the window_ pointer because BrowserViewTest::TearDown
315    // deletes the BrowserView instance created.
316    release_browser_window();
317    BrowserViewTest::TearDown();
318    browser_view_ = NULL;
319  }
320
321  virtual BrowserWindow* CreateBrowserWindow() OVERRIDE {
322    // We need an incognito profile for the window switcher button to be
323    // visible.
324    // This profile instance is owned by the TestingProfile instance within the
325    // BrowserWithTestWindowTest class.
326    TestingProfile* incognito_profile = new TestingProfile();
327    incognito_profile->set_incognito(true);
328    GetProfile()->SetOffTheRecordProfile(incognito_profile);
329
330    browser_view_ = new TestBrowserView();
331    browser_view_->SetWindowSwitcherButton(
332        MakeWindowSwitcherButton(NULL, false));
333    return browser_view_;
334  }
335
336 private:
337  BrowserView* browser_view_;
338
339  DISALLOW_COPY_AND_ASSIGN(BrowserViewIncognitoSwitcherTest);
340};
341
342// Test whether the windows incognito/normal browser window switcher button
343// is the event handler for a point within its bounds. The event handler for
344// a point in the View class is dependent on the order in which children are
345// added to it. This test ensures that we don't regress in the window switcher
346// functionality when additional children are added to the BrowserView class.
347TEST_F(BrowserViewIncognitoSwitcherTest,
348       BrowserViewIncognitoSwitcherEventHandlerTest) {
349  // |browser_view_| owns the Browser, not the test class.
350  EXPECT_FALSE(browser());
351  EXPECT_TRUE(browser_view()->browser());
352  // Test initial state.
353  EXPECT_TRUE(browser_view()->IsTabStripVisible());
354  // Validate whether the window switcher button is the target for the position
355  // passed in.
356  gfx::Point switcher_point(browser_view()->window_switcher_button()->x() + 2,
357                            browser_view()->window_switcher_button()->y());
358  EXPECT_EQ(browser_view()->GetEventHandlerForPoint(switcher_point),
359            browser_view()->window_switcher_button());
360}
361#endif
362