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/profiles/profile.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_commands.h"
20 #include "chrome/browser/ui/browser_list.h"
21 #include "chrome/browser/ui/browser_window.h"
22 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
23 #import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
24 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
25 #import "chrome/browser/ui/cocoa/history_overlay_controller.h"
26 #import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
27 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
28 #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h"
29 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
30 #import "chrome/browser/ui/cocoa/profiles/avatar_base_controller.h"
31 #import "chrome/browser/ui/cocoa/tab_contents/overlayable_contents_controller.h"
32 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
33 #include "chrome/browser/ui/extensions/application_launch.h"
34 #include "chrome/browser/ui/find_bar/find_bar.h"
35 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
36 #include "chrome/browser/ui/infobar_container_delegate.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 "components/infobars/core/simple_alert_infobar_delegate.h"
41 #include "content/public/browser/web_contents.h"
42 #include "content/public/test/test_utils.h"
43 #import "testing/gtest_mac.h"
44 #import "third_party/ocmock/OCMock/OCMock.h"
45 #import "ui/base/cocoa/nsview_additions.h"
46 #include "ui/gfx/animation/slide_animation.h"
50 // Creates a mock of an NSWindow that has the given |frame|.
51 id MockWindowWithFrame(NSRect frame) {
52 id window = [OCMockObject mockForClass:[NSWindow class]];
53 NSValue* window_frame =
54 [NSValue valueWithBytes:&frame objCType:@encode(NSRect)];
55 [[[window stub] andReturnValue:window_frame] frame];
59 void CreateProfileCallback(const base::Closure& quit_closure,
61 Profile::CreateStatus status) {
63 EXPECT_NE(Profile::CREATE_STATUS_LOCAL_FAIL, status);
64 EXPECT_NE(Profile::CREATE_STATUS_REMOTE_FAIL, status);
65 // This will be called multiple times. Wait until the profile is initialized
66 // fully to quit the loop.
67 if (status == Profile::CREATE_STATUS_INITIALIZED)
76 VIEW_ID_DOWNLOAD_SHELF,
77 VIEW_ID_TAB_CONTENT_AREA,
78 VIEW_ID_FULLSCREEN_FLOATING_BAR,
82 // Checks that no views draw on top of the supposedly exposed view.
83 class ViewExposedChecker {
85 ViewExposedChecker() : below_exposed_view_(YES) {}
86 ~ViewExposedChecker() {}
88 void SetExceptions(NSArray* exceptions) {
89 exceptions_.reset([exceptions retain]);
92 // Checks that no views draw in front of |view|, with the exception of
94 void CheckViewExposed(NSView* view) {
95 below_exposed_view_ = YES;
96 exposed_view_.reset([view retain]);
97 CheckViewsDoNotObscure([[[view window] contentView] superview]);
101 // Checks that |view| does not draw on top of |exposed_view_|.
102 void CheckViewDoesNotObscure(NSView* view) {
103 NSRect viewWindowFrame = [view convertRect:[view bounds] toView:nil];
104 NSRect viewBeingVerifiedWindowFrame =
105 [exposed_view_ convertRect:[exposed_view_ bounds] toView:nil];
107 // The views do not intersect.
108 if (!NSIntersectsRect(viewBeingVerifiedWindowFrame, viewWindowFrame))
111 // No view can be above the view being checked.
112 EXPECT_TRUE(below_exposed_view_);
114 // If |view| is a parent of |exposed_view_|, then there's nothing else
116 NSView* parent = exposed_view_;
117 while (parent != nil) {
118 parent = [parent superview];
123 if ([exposed_view_ layer])
126 // If the view being verified doesn't have a layer, then no views that
127 // intersect it can have a layer.
128 if ([exceptions_ containsObject:view]) {
129 EXPECT_FALSE([view isOpaque]);
133 EXPECT_TRUE(![view layer]) << [[view description] UTF8String] << " " <<
134 [NSStringFromRect(viewWindowFrame) UTF8String];
137 // Recursively checks that |view| and its subviews do not draw on top of
138 // |exposed_view_|. The recursion passes through all views in order of
139 // back-most in Z-order to front-most in Z-order.
140 void CheckViewsDoNotObscure(NSView* view) {
141 // If this is the view being checked, don't recurse into its subviews. All
142 // future views encountered in the recursion are in front of the view being
144 if (view == exposed_view_) {
145 below_exposed_view_ = NO;
149 CheckViewDoesNotObscure(view);
151 // Perform the recursion.
152 for (NSView* subview in [view subviews])
153 CheckViewsDoNotObscure(subview);
156 // The method CheckViewExposed() recurses through the views in the view
157 // hierarchy and checks that none of the views obscure |exposed_view_|.
158 base::scoped_nsobject<NSView> exposed_view_;
160 // While this flag is true, the views being recursed through are below
161 // |exposed_view_| in Z-order. After the recursion passes |exposed_view_|,
162 // this flag is set to false.
163 BOOL below_exposed_view_;
165 // Exceptions are allowed to overlap |exposed_view_|. Exceptions must still
166 // be Z-order behind |exposed_view_|.
167 base::scoped_nsobject<NSArray> exceptions_;
169 DISALLOW_COPY_AND_ASSIGN(ViewExposedChecker);
174 @interface InfoBarContainerController(TestingAPI)
175 - (BOOL)isTopInfoBarAnimationRunning;
178 @implementation InfoBarContainerController(TestingAPI)
179 - (BOOL)isTopInfoBarAnimationRunning {
180 InfoBarController* infoBarController = [infobarControllers_ objectAtIndex:0];
181 if (infoBarController) {
182 const gfx::SlideAnimation& infobarAnimation =
183 static_cast<const InfoBarCocoa*>(
184 infoBarController.infobar)->animation();
185 return infobarAnimation.is_animating();
191 class BrowserWindowControllerTest : public InProcessBrowserTest {
193 BrowserWindowControllerTest() : InProcessBrowserTest() {
196 void SetUpOnMainThread() override {
197 [[controller() bookmarkBarController] setStateAnimationsEnabled:NO];
198 [[controller() bookmarkBarController] setInnerContentAnimationsEnabled:NO];
201 BrowserWindowController* controller() const {
202 return [BrowserWindowController browserWindowControllerForWindow:
203 browser()->window()->GetNativeWindow()];
206 static void ShowInfoBar(Browser* browser) {
207 SimpleAlertInfoBarDelegate::Create(
208 InfoBarService::FromWebContents(
209 browser->tab_strip_model()->GetActiveWebContents()),
210 0, base::string16(), false);
213 NSView* GetViewWithID(ViewID view_id) const {
215 case VIEW_ID_FULLSCREEN_FLOATING_BAR:
216 return [controller() floatingBarBackingView];
217 case VIEW_ID_TOOLBAR:
218 return [[controller() toolbarController] view];
219 case VIEW_ID_BOOKMARK_BAR:
220 return [[controller() bookmarkBarController] view];
221 case VIEW_ID_INFO_BAR:
222 return [[controller() infoBarContainerController] view];
223 case VIEW_ID_FIND_BAR:
224 return [[controller() findBarCocoaController] view];
225 case VIEW_ID_DOWNLOAD_SHELF:
226 return [[controller() downloadShelf] view];
227 case VIEW_ID_TAB_CONTENT_AREA:
228 return [controller() tabContentArea];
235 void VerifyZOrder(const std::vector<ViewID>& view_list) const {
236 std::vector<NSView*> visible_views;
237 for (size_t i = 0; i < view_list.size(); ++i) {
238 NSView* view = GetViewWithID(view_list[i]);
239 if ([view superview])
240 visible_views.push_back(view);
243 for (size_t i = 0; i < visible_views.size() - 1; ++i) {
244 NSView* bottom_view = visible_views[i];
245 NSView* top_view = visible_views[i + 1];
247 EXPECT_NSEQ([bottom_view superview], [top_view superview]);
248 EXPECT_TRUE([bottom_view cr_isBelowView:top_view]);
251 // Views not in |view_list| must either be nil or not parented.
252 for (size_t i = 0; i < VIEW_ID_COUNT; ++i) {
253 if (std::find(view_list.begin(), view_list.end(), i) == view_list.end()) {
254 NSView* view = GetViewWithID(static_cast<ViewID>(i));
255 EXPECT_TRUE(!view || ![view superview]);
260 CGFloat GetViewHeight(ViewID viewID) const {
261 CGFloat height = NSHeight([GetViewWithID(viewID) frame]);
262 if (viewID == VIEW_ID_INFO_BAR) {
263 height -= [[controller() infoBarContainerController]
264 overlappingTipHeight];
269 static void CheckTopInfoBarAnimation(
270 InfoBarContainerController* info_bar_container_controller,
271 const base::Closure& quit_task) {
272 if (![info_bar_container_controller isTopInfoBarAnimationRunning])
276 static void CheckBookmarkBarAnimation(
277 BookmarkBarController* bookmark_bar_controller,
278 const base::Closure& quit_task) {
279 if (![bookmark_bar_controller isAnimationRunning])
283 void WaitForTopInfoBarAnimationToFinish() {
284 scoped_refptr<content::MessageLoopRunner> runner =
285 new content::MessageLoopRunner;
287 base::Timer timer(false, true);
290 base::TimeDelta::FromMilliseconds(15),
291 base::Bind(&CheckTopInfoBarAnimation,
292 [controller() infoBarContainerController],
293 runner->QuitClosure()));
297 void WaitForBookmarkBarAnimationToFinish() {
298 scoped_refptr<content::MessageLoopRunner> runner =
299 new content::MessageLoopRunner;
301 base::Timer timer(false, true);
304 base::TimeDelta::FromMilliseconds(15),
305 base::Bind(&CheckBookmarkBarAnimation,
306 [controller() bookmarkBarController],
307 runner->QuitClosure()));
311 NSInteger GetExpectedTopInfoBarTipHeight() {
312 InfoBarContainerController* info_bar_container_controller =
313 [controller() infoBarContainerController];
314 CGFloat overlapping_tip_height =
315 [info_bar_container_controller overlappingTipHeight];
316 LocationBarViewMac* location_bar_view = [controller() locationBarBridge];
317 NSPoint icon_bottom = location_bar_view->GetPageInfoBubblePoint();
319 NSPoint info_bar_top = NSMakePoint(0,
320 NSHeight([info_bar_container_controller view].frame) -
321 overlapping_tip_height);
322 info_bar_top = [[info_bar_container_controller view]
323 convertPoint:info_bar_top toView:nil];
324 return icon_bottom.y - info_bar_top.y;
327 // Nothing should draw on top of the window controls.
328 void VerifyWindowControlsZOrder() {
329 NSWindow* window = [controller() window];
330 ViewExposedChecker checker;
332 // The exceptions are the contentView, chromeContentView and tabStripView,
333 // which are layer backed but transparent.
334 NSArray* exceptions = @[
335 [window contentView],
336 controller().chromeContentView,
337 controller().tabStripView
339 checker.SetExceptions(exceptions);
341 checker.CheckViewExposed([window standardWindowButton:NSWindowCloseButton]);
342 checker.CheckViewExposed(
343 [window standardWindowButton:NSWindowMiniaturizeButton]);
344 checker.CheckViewExposed([window standardWindowButton:NSWindowZoomButton]);
346 // There is no fullscreen button on OSX 10.6 or OSX 10.10+.
347 NSView* view = [window standardWindowButton:NSWindowFullScreenButton];
349 checker.CheckViewExposed(view);
353 DISALLOW_COPY_AND_ASSIGN(BrowserWindowControllerTest);
356 // Tests that adding the first profile moves the Lion fullscreen button over
358 // DISABLED_ because it regularly times out: http://crbug.com/159002.
359 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
360 DISABLED_ProfileAvatarFullscreenButton) {
361 if (base::mac::IsOSSnowLeopard())
364 // Initialize the locals.
365 ProfileManager* profile_manager = g_browser_process->profile_manager();
366 ASSERT_TRUE(profile_manager);
368 NSWindow* window = browser()->window()->GetNativeWindow();
371 // With only one profile, the fullscreen button should be visible, but the
372 // avatar button should not.
373 EXPECT_EQ(1u, profile_manager->GetNumberOfProfiles());
375 NSButton* fullscreen_button =
376 [window standardWindowButton:NSWindowFullScreenButton];
377 EXPECT_TRUE(fullscreen_button);
378 EXPECT_FALSE([fullscreen_button isHidden]);
380 AvatarBaseController* avatar_controller =
381 [controller() avatarButtonController];
382 NSView* avatar = [avatar_controller view];
384 EXPECT_TRUE([avatar isHidden]);
386 // Create a profile asynchronously and run the loop until its creation
388 base::RunLoop run_loop;
390 ProfileManager::CreateCallback create_callback =
391 base::Bind(&CreateProfileCallback, run_loop.QuitClosure());
392 profile_manager->CreateProfileAsync(
393 profile_manager->user_data_dir().Append("test"),
395 base::ASCIIToUTF16("avatar_test"),
401 // There should now be two profiles, and the avatar button and fullscreen
402 // button are both visible.
403 EXPECT_EQ(2u, profile_manager->GetNumberOfProfiles());
404 EXPECT_FALSE([avatar isHidden]);
405 EXPECT_FALSE([fullscreen_button isHidden]);
406 EXPECT_EQ([avatar window], [fullscreen_button window]);
408 // Make sure the visual order of the buttons is correct and that they don't
410 NSRect avatar_frame = [avatar frame];
411 NSRect fullscreen_frame = [fullscreen_button frame];
413 EXPECT_LT(NSMinX(fullscreen_frame), NSMinX(avatar_frame));
414 EXPECT_LT(NSMaxX(fullscreen_frame), NSMinX(avatar_frame));
417 // Verify that in non-Instant normal mode that the find bar and download shelf
418 // are above the content area. Everything else should be below it.
419 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, ZOrderNormal) {
420 browser()->GetFindBarController(); // add find bar
422 std::vector<ViewID> view_list;
423 view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
424 view_list.push_back(VIEW_ID_BOOKMARK_BAR);
425 view_list.push_back(VIEW_ID_TOOLBAR);
426 view_list.push_back(VIEW_ID_INFO_BAR);
427 view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
428 view_list.push_back(VIEW_ID_FIND_BAR);
429 VerifyZOrder(view_list);
431 [controller() showOverlay];
432 [controller() removeOverlay];
433 VerifyZOrder(view_list);
435 [controller() enterImmersiveFullscreen];
436 [controller() exitImmersiveFullscreen];
437 VerifyZOrder(view_list);
440 // Verify that in non-Instant presentation mode that the info bar is below the
441 // content are and everything else is above it.
442 // DISABLED due to flaky failures on trybots. http://crbug.com/178778
443 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
444 DISABLED_ZOrderPresentationMode) {
445 chrome::ToggleFullscreenMode(browser());
446 browser()->GetFindBarController(); // add find bar
448 std::vector<ViewID> view_list;
449 view_list.push_back(VIEW_ID_INFO_BAR);
450 view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
451 view_list.push_back(VIEW_ID_FULLSCREEN_FLOATING_BAR);
452 view_list.push_back(VIEW_ID_BOOKMARK_BAR);
453 view_list.push_back(VIEW_ID_TOOLBAR);
454 view_list.push_back(VIEW_ID_FIND_BAR);
455 view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
456 VerifyZOrder(view_list);
459 // Verify that if the fullscreen floating bar view is below the tab content area
460 // then calling |updateSubviewZOrder:| will correctly move back above.
461 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
462 DISABLED_FloatingBarBelowContentView) {
463 // TODO(kbr): re-enable: http://crbug.com/222296
464 if (base::mac::IsOSMountainLionOrLater())
467 chrome::ToggleFullscreenMode(browser());
469 NSView* fullscreen_floating_bar =
470 GetViewWithID(VIEW_ID_FULLSCREEN_FLOATING_BAR);
471 [fullscreen_floating_bar removeFromSuperview];
472 [[[controller() window] contentView] addSubview:fullscreen_floating_bar
473 positioned:NSWindowBelow
475 [controller() updateSubviewZOrder];
477 std::vector<ViewID> view_list;
478 view_list.push_back(VIEW_ID_INFO_BAR);
479 view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
480 view_list.push_back(VIEW_ID_FULLSCREEN_FLOATING_BAR);
481 view_list.push_back(VIEW_ID_BOOKMARK_BAR);
482 view_list.push_back(VIEW_ID_TOOLBAR);
483 view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
484 VerifyZOrder(view_list);
487 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, SheetPosition) {
488 ASSERT_TRUE([controller() isKindOfClass:[BrowserWindowController class]]);
489 EXPECT_TRUE([controller() isTabbedWindow]);
490 EXPECT_TRUE([controller() hasTabStrip]);
491 EXPECT_FALSE([controller() hasTitleBar]);
492 EXPECT_TRUE([controller() hasToolbar]);
493 EXPECT_FALSE([controller() isBookmarkBarVisible]);
495 id sheet = MockWindowWithFrame(NSMakeRect(0, 0, 300, 200));
496 NSWindow* window = browser()->window()->GetNativeWindow();
497 NSRect contentFrame = [[window contentView] frame];
498 NSRect defaultLocation =
499 NSMakeRect(0, NSMaxY(contentFrame), NSWidth(contentFrame), 0);
501 NSRect sheetLocation = [controller() window:window
502 willPositionSheet:nil
503 usingRect:defaultLocation];
504 NSRect toolbarFrame = [[[controller() toolbarController] view] frame];
505 EXPECT_EQ(NSMinY(toolbarFrame), NSMinY(sheetLocation));
507 // Open sheet with normal browser window, persistent bookmark bar.
508 chrome::ToggleBookmarkBarWhenVisible(browser()->profile());
509 EXPECT_TRUE([controller() isBookmarkBarVisible]);
510 sheetLocation = [controller() window:window
511 willPositionSheet:sheet
512 usingRect:defaultLocation];
513 NSRect bookmarkBarFrame = [[[controller() bookmarkBarController] view] frame];
514 EXPECT_EQ(NSMinY(bookmarkBarFrame), NSMinY(sheetLocation));
516 // If the sheet is too large, it should be positioned at the top of the
518 sheet = MockWindowWithFrame(NSMakeRect(0, 0, 300, 2000));
519 sheetLocation = [controller() window:window
520 willPositionSheet:sheet
521 usingRect:defaultLocation];
522 EXPECT_EQ(NSHeight([window frame]), NSMinY(sheetLocation));
524 // Reset the sheet's size.
525 sheet = MockWindowWithFrame(NSMakeRect(0, 0, 300, 200));
527 // Make sure the profile does not have the bookmark visible so that
528 // we'll create the shortcut window without the bookmark bar.
529 chrome::ToggleBookmarkBarWhenVisible(browser()->profile());
530 // Open application mode window.
531 OpenAppShortcutWindow(browser()->profile(), GURL("about:blank"));
532 Browser* popup_browser = BrowserList::GetInstance(
533 chrome::GetActiveDesktop())->GetLastActive();
534 NSWindow* popupWindow = popup_browser->window()->GetNativeWindow();
535 BrowserWindowController* popupController =
536 [BrowserWindowController browserWindowControllerForWindow:popupWindow];
537 ASSERT_TRUE([popupController isKindOfClass:[BrowserWindowController class]]);
538 EXPECT_FALSE([popupController isTabbedWindow]);
539 EXPECT_FALSE([popupController hasTabStrip]);
540 EXPECT_TRUE([popupController hasTitleBar]);
541 EXPECT_FALSE([popupController isBookmarkBarVisible]);
542 EXPECT_FALSE([popupController hasToolbar]);
544 // Open sheet in an application window.
545 [popupController showWindow:nil];
546 sheetLocation = [popupController window:popupWindow
547 willPositionSheet:sheet
548 usingRect:defaultLocation];
549 EXPECT_EQ(NSHeight([[popupWindow contentView] frame]), NSMinY(sheetLocation));
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 =
607 InfoBarContainerDelegate::kMaximumArrowTargetHeight +
608 InfoBarContainerDelegate::kSeparatorLineHeight;
610 chrome::ExecuteCommand(browser(), IDC_SHOW_BOOKMARK_BAR);
611 WaitForBookmarkBarAnimationToFinish();
612 EXPECT_TRUE([controller() isBookmarkBarVisible]);
613 EXPECT_EQ(std::min(GetExpectedTopInfoBarTipHeight(), max_tip_height),
614 [[controller() infoBarContainerController] overlappingTipHeight]);
616 chrome::ExecuteCommand(browser(), IDC_SHOW_BOOKMARK_BAR);
617 WaitForBookmarkBarAnimationToFinish();
618 EXPECT_FALSE([controller() isBookmarkBarVisible]);
619 EXPECT_EQ(std::min(GetExpectedTopInfoBarTipHeight(), max_tip_height),
620 [[controller() infoBarContainerController] overlappingTipHeight]);
623 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, TrafficLightZOrder) {
624 // Verify z order immediately after creation.
625 VerifyWindowControlsZOrder();
627 // Verify z order in and out of overlay.
628 [controller() showOverlay];
629 VerifyWindowControlsZOrder();
630 [controller() removeOverlay];
631 VerifyWindowControlsZOrder();
633 // Toggle immersive fullscreen, then verify z order. In immersive fullscreen,
634 // there are no window controls.
635 [controller() enterImmersiveFullscreen];
636 [controller() exitImmersiveFullscreen];
637 VerifyWindowControlsZOrder();