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