Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2009, 2010, 2011 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #import "config.h"
     27 
     28 #if ENABLE(FULLSCREEN_API)
     29 
     30 #import "WKFullScreenWindowController.h"
     31 
     32 #import "LayerTreeContext.h"
     33 #import "WKAPICast.h"
     34 #import "WKViewInternal.h"
     35 #import "WebFullScreenManagerProxy.h"
     36 #import "WebPageProxy.h"
     37 #import <Carbon/Carbon.h> // For SetSystemUIMode()
     38 #import <IOKit/pwr_mgt/IOPMLib.h> // For IOPMAssertionCreate()
     39 #import <QuartzCore/QuartzCore.h>
     40 #import <WebCore/FloatRect.h>
     41 #import <WebCore/IntRect.h>
     42 #import <WebKitSystemInterface.h>
     43 
     44 static const NSTimeInterval tickleTimerInterval = 1.0;
     45 
     46 using namespace WebKit;
     47 using namespace WebCore;
     48 
     49 #if defined(BUILDING_ON_LEOPARD)
     50 @interface CATransaction(SnowLeopardConvenienceFunctions)
     51 + (void)setDisableActions:(BOOL)flag;
     52 + (void)setAnimationDuration:(CFTimeInterval)dur;
     53 @end
     54 
     55 @implementation CATransaction(SnowLeopardConvenienceFunctions)
     56 + (void)setDisableActions:(BOOL)flag
     57 {
     58     [self setValue:[NSNumber numberWithBool:flag] forKey:kCATransactionDisableActions];
     59 }
     60 
     61 + (void)setAnimationDuration:(CFTimeInterval)dur
     62 {
     63     [self setValue:[NSNumber numberWithDouble:dur] forKey:kCATransactionAnimationDuration];
     64 }
     65 @end
     66 
     67 #endif
     68 
     69 @interface WKFullScreenWindow : NSWindow
     70 {
     71     NSView* _animationView;
     72     CALayer* _backgroundLayer;
     73 }
     74 - (CALayer*)backgroundLayer;
     75 - (NSView*)animationView;
     76 @end
     77 
     78 @interface WKFullScreenWindowController(Private)
     79 - (void)_requestExitFullScreenWithAnimation:(BOOL)animation;
     80 - (void)_updateMenuAndDockForFullScreen;
     81 - (void)_updatePowerAssertions;
     82 - (WKFullScreenWindow *)_fullScreenWindow;
     83 - (CFTimeInterval)_animationDuration;
     84 - (void)_swapView:(NSView*)view with:(NSView*)otherView;
     85 - (WebFullScreenManagerProxy*)_manager;
     86 @end
     87 
     88 @interface NSWindow(IsOnActiveSpaceAdditionForTigerAndLeopard)
     89 - (BOOL)isOnActiveSpace;
     90 @end
     91 
     92 @implementation WKFullScreenWindowController
     93 
     94 #pragma mark -
     95 #pragma mark Initialization
     96 - (id)init
     97 {
     98     NSWindow *window = [[WKFullScreenWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
     99     self = [super initWithWindow:window];
    100     [window release];
    101     if (!self)
    102         return nil;
    103     [self windowDidLoad];
    104 
    105     return self;
    106 }
    107 
    108 - (void)dealloc
    109 {
    110     [self setWebView:nil];
    111 
    112     [NSObject cancelPreviousPerformRequestsWithTarget:self];
    113 
    114     [[NSNotificationCenter defaultCenter] removeObserver:self];
    115     [super dealloc];
    116 }
    117 
    118 - (void)windowDidLoad
    119 {
    120     [super windowDidLoad];
    121 
    122     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidResignActive:) name:NSApplicationDidResignActiveNotification object:NSApp];
    123     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeScreenParameters:) name:NSApplicationDidChangeScreenParametersNotification object:NSApp];
    124 }
    125 
    126 #pragma mark -
    127 #pragma mark Accessors
    128 
    129 - (WKView*)webView
    130 {
    131     return _webView;
    132 }
    133 
    134 - (void)setWebView:(WKView *)webView
    135 {
    136     [webView retain];
    137     [_webView release];
    138     _webView = webView;
    139 }
    140 
    141 #pragma mark -
    142 #pragma mark Notifications
    143 
    144 - (void)applicationDidResignActive:(NSNotification*)notification
    145 {
    146     // Check to see if the fullScreenWindow is on the active space; this function is available
    147     // on 10.6 and later, so default to YES if the function is not available:
    148     NSWindow* fullScreenWindow = [self _fullScreenWindow];
    149     BOOL isOnActiveSpace = ([fullScreenWindow respondsToSelector:@selector(isOnActiveSpace)] ? [fullScreenWindow isOnActiveSpace] : YES);
    150 
    151     // Replicate the QuickTime Player (X) behavior when losing active application status:
    152     // Is the fullScreen screen the main screen? (Note: this covers the case where only a
    153     // single screen is available.)  Is the fullScreen screen on the current space? IFF so,
    154     // then exit fullScreen mode.
    155     if ([fullScreenWindow screen] == [[NSScreen screens] objectAtIndex:0] && isOnActiveSpace)
    156         [self _requestExitFullScreenWithAnimation:NO];
    157 }
    158 
    159 - (void)applicationDidChangeScreenParameters:(NSNotification*)notification
    160 {
    161     // The user may have changed the main screen by moving the menu bar, or they may have changed
    162     // the Dock's size or location, or they may have changed the fullScreen screen's dimensions.
    163     // Update our presentation parameters, and ensure that the full screen window occupies the
    164     // entire screen:
    165     [self _updateMenuAndDockForFullScreen];
    166     NSWindow* window = [self window];
    167     [window setFrame:[[window screen] frame] display:YES];
    168 }
    169 
    170 #pragma mark -
    171 #pragma mark Exposed Interface
    172 
    173 - (void)enterFullScreen:(NSScreen *)screen
    174 {
    175     if (_isFullScreen)
    176         return;
    177 
    178     _isFullScreen = YES;
    179     _isAnimating = YES;
    180 
    181     NSDisableScreenUpdates();
    182 
    183     if (!screen)
    184         screen = [NSScreen mainScreen];
    185     NSRect screenFrame = [screen frame];
    186 
    187     NSRect webViewFrame = [_webView convertRectToBase:[_webView frame]];
    188     webViewFrame.origin = [[_webView window] convertBaseToScreen:webViewFrame.origin];
    189 
    190     // In the case of a multi-monitor setup where the webView straddles two
    191     // monitors, we must create a window large enough to contain the destination
    192     // frame and the initial frame.
    193     NSRect windowFrame = NSUnionRect(screenFrame, webViewFrame);
    194     [[self window] setFrame:windowFrame display:YES];
    195 
    196     CALayer* backgroundLayer = [[self _fullScreenWindow] backgroundLayer];
    197     NSRect backgroundFrame = {[[self window] convertScreenToBase:screenFrame.origin], screenFrame.size};
    198     backgroundFrame = [[[self window] contentView] convertRectFromBase:backgroundFrame];
    199 
    200     [CATransaction begin];
    201     [CATransaction setDisableActions:YES];
    202     [backgroundLayer setFrame:NSRectToCGRect(backgroundFrame)];
    203     [CATransaction commit];
    204 
    205     CFTimeInterval duration = [self _animationDuration];
    206     [self _manager]->willEnterFullScreen();
    207     [self _manager]->beginEnterFullScreenAnimation(duration);
    208 }
    209 
    210 - (void)beganEnterFullScreenAnimation
    211 {
    212     [self _updateMenuAndDockForFullScreen];
    213     [self _updatePowerAssertions];
    214 
    215     // In a previous incarnation, the NSWindow attached to this controller may have
    216     // been on a different screen. Temporarily change the collectionBehavior of the window:
    217     NSWindow* fullScreenWindow = [self window];
    218     NSWindowCollectionBehavior behavior = [fullScreenWindow collectionBehavior];
    219     [fullScreenWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
    220     [fullScreenWindow makeKeyAndOrderFront:self];
    221     [fullScreenWindow setCollectionBehavior:behavior];
    222 
    223     // Start the opacity animation. We can use implicit animations here because we don't care when
    224     // the animation finishes.
    225     [CATransaction begin];
    226     [CATransaction setAnimationDuration:[self _animationDuration]];
    227     [[[self _fullScreenWindow] backgroundLayer] setOpacity:1];
    228     [CATransaction commit];
    229 
    230     NSEnableScreenUpdates();
    231     _isAnimating = YES;
    232 }
    233 
    234 - (void)finishedEnterFullScreenAnimation:(bool)completed
    235 {
    236     NSDisableScreenUpdates();
    237 
    238     if (completed) {
    239         // Swap the webView placeholder into place.
    240         if (!_webViewPlaceholder)
    241             _webViewPlaceholder.adoptNS([[NSView alloc] init]);
    242         [self _swapView:_webView with:_webViewPlaceholder.get()];
    243 
    244         // Then insert the WebView into the full screen window
    245         NSView* animationView = [[self _fullScreenWindow] animationView];
    246         [animationView addSubview:_webView positioned:NSWindowBelow relativeTo:_layerHostingView.get()];
    247         [_webView setFrame:[animationView bounds]];
    248 
    249         // FIXME: In Barolo, orderIn will animate, which is not what we want.  Find a way
    250         // to work around this behavior.
    251         //[[_webViewPlaceholder.get() window] orderOut:self];
    252         [[self window] makeKeyAndOrderFront:self];
    253     }
    254 
    255     [self _manager]->didEnterFullScreen();
    256     NSEnableScreenUpdates();
    257 
    258     _isAnimating = NO;
    259 }
    260 
    261 - (void)exitFullScreen
    262 {
    263     if (!_isFullScreen)
    264         return;
    265 
    266     _isFullScreen = NO;
    267     _isAnimating = YES;
    268 
    269     NSDisableScreenUpdates();
    270 
    271     [self _manager]->willExitFullScreen();
    272     [self _manager]->beginExitFullScreenAnimation([self _animationDuration]);
    273 }
    274 
    275 - (void)beganExitFullScreenAnimation
    276 {
    277     [self _updateMenuAndDockForFullScreen];
    278     [self _updatePowerAssertions];
    279 
    280     // The user may have moved the fullScreen window in Spaces, so temporarily change
    281     // the collectionBehavior of the webView's window:
    282     NSWindow* webWindow = [[self webView] window];
    283     NSWindowCollectionBehavior behavior = [webWindow collectionBehavior];
    284     [webWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
    285     [webWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
    286     [webWindow setCollectionBehavior:behavior];
    287 
    288     // Swap the webView back into its original position:
    289     if ([_webView window] == [self window])
    290         [self _swapView:_webViewPlaceholder.get() with:_webView];
    291 
    292     [CATransaction begin];
    293     [CATransaction setAnimationDuration:[self _animationDuration]];
    294     [[[self _fullScreenWindow] backgroundLayer] setOpacity:0];
    295     [CATransaction commit];
    296 
    297     NSEnableScreenUpdates();
    298     _isAnimating = YES;
    299 }
    300 
    301 - (void)finishedExitFullScreenAnimation:(bool)completed
    302 {
    303     NSDisableScreenUpdates();
    304 
    305     if (completed) {
    306         [self _updateMenuAndDockForFullScreen];
    307         [self _updatePowerAssertions];
    308         [NSCursor setHiddenUntilMouseMoves:YES];
    309 
    310         [[self window] orderOut:self];
    311         [[_webView window] makeKeyAndOrderFront:self];
    312     }
    313 
    314     [self _manager]->didExitFullScreen();
    315     NSEnableScreenUpdates();
    316 
    317     _isAnimating = NO;
    318 }
    319 
    320 - (void)enterAcceleratedCompositingMode:(const WebKit::LayerTreeContext&)layerTreeContext
    321 {
    322     if (_layerHostingView)
    323         return;
    324 
    325     ASSERT(!layerTreeContext.isEmpty());
    326 
    327     // Create an NSView that will host our layer tree.
    328     _layerHostingView.adoptNS([[NSView alloc] initWithFrame:[[self window] frame]]);
    329     [_layerHostingView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
    330 
    331     [CATransaction begin];
    332     [CATransaction setDisableActions:YES];
    333     WKFullScreenWindow* window = [self _fullScreenWindow];
    334     [[window animationView] addSubview:_layerHostingView.get()];
    335 
    336     // Create a root layer that will back the NSView.
    337     RetainPtr<CALayer> rootLayer(AdoptNS, [[CALayer alloc] init]);
    338 #ifndef NDEBUG
    339     [rootLayer.get() setName:@"Hosting root layer"];
    340 #endif
    341 
    342     CALayer *renderLayer = WKMakeRenderLayer(layerTreeContext.contextID);
    343     [rootLayer.get() addSublayer:renderLayer];
    344 
    345     [_layerHostingView.get() setLayer:rootLayer.get()];
    346     [_layerHostingView.get() setWantsLayer:YES];
    347     [[window backgroundLayer] setHidden:NO];
    348     [CATransaction commit];
    349 }
    350 
    351 - (void)exitAcceleratedCompositingMode
    352 {
    353     if (!_layerHostingView)
    354         return;
    355 
    356     [CATransaction begin];
    357     [CATransaction setDisableActions:YES];
    358     [_layerHostingView.get() removeFromSuperview];
    359     [_layerHostingView.get() setLayer:nil];
    360     [_layerHostingView.get() setWantsLayer:NO];
    361     [[[self _fullScreenWindow] backgroundLayer] setHidden:YES];
    362     [CATransaction commit];
    363 
    364     _layerHostingView = 0;
    365 }
    366 
    367 - (WebCore::IntRect)getFullScreenRect
    368 {
    369     return enclosingIntRect([[self window] frame]);
    370 }
    371 
    372 #pragma mark -
    373 #pragma mark Internal Interface
    374 
    375 - (void)_updateMenuAndDockForFullScreen
    376 {
    377     // NSApplicationPresentationOptions is available on > 10.6 only:
    378 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    379     NSApplicationPresentationOptions options = NSApplicationPresentationDefault;
    380     NSScreen* fullScreenScreen = [[self window] screen];
    381 
    382     if (_isFullScreen) {
    383         // Auto-hide the menu bar if the fullScreenScreen contains the menu bar:
    384         // NOTE: if the fullScreenScreen contains the menu bar but not the dock, we must still
    385         // auto-hide the dock, or an exception will be thrown.
    386         if ([[NSScreen screens] objectAtIndex:0] == fullScreenScreen)
    387             options |= (NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock);
    388         // Check if the current screen contains the dock by comparing the screen's frame to its
    389         // visibleFrame; if a dock is present, the visibleFrame will differ. If the current screen
    390         // contains the dock, hide it.
    391         else if (!NSEqualRects([fullScreenScreen frame], [fullScreenScreen visibleFrame]))
    392             options |= NSApplicationPresentationAutoHideDock;
    393     }
    394 
    395     if ([NSApp respondsToSelector:@selector(setPresentationOptions:)])
    396         [NSApp setPresentationOptions:options];
    397     else
    398 #endif
    399         SetSystemUIMode(_isFullScreen ? kUIModeNormal : kUIModeAllHidden, 0);
    400 }
    401 
    402 #if !defined(BUILDING_ON_TIGER) // IOPMAssertionCreateWithName not defined on < 10.5
    403 - (void)_disableIdleDisplaySleep
    404 {
    405     if (_idleDisplaySleepAssertion == kIOPMNullAssertionID)
    406 #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK
    407         IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &_idleDisplaySleepAssertion);
    408 #else // IOPMAssertionCreate is depreciated in > 10.5
    409     IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullScreen."), &_idleDisplaySleepAssertion);
    410 #endif
    411 }
    412 
    413 - (void)_enableIdleDisplaySleep
    414 {
    415     if (_idleDisplaySleepAssertion != kIOPMNullAssertionID) {
    416         IOPMAssertionRelease(_idleDisplaySleepAssertion);
    417         _idleDisplaySleepAssertion = kIOPMNullAssertionID;
    418     }
    419 }
    420 
    421 - (void)_disableIdleSystemSleep
    422 {
    423     if (_idleSystemSleepAssertion == kIOPMNullAssertionID)
    424 #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK
    425         IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_idleSystemSleepAssertion);
    426 #else // IOPMAssertionCreate is depreciated in > 10.5
    427     IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullScreen."), &_idleSystemSleepAssertion);
    428 #endif
    429 }
    430 
    431 - (void)_enableIdleSystemSleep
    432 {
    433     if (_idleSystemSleepAssertion != kIOPMNullAssertionID) {
    434         IOPMAssertionRelease(_idleSystemSleepAssertion);
    435         _idleSystemSleepAssertion = kIOPMNullAssertionID;
    436     }
    437 }
    438 
    439 - (void)_enableTickleTimer
    440 {
    441     [_tickleTimer invalidate];
    442     [_tickleTimer release];
    443     _tickleTimer = [[NSTimer scheduledTimerWithTimeInterval:tickleTimerInterval target:self selector:@selector(_tickleTimerFired) userInfo:nil repeats:YES] retain];
    444 }
    445 
    446 - (void)_disableTickleTimer
    447 {
    448     [_tickleTimer invalidate];
    449     [_tickleTimer release];
    450     _tickleTimer = nil;
    451 }
    452 
    453 - (void)_tickleTimerFired
    454 {
    455     UpdateSystemActivity(OverallAct);
    456 }
    457 #endif
    458 
    459 - (void)_updatePowerAssertions
    460 {
    461 #if !defined(BUILDING_ON_TIGER)
    462     if (_isPlaying && _isFullScreen) {
    463         [self _disableIdleSystemSleep];
    464         [self _disableIdleDisplaySleep];
    465         [self _enableTickleTimer];
    466     } else {
    467         [self _enableIdleSystemSleep];
    468         [self _enableIdleDisplaySleep];
    469         [self _disableTickleTimer];
    470     }
    471 #endif
    472 }
    473 
    474 - (WebFullScreenManagerProxy*)_manager
    475 {
    476     WebPageProxy* webPage = toImpl([_webView pageRef]);
    477     if (!webPage)
    478         return 0;
    479     return webPage->fullScreenManager();
    480 }
    481 
    482 - (void)_requestExit
    483 {
    484     [self exitFullScreen];
    485     _forceDisableAnimation = NO;
    486 }
    487 
    488 - (void)_requestExitFullScreenWithAnimation:(BOOL)animation
    489 {
    490     _forceDisableAnimation = !animation;
    491     [self performSelector:@selector(_requestExit) withObject:nil afterDelay:0];
    492 
    493 }
    494 
    495 - (void)_swapView:(NSView*)view with:(NSView*)otherView
    496 {
    497     [otherView setFrame:[view frame]];
    498     [otherView setAutoresizingMask:[view autoresizingMask]];
    499     [otherView removeFromSuperview];
    500     [[view superview] replaceSubview:view with:otherView];
    501 }
    502 
    503 #pragma mark -
    504 #pragma mark Utility Functions
    505 
    506 - (WKFullScreenWindow *)_fullScreenWindow
    507 {
    508     ASSERT([[self window] isKindOfClass:[WKFullScreenWindow class]]);
    509     return (WKFullScreenWindow *)[self window];
    510 }
    511 
    512 - (CFTimeInterval)_animationDuration
    513 {
    514     static const CFTimeInterval defaultDuration = 0.5;
    515     CFTimeInterval duration = defaultDuration;
    516 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    517     NSUInteger modifierFlags = [NSEvent modifierFlags];
    518 #else
    519     NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags];
    520 #endif
    521     if ((modifierFlags & NSControlKeyMask) == NSControlKeyMask)
    522         duration *= 2;
    523     if ((modifierFlags & NSShiftKeyMask) == NSShiftKeyMask)
    524         duration *= 10;
    525     if (_forceDisableAnimation) {
    526         // This will disable scale animation
    527         duration = 0;
    528     }
    529     return duration;
    530 }
    531 
    532 @end
    533 
    534 #pragma mark -
    535 @implementation WKFullScreenWindow
    536 
    537 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag
    538 {
    539     UNUSED_PARAM(aStyle);
    540     self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag];
    541     if (!self)
    542         return nil;
    543     [self setOpaque:NO];
    544     [self setBackgroundColor:[NSColor clearColor]];
    545     [self setIgnoresMouseEvents:NO];
    546     [self setAcceptsMouseMovedEvents:YES];
    547     [self setReleasedWhenClosed:NO];
    548     [self setHasShadow:YES];
    549 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    550     [self setMovable:NO];
    551 #else
    552     [self setMovableByWindowBackground:NO];
    553 #endif
    554 
    555     NSView* contentView = [self contentView];
    556     _animationView = [[NSView alloc] initWithFrame:[contentView bounds]];
    557 
    558     CALayer* contentLayer = [[CALayer alloc] init];
    559     [_animationView setLayer:contentLayer];
    560     [_animationView setWantsLayer:YES];
    561     [_animationView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
    562     [contentView addSubview:_animationView];
    563 
    564     _backgroundLayer = [[CALayer alloc] init];
    565     [contentLayer addSublayer:_backgroundLayer];
    566 
    567     [_backgroundLayer setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)];
    568     [_backgroundLayer setOpacity:0];
    569     return self;
    570 }
    571 
    572 - (void)dealloc
    573 {
    574     [_animationView release];
    575     [_backgroundLayer release];
    576     [super dealloc];
    577 }
    578 
    579 - (BOOL)canBecomeKeyWindow
    580 {
    581     return YES;
    582 }
    583 
    584 - (void)keyDown:(NSEvent *)theEvent
    585 {
    586     if ([[theEvent charactersIgnoringModifiers] isEqual:@"\e"]) // Esacpe key-code
    587         [self cancelOperation:self];
    588     else [super keyDown:theEvent];
    589 }
    590 
    591 - (void)cancelOperation:(id)sender
    592 {
    593     UNUSED_PARAM(sender);
    594     [[self windowController] _requestExitFullScreenWithAnimation:YES];
    595 }
    596 
    597 - (CALayer*)backgroundLayer
    598 {
    599     return _backgroundLayer;
    600 }
    601 
    602 - (NSView*)animationView
    603 {
    604     return _animationView;
    605 }
    606 @end
    607 
    608 #endif
    609