1// Copyright (c) 2012 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#import "chrome/browser/ui/cocoa/browser_window_controller.h" 6 7#include "base/mac/mac_util.h" 8#import "base/mac/scoped_nsobject.h" 9#include "base/memory/scoped_ptr.h" 10#include "base/prefs/pref_service.h" 11#include "base/strings/utf_string_conversions.h" 12#include "chrome/app/chrome_command_ids.h" 13#include "chrome/browser/chrome_notification_types.h" 14#include "chrome/browser/signin/fake_signin_manager.h" 15#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 16#include "chrome/browser/signin/signin_manager_factory.h" 17#include "chrome/browser/sync/profile_sync_service.h" 18#include "chrome/browser/sync/profile_sync_service_factory.h" 19#include "chrome/browser/sync/profile_sync_service_mock.h" 20#include "chrome/browser/ui/browser_list.h" 21#include "chrome/browser/ui/browser_window.h" 22#include "chrome/browser/ui/cocoa/cocoa_profile_test.h" 23#include "chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h" 24#include "chrome/browser/ui/cocoa/tabs/tab_strip_view.h" 25#import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" 26#include "chrome/browser/ui/host_desktop.h" 27#include "chrome/common/pref_names.h" 28#include "chrome/grit/chromium_strings.h" 29#include "chrome/grit/generated_resources.h" 30#include "chrome/test/base/testing_profile.h" 31#include "components/signin/core/browser/fake_auth_status_provider.h" 32#include "components/signin/core/browser/profile_oauth2_token_service.h" 33#include "components/signin/core/browser/signin_error_controller.h" 34#include "components/signin/core/browser/signin_manager.h" 35#include "content/public/browser/notification_service.h" 36#include "content/public/test/test_utils.h" 37#include "testing/gmock/include/gmock/gmock.h" 38#include "ui/base/l10n/l10n_util.h" 39#include "ui/base/l10n/l10n_util_mac.h" 40 41using ::testing::Return; 42 43@interface BrowserWindowController (JustForTesting) 44// Already defined in BWC. 45- (void)saveWindowPositionIfNeeded; 46- (void)layoutSubviews; 47@end 48 49@interface BrowserWindowController (ExposedForTesting) 50// Implementations are below. 51- (NSView*)infoBarContainerView; 52- (NSView*)toolbarView; 53- (NSView*)bookmarkView; 54- (BOOL)bookmarkBarVisible; 55@end 56 57@implementation BrowserWindowController (ExposedForTesting) 58- (NSView*)infoBarContainerView { 59 return [infoBarContainerController_ view]; 60} 61 62- (NSView*)toolbarView { 63 return [toolbarController_ view]; 64} 65 66- (NSView*)bookmarkView { 67 return [bookmarkBarController_ view]; 68} 69 70- (NSView*)findBarView { 71 return [findBarCocoaController_ view]; 72} 73 74- (BOOL)bookmarkBarVisible { 75 return [bookmarkBarController_ isVisible]; 76} 77@end 78 79class BrowserWindowControllerTest : public CocoaProfileTest { 80 public: 81 virtual void SetUp() { 82 CocoaProfileTest::SetUp(); 83 ASSERT_TRUE(browser()); 84 85 controller_ = [[BrowserWindowController alloc] initWithBrowser:browser() 86 takeOwnership:NO]; 87 } 88 89 virtual void TearDown() { 90 [controller_ close]; 91 CocoaProfileTest::TearDown(); 92 } 93 94 public: 95 BrowserWindowController* controller_; 96}; 97 98TEST_F(BrowserWindowControllerTest, TestSaveWindowPosition) { 99 PrefService* prefs = profile()->GetPrefs(); 100 ASSERT_TRUE(prefs != NULL); 101 102 // Check to make sure there is no existing pref for window placement. 103 const base::DictionaryValue* browser_window_placement = 104 prefs->GetDictionary(prefs::kBrowserWindowPlacement); 105 ASSERT_TRUE(browser_window_placement); 106 EXPECT_TRUE(browser_window_placement->empty()); 107 108 // Ask the window to save its position, then check that a preference 109 // exists. 110 BrowserList::SetLastActive(browser()); 111 [controller_ saveWindowPositionIfNeeded]; 112 browser_window_placement = 113 prefs->GetDictionary(prefs::kBrowserWindowPlacement); 114 ASSERT_TRUE(browser_window_placement); 115 EXPECT_FALSE(browser_window_placement->empty()); 116} 117 118TEST_F(BrowserWindowControllerTest, TestFullScreenWindow) { 119 // Confirm that |-createFullscreenWindow| doesn't return nil. 120 // See BrowserWindowFullScreenControllerTest for more fullscreen tests. 121 EXPECT_TRUE([controller_ createFullscreenWindow]); 122} 123 124TEST_F(BrowserWindowControllerTest, TestNormal) { 125 // Force the bookmark bar to be shown. 126 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true); 127 [controller_ browserWindow]->BookmarkBarStateChanged( 128 BookmarkBar::DONT_ANIMATE_STATE_CHANGE); 129 130 // Make sure a normal BrowserWindowController is, uh, normal. 131 EXPECT_TRUE([controller_ isTabbedWindow]); 132 EXPECT_TRUE([controller_ hasTabStrip]); 133 EXPECT_FALSE([controller_ hasTitleBar]); 134 EXPECT_TRUE([controller_ isBookmarkBarVisible]); 135 136 // And make sure a controller for a pop-up window is not normal. 137 // popup_browser will be owned by its window. 138 Browser* popup_browser(new Browser( 139 Browser::CreateParams(Browser::TYPE_POPUP, profile(), 140 chrome::GetActiveDesktop()))); 141 NSWindow *cocoaWindow = popup_browser->window()->GetNativeWindow(); 142 BrowserWindowController* controller = 143 static_cast<BrowserWindowController*>([cocoaWindow windowController]); 144 ASSERT_TRUE([controller isKindOfClass:[BrowserWindowController class]]); 145 EXPECT_FALSE([controller isTabbedWindow]); 146 EXPECT_FALSE([controller hasTabStrip]); 147 EXPECT_TRUE([controller hasTitleBar]); 148 EXPECT_FALSE([controller isBookmarkBarVisible]); 149 [controller close]; 150} 151 152TEST_F(BrowserWindowControllerTest, TestSetBounds) { 153 // Create a normal browser with bounds smaller than the minimum. 154 Browser::CreateParams params(Browser::TYPE_TABBED, profile(), 155 chrome::GetActiveDesktop()); 156 params.initial_bounds = gfx::Rect(0, 0, 50, 50); 157 Browser* browser = new Browser(params); 158 NSWindow *cocoaWindow = browser->window()->GetNativeWindow(); 159 BrowserWindowController* controller = 160 static_cast<BrowserWindowController*>([cocoaWindow windowController]); 161 162 ASSERT_TRUE([controller isTabbedWindow]); 163 BrowserWindow* browser_window = [controller browserWindow]; 164 EXPECT_EQ(browser_window, browser->window()); 165 gfx::Rect bounds = browser_window->GetBounds(); 166 EXPECT_EQ(400, bounds.width()); 167 EXPECT_EQ(272, bounds.height()); 168 169 // Try to set the bounds smaller than the minimum. 170 browser_window->SetBounds(gfx::Rect(0, 0, 50, 50)); 171 bounds = browser_window->GetBounds(); 172 EXPECT_EQ(400, bounds.width()); 173 EXPECT_EQ(272, bounds.height()); 174 175 [controller close]; 176} 177 178TEST_F(BrowserWindowControllerTest, TestSetBoundsPopup) { 179 // Create a popup with bounds smaller than the minimum. 180 Browser::CreateParams params(Browser::TYPE_POPUP, profile(), 181 chrome::GetActiveDesktop()); 182 params.initial_bounds = gfx::Rect(0, 0, 50, 50); 183 Browser* browser = new Browser(params); 184 NSWindow *cocoaWindow = browser->window()->GetNativeWindow(); 185 BrowserWindowController* controller = 186 static_cast<BrowserWindowController*>([cocoaWindow windowController]); 187 188 ASSERT_FALSE([controller isTabbedWindow]); 189 BrowserWindow* browser_window = [controller browserWindow]; 190 EXPECT_EQ(browser_window, browser->window()); 191 gfx::Rect bounds = browser_window->GetBounds(); 192 EXPECT_EQ(100, bounds.width()); 193 EXPECT_EQ(122, bounds.height()); 194 195 // Try to set the bounds smaller than the minimum. 196 browser_window->SetBounds(gfx::Rect(0, 0, 50, 50)); 197 bounds = browser_window->GetBounds(); 198 EXPECT_EQ(100, bounds.width()); 199 EXPECT_EQ(122, bounds.height()); 200 201 [controller close]; 202} 203 204TEST_F(BrowserWindowControllerTest, TestTheme) { 205 [controller_ userChangedTheme]; 206} 207 208TEST_F(BrowserWindowControllerTest, BookmarkBarControllerIndirection) { 209 EXPECT_FALSE([controller_ isBookmarkBarVisible]); 210 211 // Explicitly show the bar. Can't use chrome::ToggleBookmarkBarWhenVisible() 212 // because of the notification issues. 213 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true); 214 215 [controller_ browserWindow]->BookmarkBarStateChanged( 216 BookmarkBar::DONT_ANIMATE_STATE_CHANGE); 217 EXPECT_TRUE([controller_ isBookmarkBarVisible]); 218} 219 220#if 0 221// TODO(jrg): This crashes trying to create the BookmarkBarController, adding 222// an observer to the BookmarkModel. 223TEST_F(BrowserWindowControllerTest, TestIncognitoWidthSpace) { 224 scoped_ptr<TestingProfile> incognito_profile(new TestingProfile()); 225 incognito_profile->set_off_the_record(true); 226 scoped_ptr<Browser> browser( 227 new Browser(Browser::CreateParams(incognito_profile.get(), 228 chrome::GetActiveDesktop())); 229 controller_.reset([[BrowserWindowController alloc] 230 initWithBrowser:browser.get() 231 takeOwnership:NO]); 232 233 NSRect tabFrame = [[controller_ tabStripView] frame]; 234 [controller_ installIncognitoBadge]; 235 NSRect newTabFrame = [[controller_ tabStripView] frame]; 236 EXPECT_GT(tabFrame.size.width, newTabFrame.size.width); 237 238 controller_.release(); 239} 240#endif 241 242namespace { 243 244// Verifies that the toolbar, infobar, tab content area, and download shelf 245// completely fill the area under the tabstrip. 246void CheckViewPositions(BrowserWindowController* controller) { 247 NSRect contentView = [[[controller window] contentView] bounds]; 248 NSRect tabstrip = [[controller tabStripView] frame]; 249 NSRect toolbar = [[controller toolbarView] frame]; 250 NSRect infobar = [[controller infoBarContainerView] frame]; 251 NSRect contentArea = [[controller tabContentArea] frame]; 252 NSRect download = NSZeroRect; 253 if ([[[controller downloadShelf] view] superview]) 254 download = [[[controller downloadShelf] view] frame]; 255 256 EXPECT_EQ(NSMinY(contentView), NSMinY(download)); 257 EXPECT_EQ(NSMaxY(download), NSMinY(contentArea)); 258 EXPECT_EQ(NSMaxY(contentArea), NSMinY(infobar)); 259 260 // Bookmark bar frame is random memory when hidden. 261 if ([controller bookmarkBarVisible]) { 262 NSRect bookmark = [[controller bookmarkView] frame]; 263 EXPECT_EQ(NSMaxY(infobar), NSMinY(bookmark)); 264 EXPECT_EQ(NSMaxY(bookmark), NSMinY(toolbar)); 265 EXPECT_FALSE([[controller bookmarkView] isHidden]); 266 } else { 267 EXPECT_EQ(NSMaxY(infobar), NSMinY(toolbar)); 268 EXPECT_TRUE([[controller bookmarkView] isHidden]); 269 } 270 271 // Toolbar should start immediately under the tabstrip, but the tabstrip is 272 // not necessarily fixed with respect to the content view. 273 EXPECT_EQ(NSMinY(tabstrip), NSMaxY(toolbar)); 274} 275 276} // end namespace 277 278TEST_F(BrowserWindowControllerTest, TestAdjustWindowHeight) { 279 NSWindow* window = [controller_ window]; 280 NSRect workarea = [[window screen] visibleFrame]; 281 282 // Place the window well above the bottom of the screen and try to adjust its 283 // height. It should change appropriately (and only downwards). Then get it to 284 // shrink by the same amount; it should return to its original state. 285 NSRect initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y + 100, 286 200, 200); 287 [window setFrame:initialFrame display:YES]; 288 [controller_ resetWindowGrowthState]; 289 [controller_ adjustWindowHeightBy:40]; 290 NSRect finalFrame = [window frame]; 291 EXPECT_FALSE(NSEqualRects(finalFrame, initialFrame)); 292 EXPECT_FLOAT_EQ(NSMaxY(finalFrame), NSMaxY(initialFrame)); 293 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40); 294 [controller_ adjustWindowHeightBy:-40]; 295 finalFrame = [window frame]; 296 EXPECT_FLOAT_EQ(NSMaxY(finalFrame), NSMaxY(initialFrame)); 297 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame)); 298 299 // Place the window at the bottom of the screen and try again. Its height 300 // should still change, but it should not grow down below the work area; it 301 // should instead move upwards. Then shrink it and make sure it goes back to 302 // the way it was. 303 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y, 200, 200); 304 [window setFrame:initialFrame display:YES]; 305 [controller_ resetWindowGrowthState]; 306 [controller_ adjustWindowHeightBy:40]; 307 finalFrame = [window frame]; 308 EXPECT_FALSE(NSEqualRects(finalFrame, initialFrame)); 309 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame)); 310 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40); 311 [controller_ adjustWindowHeightBy:-40]; 312 finalFrame = [window frame]; 313 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame)); 314 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame)); 315 316 // Put the window slightly offscreen and try again. The height should not 317 // change this time. 318 initialFrame = NSMakeRect(workarea.origin.x - 10, 0, 200, 200); 319 [window setFrame:initialFrame display:YES]; 320 [controller_ resetWindowGrowthState]; 321 [controller_ adjustWindowHeightBy:40]; 322 EXPECT_TRUE(NSEqualRects([window frame], initialFrame)); 323 [controller_ adjustWindowHeightBy:-40]; 324 EXPECT_TRUE(NSEqualRects([window frame], initialFrame)); 325 326 // Make the window the same size as the workarea. Resizing both larger and 327 // smaller should have no effect. 328 [window setFrame:workarea display:YES]; 329 [controller_ resetWindowGrowthState]; 330 [controller_ adjustWindowHeightBy:40]; 331 EXPECT_TRUE(NSEqualRects([window frame], workarea)); 332 [controller_ adjustWindowHeightBy:-40]; 333 EXPECT_TRUE(NSEqualRects([window frame], workarea)); 334 335 // Make the window smaller than the workarea and place it near the bottom of 336 // the workarea. The window should grow down until it hits the bottom and 337 // then continue to grow up. Then shrink it, and it should return to where it 338 // was. 339 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y + 5, 340 200, 200); 341 [window setFrame:initialFrame display:YES]; 342 [controller_ resetWindowGrowthState]; 343 [controller_ adjustWindowHeightBy:40]; 344 finalFrame = [window frame]; 345 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame)); 346 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40); 347 [controller_ adjustWindowHeightBy:-40]; 348 finalFrame = [window frame]; 349 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame)); 350 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame)); 351 352 // Inset the window slightly from the workarea. It should not grow to be 353 // larger than the workarea. Shrink it; it should return to where it started. 354 initialFrame = NSInsetRect(workarea, 0, 5); 355 [window setFrame:initialFrame display:YES]; 356 [controller_ resetWindowGrowthState]; 357 [controller_ adjustWindowHeightBy:40]; 358 finalFrame = [window frame]; 359 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame)); 360 EXPECT_FLOAT_EQ(NSHeight(workarea), NSHeight(finalFrame)); 361 [controller_ adjustWindowHeightBy:-40]; 362 finalFrame = [window frame]; 363 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame)); 364 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame)); 365 366 // Place the window at the bottom of the screen and grow; it should grow 367 // upwards. Move the window off the bottom, then shrink. It should then shrink 368 // from the bottom. 369 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y, 200, 200); 370 [window setFrame:initialFrame display:YES]; 371 [controller_ resetWindowGrowthState]; 372 [controller_ adjustWindowHeightBy:40]; 373 finalFrame = [window frame]; 374 EXPECT_FALSE(NSEqualRects(finalFrame, initialFrame)); 375 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame)); 376 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40); 377 NSPoint oldOrigin = initialFrame.origin; 378 NSPoint newOrigin = NSMakePoint(oldOrigin.x, oldOrigin.y + 10); 379 [window setFrameOrigin:newOrigin]; 380 initialFrame = [window frame]; 381 EXPECT_FLOAT_EQ(NSMinY(initialFrame), oldOrigin.y + 10); 382 [controller_ adjustWindowHeightBy:-40]; 383 finalFrame = [window frame]; 384 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame) + 40); 385 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) - 40); 386 387 // Do the "inset" test above, but using multiple calls to 388 // |-adjustWindowHeightBy|; the result should be the same. 389 initialFrame = NSInsetRect(workarea, 0, 5); 390 [window setFrame:initialFrame display:YES]; 391 [controller_ resetWindowGrowthState]; 392 for (int i = 0; i < 8; i++) 393 [controller_ adjustWindowHeightBy:5]; 394 finalFrame = [window frame]; 395 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame)); 396 EXPECT_FLOAT_EQ(NSHeight(workarea), NSHeight(finalFrame)); 397 for (int i = 0; i < 8; i++) 398 [controller_ adjustWindowHeightBy:-5]; 399 finalFrame = [window frame]; 400 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame)); 401 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame)); 402} 403 404// Test to make sure resizing and relaying-out subviews works correctly. 405TEST_F(BrowserWindowControllerTest, TestResizeViews) { 406 TabStripView* tabstrip = [controller_ tabStripView]; 407 NSView* contentView = [[tabstrip window] contentView]; 408 NSView* toolbar = [controller_ toolbarView]; 409 NSView* infobar = [controller_ infoBarContainerView]; 410 411 // We need to muck with the views a bit to put us in a consistent state before 412 // we start resizing. In particular, we need to move the tab strip to be 413 // immediately above the content area, since we layout views to be directly 414 // under the tab strip. 415 NSRect tabstripFrame = [tabstrip frame]; 416 tabstripFrame.origin.y = NSMaxY([contentView frame]); 417 [tabstrip setFrame:tabstripFrame]; 418 419 // Make the download shelf and set its initial height to 0. 420 [controller_ createAndAddDownloadShelf]; 421 NSView* download = [[controller_ downloadShelf] view]; 422 NSRect downloadFrame = [download frame]; 423 downloadFrame.size.height = 0; 424 [download setFrame:downloadFrame]; 425 426 // Force a layout and check each view's frame. 427 [controller_ layoutSubviews]; 428 CheckViewPositions(controller_); 429 430 // Expand the infobar to 60px and recheck 431 [controller_ resizeView:infobar newHeight:60]; 432 CheckViewPositions(controller_); 433 434 // Expand the toolbar to 64px and recheck 435 [controller_ resizeView:toolbar newHeight:64]; 436 CheckViewPositions(controller_); 437 438 // Add a 30px download shelf and recheck 439 [controller_ resizeView:download newHeight:30]; 440 CheckViewPositions(controller_); 441 442 // Shrink the infobar to 0px and toolbar to 39px and recheck 443 [controller_ resizeView:infobar newHeight:0]; 444 [controller_ resizeView:toolbar newHeight:39]; 445 CheckViewPositions(controller_); 446} 447 448TEST_F(BrowserWindowControllerTest, TestResizeViewsWithBookmarkBar) { 449 // Force a display of the bookmark bar. 450 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true); 451 [controller_ browserWindow]->BookmarkBarStateChanged( 452 BookmarkBar::DONT_ANIMATE_STATE_CHANGE); 453 454 TabStripView* tabstrip = [controller_ tabStripView]; 455 NSView* contentView = [[tabstrip window] contentView]; 456 NSView* toolbar = [controller_ toolbarView]; 457 NSView* bookmark = [controller_ bookmarkView]; 458 NSView* infobar = [controller_ infoBarContainerView]; 459 460 // We need to muck with the views a bit to put us in a consistent state before 461 // we start resizing. In particular, we need to move the tab strip to be 462 // immediately above the content area, since we layout views to be directly 463 // under the tab strip. 464 NSRect tabstripFrame = [tabstrip frame]; 465 tabstripFrame.origin.y = NSMaxY([contentView frame]); 466 [tabstrip setFrame:tabstripFrame]; 467 468 // The download shelf is created lazily. Force-create it and set its initial 469 // height to 0. 470 [controller_ createAndAddDownloadShelf]; 471 NSView* download = [[controller_ downloadShelf] view]; 472 NSRect downloadFrame = [download frame]; 473 downloadFrame.size.height = 0; 474 [download setFrame:downloadFrame]; 475 476 // Force a layout and check each view's frame. 477 [controller_ layoutSubviews]; 478 CheckViewPositions(controller_); 479 480 // Add the bookmark bar and recheck. 481 [controller_ resizeView:bookmark newHeight:40]; 482 CheckViewPositions(controller_); 483 484 // Expand the infobar to 60px and recheck 485 [controller_ resizeView:infobar newHeight:60]; 486 CheckViewPositions(controller_); 487 488 // Expand the toolbar to 64px and recheck 489 [controller_ resizeView:toolbar newHeight:64]; 490 CheckViewPositions(controller_); 491 492 // Add a 30px download shelf and recheck 493 [controller_ resizeView:download newHeight:30]; 494 CheckViewPositions(controller_); 495 496 // Remove the bookmark bar and recheck 497 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, false); 498 [controller_ browserWindow]->BookmarkBarStateChanged( 499 BookmarkBar::DONT_ANIMATE_STATE_CHANGE); 500 [controller_ resizeView:bookmark newHeight:0]; 501 CheckViewPositions(controller_); 502 503 // Shrink the infobar to 0px and toolbar to 39px and recheck 504 [controller_ resizeView:infobar newHeight:0]; 505 [controller_ resizeView:toolbar newHeight:39]; 506 CheckViewPositions(controller_); 507} 508 509// Make sure, by default, the bookmark bar and the toolbar are the same width. 510TEST_F(BrowserWindowControllerTest, BookmarkBarIsSameWidth) { 511 // Set the pref to the bookmark bar is visible when the toolbar is 512 // first created. 513 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true); 514 515 // Make sure the bookmark bar is the same width as the toolbar 516 NSView* bookmarkBarView = [controller_ bookmarkView]; 517 NSView* toolbarView = [controller_ toolbarView]; 518 EXPECT_EQ([toolbarView frame].size.width, 519 [bookmarkBarView frame].size.width); 520} 521 522TEST_F(BrowserWindowControllerTest, TestTopRightForBubble) { 523 // The bookmark bubble must be attached to a lit and visible star. 524 [controller_ setStarredState:YES]; 525 NSPoint p = [controller_ bookmarkBubblePoint]; // Window coordinates. 526 NSRect all = [[controller_ window] frame]; // Screen coordinates. 527 528 // As a sanity check make sure the point is vaguely in the top right 529 // of the window. 530 EXPECT_GT(p.y, all.size.height / 2); 531 EXPECT_GT(p.x, all.size.width / 2); 532} 533 534// By the "zoom frame", we mean what Apple calls the "standard frame". 535TEST_F(BrowserWindowControllerTest, TestZoomFrame) { 536 NSWindow* window = [controller_ window]; 537 ASSERT_TRUE(window); 538 NSRect screenFrame = [[window screen] visibleFrame]; 539 ASSERT_FALSE(NSIsEmptyRect(screenFrame)); 540 541 // Minimum zoomed width is the larger of 60% of available horizontal space or 542 // 60% of available vertical space, subject to available horizontal space. 543 CGFloat minZoomWidth = 544 std::min(std::max((CGFloat)0.6 * screenFrame.size.width, 545 (CGFloat)0.6 * screenFrame.size.height), 546 screenFrame.size.width); 547 548 // |testFrame| is the size of the window we start out with, and |zoomFrame| is 549 // the one returned by |-windowWillUseStandardFrame:defaultFrame:|. 550 NSRect testFrame; 551 NSRect zoomFrame; 552 553 // 1. Test a case where it zooms the window both horizontally and vertically, 554 // and only moves it vertically. "+ 32", etc. are just arbitrary constants 555 // used to check that the window is moved properly and not just to the origin; 556 // they should be small enough to not shove windows off the screen. 557 testFrame.size.width = 0.5 * minZoomWidth; 558 testFrame.size.height = 0.5 * screenFrame.size.height; 559 testFrame.origin.x = screenFrame.origin.x + 32; // See above. 560 testFrame.origin.y = screenFrame.origin.y + 23; 561 [window setFrame:testFrame display:NO]; 562 zoomFrame = [controller_ windowWillUseStandardFrame:window 563 defaultFrame:screenFrame]; 564 EXPECT_LE(minZoomWidth, zoomFrame.size.width); 565 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height); 566 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x); 567 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y); 568 569 // 2. Test a case where it zooms the window only horizontally, and only moves 570 // it horizontally. 571 testFrame.size.width = 0.5 * minZoomWidth; 572 testFrame.size.height = screenFrame.size.height; 573 testFrame.origin.x = screenFrame.origin.x + screenFrame.size.width - 574 testFrame.size.width; 575 testFrame.origin.y = screenFrame.origin.y; 576 [window setFrame:testFrame display:NO]; 577 zoomFrame = [controller_ windowWillUseStandardFrame:window 578 defaultFrame:screenFrame]; 579 EXPECT_LE(minZoomWidth, zoomFrame.size.width); 580 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height); 581 EXPECT_EQ(screenFrame.origin.x + screenFrame.size.width - 582 zoomFrame.size.width, zoomFrame.origin.x); 583 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y); 584 585 // 3. Test a case where it zooms the window only vertically, and only moves it 586 // vertically. 587 testFrame.size.width = std::min((CGFloat)1.1 * minZoomWidth, 588 screenFrame.size.width); 589 testFrame.size.height = 0.3 * screenFrame.size.height; 590 testFrame.origin.x = screenFrame.origin.x + 32; // See above (in 1.). 591 testFrame.origin.y = screenFrame.origin.y + 123; 592 [window setFrame:testFrame display:NO]; 593 zoomFrame = [controller_ windowWillUseStandardFrame:window 594 defaultFrame:screenFrame]; 595 // Use the actual width of the window frame, since it's subject to rounding. 596 EXPECT_EQ([window frame].size.width, zoomFrame.size.width); 597 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height); 598 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x); 599 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y); 600 601 // 4. Test a case where zooming should do nothing (i.e., we're already at a 602 // zoomed frame). 603 testFrame.size.width = std::min((CGFloat)1.1 * minZoomWidth, 604 screenFrame.size.width); 605 testFrame.size.height = screenFrame.size.height; 606 testFrame.origin.x = screenFrame.origin.x + 32; // See above (in 1.). 607 testFrame.origin.y = screenFrame.origin.y; 608 [window setFrame:testFrame display:NO]; 609 zoomFrame = [controller_ windowWillUseStandardFrame:window 610 defaultFrame:screenFrame]; 611 // Use the actual width of the window frame, since it's subject to rounding. 612 EXPECT_EQ([window frame].size.width, zoomFrame.size.width); 613 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height); 614 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x); 615 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y); 616} 617 618TEST_F(BrowserWindowControllerTest, TestFindBarOnTop) { 619 FindBarBridge bridge(NULL); 620 [controller_ addFindBar:bridge.find_bar_cocoa_controller()]; 621 622 // Test that the Z-order of the find bar is on top of everything. 623 NSArray* subviews = [[[controller_ window] contentView] subviews]; 624 NSUInteger findBar_index = 625 [subviews indexOfObject:[controller_ findBarView]]; 626 EXPECT_NE(NSNotFound, findBar_index); 627 NSUInteger toolbar_index = 628 [subviews indexOfObject:[controller_ toolbarView]]; 629 EXPECT_NE(NSNotFound, toolbar_index); 630 NSUInteger bookmark_index = 631 [subviews indexOfObject:[controller_ bookmarkView]]; 632 EXPECT_NE(NSNotFound, bookmark_index); 633 634 EXPECT_GT(findBar_index, toolbar_index); 635 EXPECT_GT(findBar_index, bookmark_index); 636} 637 638TEST_F(BrowserWindowControllerTest, TestSigninMenuItemNoErrors) { 639 base::scoped_nsobject<NSMenuItem> syncMenuItem( 640 [[NSMenuItem alloc] initWithTitle:@"" 641 action:@selector(commandDispatch) 642 keyEquivalent:@""]); 643 [syncMenuItem setTag:IDC_SHOW_SYNC_SETUP]; 644 645 NSString* startSignin = 646 l10n_util::GetNSStringFWithFixup( 647 IDS_SYNC_MENU_PRE_SYNCED_LABEL, 648 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)); 649 650 // Make sure shouldShow parameter is obeyed, and we get the default 651 // label if not signed in. 652 [BrowserWindowController updateSigninItem:syncMenuItem 653 shouldShow:YES 654 currentProfile:profile()]; 655 656 EXPECT_TRUE([[syncMenuItem title] isEqualTo:startSignin]); 657 EXPECT_FALSE([syncMenuItem isHidden]); 658 659 [BrowserWindowController updateSigninItem:syncMenuItem 660 shouldShow:NO 661 currentProfile:profile()]; 662 EXPECT_TRUE([[syncMenuItem title] isEqualTo:startSignin]); 663 EXPECT_TRUE([syncMenuItem isHidden]); 664 665 // Now sign in. 666 std::string username = "foo@example.com"; 667 NSString* alreadySignedIn = 668 l10n_util::GetNSStringFWithFixup(IDS_SYNC_MENU_SYNCED_LABEL, 669 base::UTF8ToUTF16(username)); 670 SigninManager* signin = SigninManagerFactory::GetForProfile(profile()); 671 signin->SetAuthenticatedUsername(username); 672 ProfileSyncService* sync = 673 ProfileSyncServiceFactory::GetForProfile(profile()); 674 sync->SetSyncSetupCompleted(); 675 [BrowserWindowController updateSigninItem:syncMenuItem 676 shouldShow:YES 677 currentProfile:profile()]; 678 EXPECT_TRUE([[syncMenuItem title] isEqualTo:alreadySignedIn]); 679 EXPECT_FALSE([syncMenuItem isHidden]); 680} 681 682TEST_F(BrowserWindowControllerTest, TestSigninMenuItemAuthError) { 683 base::scoped_nsobject<NSMenuItem> syncMenuItem( 684 [[NSMenuItem alloc] initWithTitle:@"" 685 action:@selector(commandDispatch) 686 keyEquivalent:@""]); 687 [syncMenuItem setTag:IDC_SHOW_SYNC_SETUP]; 688 689 // Now sign in. 690 std::string username = "foo@example.com"; 691 SigninManager* signin = SigninManagerFactory::GetForProfile(profile()); 692 signin->SetAuthenticatedUsername(username); 693 ProfileSyncService* sync = 694 ProfileSyncServiceFactory::GetForProfile(profile()); 695 sync->SetSyncSetupCompleted(); 696 // Force an auth error. 697 FakeAuthStatusProvider provider( 698 ProfileOAuth2TokenServiceFactory::GetForProfile(profile())-> 699 signin_error_controller()); 700 GoogleServiceAuthError error( 701 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 702 provider.SetAuthError("user@gmail.com", "user@gmail.com", error); 703 [BrowserWindowController updateSigninItem:syncMenuItem 704 shouldShow:YES 705 currentProfile:profile()]; 706 NSString* authError = 707 l10n_util::GetNSStringWithFixup(IDS_SYNC_SIGN_IN_ERROR_WRENCH_MENU_ITEM); 708 EXPECT_TRUE([[syncMenuItem title] isEqualTo:authError]); 709 EXPECT_FALSE([syncMenuItem isHidden]); 710 711} 712 713// If there's a separator after the signin menu item, make sure it is hidden/ 714// shown when the signin menu item is. 715TEST_F(BrowserWindowControllerTest, TestSigninMenuItemWithSeparator) { 716 base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@""]); 717 NSMenuItem* signinMenuItem = 718 [menu addItemWithTitle:@"" 719 action:@selector(commandDispatch) 720 keyEquivalent:@""]; 721 [signinMenuItem setTag:IDC_SHOW_SYNC_SETUP]; 722 NSMenuItem* followingSeparator = [NSMenuItem separatorItem]; 723 [menu addItem:followingSeparator]; 724 [signinMenuItem setHidden:NO]; 725 [followingSeparator setHidden:NO]; 726 727 [BrowserWindowController updateSigninItem:signinMenuItem 728 shouldShow:NO 729 currentProfile:profile()]; 730 731 EXPECT_FALSE([followingSeparator isEnabled]); 732 EXPECT_TRUE([signinMenuItem isHidden]); 733 EXPECT_TRUE([followingSeparator isHidden]); 734 735 [BrowserWindowController updateSigninItem:signinMenuItem 736 shouldShow:YES 737 currentProfile:profile()]; 738 739 EXPECT_FALSE([followingSeparator isEnabled]); 740 EXPECT_FALSE([signinMenuItem isHidden]); 741 EXPECT_FALSE([followingSeparator isHidden]); 742} 743 744// If there's a non-separator item after the signin menu item, it should not 745// change state when the signin menu item is hidden/shown. 746TEST_F(BrowserWindowControllerTest, TestSigninMenuItemWithNonSeparator) { 747 base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@""]); 748 NSMenuItem* signinMenuItem = 749 [menu addItemWithTitle:@"" 750 action:@selector(commandDispatch) 751 keyEquivalent:@""]; 752 [signinMenuItem setTag:IDC_SHOW_SYNC_SETUP]; 753 NSMenuItem* followingNonSeparator = 754 [menu addItemWithTitle:@"" 755 action:@selector(commandDispatch) 756 keyEquivalent:@""]; 757 [signinMenuItem setHidden:NO]; 758 [followingNonSeparator setHidden:NO]; 759 760 [BrowserWindowController updateSigninItem:signinMenuItem 761 shouldShow:NO 762 currentProfile:profile()]; 763 764 EXPECT_TRUE([followingNonSeparator isEnabled]); 765 EXPECT_TRUE([signinMenuItem isHidden]); 766 EXPECT_FALSE([followingNonSeparator isHidden]); 767 768 [followingNonSeparator setHidden:YES]; 769 [BrowserWindowController updateSigninItem:signinMenuItem 770 shouldShow:YES 771 currentProfile:profile()]; 772 773 EXPECT_TRUE([followingNonSeparator isEnabled]); 774 EXPECT_FALSE([signinMenuItem isHidden]); 775 EXPECT_TRUE([followingNonSeparator isHidden]); 776} 777 778// Verify that hit testing works correctly when the bookmark bar overlaps 779// web contents. 780TEST_F(BrowserWindowControllerTest, BookmarkBarHitTest) { 781 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true); 782 [controller_ browserWindow]->BookmarkBarStateChanged( 783 BookmarkBar::DONT_ANIMATE_STATE_CHANGE); 784 785 NSView* bookmarkView = [controller_ bookmarkView]; 786 NSView* contentView = [[controller_ window] contentView]; 787 NSPoint point = [bookmarkView convertPoint:NSMakePoint(1, 1) 788 toView:[contentView superview]]; 789 790 EXPECT_TRUE([[contentView hitTest:point] isDescendantOf:bookmarkView]); 791} 792 793@interface BrowserWindowControllerFakeFullscreen : BrowserWindowController { 794 @private 795 // We release the window ourselves, so we don't have to rely on the unittest 796 // doing it for us. 797 base::scoped_nsobject<NSWindow> testFullscreenWindow_; 798} 799@end 800 801class BrowserWindowFullScreenControllerTest : public CocoaProfileTest { 802 public: 803 virtual void SetUp() { 804 CocoaProfileTest::SetUp(); 805 ASSERT_TRUE(browser()); 806 807 controller_ = 808 [[BrowserWindowControllerFakeFullscreen alloc] initWithBrowser:browser() 809 takeOwnership:NO]; 810 } 811 812 virtual void TearDown() { 813 [controller_ close]; 814 CocoaProfileTest::TearDown(); 815 } 816 817 public: 818 BrowserWindowController* controller_; 819}; 820 821// Check if the window is front most or if one of its child windows (such 822// as a status bubble) is front most. 823static bool IsFrontWindow(NSWindow *window) { 824 NSWindow* frontmostWindow = [[NSApp orderedWindows] objectAtIndex:0]; 825 return [frontmostWindow isEqual:window] || 826 [[frontmostWindow parentWindow] isEqual:window]; 827} 828 829void WaitForFullScreenTransition() { 830 content::WindowedNotificationObserver observer( 831 chrome::NOTIFICATION_FULLSCREEN_CHANGED, 832 content::NotificationService::AllSources()); 833 observer.Wait(); 834} 835 836// http://crbug.com/53586 837TEST_F(BrowserWindowFullScreenControllerTest, DISABLED_TestFullscreen) { 838 [controller_ showWindow:nil]; 839 EXPECT_FALSE([controller_ isInAnyFullscreenMode]); 840 841 [controller_ enterFullscreenWithChrome]; 842 WaitForFullScreenTransition(); 843 EXPECT_TRUE([controller_ isInAnyFullscreenMode]); 844 845 [controller_ exitAnyFullscreen]; 846 WaitForFullScreenTransition(); 847 EXPECT_FALSE([controller_ isInAnyFullscreenMode]); 848} 849 850// If this test fails, it is usually a sign that the bots have some sort of 851// problem (such as a modal dialog up). This tests is a very useful canary, so 852// please do not mark it as flaky without first verifying that there are no bot 853// problems. 854// http://crbug.com/53586 855TEST_F(BrowserWindowFullScreenControllerTest, DISABLED_TestActivate) { 856 [controller_ showWindow:nil]; 857 858 EXPECT_FALSE([controller_ isInAnyFullscreenMode]); 859 860 [controller_ activate]; 861 EXPECT_TRUE(IsFrontWindow([controller_ window])); 862 863 [controller_ enterFullscreenWithChrome]; 864 WaitForFullScreenTransition(); 865 [controller_ activate]; 866 867 // No fullscreen window on 10.7+. 868 if (base::mac::IsOSSnowLeopard()) 869 EXPECT_TRUE(IsFrontWindow([controller_ createFullscreenWindow])); 870 871 // We have to cleanup after ourselves by unfullscreening. 872 [controller_ exitAnyFullscreen]; 873 WaitForFullScreenTransition(); 874} 875 876@implementation BrowserWindowControllerFakeFullscreen 877// Override |-createFullscreenWindow| to return a dummy window. This isn't 878// needed to pass the test, but because the dummy window is only 100x100, it 879// prevents the real fullscreen window from flashing up and taking over the 880// whole screen. We have to return an actual window because |-layoutSubviews| 881// looks at the window's frame. 882- (NSWindow*)createFullscreenWindow { 883 if (testFullscreenWindow_.get()) 884 return testFullscreenWindow_.get(); 885 886 testFullscreenWindow_.reset( 887 [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,400,400) 888 styleMask:NSBorderlessWindowMask 889 backing:NSBackingStoreBuffered 890 defer:NO]); 891 return testFullscreenWindow_.get(); 892} 893@end 894 895/* TODO(???): test other methods of BrowserWindowController */ 896