1 // Copyright 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.
5 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
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 "chrome/app/chrome_command_ids.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/ui/browser_list.h"
14 #include "chrome/browser/ui/browser_window.h"
15 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
16 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
17 #include "chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h"
18 #include "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
19 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
20 #include "chrome/browser/ui/host_desktop.h"
21 #include "chrome/common/pref_names.h"
22 #include "chrome/test/base/testing_profile.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/test/test_utils.h"
25 #include "testing/gmock/include/gmock/gmock.h"
27 using ::testing::Return;
29 @interface BrowserWindowController (JustForTesting)
30 // Already defined in BWC.
31 - (void)saveWindowPositionIfNeeded;
32 - (void)layoutSubviews;
35 @interface BrowserWindowController (ExposedForTesting)
36 // Implementations are below.
37 - (NSView*)infoBarContainerView;
38 - (NSView*)toolbarView;
39 - (NSView*)bookmarkView;
40 - (BOOL)bookmarkBarVisible;
43 @implementation BrowserWindowController (ExposedForTesting)
44 - (NSView*)infoBarContainerView {
45 return [infoBarContainerController_ view];
48 - (NSView*)toolbarView {
49 return [toolbarController_ view];
52 - (NSView*)bookmarkView {
53 return [bookmarkBarController_ view];
56 - (NSView*)findBarView {
57 return [findBarCocoaController_ view];
60 - (BOOL)bookmarkBarVisible {
61 return [bookmarkBarController_ isVisible];
65 class BrowserWindowControllerTest : public CocoaProfileTest {
67 void SetUp() override {
68 CocoaProfileTest::SetUp();
69 ASSERT_TRUE(browser());
71 controller_ = [[BrowserWindowController alloc] initWithBrowser:browser()
75 void TearDown() override {
77 CocoaProfileTest::TearDown();
81 BrowserWindowController* controller_;
84 TEST_F(BrowserWindowControllerTest, TestSaveWindowPosition) {
85 PrefService* prefs = profile()->GetPrefs();
86 ASSERT_TRUE(prefs != NULL);
88 // Check to make sure there is no existing pref for window placement.
89 const base::DictionaryValue* browser_window_placement =
90 prefs->GetDictionary(prefs::kBrowserWindowPlacement);
91 ASSERT_TRUE(browser_window_placement);
92 EXPECT_TRUE(browser_window_placement->empty());
94 // Ask the window to save its position, then check that a preference
96 BrowserList::SetLastActive(browser());
97 [controller_ saveWindowPositionIfNeeded];
98 browser_window_placement =
99 prefs->GetDictionary(prefs::kBrowserWindowPlacement);
100 ASSERT_TRUE(browser_window_placement);
101 EXPECT_FALSE(browser_window_placement->empty());
104 TEST_F(BrowserWindowControllerTest, TestFullScreenWindow) {
105 // Confirm that |-createFullscreenWindow| doesn't return nil.
106 // See BrowserWindowFullScreenControllerTest for more fullscreen tests.
107 EXPECT_TRUE([controller_ createFullscreenWindow]);
110 TEST_F(BrowserWindowControllerTest, TestNormal) {
111 // Force the bookmark bar to be shown.
112 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
113 [controller_ browserWindow]->BookmarkBarStateChanged(
114 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
116 // Make sure a normal BrowserWindowController is, uh, normal.
117 EXPECT_TRUE([controller_ isTabbedWindow]);
118 EXPECT_TRUE([controller_ hasTabStrip]);
119 EXPECT_FALSE([controller_ hasTitleBar]);
120 EXPECT_TRUE([controller_ isBookmarkBarVisible]);
122 // And make sure a controller for a pop-up window is not normal.
123 // popup_browser will be owned by its window.
124 Browser* popup_browser(new Browser(
125 Browser::CreateParams(Browser::TYPE_POPUP, profile(),
126 chrome::GetActiveDesktop())));
127 NSWindow* cocoaWindow = popup_browser->window()->GetNativeWindow();
128 BrowserWindowController* controller =
129 static_cast<BrowserWindowController*>([cocoaWindow windowController]);
130 ASSERT_TRUE([controller isKindOfClass:[BrowserWindowController class]]);
131 EXPECT_FALSE([controller isTabbedWindow]);
132 EXPECT_FALSE([controller hasTabStrip]);
133 EXPECT_TRUE([controller hasTitleBar]);
134 EXPECT_FALSE([controller isBookmarkBarVisible]);
138 TEST_F(BrowserWindowControllerTest, TestSetBounds) {
139 // Create a normal browser with bounds smaller than the minimum.
140 Browser::CreateParams params(Browser::TYPE_TABBED, profile(),
141 chrome::GetActiveDesktop());
142 params.initial_bounds = gfx::Rect(0, 0, 50, 50);
143 Browser* browser = new Browser(params);
144 NSWindow* cocoaWindow = browser->window()->GetNativeWindow();
145 BrowserWindowController* controller =
146 static_cast<BrowserWindowController*>([cocoaWindow windowController]);
148 ASSERT_TRUE([controller isTabbedWindow]);
149 BrowserWindow* browser_window = [controller browserWindow];
150 EXPECT_EQ(browser_window, browser->window());
151 gfx::Rect bounds = browser_window->GetBounds();
152 EXPECT_EQ(400, bounds.width());
153 EXPECT_EQ(272, bounds.height());
155 // Try to set the bounds smaller than the minimum.
156 browser_window->SetBounds(gfx::Rect(0, 0, 50, 50));
157 bounds = browser_window->GetBounds();
158 EXPECT_EQ(400, bounds.width());
159 EXPECT_EQ(272, bounds.height());
164 TEST_F(BrowserWindowControllerTest, TestSetBoundsPopup) {
165 // Create a popup with bounds smaller than the minimum.
166 Browser::CreateParams params(Browser::TYPE_POPUP, profile(),
167 chrome::GetActiveDesktop());
168 params.initial_bounds = gfx::Rect(0, 0, 50, 50);
169 Browser* browser = new Browser(params);
170 NSWindow* cocoaWindow = browser->window()->GetNativeWindow();
171 BrowserWindowController* controller =
172 static_cast<BrowserWindowController*>([cocoaWindow windowController]);
174 ASSERT_FALSE([controller isTabbedWindow]);
175 BrowserWindow* browser_window = [controller browserWindow];
176 EXPECT_EQ(browser_window, browser->window());
177 gfx::Rect bounds = browser_window->GetBounds();
178 EXPECT_EQ(100, bounds.width());
179 EXPECT_EQ(122, bounds.height());
181 // Try to set the bounds smaller than the minimum.
182 browser_window->SetBounds(gfx::Rect(0, 0, 50, 50));
183 bounds = browser_window->GetBounds();
184 EXPECT_EQ(100, bounds.width());
185 EXPECT_EQ(122, bounds.height());
190 TEST_F(BrowserWindowControllerTest, TestTheme) {
191 [controller_ userChangedTheme];
194 TEST_F(BrowserWindowControllerTest, BookmarkBarControllerIndirection) {
195 EXPECT_FALSE([controller_ isBookmarkBarVisible]);
197 // Explicitly show the bar. Can't use chrome::ToggleBookmarkBarWhenVisible()
198 // because of the notification issues.
199 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
201 [controller_ browserWindow]->BookmarkBarStateChanged(
202 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
203 EXPECT_TRUE([controller_ isBookmarkBarVisible]);
206 TEST_F(BrowserWindowControllerTest, BookmarkBarToggleRespectMinWindowHeight) {
207 Browser::CreateParams params(Browser::TYPE_TABBED, profile(),
208 chrome::GetActiveDesktop());
209 params.initial_bounds = gfx::Rect(0, 0, 50, 280);
210 Browser* browser = new Browser(params);
211 NSWindow* cocoaWindow = browser->window()->GetNativeWindow();
212 BrowserWindowController* controller =
213 static_cast<BrowserWindowController*>([cocoaWindow windowController]);
214 BrowserWindow* browser_window = [controller browserWindow];
215 gfx::Rect bounds = browser_window->GetBounds();
216 EXPECT_EQ(280, bounds.height());
218 // Try to set the bounds smaller than the minimum.
219 // Explicitly show the bar. Can't use chrome::ToggleBookmarkBarWhenVisible()
220 // because of the notification issues.
221 [controller adjustWindowHeightBy:-20];
222 bounds = browser_window->GetBounds();
223 EXPECT_EQ(272, bounds.height());
229 // TODO(jrg): This crashes trying to create the BookmarkBarController, adding
230 // an observer to the BookmarkModel.
231 TEST_F(BrowserWindowControllerTest, TestIncognitoWidthSpace) {
232 scoped_ptr<TestingProfile> incognito_profile(new TestingProfile());
233 incognito_profile->set_off_the_record(true);
234 scoped_ptr<Browser> browser(
235 new Browser(Browser::CreateParams(incognito_profile.get(),
236 chrome::GetActiveDesktop()));
237 controller_.reset([[BrowserWindowController alloc]
238 initWithBrowser:browser.get()
241 NSRect tabFrame = [[controller_ tabStripView] frame];
242 [controller_ installIncognitoBadge];
243 NSRect newTabFrame = [[controller_ tabStripView] frame];
244 EXPECT_GT(tabFrame.size.width, newTabFrame.size.width);
246 controller_.release();
252 // Returns the frame of the view in window coordinates.
253 NSRect FrameInWindowForView(NSView* view) {
254 return [[view superview] convertRect:[view frame] toView:nil];
257 // Whether the view's frame is within the bounds of the superview.
258 BOOL ViewContainmentValid(NSView* view) {
259 if (NSIsEmptyRect([view frame]))
262 return NSContainsRect([[view superview] bounds], [view frame]);
265 // Checks the view hierarchy rooted at |view| to ensure that each view is
266 // properly contained.
267 BOOL ViewHierarchyContainmentValid(NSView* view) {
268 // TODO(erikchen): Fix these views to have correct containment.
269 // http://crbug.com/397665.
270 if ([view isKindOfClass:NSClassFromString(@"DownloadShelfView")])
272 if ([view isKindOfClass:NSClassFromString(@"BookmarkBarToolbarView")])
274 if ([view isKindOfClass:NSClassFromString(@"BrowserActionsContainerView")])
277 if (!ViewContainmentValid(view)) {
278 LOG(ERROR) << "View violates containment: " <<
279 [[view description] UTF8String];
283 for (NSView* subview in [view subviews]) {
284 BOOL result = ViewHierarchyContainmentValid(subview);
292 // Verifies that the toolbar, infobar, tab content area, and download shelf
293 // completely fill the area under the tabstrip.
294 void CheckViewPositions(BrowserWindowController* controller) {
295 EXPECT_TRUE(ViewHierarchyContainmentValid([[controller window] contentView]));
297 NSRect contentView = FrameInWindowForView([[controller window] contentView]);
298 NSRect tabstrip = FrameInWindowForView([controller tabStripView]);
299 NSRect toolbar = FrameInWindowForView([controller toolbarView]);
300 NSRect infobar = FrameInWindowForView([controller infoBarContainerView]);
301 NSRect tabContent = FrameInWindowForView([controller tabContentArea]);
302 NSRect download = NSZeroRect;
303 if ([[[controller downloadShelf] view] superview])
304 download = [[[controller downloadShelf] view] frame];
306 EXPECT_EQ(NSMinY(contentView), NSMinY(download));
307 EXPECT_EQ(NSMaxY(download), NSMinY(tabContent));
308 EXPECT_EQ(NSMaxY(tabContent), NSMinY(infobar));
310 // Bookmark bar frame is random memory when hidden.
311 if ([controller bookmarkBarVisible]) {
312 NSRect bookmark = [[controller bookmarkView] frame];
313 EXPECT_EQ(NSMaxY(infobar), NSMinY(bookmark));
314 EXPECT_EQ(NSMaxY(bookmark), NSMinY(toolbar));
315 EXPECT_FALSE([[controller bookmarkView] isHidden]);
317 EXPECT_EQ(NSMaxY(infobar), NSMinY(toolbar));
318 EXPECT_TRUE([[controller bookmarkView] isHidden]);
321 // Toolbar should start immediately under the tabstrip, but the tabstrip is
322 // not necessarily fixed with respect to the content view.
323 EXPECT_EQ(NSMinY(tabstrip), NSMaxY(toolbar));
328 TEST_F(BrowserWindowControllerTest, TestAdjustWindowHeight) {
329 NSWindow* window = [controller_ window];
330 NSRect workarea = [[window screen] visibleFrame];
332 // Place the window well above the bottom of the screen and try to adjust its
333 // height. It should change appropriately (and only downwards). Then get it to
334 // shrink by the same amount; it should return to its original state.
335 NSRect initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y + 100,
337 [window setFrame:initialFrame display:YES];
338 [controller_ resetWindowGrowthState];
339 [controller_ adjustWindowHeightBy:40];
340 NSRect finalFrame = [window frame];
341 EXPECT_FALSE(NSEqualRects(finalFrame, initialFrame));
342 EXPECT_FLOAT_EQ(NSMaxY(finalFrame), NSMaxY(initialFrame));
343 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
344 [controller_ adjustWindowHeightBy:-40];
345 finalFrame = [window frame];
346 EXPECT_FLOAT_EQ(NSMaxY(finalFrame), NSMaxY(initialFrame));
347 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame));
349 // Place the window at the bottom of the screen and try again. Its height
350 // should still change, but it should not grow down below the work area; it
351 // should instead move upwards. Then shrink it and make sure it goes back to
353 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y, 200, 280);
354 [window setFrame:initialFrame display:YES];
355 [controller_ resetWindowGrowthState];
356 [controller_ adjustWindowHeightBy:40];
357 finalFrame = [window frame];
358 EXPECT_FALSE(NSEqualRects(finalFrame, initialFrame));
359 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame));
360 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
361 [controller_ adjustWindowHeightBy:-40];
362 finalFrame = [window frame];
363 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame));
364 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame));
366 // Put the window slightly offscreen and try again. The height should not
368 initialFrame = NSMakeRect(workarea.origin.x - 10, 0, 200, 280);
369 [window setFrame:initialFrame display:YES];
370 [controller_ resetWindowGrowthState];
371 [controller_ adjustWindowHeightBy:40];
372 EXPECT_TRUE(NSEqualRects([window frame], initialFrame));
373 [controller_ adjustWindowHeightBy:-40];
374 EXPECT_TRUE(NSEqualRects([window frame], initialFrame));
376 // Make the window the same size as the workarea. Resizing both larger and
377 // smaller should have no effect.
378 [window setFrame:workarea display:YES];
379 [controller_ resetWindowGrowthState];
380 [controller_ adjustWindowHeightBy:40];
381 EXPECT_TRUE(NSEqualRects([window frame], workarea));
382 [controller_ adjustWindowHeightBy:-40];
383 EXPECT_TRUE(NSEqualRects([window frame], workarea));
385 // Make the window smaller than the workarea and place it near the bottom of
386 // the workarea. The window should grow down until it hits the bottom and
387 // then continue to grow up. Then shrink it, and it should return to where it
389 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y + 5,
391 [window setFrame:initialFrame display:YES];
392 [controller_ resetWindowGrowthState];
393 [controller_ adjustWindowHeightBy:40];
394 finalFrame = [window frame];
395 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame));
396 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
397 [controller_ adjustWindowHeightBy:-40];
398 finalFrame = [window frame];
399 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame));
400 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame));
402 // Inset the window slightly from the workarea. It should not grow to be
403 // larger than the workarea. Shrink it; it should return to where it started.
404 initialFrame = NSInsetRect(workarea, 0, 5);
405 [window setFrame:initialFrame display:YES];
406 [controller_ resetWindowGrowthState];
407 [controller_ adjustWindowHeightBy:40];
408 finalFrame = [window frame];
409 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame));
410 EXPECT_FLOAT_EQ(NSHeight(workarea), NSHeight(finalFrame));
411 [controller_ adjustWindowHeightBy:-40];
412 finalFrame = [window frame];
413 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame));
414 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame));
416 // Place the window at the bottom of the screen and grow; it should grow
417 // upwards. Move the window off the bottom, then shrink. It should then shrink
419 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y, 200, 280);
420 [window setFrame:initialFrame display:YES];
421 [controller_ resetWindowGrowthState];
422 [controller_ adjustWindowHeightBy:40];
423 finalFrame = [window frame];
424 EXPECT_FALSE(NSEqualRects(finalFrame, initialFrame));
425 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame));
426 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
427 NSPoint oldOrigin = initialFrame.origin;
428 NSPoint newOrigin = NSMakePoint(oldOrigin.x, oldOrigin.y + 10);
429 [window setFrameOrigin:newOrigin];
430 initialFrame = [window frame];
431 EXPECT_FLOAT_EQ(NSMinY(initialFrame), oldOrigin.y + 10);
432 [controller_ adjustWindowHeightBy:-40];
433 finalFrame = [window frame];
434 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame) + 40);
435 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) - 40);
437 // Do the "inset" test above, but using multiple calls to
438 // |-adjustWindowHeightBy|; the result should be the same.
439 initialFrame = NSInsetRect(workarea, 0, 5);
440 [window setFrame:initialFrame display:YES];
441 [controller_ resetWindowGrowthState];
442 for (int i = 0; i < 8; i++)
443 [controller_ adjustWindowHeightBy:5];
444 finalFrame = [window frame];
445 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame));
446 EXPECT_FLOAT_EQ(NSHeight(workarea), NSHeight(finalFrame));
447 for (int i = 0; i < 8; i++)
448 [controller_ adjustWindowHeightBy:-5];
449 finalFrame = [window frame];
450 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame));
451 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame));
454 // Test to make sure resizing and relaying-out subviews works correctly.
455 TEST_F(BrowserWindowControllerTest, TestResizeViews) {
456 TabStripView* tabstrip = [controller_ tabStripView];
457 NSView* contentView = [[tabstrip window] contentView];
458 NSView* toolbar = [controller_ toolbarView];
459 NSView* infobar = [controller_ infoBarContainerView];
461 // We need to muck with the views a bit to put us in a consistent state before
462 // we start resizing. In particular, we need to move the tab strip to be
463 // immediately above the content area, since we layout views to be directly
464 // under the tab strip.
465 NSRect tabstripFrame = [tabstrip frame];
466 tabstripFrame.origin.y = NSMaxY([contentView frame]);
467 [tabstrip setFrame:tabstripFrame];
469 // Make the download shelf and set its initial 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];
476 // Force a layout and check each view's frame.
477 [controller_ layoutSubviews];
478 CheckViewPositions(controller_);
480 // Expand the infobar to 60px and recheck
481 [controller_ resizeView:infobar newHeight:60];
482 CheckViewPositions(controller_);
484 // Expand the toolbar to 64px and recheck
485 [controller_ resizeView:toolbar newHeight:64];
486 CheckViewPositions(controller_);
488 // Add a 30px download shelf and recheck
489 [controller_ resizeView:download newHeight:30];
490 CheckViewPositions(controller_);
492 // Shrink the infobar to 0px and toolbar to 39px and recheck
493 [controller_ resizeView:infobar newHeight:0];
494 [controller_ resizeView:toolbar newHeight:39];
495 CheckViewPositions(controller_);
498 TEST_F(BrowserWindowControllerTest, TestResizeViewsWithBookmarkBar) {
499 // Force a display of the bookmark bar.
500 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
501 [controller_ browserWindow]->BookmarkBarStateChanged(
502 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
504 TabStripView* tabstrip = [controller_ tabStripView];
505 NSView* contentView = [[tabstrip window] contentView];
506 NSView* toolbar = [controller_ toolbarView];
507 NSView* bookmark = [controller_ bookmarkView];
508 NSView* infobar = [controller_ infoBarContainerView];
510 // We need to muck with the views a bit to put us in a consistent state before
511 // we start resizing. In particular, we need to move the tab strip to be
512 // immediately above the content area, since we layout views to be directly
513 // under the tab strip.
514 NSRect tabstripFrame = [tabstrip frame];
515 tabstripFrame.origin.y = NSMaxY([contentView frame]);
516 [tabstrip setFrame:tabstripFrame];
518 // The download shelf is created lazily. Force-create it and set its initial
520 [controller_ createAndAddDownloadShelf];
521 NSView* download = [[controller_ downloadShelf] view];
522 NSRect downloadFrame = [download frame];
523 downloadFrame.size.height = 0;
524 [download setFrame:downloadFrame];
526 // Force a layout and check each view's frame.
527 [controller_ layoutSubviews];
528 CheckViewPositions(controller_);
530 // Add the bookmark bar and recheck.
531 [controller_ resizeView:bookmark newHeight:40];
532 CheckViewPositions(controller_);
534 // Expand the infobar to 60px and recheck
535 [controller_ resizeView:infobar newHeight:60];
536 CheckViewPositions(controller_);
538 // Expand the toolbar to 64px and recheck
539 [controller_ resizeView:toolbar newHeight:64];
540 CheckViewPositions(controller_);
542 // Add a 30px download shelf and recheck
543 [controller_ resizeView:download newHeight:30];
544 CheckViewPositions(controller_);
546 // Remove the bookmark bar and recheck
547 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, false);
548 [controller_ browserWindow]->BookmarkBarStateChanged(
549 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
550 [controller_ resizeView:bookmark newHeight:0];
551 CheckViewPositions(controller_);
553 // Shrink the infobar to 0px and toolbar to 39px and recheck
554 [controller_ resizeView:infobar newHeight:0];
555 [controller_ resizeView:toolbar newHeight:39];
556 CheckViewPositions(controller_);
559 // Make sure, by default, the bookmark bar and the toolbar are the same width.
560 TEST_F(BrowserWindowControllerTest, BookmarkBarIsSameWidth) {
561 // Set the pref to the bookmark bar is visible when the toolbar is
563 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
565 // Make sure the bookmark bar is the same width as the toolbar
566 NSView* bookmarkBarView = [controller_ bookmarkView];
567 NSView* toolbarView = [controller_ toolbarView];
568 EXPECT_EQ([toolbarView frame].size.width,
569 [bookmarkBarView frame].size.width);
572 TEST_F(BrowserWindowControllerTest, TestTopRightForBubble) {
573 // The bookmark bubble must be attached to a lit and visible star.
574 [controller_ setStarredState:YES];
575 NSPoint p = [controller_ bookmarkBubblePoint]; // Window coordinates.
576 NSRect all = [[controller_ window] frame]; // Screen coordinates.
578 // As a sanity check make sure the point is vaguely in the top right
580 EXPECT_GT(p.y, all.size.height / 2);
581 EXPECT_GT(p.x, all.size.width / 2);
584 // By the "zoom frame", we mean what Apple calls the "standard frame".
585 TEST_F(BrowserWindowControllerTest, TestZoomFrame) {
586 NSWindow* window = [controller_ window];
588 NSRect screenFrame = [[window screen] visibleFrame];
589 ASSERT_FALSE(NSIsEmptyRect(screenFrame));
591 // Minimum zoomed width is the larger of 60% of available horizontal space or
592 // 60% of available vertical space, subject to available horizontal space.
593 CGFloat minZoomWidth =
594 std::min(std::max((CGFloat)0.6 * screenFrame.size.width,
595 (CGFloat)0.6 * screenFrame.size.height),
596 screenFrame.size.width);
598 // |testFrame| is the size of the window we start out with, and |zoomFrame| is
599 // the one returned by |-windowWillUseStandardFrame:defaultFrame:|.
603 // 1. Test a case where it zooms the window both horizontally and vertically,
604 // and only moves it vertically. "+ 32", etc. are just arbitrary constants
605 // used to check that the window is moved properly and not just to the origin;
606 // they should be small enough to not shove windows off the screen.
607 testFrame.size.width = 0.5 * minZoomWidth;
608 testFrame.size.height = 0.5 * screenFrame.size.height;
609 testFrame.origin.x = screenFrame.origin.x + 32; // See above.
610 testFrame.origin.y = screenFrame.origin.y + 23;
611 [window setFrame:testFrame display:NO];
612 zoomFrame = [controller_ windowWillUseStandardFrame:window
613 defaultFrame:screenFrame];
614 EXPECT_LE(minZoomWidth, zoomFrame.size.width);
615 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
616 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x);
617 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
619 // 2. Test a case where it zooms the window only horizontally, and only moves
621 testFrame.size.width = 0.5 * minZoomWidth;
622 testFrame.size.height = screenFrame.size.height;
623 testFrame.origin.x = screenFrame.origin.x + screenFrame.size.width -
624 testFrame.size.width;
625 testFrame.origin.y = screenFrame.origin.y;
626 [window setFrame:testFrame display:NO];
627 zoomFrame = [controller_ windowWillUseStandardFrame:window
628 defaultFrame:screenFrame];
629 EXPECT_LE(minZoomWidth, zoomFrame.size.width);
630 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
631 EXPECT_EQ(screenFrame.origin.x + screenFrame.size.width -
632 zoomFrame.size.width, zoomFrame.origin.x);
633 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
635 // 3. Test a case where it zooms the window only vertically, and only moves it
637 testFrame.size.width = std::min((CGFloat)1.1 * minZoomWidth,
638 screenFrame.size.width);
639 testFrame.size.height = 0.3 * screenFrame.size.height;
640 testFrame.origin.x = screenFrame.origin.x + 32; // See above (in 1.).
641 testFrame.origin.y = screenFrame.origin.y + 123;
642 [window setFrame:testFrame display:NO];
643 zoomFrame = [controller_ windowWillUseStandardFrame:window
644 defaultFrame:screenFrame];
645 // Use the actual width of the window frame, since it's subject to rounding.
646 EXPECT_EQ([window frame].size.width, zoomFrame.size.width);
647 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
648 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x);
649 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
651 // 4. Test a case where zooming should do nothing (i.e., we're already at a
653 testFrame.size.width = std::min((CGFloat)1.1 * minZoomWidth,
654 screenFrame.size.width);
655 testFrame.size.height = screenFrame.size.height;
656 testFrame.origin.x = screenFrame.origin.x + 32; // See above (in 1.).
657 testFrame.origin.y = screenFrame.origin.y;
658 [window setFrame:testFrame display:NO];
659 zoomFrame = [controller_ windowWillUseStandardFrame:window
660 defaultFrame:screenFrame];
661 // Use the actual width of the window frame, since it's subject to rounding.
662 EXPECT_EQ([window frame].size.width, zoomFrame.size.width);
663 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
664 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x);
665 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
668 TEST_F(BrowserWindowControllerTest, TestFindBarOnTop) {
669 FindBarBridge bridge(NULL);
670 [controller_ addFindBar:bridge.find_bar_cocoa_controller()];
672 // Test that the Z-order of the find bar is on top of everything.
673 NSArray* subviews = [controller_.chromeContentView subviews];
674 NSUInteger findBar_index =
675 [subviews indexOfObject:[controller_ findBarView]];
676 EXPECT_NE(NSNotFound, findBar_index);
677 NSUInteger toolbar_index =
678 [subviews indexOfObject:[controller_ toolbarView]];
679 EXPECT_NE(NSNotFound, toolbar_index);
680 NSUInteger bookmark_index =
681 [subviews indexOfObject:[controller_ bookmarkView]];
682 EXPECT_NE(NSNotFound, bookmark_index);
684 EXPECT_GT(findBar_index, toolbar_index);
685 EXPECT_GT(findBar_index, bookmark_index);
688 // Verify that hit testing works correctly when the bookmark bar overlaps
690 TEST_F(BrowserWindowControllerTest, BookmarkBarHitTest) {
691 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
692 [controller_ browserWindow]->BookmarkBarStateChanged(
693 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
695 NSView* bookmarkView = [controller_ bookmarkView];
696 NSView* contentView = [[controller_ window] contentView];
697 NSPoint point = [bookmarkView convertPoint:NSMakePoint(1, 1)
698 toView:[contentView superview]];
700 EXPECT_TRUE([[contentView hitTest:point] isDescendantOf:bookmarkView]);
703 @interface BrowserWindowControllerFakeFullscreen : BrowserWindowController {
705 // We release the window ourselves, so we don't have to rely on the unittest
707 base::scoped_nsobject<NSWindow> testFullscreenWindow_;
711 class BrowserWindowFullScreenControllerTest : public CocoaProfileTest {
713 void SetUp() override {
714 CocoaProfileTest::SetUp();
715 ASSERT_TRUE(browser());
718 [[BrowserWindowControllerFakeFullscreen alloc] initWithBrowser:browser()
722 void TearDown() override {
724 CocoaProfileTest::TearDown();
728 BrowserWindowController* controller_;
731 // Check if the window is front most or if one of its child windows (such
732 // as a status bubble) is front most.
733 static bool IsFrontWindow(NSWindow* window) {
734 NSWindow* frontmostWindow = [[NSApp orderedWindows] objectAtIndex:0];
735 return [frontmostWindow isEqual:window] ||
736 [[frontmostWindow parentWindow] isEqual:window];
739 void WaitForFullScreenTransition() {
740 content::WindowedNotificationObserver observer(
741 chrome::NOTIFICATION_FULLSCREEN_CHANGED,
742 content::NotificationService::AllSources());
746 // http://crbug.com/53586
747 TEST_F(BrowserWindowFullScreenControllerTest, DISABLED_TestFullscreen) {
748 [controller_ showWindow:nil];
749 EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
751 [controller_ enterBrowserFullscreenWithToolbar:YES];
752 WaitForFullScreenTransition();
753 EXPECT_TRUE([controller_ isInAnyFullscreenMode]);
755 [controller_ exitAnyFullscreen];
756 WaitForFullScreenTransition();
757 EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
760 // If this test fails, it is usually a sign that the bots have some sort of
761 // problem (such as a modal dialog up). This tests is a very useful canary, so
762 // please do not mark it as flaky without first verifying that there are no bot
764 // http://crbug.com/53586
765 TEST_F(BrowserWindowFullScreenControllerTest, DISABLED_TestActivate) {
766 [controller_ showWindow:nil];
768 EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
770 [controller_ activate];
771 EXPECT_TRUE(IsFrontWindow([controller_ window]));
773 [controller_ enterBrowserFullscreenWithToolbar:YES];
774 WaitForFullScreenTransition();
775 [controller_ activate];
777 // No fullscreen window on 10.7+.
778 if (base::mac::IsOSSnowLeopard())
779 EXPECT_TRUE(IsFrontWindow([controller_ createFullscreenWindow]));
781 // We have to cleanup after ourselves by unfullscreening.
782 [controller_ exitAnyFullscreen];
783 WaitForFullScreenTransition();
786 @implementation BrowserWindowControllerFakeFullscreen
787 // Override |-createFullscreenWindow| to return a dummy window. This isn't
788 // needed to pass the test, but because the dummy window is only 100x100, it
789 // prevents the real fullscreen window from flashing up and taking over the
790 // whole screen. We have to return an actual window because |-layoutSubviews|
791 // looks at the window's frame.
792 - (NSWindow*)createFullscreenWindow {
793 if (testFullscreenWindow_.get())
794 return testFullscreenWindow_.get();
796 testFullscreenWindow_.reset(
797 [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,400,400)
798 styleMask:NSBorderlessWindowMask
799 backing:NSBackingStoreBuffered
801 [[testFullscreenWindow_ contentView] setWantsLayer:YES];
802 return testFullscreenWindow_.get();
806 /* TODO(???): test other methods of BrowserWindowController */