Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2007, 2008, 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 COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #import "config.h"
     27 
     28 #if ENABLE(VIDEO)
     29 
     30 #import "MediaPlayerPrivateQTKit.h"
     31 
     32 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
     33 #include "ApplicationCacheHost.h"
     34 #include "ApplicationCacheResource.h"
     35 #include "DocumentLoader.h"
     36 #endif
     37 
     38 #ifdef BUILDING_ON_TIGER
     39 #import "AutodrainedPool.h"
     40 #endif
     41 
     42 #import "BlockExceptions.h"
     43 #import "DocumentLoader.h"
     44 #import "FrameView.h"
     45 #import "GraphicsContext.h"
     46 #import "KURL.h"
     47 #import "MIMETypeRegistry.h"
     48 #import "SoftLinking.h"
     49 #import "TimeRanges.h"
     50 #import "WebCoreSystemInterface.h"
     51 #import <QTKit/QTKit.h>
     52 #import <objc/objc-runtime.h>
     53 #import <wtf/UnusedParam.h>
     54 
     55 #if USE(ACCELERATED_COMPOSITING)
     56 #include "GraphicsLayer.h"
     57 #endif
     58 
     59 #if DRAW_FRAME_RATE
     60 #import "Font.h"
     61 #import "Frame.h"
     62 #import "Document.h"
     63 #import "RenderObject.h"
     64 #import "RenderStyle.h"
     65 #endif
     66 
     67 #ifdef BUILDING_ON_TIGER
     68 static IMP method_setImplementation(Method m, IMP imp)
     69 {
     70     IMP result = m->method_imp;
     71     m->method_imp = imp;
     72     return result;
     73 }
     74 #endif
     75 
     76 SOFT_LINK_FRAMEWORK(QTKit)
     77 
     78 SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (timeValue, timeScale))
     79 
     80 SOFT_LINK_CLASS(QTKit, QTMovie)
     81 SOFT_LINK_CLASS(QTKit, QTMovieView)
     82 SOFT_LINK_CLASS(QTKit, QTMovieLayer)
     83 
     84 SOFT_LINK_POINTER(QTKit, QTTrackMediaTypeAttribute, NSString *)
     85 SOFT_LINK_POINTER(QTKit, QTMediaTypeAttribute, NSString *)
     86 SOFT_LINK_POINTER(QTKit, QTMediaTypeBase, NSString *)
     87 SOFT_LINK_POINTER(QTKit, QTMediaTypeMPEG, NSString *)
     88 SOFT_LINK_POINTER(QTKit, QTMediaTypeSound, NSString *)
     89 SOFT_LINK_POINTER(QTKit, QTMediaTypeText, NSString *)
     90 SOFT_LINK_POINTER(QTKit, QTMediaTypeVideo, NSString *)
     91 SOFT_LINK_POINTER(QTKit, QTMovieAskUnresolvedDataRefsAttribute, NSString *)
     92 SOFT_LINK_POINTER(QTKit, QTMovieLoopsAttribute, NSString *)
     93 SOFT_LINK_POINTER(QTKit, QTMovieDataAttribute, NSString *)
     94 SOFT_LINK_POINTER(QTKit, QTMovieDataSizeAttribute, NSString *)
     95 SOFT_LINK_POINTER(QTKit, QTMovieDidEndNotification, NSString *)
     96 SOFT_LINK_POINTER(QTKit, QTMovieHasVideoAttribute, NSString *)
     97 SOFT_LINK_POINTER(QTKit, QTMovieHasAudioAttribute, NSString *)
     98 SOFT_LINK_POINTER(QTKit, QTMovieIsActiveAttribute, NSString *)
     99 SOFT_LINK_POINTER(QTKit, QTMovieLoadStateAttribute, NSString *)
    100 SOFT_LINK_POINTER(QTKit, QTMovieLoadStateDidChangeNotification, NSString *)
    101 SOFT_LINK_POINTER(QTKit, QTMovieNaturalSizeAttribute, NSString *)
    102 SOFT_LINK_POINTER(QTKit, QTMovieCurrentSizeAttribute, NSString *)
    103 SOFT_LINK_POINTER(QTKit, QTMoviePreventExternalURLLinksAttribute, NSString *)
    104 SOFT_LINK_POINTER(QTKit, QTMovieRateChangesPreservePitchAttribute, NSString *)
    105 SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *)
    106 SOFT_LINK_POINTER(QTKit, QTMovieSizeDidChangeNotification, NSString *)
    107 SOFT_LINK_POINTER(QTKit, QTMovieTimeDidChangeNotification, NSString *)
    108 SOFT_LINK_POINTER(QTKit, QTMovieTimeScaleAttribute, NSString *)
    109 SOFT_LINK_POINTER(QTKit, QTMovieURLAttribute, NSString *)
    110 SOFT_LINK_POINTER(QTKit, QTMovieVolumeDidChangeNotification, NSString *)
    111 SOFT_LINK_POINTER(QTKit, QTSecurityPolicyNoCrossSiteAttribute, NSString *)
    112 SOFT_LINK_POINTER(QTKit, QTVideoRendererWebKitOnlyNewImageAvailableNotification, NSString *)
    113 #ifndef BUILDING_ON_TIGER
    114 SOFT_LINK_POINTER(QTKit, QTMovieApertureModeClean, NSString *)
    115 SOFT_LINK_POINTER(QTKit, QTMovieApertureModeAttribute, NSString *)
    116 #endif
    117 
    118 #define QTMovie getQTMovieClass()
    119 #define QTMovieView getQTMovieViewClass()
    120 #define QTMovieLayer getQTMovieLayerClass()
    121 
    122 #define QTTrackMediaTypeAttribute getQTTrackMediaTypeAttribute()
    123 #define QTMediaTypeAttribute getQTMediaTypeAttribute()
    124 #define QTMediaTypeBase getQTMediaTypeBase()
    125 #define QTMediaTypeMPEG getQTMediaTypeMPEG()
    126 #define QTMediaTypeSound getQTMediaTypeSound()
    127 #define QTMediaTypeText getQTMediaTypeText()
    128 #define QTMediaTypeVideo getQTMediaTypeVideo()
    129 #define QTMovieAskUnresolvedDataRefsAttribute getQTMovieAskUnresolvedDataRefsAttribute()
    130 #define QTMovieLoopsAttribute getQTMovieLoopsAttribute()
    131 #define QTMovieDataAttribute getQTMovieDataAttribute()
    132 #define QTMovieDataSizeAttribute getQTMovieDataSizeAttribute()
    133 #define QTMovieDidEndNotification getQTMovieDidEndNotification()
    134 #define QTMovieHasVideoAttribute getQTMovieHasVideoAttribute()
    135 #define QTMovieHasAudioAttribute getQTMovieHasAudioAttribute()
    136 #define QTMovieIsActiveAttribute getQTMovieIsActiveAttribute()
    137 #define QTMovieLoadStateAttribute getQTMovieLoadStateAttribute()
    138 #define QTMovieLoadStateDidChangeNotification getQTMovieLoadStateDidChangeNotification()
    139 #define QTMovieNaturalSizeAttribute getQTMovieNaturalSizeAttribute()
    140 #define QTMovieCurrentSizeAttribute getQTMovieCurrentSizeAttribute()
    141 #define QTMoviePreventExternalURLLinksAttribute getQTMoviePreventExternalURLLinksAttribute()
    142 #define QTMovieRateChangesPreservePitchAttribute getQTMovieRateChangesPreservePitchAttribute()
    143 #define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification()
    144 #define QTMovieSizeDidChangeNotification getQTMovieSizeDidChangeNotification()
    145 #define QTMovieTimeDidChangeNotification getQTMovieTimeDidChangeNotification()
    146 #define QTMovieTimeScaleAttribute getQTMovieTimeScaleAttribute()
    147 #define QTMovieURLAttribute getQTMovieURLAttribute()
    148 #define QTMovieVolumeDidChangeNotification getQTMovieVolumeDidChangeNotification()
    149 #define QTSecurityPolicyNoCrossSiteAttribute getQTSecurityPolicyNoCrossSiteAttribute()
    150 #define QTVideoRendererWebKitOnlyNewImageAvailableNotification getQTVideoRendererWebKitOnlyNewImageAvailableNotification()
    151 #ifndef BUILDING_ON_TIGER
    152 #define QTMovieApertureModeClean getQTMovieApertureModeClean()
    153 #define QTMovieApertureModeAttribute getQTMovieApertureModeAttribute()
    154 #endif
    155 
    156 // Older versions of the QTKit header don't have these constants.
    157 #if !defined QTKIT_VERSION_MAX_ALLOWED || QTKIT_VERSION_MAX_ALLOWED <= QTKIT_VERSION_7_0
    158 enum {
    159     QTMovieLoadStateError = -1L,
    160     QTMovieLoadStateLoaded  = 2000L,
    161     QTMovieLoadStatePlayable = 10000L,
    162     QTMovieLoadStatePlaythroughOK = 20000L,
    163     QTMovieLoadStateComplete = 100000L
    164 };
    165 #endif
    166 
    167 @interface FakeQTMovieView : NSObject
    168 - (WebCoreMovieObserver *)delegate;
    169 @end
    170 
    171 using namespace WebCore;
    172 using namespace std;
    173 
    174 @interface WebCoreMovieObserver : NSObject
    175 {
    176     MediaPlayerPrivateQTKit* m_callback;
    177     NSView* m_view;
    178     BOOL m_delayCallbacks;
    179 }
    180 -(id)initWithCallback:(MediaPlayerPrivateQTKit*)callback;
    181 -(void)disconnect;
    182 -(void)setView:(NSView*)view;
    183 -(void)repaint;
    184 -(void)setDelayCallbacks:(BOOL)shouldDelay;
    185 -(void)loadStateChanged:(NSNotification *)notification;
    186 -(void)rateChanged:(NSNotification *)notification;
    187 -(void)sizeChanged:(NSNotification *)notification;
    188 -(void)timeChanged:(NSNotification *)notification;
    189 -(void)didEnd:(NSNotification *)notification;
    190 @end
    191 
    192 @protocol WebKitVideoRenderingDetails
    193 -(void)setMovie:(id)movie;
    194 -(void)drawInRect:(NSRect)rect;
    195 @end
    196 
    197 namespace WebCore {
    198 
    199 #ifdef BUILDING_ON_TIGER
    200 static const long minimumQuickTimeVersion = 0x07300000; // 7.3
    201 #endif
    202 
    203 
    204 MediaPlayerPrivateInterface* MediaPlayerPrivateQTKit::create(MediaPlayer* player)
    205 {
    206     return new MediaPlayerPrivateQTKit(player);
    207 }
    208 
    209 void MediaPlayerPrivateQTKit::registerMediaEngine(MediaEngineRegistrar registrar)
    210 {
    211     if (isAvailable())
    212         registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
    213 }
    214 
    215 MediaPlayerPrivateQTKit::MediaPlayerPrivateQTKit(MediaPlayer* player)
    216     : m_player(player)
    217     , m_objcObserver(AdoptNS, [[WebCoreMovieObserver alloc] initWithCallback:this])
    218     , m_seekTo(-1)
    219     , m_seekTimer(this, &MediaPlayerPrivateQTKit::seekTimerFired)
    220     , m_networkState(MediaPlayer::Empty)
    221     , m_readyState(MediaPlayer::HaveNothing)
    222     , m_rect()
    223     , m_scaleFactor(1, 1)
    224     , m_enabledTrackCount(0)
    225     , m_totalTrackCount(0)
    226     , m_reportedDuration(-1)
    227     , m_cachedDuration(-1)
    228     , m_timeToRestore(-1)
    229     , m_preload(MediaPlayer::Auto)
    230     , m_startedPlaying(false)
    231     , m_isStreaming(false)
    232     , m_visible(false)
    233     , m_hasUnsupportedTracks(false)
    234     , m_videoFrameHasDrawn(false)
    235     , m_isAllowedToRender(false)
    236     , m_privateBrowsing(false)
    237 #if DRAW_FRAME_RATE
    238     , m_frameCountWhilePlaying(0)
    239     , m_timeStartedPlaying(0)
    240     , m_timeStoppedPlaying(0)
    241 #endif
    242 {
    243 }
    244 
    245 MediaPlayerPrivateQTKit::~MediaPlayerPrivateQTKit()
    246 {
    247     tearDownVideoRendering();
    248 
    249     [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
    250     [m_objcObserver.get() disconnect];
    251 }
    252 
    253 NSMutableDictionary* MediaPlayerPrivateQTKit::commonMovieAttributes()
    254 {
    255     return [NSMutableDictionary dictionaryWithObjectsAndKeys:
    256             [NSNumber numberWithBool:m_player->preservesPitch()], QTMovieRateChangesPreservePitchAttribute,
    257             [NSNumber numberWithBool:YES], QTMoviePreventExternalURLLinksAttribute,
    258             [NSNumber numberWithBool:YES], QTSecurityPolicyNoCrossSiteAttribute,
    259             [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute,
    260             [NSNumber numberWithBool:NO], QTMovieLoopsAttribute,
    261             [NSNumber numberWithBool:!m_privateBrowsing], @"QTMovieAllowPersistentCacheAttribute",
    262 #ifndef BUILDING_ON_TIGER
    263             QTMovieApertureModeClean, QTMovieApertureModeAttribute,
    264 #endif
    265             nil];
    266 }
    267 
    268 void MediaPlayerPrivateQTKit::createQTMovie(const String& url)
    269 {
    270     NSURL *cocoaURL = KURL(ParsedURLString, url);
    271     NSMutableDictionary *movieAttributes = commonMovieAttributes();
    272     [movieAttributes setValue:cocoaURL forKey:QTMovieURLAttribute];
    273 
    274 #if !defined(BUILDING_ON_LEOPARD)
    275     CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
    276     CFArrayRef proxiesForURL = CFNetworkCopyProxiesForURL((CFURLRef)cocoaURL, proxySettings);
    277     BOOL willUseProxy = YES;
    278 
    279     if (!proxiesForURL || !CFArrayGetCount(proxiesForURL))
    280         willUseProxy = NO;
    281 
    282     if (CFArrayGetCount(proxiesForURL) == 1) {
    283         CFDictionaryRef proxy = (CFDictionaryRef)CFArrayGetValueAtIndex(proxiesForURL, 0);
    284         ASSERT(CFGetTypeID(proxy) == CFDictionaryGetTypeID());
    285 
    286         CFStringRef proxyType = (CFStringRef)CFDictionaryGetValue(proxy, kCFProxyTypeKey);
    287         ASSERT(CFGetTypeID(proxyType) == CFStringGetTypeID());
    288 
    289         if (CFStringCompare(proxyType, kCFProxyTypeNone, 0) == kCFCompareEqualTo)
    290             willUseProxy = NO;
    291     }
    292 
    293     if (!willUseProxy) {
    294         // Only pass the QTMovieOpenForPlaybackAttribute flag if there are no proxy servers, due
    295         // to rdar://problem/7531776.
    296         [movieAttributes setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenForPlaybackAttribute"];
    297     }
    298 
    299     if (proxiesForURL)
    300         CFRelease(proxiesForURL);
    301     if (proxySettings)
    302         CFRelease(proxySettings);
    303 #endif
    304 
    305     createQTMovie(cocoaURL, movieAttributes);
    306 }
    307 
    308 void MediaPlayerPrivateQTKit::createQTMovie(ApplicationCacheResource* resource)
    309 {
    310 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
    311     ASSERT(resource);
    312 
    313     NSMutableDictionary *movieAttributes = commonMovieAttributes();
    314     [movieAttributes setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenForPlaybackAttribute"];
    315 
    316     // ApplicationCacheResources can supply either a data pointer, or a path to a locally cached
    317     // flat file.  We would prefer the path over the data, but QTKit can handle either:
    318     String localPath = resource->path();
    319     NSURL* cocoaURL = !localPath.isEmpty() ? [NSURL fileURLWithPath:localPath isDirectory:NO] : nil;
    320     if (cocoaURL)
    321         [movieAttributes setValue:cocoaURL forKey:QTMovieURLAttribute];
    322     else {
    323         NSData* movieData = resource->data()->createNSData();
    324         [movieAttributes setValue:movieData forKey:QTMovieDataAttribute];
    325         [movieData release];
    326     }
    327 
    328     createQTMovie(cocoaURL, movieAttributes);
    329 
    330 #else
    331     ASSERT_NOT_REACHED();
    332 #endif
    333 }
    334 
    335 static void disableComponentsOnce()
    336 {
    337     static bool sComponentsDisabled = false;
    338     if (sComponentsDisabled)
    339         return;
    340     sComponentsDisabled = true;
    341 
    342     // eat/PDF and grip/PDF components must be disabled twice since they are registered twice
    343     // with different flags.  However, there is currently a bug in 64-bit QTKit (<rdar://problem/8378237>)
    344     // which causes subsequent disable component requests of exactly the same type to be ignored if
    345     // QTKitServer has not yet started.  As a result, we must pass in exactly the flags we want to
    346     // disable per component.  As a failsafe, if in the future these flags change, we will disable the
    347     // PDF components for a third time with a wildcard flags field:
    348     uint32_t componentsToDisable[11][5] = {
    349         {'eat ', 'TEXT', 'text', 0, 0},
    350         {'eat ', 'TXT ', 'text', 0, 0},
    351         {'eat ', 'utxt', 'text', 0, 0},
    352         {'eat ', 'TEXT', 'tx3g', 0, 0},
    353         {'eat ', 'PDF ', 'vide', 0x44802, 0},
    354         {'eat ', 'PDF ', 'vide', 0x45802, 0},
    355         {'eat ', 'PDF ', 'vide', 0, 0},
    356         {'grip', 'PDF ', 'appl', 0x844a00, 0},
    357         {'grip', 'PDF ', 'appl', 0x845a00, 0},
    358         {'grip', 'PDF ', 'appl', 0, 0},
    359         {'imdc', 'pdf ', 'appl', 0, 0},
    360     };
    361 
    362     for (size_t i = 0; i < WTF_ARRAY_LENGTH(componentsToDisable); ++i)
    363         wkQTMovieDisableComponent(componentsToDisable[i]);
    364 }
    365 
    366 void MediaPlayerPrivateQTKit::createQTMovie(NSURL *url, NSDictionary *movieAttributes)
    367 {
    368     disableComponentsOnce();
    369 
    370     [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
    371 
    372     bool recreating = false;
    373     if (m_qtMovie) {
    374         recreating = true;
    375         destroyQTVideoRenderer();
    376         m_qtMovie = 0;
    377     }
    378 
    379     // Disable rtsp streams for now, <rdar://problem/5693967>
    380     if (protocolIs([url scheme], "rtsp"))
    381         return;
    382 
    383     NSError *error = nil;
    384     m_qtMovie.adoptNS([[QTMovie alloc] initWithAttributes:movieAttributes error:&error]);
    385 
    386     if (!m_qtMovie)
    387         return;
    388 
    389     [m_qtMovie.get() setVolume:m_player->volume()];
    390 
    391     if (recreating && hasVideo())
    392         createQTVideoRenderer(QTVideoRendererModeListensForNewImages);
    393 
    394     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
    395                                              selector:@selector(loadStateChanged:)
    396                                                  name:QTMovieLoadStateDidChangeNotification
    397                                                object:m_qtMovie.get()];
    398 
    399     // In updateState(), we track when maxTimeLoaded() == duration().
    400     // In newer version of QuickTime, a notification is emitted when maxTimeLoaded changes.
    401     // In older version of QuickTime, QTMovieLoadStateDidChangeNotification be fired.
    402     if (NSString *maxTimeLoadedChangeNotification = wkQTMovieMaxTimeLoadedChangeNotification()) {
    403         [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
    404                                                  selector:@selector(loadStateChanged:)
    405                                                      name:maxTimeLoadedChangeNotification
    406                                                    object:m_qtMovie.get()];
    407     }
    408 
    409     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
    410                                              selector:@selector(rateChanged:)
    411                                                  name:QTMovieRateDidChangeNotification
    412                                                object:m_qtMovie.get()];
    413     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
    414                                              selector:@selector(sizeChanged:)
    415                                                  name:QTMovieSizeDidChangeNotification
    416                                                object:m_qtMovie.get()];
    417     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
    418                                              selector:@selector(timeChanged:)
    419                                                  name:QTMovieTimeDidChangeNotification
    420                                                object:m_qtMovie.get()];
    421     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
    422                                              selector:@selector(didEnd:)
    423                                                  name:QTMovieDidEndNotification
    424                                                object:m_qtMovie.get()];
    425 }
    426 
    427 static void mainThreadSetNeedsDisplay(id self, SEL)
    428 {
    429     id view = [self superview];
    430     ASSERT(!view || [view isKindOfClass:[QTMovieView class]]);
    431     if (!view || ![view isKindOfClass:[QTMovieView class]])
    432         return;
    433 
    434     FakeQTMovieView *movieView = static_cast<FakeQTMovieView *>(view);
    435     WebCoreMovieObserver* delegate = [movieView delegate];
    436     ASSERT(!delegate || [delegate isKindOfClass:[WebCoreMovieObserver class]]);
    437     if (!delegate || ![delegate isKindOfClass:[WebCoreMovieObserver class]])
    438         return;
    439 
    440     [delegate repaint];
    441 }
    442 
    443 static Class QTVideoRendererClass()
    444 {
    445      static Class QTVideoRendererWebKitOnlyClass = NSClassFromString(@"QTVideoRendererWebKitOnly");
    446      return QTVideoRendererWebKitOnlyClass;
    447 }
    448 
    449 void MediaPlayerPrivateQTKit::createQTMovieView()
    450 {
    451     detachQTMovieView();
    452 
    453     static bool addedCustomMethods = false;
    454     if (!m_player->inMediaDocument() && !addedCustomMethods) {
    455         Class QTMovieContentViewClass = NSClassFromString(@"QTMovieContentView");
    456         ASSERT(QTMovieContentViewClass);
    457 
    458         Method mainThreadSetNeedsDisplayMethod = class_getInstanceMethod(QTMovieContentViewClass, @selector(_mainThreadSetNeedsDisplay));
    459         ASSERT(mainThreadSetNeedsDisplayMethod);
    460 
    461         method_setImplementation(mainThreadSetNeedsDisplayMethod, reinterpret_cast<IMP>(mainThreadSetNeedsDisplay));
    462         addedCustomMethods = true;
    463     }
    464 
    465     // delay callbacks as we *will* get notifications during setup
    466     [m_objcObserver.get() setDelayCallbacks:YES];
    467 
    468     m_qtMovieView.adoptNS([[QTMovieView alloc] init]);
    469     setSize(m_player->size());
    470     NSView* parentView = m_player->frameView()->documentView();
    471     [parentView addSubview:m_qtMovieView.get()];
    472 #ifdef BUILDING_ON_TIGER
    473     // setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy
    474     [m_qtMovieView.get() performSelector:@selector(setDelegate:) withObject:m_objcObserver.get()];
    475 #else
    476     [m_qtMovieView.get() setDelegate:m_objcObserver.get()];
    477 #endif
    478     [m_objcObserver.get() setView:m_qtMovieView.get()];
    479     [m_qtMovieView.get() setMovie:m_qtMovie.get()];
    480     [m_qtMovieView.get() setControllerVisible:NO];
    481     [m_qtMovieView.get() setPreservesAspectRatio:NO];
    482     // the area not covered by video should be transparent
    483     [m_qtMovieView.get() setFillColor:[NSColor clearColor]];
    484 
    485     // If we're in a media document, allow QTMovieView to render in its default mode;
    486     // otherwise tell it to draw synchronously.
    487     // Note that we expect mainThreadSetNeedsDisplay to be invoked only when synchronous drawing is requested.
    488     if (!m_player->inMediaDocument())
    489         wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES);
    490 
    491     [m_objcObserver.get() setDelayCallbacks:NO];
    492 }
    493 
    494 void MediaPlayerPrivateQTKit::detachQTMovieView()
    495 {
    496     if (m_qtMovieView) {
    497         [m_objcObserver.get() setView:nil];
    498 #ifdef BUILDING_ON_TIGER
    499         // setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy
    500         [m_qtMovieView.get() performSelector:@selector(setDelegate:) withObject:nil];
    501 #else
    502         [m_qtMovieView.get() setDelegate:nil];
    503 #endif
    504         [m_qtMovieView.get() removeFromSuperview];
    505         m_qtMovieView = nil;
    506     }
    507 }
    508 
    509 void MediaPlayerPrivateQTKit::createQTVideoRenderer(QTVideoRendererMode rendererMode)
    510 {
    511     destroyQTVideoRenderer();
    512 
    513     m_qtVideoRenderer.adoptNS([[QTVideoRendererClass() alloc] init]);
    514     if (!m_qtVideoRenderer)
    515         return;
    516 
    517     // associate our movie with our instance of QTVideoRendererWebKitOnly
    518     [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:m_qtMovie.get()];
    519 
    520     if (rendererMode == QTVideoRendererModeListensForNewImages) {
    521         // listen to QTVideoRendererWebKitOnly's QTVideoRendererWebKitOnlyNewImageDidBecomeAvailableNotification
    522         [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
    523                                                  selector:@selector(newImageAvailable:)
    524                                                      name:QTVideoRendererWebKitOnlyNewImageAvailableNotification
    525                                                    object:m_qtVideoRenderer.get()];
    526     }
    527 }
    528 
    529 void MediaPlayerPrivateQTKit::destroyQTVideoRenderer()
    530 {
    531     if (!m_qtVideoRenderer)
    532         return;
    533 
    534     // stop observing the renderer's notifications before we toss it
    535     [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()
    536                                                     name:QTVideoRendererWebKitOnlyNewImageAvailableNotification
    537                                                   object:m_qtVideoRenderer.get()];
    538 
    539     // disassociate our movie from our instance of QTVideoRendererWebKitOnly
    540     [(id<WebKitVideoRenderingDetails>)m_qtVideoRenderer.get() setMovie:nil];
    541 
    542     m_qtVideoRenderer = nil;
    543 }
    544 
    545 void MediaPlayerPrivateQTKit::createQTMovieLayer()
    546 {
    547 #if USE(ACCELERATED_COMPOSITING)
    548     if (!m_qtMovie)
    549         return;
    550 
    551     ASSERT(supportsAcceleratedRendering());
    552 
    553     if (!m_qtVideoLayer) {
    554         m_qtVideoLayer.adoptNS([[QTMovieLayer alloc] init]);
    555         if (!m_qtVideoLayer)
    556             return;
    557 
    558         [m_qtVideoLayer.get() setMovie:m_qtMovie.get()];
    559 #ifndef NDEBUG
    560         [(CALayer *)m_qtVideoLayer.get() setName:@"Video layer"];
    561 #endif
    562         // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration().
    563     }
    564 #endif
    565 }
    566 
    567 void MediaPlayerPrivateQTKit::destroyQTMovieLayer()
    568 {
    569 #if USE(ACCELERATED_COMPOSITING)
    570     if (!m_qtVideoLayer)
    571         return;
    572 
    573     // disassociate our movie from our instance of QTMovieLayer
    574     [m_qtVideoLayer.get() setMovie:nil];
    575     m_qtVideoLayer = nil;
    576 #endif
    577 }
    578 
    579 MediaPlayerPrivateQTKit::MediaRenderingMode MediaPlayerPrivateQTKit::currentRenderingMode() const
    580 {
    581     if (m_qtMovieView)
    582         return MediaRenderingMovieView;
    583 
    584     if (m_qtVideoLayer)
    585         return MediaRenderingMovieLayer;
    586 
    587     if (m_qtVideoRenderer)
    588         return MediaRenderingSoftwareRenderer;
    589 
    590     return MediaRenderingNone;
    591 }
    592 
    593 MediaPlayerPrivateQTKit::MediaRenderingMode MediaPlayerPrivateQTKit::preferredRenderingMode() const
    594 {
    595     if (!m_player->frameView() || !m_qtMovie)
    596         return MediaRenderingNone;
    597 
    598 #if USE(ACCELERATED_COMPOSITING)
    599     if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player))
    600         return MediaRenderingMovieLayer;
    601 #endif
    602 
    603     if (!QTVideoRendererClass())
    604         return MediaRenderingMovieView;
    605 
    606     return MediaRenderingSoftwareRenderer;
    607 }
    608 
    609 void MediaPlayerPrivateQTKit::setUpVideoRendering()
    610 {
    611     if (!isReadyForVideoSetup())
    612         return;
    613 
    614     MediaRenderingMode currentMode = currentRenderingMode();
    615     MediaRenderingMode preferredMode = preferredRenderingMode();
    616     if (currentMode == preferredMode && currentMode != MediaRenderingNone)
    617         return;
    618 
    619     if (currentMode != MediaRenderingNone)
    620         tearDownVideoRendering();
    621 
    622     switch (preferredMode) {
    623     case MediaRenderingMovieView:
    624         createQTMovieView();
    625         break;
    626     case MediaRenderingNone:
    627     case MediaRenderingSoftwareRenderer:
    628         createQTVideoRenderer(QTVideoRendererModeListensForNewImages);
    629         break;
    630     case MediaRenderingMovieLayer:
    631         createQTMovieLayer();
    632         break;
    633     }
    634 
    635     // If using a movie layer, inform the client so the compositing tree is updated.
    636     if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer)
    637         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
    638 }
    639 
    640 void MediaPlayerPrivateQTKit::tearDownVideoRendering()
    641 {
    642     if (m_qtMovieView)
    643         detachQTMovieView();
    644     if (m_qtVideoRenderer)
    645         destroyQTVideoRenderer();
    646     if (m_qtVideoLayer)
    647         destroyQTMovieLayer();
    648 }
    649 
    650 bool MediaPlayerPrivateQTKit::hasSetUpVideoRendering() const
    651 {
    652     return m_qtMovieView
    653         || m_qtVideoLayer
    654         || m_qtVideoRenderer;
    655 }
    656 
    657 QTTime MediaPlayerPrivateQTKit::createQTTime(float time) const
    658 {
    659     if (!metaDataAvailable())
    660         return QTMakeTime(0, 600);
    661     long timeScale = [[m_qtMovie.get() attributeForKey:QTMovieTimeScaleAttribute] longValue];
    662     return QTMakeTime(lroundf(time * timeScale), timeScale);
    663 }
    664 
    665 void MediaPlayerPrivateQTKit::resumeLoad()
    666 {
    667     m_delayingLoad = false;
    668 
    669     if (!m_movieURL.isNull())
    670         loadInternal(m_movieURL);
    671 }
    672 
    673 void MediaPlayerPrivateQTKit::load(const String& url)
    674 {
    675     m_movieURL = url;
    676 
    677     // If the element is not supposed to load any data return immediately because QTKit
    678     // doesn't have API to throttle loading.
    679     if (m_preload == MediaPlayer::None) {
    680         m_delayingLoad = true;
    681         return;
    682     }
    683 
    684     loadInternal(url);
    685 }
    686 
    687 void MediaPlayerPrivateQTKit::loadInternal(const String& url)
    688 {
    689     if (m_networkState != MediaPlayer::Loading) {
    690         m_networkState = MediaPlayer::Loading;
    691         m_player->networkStateChanged();
    692     }
    693     if (m_readyState != MediaPlayer::HaveNothing) {
    694         m_readyState = MediaPlayer::HaveNothing;
    695         m_player->readyStateChanged();
    696     }
    697     cancelSeek();
    698     m_videoFrameHasDrawn = false;
    699 
    700     [m_objcObserver.get() setDelayCallbacks:YES];
    701 
    702 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
    703     Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : NULL;
    704     ApplicationCacheHost* cacheHost = frame ? frame->loader()->documentLoader()->applicationCacheHost() : NULL;
    705     ApplicationCacheResource* resource = NULL;
    706     if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource) && resource)
    707         createQTMovie(resource);
    708     else
    709 #endif
    710     createQTMovie(url);
    711 
    712     [m_objcObserver.get() loadStateChanged:nil];
    713     [m_objcObserver.get() setDelayCallbacks:NO];
    714 }
    715 
    716 void MediaPlayerPrivateQTKit::prepareToPlay()
    717 {
    718     if (!m_qtMovie || m_delayingLoad)
    719         resumeLoad();
    720 }
    721 
    722 PlatformMedia MediaPlayerPrivateQTKit::platformMedia() const
    723 {
    724     PlatformMedia pm;
    725     pm.type = PlatformMedia::QTMovieType;
    726     pm.media.qtMovie = m_qtMovie.get();
    727     return pm;
    728 }
    729 
    730 #if USE(ACCELERATED_COMPOSITING)
    731 PlatformLayer* MediaPlayerPrivateQTKit::platformLayer() const
    732 {
    733     return m_qtVideoLayer.get();
    734 }
    735 #endif
    736 
    737 void MediaPlayerPrivateQTKit::play()
    738 {
    739     if (!metaDataAvailable())
    740         return;
    741     m_startedPlaying = true;
    742 #if DRAW_FRAME_RATE
    743     m_frameCountWhilePlaying = 0;
    744 #endif
    745     [m_objcObserver.get() setDelayCallbacks:YES];
    746     [m_qtMovie.get() setRate:m_player->rate()];
    747     [m_objcObserver.get() setDelayCallbacks:NO];
    748 }
    749 
    750 void MediaPlayerPrivateQTKit::pause()
    751 {
    752     if (!metaDataAvailable())
    753         return;
    754     m_startedPlaying = false;
    755 #if DRAW_FRAME_RATE
    756     m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate];
    757 #endif
    758     [m_objcObserver.get() setDelayCallbacks:YES];
    759     [m_qtMovie.get() stop];
    760     [m_objcObserver.get() setDelayCallbacks:NO];
    761 }
    762 
    763 float MediaPlayerPrivateQTKit::duration() const
    764 {
    765     if (!metaDataAvailable())
    766         return 0;
    767 
    768     if (m_cachedDuration != -1.0f)
    769         return m_cachedDuration;
    770 
    771     QTTime time = [m_qtMovie.get() duration];
    772     if (time.flags == kQTTimeIsIndefinite)
    773         return numeric_limits<float>::infinity();
    774     return static_cast<float>(time.timeValue) / time.timeScale;
    775 }
    776 
    777 float MediaPlayerPrivateQTKit::currentTime() const
    778 {
    779     if (!metaDataAvailable())
    780         return 0;
    781     QTTime time = [m_qtMovie.get() currentTime];
    782     return static_cast<float>(time.timeValue) / time.timeScale;
    783 }
    784 
    785 void MediaPlayerPrivateQTKit::seek(float time)
    786 {
    787     // Nothing to do if we are already in the middle of a seek to the same time.
    788     if (time == m_seekTo)
    789         return;
    790 
    791     cancelSeek();
    792 
    793     if (!metaDataAvailable())
    794         return;
    795 
    796     if (time > duration())
    797         time = duration();
    798 
    799     m_seekTo = time;
    800     if (maxTimeSeekable() >= m_seekTo)
    801         doSeek();
    802     else
    803         m_seekTimer.start(0, 0.5f);
    804 }
    805 
    806 void MediaPlayerPrivateQTKit::doSeek()
    807 {
    808     QTTime qttime = createQTTime(m_seekTo);
    809     // setCurrentTime generates several event callbacks, update afterwards
    810     [m_objcObserver.get() setDelayCallbacks:YES];
    811     float oldRate = [m_qtMovie.get() rate];
    812 
    813     if (oldRate)
    814         [m_qtMovie.get() setRate:0];
    815     [m_qtMovie.get() setCurrentTime:qttime];
    816 
    817     // restore playback only if not at end, otherwise QTMovie will loop
    818     float timeAfterSeek = currentTime();
    819     if (oldRate && timeAfterSeek < duration())
    820         [m_qtMovie.get() setRate:oldRate];
    821 
    822     cancelSeek();
    823     [m_objcObserver.get() setDelayCallbacks:NO];
    824 }
    825 
    826 void MediaPlayerPrivateQTKit::cancelSeek()
    827 {
    828     m_seekTo = -1;
    829     m_seekTimer.stop();
    830 }
    831 
    832 void MediaPlayerPrivateQTKit::seekTimerFired(Timer<MediaPlayerPrivateQTKit>*)
    833 {
    834     if (!metaDataAvailable()|| !seeking() || currentTime() == m_seekTo) {
    835         cancelSeek();
    836         updateStates();
    837         m_player->timeChanged();
    838         return;
    839     }
    840 
    841     if (maxTimeSeekable() >= m_seekTo)
    842         doSeek();
    843     else {
    844         MediaPlayer::NetworkState state = networkState();
    845         if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) {
    846             cancelSeek();
    847             updateStates();
    848             m_player->timeChanged();
    849         }
    850     }
    851 }
    852 
    853 bool MediaPlayerPrivateQTKit::paused() const
    854 {
    855     if (!metaDataAvailable())
    856         return true;
    857     return [m_qtMovie.get() rate] == 0;
    858 }
    859 
    860 bool MediaPlayerPrivateQTKit::seeking() const
    861 {
    862     if (!metaDataAvailable())
    863         return false;
    864     return m_seekTo >= 0;
    865 }
    866 
    867 IntSize MediaPlayerPrivateQTKit::naturalSize() const
    868 {
    869     if (!metaDataAvailable())
    870         return IntSize();
    871 
    872     // In spite of the name of this method, return QTMovieNaturalSizeAttribute transformed by the
    873     // initial movie scale because the spec says intrinsic size is:
    874     //
    875     //    ... the dimensions of the resource in CSS pixels after taking into account the resource's
    876     //    dimensions, aspect ratio, clean aperture, resolution, and so forth, as defined for the
    877     //    format used by the resource
    878 
    879     FloatSize naturalSize([[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]);
    880     if (naturalSize.isEmpty() && m_isStreaming) {
    881         // HTTP Live Streams will occasionally return {0,0} natural sizes while scrubbing.
    882         // Work around this problem (<rdar://problem/9078563>) by returning the last valid
    883         // cached natural size:
    884         naturalSize = m_cachedNaturalSize;
    885     } else {
    886         // Unfortunately, due to another QTKit bug (<rdar://problem/9082071>) we won't get a sizeChanged
    887         // event when this happens, so we must cache the last valid naturalSize here:
    888         m_cachedNaturalSize = naturalSize;
    889     }
    890 
    891     return IntSize(naturalSize.width() * m_scaleFactor.width(), naturalSize.height() * m_scaleFactor.height());
    892 }
    893 
    894 bool MediaPlayerPrivateQTKit::hasVideo() const
    895 {
    896     if (!metaDataAvailable())
    897         return false;
    898     return [[m_qtMovie.get() attributeForKey:QTMovieHasVideoAttribute] boolValue];
    899 }
    900 
    901 bool MediaPlayerPrivateQTKit::hasAudio() const
    902 {
    903     if (!m_qtMovie)
    904         return false;
    905     return [[m_qtMovie.get() attributeForKey:QTMovieHasAudioAttribute] boolValue];
    906 }
    907 
    908 bool MediaPlayerPrivateQTKit::supportsFullscreen() const
    909 {
    910 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    911     return true;
    912 #else
    913     // See <rdar://problem/7389945>
    914     return false;
    915 #endif
    916 }
    917 
    918 void MediaPlayerPrivateQTKit::setVolume(float volume)
    919 {
    920     if (m_qtMovie)
    921         [m_qtMovie.get() setVolume:volume];
    922 }
    923 
    924 bool MediaPlayerPrivateQTKit::hasClosedCaptions() const
    925 {
    926     if (!metaDataAvailable())
    927         return false;
    928     return wkQTMovieHasClosedCaptions(m_qtMovie.get());
    929 }
    930 
    931 void MediaPlayerPrivateQTKit::setClosedCaptionsVisible(bool closedCaptionsVisible)
    932 {
    933     if (metaDataAvailable()) {
    934         wkQTMovieSetShowClosedCaptions(m_qtMovie.get(), closedCaptionsVisible);
    935 
    936 #if USE(ACCELERATED_COMPOSITING) && (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD))
    937     if (closedCaptionsVisible && m_qtVideoLayer) {
    938         // Captions will be rendered upside down unless we flag the movie as flipped (again). See <rdar://7408440>.
    939         [m_qtVideoLayer.get() setGeometryFlipped:YES];
    940     }
    941 #endif
    942     }
    943 }
    944 
    945 void MediaPlayerPrivateQTKit::setRate(float rate)
    946 {
    947     if (m_qtMovie)
    948         [m_qtMovie.get() setRate:rate];
    949 }
    950 
    951 void MediaPlayerPrivateQTKit::setPreservesPitch(bool preservesPitch)
    952 {
    953     if (!m_qtMovie)
    954         return;
    955 
    956     // QTMovieRateChangesPreservePitchAttribute cannot be changed dynamically after QTMovie creation.
    957     // If the passed in value is different than what already exists, we need to recreate the QTMovie for it to take effect.
    958     if ([[m_qtMovie.get() attributeForKey:QTMovieRateChangesPreservePitchAttribute] boolValue] == preservesPitch)
    959         return;
    960 
    961     RetainPtr<NSDictionary> movieAttributes(AdoptNS, [[m_qtMovie.get() movieAttributes] mutableCopy]);
    962     ASSERT(movieAttributes);
    963     [movieAttributes.get() setValue:[NSNumber numberWithBool:preservesPitch] forKey:QTMovieRateChangesPreservePitchAttribute];
    964     m_timeToRestore = currentTime();
    965 
    966     createQTMovie([movieAttributes.get() valueForKey:QTMovieURLAttribute], movieAttributes.get());
    967 }
    968 
    969 PassRefPtr<TimeRanges> MediaPlayerPrivateQTKit::buffered() const
    970 {
    971     RefPtr<TimeRanges> timeRanges = TimeRanges::create();
    972     float loaded = maxTimeLoaded();
    973     if (loaded > 0)
    974         timeRanges->add(0, loaded);
    975     return timeRanges.release();
    976 }
    977 
    978 float MediaPlayerPrivateQTKit::maxTimeSeekable() const
    979 {
    980     if (!metaDataAvailable())
    981         return 0;
    982 
    983     // infinite duration means live stream
    984     if (isinf(duration()))
    985         return 0;
    986 
    987     return wkQTMovieMaxTimeSeekable(m_qtMovie.get());
    988 }
    989 
    990 float MediaPlayerPrivateQTKit::maxTimeLoaded() const
    991 {
    992     if (!metaDataAvailable())
    993         return 0;
    994     return wkQTMovieMaxTimeLoaded(m_qtMovie.get());
    995 }
    996 
    997 unsigned MediaPlayerPrivateQTKit::bytesLoaded() const
    998 {
    999     float dur = duration();
   1000     if (!dur)
   1001         return 0;
   1002     return totalBytes() * maxTimeLoaded() / dur;
   1003 }
   1004 
   1005 unsigned MediaPlayerPrivateQTKit::totalBytes() const
   1006 {
   1007     if (!metaDataAvailable())
   1008         return 0;
   1009     return [[m_qtMovie.get() attributeForKey:QTMovieDataSizeAttribute] intValue];
   1010 }
   1011 
   1012 void MediaPlayerPrivateQTKit::cancelLoad()
   1013 {
   1014     // FIXME: Is there a better way to check for this?
   1015     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
   1016         return;
   1017 
   1018     tearDownVideoRendering();
   1019     m_qtMovie = nil;
   1020 
   1021     updateStates();
   1022 }
   1023 
   1024 void MediaPlayerPrivateQTKit::cacheMovieScale()
   1025 {
   1026     NSSize initialSize = NSZeroSize;
   1027     NSSize naturalSize = [[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
   1028 
   1029 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
   1030     // QTMovieCurrentSizeAttribute is not allowed with instances of QTMovie that have been
   1031     // opened with QTMovieOpenForPlaybackAttribute, so ask for the display transform attribute instead.
   1032     NSAffineTransform *displayTransform = [m_qtMovie.get() attributeForKey:@"QTMoviePreferredTransformAttribute"];
   1033     if (displayTransform)
   1034         initialSize = [displayTransform transformSize:naturalSize];
   1035     else {
   1036         initialSize.width = naturalSize.width;
   1037         initialSize.height = naturalSize.height;
   1038     }
   1039 #else
   1040     initialSize = [[m_qtMovie.get() attributeForKey:QTMovieCurrentSizeAttribute] sizeValue];
   1041 #endif
   1042 
   1043     if (naturalSize.width)
   1044         m_scaleFactor.setWidth(initialSize.width / naturalSize.width);
   1045     if (naturalSize.height)
   1046         m_scaleFactor.setHeight(initialSize.height / naturalSize.height);
   1047 }
   1048 
   1049 bool MediaPlayerPrivateQTKit::isReadyForVideoSetup() const
   1050 {
   1051     return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
   1052 }
   1053 
   1054 void MediaPlayerPrivateQTKit::prepareForRendering()
   1055 {
   1056     if (m_isAllowedToRender)
   1057         return;
   1058     m_isAllowedToRender = true;
   1059 
   1060     if (!hasSetUpVideoRendering())
   1061         setUpVideoRendering();
   1062 
   1063     // If using a movie layer, inform the client so the compositing tree is updated. This is crucial if the movie
   1064     // has a poster, as it will most likely not have a layer and we will now be rendering frames to the movie layer.
   1065     if (currentRenderingMode() == MediaRenderingMovieLayer || preferredRenderingMode() == MediaRenderingMovieLayer)
   1066         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
   1067 }
   1068 
   1069 void MediaPlayerPrivateQTKit::updateStates()
   1070 {
   1071     MediaPlayer::NetworkState oldNetworkState = m_networkState;
   1072     MediaPlayer::ReadyState oldReadyState = m_readyState;
   1073 
   1074     long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : static_cast<long>(QTMovieLoadStateError);
   1075 
   1076     if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) {
   1077         disableUnsupportedTracks();
   1078         if (m_player->inMediaDocument()) {
   1079             if (!m_enabledTrackCount || m_hasUnsupportedTracks) {
   1080                 // This has a type of media that we do not handle directly with a <video>
   1081                 // element, eg. a rtsp track or QuickTime VR. Tell the MediaPlayerClient
   1082                 // that we noticed.
   1083                 sawUnsupportedTracks();
   1084                 return;
   1085             }
   1086         } else if (!m_enabledTrackCount)
   1087             loadState = QTMovieLoadStateError;
   1088 
   1089         if (loadState != QTMovieLoadStateError) {
   1090             wkQTMovieSelectPreferredAlternates(m_qtMovie.get());
   1091             cacheMovieScale();
   1092             MediaPlayer::MovieLoadType movieType = movieLoadType();
   1093             m_isStreaming = movieType == MediaPlayer::StoredStream || movieType == MediaPlayer::LiveStream;
   1094         }
   1095     }
   1096 
   1097     // If this movie is reloading and we mean to restore the current time/rate, this might be the right time to do it.
   1098     if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::Loaded && m_timeToRestore != -1.0f) {
   1099         QTTime qttime = createQTTime(m_timeToRestore);
   1100         m_timeToRestore = -1.0f;
   1101 
   1102         // Disable event callbacks from setCurrentTime for restoring time in a recreated video
   1103         [m_objcObserver.get() setDelayCallbacks:YES];
   1104         [m_qtMovie.get() setCurrentTime:qttime];
   1105         [m_qtMovie.get() setRate:m_player->rate()];
   1106         [m_objcObserver.get() setDelayCallbacks:NO];
   1107     }
   1108 
   1109     BOOL completelyLoaded = !m_isStreaming && (loadState >= QTMovieLoadStateComplete);
   1110 
   1111     // Note: QT indicates that we are fully loaded with QTMovieLoadStateComplete.
   1112     // However newer versions of QT do not, so we check maxTimeLoaded against duration.
   1113     if (!completelyLoaded && !m_isStreaming && metaDataAvailable())
   1114         completelyLoaded = maxTimeLoaded() == duration();
   1115 
   1116     if (completelyLoaded) {
   1117         // "Loaded" is reserved for fully buffered movies, never the case when streaming
   1118         m_networkState = MediaPlayer::Loaded;
   1119         m_readyState = MediaPlayer::HaveEnoughData;
   1120     } else if (loadState >= QTMovieLoadStatePlaythroughOK) {
   1121         m_readyState = MediaPlayer::HaveEnoughData;
   1122         m_networkState = MediaPlayer::Loading;
   1123     } else if (loadState >= QTMovieLoadStatePlayable) {
   1124         // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967>
   1125         m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
   1126         m_networkState = MediaPlayer::Loading;
   1127     } else if (loadState >= QTMovieLoadStateLoaded) {
   1128         m_readyState = MediaPlayer::HaveMetadata;
   1129         m_networkState = MediaPlayer::Loading;
   1130     } else if (loadState > QTMovieLoadStateError) {
   1131         m_readyState = MediaPlayer::HaveNothing;
   1132         m_networkState = MediaPlayer::Loading;
   1133     } else {
   1134         // Loading or decoding failed.
   1135 
   1136         if (m_player->inMediaDocument()) {
   1137             // Something went wrong in the loading of media within a standalone file.
   1138             // This can occur with chained refmovies pointing to streamed media.
   1139             sawUnsupportedTracks();
   1140             return;
   1141         }
   1142 
   1143         float loaded = maxTimeLoaded();
   1144         if (!loaded)
   1145             m_readyState = MediaPlayer::HaveNothing;
   1146 
   1147         if (!m_enabledTrackCount)
   1148             m_networkState = MediaPlayer::FormatError;
   1149         else {
   1150             // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692>
   1151             if (loaded > 0)
   1152                 m_networkState = MediaPlayer::DecodeError;
   1153             else
   1154                 m_readyState = MediaPlayer::HaveNothing;
   1155         }
   1156     }
   1157 
   1158     if (isReadyForVideoSetup() && !hasSetUpVideoRendering())
   1159         setUpVideoRendering();
   1160 
   1161     if (seeking())
   1162         m_readyState = m_readyState >= MediaPlayer::HaveMetadata ? MediaPlayer::HaveMetadata : MediaPlayer::HaveNothing;
   1163 
   1164     // Streaming movies don't use the network when paused.
   1165     if (m_isStreaming && m_readyState >= MediaPlayer::HaveMetadata && m_networkState >= MediaPlayer::Loading && [m_qtMovie.get() rate] == 0)
   1166         m_networkState = MediaPlayer::Idle;
   1167 
   1168     if (m_networkState != oldNetworkState)
   1169         m_player->networkStateChanged();
   1170 
   1171     if (m_readyState != oldReadyState)
   1172         m_player->readyStateChanged();
   1173 
   1174     if (loadState >= QTMovieLoadStateLoaded) {
   1175         float dur = duration();
   1176         if (dur != m_reportedDuration) {
   1177             if (m_reportedDuration != -1.0f)
   1178                 m_player->durationChanged();
   1179             m_reportedDuration = dur;
   1180         }
   1181     }
   1182 }
   1183 
   1184 void MediaPlayerPrivateQTKit::loadStateChanged()
   1185 {
   1186     if (!m_hasUnsupportedTracks)
   1187         updateStates();
   1188 }
   1189 
   1190 void MediaPlayerPrivateQTKit::rateChanged()
   1191 {
   1192     if (m_hasUnsupportedTracks)
   1193         return;
   1194 
   1195     updateStates();
   1196     m_player->rateChanged();
   1197 }
   1198 
   1199 void MediaPlayerPrivateQTKit::sizeChanged()
   1200 {
   1201     if (!m_hasUnsupportedTracks)
   1202         m_player->sizeChanged();
   1203 }
   1204 
   1205 void MediaPlayerPrivateQTKit::timeChanged()
   1206 {
   1207     if (m_hasUnsupportedTracks)
   1208         return;
   1209 
   1210     // It may not be possible to seek to a specific time in a streamed movie. When seeking in a
   1211     // stream QuickTime sets the movie time to closest time possible and posts a timechanged
   1212     // notification. Update m_seekTo so we can detect when the seek completes.
   1213     if (m_seekTo != -1)
   1214         m_seekTo = currentTime();
   1215 
   1216     m_timeToRestore = -1.0f;
   1217     updateStates();
   1218     m_player->timeChanged();
   1219 }
   1220 
   1221 void MediaPlayerPrivateQTKit::didEnd()
   1222 {
   1223     if (m_hasUnsupportedTracks)
   1224         return;
   1225 
   1226     m_startedPlaying = false;
   1227 #if DRAW_FRAME_RATE
   1228     m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate];
   1229 #endif
   1230 
   1231     // Hang onto the current time and use it as duration from now on since QuickTime is telling us we
   1232     // are at the end. Do this because QuickTime sometimes reports one time for duration and stops
   1233     // playback at another time, which causes problems in HTMLMediaElement. QTKit's 'ended' event
   1234     // fires when playing in reverse so don't update duration when at time zero!
   1235     float now = currentTime();
   1236     if (now > 0)
   1237         m_cachedDuration = now;
   1238 
   1239     updateStates();
   1240     m_player->timeChanged();
   1241 }
   1242 
   1243 void MediaPlayerPrivateQTKit::setSize(const IntSize&)
   1244 {
   1245     // Don't resize the view now because [view setFrame] also resizes the movie itself, and because
   1246     // the renderer calls this function immediately when we report a size change (QTMovieSizeDidChangeNotification)
   1247     // we can get into a feedback loop observing the size change and resetting the size, and this can cause
   1248     // QuickTime to miss resetting a movie's size when the media size changes (as happens with an rtsp movie
   1249     // once the rtsp server sends the track sizes). Instead we remember the size passed to paint() and resize
   1250     // the view when it changes.
   1251     // <rdar://problem/6336092> REGRESSION: rtsp movie does not resize correctly
   1252 }
   1253 
   1254 void MediaPlayerPrivateQTKit::setVisible(bool b)
   1255 {
   1256     if (m_visible != b) {
   1257         m_visible = b;
   1258         if (b)
   1259             setUpVideoRendering();
   1260         else
   1261             tearDownVideoRendering();
   1262     }
   1263 }
   1264 
   1265 bool MediaPlayerPrivateQTKit::hasAvailableVideoFrame() const
   1266 {
   1267     // When using a QTMovieLayer return true as soon as the movie reaches QTMovieLoadStatePlayable
   1268     // because although we don't *know* when the first frame has decoded, by the time we get and
   1269     // process the notification a frame should have propagated the VisualContext and been set on
   1270     // the layer.
   1271     if (currentRenderingMode() == MediaRenderingMovieLayer)
   1272         return m_readyState >= MediaPlayer::HaveCurrentData;
   1273 
   1274     // When using the software renderer QuickTime signals that a frame is available so we might as well
   1275     // wait until we know that a frame has been drawn.
   1276     return m_videoFrameHasDrawn;
   1277 }
   1278 
   1279 void MediaPlayerPrivateQTKit::repaint()
   1280 {
   1281     if (m_hasUnsupportedTracks)
   1282         return;
   1283 
   1284 #if DRAW_FRAME_RATE
   1285     if (m_startedPlaying) {
   1286         m_frameCountWhilePlaying++;
   1287         // to eliminate preroll costs from our calculation,
   1288         // our frame rate calculation excludes the first frame drawn after playback starts
   1289         if (1==m_frameCountWhilePlaying)
   1290             m_timeStartedPlaying = [NSDate timeIntervalSinceReferenceDate];
   1291     }
   1292 #endif
   1293     m_videoFrameHasDrawn = true;
   1294     m_player->repaint();
   1295 }
   1296 
   1297 void MediaPlayerPrivateQTKit::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& r)
   1298 {
   1299     id qtVideoRenderer = m_qtVideoRenderer.get();
   1300     if (!qtVideoRenderer && currentRenderingMode() == MediaRenderingMovieLayer) {
   1301         // We're being told to render into a context, but we already have the
   1302         // MovieLayer going. This probably means we've been called from <canvas>.
   1303         // Set up a QTVideoRenderer to use, but one that doesn't register for
   1304         // update callbacks. That way, it won't bother us asking to repaint.
   1305         createQTVideoRenderer(QTVideoRendererModeDefault);
   1306         qtVideoRenderer = m_qtVideoRenderer.get();
   1307     }
   1308     paint(context, r);
   1309 }
   1310 
   1311 void MediaPlayerPrivateQTKit::paint(GraphicsContext* context, const IntRect& r)
   1312 {
   1313     if (context->paintingDisabled() || m_hasUnsupportedTracks)
   1314         return;
   1315     NSView *view = m_qtMovieView.get();
   1316     id qtVideoRenderer = m_qtVideoRenderer.get();
   1317     if (!view && !qtVideoRenderer)
   1318         return;
   1319 
   1320     [m_objcObserver.get() setDelayCallbacks:YES];
   1321     BEGIN_BLOCK_OBJC_EXCEPTIONS;
   1322     context->save();
   1323     context->translate(r.x(), r.y() + r.height());
   1324     context->scale(FloatSize(1.0f, -1.0f));
   1325     context->setImageInterpolationQuality(InterpolationLow);
   1326     IntRect paintRect(IntPoint(0, 0), IntSize(r.width(), r.height()));
   1327 
   1328 #ifdef BUILDING_ON_TIGER
   1329     AutodrainedPool pool;
   1330 #endif
   1331     NSGraphicsContext* newContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context->platformContext() flipped:NO];
   1332 
   1333     // draw the current video frame
   1334     if (qtVideoRenderer) {
   1335         [NSGraphicsContext saveGraphicsState];
   1336         [NSGraphicsContext setCurrentContext:newContext];
   1337         [(id<WebKitVideoRenderingDetails>)qtVideoRenderer drawInRect:paintRect];
   1338         [NSGraphicsContext restoreGraphicsState];
   1339     } else {
   1340         if (m_rect != r) {
   1341              m_rect = r;
   1342             if (m_player->inMediaDocument()) {
   1343                 // the QTMovieView needs to be placed in the proper location for document mode
   1344                 [view setFrame:m_rect];
   1345             }
   1346             else {
   1347                 // We don't really need the QTMovieView in any specific location so let's just get it out of the way
   1348                 // where it won't intercept events or try to bring up the context menu.
   1349                 IntRect farAwayButCorrectSize(m_rect);
   1350                 farAwayButCorrectSize.move(-1000000, -1000000);
   1351                 [view setFrame:farAwayButCorrectSize];
   1352             }
   1353         }
   1354 
   1355         if (m_player->inMediaDocument()) {
   1356             // If we're using a QTMovieView in a media document, the view may get layer-backed. AppKit won't update
   1357             // the layer hosting correctly if we call displayRectIgnoringOpacity:inContext:, so use displayRectIgnoringOpacity:
   1358             // in this case. See <rdar://problem/6702882>.
   1359             [view displayRectIgnoringOpacity:paintRect];
   1360         } else
   1361             [view displayRectIgnoringOpacity:paintRect inContext:newContext];
   1362     }
   1363 
   1364 #if DRAW_FRAME_RATE
   1365     // Draw the frame rate only after having played more than 10 frames.
   1366     if (m_frameCountWhilePlaying > 10) {
   1367         Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : NULL;
   1368         Document* document = frame ? frame->document() : NULL;
   1369         RenderObject* renderer = document ? document->renderer() : NULL;
   1370         RenderStyle* styleToUse = renderer ? renderer->style() : NULL;
   1371         if (styleToUse) {
   1372             double frameRate = (m_frameCountWhilePlaying - 1) / ( m_startedPlaying ? ([NSDate timeIntervalSinceReferenceDate] - m_timeStartedPlaying) :
   1373                 (m_timeStoppedPlaying - m_timeStartedPlaying) );
   1374             String text = String::format("%1.2f", frameRate);
   1375             TextRun textRun(text.characters(), text.length());
   1376             const Color color(255, 0, 0);
   1377             context->scale(FloatSize(1.0f, -1.0f));
   1378             context->setStrokeColor(color, styleToUse->colorSpace());
   1379             context->setStrokeStyle(SolidStroke);
   1380             context->setStrokeThickness(1.0f);
   1381             context->setFillColor(color, styleToUse->colorSpace());
   1382             context->drawText(styleToUse->font(), textRun, IntPoint(2, -3));
   1383         }
   1384     }
   1385 #endif
   1386 
   1387     context->restore();
   1388     END_BLOCK_OBJC_EXCEPTIONS;
   1389     [m_objcObserver.get() setDelayCallbacks:NO];
   1390 }
   1391 
   1392 static void addFileTypesToCache(NSArray * fileTypes, HashSet<String> &cache)
   1393 {
   1394     int count = [fileTypes count];
   1395     for (int n = 0; n < count; n++) {
   1396         CFStringRef ext = reinterpret_cast<CFStringRef>([fileTypes objectAtIndex:n]);
   1397         RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL));
   1398         if (!uti)
   1399             continue;
   1400         RetainPtr<CFStringRef> mime(AdoptCF, UTTypeCopyPreferredTagWithClass(uti.get(), kUTTagClassMIMEType));
   1401         if (mime)
   1402             cache.add(mime.get());
   1403 
   1404         // -movieFileTypes: returns both file extensions and OSTypes. The later are surrounded by single
   1405         // quotes, eg. 'MooV', so don't bother looking at those.
   1406         if (CFStringGetCharacterAtIndex(ext, 0) != '\'') {
   1407             // UTI is missing many media related MIME types supported by QTKit (see rdar://6434168), and not all
   1408             // web servers use the MIME type UTI returns for an extension (see rdar://7875393), so even if UTI
   1409             // has a type for this extension add any types in hard coded table in the MIME type regsitry.
   1410             Vector<String> typesForExtension = MIMETypeRegistry::getMediaMIMETypesForExtension(ext);
   1411             unsigned count = typesForExtension.size();
   1412             for (unsigned ndx = 0; ndx < count; ++ndx) {
   1413                 if (!cache.contains(typesForExtension[ndx]))
   1414                     cache.add(typesForExtension[ndx]);
   1415             }
   1416         }
   1417     }
   1418 }
   1419 
   1420 static HashSet<String> mimeCommonTypesCache()
   1421 {
   1422     DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
   1423     static bool typeListInitialized = false;
   1424 
   1425     if (!typeListInitialized) {
   1426         typeListInitialized = true;
   1427         NSArray* fileTypes = [QTMovie movieFileTypes:QTIncludeCommonTypes];
   1428         addFileTypesToCache(fileTypes, cache);
   1429     }
   1430 
   1431     return cache;
   1432 }
   1433 
   1434 static HashSet<String> mimeModernTypesCache()
   1435 {
   1436     DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
   1437     static bool typeListInitialized = false;
   1438 
   1439     if (!typeListInitialized) {
   1440         typeListInitialized = true;
   1441         NSArray* fileTypes = [QTMovie movieFileTypes:(QTMovieFileTypeOptions)wkQTIncludeOnlyModernMediaFileTypes()];
   1442         addFileTypesToCache(fileTypes, cache);
   1443     }
   1444 
   1445     return cache;
   1446 }
   1447 
   1448 void MediaPlayerPrivateQTKit::getSupportedTypes(HashSet<String>& supportedTypes)
   1449 {
   1450     supportedTypes = mimeModernTypesCache();
   1451 
   1452     // Note: this method starts QTKitServer if it isn't already running when in 64-bit because it has to return the list
   1453     // of every MIME type supported by QTKit.
   1454     HashSet<String> commonTypes = mimeCommonTypesCache();
   1455     HashSet<String>::const_iterator it = commonTypes.begin();
   1456     HashSet<String>::const_iterator end = commonTypes.end();
   1457     for (; it != end; ++it)
   1458         supportedTypes.add(*it);
   1459 }
   1460 
   1461 MediaPlayer::SupportsType MediaPlayerPrivateQTKit::supportsType(const String& type, const String& codecs)
   1462 {
   1463     // Only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an
   1464     // extended MIME type yet.
   1465 
   1466     // We check the "modern" type cache first, as it doesn't require QTKitServer to start.
   1467     if (mimeModernTypesCache().contains(type) || mimeCommonTypesCache().contains(type))
   1468         return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
   1469 
   1470     return MediaPlayer::IsNotSupported;
   1471 }
   1472 
   1473 bool MediaPlayerPrivateQTKit::isAvailable()
   1474 {
   1475 #ifdef BUILDING_ON_TIGER
   1476     SInt32 version;
   1477     OSErr result;
   1478     result = Gestalt(gestaltQuickTime, &version);
   1479     if (result != noErr) {
   1480         LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support.");
   1481         return false;
   1482     }
   1483     if (version < minimumQuickTimeVersion) {
   1484         LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", version, minimumQuickTimeVersion);
   1485         return false;
   1486     }
   1487     return true;
   1488 #else
   1489     // On 10.5 and higher, QuickTime will always be new enough for <video> and <audio> support, so we just check that the framework can be loaded.
   1490     return QTKitLibrary();
   1491 #endif
   1492 }
   1493 
   1494 void MediaPlayerPrivateQTKit::disableUnsupportedTracks()
   1495 {
   1496     if (!m_qtMovie) {
   1497         m_enabledTrackCount = 0;
   1498         m_totalTrackCount = 0;
   1499         return;
   1500     }
   1501 
   1502     static HashSet<String>* allowedTrackTypes = 0;
   1503     if (!allowedTrackTypes) {
   1504         allowedTrackTypes = new HashSet<String>;
   1505         allowedTrackTypes->add(QTMediaTypeVideo);
   1506         allowedTrackTypes->add(QTMediaTypeSound);
   1507         allowedTrackTypes->add(QTMediaTypeText);
   1508         allowedTrackTypes->add(QTMediaTypeBase);
   1509         allowedTrackTypes->add(QTMediaTypeMPEG);
   1510         allowedTrackTypes->add("clcp"); // Closed caption
   1511         allowedTrackTypes->add("sbtl"); // Subtitle
   1512         allowedTrackTypes->add("odsm"); // MPEG-4 object descriptor stream
   1513         allowedTrackTypes->add("sdsm"); // MPEG-4 scene description stream
   1514         allowedTrackTypes->add("tmcd"); // timecode
   1515         allowedTrackTypes->add("tc64"); // timcode-64
   1516         allowedTrackTypes->add("tmet"); // timed metadata
   1517     }
   1518 
   1519     NSArray *tracks = [m_qtMovie.get() tracks];
   1520 
   1521     m_totalTrackCount = [tracks count];
   1522     m_enabledTrackCount = m_totalTrackCount;
   1523     for (unsigned trackIndex = 0; trackIndex < m_totalTrackCount; trackIndex++) {
   1524         // Grab the track at the current index. If there isn't one there, then
   1525         // we can move onto the next one.
   1526         QTTrack *track = [tracks objectAtIndex:trackIndex];
   1527         if (!track)
   1528             continue;
   1529 
   1530         // Check to see if the track is disabled already, we should move along.
   1531         // We don't need to re-disable it.
   1532         if (![track isEnabled]) {
   1533             --m_enabledTrackCount;
   1534             continue;
   1535         }
   1536 
   1537         // Get the track's media type.
   1538         NSString *mediaType = [track attributeForKey:QTTrackMediaTypeAttribute];
   1539         if (!mediaType)
   1540             continue;
   1541 
   1542         // Test whether the media type is in our white list.
   1543         if (!allowedTrackTypes->contains(mediaType)) {
   1544             // If this track type is not allowed, then we need to disable it.
   1545             [track setEnabled:NO];
   1546             --m_enabledTrackCount;
   1547             m_hasUnsupportedTracks = true;
   1548         }
   1549 
   1550         // Disable chapter tracks. These are most likely to lead to trouble, as
   1551         // they will be composited under the video tracks, forcing QT to do extra
   1552         // work.
   1553         QTTrack *chapterTrack = [track performSelector:@selector(chapterlist)];
   1554         if (!chapterTrack)
   1555             continue;
   1556 
   1557         // Try to grab the media for the track.
   1558         QTMedia *chapterMedia = [chapterTrack media];
   1559         if (!chapterMedia)
   1560             continue;
   1561 
   1562         // Grab the media type for this track.
   1563         id chapterMediaType = [chapterMedia attributeForKey:QTMediaTypeAttribute];
   1564         if (!chapterMediaType)
   1565             continue;
   1566 
   1567         // Check to see if the track is a video track. We don't care about
   1568         // other non-video tracks.
   1569         if (![chapterMediaType isEqual:QTMediaTypeVideo])
   1570             continue;
   1571 
   1572         // Check to see if the track is already disabled. If it is, we
   1573         // should move along.
   1574         if (![chapterTrack isEnabled])
   1575             continue;
   1576 
   1577         // Disable the evil, evil track.
   1578         [chapterTrack setEnabled:NO];
   1579         --m_enabledTrackCount;
   1580         m_hasUnsupportedTracks = true;
   1581     }
   1582 }
   1583 
   1584 void MediaPlayerPrivateQTKit::sawUnsupportedTracks()
   1585 {
   1586     m_hasUnsupportedTracks = true;
   1587     m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player);
   1588 }
   1589 
   1590 #if USE(ACCELERATED_COMPOSITING)
   1591 bool MediaPlayerPrivateQTKit::supportsAcceleratedRendering() const
   1592 {
   1593     return isReadyForVideoSetup() && getQTMovieLayerClass() != Nil;
   1594 }
   1595 
   1596 void MediaPlayerPrivateQTKit::acceleratedRenderingStateChanged()
   1597 {
   1598     // Set up or change the rendering path if necessary.
   1599     setUpVideoRendering();
   1600 }
   1601 #endif
   1602 
   1603 bool MediaPlayerPrivateQTKit::hasSingleSecurityOrigin() const
   1604 {
   1605     // We tell quicktime to disallow resources that come from different origins
   1606     // so we know all media is single origin.
   1607     return true;
   1608 }
   1609 
   1610 MediaPlayer::MovieLoadType MediaPlayerPrivateQTKit::movieLoadType() const
   1611 {
   1612     if (!m_qtMovie)
   1613         return MediaPlayer::Unknown;
   1614 
   1615     MediaPlayer::MovieLoadType movieType = (MediaPlayer::MovieLoadType)wkQTMovieGetType(m_qtMovie.get());
   1616 
   1617     // Can't include WebKitSystemInterface from WebCore so we can't get the enum returned
   1618     // by wkQTMovieGetType, but at least verify that the value is in the valid range.
   1619     ASSERT(movieType >= MediaPlayer::Unknown && movieType <= MediaPlayer::LiveStream);
   1620 
   1621     return movieType;
   1622 }
   1623 
   1624 void MediaPlayerPrivateQTKit::setPreload(MediaPlayer::Preload preload)
   1625 {
   1626     m_preload = preload;
   1627     if (m_delayingLoad && m_preload != MediaPlayer::None)
   1628         resumeLoad();
   1629 }
   1630 
   1631 float MediaPlayerPrivateQTKit::mediaTimeForTimeValue(float timeValue) const
   1632 {
   1633     if (!metaDataAvailable())
   1634         return timeValue;
   1635 
   1636     QTTime qttime = createQTTime(timeValue);
   1637     return static_cast<float>(qttime.timeValue) / qttime.timeScale;
   1638 }
   1639 
   1640 void MediaPlayerPrivateQTKit::setPrivateBrowsingMode(bool privateBrowsing)
   1641 {
   1642     m_privateBrowsing = privateBrowsing;
   1643     if (!m_qtMovie)
   1644         return;
   1645     [m_qtMovie.get() setAttribute:[NSNumber numberWithBool:!privateBrowsing] forKey:@"QTMovieAllowPersistentCacheAttribute"];
   1646 }
   1647 
   1648 
   1649 } // namespace WebCore
   1650 
   1651 @implementation WebCoreMovieObserver
   1652 
   1653 - (id)initWithCallback:(MediaPlayerPrivateQTKit*)callback
   1654 {
   1655     m_callback = callback;
   1656     return [super init];
   1657 }
   1658 
   1659 - (void)disconnect
   1660 {
   1661     [NSObject cancelPreviousPerformRequestsWithTarget:self];
   1662     m_callback = 0;
   1663 }
   1664 
   1665 -(NSMenu*)menuForEventDelegate:(NSEvent*)theEvent
   1666 {
   1667     // Get the contextual menu from the QTMovieView's superview, the frame view
   1668     return [[m_view superview] menuForEvent:theEvent];
   1669 }
   1670 
   1671 -(void)setView:(NSView*)view
   1672 {
   1673     m_view = view;
   1674 }
   1675 
   1676 -(void)repaint
   1677 {
   1678     if (m_delayCallbacks)
   1679         [self performSelector:_cmd withObject:nil afterDelay:0.];
   1680     else if (m_callback)
   1681         m_callback->repaint();
   1682 }
   1683 
   1684 - (void)loadStateChanged:(NSNotification *)unusedNotification
   1685 {
   1686     UNUSED_PARAM(unusedNotification);
   1687     if (m_delayCallbacks)
   1688         [self performSelector:_cmd withObject:nil afterDelay:0];
   1689     else
   1690         m_callback->loadStateChanged();
   1691 }
   1692 
   1693 - (void)rateChanged:(NSNotification *)unusedNotification
   1694 {
   1695     UNUSED_PARAM(unusedNotification);
   1696     if (m_delayCallbacks)
   1697         [self performSelector:_cmd withObject:nil afterDelay:0];
   1698     else
   1699         m_callback->rateChanged();
   1700 }
   1701 
   1702 - (void)sizeChanged:(NSNotification *)unusedNotification
   1703 {
   1704     UNUSED_PARAM(unusedNotification);
   1705     if (m_delayCallbacks)
   1706         [self performSelector:_cmd withObject:nil afterDelay:0];
   1707     else
   1708         m_callback->sizeChanged();
   1709 }
   1710 
   1711 - (void)timeChanged:(NSNotification *)unusedNotification
   1712 {
   1713     UNUSED_PARAM(unusedNotification);
   1714     if (m_delayCallbacks)
   1715         [self performSelector:_cmd withObject:nil afterDelay:0];
   1716     else
   1717         m_callback->timeChanged();
   1718 }
   1719 
   1720 - (void)didEnd:(NSNotification *)unusedNotification
   1721 {
   1722     UNUSED_PARAM(unusedNotification);
   1723     if (m_delayCallbacks)
   1724         [self performSelector:_cmd withObject:nil afterDelay:0];
   1725     else
   1726         m_callback->didEnd();
   1727 }
   1728 
   1729 - (void)newImageAvailable:(NSNotification *)unusedNotification
   1730 {
   1731     UNUSED_PARAM(unusedNotification);
   1732     [self repaint];
   1733 }
   1734 
   1735 - (void)setDelayCallbacks:(BOOL)shouldDelay
   1736 {
   1737     m_delayCallbacks = shouldDelay;
   1738 }
   1739 
   1740 @end
   1741 
   1742 #endif
   1743