09c582d6a9f9b259baf2bd621d8286eb22cf87d1
[WebKit-https.git] / 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 <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         [CATransaction begin];
250         [CATransaction setDisableActions:YES];
251         [[[self _fullScreenWindow] backgroundLayer] setHidden:YES];
252         [CATransaction commit];
253         
254         // FIXME: In Barolo, orderIn will animate, which is not what we want.  Find a way
255         // to work around this behavior.
256         //[[_webViewPlaceholder.get() window] orderOut:self];
257         [[self window] makeKeyAndOrderFront:self];
258     }
259     
260     [self _manager]->didEnterFullScreen();
261     NSEnableScreenUpdates();
262     
263     _isAnimating = NO;
264 }
265
266 - (void)exitFullScreen
267 {
268     if (!_isFullScreen)
269         return;
270     
271     _isFullScreen = NO;
272     _isAnimating = YES;
273     
274     NSDisableScreenUpdates();
275     
276     [self _manager]->willExitFullScreen();
277     [self _manager]->beginExitFullScreenAnimation([self _animationDuration]);
278 }
279
280 - (void)beganExitFullScreenAnimation
281 {   
282     [self _updateMenuAndDockForFullScreen];   
283     [self _updatePowerAssertions];
284     
285     // The user may have moved the fullScreen window in Spaces, so temporarily change 
286     // the collectionBehavior of the webView's window:
287     NSWindow* webWindow = [[self webView] window];
288     NSWindowCollectionBehavior behavior = [webWindow collectionBehavior];
289     [webWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
290     [webWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
291     [webWindow setCollectionBehavior:behavior];
292     
293     // Swap the webView back into its original position:
294     if ([_webView window] == [self window])
295         [self _swapView:_webViewPlaceholder.get() with:_webView];
296     
297     [CATransaction begin];
298     [CATransaction setAnimationDuration:[self _animationDuration]];
299     [[[self _fullScreenWindow] backgroundLayer] setOpacity:0];
300     [CATransaction commit];
301     
302     NSEnableScreenUpdates();
303     _isAnimating = YES;
304 }
305
306 - (void)finishedExitFullScreenAnimation:(bool)completed
307 {
308     NSDisableScreenUpdates();
309     
310     if (completed) {
311         [self _updateMenuAndDockForFullScreen];
312         [self _updatePowerAssertions];
313         [NSCursor setHiddenUntilMouseMoves:YES];
314                 
315         [[self window] orderOut:self];
316         [[_webView window] makeKeyAndOrderFront:self];
317     }
318     
319     [self _manager]->didExitFullScreen();
320     NSEnableScreenUpdates();
321     
322     _isAnimating = NO;
323 }
324
325 - (void)enterAcceleratedCompositingMode:(const WebKit::LayerTreeContext&)layerTreeContext
326 {
327     if (_layerHostingView)
328         return;
329
330     ASSERT(!layerTreeContext.isEmpty());
331     
332     // Create an NSView that will host our layer tree.
333     _layerHostingView.adoptNS([[NSView alloc] initWithFrame:[[self window] frame]]);
334     [_layerHostingView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
335     
336     [CATransaction begin];
337     [CATransaction setDisableActions:YES];
338     WKFullScreenWindow* window = [self _fullScreenWindow];
339     [[window animationView] addSubview:_layerHostingView.get()];
340     
341     // Create a root layer that will back the NSView.
342     RetainPtr<CALayer> rootLayer(AdoptNS, [[CALayer alloc] init]);
343 #ifndef NDEBUG
344     [rootLayer.get() setName:@"Hosting root layer"];
345 #endif
346     
347     CALayer *renderLayer = WKMakeRenderLayer(layerTreeContext.contextID);
348     [rootLayer.get() addSublayer:renderLayer];
349     
350     [_layerHostingView.get() setLayer:rootLayer.get()];
351     [_layerHostingView.get() setWantsLayer:YES];
352     [[window backgroundLayer] setHidden:NO];
353     [CATransaction commit];
354 }
355
356 - (void)exitAcceleratedCompositingMode
357 {
358     if (!_layerHostingView)
359         return;
360     
361     [CATransaction begin];
362     [CATransaction setDisableActions:YES];
363     [_layerHostingView.get() removeFromSuperview];
364     [_layerHostingView.get() setLayer:nil];
365     [_layerHostingView.get() setWantsLayer:NO];
366     [[[self _fullScreenWindow] backgroundLayer] setHidden:YES];
367     [CATransaction commit];
368     
369     _layerHostingView = 0;
370 }
371
372 - (WebCore::IntRect)getFullScreenRect
373 {
374     return enclosingIntRect([[self window] frame]);
375 }
376
377 #pragma mark -
378 #pragma mark Internal Interface
379
380 - (void)_updateMenuAndDockForFullScreen
381 {
382     // NSApplicationPresentationOptions is available on > 10.6 only:
383 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
384     NSApplicationPresentationOptions options = NSApplicationPresentationDefault;
385     NSScreen* fullScreenScreen = [[self window] screen];
386     
387     if (_isFullScreen) {
388         // Auto-hide the menu bar if the fullScreenScreen contains the menu bar:
389         // NOTE: if the fullScreenScreen contains the menu bar but not the dock, we must still 
390         // auto-hide the dock, or an exception will be thrown.
391         if ([[NSScreen screens] objectAtIndex:0] == fullScreenScreen)
392             options |= (NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock);
393         // Check if the current screen contains the dock by comparing the screen's frame to its
394         // visibleFrame; if a dock is present, the visibleFrame will differ. If the current screen
395         // contains the dock, hide it.
396         else if (!NSEqualRects([fullScreenScreen frame], [fullScreenScreen visibleFrame]))
397             options |= NSApplicationPresentationAutoHideDock;
398     }
399     
400     if ([NSApp respondsToSelector:@selector(setPresentationOptions:)])
401         [NSApp setPresentationOptions:options];
402     else
403 #endif
404         SetSystemUIMode(_isFullScreen ? kUIModeNormal : kUIModeAllHidden, 0);
405 }
406
407 #if !defined(BUILDING_ON_TIGER) // IOPMAssertionCreateWithName not defined on < 10.5
408 - (void)_disableIdleDisplaySleep
409 {
410     if (_idleDisplaySleepAssertion == kIOPMNullAssertionID) 
411 #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK
412         IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &_idleDisplaySleepAssertion);
413 #else // IOPMAssertionCreate is depreciated in > 10.5
414     IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullScreen."), &_idleDisplaySleepAssertion);
415 #endif
416 }
417
418 - (void)_enableIdleDisplaySleep
419 {
420     if (_idleDisplaySleepAssertion != kIOPMNullAssertionID) {
421         IOPMAssertionRelease(_idleDisplaySleepAssertion);
422         _idleDisplaySleepAssertion = kIOPMNullAssertionID;
423     }
424 }
425
426 - (void)_disableIdleSystemSleep
427 {
428     if (_idleSystemSleepAssertion == kIOPMNullAssertionID) 
429 #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK
430         IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_idleSystemSleepAssertion);
431 #else // IOPMAssertionCreate is depreciated in > 10.5
432     IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullScreen."), &_idleSystemSleepAssertion);
433 #endif
434 }
435
436 - (void)_enableIdleSystemSleep
437 {
438     if (_idleSystemSleepAssertion != kIOPMNullAssertionID) {
439         IOPMAssertionRelease(_idleSystemSleepAssertion);
440         _idleSystemSleepAssertion = kIOPMNullAssertionID;
441     }
442 }
443
444 - (void)_enableTickleTimer
445 {
446     [_tickleTimer invalidate];
447     [_tickleTimer release];
448     _tickleTimer = [[NSTimer scheduledTimerWithTimeInterval:tickleTimerInterval target:self selector:@selector(_tickleTimerFired) userInfo:nil repeats:YES] retain];
449 }
450
451 - (void)_disableTickleTimer
452 {
453     [_tickleTimer invalidate];
454     [_tickleTimer release];
455     _tickleTimer = nil;
456 }
457
458 - (void)_tickleTimerFired
459 {
460     UpdateSystemActivity(OverallAct);
461 }
462 #endif
463
464 - (void)_updatePowerAssertions
465 {
466 #if !defined(BUILDING_ON_TIGER) 
467     if (_isPlaying && _isFullScreen) {
468         [self _disableIdleSystemSleep];
469         [self _disableIdleDisplaySleep];
470         [self _enableTickleTimer];
471     } else {
472         [self _enableIdleSystemSleep];
473         [self _enableIdleDisplaySleep];
474         [self _disableTickleTimer];
475     }
476 #endif
477 }
478
479 - (WebFullScreenManagerProxy*)_manager
480 {
481     WebPageProxy* webPage = toImpl([_webView pageRef]);
482     if (!webPage)
483         return 0;
484     return webPage->fullScreenManager();
485 }
486
487 - (void)_requestExit
488 {
489     [self exitFullScreen];
490     _forceDisableAnimation = NO;
491 }
492
493 - (void)_requestExitFullScreenWithAnimation:(BOOL)animation
494 {
495     _forceDisableAnimation = !animation;
496     [self performSelector:@selector(_requestExit) withObject:nil afterDelay:0];
497     
498 }
499
500 - (void)_swapView:(NSView*)view with:(NSView*)otherView
501 {
502     [otherView setFrame:[view frame]];        
503     [otherView setAutoresizingMask:[view autoresizingMask]];
504     [otherView removeFromSuperview];
505     [[view superview] replaceSubview:view with:otherView];
506 }
507
508 #pragma mark -
509 #pragma mark Utility Functions
510
511 - (WKFullScreenWindow *)_fullScreenWindow
512 {
513     ASSERT([[self window] isKindOfClass:[WKFullScreenWindow class]]);
514     return (WKFullScreenWindow *)[self window];
515 }
516
517 - (CFTimeInterval)_animationDuration
518 {
519     static const CFTimeInterval defaultDuration = 0.5;
520     CFTimeInterval duration = defaultDuration;
521 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
522     NSUInteger modifierFlags = [NSEvent modifierFlags];
523 #else
524     NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags];
525 #endif
526     if ((modifierFlags & NSControlKeyMask) == NSControlKeyMask)
527         duration *= 2;
528     if ((modifierFlags & NSShiftKeyMask) == NSShiftKeyMask)
529         duration *= 10;
530     if (_forceDisableAnimation) {
531         // This will disable scale animation
532         duration = 0;
533     }
534     return duration;
535 }
536
537 @end
538
539 #pragma mark -
540 @implementation WKFullScreenWindow
541
542 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag
543 {
544     UNUSED_PARAM(aStyle);
545     self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag];
546     if (!self)
547         return nil;
548     [self setOpaque:NO];
549     [self setBackgroundColor:[NSColor clearColor]];
550     [self setIgnoresMouseEvents:NO];
551     [self setAcceptsMouseMovedEvents:YES];
552     [self setReleasedWhenClosed:NO];
553     [self setHasShadow:YES];
554 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
555     [self setMovable:NO];
556 #else
557     [self setMovableByWindowBackground:NO];
558 #endif
559     
560     NSView* contentView = [self contentView];
561     _animationView = [[NSView alloc] initWithFrame:[contentView bounds]];
562     
563     CALayer* contentLayer = [[CALayer alloc] init];
564     [_animationView setLayer:contentLayer];
565     [_animationView setWantsLayer:YES];
566     [_animationView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
567     [contentView addSubview:_animationView];
568     
569     _backgroundLayer = [[CALayer alloc] init];
570     [contentLayer addSublayer:_backgroundLayer];
571     
572     [_backgroundLayer setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)];
573     [_backgroundLayer setOpacity:0];
574     return self;
575 }
576
577 - (void)dealloc
578 {
579     [_animationView release];
580     [_backgroundLayer release];
581     [super dealloc];
582 }
583
584 - (BOOL)canBecomeKeyWindow
585 {
586     return YES;
587 }
588
589 - (void)keyDown:(NSEvent *)theEvent
590 {
591     if ([[theEvent charactersIgnoringModifiers] isEqual:@"\e"]) // Esacpe key-code
592         [self cancelOperation:self];
593     else [super keyDown:theEvent];
594 }
595
596 - (void)cancelOperation:(id)sender
597 {
598     UNUSED_PARAM(sender);
599     [[self windowController] _requestExitFullScreenWithAnimation:YES];
600 }
601
602 - (CALayer*)backgroundLayer
603 {
604     return _backgroundLayer;
605 }
606
607 - (NSView*)animationView
608 {
609     return _animationView;
610 }
611 @end
612
613 #endif