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/run_loop_testing.h"
19 #include "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
20 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
21 #include "chrome/browser/ui/host_desktop.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/test/base/testing_profile.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/test/test_utils.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #import "testing/gtest_mac.h"
28 #import "third_party/ocmock/OCMock/OCMock.h"
30 using ::testing::Return;
32 @interface BrowserWindowController (JustForTesting)
33 // Already defined in BWC.
34 - (void)saveWindowPositionIfNeeded;
35 - (void)layoutSubviews;
38 @interface BrowserWindowController (ExposedForTesting)
39 // Implementations are below.
40 - (NSView*)infoBarContainerView;
41 - (NSView*)toolbarView;
42 - (NSView*)bookmarkView;
43 - (BOOL)bookmarkBarVisible;
46 @implementation BrowserWindowController (ExposedForTesting)
47 - (NSView*)infoBarContainerView {
48 return [infoBarContainerController_ view];
51 - (NSView*)toolbarView {
52 return [toolbarController_ view];
55 - (NSView*)bookmarkView {
56 return [bookmarkBarController_ view];
59 - (NSView*)findBarView {
60 return [findBarCocoaController_ view];
63 - (BOOL)bookmarkBarVisible {
64 return [bookmarkBarController_ isVisible];
68 class BrowserWindowControllerTest : public CocoaProfileTest {
70 void SetUp() override {
71 CocoaProfileTest::SetUp();
72 ASSERT_TRUE(browser());
74 controller_ = [[BrowserWindowController alloc] initWithBrowser:browser()
78 void TearDown() override {
80 CocoaProfileTest::TearDown();
84 BrowserWindowController* controller_;
87 TEST_F(BrowserWindowControllerTest, TestSaveWindowPosition) {
88 PrefService* prefs = profile()->GetPrefs();
89 ASSERT_TRUE(prefs != NULL);
91 // Check to make sure there is no existing pref for window placement.
92 const base::DictionaryValue* browser_window_placement =
93 prefs->GetDictionary(prefs::kBrowserWindowPlacement);
94 ASSERT_TRUE(browser_window_placement);
95 EXPECT_TRUE(browser_window_placement->empty());
97 // Ask the window to save its position, then check that a preference
99 BrowserList::SetLastActive(browser());
100 [controller_ saveWindowPositionIfNeeded];
101 browser_window_placement =
102 prefs->GetDictionary(prefs::kBrowserWindowPlacement);
103 ASSERT_TRUE(browser_window_placement);
104 EXPECT_FALSE(browser_window_placement->empty());
107 TEST_F(BrowserWindowControllerTest, TestFullScreenWindow) {
108 // Confirm that |-createFullscreenWindow| doesn't return nil.
109 // See BrowserWindowFullScreenControllerTest for more fullscreen tests.
110 EXPECT_TRUE([controller_ createFullscreenWindow]);
113 TEST_F(BrowserWindowControllerTest, TestNormal) {
114 // Force the bookmark bar to be shown.
115 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
116 [controller_ browserWindow]->BookmarkBarStateChanged(
117 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
119 // Make sure a normal BrowserWindowController is, uh, normal.
120 EXPECT_TRUE([controller_ isTabbedWindow]);
121 EXPECT_TRUE([controller_ hasTabStrip]);
122 EXPECT_FALSE([controller_ hasTitleBar]);
123 EXPECT_TRUE([controller_ isBookmarkBarVisible]);
125 // And make sure a controller for a pop-up window is not normal.
126 // popup_browser will be owned by its window.
127 Browser* popup_browser(new Browser(
128 Browser::CreateParams(Browser::TYPE_POPUP, profile(),
129 chrome::GetActiveDesktop())));
130 NSWindow* cocoaWindow = popup_browser->window()->GetNativeWindow();
131 BrowserWindowController* controller =
132 static_cast<BrowserWindowController*>([cocoaWindow windowController]);
133 ASSERT_TRUE([controller isKindOfClass:[BrowserWindowController class]]);
134 EXPECT_FALSE([controller isTabbedWindow]);
135 EXPECT_FALSE([controller hasTabStrip]);
136 EXPECT_TRUE([controller hasTitleBar]);
137 EXPECT_FALSE([controller isBookmarkBarVisible]);
141 TEST_F(BrowserWindowControllerTest, TestSetBounds) {
142 // Create a normal browser with bounds smaller than the minimum.
143 Browser::CreateParams params(Browser::TYPE_TABBED, profile(),
144 chrome::GetActiveDesktop());
145 params.initial_bounds = gfx::Rect(0, 0, 50, 50);
146 Browser* browser = new Browser(params);
147 NSWindow* cocoaWindow = browser->window()->GetNativeWindow();
148 BrowserWindowController* controller =
149 static_cast<BrowserWindowController*>([cocoaWindow windowController]);
151 ASSERT_TRUE([controller isTabbedWindow]);
152 BrowserWindow* browser_window = [controller browserWindow];
153 EXPECT_EQ(browser_window, browser->window());
154 gfx::Rect bounds = browser_window->GetBounds();
155 EXPECT_EQ(400, bounds.width());
156 EXPECT_EQ(272, bounds.height());
158 // Try to set the bounds smaller than the minimum.
159 browser_window->SetBounds(gfx::Rect(0, 0, 50, 50));
160 bounds = browser_window->GetBounds();
161 EXPECT_EQ(400, bounds.width());
162 EXPECT_EQ(272, bounds.height());
167 TEST_F(BrowserWindowControllerTest, TestSetBoundsPopup) {
168 // Create a popup with bounds smaller than the minimum.
169 Browser::CreateParams params(Browser::TYPE_POPUP, profile(),
170 chrome::GetActiveDesktop());
171 params.initial_bounds = gfx::Rect(0, 0, 50, 50);
172 Browser* browser = new Browser(params);
173 NSWindow* cocoaWindow = browser->window()->GetNativeWindow();
174 BrowserWindowController* controller =
175 static_cast<BrowserWindowController*>([cocoaWindow windowController]);
177 ASSERT_FALSE([controller isTabbedWindow]);
178 BrowserWindow* browser_window = [controller browserWindow];
179 EXPECT_EQ(browser_window, browser->window());
180 gfx::Rect bounds = browser_window->GetBounds();
181 EXPECT_EQ(100, bounds.width());
182 EXPECT_EQ(122, bounds.height());
184 // Try to set the bounds smaller than the minimum.
185 browser_window->SetBounds(gfx::Rect(0, 0, 50, 50));
186 bounds = browser_window->GetBounds();
187 EXPECT_EQ(100, bounds.width());
188 EXPECT_EQ(122, bounds.height());
193 TEST_F(BrowserWindowControllerTest, TestTheme) {
194 [controller_ userChangedTheme];
197 TEST_F(BrowserWindowControllerTest, BookmarkBarControllerIndirection) {
198 EXPECT_FALSE([controller_ isBookmarkBarVisible]);
200 // Explicitly show the bar. Can't use chrome::ToggleBookmarkBarWhenVisible()
201 // because of the notification issues.
202 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
204 [controller_ browserWindow]->BookmarkBarStateChanged(
205 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
206 EXPECT_TRUE([controller_ isBookmarkBarVisible]);
209 TEST_F(BrowserWindowControllerTest, BookmarkBarToggleRespectMinWindowHeight) {
210 Browser::CreateParams params(Browser::TYPE_TABBED, profile(),
211 chrome::GetActiveDesktop());
212 params.initial_bounds = gfx::Rect(0, 0, 50, 280);
213 Browser* browser = new Browser(params);
214 NSWindow* cocoaWindow = browser->window()->GetNativeWindow();
215 BrowserWindowController* controller =
216 static_cast<BrowserWindowController*>([cocoaWindow windowController]);
217 BrowserWindow* browser_window = [controller browserWindow];
218 gfx::Rect bounds = browser_window->GetBounds();
219 EXPECT_EQ(280, bounds.height());
221 // Try to set the bounds smaller than the minimum.
222 // Explicitly show the bar. Can't use chrome::ToggleBookmarkBarWhenVisible()
223 // because of the notification issues.
224 [controller adjustWindowHeightBy:-20];
225 bounds = browser_window->GetBounds();
226 EXPECT_EQ(272, bounds.height());
232 // TODO(jrg): This crashes trying to create the BookmarkBarController, adding
233 // an observer to the BookmarkModel.
234 TEST_F(BrowserWindowControllerTest, TestIncognitoWidthSpace) {
235 scoped_ptr<TestingProfile> incognito_profile(new TestingProfile());
236 incognito_profile->set_off_the_record(true);
237 scoped_ptr<Browser> browser(
238 new Browser(Browser::CreateParams(incognito_profile.get(),
239 chrome::GetActiveDesktop()));
240 controller_.reset([[BrowserWindowController alloc]
241 initWithBrowser:browser.get()
244 NSRect tabFrame = [[controller_ tabStripView] frame];
245 [controller_ installIncognitoBadge];
246 NSRect newTabFrame = [[controller_ tabStripView] frame];
247 EXPECT_GT(tabFrame.size.width, newTabFrame.size.width);
249 controller_.release();
255 // Returns the frame of the view in window coordinates.
256 NSRect FrameInWindowForView(NSView* view) {
257 return [[view superview] convertRect:[view frame] toView:nil];
260 // Whether the view's frame is within the bounds of the superview.
261 BOOL ViewContainmentValid(NSView* view) {
262 if (NSIsEmptyRect([view frame]))
265 return NSContainsRect([[view superview] bounds], [view frame]);
268 // Checks the view hierarchy rooted at |view| to ensure that each view is
269 // properly contained.
270 BOOL ViewHierarchyContainmentValid(NSView* view) {
271 // TODO(erikchen): Fix these views to have correct containment.
272 // http://crbug.com/397665.
273 if ([view isKindOfClass:NSClassFromString(@"DownloadShelfView")])
275 if ([view isKindOfClass:NSClassFromString(@"BookmarkBarToolbarView")])
277 if ([view isKindOfClass:NSClassFromString(@"BrowserActionsContainerView")])
280 if (!ViewContainmentValid(view)) {
281 LOG(ERROR) << "View violates containment: " <<
282 [[view description] UTF8String];
286 for (NSView* subview in [view subviews]) {
287 BOOL result = ViewHierarchyContainmentValid(subview);
295 // Verifies that the toolbar, infobar, tab content area, and download shelf
296 // completely fill the area under the tabstrip.
297 void CheckViewPositions(BrowserWindowController* controller) {
298 EXPECT_TRUE(ViewHierarchyContainmentValid([[controller window] contentView]));
300 NSRect contentView = FrameInWindowForView([[controller window] contentView]);
301 NSRect tabstrip = FrameInWindowForView([controller tabStripView]);
302 NSRect toolbar = FrameInWindowForView([controller toolbarView]);
303 NSRect infobar = FrameInWindowForView([controller infoBarContainerView]);
304 NSRect tabContent = FrameInWindowForView([controller tabContentArea]);
305 NSRect download = NSZeroRect;
306 if ([[[controller downloadShelf] view] superview])
307 download = [[[controller downloadShelf] view] frame];
309 EXPECT_EQ(NSMinY(contentView), NSMinY(download));
310 EXPECT_EQ(NSMaxY(download), NSMinY(tabContent));
311 EXPECT_EQ(NSMaxY(tabContent), NSMinY(infobar));
313 // Bookmark bar frame is random memory when hidden.
314 if ([controller bookmarkBarVisible]) {
315 NSRect bookmark = [[controller bookmarkView] frame];
316 EXPECT_EQ(NSMaxY(infobar), NSMinY(bookmark));
317 EXPECT_EQ(NSMaxY(bookmark), NSMinY(toolbar));
318 EXPECT_FALSE([[controller bookmarkView] isHidden]);
320 EXPECT_EQ(NSMaxY(infobar), NSMinY(toolbar));
321 EXPECT_TRUE([[controller bookmarkView] isHidden]);
324 // Toolbar should start immediately under the tabstrip, but the tabstrip is
325 // not necessarily fixed with respect to the content view.
326 EXPECT_EQ(NSMinY(tabstrip), NSMaxY(toolbar));
331 TEST_F(BrowserWindowControllerTest, TestAdjustWindowHeight) {
332 NSWindow* window = [controller_ window];
333 NSRect workarea = [[window screen] visibleFrame];
335 // Place the window well above the bottom of the screen and try to adjust its
336 // height. It should change appropriately (and only downwards). Then get it to
337 // shrink by the same amount; it should return to its original state.
338 NSRect initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y + 100,
340 [window setFrame:initialFrame display:YES];
341 [controller_ resetWindowGrowthState];
342 [controller_ adjustWindowHeightBy:40];
343 NSRect finalFrame = [window frame];
344 EXPECT_NSNE(finalFrame, initialFrame);
345 EXPECT_FLOAT_EQ(NSMaxY(finalFrame), NSMaxY(initialFrame));
346 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
347 [controller_ adjustWindowHeightBy:-40];
348 finalFrame = [window frame];
349 EXPECT_FLOAT_EQ(NSMaxY(finalFrame), NSMaxY(initialFrame));
350 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame));
352 // Place the window at the bottom of the screen and try again. Its height
353 // should still change, but it should not grow down below the work area; it
354 // should instead move upwards. Then shrink it and make sure it goes back to
356 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y, 200, 280);
357 [window setFrame:initialFrame display:YES];
358 [controller_ resetWindowGrowthState];
359 [controller_ adjustWindowHeightBy:40];
360 finalFrame = [window frame];
361 EXPECT_NSNE(finalFrame, initialFrame);
362 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame));
363 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
364 [controller_ adjustWindowHeightBy:-40];
365 finalFrame = [window frame];
366 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame));
367 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame));
369 // Put the window slightly offscreen and try again. The height should not
371 initialFrame = NSMakeRect(workarea.origin.x - 10, 0, 200, 280);
372 [window setFrame:initialFrame display:YES];
373 [controller_ resetWindowGrowthState];
374 [controller_ adjustWindowHeightBy:40];
375 EXPECT_NSEQ([window frame], initialFrame);
376 [controller_ adjustWindowHeightBy:-40];
377 EXPECT_NSEQ([window frame], initialFrame);
379 // Make the window the same size as the workarea. Resizing both larger and
380 // smaller should have no effect.
381 [window setFrame:workarea display:YES];
382 [controller_ resetWindowGrowthState];
383 [controller_ adjustWindowHeightBy:40];
384 EXPECT_NSEQ([window frame], workarea);
385 [controller_ adjustWindowHeightBy:-40];
386 EXPECT_NSEQ([window frame], workarea);
388 // Make the window smaller than the workarea and place it near the bottom of
389 // the workarea. The window should grow down until it hits the bottom and
390 // then continue to grow up. Then shrink it, and it should return to where it
392 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y + 5,
394 [window setFrame:initialFrame display:YES];
395 [controller_ resetWindowGrowthState];
396 [controller_ adjustWindowHeightBy:40];
397 finalFrame = [window frame];
398 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame));
399 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
400 [controller_ adjustWindowHeightBy:-40];
401 finalFrame = [window frame];
402 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame));
403 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame));
405 // Inset the window slightly from the workarea. It should not grow to be
406 // larger than the workarea. Shrink it; it should return to where it started.
407 initialFrame = NSInsetRect(workarea, 0, 5);
408 [window setFrame:initialFrame display:YES];
409 [controller_ resetWindowGrowthState];
410 [controller_ adjustWindowHeightBy:40];
411 finalFrame = [window frame];
412 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame));
413 EXPECT_FLOAT_EQ(NSHeight(workarea), NSHeight(finalFrame));
414 [controller_ adjustWindowHeightBy:-40];
415 finalFrame = [window frame];
416 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame));
417 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame));
419 // Place the window at the bottom of the screen and grow; it should grow
420 // upwards. Move the window off the bottom, then shrink. It should then shrink
422 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y, 200, 280);
423 [window setFrame:initialFrame display:YES];
424 [controller_ resetWindowGrowthState];
425 [controller_ adjustWindowHeightBy:40];
426 finalFrame = [window frame];
427 EXPECT_NSNE(finalFrame, initialFrame);
428 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame));
429 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
430 NSPoint oldOrigin = initialFrame.origin;
431 NSPoint newOrigin = NSMakePoint(oldOrigin.x, oldOrigin.y + 10);
432 [window setFrameOrigin:newOrigin];
433 initialFrame = [window frame];
434 EXPECT_FLOAT_EQ(NSMinY(initialFrame), oldOrigin.y + 10);
435 [controller_ adjustWindowHeightBy:-40];
436 finalFrame = [window frame];
437 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame) + 40);
438 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) - 40);
440 // Do the "inset" test above, but using multiple calls to
441 // |-adjustWindowHeightBy|; the result should be the same.
442 initialFrame = NSInsetRect(workarea, 0, 5);
443 [window setFrame:initialFrame display:YES];
444 [controller_ resetWindowGrowthState];
445 for (int i = 0; i < 8; i++)
446 [controller_ adjustWindowHeightBy:5];
447 finalFrame = [window frame];
448 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame));
449 EXPECT_FLOAT_EQ(NSHeight(workarea), NSHeight(finalFrame));
450 for (int i = 0; i < 8; i++)
451 [controller_ adjustWindowHeightBy:-5];
452 finalFrame = [window frame];
453 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame));
454 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame));
457 // Test to make sure resizing and relaying-out subviews works correctly.
458 TEST_F(BrowserWindowControllerTest, TestResizeViews) {
459 TabStripView* tabstrip = [controller_ tabStripView];
460 NSView* contentView = [[tabstrip window] contentView];
461 NSView* toolbar = [controller_ toolbarView];
462 NSView* infobar = [controller_ infoBarContainerView];
464 // We need to muck with the views a bit to put us in a consistent state before
465 // we start resizing. In particular, we need to move the tab strip to be
466 // immediately above the content area, since we layout views to be directly
467 // under the tab strip.
468 NSRect tabstripFrame = [tabstrip frame];
469 tabstripFrame.origin.y = NSMaxY([contentView frame]);
470 [tabstrip setFrame:tabstripFrame];
472 // Make the download shelf and set its initial height to 0.
473 [controller_ createAndAddDownloadShelf];
474 NSView* download = [[controller_ downloadShelf] view];
475 NSRect downloadFrame = [download frame];
476 downloadFrame.size.height = 0;
477 [download setFrame:downloadFrame];
479 // Force a layout and check each view's frame.
480 [controller_ layoutSubviews];
481 CheckViewPositions(controller_);
483 // Expand the infobar to 60px and recheck
484 [controller_ resizeView:infobar newHeight:60];
485 CheckViewPositions(controller_);
487 // Expand the toolbar to 64px and recheck
488 [controller_ resizeView:toolbar newHeight:64];
489 CheckViewPositions(controller_);
491 // Add a 30px download shelf and recheck
492 [controller_ resizeView:download newHeight:30];
493 CheckViewPositions(controller_);
495 // Shrink the infobar to 0px and toolbar to 39px and recheck
496 [controller_ resizeView:infobar newHeight:0];
497 [controller_ resizeView:toolbar newHeight:39];
498 CheckViewPositions(controller_);
501 TEST_F(BrowserWindowControllerTest, TestResizeViewsWithBookmarkBar) {
502 // Force a display of the bookmark bar.
503 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
504 [controller_ browserWindow]->BookmarkBarStateChanged(
505 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
507 TabStripView* tabstrip = [controller_ tabStripView];
508 NSView* contentView = [[tabstrip window] contentView];
509 NSView* toolbar = [controller_ toolbarView];
510 NSView* bookmark = [controller_ bookmarkView];
511 NSView* infobar = [controller_ infoBarContainerView];
513 // We need to muck with the views a bit to put us in a consistent state before
514 // we start resizing. In particular, we need to move the tab strip to be
515 // immediately above the content area, since we layout views to be directly
516 // under the tab strip.
517 NSRect tabstripFrame = [tabstrip frame];
518 tabstripFrame.origin.y = NSMaxY([contentView frame]);
519 [tabstrip setFrame:tabstripFrame];
521 // The download shelf is created lazily. Force-create it and set its initial
523 [controller_ createAndAddDownloadShelf];
524 NSView* download = [[controller_ downloadShelf] view];
525 NSRect downloadFrame = [download frame];
526 downloadFrame.size.height = 0;
527 [download setFrame:downloadFrame];
529 // Force a layout and check each view's frame.
530 [controller_ layoutSubviews];
531 CheckViewPositions(controller_);
533 // Add the bookmark bar and recheck.
534 [controller_ resizeView:bookmark newHeight:40];
535 CheckViewPositions(controller_);
537 // Expand the infobar to 60px and recheck
538 [controller_ resizeView:infobar newHeight:60];
539 CheckViewPositions(controller_);
541 // Expand the toolbar to 64px and recheck
542 [controller_ resizeView:toolbar newHeight:64];
543 CheckViewPositions(controller_);
545 // Add a 30px download shelf and recheck
546 [controller_ resizeView:download newHeight:30];
547 CheckViewPositions(controller_);
549 // Remove the bookmark bar and recheck
550 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, false);
551 [controller_ browserWindow]->BookmarkBarStateChanged(
552 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
553 [controller_ resizeView:bookmark newHeight:0];
554 CheckViewPositions(controller_);
556 // Shrink the infobar to 0px and toolbar to 39px and recheck
557 [controller_ resizeView:infobar newHeight:0];
558 [controller_ resizeView:toolbar newHeight:39];
559 CheckViewPositions(controller_);
562 // Make sure, by default, the bookmark bar and the toolbar are the same width.
563 TEST_F(BrowserWindowControllerTest, BookmarkBarIsSameWidth) {
564 // Set the pref to the bookmark bar is visible when the toolbar is
566 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
568 // Make sure the bookmark bar is the same width as the toolbar
569 NSView* bookmarkBarView = [controller_ bookmarkView];
570 NSView* toolbarView = [controller_ toolbarView];
571 EXPECT_EQ([toolbarView frame].size.width,
572 [bookmarkBarView frame].size.width);
575 TEST_F(BrowserWindowControllerTest, TestTopRightForBubble) {
576 // The bookmark bubble must be attached to a lit and visible star.
577 [controller_ setStarredState:YES];
578 NSPoint p = [controller_ bookmarkBubblePoint]; // Window coordinates.
579 NSRect all = [[controller_ window] frame]; // Screen coordinates.
581 // As a sanity check make sure the point is vaguely in the top right
583 EXPECT_GT(p.y, all.size.height / 2);
584 EXPECT_GT(p.x, all.size.width / 2);
587 // By the "zoom frame", we mean what Apple calls the "standard frame".
588 TEST_F(BrowserWindowControllerTest, TestZoomFrame) {
589 NSWindow* window = [controller_ window];
591 NSRect screenFrame = [[window screen] visibleFrame];
592 ASSERT_FALSE(NSIsEmptyRect(screenFrame));
594 // Minimum zoomed width is the larger of 60% of available horizontal space or
595 // 60% of available vertical space, subject to available horizontal space.
596 CGFloat minZoomWidth =
597 std::min(std::max((CGFloat)0.6 * screenFrame.size.width,
598 (CGFloat)0.6 * screenFrame.size.height),
599 screenFrame.size.width);
601 // |testFrame| is the size of the window we start out with, and |zoomFrame| is
602 // the one returned by |-windowWillUseStandardFrame:defaultFrame:|.
606 // 1. Test a case where it zooms the window both horizontally and vertically,
607 // and only moves it vertically. "+ 32", etc. are just arbitrary constants
608 // used to check that the window is moved properly and not just to the origin;
609 // they should be small enough to not shove windows off the screen.
610 testFrame.size.width = 0.5 * minZoomWidth;
611 testFrame.size.height = 0.5 * screenFrame.size.height;
612 testFrame.origin.x = screenFrame.origin.x + 32; // See above.
613 testFrame.origin.y = screenFrame.origin.y + 23;
614 [window setFrame:testFrame display:NO];
615 zoomFrame = [controller_ windowWillUseStandardFrame:window
616 defaultFrame:screenFrame];
617 EXPECT_LE(minZoomWidth, zoomFrame.size.width);
618 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
619 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x);
620 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
622 // 2. Test a case where it zooms the window only horizontally, and only moves
624 testFrame.size.width = 0.5 * minZoomWidth;
625 testFrame.size.height = screenFrame.size.height;
626 testFrame.origin.x = screenFrame.origin.x + screenFrame.size.width -
627 testFrame.size.width;
628 testFrame.origin.y = screenFrame.origin.y;
629 [window setFrame:testFrame display:NO];
630 zoomFrame = [controller_ windowWillUseStandardFrame:window
631 defaultFrame:screenFrame];
632 EXPECT_LE(minZoomWidth, zoomFrame.size.width);
633 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
634 EXPECT_EQ(screenFrame.origin.x + screenFrame.size.width -
635 zoomFrame.size.width, zoomFrame.origin.x);
636 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
638 // 3. Test a case where it zooms the window only vertically, and only moves it
640 testFrame.size.width = std::min((CGFloat)1.1 * minZoomWidth,
641 screenFrame.size.width);
642 testFrame.size.height = 0.3 * screenFrame.size.height;
643 testFrame.origin.x = screenFrame.origin.x + 32; // See above (in 1.).
644 testFrame.origin.y = screenFrame.origin.y + 123;
645 [window setFrame:testFrame display:NO];
646 zoomFrame = [controller_ windowWillUseStandardFrame:window
647 defaultFrame:screenFrame];
648 // Use the actual width of the window frame, since it's subject to rounding.
649 EXPECT_EQ([window frame].size.width, zoomFrame.size.width);
650 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
651 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x);
652 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
654 // 4. Test a case where zooming should do nothing (i.e., we're already at a
656 testFrame.size.width = std::min((CGFloat)1.1 * minZoomWidth,
657 screenFrame.size.width);
658 testFrame.size.height = screenFrame.size.height;
659 testFrame.origin.x = screenFrame.origin.x + 32; // See above (in 1.).
660 testFrame.origin.y = screenFrame.origin.y;
661 [window setFrame:testFrame display:NO];
662 zoomFrame = [controller_ windowWillUseStandardFrame:window
663 defaultFrame:screenFrame];
664 // Use the actual width of the window frame, since it's subject to rounding.
665 EXPECT_EQ([window frame].size.width, zoomFrame.size.width);
666 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
667 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x);
668 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
671 TEST_F(BrowserWindowControllerTest, TestFindBarOnTop) {
672 FindBarBridge bridge(NULL);
673 [controller_ addFindBar:bridge.find_bar_cocoa_controller()];
675 // Test that the Z-order of the find bar is on top of everything.
676 NSArray* subviews = [controller_.chromeContentView subviews];
677 NSUInteger findBar_index =
678 [subviews indexOfObject:[controller_ findBarView]];
679 EXPECT_NE(NSNotFound, findBar_index);
680 NSUInteger toolbar_index =
681 [subviews indexOfObject:[controller_ toolbarView]];
682 EXPECT_NE(NSNotFound, toolbar_index);
683 NSUInteger bookmark_index =
684 [subviews indexOfObject:[controller_ bookmarkView]];
685 EXPECT_NE(NSNotFound, bookmark_index);
687 EXPECT_GT(findBar_index, toolbar_index);
688 EXPECT_GT(findBar_index, bookmark_index);
691 // Verify that hit testing works correctly when the bookmark bar overlaps
693 TEST_F(BrowserWindowControllerTest, BookmarkBarHitTest) {
694 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
695 [controller_ browserWindow]->BookmarkBarStateChanged(
696 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
698 NSView* bookmarkView = [controller_ bookmarkView];
699 NSView* contentView = [[controller_ window] contentView];
700 NSPoint point = [bookmarkView convertPoint:NSMakePoint(1, 1)
701 toView:[contentView superview]];
703 EXPECT_TRUE([[contentView hitTest:point] isDescendantOf:bookmarkView]);
706 // Check that when the window becomes/resigns main, the tab strip's background
708 TEST_F(BrowserWindowControllerTest, TabStripBackgroundViewRedrawTest) {
709 NSView* view = controller_.tabStripBackgroundView;
710 id partial_mock = [OCMockObject partialMockForObject:view];
712 [[partial_mock expect] setNeedsDisplay:YES];
713 [[NSNotificationCenter defaultCenter]
714 postNotificationName:NSWindowDidBecomeMainNotification
715 object:controller_.window];
716 [partial_mock verify];
718 [[partial_mock expect] setNeedsDisplay:YES];
719 [[NSNotificationCenter defaultCenter]
720 postNotificationName:NSWindowDidResignMainNotification
721 object:controller_.window];
722 [partial_mock verify];
725 @interface BrowserWindowControllerFakeFullscreen : BrowserWindowController {
727 // We release the window ourselves, so we don't have to rely on the unittest
729 base::scoped_nsobject<NSWindow> testFullscreenWindow_;
733 class BrowserWindowFullScreenControllerTest : public CocoaProfileTest {
735 void SetUp() override {
736 CocoaProfileTest::SetUp();
737 ASSERT_TRUE(browser());
740 [[BrowserWindowControllerFakeFullscreen alloc] initWithBrowser:browser()
744 void TearDown() override {
746 CocoaProfileTest::TearDown();
750 BrowserWindowController* controller_;
753 // Check if the window is front most or if one of its child windows (such
754 // as a status bubble) is front most.
755 static bool IsFrontWindow(NSWindow* window) {
756 NSWindow* frontmostWindow = [[NSApp orderedWindows] objectAtIndex:0];
757 return [frontmostWindow isEqual:window] ||
758 [[frontmostWindow parentWindow] isEqual:window];
761 void WaitForFullScreenTransition() {
762 content::WindowedNotificationObserver observer(
763 chrome::NOTIFICATION_FULLSCREEN_CHANGED,
764 content::NotificationService::AllSources());
768 // http://crbug.com/53586
769 TEST_F(BrowserWindowFullScreenControllerTest, TestFullscreen) {
770 [controller_ showWindow:nil];
771 EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
773 [controller_ enterBrowserFullscreenWithToolbar:YES];
774 WaitForFullScreenTransition();
775 EXPECT_TRUE([controller_ isInAnyFullscreenMode]);
777 [controller_ exitAnyFullscreen];
778 WaitForFullScreenTransition();
779 EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
782 // If this test fails, it is usually a sign that the bots have some sort of
783 // problem (such as a modal dialog up). This tests is a very useful canary, so
784 // please do not mark it as flaky without first verifying that there are no bot
786 // http://crbug.com/53586
787 TEST_F(BrowserWindowFullScreenControllerTest, TestActivate) {
788 [controller_ showWindow:nil];
790 EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
792 [controller_ activate];
793 chrome::testing::NSRunLoopRunAllPending();
794 EXPECT_TRUE(IsFrontWindow([controller_ window]));
796 [controller_ enterBrowserFullscreenWithToolbar:YES];
797 WaitForFullScreenTransition();
798 [controller_ activate];
799 chrome::testing::NSRunLoopRunAllPending();
801 // No fullscreen window on 10.7+.
802 if (base::mac::IsOSSnowLeopard())
803 EXPECT_TRUE(IsFrontWindow([controller_ createFullscreenWindow]));
805 // We have to cleanup after ourselves by unfullscreening.
806 [controller_ exitAnyFullscreen];
807 WaitForFullScreenTransition();
810 @implementation BrowserWindowControllerFakeFullscreen
811 // Override |-createFullscreenWindow| to return a dummy window. This isn't
812 // needed to pass the test, but because the dummy window is only 100x100, it
813 // prevents the real fullscreen window from flashing up and taking over the
814 // whole screen. We have to return an actual window because |-layoutSubviews|
815 // looks at the window's frame.
816 - (NSWindow*)createFullscreenWindow {
817 if (testFullscreenWindow_.get())
818 return testFullscreenWindow_.get();
820 testFullscreenWindow_.reset(
821 [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,400,400)
822 styleMask:NSBorderlessWindowMask
823 backing:NSBackingStoreBuffered
825 [[testFullscreenWindow_ contentView] setWantsLayer:YES];
826 return testFullscreenWindow_.get();
830 /* TODO(???): test other methods of BrowserWindowController */