7dadf209dc724d622ac07170e5bef3c846673ebe
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / browser_window_controller_browsertest.mm
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/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 "ui/base/cocoa/nsview_additions.h"
44 #include "ui/gfx/animation/slide_animation.h"
45
46 namespace {
47
48 void CreateProfileCallback(const base::Closure& quit_closure,
49                            Profile* profile,
50                            Profile::CreateStatus status) {
51   EXPECT_TRUE(profile);
52   EXPECT_NE(Profile::CREATE_STATUS_LOCAL_FAIL, status);
53   EXPECT_NE(Profile::CREATE_STATUS_REMOTE_FAIL, status);
54   // This will be called multiple times. Wait until the profile is initialized
55   // fully to quit the loop.
56   if (status == Profile::CREATE_STATUS_INITIALIZED)
57     quit_closure.Run();
58 }
59
60 enum ViewID {
61   VIEW_ID_TOOLBAR,
62   VIEW_ID_BOOKMARK_BAR,
63   VIEW_ID_INFO_BAR,
64   VIEW_ID_FIND_BAR,
65   VIEW_ID_DOWNLOAD_SHELF,
66   VIEW_ID_TAB_CONTENT_AREA,
67   VIEW_ID_FULLSCREEN_FLOATING_BAR,
68   VIEW_ID_COUNT,
69 };
70
71 }  // namespace
72
73 @interface InfoBarContainerController(TestingAPI)
74 - (BOOL)isTopInfoBarAnimationRunning;
75 @end
76
77 @implementation InfoBarContainerController(TestingAPI)
78 - (BOOL)isTopInfoBarAnimationRunning {
79   InfoBarController* infoBarController = [infobarControllers_ objectAtIndex:0];
80   if (infoBarController) {
81     const gfx::SlideAnimation& infobarAnimation =
82         static_cast<const InfoBarCocoa*>(
83             infoBarController.infobar)->animation();
84     return infobarAnimation.is_animating();
85   }
86   return NO;
87 }
88 @end
89
90 class BrowserWindowControllerTest : public InProcessBrowserTest {
91  public:
92   BrowserWindowControllerTest() : InProcessBrowserTest() {
93   }
94
95   virtual void SetUpOnMainThread() OVERRIDE {
96     [[controller() bookmarkBarController] setStateAnimationsEnabled:NO];
97     [[controller() bookmarkBarController] setInnerContentAnimationsEnabled:NO];
98   }
99
100   BrowserWindowController* controller() const {
101     return [BrowserWindowController browserWindowControllerForWindow:
102         browser()->window()->GetNativeWindow()];
103   }
104
105   static void ShowInfoBar(Browser* browser) {
106     SimpleAlertInfoBarDelegate::Create(
107         InfoBarService::FromWebContents(
108             browser->tab_strip_model()->GetActiveWebContents()),
109         0, base::string16(), false);
110   }
111
112   NSView* GetViewWithID(ViewID view_id) const {
113     switch (view_id) {
114       case VIEW_ID_FULLSCREEN_FLOATING_BAR:
115         return [controller() floatingBarBackingView];
116       case VIEW_ID_TOOLBAR:
117         return [[controller() toolbarController] view];
118       case VIEW_ID_BOOKMARK_BAR:
119         return [[controller() bookmarkBarController] view];
120       case VIEW_ID_INFO_BAR:
121         return [[controller() infoBarContainerController] view];
122       case VIEW_ID_FIND_BAR:
123         return [[controller() findBarCocoaController] view];
124       case VIEW_ID_DOWNLOAD_SHELF:
125         return [[controller() downloadShelf] view];
126       case VIEW_ID_TAB_CONTENT_AREA:
127         return [controller() tabContentArea];
128       default:
129         NOTREACHED();
130         return nil;
131     }
132   }
133
134   void VerifyZOrder(const std::vector<ViewID>& view_list) const {
135     std::vector<NSView*> visible_views;
136     for (size_t i = 0; i < view_list.size(); ++i) {
137       NSView* view = GetViewWithID(view_list[i]);
138       if ([view superview])
139         visible_views.push_back(view);
140     }
141
142     for (size_t i = 0; i < visible_views.size() - 1; ++i) {
143       NSView* bottom_view = visible_views[i];
144       NSView* top_view = visible_views[i + 1];
145
146       EXPECT_NSEQ([bottom_view superview], [top_view superview]);
147       EXPECT_TRUE([bottom_view cr_isBelowView:top_view]);
148     }
149
150     // Views not in |view_list| must either be nil or not parented.
151     for (size_t i = 0; i < VIEW_ID_COUNT; ++i) {
152       if (std::find(view_list.begin(), view_list.end(), i) == view_list.end()) {
153         NSView* view = GetViewWithID(static_cast<ViewID>(i));
154         EXPECT_TRUE(!view || ![view superview]);
155       }
156     }
157   }
158
159   CGFloat GetViewHeight(ViewID viewID) const {
160     CGFloat height = NSHeight([GetViewWithID(viewID) frame]);
161     if (viewID == VIEW_ID_INFO_BAR) {
162       height -= [[controller() infoBarContainerController]
163           overlappingTipHeight];
164     }
165     return height;
166   }
167
168   static void CheckTopInfoBarAnimation(
169       InfoBarContainerController* info_bar_container_controller,
170       const base::Closure& quit_task) {
171     if (![info_bar_container_controller isTopInfoBarAnimationRunning])
172       quit_task.Run();
173   }
174
175   static void CheckBookmarkBarAnimation(
176       BookmarkBarController* bookmark_bar_controller,
177       const base::Closure& quit_task) {
178     if (![bookmark_bar_controller isAnimationRunning])
179       quit_task.Run();
180   }
181
182   void WaitForTopInfoBarAnimationToFinish() {
183     scoped_refptr<content::MessageLoopRunner> runner =
184         new content::MessageLoopRunner;
185
186     base::Timer timer(false, true);
187     timer.Start(
188         FROM_HERE,
189         base::TimeDelta::FromMilliseconds(15),
190         base::Bind(&CheckTopInfoBarAnimation,
191                    [controller() infoBarContainerController],
192                    runner->QuitClosure()));
193     runner->Run();
194   }
195
196   void WaitForBookmarkBarAnimationToFinish() {
197     scoped_refptr<content::MessageLoopRunner> runner =
198         new content::MessageLoopRunner;
199
200     base::Timer timer(false, true);
201     timer.Start(
202         FROM_HERE,
203         base::TimeDelta::FromMilliseconds(15),
204         base::Bind(&CheckBookmarkBarAnimation,
205                    [controller() bookmarkBarController],
206                    runner->QuitClosure()));
207     runner->Run();
208   }
209
210   NSInteger GetExpectedTopInfoBarTipHeight() {
211     InfoBarContainerController* info_bar_container_controller =
212         [controller() infoBarContainerController];
213     CGFloat overlapping_tip_height =
214         [info_bar_container_controller overlappingTipHeight];
215     LocationBarViewMac* location_bar_view = [controller() locationBarBridge];
216     NSPoint icon_bottom = location_bar_view->GetPageInfoBubblePoint();
217
218     NSPoint info_bar_top = NSMakePoint(0,
219         NSHeight([info_bar_container_controller view].frame) -
220         overlapping_tip_height);
221     info_bar_top = [[info_bar_container_controller view]
222         convertPoint:info_bar_top toView:nil];
223     return icon_bottom.y - info_bar_top.y;
224   }
225
226   // The traffic lights should always be in front of the content view and the
227   // tab strip view. Since the traffic lights change across OSX versions, this
228   // test verifies that the contentView is in the back, and if the tab strip
229   // view is a sibling, it is directly in front of the content view.
230   void VerifyTrafficLightZOrder() const {
231     NSView* contentView = [[controller() window] contentView];
232     NSView* rootView = [contentView superview];
233     EXPECT_EQ(contentView, [[rootView subviews] objectAtIndex:0]);
234
235     NSView* tabStripView = [controller() tabStripView];
236     if ([[rootView subviews] containsObject:tabStripView])
237       EXPECT_EQ(tabStripView, [[rootView subviews] objectAtIndex:1]);
238   }
239
240  private:
241   DISALLOW_COPY_AND_ASSIGN(BrowserWindowControllerTest);
242 };
243
244 // Tests that adding the first profile moves the Lion fullscreen button over
245 // correctly.
246 // DISABLED_ because it regularly times out: http://crbug.com/159002.
247 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
248                        DISABLED_ProfileAvatarFullscreenButton) {
249   if (base::mac::IsOSSnowLeopard())
250     return;
251
252   // Initialize the locals.
253   ProfileManager* profile_manager = g_browser_process->profile_manager();
254   ASSERT_TRUE(profile_manager);
255
256   NSWindow* window = browser()->window()->GetNativeWindow();
257   ASSERT_TRUE(window);
258
259   // With only one profile, the fullscreen button should be visible, but the
260   // avatar button should not.
261   EXPECT_EQ(1u, profile_manager->GetNumberOfProfiles());
262
263   NSButton* fullscreen_button =
264       [window standardWindowButton:NSWindowFullScreenButton];
265   EXPECT_TRUE(fullscreen_button);
266   EXPECT_FALSE([fullscreen_button isHidden]);
267
268   AvatarBaseController* avatar_controller =
269       [controller() avatarButtonController];
270   NSView* avatar = [avatar_controller view];
271   EXPECT_TRUE(avatar);
272   EXPECT_TRUE([avatar isHidden]);
273
274   // Create a profile asynchronously and run the loop until its creation
275   // is complete.
276   base::RunLoop run_loop;
277
278   ProfileManager::CreateCallback create_callback =
279       base::Bind(&CreateProfileCallback, run_loop.QuitClosure());
280   profile_manager->CreateProfileAsync(
281       profile_manager->user_data_dir().Append("test"),
282       create_callback,
283       base::ASCIIToUTF16("avatar_test"),
284       base::string16(),
285       std::string());
286
287   run_loop.Run();
288
289   // There should now be two profiles, and the avatar button and fullscreen
290   // button are both visible.
291   EXPECT_EQ(2u, profile_manager->GetNumberOfProfiles());
292   EXPECT_FALSE([avatar isHidden]);
293   EXPECT_FALSE([fullscreen_button isHidden]);
294   EXPECT_EQ([avatar window], [fullscreen_button window]);
295
296   // Make sure the visual order of the buttons is correct and that they don't
297   // overlap.
298   NSRect avatar_frame = [avatar frame];
299   NSRect fullscreen_frame = [fullscreen_button frame];
300
301   EXPECT_LT(NSMinX(fullscreen_frame), NSMinX(avatar_frame));
302   EXPECT_LT(NSMaxX(fullscreen_frame), NSMinX(avatar_frame));
303 }
304
305 // Verify that in non-Instant normal mode that the find bar and download shelf
306 // are above the content area. Everything else should be below it.
307 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, ZOrderNormal) {
308   browser()->GetFindBarController();  // add find bar
309
310   std::vector<ViewID> view_list;
311   view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
312   view_list.push_back(VIEW_ID_BOOKMARK_BAR);
313   view_list.push_back(VIEW_ID_TOOLBAR);
314   view_list.push_back(VIEW_ID_INFO_BAR);
315   view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
316   view_list.push_back(VIEW_ID_FIND_BAR);
317   VerifyZOrder(view_list);
318
319   [controller() showOverlay];
320   [controller() removeOverlay];
321   VerifyZOrder(view_list);
322
323   [controller() enterImmersiveFullscreen];
324   [controller() exitImmersiveFullscreen];
325   VerifyZOrder(view_list);
326 }
327
328 // Verify that in non-Instant presentation mode that the info bar is below the
329 // content are and everything else is above it.
330 // DISABLED due to flaky failures on trybots. http://crbug.com/178778
331 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
332                        DISABLED_ZOrderPresentationMode) {
333   chrome::ToggleFullscreenMode(browser());
334   browser()->GetFindBarController();  // add find bar
335
336   std::vector<ViewID> view_list;
337   view_list.push_back(VIEW_ID_INFO_BAR);
338   view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
339   view_list.push_back(VIEW_ID_FULLSCREEN_FLOATING_BAR);
340   view_list.push_back(VIEW_ID_BOOKMARK_BAR);
341   view_list.push_back(VIEW_ID_TOOLBAR);
342   view_list.push_back(VIEW_ID_FIND_BAR);
343   view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
344   VerifyZOrder(view_list);
345 }
346
347 // Verify that if the fullscreen floating bar view is below the tab content area
348 // then calling |updateSubviewZOrder:| will correctly move back above.
349 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
350                        DISABLED_FloatingBarBelowContentView) {
351   // TODO(kbr): re-enable: http://crbug.com/222296
352   if (base::mac::IsOSMountainLionOrLater())
353     return;
354
355   chrome::ToggleFullscreenMode(browser());
356
357   NSView* fullscreen_floating_bar =
358       GetViewWithID(VIEW_ID_FULLSCREEN_FLOATING_BAR);
359   [fullscreen_floating_bar removeFromSuperview];
360   [[[controller() window] contentView] addSubview:fullscreen_floating_bar
361                                        positioned:NSWindowBelow
362                                        relativeTo:nil];
363   [controller() updateSubviewZOrder];
364
365   std::vector<ViewID> view_list;
366   view_list.push_back(VIEW_ID_INFO_BAR);
367   view_list.push_back(VIEW_ID_TAB_CONTENT_AREA);
368   view_list.push_back(VIEW_ID_FULLSCREEN_FLOATING_BAR);
369   view_list.push_back(VIEW_ID_BOOKMARK_BAR);
370   view_list.push_back(VIEW_ID_TOOLBAR);
371   view_list.push_back(VIEW_ID_DOWNLOAD_SHELF);
372   VerifyZOrder(view_list);
373 }
374
375 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, SheetPosition) {
376   ASSERT_TRUE([controller() isKindOfClass:[BrowserWindowController class]]);
377   EXPECT_TRUE([controller() isTabbedWindow]);
378   EXPECT_TRUE([controller() hasTabStrip]);
379   EXPECT_FALSE([controller() hasTitleBar]);
380   EXPECT_TRUE([controller() hasToolbar]);
381   EXPECT_FALSE([controller() isBookmarkBarVisible]);
382
383   NSRect defaultAlertFrame = NSMakeRect(0, 0, 300, 200);
384   NSWindow* window = browser()->window()->GetNativeWindow();
385   NSRect alertFrame = [controller() window:window
386                          willPositionSheet:nil
387                                  usingRect:defaultAlertFrame];
388   NSRect toolbarFrame = [[[controller() toolbarController] view] frame];
389   EXPECT_EQ(NSMinY(alertFrame), NSMinY(toolbarFrame));
390
391   // Open sheet with normal browser window, persistent bookmark bar.
392   chrome::ToggleBookmarkBarWhenVisible(browser()->profile());
393   EXPECT_TRUE([controller() isBookmarkBarVisible]);
394   alertFrame = [controller() window:window
395                   willPositionSheet:nil
396                           usingRect:defaultAlertFrame];
397   NSRect bookmarkBarFrame = [[[controller() bookmarkBarController] view] frame];
398   EXPECT_EQ(NSMinY(alertFrame), NSMinY(bookmarkBarFrame));
399
400   // Make sure the profile does not have the bookmark visible so that
401   // we'll create the shortcut window without the bookmark bar.
402   chrome::ToggleBookmarkBarWhenVisible(browser()->profile());
403   // Open application mode window.
404   OpenAppShortcutWindow(browser()->profile(), GURL("about:blank"));
405   Browser* popup_browser = BrowserList::GetInstance(
406       chrome::GetActiveDesktop())->GetLastActive();
407   NSWindow* popupWindow = popup_browser->window()->GetNativeWindow();
408   BrowserWindowController* popupController =
409       [BrowserWindowController browserWindowControllerForWindow:popupWindow];
410   ASSERT_TRUE([popupController isKindOfClass:[BrowserWindowController class]]);
411   EXPECT_FALSE([popupController isTabbedWindow]);
412   EXPECT_FALSE([popupController hasTabStrip]);
413   EXPECT_TRUE([popupController hasTitleBar]);
414   EXPECT_FALSE([popupController isBookmarkBarVisible]);
415   EXPECT_FALSE([popupController hasToolbar]);
416
417   // Open sheet in an application window.
418   [popupController showWindow:nil];
419   alertFrame = [popupController window:popupWindow
420                      willPositionSheet:nil
421                              usingRect:defaultAlertFrame];
422   EXPECT_EQ(NSMinY(alertFrame),
423             NSHeight([[popupWindow contentView] frame]) -
424             defaultAlertFrame.size.height);
425
426   // Close the application window.
427   popup_browser->tab_strip_model()->CloseSelectedTabs();
428   [popupController close];
429 }
430
431 // Verify that the info bar tip is hidden when the toolbar is not visible.
432 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
433                        InfoBarTipHiddenForWindowWithoutToolbar) {
434   ShowInfoBar(browser());
435   EXPECT_FALSE(
436       [[controller() infoBarContainerController] shouldSuppressTopInfoBarTip]);
437
438   OpenAppShortcutWindow(browser()->profile(), GURL("about:blank"));
439   Browser* popup_browser = BrowserList::GetInstance(
440       chrome::HOST_DESKTOP_TYPE_NATIVE)->GetLastActive();
441   NSWindow* popupWindow = popup_browser->window()->GetNativeWindow();
442   BrowserWindowController* popupController =
443       [BrowserWindowController browserWindowControllerForWindow:popupWindow];
444   EXPECT_FALSE([popupController hasToolbar]);
445
446   // Show infobar for controller.
447   ShowInfoBar(popup_browser);
448   EXPECT_TRUE(
449       [[popupController infoBarContainerController]
450           shouldSuppressTopInfoBarTip]);
451 }
452
453 // Tests that status bubble's base frame does move when devTools are docked.
454 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
455                        StatusBubblePositioning) {
456   NSPoint origin = [controller() statusBubbleBaseFrame].origin;
457
458   DevToolsWindow* devtools_window =
459       DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true);
460   DevToolsWindowTesting::Get(devtools_window)->SetInspectedPageBounds(
461       gfx::Rect(10, 10, 100, 100));
462
463   NSPoint originWithDevTools = [controller() statusBubbleBaseFrame].origin;
464   EXPECT_FALSE(NSEqualPoints(origin, originWithDevTools));
465
466   DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
467 }
468
469 // Tests that top infobar tip is streched when bookmark bar becomes SHOWN/HIDDEN
470 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest,
471                        InfoBarTipStretchedWhenBookmarkBarStatusChanged) {
472   EXPECT_FALSE([controller() isBookmarkBarVisible]);
473   ShowInfoBar(browser());
474   // The infobar tip is animated during the infobar is being added, wait until
475   // it completes.
476   WaitForTopInfoBarAnimationToFinish();
477
478   EXPECT_FALSE([[controller() infoBarContainerController]
479       shouldSuppressTopInfoBarTip]);
480
481   NSInteger max_tip_height = infobars::InfoBar::kMaximumArrowTargetHeight +
482       infobars::InfoBar::kSeparatorLineHeight;
483
484   chrome::ExecuteCommand(browser(), IDC_SHOW_BOOKMARK_BAR);
485   WaitForBookmarkBarAnimationToFinish();
486   EXPECT_TRUE([controller() isBookmarkBarVisible]);
487   EXPECT_EQ(std::min(GetExpectedTopInfoBarTipHeight(), max_tip_height),
488             [[controller() infoBarContainerController] overlappingTipHeight]);
489
490   chrome::ExecuteCommand(browser(), IDC_SHOW_BOOKMARK_BAR);
491   WaitForBookmarkBarAnimationToFinish();
492   EXPECT_FALSE([controller() isBookmarkBarVisible]);
493   EXPECT_EQ(std::min(GetExpectedTopInfoBarTipHeight(), max_tip_height),
494             [[controller() infoBarContainerController] overlappingTipHeight]);
495 }
496
497 IN_PROC_BROWSER_TEST_F(BrowserWindowControllerTest, TrafficLightZOrder) {
498   // Verify z order immediately after creation.
499   VerifyTrafficLightZOrder();
500
501   // Toggle overlay, then verify z order.
502   [controller() showOverlay];
503   [controller() removeOverlay];
504   VerifyTrafficLightZOrder();
505
506   // Toggle immersive fullscreen, then verify z order.
507   [controller() enterImmersiveFullscreen];
508   [controller() exitImmersiveFullscreen];
509   VerifyTrafficLightZOrder();
510 }