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 #import "base/mac/mac_util.h"
8 #include "base/mac/sdk_forward_declarations.h"
9 #include "base/run_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/app/chrome_command_ids.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/devtools/devtools_window_testing.h"
14 #include "chrome/browser/infobars/infobar_service.h"
15 #include "chrome/browser/infobars/simple_alert_infobar_delegate.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/profiles/profile_manager.h"
18 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_commands.h"
21 #include "chrome/browser/ui/browser_list.h"
22 #include "chrome/browser/ui/browser_window.h"
23 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
24 #import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
25 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
26 #import "chrome/browser/ui/cocoa/history_overlay_controller.h"
27 #import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
28 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
29 #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h"
30 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
31 #import "chrome/browser/ui/cocoa/profiles/avatar_base_controller.h"
32 #import "chrome/browser/ui/cocoa/tab_contents/overlayable_contents_controller.h"
33 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
34 #include "chrome/browser/ui/extensions/application_launch.h"
35 #include "chrome/browser/ui/find_bar/find_bar.h"
36 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
37 #include "chrome/browser/ui/tabs/tab_strip_model.h"
38 #include "chrome/test/base/in_process_browser_test.h"
39 #include "chrome/test/base/testing_profile.h"
40 #include "content/public/browser/web_contents.h"
41 #include "content/public/test/test_utils.h"
42 #import "testing/gtest_mac.h"
43 #import "third_party/ocmock/OCMock/OCMock.h"
44 #import "ui/base/cocoa/nsview_additions.h"
45 #include "ui/gfx/animation/slide_animation.h"
49 // Creates a mock of an NSWindow that has the given |frame|.
50 id MockWindowWithFrame(NSRect frame) {
51 id window = [OCMockObject mockForClass:[NSWindow class]];
52 NSValue* window_frame =
53 [NSValue valueWithBytes:&frame objCType:@encode(NSRect)];
54 [[[window stub] andReturnValue:window_frame] frame];
58 void CreateProfileCallback(const base::Closure& quit_closure,
60 Profile::CreateStatus status) {
62 EXPECT_NE(Profile::CREATE_STATUS_LOCAL_FAIL, status);
63 EXPECT_NE(Profile::CREATE_STATUS_REMOTE_FAIL, status);
64 // This will be called multiple times. Wait until the profile is initialized
65 // fully to quit the loop.
66 if (status == Profile::CREATE_STATUS_INITIALIZED)
75 VIEW_ID_DOWNLOAD_SHELF,
76 VIEW_ID_TAB_CONTENT_AREA,
77 VIEW_ID_FULLSCREEN_FLOATING_BAR,
81 // Checks that no views draw on top of the supposedly exposed view.
82 class ViewExposedChecker {
84 ViewExposedChecker() : below_exposed_view_(YES) {}
85 ~ViewExposedChecker() {}
87 void SetExceptions(NSArray* exceptions) {
88 exceptions_.reset([exceptions retain]);
91 // Checks that no views draw in front of |view|, with the exception of
93 void CheckViewExposed(NSView* view) {
94 below_exposed_view_ = YES;
95 exposed_view_.reset([view retain]);
96 CheckViewsDoNotObscure([[[view window] contentView] superview]);
100 // Checks that |view| does not draw on top of |exposed_view_|.
101 void CheckViewDoesNotObscure(NSView* view) {
102 NSRect viewWindowFrame = [view convertRect:[view bounds] toView:nil];
103 NSRect viewBeingVerifiedWindowFrame =
104 [exposed_view_ convertRect:[exposed_view_ bounds] toView:nil];
106 // The views do not intersect.
107 if (!NSIntersectsRect(viewBeingVerifiedWindowFrame, viewWindowFrame))
110 // No view can be above the view being checked.
111 EXPECT_TRUE(below_exposed_view_);
113 // If |view| is a parent of |exposed_view_|, then there's nothing else
115 NSView* parent = exposed_view_;
116 while (parent != nil) {
117 parent = [parent superview];
122 if ([exposed_view_ layer])
125 // If the view being verified doesn't have a layer, then no views that
126 // intersect it can have a layer.
127 if ([exceptions_ containsObject:view]) {
128 EXPECT_FALSE([view isOpaque]);
132 EXPECT_TRUE(![view layer]) << [[view description] UTF8String] << " " <<
133 [NSStringFromRect(viewWindowFrame) UTF8String];
136 // Recursively checks that |view| and its subviews do not draw on top of
137 // |exposed_view_|. The recursion passes through all views in order of
138 // back-most in Z-order to front-most in Z-order.
139 void CheckViewsDoNotObscure(NSView* view) {
140 // If this is the view being checked, don't recurse into its subviews. All
141 // future views encountered in the recursion are in front of the view being
143 if (view == exposed_view_) {
144 below_exposed_view_ = NO;
148 CheckViewDoesNotObscure(view);
150 // Perform the recursion.
151 for (NSView* subview in [view subviews])
152 CheckViewsDoNotObscure(subview);
155 // The method CheckViewExposed() recurses through the views in the view
156 // hierarchy and checks that none of the views obscure |exposed_view_|.
157 base::scoped_nsobject<NSView> exposed_view_;
159 // While this flag is true, the views being recursed through are below
160 // |exposed_view_| in Z-order. After the recursion passes |exposed_view_|,
161 // this flag is set to false.
162 BOOL below_exposed_view_;
164 // Exceptions are allowed to overlap |exposed_view_|. Exceptions must still
165 // be Z-order behind |exposed_view_|.
166 base::scoped_nsobject<NSArray> exceptions_;
168 DISALLOW_COPY_AND_ASSIGN(ViewExposedChecker);
173 @interface InfoBarContainerController(TestingAPI)
174 - (BOOL)isTopInfoBarAnimationRunning;
177 @implementation InfoBarContainerController(TestingAPI)
178 - (BOOL)isTopInfoBarAnimationRunning {
179 InfoBarController* infoBarController = [infobarControllers_ objectAtIndex:0];
180 if (infoBarController) {
181 const gfx::SlideAnimation& infobarAnimation =
182 static_cast<const InfoBarCocoa*>(
183 infoBarController.infobar)->animation();
184 return infobarAnimation.is_animating();
190 class BrowserWindowControllerTest : public InProcessBrowserTest {
192 BrowserWindowControllerTest() : InProcessBrowserTest() {
195 void SetUpOnMainThread() override {
196 [[controller() bookmarkBarController] setStateAnimationsEnabled:NO];
197 [[controller() bookmarkBarController] setInnerContentAnimationsEnabled:NO];
200 BrowserWindowController* controller() const {
201 return [BrowserWindowController browserWindowControllerForWindow:
202 browser()->window()->GetNativeWindow()];
205 static void ShowInfoBar(Browser* browser) {
206 SimpleAlertInfoBarDelegate::Create(
207 InfoBarService::FromWebContents(
208 browser->tab_strip_model()->GetActiveWebContents()),
209 0, base::string16(), false);
212 NSView* GetViewWithID(ViewID view_id) const {
214 case VIEW_ID_FULLSCREEN_FLOATING_BAR:
215 return [controller() floatingBarBackingView];
216 case VIEW_ID_TOOLBAR:
217 return [[controller() toolbarController] view];
218 case VIEW_ID_BOOKMARK_BAR:
219 return [[controller() bookmarkBarController] view];
220 case VIEW_ID_INFO_BAR:
221 return [[controller() infoBarContainerController] view];
222 case VIEW_ID_FIND_BAR:
223 return [[controller() findBarCocoaController] view];
224 case VIEW_ID_DOWNLOAD_SHELF:
225 return [[controller() downloadShelf] view];
226 case VIEW_ID_TAB_CONTENT_AREA:
227 return [controller() tabContentArea];
234 void VerifyZOrder(const std::vector<ViewID>& view_list) const {
235 std::vector<NSView*> visible_views;
236 for (size_t i = 0; i < view_list.size(); ++i) {
237 NSView* view = GetViewWithID(view_list[i]);
238 if ([view superview])
239 visible_views.push_back(view);
242 for (size_t i = 0; i < visible_views.size() - 1; ++i) {
243 NSView* bottom_view = visible_views[i];
244 NSView* top_view = visible_views[i + 1];
246 EXPECT_NSEQ([bottom_view superview], [top_view superview]);
247 EXPECT_TRUE([bottom_view cr_isBelowView:top_view]);
250 // Views not in |view_list| must either be nil or not parented.
251 for (size_t i = 0; i < VIEW_ID_COUNT; ++i) {
252 if (std::find(view_list.begin(), view_list.end(), i) == view_list.end()) {
253 NSView* view = GetViewWithID(static_cast<ViewID>(i));
254 EXPECT_TRUE(!view || ![view superview]);
259 CGFloat GetViewHeight(ViewID viewID) const {
260 CGFloat height = NSHeight([GetViewWithID(viewID) frame]);
261 if (viewID == VIEW_ID_INFO_BAR) {
262 height -= [[controller() infoBarContainerController]
263 overlappingTipHeight];
268 static void CheckTopInfoBarAnimation(
269 InfoBarContainerController* info_bar_container_controller,
270 const base::Closure& quit_task) {
271 if (![info_bar_container_controller isTopInfoBarAnimationRunning])
275 static void CheckBookmarkBarAnimation(
276 BookmarkBarController* bookmark_bar_controller,
277 const base::Closure& quit_task) {
278 if (![bookmark_bar_controller isAnimationRunning])
282 void WaitForTopInfoBarAnimationToFinish() {
283 scoped_refptr<content::MessageLoopRunner> runner =
284 new content::MessageLoopRunner;
286 base::Timer timer(false, true);
289 base::TimeDelta::FromMilliseconds(15),
290 base::Bind(&CheckTopInfoBarAnimation,
291 [controller() infoBarContainerController],
292 runner->QuitClosure()));
296 void WaitForBookmarkBarAnimationToFinish() {
297 scoped_refptr<content::MessageLoopRunner> runner =
298 new content::MessageLoopRunner;
300 base::Timer timer(false, true);
303 base::TimeDelta::FromMilliseconds(15),
304 base::Bind(&CheckBookmarkBarAnimation,
305 [controller() bookmarkBarController],
306 runner->QuitClosure()));
310 NSInteger GetExpectedTopInfoBarTipHeight() {
311 InfoBarContainerController* info_bar_container_controller =
312 [controller() infoBarContainerController];
313 CGFloat overlapping_tip_height =
314 [info_bar_container_controller overlappingTipHeight];
315 LocationBarViewMac* location_bar_view = [controller() locationBarBridge];
316 NSPoint icon_bottom = location_bar_view->GetPageInfoBubblePoint();
318 NSPoint info_bar_top = NSMakePoint(0,
319 NSHeight([info_bar_container_controller view].frame) -
320 overlapping_tip_height);
321 info_bar_top = [[info_bar_container_controller view]
322 convertPoint:info_bar_top toView:nil];
323 return icon_bottom.y - info_bar_top.y;
326 // Nothing should draw on top of the window controls.
327 void VerifyWindowControlsZOrder() {
328 NSWindow* window = [controller() window];
329 ViewExposedChecker checker;
331 // The exceptions are the contentView, chromeContentView and tabStripView,
332 // which are layer backed but transparent.
333 NSArray* exceptions = @[
334 [window contentView],
335 controller().chromeContentView,
336 controller().tabStripView
338 checker.SetExceptions(exceptions);
340 checker.CheckViewExposed([window standardWindowButton:NSWindowCloseButton]);
341 checker.CheckViewExposed(
342 [window standardWindowButton:NSWindowMiniaturizeButton]);
343 checker.CheckViewExposed([window standardWindowButton:NSWindowZoomButton]);
345 // There is no fullscreen button on OSX 10.6 or OSX 10.10+.
346 NSView* view = [window standardWindowButton:NSWindowFullScreenButton];
348 checker.CheckViewExposed(view);
352 DISALLOW_COPY_AND_ASSIGN(BrowserWindowControllerTest);
355 // Tests that adding the first profile moves the Lion fullscreen button over
357 // DISABLED_ because it regularly times out: http://crbug.com/159002.
358 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
359 DISABLED_ProfileAvatarFullscreenButton) {
360 if (base::mac::IsOSSnowLeopard())
363 // Initialize the locals.
364 ProfileManager* profile_manager = g_browser_process->profile_manager();
365 ASSERT_TRUE(profile_manager);
367 NSWindow* window = browser()->window()->GetNativeWindow();
370 // With only one profile, the fullscreen button should be visible, but the
371 // avatar button should not.
372 EXPECT_EQ(1u, profile_manager->GetNumberOfProfiles());
374 NSButton* fullscreen_button =
375 [window standardWindowButton:NSWindowFullScreenButton];
376 EXPECT_TRUE(fullscreen_button);
377 EXPECT_FALSE([fullscreen_button isHidden]);
379 AvatarBaseController* avatar_controller =
380 [controller() avatarButtonController];
381 NSView* avatar = [avatar_controller view];
383 EXPECT_TRUE([avatar isHidden]);
385 // Create a profile asynchronously and run the loop until its creation
387 base::RunLoop run_loop;
389 ProfileManager::CreateCallback create_callback =
390 base::Bind(&CreateProfileCallback, run_loop.QuitClosure());
391 profile_manager->CreateProfileAsync(
392 profile_manager->user_data_dir().Append("test"),
394 base::ASCIIToUTF16("avatar_test"),
400 // There should now be two profiles, and the avatar button and fullscreen
401 // button are both visible.
402 EXPECT_EQ(2u, profile_manager->GetNumberOfProfiles());
403 EXPECT_FALSE([avatar isHidden]);
404 EXPECT_FALSE([fullscreen_button isHidden]);
405 EXPECT_EQ([avatar window], [fullscreen_button window]);
407 // Make sure the visual order of the buttons is correct and that they don't
409 NSRect avatar_frame = [avatar frame];
410 NSRect fullscreen_frame = [fullscreen_button frame];
412 EXPECT_LT(NSMinX(fullscreen_frame), NSMinX(avatar_frame));
413 EXPECT_LT(NSMaxX(fullscreen_frame), NSMinX(avatar_frame));
416 // Verify that in non-Instant normal mode that the find bar and download shelf
417 // are above the content area. Everything else should be below it.
418 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, ZOrderNormal) {
419 browser()->GetFindBarController(); // add find bar
421 std::vector<ViewID> view_list;
422 view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
423 view_list.push_back(VIEW_ID_BOOKMARK_BAR);
424 view_list.push_back(VIEW_ID_TOOLBAR);
425 view_list.push_back(VIEW_ID_INFO_BAR);
426 view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
427 view_list.push_back(VIEW_ID_FIND_BAR);
428 VerifyZOrder(view_list);
430 [controller() showOverlay];
431 [controller() removeOverlay];
432 VerifyZOrder(view_list);
434 [controller() enterImmersiveFullscreen];
435 [controller() exitImmersiveFullscreen];
436 VerifyZOrder(view_list);
439 // Verify that in non-Instant presentation mode that the info bar is below the
440 // content are and everything else is above it.
441 // DISABLED due to flaky failures on trybots. http://crbug.com/178778
442 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
443 DISABLED_ZOrderPresentationMode) {
444 chrome::ToggleFullscreenMode(browser());
445 browser()->GetFindBarController(); // add find bar
447 std::vector<ViewID> view_list;
448 view_list.push_back(VIEW_ID_INFO_BAR);
449 view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
450 view_list.push_back(VIEW_ID_FULLSCREEN_FLOATING_BAR);
451 view_list.push_back(VIEW_ID_BOOKMARK_BAR);
452 view_list.push_back(VIEW_ID_TOOLBAR);
453 view_list.push_back(VIEW_ID_FIND_BAR);
454 view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
455 VerifyZOrder(view_list);
458 // Verify that if the fullscreen floating bar view is below the tab content area
459 // then calling |updateSubviewZOrder:| will correctly move back above.
460 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
461 DISABLED_FloatingBarBelowContentView) {
462 // TODO(kbr): re-enable: http://crbug.com/222296
463 if (base::mac::IsOSMountainLionOrLater())
466 chrome::ToggleFullscreenMode(browser());
468 NSView* fullscreen_floating_bar =
469 GetViewWithID(VIEW_ID_FULLSCREEN_FLOATING_BAR);
470 [fullscreen_floating_bar removeFromSuperview];
471 [[[controller() window] contentView] addSubview:fullscreen_floating_bar
472 positioned:NSWindowBelow
474 [controller() updateSubviewZOrder];
476 std::vector<ViewID> view_list;
477 view_list.push_back(VIEW_ID_INFO_BAR);
478 view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
479 view_list.push_back(VIEW_ID_FULLSCREEN_FLOATING_BAR);
480 view_list.push_back(VIEW_ID_BOOKMARK_BAR);
481 view_list.push_back(VIEW_ID_TOOLBAR);
482 view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
483 VerifyZOrder(view_list);
486 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, SheetPosition) {
487 ASSERT_TRUE([controller() isKindOfClass:[BrowserWindowController class]]);
488 EXPECT_TRUE([controller() isTabbedWindow]);
489 EXPECT_TRUE([controller() hasTabStrip]);
490 EXPECT_FALSE([controller() hasTitleBar]);
491 EXPECT_TRUE([controller() hasToolbar]);
492 EXPECT_FALSE([controller() isBookmarkBarVisible]);
494 NSRect defaultAlertFrame = NSMakeRect(0, 0, 300, 200);
495 id sheet = MockWindowWithFrame(defaultAlertFrame);
496 NSWindow* window = browser()->window()->GetNativeWindow();
497 NSRect alertFrame = [controller() window:window
498 willPositionSheet:nil
499 usingRect:defaultAlertFrame];
500 NSRect toolbarFrame = [[[controller() toolbarController] view] frame];
501 EXPECT_EQ(NSMinY(alertFrame), NSMinY(toolbarFrame));
503 // Open sheet with normal browser window, persistent bookmark bar.
504 chrome::ToggleBookmarkBarWhenVisible(browser()->profile());
505 EXPECT_TRUE([controller() isBookmarkBarVisible]);
506 alertFrame = [controller() window:window
507 willPositionSheet:sheet
508 usingRect:defaultAlertFrame];
509 NSRect bookmarkBarFrame = [[[controller() bookmarkBarController] view] frame];
510 EXPECT_EQ(NSMinY(alertFrame), NSMinY(bookmarkBarFrame));
512 // If the sheet is too large, it should be positioned at the top of the
514 defaultAlertFrame = NSMakeRect(0, 0, 300, 2000);
515 sheet = MockWindowWithFrame(defaultAlertFrame);
516 alertFrame = [controller() window:window
517 willPositionSheet:sheet
518 usingRect:defaultAlertFrame];
519 EXPECT_EQ(NSMinY(alertFrame), NSHeight([window frame]));
521 // Reset the sheet's size.
522 defaultAlertFrame = NSMakeRect(0, 0, 300, 200);
523 sheet = MockWindowWithFrame(defaultAlertFrame);
525 // Make sure the profile does not have the bookmark visible so that
526 // we'll create the shortcut window without the bookmark bar.
527 chrome::ToggleBookmarkBarWhenVisible(browser()->profile());
528 // Open application mode window.
529 OpenAppShortcutWindow(browser()->profile(), GURL("about:blank"));
530 Browser* popup_browser = BrowserList::GetInstance(
531 chrome::GetActiveDesktop())->GetLastActive();
532 NSWindow* popupWindow = popup_browser->window()->GetNativeWindow();
533 BrowserWindowController* popupController =
534 [BrowserWindowController browserWindowControllerForWindow:popupWindow];
535 ASSERT_TRUE([popupController isKindOfClass:[BrowserWindowController class]]);
536 EXPECT_FALSE([popupController isTabbedWindow]);
537 EXPECT_FALSE([popupController hasTabStrip]);
538 EXPECT_TRUE([popupController hasTitleBar]);
539 EXPECT_FALSE([popupController isBookmarkBarVisible]);
540 EXPECT_FALSE([popupController hasToolbar]);
542 // Open sheet in an application window.
543 [popupController showWindow:nil];
544 alertFrame = [popupController window:popupWindow
545 willPositionSheet:sheet
546 usingRect:defaultAlertFrame];
547 EXPECT_EQ(NSMinY(alertFrame),
548 NSHeight([[popupWindow contentView] frame]) -
549 defaultAlertFrame.size.height);
551 // Close the application window.
552 popup_browser->tab_strip_model()->CloseSelectedTabs();
553 [popupController close];
556 // Verify that the info bar tip is hidden when the toolbar is not visible.
557 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
558 InfoBarTipHiddenForWindowWithoutToolbar) {
559 ShowInfoBar(browser());
561 [[controller() infoBarContainerController] shouldSuppressTopInfoBarTip]);
563 OpenAppShortcutWindow(browser()->profile(), GURL("about:blank"));
564 Browser* popup_browser = BrowserList::GetInstance(
565 chrome::HOST_DESKTOP_TYPE_NATIVE)->GetLastActive();
566 NSWindow* popupWindow = popup_browser->window()->GetNativeWindow();
567 BrowserWindowController* popupController =
568 [BrowserWindowController browserWindowControllerForWindow:popupWindow];
569 EXPECT_FALSE([popupController hasToolbar]);
571 // Show infobar for controller.
572 ShowInfoBar(popup_browser);
574 [[popupController infoBarContainerController]
575 shouldSuppressTopInfoBarTip]);
578 // Tests that status bubble's base frame does move when devTools are docked.
579 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
580 StatusBubblePositioning) {
581 NSPoint origin = [controller() statusBubbleBaseFrame].origin;
583 DevToolsWindow* devtools_window =
584 DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true);
585 DevToolsWindowTesting::Get(devtools_window)->SetInspectedPageBounds(
586 gfx::Rect(10, 10, 100, 100));
588 NSPoint originWithDevTools = [controller() statusBubbleBaseFrame].origin;
589 EXPECT_FALSE(NSEqualPoints(origin, originWithDevTools));
591 DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
594 // Tests that top infobar tip is streched when bookmark bar becomes SHOWN/HIDDEN
595 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
596 InfoBarTipStretchedWhenBookmarkBarStatusChanged) {
597 EXPECT_FALSE([controller() isBookmarkBarVisible]);
598 ShowInfoBar(browser());
599 // The infobar tip is animated during the infobar is being added, wait until
601 WaitForTopInfoBarAnimationToFinish();
603 EXPECT_FALSE([[controller() infoBarContainerController]
604 shouldSuppressTopInfoBarTip]);
606 NSInteger max_tip_height = infobars::InfoBar::kMaximumArrowTargetHeight +
607 infobars::InfoBar::kSeparatorLineHeight;
609 chrome::ExecuteCommand(browser(), IDC_SHOW_BOOKMARK_BAR);
610 WaitForBookmarkBarAnimationToFinish();
611 EXPECT_TRUE([controller() isBookmarkBarVisible]);
612 EXPECT_EQ(std::min(GetExpectedTopInfoBarTipHeight(), max_tip_height),
613 [[controller() infoBarContainerController] overlappingTipHeight]);
615 chrome::ExecuteCommand(browser(), IDC_SHOW_BOOKMARK_BAR);
616 WaitForBookmarkBarAnimationToFinish();
617 EXPECT_FALSE([controller() isBookmarkBarVisible]);
618 EXPECT_EQ(std::min(GetExpectedTopInfoBarTipHeight(), max_tip_height),
619 [[controller() infoBarContainerController] overlappingTipHeight]);
622 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, TrafficLightZOrder) {
623 // Verify z order immediately after creation.
624 VerifyWindowControlsZOrder();
626 // Verify z order in and out of overlay.
627 [controller() showOverlay];
628 VerifyWindowControlsZOrder();
629 [controller() removeOverlay];
630 VerifyWindowControlsZOrder();
632 // Toggle immersive fullscreen, then verify z order. In immersive fullscreen,
633 // there are no window controls.
634 [controller() enterImmersiveFullscreen];
635 [controller() exitImmersiveFullscreen];
636 VerifyWindowControlsZOrder();