Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / browser_window_controller_unittest.mm
blob9bc2ce5c7c6ec1508f7b6bc718b15d842e2933da
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;
36 @end
38 @interface BrowserWindowController (ExposedForTesting)
39 // Implementations are below.
40 - (NSView*)infoBarContainerView;
41 - (NSView*)toolbarView;
42 - (NSView*)bookmarkView;
43 - (BOOL)bookmarkBarVisible;
44 @end
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];
66 @end
68 class BrowserWindowControllerTest : public CocoaProfileTest {
69  public:
70   void SetUp() override {
71     CocoaProfileTest::SetUp();
72     ASSERT_TRUE(browser());
74     controller_ = [[BrowserWindowController alloc] initWithBrowser:browser()
75                                                      takeOwnership:NO];
76   }
78   void TearDown() override {
79     [controller_ close];
80     CocoaProfileTest::TearDown();
81   }
83  public:
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
98   // exists.
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]);
138   [controller close];
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());
164   [controller close];
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());
190   [controller close];
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());
228   [controller close];
231 #if 0
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()
242                                 takeOwnership:NO]);
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();
251 #endif
253 namespace {
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]))
263     return YES;
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")])
274     return YES;
275   if ([view isKindOfClass:NSClassFromString(@"BookmarkBarToolbarView")])
276     return YES;
277   if ([view isKindOfClass:NSClassFromString(@"BrowserActionsContainerView")])
278     return YES;
280   if (!ViewContainmentValid(view)) {
281     LOG(ERROR) << "View violates containment: " <<
282         [[view description] UTF8String];
283     return NO;
284   }
286   for (NSView* subview in [view subviews]) {
287     BOOL result = ViewHierarchyContainmentValid(subview);
288     if (!result)
289       return NO;
290   }
292   return YES;
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]);
319   } else {
320     EXPECT_EQ(NSMaxY(infobar), NSMinY(toolbar));
321     EXPECT_TRUE([[controller bookmarkView] isHidden]);
322   }
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));
329 }  // end namespace
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,
339                                    200, 280);
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
355   // the way it was.
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
370   // change this time.
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
391   // was.
392   initialFrame = NSMakeRect(workarea.origin.x, workarea.origin.y + 5,
393                             200, 280);
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
421   // from the bottom.
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
522   // height to 0.
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
565   // first created.
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
582   // of the window.
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];
590   ASSERT_TRUE(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:|.
603   NSRect testFrame;
604   NSRect zoomFrame;
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
623   // it horizontally.
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
639   // vertically.
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
655   // zoomed frame).
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
692 // web contents.
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
707 // view is redrawn.
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 {
726  @private
727   // We release the window ourselves, so we don't have to rely on the unittest
728   // doing it for us.
729   base::scoped_nsobject<NSWindow> testFullscreenWindow_;
731 @end
733 class BrowserWindowFullScreenControllerTest : public CocoaProfileTest {
734  public:
735   void SetUp() override {
736     CocoaProfileTest::SetUp();
737     ASSERT_TRUE(browser());
739     controller_ =
740         [[BrowserWindowControllerFakeFullscreen alloc] initWithBrowser:browser()
741                                                          takeOwnership:NO];
742   }
744   void TearDown() override {
745     [controller_ close];
746     CocoaProfileTest::TearDown();
747   }
749  public:
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());
765   observer.Wait();
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
785 // problems.
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
824                                       defer:NO]);
825   [[testFullscreenWindow_ contentView] setWantsLayer:YES];
826   return testFullscreenWindow_.get();
828 @end
830 /* TODO(???): test other methods of BrowserWindowController */