Home | History | Annotate | Download | only in WebView
      1 /*
      2  * Copyright (C) 2009 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 #if ENABLE(VIDEO)
     27 
     28 #import "WebVideoFullscreenController.h"
     29 
     30 #import "WebTypesInternal.h"
     31 #import "WebVideoFullscreenHUDWindowController.h"
     32 #import "WebWindowAnimation.h"
     33 #import <IOKit/pwr_mgt/IOPMLib.h>
     34 #import <OSServices/Power.h>
     35 #import <QTKit/QTKit.h>
     36 #import <WebCore/HTMLMediaElement.h>
     37 #import <WebCore/SoftLinking.h>
     38 #import <objc/objc-runtime.h>
     39 #import <wtf/UnusedParam.h>
     40 
     41 #if USE(GSTREAMER)
     42 #import <WebCore/GStreamerGWorld.h>
     43 #endif
     44 
     45 SOFT_LINK_FRAMEWORK(QTKit)
     46 SOFT_LINK_CLASS(QTKit, QTMovieLayer)
     47 
     48 SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *)
     49 
     50 #define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification()
     51 static const NSTimeInterval tickleTimerInterval = 1.0;
     52 
     53 @interface WebVideoFullscreenWindow : NSWindow
     54 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_TIGER)
     55 <NSAnimationDelegate>
     56 #endif
     57 {
     58     SEL _controllerActionOnAnimationEnd;
     59     WebWindowScaleAnimation *_fullscreenAnimation; // (retain)
     60 }
     61 - (void)animateFromRect:(NSRect)startRect toRect:(NSRect)endRect withSubAnimation:(NSAnimation *)subAnimation controllerAction:(SEL)controllerAction;
     62 @end
     63 
     64 @interface WebVideoFullscreenController(HUDWindowControllerDelegate) <WebVideoFullscreenHUDWindowControllerDelegate>
     65 - (void)requestExitFullscreenWithAnimation:(BOOL)animation;
     66 - (void)updateMenuAndDockForFullscreen;
     67 - (void)updatePowerAssertions;
     68 @end
     69 
     70 @interface NSWindow(IsOnActiveSpaceAdditionForTigerAndLeopard)
     71 - (BOOL)isOnActiveSpace;
     72 @end
     73 
     74 @implementation WebVideoFullscreenController
     75 - (id)init
     76 {
     77     // Do not defer window creation, to make sure -windowNumber is created (needed by WebWindowScaleAnimation).
     78     NSWindow *window = [[WebVideoFullscreenWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
     79     self = [super initWithWindow:window];
     80     [window release];
     81     if (!self)
     82         return nil;
     83     [self windowDidLoad];
     84     return self;
     85 
     86 }
     87 - (void)dealloc
     88 {
     89     ASSERT(!_backgroundFullscreenWindow);
     90     ASSERT(!_fadeAnimation);
     91     [_tickleTimer invalidate];
     92     [_tickleTimer release];
     93     _tickleTimer = nil;
     94     [[NSNotificationCenter defaultCenter] removeObserver:self];
     95     [super dealloc];
     96 }
     97 
     98 - (WebVideoFullscreenWindow *)fullscreenWindow
     99 {
    100     return (WebVideoFullscreenWindow *)[super window];
    101 }
    102 
    103 - (void)setupVideoOverlay:(QTMovieLayer*)layer
    104 {
    105     WebVideoFullscreenWindow *window = [self fullscreenWindow];
    106 #if USE(GSTREAMER)
    107     if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::GStreamerGWorldType) {
    108         WebCore::GStreamerGWorld* gstGworld = _mediaElement->platformMedia().media.gstreamerGWorld;
    109         if (gstGworld->enterFullscreen())
    110             [window setContentView:gstGworld->platformVideoWindow()->window()];
    111     }
    112 #else
    113     [[window contentView] setLayer:layer];
    114     [[window contentView] setWantsLayer:YES];
    115     if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::QTMovieType)
    116         [layer setMovie:_mediaElement->platformMedia().media.qtMovie];
    117 #endif
    118 }
    119 
    120 - (void)windowDidLoad
    121 {
    122 #ifdef BUILDING_ON_TIGER
    123     // WebVideoFullscreenController is not supported on Tiger:
    124     ASSERT_NOT_REACHED();
    125 #else
    126     WebVideoFullscreenWindow *window = [self fullscreenWindow];
    127     [window setHasShadow:YES]; // This is nicer with a shadow.
    128     [window setLevel:NSPopUpMenuWindowLevel-1];
    129 
    130     QTMovieLayer *layer = [[getQTMovieLayerClass() alloc] init];
    131     [self setupVideoOverlay:layer];
    132     [layer release];
    133 
    134 #if !USE(GSTREAMER)
    135     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidResignActive:) name:NSApplicationDidResignActiveNotification object:NSApp];
    136     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeScreenParameters:) name:NSApplicationDidChangeScreenParametersNotification object:NSApp];
    137 #endif
    138 #endif
    139 }
    140 
    141 - (WebCore::HTMLMediaElement*)mediaElement
    142 {
    143     return _mediaElement.get();
    144 }
    145 
    146 - (void)setMediaElement:(WebCore::HTMLMediaElement*)mediaElement
    147 {
    148 #ifdef BUILDING_ON_TIGER
    149     // WebVideoFullscreenController is not supported on Tiger:
    150     ASSERT_NOT_REACHED();
    151 #else
    152     _mediaElement = mediaElement;
    153     if ([self isWindowLoaded]) {
    154         QTMovieLayer *movieLayer = (QTMovieLayer *)[[[self fullscreenWindow] contentView] layer];
    155 
    156         ASSERT(movieLayer && [movieLayer isKindOfClass:[getQTMovieLayerClass() class]]);
    157         [self setupVideoOverlay:movieLayer];
    158 #if !USE(GSTREAMER)
    159         ASSERT([movieLayer movie]);
    160         [[NSNotificationCenter defaultCenter] addObserver:self
    161                                                  selector:@selector(rateChanged:)
    162                                                      name:QTMovieRateDidChangeNotification
    163                                                    object:[movieLayer movie]];
    164 #endif
    165     }
    166 #endif
    167 }
    168 
    169 - (id <WebVideoFullscreenControllerDelegate>)delegate
    170 {
    171     return _delegate;
    172 }
    173 
    174 - (void)setDelegate:(id <WebVideoFullscreenControllerDelegate>)delegate
    175 {
    176     _delegate = delegate;
    177 }
    178 
    179 - (CGFloat)clearFadeAnimation
    180 {
    181     [_fadeAnimation stopAnimation];
    182     CGFloat previousAlpha = [_fadeAnimation currentAlpha];
    183     [_fadeAnimation setWindow:nil];
    184     [_fadeAnimation release];
    185     _fadeAnimation = nil;
    186     return previousAlpha;
    187 }
    188 
    189 - (void)windowDidExitFullscreen
    190 {
    191 #if USE(GSTREAMER)
    192     if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::GStreamerGWorldType)
    193         _mediaElement->platformMedia().media.gstreamerGWorld->exitFullscreen();
    194 #endif
    195     [self clearFadeAnimation];
    196     [[self window] close];
    197     [self setWindow:nil];
    198     [self updateMenuAndDockForFullscreen];
    199     [self updatePowerAssertions];
    200     [_hudController setDelegate:nil];
    201     [_hudController release];
    202     _hudController = nil;
    203     [_backgroundFullscreenWindow close];
    204     [_backgroundFullscreenWindow release];
    205     _backgroundFullscreenWindow = nil;
    206 
    207     [self autorelease]; // Associated -retain is in -exitFullscreen.
    208     _isEndingFullscreen = NO;
    209 }
    210 
    211 - (void)windowDidEnterFullscreen
    212 {
    213     [self clearFadeAnimation];
    214 
    215     ASSERT(!_hudController);
    216     _hudController = [[WebVideoFullscreenHUDWindowController alloc] init];
    217     [_hudController setDelegate:self];
    218 
    219     [self updateMenuAndDockForFullscreen];
    220     [self updatePowerAssertions];
    221     [NSCursor setHiddenUntilMouseMoves:YES];
    222 
    223     // Give the HUD keyboard focus initially
    224     [_hudController fadeWindowIn];
    225 }
    226 
    227 - (NSRect)mediaElementRect
    228 {
    229     return _mediaElement->screenRect();
    230 }
    231 
    232 - (void)applicationDidResignActive:(NSNotification*)notification
    233 {
    234     // Check to see if the fullscreenWindow is on the active space; this function is available
    235     // on 10.6 and later, so default to YES if the function is not available:
    236     NSWindow* fullscreenWindow = [self fullscreenWindow];
    237     BOOL isOnActiveSpace = ([fullscreenWindow respondsToSelector:@selector(isOnActiveSpace)] ? [fullscreenWindow isOnActiveSpace] : YES);
    238 
    239     // Replicate the QuickTime Player (X) behavior when losing active application status:
    240     // Is the fullscreen screen the main screen? (Note: this covers the case where only a
    241     // single screen is available.)  Is the fullscreen screen on the current space? IFF so,
    242     // then exit fullscreen mode.
    243     if ([fullscreenWindow screen] == [[NSScreen screens] objectAtIndex:0] && isOnActiveSpace)
    244          [self requestExitFullscreenWithAnimation:NO];
    245 }
    246 
    247 
    248 // MARK: -
    249 // MARK: Exposed Interface
    250 
    251 static void constrainFrameToRatioOfFrame(NSRect *frameToConstrain, const NSRect *frame)
    252 {
    253     // Keep a constrained aspect ratio for the destination window
    254     double originalRatio = frame->size.width / frame->size.height;
    255     double newRatio = frameToConstrain->size.width / frameToConstrain->size.height;
    256     if (newRatio > originalRatio) {
    257         double newWidth = originalRatio * frameToConstrain->size.height;
    258         double diff = frameToConstrain->size.width - newWidth;
    259         frameToConstrain->size.width = newWidth;
    260         frameToConstrain->origin.x += diff / 2;
    261     } else {
    262         double newHeight = frameToConstrain->size.width / originalRatio;
    263         double diff = frameToConstrain->size.height - newHeight;
    264         frameToConstrain->size.height = newHeight;
    265         frameToConstrain->origin.y += diff / 2;
    266     }
    267 }
    268 
    269 static NSWindow *createBackgroundFullscreenWindow(NSRect frame, int level)
    270 {
    271     NSWindow *window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
    272     [window setOpaque:YES];
    273     [window setBackgroundColor:[NSColor blackColor]];
    274     [window setLevel:level];
    275     [window setReleasedWhenClosed:NO];
    276     return window;
    277 }
    278 
    279 - (void)setupFadeAnimationIfNeededAndFadeIn:(BOOL)fadeIn
    280 {
    281     CGFloat initialAlpha = fadeIn ? 0 : 1;
    282     if (_fadeAnimation) {
    283         // Make sure we support queuing animation if the previous one isn't over yet
    284         initialAlpha = [self clearFadeAnimation];
    285     }
    286     if (!_forceDisableAnimation)
    287         _fadeAnimation = [[WebWindowFadeAnimation alloc] initWithDuration:0.2 window:_backgroundFullscreenWindow initialAlpha:initialAlpha finalAlpha:fadeIn ? 1 : 0];
    288 }
    289 
    290 - (void)enterFullscreen:(NSScreen *)screen
    291 {
    292     if (!screen)
    293         screen = [NSScreen mainScreen];
    294 
    295     NSRect frame = [self mediaElementRect];
    296     NSRect endFrame = [screen frame];
    297     constrainFrameToRatioOfFrame(&endFrame, &frame);
    298 
    299     // Create a black window if needed
    300     if (!_backgroundFullscreenWindow)
    301         _backgroundFullscreenWindow = createBackgroundFullscreenWindow([screen frame], [[self window] level]-1);
    302     else
    303         [_backgroundFullscreenWindow setFrame:[screen frame] display:NO];
    304 
    305     [self setupFadeAnimationIfNeededAndFadeIn:YES];
    306     if (_forceDisableAnimation) {
    307         // This will disable scale animation
    308         frame = NSZeroRect;
    309     }
    310     [[self fullscreenWindow] animateFromRect:frame toRect:endFrame withSubAnimation:_fadeAnimation controllerAction:@selector(windowDidEnterFullscreen)];
    311 
    312     [_backgroundFullscreenWindow orderWindow:NSWindowBelow relativeTo:[[self fullscreenWindow] windowNumber]];
    313 }
    314 
    315 - (void)exitFullscreen
    316 {
    317     if (_isEndingFullscreen)
    318         return;
    319     _isEndingFullscreen = YES;
    320     [_hudController closeWindow];
    321 
    322     NSRect endFrame = [self mediaElementRect];
    323 
    324     [self setupFadeAnimationIfNeededAndFadeIn:NO];
    325     if (_forceDisableAnimation) {
    326         // This will disable scale animation
    327         endFrame = NSZeroRect;
    328     }
    329 
    330     // We have to retain ourselves because we want to be alive for the end of the animation.
    331     // If our owner releases us we could crash if this is not the case.
    332     // Balanced in windowDidExitFullscreen
    333     [self retain];
    334 
    335     [[self fullscreenWindow] animateFromRect:[[self window] frame] toRect:endFrame withSubAnimation:_fadeAnimation controllerAction:@selector(windowDidExitFullscreen)];
    336 }
    337 
    338 - (void)applicationDidChangeScreenParameters:(NSNotification*)notification
    339 {
    340     // The user may have changed the main screen by moving the menu bar, or they may have changed
    341     // the Dock's size or location, or they may have changed the fullscreen screen's dimensions.
    342     // Update our presentation parameters, and ensure that the full screen window occupies the
    343     // entire screen:
    344     [self updateMenuAndDockForFullscreen];
    345     [[self window] setFrame:[[[self window] screen] frame] display:YES];
    346 }
    347 
    348 - (void)updateMenuAndDockForFullscreen
    349 {
    350     // NSApplicationPresentationOptions is available on > 10.6 only:
    351 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    352     NSApplicationPresentationOptions options = NSApplicationPresentationDefault;
    353     NSScreen* fullscreenScreen = [[self window] screen];
    354 
    355     if (!_isEndingFullscreen) {
    356         // Auto-hide the menu bar if the fullscreenScreen contains the menu bar:
    357         // NOTE: if the fullscreenScreen contains the menu bar but not the dock, we must still
    358         // auto-hide the dock, or an exception will be thrown.
    359         if ([[NSScreen screens] objectAtIndex:0] == fullscreenScreen)
    360             options |= (NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock);
    361         // Check if the current screen contains the dock by comparing the screen's frame to its
    362         // visibleFrame; if a dock is present, the visibleFrame will differ.  If the current screen
    363         // contains the dock, hide it.
    364         else if (!NSEqualRects([fullscreenScreen frame], [fullscreenScreen visibleFrame]))
    365             options |= NSApplicationPresentationAutoHideDock;
    366     }
    367 
    368     if ([NSApp respondsToSelector:@selector(setPresentationOptions:)])
    369         [NSApp setPresentationOptions:options];
    370     else
    371 #endif
    372         SetSystemUIMode(_isEndingFullscreen ? kUIModeNormal : kUIModeAllHidden, 0);
    373 }
    374 
    375 #if !defined(BUILDING_ON_TIGER) // IOPMAssertionCreateWithName not defined on < 10.5
    376 - (void)_disableIdleDisplaySleep
    377 {
    378     if (_idleDisplaySleepAssertion == kIOPMNullAssertionID)
    379 #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK
    380         IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &_idleDisplaySleepAssertion);
    381 #else // IOPMAssertionCreate is depreciated in > 10.5
    382         IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullscreen."), &_idleDisplaySleepAssertion);
    383 #endif
    384 }
    385 
    386 - (void)_enableIdleDisplaySleep
    387 {
    388     if (_idleDisplaySleepAssertion != kIOPMNullAssertionID) {
    389         IOPMAssertionRelease(_idleDisplaySleepAssertion);
    390         _idleDisplaySleepAssertion = kIOPMNullAssertionID;
    391     }
    392 }
    393 
    394 - (void)_disableIdleSystemSleep
    395 {
    396     if (_idleSystemSleepAssertion == kIOPMNullAssertionID)
    397 #if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK
    398         IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_idleSystemSleepAssertion);
    399 #else // IOPMAssertionCreate is depreciated in > 10.5
    400     IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullscreen."), &_idleSystemSleepAssertion);
    401 #endif
    402 }
    403 
    404 - (void)_enableIdleSystemSleep
    405 {
    406     if (_idleSystemSleepAssertion != kIOPMNullAssertionID) {
    407         IOPMAssertionRelease(_idleSystemSleepAssertion);
    408         _idleSystemSleepAssertion = kIOPMNullAssertionID;
    409     }
    410 }
    411 
    412 - (void)_enableTickleTimer
    413 {
    414     [_tickleTimer invalidate];
    415     [_tickleTimer release];
    416     _tickleTimer = [[NSTimer scheduledTimerWithTimeInterval:tickleTimerInterval target:self selector:@selector(_tickleTimerFired) userInfo:nil repeats:YES] retain];
    417 }
    418 
    419 - (void)_disableTickleTimer
    420 {
    421     [_tickleTimer invalidate];
    422     [_tickleTimer release];
    423     _tickleTimer = nil;
    424 }
    425 
    426 - (void)_tickleTimerFired
    427 {
    428     UpdateSystemActivity(OverallAct);
    429 }
    430 #endif
    431 
    432 - (void)updatePowerAssertions
    433 {
    434 #if !defined(BUILDING_ON_TIGER)
    435     float rate = 0;
    436     if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::QTMovieType)
    437         rate = [_mediaElement->platformMedia().media.qtMovie rate];
    438 
    439     if (rate && !_isEndingFullscreen) {
    440         [self _disableIdleSystemSleep];
    441         [self _disableIdleDisplaySleep];
    442         [self _enableTickleTimer];
    443     } else {
    444         [self _enableIdleSystemSleep];
    445         [self _enableIdleDisplaySleep];
    446         [self _disableTickleTimer];
    447     }
    448 #endif
    449 }
    450 
    451 // MARK: -
    452 // MARK: Window callback
    453 
    454 - (void)_requestExit
    455 {
    456     if (_mediaElement)
    457         _mediaElement->exitFullscreen();
    458     _forceDisableAnimation = NO;
    459 }
    460 
    461 - (void)requestExitFullscreenWithAnimation:(BOOL)animation
    462 {
    463     if (_isEndingFullscreen)
    464         return;
    465 
    466     _forceDisableAnimation = !animation;
    467     [self performSelector:@selector(_requestExit) withObject:nil afterDelay:0];
    468 
    469 }
    470 
    471 - (void)requestExitFullscreen
    472 {
    473     [self requestExitFullscreenWithAnimation:YES];
    474 }
    475 
    476 - (void)fadeHUDIn
    477 {
    478     [_hudController fadeWindowIn];
    479 }
    480 
    481 // MARK: -
    482 // MARK: QTMovie callbacks
    483 
    484 - (void)rateChanged:(NSNotification *)unusedNotification
    485 {
    486     UNUSED_PARAM(unusedNotification);
    487     [_hudController updateRate];
    488     [self updatePowerAssertions];
    489 }
    490 
    491 @end
    492 
    493 @implementation WebVideoFullscreenWindow
    494 
    495 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag
    496 {
    497     UNUSED_PARAM(aStyle);
    498     self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag];
    499     if (!self)
    500         return nil;
    501     [self setOpaque:NO];
    502     [self setBackgroundColor:[NSColor clearColor]];
    503     [self setIgnoresMouseEvents:NO];
    504     [self setAcceptsMouseMovedEvents:YES];
    505     return self;
    506 }
    507 
    508 - (void)dealloc
    509 {
    510     ASSERT(!_fullscreenAnimation);
    511     [super dealloc];
    512 }
    513 
    514 - (BOOL)resignFirstResponder
    515 {
    516     return NO;
    517 }
    518 
    519 - (BOOL)canBecomeKeyWindow
    520 {
    521     return NO;
    522 }
    523 
    524 - (void)mouseDown:(NSEvent *)theEvent
    525 {
    526     UNUSED_PARAM(theEvent);
    527 }
    528 
    529 - (void)cancelOperation:(id)sender
    530 {
    531     UNUSED_PARAM(sender);
    532     [[self windowController] requestExitFullscreen];
    533 }
    534 
    535 - (void)animatedResizeDidEnd
    536 {
    537     // Call our windowController.
    538     if (_controllerActionOnAnimationEnd)
    539         [[self windowController] performSelector:_controllerActionOnAnimationEnd];
    540     _controllerActionOnAnimationEnd = NULL;
    541 }
    542 
    543 //
    544 // This function will animate a change of frame rectangle
    545 // We support queuing animation, that means that we'll correctly
    546 // interrupt the running animation, and queue the next one.
    547 //
    548 - (void)animateFromRect:(NSRect)startRect toRect:(NSRect)endRect withSubAnimation:(NSAnimation *)subAnimation controllerAction:(SEL)controllerAction
    549 {
    550     _controllerActionOnAnimationEnd = controllerAction;
    551 
    552     BOOL wasAnimating = NO;
    553     if (_fullscreenAnimation) {
    554         wasAnimating = YES;
    555 
    556         // Interrupt any running animation.
    557         [_fullscreenAnimation stopAnimation];
    558 
    559         // Save the current rect to ensure a smooth transition.
    560         startRect = [_fullscreenAnimation currentFrame];
    561         [_fullscreenAnimation release];
    562         _fullscreenAnimation = nil;
    563     }
    564 
    565     if (NSIsEmptyRect(startRect) || NSIsEmptyRect(endRect)) {
    566         // Fakely end the subanimation.
    567         [subAnimation setCurrentProgress:1.0];
    568         // And remove the weak link to the window.
    569         [subAnimation stopAnimation];
    570 
    571         [self setFrame:endRect display:NO];
    572         [self makeKeyAndOrderFront:self];
    573         [self animatedResizeDidEnd];
    574         return;
    575     }
    576 
    577     if (!wasAnimating) {
    578         // We'll downscale the window during the animation based on the higher resolution rect
    579         BOOL higherResolutionIsEndRect = startRect.size.width < endRect.size.width && startRect.size.height < endRect.size.height;
    580         [self setFrame:higherResolutionIsEndRect ? endRect : startRect display:NO];
    581     }
    582 
    583     ASSERT(!_fullscreenAnimation);
    584     _fullscreenAnimation = [[WebWindowScaleAnimation alloc] initWithHintedDuration:0.2 window:self initalFrame:startRect finalFrame:endRect];
    585     [_fullscreenAnimation setSubAnimation:subAnimation];
    586     [_fullscreenAnimation setDelegate:self];
    587 
    588     // Make sure the animation has scaled the window before showing it.
    589     [_fullscreenAnimation setCurrentProgress:0];
    590     [self makeKeyAndOrderFront:self];
    591 
    592     [_fullscreenAnimation startAnimation];
    593 }
    594 
    595 - (void)animationDidEnd:(NSAnimation *)animation
    596 {
    597 #if !defined(BUILDING_ON_TIGER) // Animations are never threaded on Tiger.
    598     if (![NSThread isMainThread]) {
    599         [self performSelectorOnMainThread:@selector(animationDidEnd:) withObject:animation waitUntilDone:NO];
    600         return;
    601     }
    602 #endif
    603     if (animation != _fullscreenAnimation)
    604         return;
    605 
    606     // The animation is not really over and was interrupted
    607     // Don't send completion events.
    608     if ([animation currentProgress] < 1.0)
    609         return;
    610 
    611     // Ensure that animation (and subanimation) don't keep
    612     // the weak reference to the window ivar that may be destroyed from
    613     // now on.
    614     [_fullscreenAnimation setWindow:nil];
    615 
    616     [_fullscreenAnimation autorelease];
    617     _fullscreenAnimation = nil;
    618 
    619     [self animatedResizeDidEnd];
    620 }
    621 
    622 - (void)mouseMoved:(NSEvent *)theEvent
    623 {
    624     [[self windowController] fadeHUDIn];
    625 }
    626 
    627 @end
    628 
    629 #endif /* ENABLE(VIDEO) */
    630