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