Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / presentation_mode_controller.mm
index 110629e..d92fb7b 100644 (file)
@@ -8,11 +8,12 @@
 
 #include "base/command_line.h"
 #import "base/mac/mac_util.h"
+#include "base/mac/sdk_forward_declarations.h"
 #include "chrome/browser/fullscreen.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
-#import "chrome/browser/ui/cocoa/nsview_additions.h"
 #include "chrome/common/chrome_switches.h"
 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h"
+#import "ui/base/cocoa/nsview_additions.h"
 
 NSString* const kWillEnterFullscreenNotification =
     @"WillEnterFullscreenNotification";
@@ -20,6 +21,7 @@ NSString* const kWillLeaveFullscreenNotification =
     @"WillLeaveFullscreenNotification";
 
 namespace {
+
 // The activation zone for the main menu is 4 pixels high; if we make it any
 // smaller, then the menu can be made to appear without the bar sliding down.
 const CGFloat kDropdownActivationZoneHeight = 4;
@@ -34,11 +36,27 @@ const NSTimeInterval kDropdownHideDelay = 0.2;
 // returns 0 when the menu bar is hidden.)
 const CGFloat kFloatingBarVerticalOffset = 22;
 
-}  // end namespace
+OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
+                              EventRef event,
+                              void* context) {
+  PresentationModeController* self =
+      static_cast<PresentationModeController*>(context);
+  CGFloat revealFraction = 0;
+  GetEventParameter(event,
+                    FOUR_CHAR_CODE('rvlf'),
+                    typeCGFloat,
+                    NULL,
+                    sizeof(CGFloat),
+                    NULL,
+                    &revealFraction);
+  [self setMenuBarRevealProgress:revealFraction];
+  return CallNextEventHandler(handler, event);
+}
 
+}  // end namespace
 
 // Helper class to manage animations for the dropdown bar.  Calls
