browser_window_controller_browsertest.mm revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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.
4
5#import "chrome/browser/ui/cocoa/browser_window_controller.h"
6
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/infobars/infobar_service.h"
12#include "chrome/browser/infobars/simple_alert_infobar_delegate.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/profiles/profile_manager.h"
15#include "chrome/browser/ui/bookmarks/bookmark_utils.h"
16#include "chrome/browser/ui/browser.h"
17#include "chrome/browser/ui/browser_commands.h"
18#include "chrome/browser/ui/browser_list.h"
19#include "chrome/browser/ui/browser_window.h"
20#import "chrome/browser/ui/cocoa/browser/avatar_button_controller.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/tab_contents/overlayable_contents_controller.h"
29#include "chrome/browser/ui/extensions/application_launch.h"
30#include "chrome/browser/ui/find_bar/find_bar_controller.h"
31#include "chrome/browser/ui/find_bar/find_bar.h"
32#include "chrome/browser/ui/tabs/tab_strip_model.h"
33#include "chrome/test/base/in_process_browser_test.h"
34#include "chrome/test/base/testing_profile.h"
35#include "content/public/browser/web_contents.h"
36#include "content/public/browser/web_contents_view.h"
37#import "testing/gtest_mac.h"
38
39namespace {
40
41#if !defined(MAC_OS_X_VERSION_10_7) || \
42    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
43enum {
44  NSWindowDocumentVersionsButton = 6,
45  NSWindowFullScreenButton
46};
47#endif  // MAC_OS_X_VERSION_10_7
48
49void CreateProfileCallback(const base::Closure& quit_closure,
50                           Profile* profile,
51                           Profile::CreateStatus status) {
52  EXPECT_TRUE(profile);
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)
58    quit_closure.Run();
59}
60
61enum ViewID {
62  VIEW_ID_TOOLBAR,
63  VIEW_ID_BOOKMARK_BAR,
64  VIEW_ID_INFO_BAR,
65  VIEW_ID_FIND_BAR,
66  VIEW_ID_DOWNLOAD_SHELF,
67  VIEW_ID_TAB_CONTENT_AREA,
68  VIEW_ID_FULLSCREEN_FLOATING_BAR,
69  VIEW_ID_COUNT,
70};
71
72}  // namespace
73
74class BrowserWindowControllerTest : public InProcessBrowserTest {
75 public:
76  BrowserWindowControllerTest() : InProcessBrowserTest() {
77  }
78
79  virtual void SetUpOnMainThread() OVERRIDE {
80    [[controller() bookmarkBarController] setStateAnimationsEnabled:NO];
81    [[controller() bookmarkBarController] setInnerContentAnimationsEnabled:NO];
82  }
83
84  BrowserWindowController* controller() const {
85    return [BrowserWindowController browserWindowControllerForWindow:
86        browser()->window()->GetNativeWindow()];
87  }
88
89  static void ShowInfoBar(Browser* browser) {
90    SimpleAlertInfoBarDelegate::Create(
91        InfoBarService::FromWebContents(
92            browser->tab_strip_model()->GetActiveWebContents()),
93        0, base::string16(), false);
94  }
95
96  NSView* GetViewWithID(ViewID view_id) const {
97    switch (view_id) {
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];
112      default:
113        NOTREACHED();
114        return nil;
115    }
116  }
117
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]);
124    }
125
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]);
131      }
132    }
133  }
134
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];
140    }
141    return height;
142  }
143
144 private:
145  DISALLOW_COPY_AND_ASSIGN(BrowserWindowControllerTest);
146};
147
148// Tests that adding the first profile moves the Lion fullscreen button over
149// correctly.
150// DISABLED_ because it regularly times out: http://crbug.com/159002.
151IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
152                       DISABLED_ProfileAvatarFullscreenButton) {
153  if (base::mac::IsOSSnowLeopard())
154    return;
155
156  // Initialize the locals.
157  ProfileManager* profile_manager = g_browser_process->profile_manager();
158  ASSERT_TRUE(profile_manager);
159
160  NSWindow* window = browser()->window()->GetNativeWindow();
161  ASSERT_TRUE(window);
162
163  // With only one profile, the fullscreen button should be visible, but the
164  // avatar button should not.
165  EXPECT_EQ(1u, profile_manager->GetNumberOfProfiles());
166
167  NSButton* fullscreen_button =
168      [window standardWindowButton:NSWindowFullScreenButton];
169  EXPECT_TRUE(fullscreen_button);
170  EXPECT_FALSE([fullscreen_button isHidden]);
171
172  AvatarButtonController* avatar_controller =
173      [controller() avatarButtonController];
174  NSView* avatar = [avatar_controller view];
175  EXPECT_TRUE(avatar);
176  EXPECT_TRUE([avatar isHidden]);
177
178  // Create a profile asynchronously and run the loop until its creation
179  // is complete.
180  base::RunLoop run_loop;
181
182  ProfileManager::CreateCallback create_callback =
183      base::Bind(&CreateProfileCallback, run_loop.QuitClosure());
184  profile_manager->CreateProfileAsync(
185      profile_manager->user_data_dir().Append("test"),
186      create_callback,
187      ASCIIToUTF16("avatar_test"),
188      base::string16(),
189      std::string());
190
191  run_loop.Run();
192
193  // There should now be two profiles, and the avatar button and fullscreen
194  // button are both visible.
195  EXPECT_EQ(2u, profile_manager->GetNumberOfProfiles());
196  EXPECT_FALSE([avatar isHidden]);
197  EXPECT_FALSE([fullscreen_button isHidden]);
198  EXPECT_EQ([avatar window], [fullscreen_button window]);
199
200  // Make sure the visual order of the buttons is correct and that they don't
201  // overlap.
202  NSRect avatar_frame = [avatar frame];
203  NSRect fullscreen_frame = [fullscreen_button frame];
204
205  EXPECT_LT(NSMinX(fullscreen_frame), NSMinX(avatar_frame));
206  EXPECT_LT(NSMaxX(fullscreen_frame), NSMinX(avatar_frame));
207}
208
209// Verify that in non-Instant normal mode that the find bar and download shelf
210// are above the content area. Everything else should be below it.
211IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, ZOrderNormal) {
212  browser()->GetFindBarController();  // add find bar
213
214  std::vector<ViewID> view_list;
215  view_list.push_back(VIEW_ID_BOOKMARK_BAR);
216  view_list.push_back(VIEW_ID_TOOLBAR);
217  view_list.push_back(VIEW_ID_INFO_BAR);
218  view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
219  view_list.push_back(VIEW_ID_FIND_BAR);
220  view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
221  VerifyZOrder(view_list);
222}
223
224// Verify that in non-Instant presentation mode that the info bar is below the
225// content are and everything else is above it.
226// DISABLED due to flaky failures on trybots. http://crbug.com/178778
227IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
228                       DISABLED_ZOrderPresentationMode) {
229  chrome::ToggleFullscreenMode(browser());
230  browser()->GetFindBarController();  // add find bar
231
232  std::vector<ViewID> view_list;
233  view_list.push_back(VIEW_ID_INFO_BAR);
234  view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
235  view_list.push_back(VIEW_ID_FULLSCREEN_FLOATING_BAR);
236  view_list.push_back(VIEW_ID_BOOKMARK_BAR);
237  view_list.push_back(VIEW_ID_TOOLBAR);
238  view_list.push_back(VIEW_ID_FIND_BAR);
239  view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
240  VerifyZOrder(view_list);
241}
242
243// Verify that if the fullscreen floating bar view is below the tab content area
244// then calling |updateSubviewZOrder:| will correctly move back above.
245IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
246                       DISABLED_FloatingBarBelowContentView) {
247  // TODO(kbr): re-enable: http://crbug.com/222296
248  if (base::mac::IsOSMountainLionOrLater())
249    return;
250
251  chrome::ToggleFullscreenMode(browser());
252
253  NSView* fullscreen_floating_bar =
254      GetViewWithID(VIEW_ID_FULLSCREEN_FLOATING_BAR);
255  [fullscreen_floating_bar removeFromSuperview];
256  [[[controller() window] contentView] addSubview:fullscreen_floating_bar
257                                       positioned:NSWindowBelow
258                                       relativeTo:nil];
259  [controller() updateSubviewZOrder:[controller() inPresentationMode]];
260
261  std::vector<ViewID> view_list;
262  view_list.push_back(VIEW_ID_INFO_BAR);
263  view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
264  view_list.push_back(VIEW_ID_FULLSCREEN_FLOATING_BAR);
265  view_list.push_back(VIEW_ID_BOOKMARK_BAR);
266  view_list.push_back(VIEW_ID_TOOLBAR);
267  view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
268  VerifyZOrder(view_list);
269}
270
271IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, SheetPosition) {
272  ASSERT_TRUE([controller() isKindOfClass:[BrowserWindowController class]]);
273  EXPECT_TRUE([controller() isTabbedWindow]);
274  EXPECT_TRUE([controller() hasTabStrip]);
275  EXPECT_FALSE([controller() hasTitleBar]);
276  EXPECT_TRUE([controller() hasToolbar]);
277  EXPECT_FALSE([controller() isBookmarkBarVisible]);
278
279  NSRect defaultAlertFrame = NSMakeRect(0, 0, 300, 200);
280  NSWindow* window = browser()->window()->GetNativeWindow();
281  NSRect alertFrame = [controller() window:window
282                         willPositionSheet:nil
283                                 usingRect:defaultAlertFrame];
284  NSRect toolbarFrame = [[[controller() toolbarController] view] frame];
285  EXPECT_EQ(NSMinY(alertFrame), NSMinY(toolbarFrame));
286
287  // Open sheet with normal browser window, persistent bookmark bar.
288  chrome::ToggleBookmarkBarWhenVisible(browser()->profile());
289  EXPECT_TRUE([controller() isBookmarkBarVisible]);
290  alertFrame = [controller() window:window
291                  willPositionSheet:nil
292                          usingRect:defaultAlertFrame];
293  NSRect bookmarkBarFrame = [[[controller() bookmarkBarController] view] frame];
294  EXPECT_EQ(NSMinY(alertFrame), NSMinY(bookmarkBarFrame));
295
296  // Make sure the profile does not have the bookmark visible so that
297  // we'll create the shortcut window without the bookmark bar.
298  chrome::ToggleBookmarkBarWhenVisible(browser()->profile());
299  // Open application mode window.
300  gfx::Rect initial_bounds(0, 0, 400, 400);
301  OpenAppShortcutWindow(browser()->profile(), GURL("about:blank"),
302                        initial_bounds);
303  Browser* popup_browser = BrowserList::GetInstance(
304      chrome::GetActiveDesktop())->GetLastActive();
305  NSWindow* popupWindow = popup_browser->window()->GetNativeWindow();
306  BrowserWindowController* popupController =
307      [BrowserWindowController browserWindowControllerForWindow:popupWindow];
308  ASSERT_TRUE([popupController isKindOfClass:[BrowserWindowController class]]);
309  EXPECT_FALSE([popupController isTabbedWindow]);
310  EXPECT_FALSE([popupController hasTabStrip]);
311  EXPECT_TRUE([popupController hasTitleBar]);
312  EXPECT_FALSE([popupController isBookmarkBarVisible]);
313  EXPECT_FALSE([popupController hasToolbar]);
314
315  // Open sheet in an application window.
316  [popupController showWindow:nil];
317  alertFrame = [popupController window:popupWindow
318                     willPositionSheet:nil
319                             usingRect:defaultAlertFrame];
320  EXPECT_EQ(NSMinY(alertFrame),
321            NSHeight([[popupWindow contentView] frame]) -
322            defaultAlertFrame.size.height);
323
324  // Close the application window.
325  popup_browser->tab_strip_model()->CloseSelectedTabs();
326  [popupController close];
327}
328
329// Verify that the info bar tip is hidden when the toolbar is not visible.
330IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
331                       InfoBarTipHiddenForWindowWithoutToolbar) {
332  ShowInfoBar(browser());
333  EXPECT_FALSE(
334      [[controller() infoBarContainerController] shouldSuppressTopInfoBarTip]);
335
336  gfx::Rect initial_bounds(0, 0, 400, 400);
337  OpenAppShortcutWindow(browser()->profile(), GURL("about:blank"),
338                        initial_bounds);
339  Browser* popup_browser = BrowserList::GetInstance(
340      chrome::HOST_DESKTOP_TYPE_NATIVE)->GetLastActive();
341  NSWindow* popupWindow = popup_browser->window()->GetNativeWindow();
342  BrowserWindowController* popupController =
343      [BrowserWindowController browserWindowControllerForWindow:popupWindow];
344  EXPECT_FALSE([popupController hasToolbar]);
345
346  // Show infobar for controller.
347  ShowInfoBar(popup_browser);
348  EXPECT_TRUE(
349      [[popupController infoBarContainerController]
350          shouldSuppressTopInfoBarTip]);
351}
352
353// Verify that AllowOverlappingViews is set while the history overlay is
354// visible.
355IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
356                       AllowOverlappingViewsHistoryOverlay) {
357  content::WebContentsView* web_contents_view =
358      browser()->tab_strip_model()->GetActiveWebContents()->GetView();
359  EXPECT_TRUE(web_contents_view->GetAllowOverlappingViews());
360
361  base::scoped_nsobject<HistoryOverlayController> overlay(
362      [[HistoryOverlayController alloc] initForMode:kHistoryOverlayModeBack]);
363  [overlay showPanelForView:web_contents_view->GetNativeView()];
364  EXPECT_TRUE(web_contents_view->GetAllowOverlappingViews());
365
366  overlay.reset();
367  EXPECT_TRUE(web_contents_view->GetAllowOverlappingViews());
368}
369