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