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