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