Home | History | Annotate | Download | only in frame
      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 
     43 namespace {
     44 
     45 // Tab strip bounds depend on the window frame sizes.
     46 gfx::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.
     57 BrowserContextKeyedService* CreateTemplateURLService(
     58     content::BrowserContext* profile) {
     59   return new TemplateURLService(static_cast<Profile*>(profile));
     60 }
     61 
     62 BrowserContextKeyedService* CreateAutocompleteClassifier(
     63     content::BrowserContext* profile) {
     64   return new AutocompleteClassifier(static_cast<Profile*>(profile));
     65 }
     66 
     67 }  // namespace
     68 
     69 class 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 
     91 BrowserViewTest::BrowserViewTest()
     92     : browser_view_(NULL) {
     93 }
     94 
     95 void 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 
    102 void 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 
    119 TestingProfile* 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 
    132 BrowserWindow* BrowserViewTest::CreateBrowserWindow() {
    133   // Allow BrowserWithTestWindowTest to use Browser to create the default
    134   // BrowserView and BrowserFrame.
    135   return NULL;
    136 }
    137 
    138 void 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.
    152 TEST_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.
    172 TEST_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.
    280 class 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* incognito_profile = new TestingProfile();
    328     incognito_profile->set_incognito(true);
    329     GetProfile()->SetOffTheRecordProfile(incognito_profile);
    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.
    348 TEST_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