-// [PresentationModeController changeFloatingBarShownFraction] once per
+// [PresentationModeController changeToolbarFraction] once per
 // animation step.
 @interface DropdownAnimation : NSAnimation {
  @private
@@ -71,7 +89,7 @@ const CGFloat kFloatingBarVerticalOffset = 22;
             controller:(PresentationModeController*)controller {
   // Calculate the effective duration, based on the current shown fraction.
   DCHECK(controller);
-  CGFloat fromFraction = [controller floatingBarShownFraction];
+  CGFloat fromFraction = controller.toolbarFraction;
   CGFloat effectiveDuration = fabs(fullDuration * (fromFraction - toFraction));
 
   if ((self = [super gtm_initWithDuration:effectiveDuration
@@ -89,7 +107,7 @@ const CGFloat kFloatingBarVerticalOffset = 22;
 - (void)setCurrentProgress:(NSAnimationProgress)progress {
   CGFloat fraction =
       startFraction_ + (progress * (endFraction_ - startFraction_));
-  [controller_ changeFloatingBarShownFraction:fraction];
+  [controller_ changeToolbarFraction:fraction];
 }
 
 @end
@@ -97,14 +115,16 @@ const CGFloat kFloatingBarVerticalOffset = 22;
 
 @interface PresentationModeController (PrivateMethods)
 
+// Updates the visibility of the menu bar and the dock.
+- (void)updateMenuBarAndDockVisibility;
+
+// Whether the current screen is expected to have a menu bar, regardless of
+// current visibility of the menu bar.
+- (BOOL)doesScreenHaveMenuBar;
+
 // Returns YES if the window is on the primary screen.
 - (BOOL)isWindowOnPrimaryScreen;
 
-// Returns YES if it is ok to show and hide the menu bar in response to the
-// overlay opening and closing.  Will return NO if the window is not main or not
-// on the primary monitor.
-- (BOOL)shouldToggleMenuBar;
-
 // Returns |kFullScreenModeHideAll| when the overlay is hidden and
 // |kFullScreenModeHideDock| when the overlay is shown.
 - (base::mac::FullScreenMode)desiredSystemFullscreenMode;
@@ -160,17 +180,25 @@ const CGFloat kFloatingBarVerticalOffset = 22;
 - (void)showActiveWindowUI;
 - (void)hideActiveWindowUI;
 
+// In Immersive Fullscreen, the menubar is visible iff. toolbarFraction_ >=
+// 1.0.
+- (BOOL)shouldShowMenubarInImmersiveFullscreen;
+
 @end
 
 
 @implementation PresentationModeController
 
 @synthesize inPresentationMode = inPresentationMode_;
+@synthesize slidingStyle = slidingStyle_;
+@synthesize toolbarFraction = toolbarFraction_;
 
-- (id)initWithBrowserController:(BrowserWindowController*)controller {
+- (id)initWithBrowserController:(BrowserWindowController*)controller
+                          style:(fullscreen_mac::SlidingStyle)style {
   if ((self = [super init])) {
     browserController_ = controller;
     systemFullscreenMode_ = base::mac::kFullScreenModeNormal;
+    slidingStyle_ = style;
   }
 
   // Let the world know what we're up to.
@@ -178,10 +206,19 @@ const CGFloat kFloatingBarVerticalOffset = 22;
     postNotificationName:kWillEnterFullscreenNotification
                   object:nil];
 
+  // Install the Carbon event handler for the undocumented menu bar show/hide
+  // event.
+  EventTypeSpec eventSpec = {kEventClassMenu, 2004};
+  InstallApplicationEventHandler(NewEventHandlerUPP(&MenuBarRevealHandler),
+                                 1,
+                                 &eventSpec,
+                                 self,
+                                 &menuBarTrackingHandler_);
   return self;
 }
 
 - (void)dealloc {
+  RemoveEventHandler(menuBarTrackingHandler_);
   DCHECK(!inPresentationMode_);
   DCHECK(!trackingArea_);
   [super dealloc];
@@ -193,7 +230,8 @@ const CGFloat kFloatingBarVerticalOffset = 22;
   enteringPresentationMode_ = YES;
   inPresentationMode_ = YES;
   contentView_ = contentView;
-  [self changeFloatingBarShownFraction:(showDropdown ? 1 : 0)];
+  [self changeToolbarFraction:(showDropdown ? 1 : 0)];
+  [self updateMenuBarAndDockVisibility];
 
   // Register for notifications.  Self is removed as an observer in |-cleanup|.
   NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
@@ -299,6 +337,9 @@ const CGFloat kFloatingBarVerticalOffset = 22;
   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
     return;
 
+  if (self.slidingStyle == fullscreen_mac::OMNIBOX_TABS_PRESENT)
+    return;
+
   if (animate) {
     if (delay) {
       [self startShowTimer];
@@ -317,6 +358,9 @@ const CGFloat kFloatingBarVerticalOffset = 22;
   if (!inPresentationMode_)
     return;
 
+  if (self.slidingStyle == fullscreen_mac::OMNIBOX_TABS_PRESENT)
+    return;
+
   if (animate) {
     if (delay) {
       [self startHideTimer];
@@ -337,10 +381,6 @@ const CGFloat kFloatingBarVerticalOffset = 22;
   currentAnimation_.reset();
 }
 
-- (CGFloat)floatingBarShownFraction {
-  return [browserController_ floatingBarShownFraction];
-}
-
 - (void)setSystemFullscreenModeTo:(base::mac::FullScreenMode)mode {
   if (mode == systemFullscreenMode_)
     return;
@@ -353,11 +393,34 @@ const CGFloat kFloatingBarVerticalOffset = 22;
   systemFullscreenMode_ = mode;
 }
 
-- (void)changeFloatingBarShownFraction:(CGFloat)fraction {
-  [browserController_ setFloatingBarShownFraction:fraction];
+- (void)changeToolbarFraction:(CGFloat)fraction {
+  toolbarFraction_ = fraction;
+  [browserController_ layoutSubviews];
+
+  // In AppKit fullscreen, moving the mouse to the top of the screen toggles
+  // menu visibility. Replicate the same effect for immersive fullscreen.
+  if ([browserController_ isInImmersiveFullscreen])
+    [self updateMenuBarAndDockVisibility];
+}
+
+// This method works, but is fragile.
+//
+// It gets used during view layout, which sometimes needs to be done at the
+// beginning of an animation. As such, this method needs to reflect the
+// menubarOffset expected at the end of the animation. This information is not
+// readily available. (The layout logic needs a refactor).
+//
+// For AppKit Fullscreen, the menubar always starts hidden, and
+// menubarFraction_ always starts at 0, so the logic happens to work. For
+// Immersive Fullscreen, this class controls the visibility of the menu bar, so
+// the logic is correct and not fragile.
+- (CGFloat)menubarOffset {
+  if ([browserController_ isInAppKitFullscreen])
+    return -std::floor(menubarFraction_ * [self floatingBarVerticalOffset]);
 
-  if ([self shouldToggleMenuBar])
-    [self setSystemFullscreenModeTo:[self desiredSystemFullscreenMode]];
+  return [self shouldShowMenubarInImmersiveFullscreen]
+             ? -[self floatingBarVerticalOffset]
+             : 0;
 }
 
 // Used to activate the floating bar in presentation mode.
@@ -424,25 +487,56 @@ const CGFloat kFloatingBarVerticalOffset = 22;
     [self scheduleHideForMouse];
 }
 
+- (void)setMenuBarRevealProgress:(CGFloat)progress {
+  menubarFraction_ = progress;
+
+  // If an animation is not running, then -layoutSubviews will not be called
+  // for each tick of the menu bar reveal. Do that manually.
+  // TODO(erikchen): The animation is janky. layoutSubviews need a refactor so
+  // that it calls setFrameOffset: instead of setFrame: if the frame's size has
+  // not changed.
+  if (!currentAnimation_.get())
+    [browserController_ layoutSubviews];
+}
+
 @end
 
 
 @implementation PresentationModeController (PrivateMethods)
 
+- (void)updateMenuBarAndDockVisibility {
+  if (![[browserController_ window] isMainWindow] ||
+      ![browserController_ isInImmersiveFullscreen]) {
+    [self setSystemFullscreenModeTo:base::mac::kFullScreenModeNormal];
+    return;
+  }
+
+  // The screen does not have a menu bar, so there's no need to hide it.
+  if (![self doesScreenHaveMenuBar]) {
+    [self setSystemFullscreenModeTo:base::mac::kFullScreenModeHideDock];
+    return;
+  }
+
+  [self setSystemFullscreenModeTo:[self desiredSystemFullscreenMode]];
+}
+
+- (BOOL)doesScreenHaveMenuBar {
+  if (![[NSScreen class]
+          respondsToSelector:@selector(screensHaveSeparateSpaces)])
+    return [self isWindowOnPrimaryScreen];
+
+  BOOL eachScreenShouldHaveMenuBar = [NSScreen screensHaveSeparateSpaces];
+  return eachScreenShouldHaveMenuBar ?: [self isWindowOnPrimaryScreen];
+}
+
 - (BOOL)isWindowOnPrimaryScreen {
   NSScreen* screen = [[browserController_ window] screen];
   NSScreen* primaryScreen = [[NSScreen screens] objectAtIndex:0];
   return (screen == primaryScreen);
 }
 
-- (BOOL)shouldToggleMenuBar {
-  return [browserController_ isInImmersiveFullscreen] &&
-         [self isWindowOnPrimaryScreen] &&
-         [[browserController_ window] isMainWindow];
-}
-
 - (base::mac::FullScreenMode)desiredSystemFullscreenMode {
-  if ([browserController_ floatingBarShownFraction] >= 1.0)
+  if ([self shouldShowMenubarInImmersiveFullscreen])
     return base::mac::kFullScreenModeHideDock;
   return base::mac::kFullScreenModeHideAll;
 }
@@ -452,7 +546,7 @@ const CGFloat kFloatingBarVerticalOffset = 22;
   // The non-animated case is really simple, so do it and return.
   if (!animate) {
     [currentAnimation_ stopAnimation];
-    [self changeFloatingBarShownFraction:fraction];
+    [self changeToolbarFraction:fraction];
     return;
   }
 
@@ -465,11 +559,6 @@ const CGFloat kFloatingBarVerticalOffset = 22;
   // to show or to hide).
   [currentAnimation_ stopAnimation];
 
-  // Now, if it happens to already be in the right state, there's nothing more
-  // to do.
-  if ([browserController_ floatingBarShownFraction] == fraction)
-    return;
-
   // Create the animation and set it up.
   currentAnimation_.reset(
       [[DropdownAnimation alloc] initWithFraction:fraction
@@ -637,28 +726,26 @@ const CGFloat kFloatingBarVerticalOffset = 22;
   // Call the main status resignation code to perform the associated cleanup,
   // since we will no longer be receiving actual status resignation
   // notifications.
-  [self hideActiveWindowUI];
+  [self setSystemFullscreenModeTo:base::mac::kFullScreenModeNormal];
 
   // No more calls back up to the BWC.
   browserController_ = nil;
 }
 
 - (void)showActiveWindowUI {
-  DCHECK_EQ(systemFullscreenMode_, base::mac::kFullScreenModeNormal);
-  if (systemFullscreenMode_ != base::mac::kFullScreenModeNormal)
-    return;
-
-  if ([self shouldToggleMenuBar])
-    [self setSystemFullscreenModeTo:[self desiredSystemFullscreenMode]];
+  [self updateMenuBarAndDockVisibility];
 
   // TODO(rohitrao): Insert the Exit Fullscreen button.  http://crbug.com/35956
 }
 
 - (void)hideActiveWindowUI {
-  if ([self shouldToggleMenuBar])
-    [self setSystemFullscreenModeTo:base::mac::kFullScreenModeNormal];
+  [self updateMenuBarAndDockVisibility];
 
   // TODO(rohitrao): Remove the Exit Fullscreen button.  http://crbug.com/35956
 }
 
+- (BOOL)shouldShowMenubarInImmersiveFullscreen {
+  return toolbarFraction_ >= 1.0;
+}
+
 @end