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.
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]);
207 // TODO(jrg): This crashes trying to create the BookmarkBarController, adding
208 // an observer to the BookmarkModel.
209 TEST_F(BrowserWindowControllerTest, TestIncognitoWidthSpace) {
210 scoped_ptr<TestingProfile> incognito_profile(new TestingProfile());
211 incognito_profile->set_off_the_record(true);
212 scoped_ptr<Browser> browser(
213 new Browser(Browser::CreateParams(incognito_profile.get(),
214 chrome::GetActiveDesktop()));
215 controller_.reset([[BrowserWindowController alloc]
216 initWithBrowser:browser.get()
219 NSRect tabFrame = [[controller_ tabStripView] frame];
220 [controller_ installIncognitoBadge];
221 NSRect newTabFrame = [[controller_ tabStripView] frame];
222 EXPECT_GT(tabFrame.size.width, newTabFrame.size.width);
224 controller_.release();
230 // Returns the frame of the view in window coordinates.
231 NSRect FrameInWindowForView(NSView* view) {
232 return [[view superview] convertRect:[view frame] toView:nil];
235 // Whether the view's frame is within the bounds of the superview.
236 BOOL ViewContainmentValid(NSView* view) {
237 if (NSIsEmptyRect([view frame]))
240 return NSContainsRect([[view superview] bounds], [view frame]);
243 // Checks the view hierarchy rooted at |view| to ensure that each view is
244 // properly contained.
245 BOOL ViewHierarchyContainmentValid(NSView* view) {
246 // TODO(erikchen): Fix these views to have correct containment.
247 // http://crbug.com/397665.
248 if ([view isKindOfClass:NSClassFromString(@"DownloadShelfView")])
250 if ([view isKindOfClass:NSClassFromString(@"BookmarkBarToolbarView")])
252 if ([view isKindOfClass:NSClassFromString(@"BrowserActionsContainerView")])
255 if (!ViewContainmentValid(view)) {
256 LOG(ERROR) << "View violates containment: " <<
257 [[view description] UTF8String];
261 for (NSView* subview in [view subviews]) {
262 BOOL result = ViewHierarchyContainmentValid(subview);
270 // Verifies that the toolbar, infobar, tab content area, and download shelf
271 // completely fill the area under the tabstrip.
272 void CheckViewPositions(BrowserWindowController* controller) {
273 EXPECT_TRUE(ViewHierarchyContainmentValid([[controller window] contentView]));
275 NSRect contentView = FrameInWindowForView([[controller window] contentView]);
276 NSRect tabstrip = FrameInWindowForView([controller tabStripView]);
277 NSRect toolbar = FrameInWindowForView([controller toolbarView]);
278 NSRect infobar = FrameInWindowForView([controller infoBarContainerView]);
279 NSRect tabContent = FrameInWindowForView([controller tabContentArea]);
280 NSRect download = NSZeroRect;
281 if ([[[controller downloadShelf] view] superview])
282 download = [[[controller downloadShelf] view] frame];
284 EXPECT_EQ(NSMinY(contentView), NSMinY(download));
285 EXPECT_EQ(NSMaxY(download), NSMinY(tabContent));
286 EXPECT_EQ(NSMaxY(tabContent), NSMinY(infobar));
288 // Bookmark bar frame is random memory when hidden.
289 if ([controller bookmarkBarVisible]) {
290 NSRect bookmark = [[controller bookmarkView] frame];
291 EXPECT_EQ(NSMaxY(infobar), NSMinY(bookmark));
292 EXPECT_EQ(NSMaxY(bookmark), NSMinY(toolbar));
293 EXPECT_FALSE([[controller bookmarkView] isHidden]);
295 EXPECT_EQ(NSMaxY(infobar), NSMinY(toolbar));
296 EXPECT_TRUE([[controller bookmarkView] isHidden]);
299 // Toolbar should start immediately under the tabstrip, but the tabstrip is
300 // not necessarily fixed with respect to the content view.
301 EXPECT_EQ(NSMinY(tabstrip), NSMaxY(toolbar));
306 TEST_F(BrowserWindowControllerTest, TestAdjustWindowHeight) {
307 NSWindow* window = [controller_ window];
308 NSRect workarea = [[window screen] visibleFrame];
310 // Place the window well above the bottom of the screen and try to adjust its
311 // height. It should change appropriately (and only downwards). Then get it to
312 // shrink by the same amount; it should return to its original state.
313 NSRect initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y + 100,
315 [window setFrame:initialFrame display:YES];
316 [controller_ resetWindowGrowthState];
317 [controller_ adjustWindowHeightBy:40];
318 NSRect finalFrame = [window frame];
319 EXPECT_FALSE(NSEqualRects(finalFrame, initialFrame));
320 EXPECT_FLOAT_EQ(NSMaxY(finalFrame), NSMaxY(initialFrame));
321 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
322 [controller_ adjustWindowHeightBy:-40];
323 finalFrame = [window frame];
324 EXPECT_FLOAT_EQ(NSMaxY(finalFrame), NSMaxY(initialFrame));
325 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame));
327 // Place the window at the bottom of the screen and try again. Its height
328 // should still change, but it should not grow down below the work area; it
329 // should instead move upwards. Then shrink it and make sure it goes back to
331 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y, 200, 200);
332 [window setFrame:initialFrame display:YES];
333 [controller_ resetWindowGrowthState];
334 [controller_ adjustWindowHeightBy:40];
335 finalFrame = [window frame];
336 EXPECT_FALSE(NSEqualRects(finalFrame, initialFrame));
337 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame));
338 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
339 [controller_ adjustWindowHeightBy:-40];
340 finalFrame = [window frame];
341 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame));
342 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame));
344 // Put the window slightly offscreen and try again. The height should not
346 initialFrame = NSMakeRect(workarea.origin.x - 10, 0, 200, 200);
347 [window setFrame:initialFrame display:YES];
348 [controller_ resetWindowGrowthState];
349 [controller_ adjustWindowHeightBy:40];
350 EXPECT_TRUE(NSEqualRects([window frame], initialFrame));
351 [controller_ adjustWindowHeightBy:-40];
352 EXPECT_TRUE(NSEqualRects([window frame], initialFrame));
354 // Make the window the same size as the workarea. Resizing both larger and
355 // smaller should have no effect.
356 [window setFrame:workarea display:YES];
357 [controller_ resetWindowGrowthState];
358 [controller_ adjustWindowHeightBy:40];
359 EXPECT_TRUE(NSEqualRects([window frame], workarea));
360 [controller_ adjustWindowHeightBy:-40];
361 EXPECT_TRUE(NSEqualRects([window frame], workarea));
363 // Make the window smaller than the workarea and place it near the bottom of
364 // the workarea. The window should grow down until it hits the bottom and
365 // then continue to grow up. Then shrink it, and it should return to where it
367 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y + 5,
369 [window setFrame:initialFrame display:YES];
370 [controller_ resetWindowGrowthState];
371 [controller_ adjustWindowHeightBy:40];
372 finalFrame = [window frame];
373 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame));
374 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
375 [controller_ adjustWindowHeightBy:-40];
376 finalFrame = [window frame];
377 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame));
378 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame));
380 // Inset the window slightly from the workarea. It should not grow to be
381 // larger than the workarea. Shrink it; it should return to where it started.
382 initialFrame = NSInsetRect(workarea, 0, 5);
383 [window setFrame:initialFrame display:YES];
384 [controller_ resetWindowGrowthState];
385 [controller_ adjustWindowHeightBy:40];
386 finalFrame = [window frame];
387 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame));
388 EXPECT_FLOAT_EQ(NSHeight(workarea), NSHeight(finalFrame));
389 [controller_ adjustWindowHeightBy:-40];
390 finalFrame = [window frame];
391 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame));
392 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame));
394 // Place the window at the bottom of the screen and grow; it should grow
395 // upwards. Move the window off the bottom, then shrink. It should then shrink
397 initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y, 200, 200);
398 [window setFrame:initialFrame display:YES];
399 [controller_ resetWindowGrowthState];
400 [controller_ adjustWindowHeightBy:40];
401 finalFrame = [window frame];
402 EXPECT_FALSE(NSEqualRects(finalFrame, initialFrame));
403 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame));
404 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) + 40);
405 NSPoint oldOrigin = initialFrame.origin;
406 NSPoint newOrigin = NSMakePoint(oldOrigin.x, oldOrigin.y + 10);
407 [window setFrameOrigin:newOrigin];
408 initialFrame = [window frame];
409 EXPECT_FLOAT_EQ(NSMinY(initialFrame), oldOrigin.y + 10);
410 [controller_ adjustWindowHeightBy:-40];
411 finalFrame = [window frame];
412 EXPECT_FLOAT_EQ(NSMinY(finalFrame), NSMinY(initialFrame) + 40);
413 EXPECT_FLOAT_EQ(NSHeight(finalFrame), NSHeight(initialFrame) - 40);
415 // Do the "inset" test above, but using multiple calls to
416 // |-adjustWindowHeightBy|; the result should be the same.
417 initialFrame = NSInsetRect(workarea, 0, 5);
418 [window setFrame:initialFrame display:YES];
419 [controller_ resetWindowGrowthState];
420 for (int i = 0; i < 8; i++)
421 [controller_ adjustWindowHeightBy:5];
422 finalFrame = [window frame];
423 EXPECT_FLOAT_EQ(NSMinY(workarea), NSMinY(finalFrame));
424 EXPECT_FLOAT_EQ(NSHeight(workarea), NSHeight(finalFrame));
425 for (int i = 0; i < 8; i++)
426 [controller_ adjustWindowHeightBy:-5];
427 finalFrame = [window frame];
428 EXPECT_FLOAT_EQ(NSMinY(initialFrame), NSMinY(finalFrame));
429 EXPECT_FLOAT_EQ(NSHeight(initialFrame), NSHeight(finalFrame));
432 // Test to make sure resizing and relaying-out subviews works correctly.
433 TEST_F(BrowserWindowControllerTest, TestResizeViews) {
434 TabStripView* tabstrip = [controller_ tabStripView];
435 NSView* contentView = [[tabstrip window] contentView];
436 NSView* toolbar = [controller_ toolbarView];
437 NSView* infobar = [controller_ infoBarContainerView];
439 // We need to muck with the views a bit to put us in a consistent state before
440 // we start resizing. In particular, we need to move the tab strip to be
441 // immediately above the content area, since we layout views to be directly
442 // under the tab strip.
443 NSRect tabstripFrame = [tabstrip frame];
444 tabstripFrame.origin.y = NSMaxY([contentView frame]);
445 [tabstrip setFrame:tabstripFrame];
447 // Make the download shelf and set its initial height to 0.
448 [controller_ createAndAddDownloadShelf];
449 NSView* download = [[controller_ downloadShelf] view];
450 NSRect downloadFrame = [download frame];
451 downloadFrame.size.height = 0;
452 [download setFrame:downloadFrame];
454 // Force a layout and check each view's frame.
455 [controller_ layoutSubviews];
456 CheckViewPositions(controller_);
458 // Expand the infobar to 60px and recheck
459 [controller_ resizeView:infobar newHeight:60];
460 CheckViewPositions(controller_);
462 // Expand the toolbar to 64px and recheck
463 [controller_ resizeView:toolbar newHeight:64];
464 CheckViewPositions(controller_);
466 // Add a 30px download shelf and recheck
467 [controller_ resizeView:download newHeight:30];
468 CheckViewPositions(controller_);
470 // Shrink the infobar to 0px and toolbar to 39px and recheck
471 [controller_ resizeView:infobar newHeight:0];
472 [controller_ resizeView:toolbar newHeight:39];
473 CheckViewPositions(controller_);
476 TEST_F(BrowserWindowControllerTest, TestResizeViewsWithBookmarkBar) {
477 // Force a display of the bookmark bar.
478 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
479 [controller_ browserWindow]->BookmarkBarStateChanged(
480 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
482 TabStripView* tabstrip = [controller_ tabStripView];
483 NSView* contentView = [[tabstrip window] contentView];
484 NSView* toolbar = [controller_ toolbarView];
485 NSView* bookmark = [controller_ bookmarkView];
486 NSView* infobar = [controller_ infoBarContainerView];
488 // We need to muck with the views a bit to put us in a consistent state before
489 // we start resizing. In particular, we need to move the tab strip to be
490 // immediately above the content area, since we layout views to be directly
491 // under the tab strip.
492 NSRect tabstripFrame = [tabstrip frame];
493 tabstripFrame.origin.y = NSMaxY([contentView frame]);
494 [tabstrip setFrame:tabstripFrame];
496 // The download shelf is created lazily. Force-create it and set its initial
498 [controller_ createAndAddDownloadShelf];
499 NSView* download = [[controller_ downloadShelf] view];
500 NSRect downloadFrame = [download frame];
501 downloadFrame.size.height = 0;
502 [download setFrame:downloadFrame];
504 // Force a layout and check each view's frame.
505 [controller_ layoutSubviews];
506 CheckViewPositions(controller_);
508 // Add the bookmark bar and recheck.
509 [controller_ resizeView:bookmark newHeight:40];
510 CheckViewPositions(controller_);
512 // Expand the infobar to 60px and recheck
513 [controller_ resizeView:infobar newHeight:60];
514 CheckViewPositions(controller_);
516 // Expand the toolbar to 64px and recheck
517 [controller_ resizeView:toolbar newHeight:64];
518 CheckViewPositions(controller_);
520 // Add a 30px download shelf and recheck
521 [controller_ resizeView:download newHeight:30];
522 CheckViewPositions(controller_);
524 // Remove the bookmark bar and recheck
525 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, false);
526 [controller_ browserWindow]->BookmarkBarStateChanged(
527 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
528 [controller_ resizeView:bookmark newHeight:0];
529 CheckViewPositions(controller_);
531 // Shrink the infobar to 0px and toolbar to 39px and recheck
532 [controller_ resizeView:infobar newHeight:0];
533 [controller_ resizeView:toolbar newHeight:39];
534 CheckViewPositions(controller_);
537 // Make sure, by default, the bookmark bar and the toolbar are the same width.
538 TEST_F(BrowserWindowControllerTest, BookmarkBarIsSameWidth) {
539 // Set the pref to the bookmark bar is visible when the toolbar is
541 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
543 // Make sure the bookmark bar is the same width as the toolbar
544 NSView* bookmarkBarView = [controller_ bookmarkView];
545 NSView* toolbarView = [controller_ toolbarView];
546 EXPECT_EQ([toolbarView frame].size.width,
547 [bookmarkBarView frame].size.width);
550 TEST_F(BrowserWindowControllerTest, TestTopRightForBubble) {
551 // The bookmark bubble must be attached to a lit and visible star.
552 [controller_ setStarredState:YES];
553 NSPoint p = [controller_ bookmarkBubblePoint]; // Window coordinates.
554 NSRect all = [[controller_ window] frame]; // Screen coordinates.
556 // As a sanity check make sure the point is vaguely in the top right
558 EXPECT_GT(p.y, all.size.height / 2);
559 EXPECT_GT(p.x, all.size.width / 2);
562 // By the "zoom frame", we mean what Apple calls the "standard frame".
563 TEST_F(BrowserWindowControllerTest, TestZoomFrame) {
564 NSWindow* window = [controller_ window];
566 NSRect screenFrame = [[window screen] visibleFrame];
567 ASSERT_FALSE(NSIsEmptyRect(screenFrame));
569 // Minimum zoomed width is the larger of 60% of available horizontal space or
570 // 60% of available vertical space, subject to available horizontal space.
571 CGFloat minZoomWidth =
572 std::min(std::max((CGFloat)0.6 * screenFrame.size.width,
573 (CGFloat)0.6 * screenFrame.size.height),
574 screenFrame.size.width);
576 // |testFrame| is the size of the window we start out with, and |zoomFrame| is
577 // the one returned by |-windowWillUseStandardFrame:defaultFrame:|.
581 // 1. Test a case where it zooms the window both horizontally and vertically,
582 // and only moves it vertically. "+ 32", etc. are just arbitrary constants
583 // used to check that the window is moved properly and not just to the origin;
584 // they should be small enough to not shove windows off the screen.
585 testFrame.size.width = 0.5 * minZoomWidth;
586 testFrame.size.height = 0.5 * screenFrame.size.height;
587 testFrame.origin.x = screenFrame.origin.x + 32; // See above.
588 testFrame.origin.y = screenFrame.origin.y + 23;
589 [window setFrame:testFrame display:NO];
590 zoomFrame = [controller_ windowWillUseStandardFrame:window
591 defaultFrame:screenFrame];
592 EXPECT_LE(minZoomWidth, zoomFrame.size.width);
593 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
594 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x);
595 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
597 // 2. Test a case where it zooms the window only horizontally, and only moves
599 testFrame.size.width = 0.5 * minZoomWidth;
600 testFrame.size.height = screenFrame.size.height;
601 testFrame.origin.x = screenFrame.origin.x + screenFrame.size.width -
602 testFrame.size.width;
603 testFrame.origin.y = screenFrame.origin.y;
604 [window setFrame:testFrame display:NO];
605 zoomFrame = [controller_ windowWillUseStandardFrame:window
606 defaultFrame:screenFrame];
607 EXPECT_LE(minZoomWidth, zoomFrame.size.width);
608 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
609 EXPECT_EQ(screenFrame.origin.x + screenFrame.size.width -
610 zoomFrame.size.width, zoomFrame.origin.x);
611 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
613 // 3. Test a case where it zooms the window only vertically, and only moves it
615 testFrame.size.width = std::min((CGFloat)1.1 * minZoomWidth,
616 screenFrame.size.width);
617 testFrame.size.height = 0.3 * screenFrame.size.height;
618 testFrame.origin.x = screenFrame.origin.x + 32; // See above (in 1.).
619 testFrame.origin.y = screenFrame.origin.y + 123;
620 [window setFrame:testFrame display:NO];
621 zoomFrame = [controller_ windowWillUseStandardFrame:window
622 defaultFrame:screenFrame];
623 // Use the actual width of the window frame, since it's subject to rounding.
624 EXPECT_EQ([window frame].size.width, zoomFrame.size.width);
625 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
626 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x);
627 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
629 // 4. Test a case where zooming should do nothing (i.e., we're already at a
631 testFrame.size.width = std::min((CGFloat)1.1 * minZoomWidth,
632 screenFrame.size.width);
633 testFrame.size.height = screenFrame.size.height;
634 testFrame.origin.x = screenFrame.origin.x + 32; // See above (in 1.).
635 testFrame.origin.y = screenFrame.origin.y;
636 [window setFrame:testFrame display:NO];
637 zoomFrame = [controller_ windowWillUseStandardFrame:window
638 defaultFrame:screenFrame];
639 // Use the actual width of the window frame, since it's subject to rounding.
640 EXPECT_EQ([window frame].size.width, zoomFrame.size.width);
641 EXPECT_EQ(screenFrame.size.height, zoomFrame.size.height);
642 EXPECT_EQ(testFrame.origin.x, zoomFrame.origin.x);
643 EXPECT_EQ(screenFrame.origin.y, zoomFrame.origin.y);
646 TEST_F(BrowserWindowControllerTest, TestFindBarOnTop) {
647 FindBarBridge bridge(NULL);
648 [controller_ addFindBar:bridge.find_bar_cocoa_controller()];
650 // Test that the Z-order of the find bar is on top of everything.
651 NSArray* subviews = [controller_.chromeContentView subviews];
652 NSUInteger findBar_index =
653 [subviews indexOfObject:[controller_ findBarView]];
654 EXPECT_NE(NSNotFound, findBar_index);
655 NSUInteger toolbar_index =
656 [subviews indexOfObject:[controller_ toolbarView]];
657 EXPECT_NE(NSNotFound, toolbar_index);
658 NSUInteger bookmark_index =
659 [subviews indexOfObject:[controller_ bookmarkView]];
660 EXPECT_NE(NSNotFound, bookmark_index);
662 EXPECT_GT(findBar_index, toolbar_index);
663 EXPECT_GT(findBar_index, bookmark_index);
666 // Verify that hit testing works correctly when the bookmark bar overlaps
668 TEST_F(BrowserWindowControllerTest, BookmarkBarHitTest) {
669 profile()->GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
670 [controller_ browserWindow]->BookmarkBarStateChanged(
671 BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
673 NSView* bookmarkView = [controller_ bookmarkView];
674 NSView* contentView = [[controller_ window] contentView];
675 NSPoint point = [bookmarkView convertPoint:NSMakePoint(1, 1)
676 toView:[contentView superview]];
678 EXPECT_TRUE([[contentView hitTest:point] isDescendantOf:bookmarkView]);
681 @interface BrowserWindowControllerFakeFullscreen : BrowserWindowController {
683 // We release the window ourselves, so we don't have to rely on the unittest
685 base::scoped_nsobject<NSWindow> testFullscreenWindow_;
689 class BrowserWindowFullScreenControllerTest : public CocoaProfileTest {
691 void SetUp() override {
692 CocoaProfileTest::SetUp();
693 ASSERT_TRUE(browser());
696 [[BrowserWindowControllerFakeFullscreen alloc] initWithBrowser:browser()
700 void TearDown() override {
702 CocoaProfileTest::TearDown();
706 BrowserWindowController* controller_;
709 // Check if the window is front most or if one of its child windows (such
710 // as a status bubble) is front most.
711 static bool IsFrontWindow(NSWindow *window) {
712 NSWindow* frontmostWindow = [[NSApp orderedWindows] objectAtIndex:0];
713 return [frontmostWindow isEqual:window] ||
714 [[frontmostWindow parentWindow] isEqual:window];
717 void WaitForFullScreenTransition() {
718 content::WindowedNotificationObserver observer(
719 chrome::NOTIFICATION_FULLSCREEN_CHANGED,
720 content::NotificationService::AllSources());
724 // http://crbug.com/53586
725 TEST_F(BrowserWindowFullScreenControllerTest, DISABLED_TestFullscreen) {
726 [controller_ showWindow:nil];
727 EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
729 [controller_ enterBrowserFullscreenWithToolbar:YES];
730 WaitForFullScreenTransition();
731 EXPECT_TRUE([controller_ isInAnyFullscreenMode]);
733 [controller_ exitAnyFullscreen];
734 WaitForFullScreenTransition();
735 EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
738 // If this test fails, it is usually a sign that the bots have some sort of
739 // problem (such as a modal dialog up). This tests is a very useful canary, so
740 // please do not mark it as flaky without first verifying that there are no bot
742 // http://crbug.com/53586
743 TEST_F(BrowserWindowFullScreenControllerTest, DISABLED_TestActivate) {
744 [controller_ showWindow:nil];
746 EXPECT_FALSE([controller_ isInAnyFullscreenMode]);
748 [controller_ activate];
749 EXPECT_TRUE(IsFrontWindow([controller_ window]));
751 [controller_ enterBrowserFullscreenWithToolbar:YES];
752 WaitForFullScreenTransition();
753 [controller_ activate];
755 // No fullscreen window on 10.7+.
756 if (base::mac::IsOSSnowLeopard())
757 EXPECT_TRUE(IsFrontWindow([controller_ createFullscreenWindow]));
759 // We have to cleanup after ourselves by unfullscreening.
760 [controller_ exitAnyFullscreen];
761 WaitForFullScreenTransition();
764 @implementation BrowserWindowControllerFakeFullscreen
765 // Override |-createFullscreenWindow| to return a dummy window. This isn't
766 // needed to pass the test, but because the dummy window is only 100x100, it
767 // prevents the real fullscreen window from flashing up and taking over the
768 // whole screen. We have to return an actual window because |-layoutSubviews|
769 // looks at the window's frame.
770 - (NSWindow*)createFullscreenWindow {
771 if (testFullscreenWindow_.get())
772 return testFullscreenWindow_.get();
774 testFullscreenWindow_.reset(
775 [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,400,400)
776 styleMask:NSBorderlessWindowMask
777 backing:NSBackingStoreBuffered
779 [[testFullscreenWindow_ contentView] setWantsLayer:YES];
780 return testFullscreenWindow_.get();
784 /* TODO(???): test other methods of BrowserWindowController */