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