initial import
[vuplus_webkit] / Source / WebKit2 / UIProcess / mac / WKFullScreenWindowController.mm
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 <WebKit/WebNSWindowExtras.h>
43 #import <WebKitSystemInterface.h>
44 #import <wtf/UnusedParam.h>
45
46 static const NSTimeInterval tickleTimerInterval = 1.0;
47
48 using namespace WebKit;
49 using namespace WebCore;
50
51 #if defined(BUILDING_ON_LEOPARD)
52 @interface CATransaction(SnowLeopardConvenienceFunctions)
53 + (void)setDisableActions:(BOOL)flag;
54 + (void)setAnimationDuration:(CFTimeInterval)dur;
55 @end
56
57 @implementation CATransaction(SnowLeopardConvenienceFunctions)
58 + (void)setDisableActions:(BOOL)flag
59 {
60     [self setValue:[NSNumber numberWithBool:flag] forKey:kCATransactionDisableActions];
61 }
62
63 + (void)setAnimationDuration:(CFTimeInterval)dur
64 {
65     [self setValue:[NSNumber numberWithDouble:dur] forKey:kCATransactionAnimationDuration];
66 }
67 @end
68
69 #endif
70
71 @interface WKFullScreenWindow : NSWindow
72 {
73     NSView* _animationView;
74     CALayer* _backgroundLayer;
75 }
76 - (CALayer*)backgroundLayer;
77 - (NSView*)animationView;
78 @end
79
80 @interface WKFullScreenWindowController(Private)
81 - (void)_requestExitFullScreenWithAnimation:(BOOL)animation;
82 - (void)_updateMenuAndDockForFullScreen;
83 - (void)_updatePowerAssertions;
84 - (WKFullScreenWindow *)_fullScreenWindow;
85 - (CFTimeInterval)_animationDuration;
86 - (void)_swapView:(NSView*)view with:(NSView*)otherView;
87 - (WebPageProxy*)_page;
88 - (WebFullScreenManagerProxy*)_manager;
89 @end
90
91 @interface NSWindow(IsOnActiveSpaceAdditionForTigerAndLeopard)
92 - (BOOL)isOnActiveSpace;
93 @end
94
95 @implementation WKFullScreenWindowController
96
97 #pragma mark -
98 #pragma mark Initialization
99 - (id)init
100 {
101     NSWindow *window = [[WKFullScreenWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
102     self = [super initWithWindow:window];
103     [window release];
104     if (!self)
105         return nil;
106     [self windowDidLoad];
107     
108     return self;
109 }
110
111 - (void)dealloc
112 {
113     [self setWebView:nil];
114     
115     [NSObject cancelPreviousPerformRequestsWithTarget:self];
116     
117     [[NSNotificationCenter defaultCenter] removeObserver:self];
118     [super dealloc];
119 }
120
121 - (void)windowDidLoad
122 {
123     [super windowDidLoad];
124
125     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidResignActive:) name:NSApplicationDidResignActiveNotification object:NSApp];
126     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeScreenParameters:) name:NSApplicationDidChangeScreenParametersNotification object:NSApp];
127 }
128
129 #pragma mark -
130 #pragma mark Accessors
131
132 - (WKView*)webView
133 {
134     return _webView;
135 }
136
137 - (void)setWebView:(WKView *)webView
138 {
139     [webView retain];
140     [_webView release];
141     _webView = webView;
142 }
143
144 #pragma mark -
145 #pragma mark Notifications
146
147 - (void)applicationDidResignActive:(NSNotification*)notification
148 {   
149     // Check to see if the fullScreenWindow is on the active space; this function is available
150     // on 10.6 and later, so default to YES if the function is not available:
151     NSWindow* fullScreenWindow = [self _fullScreenWindow];
152     BOOL isOnActiveSpace = ([fullScreenWindow respondsToSelector:@selector(isOnActiveSpace)] ? [fullScreenWindow isOnActiveSpace] : YES);
153     
154     // Replicate the QuickTime Player (X) behavior when losing active application status:
155     // Is the fullScreen screen the main screen? (Note: this covers the case where only a 
156     // single screen is available.)  Is the fullScreen screen on the current space? IFF so, 
157     // then exit fullScreen mode. 
158     if ([fullScreenWindow screen] == [[NSScreen screens] objectAtIndex:0] && isOnActiveSpace)
159         [self _requestExitFullScreenWithAnimation:NO];
160 }
161
162 - (void)applicationDidChangeScreenParameters:(NSNotification*)notification
163 {
164     // The user may have changed the main screen by moving the menu bar, or they may have changed
165     // the Dock's size or location, or they may have changed the fullScreen screen's dimensions. 
166     // Update our presentation parameters, and ensure that the full screen window occupies the 
167     // entire screen:
168     [self _updateMenuAndDockForFullScreen];
169     NSWindow* window = [self window];
170     [window setFrame:[[window screen] frame] display:YES];
171 }
172
173 #pragma mark -
174 #pragma mark Exposed Interface
175
176 - (void)enterFullScreen:(NSScreen *)screen
177 {
178     if (_isFullScreen)
179         return;
180     
181     _isFullScreen = YES;
182     
183     NSDisableScreenUpdates();
184     
185     if (!screen)
186         screen = [NSScreen mainScreen];
187     NSRect screenFrame = [screen frame];
188     
189     NSRect webViewFrame = [_webView convertRectToBase:[_webView frame]];
190     webViewFrame.origin = [[_webView window] convertBaseToScreen:webViewFrame.origin];
191         
192     // In the case of a multi-monitor setup where the webView straddles two
193     // monitors, we must create a window large enough to contain the destination
194     // frame and the initial frame.
195     NSRect windowFrame = NSUnionRect(screenFrame, webViewFrame);
196     
197     [CATransaction begin];
198     [CATransaction setDisableActions:YES];
199     [[self window] setFrame:windowFrame display:YES];
200     
201     CALayer* backgroundLayer = [[self _fullScreenWindow] backgroundLayer];
202     NSRect backgroundFrame = {[[self window] convertScreenToBase:screenFrame.origin], screenFrame.size};
203     backgroundFrame = [[[self window] contentView] convertRectFromBase:backgroundFrame];
204     
205     [backgroundLayer setFrame:NSRectToCGRect(backgroundFrame)];
206     [CATransaction commit];
207
208     CFTimeInterval duration = [self _animationDuration];
209     [self _manager]->willEnterFullScreen();
210     [self _manager]->beginEnterFullScreenAnimation(duration);
211 }
212
213 - (void)beganEnterFullScreenAnimation
214 {
215     if (_isEnteringFullScreen)
216         return;
217     _isEnteringFullScreen = YES;
218
219     if (_isExitingFullScreen)
220         [self finishedExitFullScreenAnimation:NO];
221
222     [self _updateMenuAndDockForFullScreen];   
223     [self _updatePowerAssertions];
224     
225     // In a previous incarnation, the NSWindow attached to this controller may have
226     // been on a different screen. Temporarily change the collectionBehavior of the window:
227     NSWindow* fullScreenWindow = [self window];
228     NSWindowCollectionBehavior behavior = [fullScreenWindow collectionBehavior];
229     [fullScreenWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
230     [fullScreenWindow makeKeyAndOrderFront:self];
231     [fullScreenWindow setCollectionBehavior:behavior];
232
233     // Start the opacity animation. We can use implicit animations here because we don't care when
234     // the animation finishes.
235     [CATransaction begin];
236     [CATransaction setAnimationDuration:[self _animationDuration]];
237     [[[self _fullScreenWindow] backgroundLayer] setOpacity:1];
238     [CATransaction commit];
239
240     NSEnableScreenUpdates();
241 }
242
243 - (void)finishedEnterFullScreenAnimation:(bool)completed
244 {
245     if (!_isEnteringFullScreen)
246         return;
247     _isEnteringFullScreen = NO;
248     
249     if (completed) {                
250         NSDisableScreenUpdates();
251
252         // Swap the webView placeholder into place.
253         if (!_webViewPlaceholder)
254             _webViewPlaceholder.adoptNS([[NSView alloc] init]);
255         NSResponder *webWindowFirstResponder = [[_webView window] firstResponder];
256         [self _swapView:_webView with:_webViewPlaceholder.get()];
257         
258         // Then insert the WebView into the full screen window
259         NSView* contentView = [[self _fullScreenWindow] contentView];
260         [contentView addSubview:_webView positioned:NSWindowBelow relativeTo:nil];
261         [_webView setFrame:[contentView bounds]];
262         [[self window] makeResponder:webWindowFirstResponder firstResponderIfDescendantOfView:_webView];
263         
264         NSWindow *webWindow = [_webViewPlaceholder.get() window];
265 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
266         // In Lion, NSWindow will animate into and out of orderOut operations. Suppress that
267         // behavior here, making sure to reset the animation behavior afterward.
268         NSWindowAnimationBehavior animationBehavior = [webWindow animationBehavior];
269         [webWindow setAnimationBehavior:NSWindowAnimationBehaviorNone];
270 #endif
271         [webWindow orderOut:self];
272 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
273         [webWindow setAnimationBehavior:animationBehavior];
274 #endif
275         [self _manager]->didEnterFullScreen();
276     }
277
278     // Complete the animation once -(void)exitCompositingMode is called.
279 }
280
281 - (void)exitFullScreen
282 {
283     if (!_isFullScreen)
284         return;
285     
286     _isFullScreen = NO;
287     
288     NSDisableScreenUpdates();
289     
290     [self _manager]->willExitFullScreen();
291     [self _manager]->beginExitFullScreenAnimation([self _animationDuration]);
292 }
293
294 - (void)beganExitFullScreenAnimation
295 {
296     if (_isExitingFullScreen)
297         return;
298     _isExitingFullScreen = YES;
299
300     if (_isEnteringFullScreen)
301         [self finishedEnterFullScreenAnimation:NO];
302
303     [self _updateMenuAndDockForFullScreen];   
304     [self _updatePowerAssertions];
305     
306     // Swap the webView back into its original position:
307     if ([_webView window] == [self window]) {
308         NSResponder *fullScreenWindowFirstResponder = [[self _fullScreenWindow] firstResponder];
309 #if defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD)
310         // Work around a bug in AppKit <rdar://problem/9443385> where moving a 
311         // layer-hosted view from a layer-backed view to a non-layer-backed view
312         // generates an exception.
313         if (![_webView wantsLayer] && [_webView layer]) {
314             [_webView removeFromSuperview];
315             for (NSView* child in [_webView subviews])
316                 [[child layer] removeFromSuperlayer];
317         }
318 #endif
319         [self _swapView:_webViewPlaceholder.get() with:_webView];
320         [[_webView window] makeResponder:fullScreenWindowFirstResponder firstResponderIfDescendantOfView:_webView];
321         NSWindow* webWindow = [_webView window];
322 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
323         // In Lion, NSWindow will animate into and out of orderOut operations. Suppress that
324         // behavior here, making sure to reset the animation behavior afterward.
325         NSWindowAnimationBehavior animationBehavior = [webWindow animationBehavior];
326         [webWindow setAnimationBehavior:NSWindowAnimationBehaviorNone];
327 #endif
328         // If the user has moved the fullScreen window into a new space, temporarily change
329         // the collectionBehavior of the webView's window so that it is pulled into the active space:
330         if (![webWindow isOnActiveSpace]) {
331             NSWindowCollectionBehavior behavior = [webWindow collectionBehavior];
332             [webWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
333             [webWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
334             [webWindow setCollectionBehavior:behavior];
335         } else
336             [webWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
337
338 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
339         [webWindow setAnimationBehavior:animationBehavior];
340 #endif
341     }
342     
343     [CATransaction begin];
344     [CATransaction setAnimationDuration:[self _animationDuration]];
345     [[[self _fullScreenWindow] backgroundLayer] setOpacity:0];
346     [CATransaction commit];
347     
348     NSEnableScreenUpdates();
349 }
350
351 - (void)finishedExitFullScreenAnimation:(bool)completed
352 {
353     if (!_isExitingFullScreen)
354         return;
355     _isExitingFullScreen = NO;
356
357     NSDisableScreenUpdates();
358     
359     [self _updateMenuAndDockForFullScreen];
360     [self _updatePowerAssertions];
361     [NSCursor setHiddenUntilMouseMoves:YES];
362
363     [self _manager]->didExitFullScreen();
364 }
365
366 - (void)enterAcceleratedCompositingMode:(const WebKit::LayerTreeContext&)layerTreeContext
367 {
368     if (_layerHostingView)
369         return;
370     
371     // Create an NSView that will host our layer tree.
372     _layerHostingView.adoptNS([[NSView alloc] initWithFrame:[[self window] frame]]);
373     [_layerHostingView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
374     
375     [CATransaction begin];
376     [CATransaction setDisableActions:YES];
377     WKFullScreenWindow* window = [self _fullScreenWindow];
378     [[window contentView] addSubview:_layerHostingView.get() positioned:NSWindowAbove relativeTo:nil];
379     
380     // Create a root layer that will back the NSView.
381     RetainPtr<CALayer> rootLayer(AdoptNS, [[CALayer alloc] init]);
382 #ifndef NDEBUG
383     [rootLayer.get() setName:@"Hosting root layer"];
384 #endif
385     
386     CALayer *renderLayer = WKMakeRenderLayer(layerTreeContext.contextID);
387     [rootLayer.get() addSublayer:renderLayer];
388     
389     [_layerHostingView.get() setLayer:rootLayer.get()];
390     [_layerHostingView.get() setWantsLayer:YES];
391     [[window backgroundLayer] setHidden:NO];
392     [CATransaction commit];
393 }
394
395 - (void)exitAcceleratedCompositingMode
396 {
397     if (!_layerHostingView)
398         return;
399
400     [CATransaction begin];
401     [CATransaction setDisableActions:YES];
402     [_layerHostingView.get() removeFromSuperview];
403     [_layerHostingView.get() setLayer:nil];
404     [_layerHostingView.get() setWantsLayer:NO];
405     [[[self _fullScreenWindow] backgroundLayer] setHidden:YES];
406     [CATransaction commit];
407
408     // Complete the animation out of full-screen mode
409     // by hiding the full-screen window:
410     if (!_isFullScreen) {
411         [[_webView window] display];
412         [[self window] orderOut:self];
413         [[_webView window] makeKeyAndOrderFront:self];
414     }
415     
416     _layerHostingView = 0;
417     NSEnableScreenUpdates();
418
419     [self _manager]->disposeOfLayerClient();
420 }
421
422 - (WebCore::IntRect)getFullScreenRect
423 {
424     return enclosingIntRect([[self window] frame]);
425 }
426
427 - (void)close
428 {
429     // We are being asked to close rapidly, most likely because the page 
430     // has closed or the web process has crashed.  Just walk through our
431     // normal exit full screen sequence, but don't wait to be called back
432     // in response.
433     if (_isFullScreen) {
434         [self exitFullScreen];
435         [self beganExitFullScreenAnimation];
436     }
437     
438     if (_isExitingFullScreen)
439         [self finishedExitFullScreenAnimation:YES];
440
441     [super close];
442 }
443
444 #pragma mark -
445 #pragma mark Internal Interface
446
447 - (void)_updateMenuAndDockForFullScreen
448 {
449     // NSApplicationPresentationOptions is available on > 10.6 only:
450 #ifndef BUILDING_ON_LEOPARD
451     NSApplicationPresentationOptions options = NSApplicationPresentationDefault;
452     NSScreen* fullScreenScreen = [[self window] screen];
453     
454     if (_isFullScreen) {
455         // Auto-hide the menu bar if the fullScreenScreen contains the menu bar:
456         // NOTE: if the fullScreenScreen contains the menu bar but not the dock, we must still 
457         // auto-hide the dock, or an exception will be thrown.
458         if ([[NSScreen screens] objectAtIndex:0] == fullScreenScreen)
459             options |= (NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock);
460         // Check if the current screen contains the dock by comparing the screen's frame to its
461         // visibleFrame; if a dock is present, the visibleFrame will differ. If the current screen
462         // contains the dock, hide it.
463         else if (!NSEqualRects([fullScreenScreen frame], [fullScreenScreen visibleFrame]))
464             options |= NSApplicationPresentationAutoHideDock;
465     }
466     
467     if ([NSApp respondsToSelector:@selector(setPresentationOptions:)])
468         [NSApp setPresentationOptions:options];
469     else
470 #endif
471         SetSystemUIMode(_isFullScreen ? kUIModeNormal : kUIModeAllHidden, 0);
472 }
473
474 - (void)_disableIdleDisplaySleep
475 {
476     if (_idleDisplaySleepAssertion == kIOPMNullAssertionID) 
477 #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK
478         IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &_idleDisplaySleepAssertion);
479 #else // IOPMAssertionCreate is depreciated in > 10.5
480     IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullScreen."), &_idleDisplaySleepAssertion);
481 #endif
482 }
483
484 - (void)_enableIdleDisplaySleep
485 {
486     if (_idleDisplaySleepAssertion != kIOPMNullAssertionID) {
487         IOPMAssertionRelease(_idleDisplaySleepAssertion);
488         _idleDisplaySleepAssertion = kIOPMNullAssertionID;
489     }
490 }
491
492 - (void)_disableIdleSystemSleep
493 {
494     if (_idleSystemSleepAssertion == kIOPMNullAssertionID) 
495 #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK
496         IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_idleSystemSleepAssertion);
497 #else // IOPMAssertionCreate is depreciated in > 10.5
498     IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullScreen."), &_idleSystemSleepAssertion);
499 #endif
500 }
501
502 - (void)_enableIdleSystemSleep
503 {
504     if (_idleSystemSleepAssertion != kIOPMNullAssertionID) {
505         IOPMAssertionRelease(_idleSystemSleepAssertion);
506         _idleSystemSleepAssertion = kIOPMNullAssertionID;
507     }
508 }
509
510 - (void)_enableTickleTimer
511 {
512     [_tickleTimer invalidate];
513     [_tickleTimer release];
514     _tickleTimer = [[NSTimer scheduledTimerWithTimeInterval:tickleTimerInterval target:self selector:@selector(_tickleTimerFired) userInfo:nil repeats:YES] retain];
515 }
516
517 - (void)_disableTickleTimer
518 {
519     [_tickleTimer invalidate];
520     [_tickleTimer release];
521     _tickleTimer = nil;
522 }
523
524 - (void)_tickleTimerFired
525 {
526     UpdateSystemActivity(OverallAct);
527 }
528
529 - (void)_updatePowerAssertions
530 {
531     if (_isPlaying && _isFullScreen) {
532         [self _disableIdleSystemSleep];
533         [self _disableIdleDisplaySleep];
534         [self _enableTickleTimer];
535     } else {
536         [self _enableIdleSystemSleep];
537         [self _enableIdleDisplaySleep];
538         [self _disableTickleTimer];
539     }
540 }
541
542 - (WebPageProxy*)_page
543 {
544     return toImpl([_webView pageRef]);
545 }
546
547 - (WebFullScreenManagerProxy*)_manager
548 {
549     WebPageProxy* webPage = [self _page];
550     if (!webPage)
551         return 0;
552     return webPage->fullScreenManager();
553 }
554
555 - (void)_requestExit
556 {
557     [self exitFullScreen];
558     _forceDisableAnimation = NO;
559 }
560
561 - (void)_requestExitFullScreenWithAnimation:(BOOL)animation
562 {
563     _forceDisableAnimation = !animation;
564     [self performSelector:@selector(_requestExit) withObject:nil afterDelay:0];
565     
566 }
567
568 - (void)_swapView:(NSView*)view with:(NSView*)otherView
569 {
570     [CATransaction begin];
571     [CATransaction setDisableActions:YES];
572     [otherView setFrame:[view frame]];        
573     [otherView setAutoresizingMask:[view autoresizingMask]];
574     [otherView removeFromSuperview];
575     [[view superview] replaceSubview:view with:otherView];
576     [CATransaction commit];
577 }
578
579 #pragma mark -
580 #pragma mark Utility Functions
581
582 - (WKFullScreenWindow *)_fullScreenWindow
583 {
584     ASSERT([[self window] isKindOfClass:[WKFullScreenWindow class]]);
585     return (WKFullScreenWindow *)[self window];
586 }
587
588 - (CFTimeInterval)_animationDuration
589 {
590     static const CFTimeInterval defaultDuration = 0.5;
591     CFTimeInterval duration = defaultDuration;
592 #ifndef BUILDING_ON_LEOPARD
593     NSUInteger modifierFlags = [NSEvent modifierFlags];
594 #else
595     NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags];
596 #endif
597     if ((modifierFlags & NSControlKeyMask) == NSControlKeyMask)
598         duration *= 2;
599     if ((modifierFlags & NSShiftKeyMask) == NSShiftKeyMask)
600         duration *= 10;
601     if (_forceDisableAnimation) {
602         // This will disable scale animation
603         duration = 0;
604     }
605     return duration;
606 }
607
608 @end
609
610 #pragma mark -
611 @implementation WKFullScreenWindow
612
613 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag
614 {
615     UNUSED_PARAM(aStyle);
616     self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag];
617     if (!self)
618         return nil;
619     [self setOpaque:NO];
620     [self setBackgroundColor:[NSColor clearColor]];
621     [self setIgnoresMouseEvents:NO];
622     [self setAcceptsMouseMovedEvents:YES];
623     [self setReleasedWhenClosed:NO];
624     [self setHasShadow:YES];
625 #ifndef BUILDING_ON_LEOPARD
626     [self setMovable:NO];
627 #else
628     [self setMovableByWindowBackground:NO];
629 #endif
630     
631     NSView* contentView = [self contentView];
632     [contentView setWantsLayer:YES];
633     _animationView = [[NSView alloc] initWithFrame:[contentView bounds]];
634     
635     CALayer* contentLayer = [[CALayer alloc] init];
636     [_animationView setLayer:contentLayer];
637     [_animationView setWantsLayer:YES];
638     [_animationView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
639     [contentView addSubview:_animationView];
640     
641     _backgroundLayer = [[CALayer alloc] init];
642     [contentLayer addSublayer:_backgroundLayer];
643     
644     [_backgroundLayer setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)];
645     [_backgroundLayer setOpacity:0];
646     return self;
647 }
648
649 - (void)dealloc
650 {
651     [_animationView release];
652     [_backgroundLayer release];
653     [super dealloc];
654 }
655
656 - (BOOL)canBecomeKeyWindow
657 {
658     return YES;
659 }
660
661 - (void)keyDown:(NSEvent *)theEvent
662 {
663     if ([[theEvent charactersIgnoringModifiers] isEqual:@"\e"]) // Esacpe key-code
664         [self cancelOperation:self];
665     else [super keyDown:theEvent];
666 }
667
668 - (void)cancelOperation:(id)sender
669 {
670     UNUSED_PARAM(sender);
671     [[self windowController] _requestExitFullScreenWithAnimation:YES];
672 }
673
674 - (CALayer*)backgroundLayer
675 {
676     return _backgroundLayer;
677 }
678
679 - (NSView*)animationView
680 {
681     return _animationView;
682 }
683 @end
684
685 #endif