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/run_loop.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/devtools/devtools_window.h"
12 #include "chrome/browser/infobars/infobar_service.h"
13 #include "chrome/browser/infobars/simple_alert_infobar_delegate.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile_manager.h"
16 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_commands.h"
19 #include "chrome/browser/ui/browser_list.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
22 #import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
23 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
24 #import "chrome/browser/ui/cocoa/history_overlay_controller.h"
25 #import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
26 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
27 #import "chrome/browser/ui/cocoa/nsview_additions.h"
28 #import "chrome/browser/ui/cocoa/profiles/avatar_base_controller.h"
29 #import "chrome/browser/ui/cocoa/tab_contents/overlayable_contents_controller.h"
30 #include "chrome/browser/ui/extensions/application_launch.h"
31 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
32 #include "chrome/browser/ui/find_bar/find_bar.h"
33 #include "chrome/browser/ui/tabs/tab_strip_model.h"
34 #include "chrome/test/base/in_process_browser_test.h"
35 #include "chrome/test/base/testing_profile.h"
36 #include "content/public/browser/web_contents.h"
37 #import "testing/gtest_mac.h"
41 #if !defined(MAC_OS_X_VERSION_10_7) || \
42 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
44 NSWindowDocumentVersionsButton = 6,
45 NSWindowFullScreenButton
47 #endif // MAC_OS_X_VERSION_10_7
49 void CreateProfileCallback(const base::Closure& quit_closure,
51 Profile::CreateStatus status) {
53 EXPECT_NE(Profile::CREATE_STATUS_LOCAL_FAIL, status);
54 EXPECT_NE(Profile::CREATE_STATUS_REMOTE_FAIL, status);
55 // This will be called multiple times. Wait until the profile is initialized
56 // fully to quit the loop.
57 if (status == Profile::CREATE_STATUS_INITIALIZED)
66 VIEW_ID_DOWNLOAD_SHELF,
67 VIEW_ID_TAB_CONTENT_AREA,
68 VIEW_ID_FULLSCREEN_FLOATING_BAR,
74 class BrowserWindowControllerTest : public InProcessBrowserTest {
76 BrowserWindowControllerTest() : InProcessBrowserTest() {
79 virtual void SetUpOnMainThread() OVERRIDE {
80 [[controller() bookmarkBarController] setStateAnimationsEnabled:NO];
81 [[controller() bookmarkBarController] setInnerContentAnimationsEnabled:NO];
84 BrowserWindowController* controller() const {
85 return [BrowserWindowController browserWindowControllerForWindow:
86 browser()->window()->GetNativeWindow()];
89 static void ShowInfoBar(Browser* browser) {
90 SimpleAlertInfoBarDelegate::Create(
91 InfoBarService::FromWebContents(
92 browser->tab_strip_model()->GetActiveWebContents()),
93 0, base::string16(), false);
96 NSView* GetViewWithID(ViewID view_id) const {
98 case VIEW_ID_FULLSCREEN_FLOATING_BAR:
99 return [controller() floatingBarBackingView];
100 case VIEW_ID_TOOLBAR:
101 return [[controller() toolbarController] view];
102 case VIEW_ID_BOOKMARK_BAR:
103 return [[controller() bookmarkBarController] view];
104 case VIEW_ID_INFO_BAR:
105 return [[controller() infoBarContainerController] view];
106 case VIEW_ID_FIND_BAR:
107 return [[controller() findBarCocoaController] view];
108 case VIEW_ID_DOWNLOAD_SHELF:
109 return [[controller() downloadShelf] view];
110 case VIEW_ID_TAB_CONTENT_AREA:
111 return [controller() tabContentArea];
118 void VerifyZOrder(const std::vector<ViewID>& view_list) const {
119 for (size_t i = 0; i < view_list.size() - 1; ++i) {
120 NSView* bottom_view = GetViewWithID(view_list[i]);
121 NSView* top_view = GetViewWithID(view_list[i + 1]);
122 EXPECT_NSEQ([bottom_view superview], [top_view superview]);
123 EXPECT_TRUE([bottom_view cr_isBelowView:top_view]);
126 // Views not in |view_list| must either be nil or not parented.
127 for (size_t i = 0; i < VIEW_ID_COUNT; ++i) {
128 if (std::find(view_list.begin(), view_list.end(), i) == view_list.end()) {
129 NSView* view = GetViewWithID(static_cast<ViewID>(i));
130 EXPECT_TRUE(!view || ![view superview]);
135 CGFloat GetViewHeight(ViewID viewID) const {
136 CGFloat height = NSHeight([GetViewWithID(viewID) frame]);
137 if (viewID == VIEW_ID_INFO_BAR) {
138 height -= [[controller() infoBarContainerController]
139 overlappingTipHeight];
144 void SetDevToolsWindowContentsBounds(
145 DevToolsWindow* window, const gfx::Rect& bounds) {
146 window->SetInspectedPageBounds(bounds);
150 DISALLOW_COPY_AND_ASSIGN(BrowserWindowControllerTest);
153 // Tests that adding the first profile moves the Lion fullscreen button over
155 // DISABLED_ because it regularly times out: http://crbug.com/159002.
156 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
157 DISABLED_ProfileAvatarFullscreenButton) {
158 if (base::mac::IsOSSnowLeopard())
161 // Initialize the locals.
162 ProfileManager* profile_manager = g_browser_process->profile_manager();
163 ASSERT_TRUE(profile_manager);
165 NSWindow* window = browser()->window()->GetNativeWindow();
168 // With only one profile, the fullscreen button should be visible, but the
169 // avatar button should not.
170 EXPECT_EQ(1u, profile_manager->GetNumberOfProfiles());
172 NSButton* fullscreen_button =
173 [window standardWindowButton:NSWindowFullScreenButton];
174 EXPECT_TRUE(fullscreen_button);
175 EXPECT_FALSE([fullscreen_button isHidden]);
177 AvatarBaseController* avatar_controller =
178 [controller() avatarButtonController];
179 NSView* avatar = [avatar_controller view];
181 EXPECT_TRUE([avatar isHidden]);
183 // Create a profile asynchronously and run the loop until its creation
185 base::RunLoop run_loop;
187 ProfileManager::CreateCallback create_callback =
188 base::Bind(&CreateProfileCallback, run_loop.QuitClosure());
189 profile_manager->CreateProfileAsync(
190 profile_manager->user_data_dir().Append("test"),
192 base::ASCIIToUTF16("avatar_test"),
198 // There should now be two profiles, and the avatar button and fullscreen
199 // button are both visible.
200 EXPECT_EQ(2u, profile_manager->GetNumberOfProfiles());
201 EXPECT_FALSE([avatar isHidden]);
202 EXPECT_FALSE([fullscreen_button isHidden]);
203 EXPECT_EQ([avatar window], [fullscreen_button window]);
205 // Make sure the visual order of the buttons is correct and that they don't
207 NSRect avatar_frame = [avatar frame];
208 NSRect fullscreen_frame = [fullscreen_button frame];
210 EXPECT_LT(NSMinX(fullscreen_frame), NSMinX(avatar_frame));
211 EXPECT_LT(NSMaxX(fullscreen_frame), NSMinX(avatar_frame));
214 // Verify that in non-Instant normal mode that the find bar and download shelf
215 // are above the content area. Everything else should be below it.
216 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, ZOrderNormal) {
217 browser()->GetFindBarController(); // add find bar
219 std::vector<ViewID> view_list;
220 view_list.push_back(VIEW_ID_BOOKMARK_BAR);
221 view_list.push_back(VIEW_ID_TOOLBAR);
222 view_list.push_back(VIEW_ID_INFO_BAR);
223 view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
224 view_list.push_back(VIEW_ID_FIND_BAR);
225 view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
226 VerifyZOrder(view_list);
229 // Verify that in non-Instant presentation mode that the info bar is below the
230 // content are and everything else is above it.
231 // DISABLED due to flaky failures on trybots. http://crbug.com/178778
232 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
233 DISABLED_ZOrderPresentationMode) {
234 chrome::ToggleFullscreenMode(browser());
235 browser()->GetFindBarController(); // add find bar
237 std::vector<ViewID> view_list;
238 view_list.push_back(VIEW_ID_INFO_BAR);
239 view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
240 view_list.push_back(VIEW_ID_FULLSCREEN_FLOATING_BAR);
241 view_list.push_back(VIEW_ID_BOOKMARK_BAR);
242 view_list.push_back(VIEW_ID_TOOLBAR);
243 view_list.push_back(VIEW_ID_FIND_BAR);
244 view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
245 VerifyZOrder(view_list);
248 // Verify that if the fullscreen floating bar view is below the tab content area
249 // then calling |updateSubviewZOrder:| will correctly move back above.
250 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
251 DISABLED_FloatingBarBelowContentView) {
252 // TODO(kbr): re-enable: http://crbug.com/222296
253 if (base::mac::IsOSMountainLionOrLater())
256 chrome::ToggleFullscreenMode(browser());
258 NSView* fullscreen_floating_bar =
259 GetViewWithID(VIEW_ID_FULLSCREEN_FLOATING_BAR);
260 [fullscreen_floating_bar removeFromSuperview];
261 [[[controller() window] contentView] addSubview:fullscreen_floating_bar
262 positioned:NSWindowBelow
264 [controller() updateSubviewZOrder:[controller() inPresentationMode]];
266 std::vector<ViewID> view_list;
267 view_list.push_back(VIEW_ID_INFO_BAR);
268 view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
269 view_list.push_back(VIEW_ID_FULLSCREEN_FLOATING_BAR);
270 view_list.push_back(VIEW_ID_BOOKMARK_BAR);
271 view_list.push_back(VIEW_ID_TOOLBAR);
272 view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
273 VerifyZOrder(view_list);
276 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, SheetPosition) {
277 ASSERT_TRUE([controller() isKindOfClass:[BrowserWindowController class]]);
278 EXPECT_TRUE([controller() isTabbedWindow]);
279 EXPECT_TRUE([controller() hasTabStrip]);
280 EXPECT_FALSE([controller() hasTitleBar]);
281 EXPECT_TRUE([controller() hasToolbar]);
282 EXPECT_FALSE([controller() isBookmarkBarVisible]);
284 NSRect defaultAlertFrame = NSMakeRect(0, 0, 300, 200);
285 NSWindow* window = browser()->window()->GetNativeWindow();
286 NSRect alertFrame = [controller() window:window
287 willPositionSheet:nil
288 usingRect:defaultAlertFrame];
289 NSRect toolbarFrame = [[[controller() toolbarController] view] frame];
290 EXPECT_EQ(NSMinY(alertFrame), NSMinY(toolbarFrame));
292 // Open sheet with normal browser window, persistent bookmark bar.
293 chrome::ToggleBookmarkBarWhenVisible(browser()->profile());
294 EXPECT_TRUE([controller() isBookmarkBarVisible]);
295 alertFrame = [controller() window:window
296 willPositionSheet:nil
297 usingRect:defaultAlertFrame];
298 NSRect bookmarkBarFrame = [[[controller() bookmarkBarController] view] frame];
299 EXPECT_EQ(NSMinY(alertFrame), NSMinY(bookmarkBarFrame));
301 // Make sure the profile does not have the bookmark visible so that
302 // we'll create the shortcut window without the bookmark bar.
303 chrome::ToggleBookmarkBarWhenVisible(browser()->profile());
304 // Open application mode window.
305 OpenAppShortcutWindow(browser()->profile(), GURL("about:blank"));
306 Browser* popup_browser = BrowserList::GetInstance(
307 chrome::GetActiveDesktop())->GetLastActive();
308 NSWindow* popupWindow = popup_browser->window()->GetNativeWindow();
309 BrowserWindowController* popupController =
310 [BrowserWindowController browserWindowControllerForWindow:popupWindow];
311 ASSERT_TRUE([popupController isKindOfClass:[BrowserWindowController class]]);
312 EXPECT_FALSE([popupController isTabbedWindow]);
313 EXPECT_FALSE([popupController hasTabStrip]);
314 EXPECT_TRUE([popupController hasTitleBar]);
315 EXPECT_FALSE([popupController isBookmarkBarVisible]);
316 EXPECT_FALSE([popupController hasToolbar]);
318 // Open sheet in an application window.
319 [popupController showWindow:nil];
320 alertFrame = [popupController window:popupWindow
321 willPositionSheet:nil
322 usingRect:defaultAlertFrame];
323 EXPECT_EQ(NSMinY(alertFrame),
324 NSHeight([[popupWindow contentView] frame]) -
325 defaultAlertFrame.size.height);
327 // Close the application window.
328 popup_browser->tab_strip_model()->CloseSelectedTabs();
329 [popupController close];
332 // Verify that the info bar tip is hidden when the toolbar is not visible.
333 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
334 InfoBarTipHiddenForWindowWithoutToolbar) {
335 ShowInfoBar(browser());
337 [[controller() infoBarContainerController] shouldSuppressTopInfoBarTip]);
339 OpenAppShortcutWindow(browser()->profile(), GURL("about:blank"));
340 Browser* popup_browser = BrowserList::GetInstance(
341 chrome::HOST_DESKTOP_TYPE_NATIVE)->GetLastActive();
342 NSWindow* popupWindow = popup_browser->window()->GetNativeWindow();
343 BrowserWindowController* popupController =
344 [BrowserWindowController browserWindowControllerForWindow:popupWindow];
345 EXPECT_FALSE([popupController hasToolbar]);
347 // Show infobar for controller.
348 ShowInfoBar(popup_browser);
350 [[popupController infoBarContainerController]
351 shouldSuppressTopInfoBarTip]);
354 // Verify that AllowOverlappingViews is set while the history overlay is
356 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
357 AllowOverlappingViewsHistoryOverlay) {
358 content::WebContents* web_contents =
359 browser()->tab_strip_model()->GetActiveWebContents();
360 EXPECT_TRUE(web_contents->GetAllowOverlappingViews());
362 base::scoped_nsobject<HistoryOverlayController> overlay(
363 [[HistoryOverlayController alloc] initForMode:kHistoryOverlayModeBack]);
364 [overlay showPanelForView:web_contents->GetNativeView()];
365 EXPECT_TRUE(web_contents->GetAllowOverlappingViews());
368 EXPECT_TRUE(web_contents->GetAllowOverlappingViews());
371 // Tests that status bubble's base frame does move when devTools are docked.
372 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
373 StatusBubblePositioning) {
374 NSPoint origin = [controller() statusBubbleBaseFrame].origin;
376 DevToolsWindow* devtools_window = DevToolsWindow::OpenDevToolsWindowForTest(
378 SetDevToolsWindowContentsBounds(devtools_window, gfx::Rect(10, 10, 100, 100));
380 NSPoint originWithDevTools = [controller() statusBubbleBaseFrame].origin;
381 EXPECT_FALSE(NSEqualPoints(origin, originWithDevTools));