Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / presentation_mode_controller.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/presentation_mode_controller.h"
6
7 #include <algorithm>
8
9 #include "base/command_line.h"
10 #import "base/mac/mac_util.h"
11 #include "base/mac/sdk_forward_declarations.h"
12 #include "chrome/browser/fullscreen.h"
13 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
14 #include "chrome/common/chrome_switches.h"
15 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h"
16 #import "ui/base/cocoa/nsview_additions.h"
17
18 NSString* const kWillEnterFullscreenNotification =
19     @"WillEnterFullscreenNotification";
20 NSString* const kWillLeaveFullscreenNotification =
21     @"WillLeaveFullscreenNotification";
22
23 namespace {
24
25 // The activation zone for the main menu is 4 pixels high; if we make it any
26 // smaller, then the menu can be made to appear without the bar sliding down.
27 const CGFloat kDropdownActivationZoneHeight = 4;
28 const NSTimeInterval kDropdownAnimationDuration = 0.12;
29 const NSTimeInterval kMouseExitCheckDelay = 0.1;
30 // This show delay attempts to match the delay for the main menu.
31 const NSTimeInterval kDropdownShowDelay = 0.3;
32 const NSTimeInterval kDropdownHideDelay = 0.2;
33
34 // The amount by which the floating bar is offset downwards (to avoid the menu)
35 // in presentation mode. (We can't use |-[NSMenu menuBarHeight]| since it
36 // returns 0 when the menu bar is hidden.)
37 const CGFloat kFloatingBarVerticalOffset = 22;
38
39 OSStatus MenuBarRevealHandler(EventHandlerCallRef handler,
40                               EventRef event,
41                               void* context) {
42   PresentationModeController* self =
43       static_cast<PresentationModeController*>(context);
44   CGFloat revealFraction = 0;
45   GetEventParameter(event,
46                     FOUR_CHAR_CODE('rvlf'),
47                     typeCGFloat,
48                     NULL,
49                     sizeof(CGFloat),
50                     NULL,
51                     &revealFraction);
52   [self setMenuBarRevealProgress:revealFraction];
53   return CallNextEventHandler(handler, event);
54 }
55
56 }  // end namespace
57
58 // Helper class to manage animations for the dropdown bar.  Calls
59 // [PresentationModeController changeToolbarFraction] once per
60 // animation step.
61 @interface DropdownAnimation : NSAnimation {
62  @private
63   PresentationModeController* controller_;
64   CGFloat startFraction_;
65   CGFloat endFraction_;
66 }
67
68 @property(readonly, nonatomic) CGFloat startFraction;
69 @property(readonly, nonatomic) CGFloat endFraction;
70
71 // Designated initializer.  Asks |controller| for the current shown fraction, so
72 // if the bar is already partially shown or partially hidden, the animation
73 // duration may be less than |fullDuration|.
74 - (id)initWithFraction:(CGFloat)fromFraction
75           fullDuration:(CGFloat)fullDuration
76         animationCurve:(NSAnimationCurve)animationCurve
77             controller:(PresentationModeController*)controller;
78
79 @end
80
81 @implementation DropdownAnimation
82
83 @synthesize startFraction = startFraction_;
84 @synthesize endFraction = endFraction_;
85
86 - (id)initWithFraction:(CGFloat)toFraction
87           fullDuration:(CGFloat)fullDuration
88         animationCurve:(NSAnimationCurve)animationCurve
89             controller:(PresentationModeController*)controller {
90   // Calculate the effective duration, based on the current shown fraction.
91   DCHECK(controller);
92   CGFloat fromFraction = controller.toolbarFraction;
93   CGFloat effectiveDuration = fabs(fullDuration * (fromFraction - toFraction));
94
95   if ((self = [super gtm_initWithDuration:effectiveDuration
96                                 eventMask:NSLeftMouseDownMask
97                            animationCurve:animationCurve])) {
98     startFraction_ = fromFraction;
99     endFraction_ = toFraction;
100     controller_ = controller;
101   }
102   return self;
103 }
104
105 // Called once per animation step.  Overridden to change the floating bar's
106 // position based on the animation's progress.
107 - (void)setCurrentProgress:(NSAnimationProgress)progress {
108   CGFloat fraction =
109       startFraction_ + (progress * (endFraction_ - startFraction_));
110   [controller_ changeToolbarFraction:fraction];
111 }
112
113 @end
114
115
116 @interface PresentationModeController (PrivateMethods)
117
118 // Updates the visibility of the menu bar and the dock.
119 - (void)updateMenuBarAndDockVisibility;
120
121 // Whether the current screen is expected to have a menu bar, regardless of
122 // current visibility of the menu bar.
123 - (BOOL)doesScreenHaveMenuBar;
124
125 // Returns YES if the window is on the primary screen.
126 - (BOOL)isWindowOnPrimaryScreen;
127
128 // Returns |kFullScreenModeHideAll| when the overlay is hidden and
129 // |kFullScreenModeHideDock| when the overlay is shown.
130 - (base::mac::FullScreenMode)desiredSystemFullscreenMode;
131
132 // Change the overlay to the given fraction, with or without animation. Only
133 // guaranteed to work properly with |fraction == 0| or |fraction == 1|. This
134 // performs the show/hide (animation) immediately. It does not touch the timers.
135 - (void)changeOverlayToFraction:(CGFloat)fraction
136                   withAnimation:(BOOL)animate;
137
138 // Schedule the floating bar to be shown/hidden because of mouse position.
139 - (void)scheduleShowForMouse;
140 - (void)scheduleHideForMouse;
141
142 // Set up the tracking area used to activate the sliding bar or keep it active
143 // using with the rectangle in |trackingAreaBounds_|, or remove the tracking
144 // area if one was previously set up.
145 - (void)setupTrackingArea;
146 - (void)removeTrackingAreaIfNecessary;
147
148 // Returns YES if the mouse is currently in any current tracking rectangle, NO
149 // otherwise.
150 - (BOOL)mouseInsideTrackingRect;
151
152 // The tracking area can "falsely" report exits when the menu slides down over
153 // it. In that case, we have to monitor for a "real" mouse exit on a timer.
154 // |-setupMouseExitCheck| schedules a check; |-cancelMouseExitCheck| cancels any
155 // scheduled check.
156 - (void)setupMouseExitCheck;
157 - (void)cancelMouseExitCheck;
158
159 // Called (after a delay) by |-setupMouseExitCheck|, to check whether the mouse
160 // has exited or not; if it hasn't, it will schedule another check.
161 - (void)checkForMouseExit;
162
163 // Start timers for showing/hiding the floating bar.
164 - (void)startShowTimer;
165 - (void)startHideTimer;
166 - (void)cancelShowTimer;
167 - (void)cancelHideTimer;
168 - (void)cancelAllTimers;
169
170 // Methods called when the show/hide timers fire. Do not call directly.
171 - (void)showTimerFire:(NSTimer*)timer;
172 - (void)hideTimerFire:(NSTimer*)timer;
173
174 // Stops any running animations, removes tracking areas, etc.
175 - (void)cleanup;
176
177 // Shows and hides the UI associated with this window being active (having main
178 // status).  This includes hiding the menu bar.  These functions are called when
179 // the window gains or loses main status as well as in |-cleanup|.
180 - (void)showActiveWindowUI;
181 - (void)hideActiveWindowUI;
182
183 // In Immersive Fullscreen, the menubar is visible iff. toolbarFraction_ >=
184 // 1.0.
185 - (BOOL)shouldShowMenubarInImmersiveFullscreen;
186
187 @end
188
189
190 @implementation PresentationModeController
191
192 @synthesize inPresentationMode = inPresentationMode_;
193 @synthesize slidingStyle = slidingStyle_;
194 @synthesize toolbarFraction = toolbarFraction_;
195
196 - (id)initWithBrowserController:(BrowserWindowController*)controller
197                           style:(fullscreen_mac::SlidingStyle)style {
198   if ((self = [super init])) {
199     browserController_ = controller;
200     systemFullscreenMode_ = base::mac::kFullScreenModeNormal;
201     slidingStyle_ = style;
202   }
203
204   // Let the world know what we're up to.
205   [[NSNotificationCenter defaultCenter]
206     postNotificationName:kWillEnterFullscreenNotification
207                   object:nil];
208
209   // Install the Carbon event handler for the undocumented menu bar show/hide
210   // event.
211   EventTypeSpec eventSpec = {kEventClassMenu, 2004};
212   InstallApplicationEventHandler(NewEventHandlerUPP(&MenuBarRevealHandler),
213                                  1,
214                                  &eventSpec,
215                                  self,
216                                  &menuBarTrackingHandler_);
217   return self;
218 }
219
220 - (void)dealloc {
221   RemoveEventHandler(menuBarTrackingHandler_);
222   DCHECK(!inPresentationMode_);
223   DCHECK(!trackingArea_);
224   [super dealloc];
225 }
226
227 - (void)enterPresentationModeForContentView:(NSView*)contentView
228                                showDropdown:(BOOL)showDropdown {
229   DCHECK(!inPresentationMode_);
230   enteringPresentationMode_ = YES;
231   inPresentationMode_ = YES;
232   contentView_ = contentView;
233   [self changeToolbarFraction:(showDropdown ? 1 : 0)];
234   [self updateMenuBarAndDockVisibility];
235
236   // Register for notifications.  Self is removed as an observer in |-cleanup|.
237   NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
238   NSWindow* window = [browserController_ window];
239
240   // Disable these notifications on Lion as they cause crashes.
241   // TODO(rohitrao): Figure out what happens if a fullscreen window changes
242   // monitors on Lion.
243   if (base::mac::IsOSSnowLeopard()) {
244     [nc addObserver:self
245            selector:@selector(windowDidChangeScreen:)
246                name:NSWindowDidChangeScreenNotification
247              object:window];
248
249     [nc addObserver:self
250            selector:@selector(windowDidMove:)
251                name:NSWindowDidMoveNotification
252              object:window];
253   }
254
255   [nc addObserver:self
256          selector:@selector(windowDidBecomeMain:)
257              name:NSWindowDidBecomeMainNotification
258            object:window];
259
260   [nc addObserver:self
261          selector:@selector(windowDidResignMain:)
262              name:NSWindowDidResignMainNotification
263            object:window];
264
265   enteringPresentationMode_ = NO;
266 }
267
268 - (void)exitPresentationMode {
269   [[NSNotificationCenter defaultCenter]
270     postNotificationName:kWillLeaveFullscreenNotification
271                   object:nil];
272   DCHECK(inPresentationMode_);
273   inPresentationMode_ = NO;
274
275   [self cleanup];
276 }
277
278 - (void)windowDidChangeScreen:(NSNotification*)notification {
279   [browserController_ resizeFullscreenWindow];
280 }
281
282 - (void)windowDidMove:(NSNotification*)notification {
283   [browserController_ resizeFullscreenWindow];
284 }
285
286 - (void)windowDidBecomeMain:(NSNotification*)notification {
287   [self showActiveWindowUI];
288 }
289
290 - (void)windowDidResignMain:(NSNotification*)notification {
291   [self hideActiveWindowUI];
292 }
293
294 // On OSX 10.8+, the menu bar shows on the secondary screen in fullscreen.
295 // On OSX 10.7, fullscreen never fills the secondary screen.
296 // On OSX 10.6, the menu bar never shows on the secondary screen in fullscreen.
297 // See http://crbug.com/388906 for full details.
298 - (CGFloat)floatingBarVerticalOffset {
299   if (base::mac::IsOSMountainLionOrLater())
300     return kFloatingBarVerticalOffset;
301   return [self isWindowOnPrimaryScreen] ? kFloatingBarVerticalOffset : 0;
302 }
303
304 - (void)overlayFrameChanged:(NSRect)frame {
305   if (!inPresentationMode_)
306     return;
307
308   // Make sure |trackingAreaBounds_| always reflects either the tracking area or
309   // the desired tracking area.
310   trackingAreaBounds_ = frame;
311   // The tracking area should always be at least the height of activation zone.
312   NSRect contentBounds = [contentView_ bounds];
313   trackingAreaBounds_.origin.y =
314       std::min(trackingAreaBounds_.origin.y,
315                NSMaxY(contentBounds) - kDropdownActivationZoneHeight);
316   trackingAreaBounds_.size.height =
317       NSMaxY(contentBounds) - trackingAreaBounds_.origin.y + 1;
318
319   // If an animation is currently running, do not set up a tracking area now.
320   // Instead, leave it to be created it in |-animationDidEnd:|.
321   if (currentAnimation_)
322     return;
323
324   // If this is part of the initial setup, lock bar visibility if the mouse is
325   // within the tracking area bounds.
326   if (enteringPresentationMode_ && [self mouseInsideTrackingRect])
327     [browserController_ lockBarVisibilityForOwner:self
328                                     withAnimation:NO
329                                             delay:NO];
330   [self setupTrackingArea];
331 }
332
333 - (void)ensureOverlayShownWithAnimation:(BOOL)animate delay:(BOOL)delay {
334   if (!inPresentationMode_)
335     return;
336
337   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
338     return;
339
340   if (self.slidingStyle == fullscreen_mac::OMNIBOX_TABS_PRESENT)
341     return;
342
343   if (animate) {
344     if (delay) {
345       [self startShowTimer];
346     } else {
347       [self cancelAllTimers];
348       [self changeOverlayToFraction:1 withAnimation:YES];
349     }
350   } else {
351     DCHECK(!delay);
352     [self cancelAllTimers];
353     [self changeOverlayToFraction:1 withAnimation:NO];
354   }
355 }
356
357 - (void)ensureOverlayHiddenWithAnimation:(BOOL)animate delay:(BOOL)delay {
358   if (!inPresentationMode_)
359     return;
360
361   if (self.slidingStyle == fullscreen_mac::OMNIBOX_TABS_PRESENT)
362     return;
363
364   if (animate) {
365     if (delay) {
366       [self startHideTimer];
367     } else {
368       [self cancelAllTimers];
369       [self changeOverlayToFraction:0 withAnimation:YES];
370     }
371   } else {
372     DCHECK(!delay);
373     [self cancelAllTimers];
374     [self changeOverlayToFraction:0 withAnimation:NO];
375   }
376 }
377
378 - (void)cancelAnimationAndTimers {
379   [self cancelAllTimers];
380   [currentAnimation_ stopAnimation];
381   currentAnimation_.reset();
382 }
383
384 - (void)setSystemFullscreenModeTo:(base::mac::FullScreenMode)mode {
385   if (mode == systemFullscreenMode_)
386     return;
387   if (systemFullscreenMode_ == base::mac::kFullScreenModeNormal)
388     base::mac::RequestFullScreen(mode);
389   else if (mode == base::mac::kFullScreenModeNormal)
390     base::mac::ReleaseFullScreen(systemFullscreenMode_);
391   else
392     base::mac::SwitchFullScreenModes(systemFullscreenMode_, mode);
393   systemFullscreenMode_ = mode;
394 }
395
396 - (void)changeToolbarFraction:(CGFloat)fraction {
397   toolbarFraction_ = fraction;
398   [browserController_ layoutSubviews];
399
400   // In AppKit fullscreen, moving the mouse to the top of the screen toggles
401   // menu visibility. Replicate the same effect for immersive fullscreen.
402   if ([browserController_ isInImmersiveFullscreen])
403     [self updateMenuBarAndDockVisibility];
404 }
405
406 // This method works, but is fragile.
407 //
408 // It gets used during view layout, which sometimes needs to be done at the
409 // beginning of an animation. As such, this method needs to reflect the
410 // menubarOffset expected at the end of the animation. This information is not
411 // readily available. (The layout logic needs a refactor).
412 //
413 // For AppKit Fullscreen, the menubar always starts hidden, and
414 // menubarFraction_ always starts at 0, so the logic happens to work. For
415 // Immersive Fullscreen, this class controls the visibility of the menu bar, so
416 // the logic is correct and not fragile.
417 - (CGFloat)menubarOffset {
418   if ([browserController_ isInAppKitFullscreen])
419     return -std::floor(menubarFraction_ * [self floatingBarVerticalOffset]);
420
421   return [self shouldShowMenubarInImmersiveFullscreen]
422              ? -[self floatingBarVerticalOffset]
423              : 0;
424 }
425
426 // Used to activate the floating bar in presentation mode.
427 - (void)mouseEntered:(NSEvent*)event {
428   DCHECK(inPresentationMode_);
429
430   // Having gotten a mouse entered, we no longer need to do exit checks.
431   [self cancelMouseExitCheck];
432
433   NSTrackingArea* trackingArea = [event trackingArea];
434   if (trackingArea == trackingArea_) {
435     // The tracking area shouldn't be active during animation.
436     DCHECK(!currentAnimation_);
437     [self scheduleShowForMouse];
438   }
439 }
440
441 // Used to deactivate the floating bar in presentation mode.
442 - (void)mouseExited:(NSEvent*)event {
443   DCHECK(inPresentationMode_);
444
445   NSTrackingArea* trackingArea = [event trackingArea];
446   if (trackingArea == trackingArea_) {
447     // The tracking area shouldn't be active during animation.
448     DCHECK(!currentAnimation_);
449
450     // We can get a false mouse exit when the menu slides down, so if the mouse
451     // is still actually over the tracking area, we ignore the mouse exit, but
452     // we set up to check the mouse position again after a delay.
453     if ([self mouseInsideTrackingRect]) {
454       [self setupMouseExitCheck];
455       return;
456     }
457
458     [self scheduleHideForMouse];
459   }
460 }
461
462 - (void)animationDidStop:(NSAnimation*)animation {
463   // Reset the |currentAnimation_| pointer now that the animation is over.
464   currentAnimation_.reset();
465
466   // Invariant says that the tracking area is not installed while animations are
467   // in progress. Ensure this is true.
468   DCHECK(!trackingArea_);
469   [self removeTrackingAreaIfNecessary];  // For paranoia.
470
471   // Don't automatically set up a new tracking area. When explicitly stopped,
472   // either another animation is going to start immediately or the state will be
473   // changed immediately.
474 }
475
476 - (void)animationDidEnd:(NSAnimation*)animation {
477   [self animationDidStop:animation];
478
479   // |trackingAreaBounds_| contains the correct tracking area bounds, including
480   // |any updates that may have come while the animation was running. Install a
481   // new tracking area with these bounds.
482   [self setupTrackingArea];
483
484   // TODO(viettrungluu): Better would be to check during the animation; doing it
485   // here means that the timing is slightly off.
486   if (![self mouseInsideTrackingRect])
487     [self scheduleHideForMouse];
488 }
489
490 - (void)setMenuBarRevealProgress:(CGFloat)progress {
491   menubarFraction_ = progress;
492
493   // If an animation is not running, then -layoutSubviews will not be called
494   // for each tick of the menu bar reveal. Do that manually.
495   // TODO(erikchen): The animation is janky. layoutSubviews need a refactor so
496   // that it calls setFrameOffset: instead of setFrame: if the frame's size has
497   // not changed.
498   if (!currentAnimation_.get())
499     [browserController_ layoutSubviews];
500 }
501
502 @end
503
504
505 @implementation PresentationModeController (PrivateMethods)
506
507 - (void)updateMenuBarAndDockVisibility {
508   if (![[browserController_ window] isMainWindow] ||
509       ![browserController_ isInImmersiveFullscreen]) {
510     [self setSystemFullscreenModeTo:base::mac::kFullScreenModeNormal];
511     return;
512   }
513
514   // The screen does not have a menu bar, so there's no need to hide it.
515   if (![self doesScreenHaveMenuBar]) {
516     [self setSystemFullscreenModeTo:base::mac::kFullScreenModeHideDock];
517     return;
518   }
519
520   [self setSystemFullscreenModeTo:[self desiredSystemFullscreenMode]];
521 }
522
523 - (BOOL)doesScreenHaveMenuBar {
524   if (![[NSScreen class]
525           respondsToSelector:@selector(screensHaveSeparateSpaces)])
526     return [self isWindowOnPrimaryScreen];
527
528   BOOL eachScreenShouldHaveMenuBar = [NSScreen screensHaveSeparateSpaces];
529   return eachScreenShouldHaveMenuBar ?: [self isWindowOnPrimaryScreen];
530 }
531
532 - (BOOL)isWindowOnPrimaryScreen {
533   NSScreen* screen = [[browserController_ window] screen];
534   NSScreen* primaryScreen = [[NSScreen screens] objectAtIndex:0];
535   return (screen == primaryScreen);
536 }
537
538 - (base::mac::FullScreenMode)desiredSystemFullscreenMode {
539   if ([self shouldShowMenubarInImmersiveFullscreen])
540     return base::mac::kFullScreenModeHideDock;
541   return base::mac::kFullScreenModeHideAll;
542 }
543
544 - (void)changeOverlayToFraction:(CGFloat)fraction
545                   withAnimation:(BOOL)animate {
546   // The non-animated case is really simple, so do it and return.
547   if (!animate) {
548     [currentAnimation_ stopAnimation];
549     [self changeToolbarFraction:fraction];
550     return;
551   }
552
553   // If we're already animating to the given fraction, then there's nothing more
554   // to do.
555   if (currentAnimation_ && [currentAnimation_ endFraction] == fraction)
556     return;
557
558   // In all other cases, we want to cancel any running animation (which may be
559   // to show or to hide).
560   [currentAnimation_ stopAnimation];
561
562   // Create the animation and set it up.
563   currentAnimation_.reset(
564       [[DropdownAnimation alloc] initWithFraction:fraction
565                                      fullDuration:kDropdownAnimationDuration
566                                    animationCurve:NSAnimationEaseOut
567                                        controller:self]);
568   DCHECK(currentAnimation_);
569   [currentAnimation_ setAnimationBlockingMode:NSAnimationNonblocking];
570   [currentAnimation_ setDelegate:self];
571
572   // If there is an existing tracking area, remove it. We do not track mouse
573   // movements during animations (see class comment in the header file).
574   [self removeTrackingAreaIfNecessary];
575
576   [currentAnimation_ startAnimation];
577 }
578
579 - (void)scheduleShowForMouse {
580   [browserController_ lockBarVisibilityForOwner:self
581                                   withAnimation:YES
582                                           delay:YES];
583 }
584
585 - (void)scheduleHideForMouse {
586   [browserController_ releaseBarVisibilityForOwner:self
587                                      withAnimation:YES
588                                              delay:YES];
589 }
590
591 - (void)setupTrackingArea {
592   if (trackingArea_) {
593     // If the tracking rectangle is already |trackingAreaBounds_|, quit early.
594     NSRect oldRect = [trackingArea_ rect];
595     if (NSEqualRects(trackingAreaBounds_, oldRect))
596       return;
597
598     // Otherwise, remove it.
599     [self removeTrackingAreaIfNecessary];
600   }
601
602   // Create and add a new tracking area for |frame|.
603   trackingArea_.reset(
604       [[NSTrackingArea alloc] initWithRect:trackingAreaBounds_
605                                    options:NSTrackingMouseEnteredAndExited |
606                                            NSTrackingActiveInKeyWindow
607                                      owner:self
608                                   userInfo:nil]);
609   DCHECK(contentView_);
610   [contentView_ addTrackingArea:trackingArea_];
611 }
612
613 - (void)removeTrackingAreaIfNecessary {
614   if (trackingArea_) {
615     DCHECK(contentView_);  // |contentView_| better be valid.
616     [contentView_ removeTrackingArea:trackingArea_];
617     trackingArea_.reset();
618   }
619 }
620
621 - (BOOL)mouseInsideTrackingRect {
622   NSWindow* window = [browserController_ window];
623   NSPoint mouseLoc = [window mouseLocationOutsideOfEventStream];
624   NSPoint mousePos = [contentView_ convertPoint:mouseLoc fromView:nil];
625   return NSMouseInRect(mousePos, trackingAreaBounds_, [contentView_ isFlipped]);
626 }
627
628 - (void)setupMouseExitCheck {
629   [self performSelector:@selector(checkForMouseExit)
630              withObject:nil
631              afterDelay:kMouseExitCheckDelay];
632 }
633
634 - (void)cancelMouseExitCheck {
635   [NSObject cancelPreviousPerformRequestsWithTarget:self
636       selector:@selector(checkForMouseExit) object:nil];
637 }
638
639 - (void)checkForMouseExit {
640   if ([self mouseInsideTrackingRect])
641     [self setupMouseExitCheck];
642   else
643     [self scheduleHideForMouse];
644 }
645
646 - (void)startShowTimer {
647   // If there's already a show timer going, just keep it.
648   if (showTimer_) {
649     DCHECK([showTimer_ isValid]);
650     DCHECK(!hideTimer_);
651     return;
652   }
653
654   // Cancel the hide timer (if necessary) and set up the new show timer.
655   [self cancelHideTimer];
656   showTimer_.reset(
657       [[NSTimer scheduledTimerWithTimeInterval:kDropdownShowDelay
658                                         target:self
659                                       selector:@selector(showTimerFire:)
660                                       userInfo:nil
661                                        repeats:NO] retain]);
662   DCHECK([showTimer_ isValid]);  // This also checks that |showTimer_ != nil|.
663 }
664
665 - (void)startHideTimer {
666   // If there's already a hide timer going, just keep it.
667   if (hideTimer_) {
668     DCHECK([hideTimer_ isValid]);
669     DCHECK(!showTimer_);
670     return;
671   }
672
673   // Cancel the show timer (if necessary) and set up the new hide timer.
674   [self cancelShowTimer];
675   hideTimer_.reset(
676       [[NSTimer scheduledTimerWithTimeInterval:kDropdownHideDelay
677                                         target:self
678                                       selector:@selector(hideTimerFire:)
679                                       userInfo:nil
680                                        repeats:NO] retain]);
681   DCHECK([hideTimer_ isValid]);  // This also checks that |hideTimer_ != nil|.
682 }
683
684 - (void)cancelShowTimer {
685   [showTimer_ invalidate];
686   showTimer_.reset();
687 }
688
689 - (void)cancelHideTimer {
690   [hideTimer_ invalidate];
691   hideTimer_.reset();
692 }
693
694 - (void)cancelAllTimers {
695   [self cancelShowTimer];
696   [self cancelHideTimer];
697 }
698
699 - (void)showTimerFire:(NSTimer*)timer {
700   DCHECK_EQ(showTimer_, timer);  // This better be our show timer.
701   [showTimer_ invalidate];       // Make sure it doesn't repeat.
702   showTimer_.reset();            // And get rid of it.
703   [self changeOverlayToFraction:1 withAnimation:YES];
704 }
705
706 - (void)hideTimerFire:(NSTimer*)timer {
707   DCHECK_EQ(hideTimer_, timer);  // This better be our hide timer.
708   [hideTimer_ invalidate];       // Make sure it doesn't repeat.
709   hideTimer_.reset();            // And get rid of it.
710   [self changeOverlayToFraction:0 withAnimation:YES];
711 }
712
713 - (void)cleanup {
714   [self cancelMouseExitCheck];
715   [self cancelAnimationAndTimers];
716   [[NSNotificationCenter defaultCenter] removeObserver:self];
717
718   [self removeTrackingAreaIfNecessary];
719   contentView_ = nil;
720
721   // This isn't tracked when not in presentation mode.
722   [browserController_ releaseBarVisibilityForOwner:self
723                                      withAnimation:NO
724                                              delay:NO];
725
726   // Call the main status resignation code to perform the associated cleanup,
727   // since we will no longer be receiving actual status resignation
728   // notifications.
729   [self setSystemFullscreenModeTo:base::mac::kFullScreenModeNormal];
730
731   // No more calls back up to the BWC.
732   browserController_ = nil;
733 }
734
735 - (void)showActiveWindowUI {
736   [self updateMenuBarAndDockVisibility];
737
738   // TODO(rohitrao): Insert the Exit Fullscreen button.  http://crbug.com/35956
739 }
740
741 - (void)hideActiveWindowUI {
742   [self updateMenuBarAndDockVisibility];
743
744   // TODO(rohitrao): Remove the Exit Fullscreen button.  http://crbug.com/35956
745 }
746
747 - (BOOL)shouldShowMenubarInImmersiveFullscreen {
748   return toolbarFraction_ >= 1.0;
749 }
750
751 @end