- fix Tiger build
[WebKit-https.git] / WebCore / platform / graphics / mac / MediaPlayerPrivateQTKit.mm
1 /*
2  * Copyright (C) 2007 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 "DeprecatedString.h"
34 #import "GraphicsContext.h"
35 #import "KURL.h"
36 #import "ScrollView.h"
37 #import "SoftLinking.h"
38 #import "WebCoreSystemInterface.h"
39 #import <QTKit/QTKit.h>
40 #import <objc/objc-runtime.h>
41
42 SOFT_LINK_FRAMEWORK(QTKit)
43
44 SOFT_LINK(QTKit, QTMakeTime, QTTime, (long long timeValue, long timeScale), (timeValue, timeScale))
45
46 SOFT_LINK_CLASS(QTKit, QTMovie)
47 SOFT_LINK_CLASS(QTKit, QTMovieView)
48
49 SOFT_LINK_POINTER(QTKit, QTMovieDataSizeAttribute, NSString *)
50 SOFT_LINK_POINTER(QTKit, QTMovieDidEndNotification, NSString *)
51 SOFT_LINK_POINTER(QTKit, QTMovieHasVideoAttribute, NSString *)
52 SOFT_LINK_POINTER(QTKit, QTMovieLoadStateAttribute, NSString *)
53 SOFT_LINK_POINTER(QTKit, QTMovieLoadStateDidChangeNotification, NSString *)
54 SOFT_LINK_POINTER(QTKit, QTMovieNaturalSizeAttribute, NSString *)
55 SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *)
56 SOFT_LINK_POINTER(QTKit, QTMovieSizeDidChangeNotification, NSString *)
57 SOFT_LINK_POINTER(QTKit, QTMovieTimeDidChangeNotification, NSString *)
58 SOFT_LINK_POINTER(QTKit, QTMovieTimeScaleAttribute, NSString *)
59 SOFT_LINK_POINTER(QTKit, QTMovieVolumeDidChangeNotification, NSString *)
60
61 #define QTMovie getQTMovieClass()
62 #define QTMovieView getQTMovieViewClass()
63
64 #define QTMovieDataSizeAttribute getQTMovieDataSizeAttribute()
65 #define QTMovieDidEndNotification getQTMovieDidEndNotification()
66 #define QTMovieHasVideoAttribute getQTMovieHasVideoAttribute()
67 #define QTMovieLoadStateAttribute getQTMovieLoadStateAttribute()
68 #define QTMovieLoadStateDidChangeNotification getQTMovieLoadStateDidChangeNotification()
69 #define QTMovieNaturalSizeAttribute getQTMovieNaturalSizeAttribute()
70 #define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification()
71 #define QTMovieSizeDidChangeNotification getQTMovieSizeDidChangeNotification()
72 #define QTMovieTimeDidChangeNotification getQTMovieTimeDidChangeNotification()
73 #define QTMovieTimeScaleAttribute getQTMovieTimeScaleAttribute()
74 #define QTMovieVolumeDidChangeNotification getQTMovieVolumeDidChangeNotification()
75
76 // Older versions of the QTKit header don't have these constants.
77 #if !defined QTKIT_VERSION_MAX_ALLOWED || QTKIT_VERSION_MAX_ALLOWED <= QTKIT_VERSION_7_0
78 enum {
79     QTMovieLoadStateLoaded  = 2000L,
80     QTMovieLoadStatePlayable = 10000L,
81     QTMovieLoadStatePlaythroughOK = 20000L,
82     QTMovieLoadStateComplete = 100000L
83 };
84 #endif
85
86 using namespace WebCore;
87 using namespace std;
88
89 @interface WebCoreMovieObserver : NSObject
90 {
91     MediaPlayerPrivate* m_callback;
92     BOOL m_delayCallbacks;
93 }
94 -(id)initWithCallback:(MediaPlayerPrivate*)callback;
95 -(void)disconnect;
96 -(void)setDelayCallbacks:(BOOL)shouldDelay;
97 -(void)loadStateChanged:(NSNotification *)notification;
98 -(void)rateChanged:(NSNotification *)notification;
99 -(void)sizeChanged:(NSNotification *)notification;
100 -(void)timeChanged:(NSNotification *)notification;
101 -(void)volumeChanged:(NSNotification *)notification;
102 -(void)didEnd:(NSNotification *)notification;
103 @end
104
105 namespace WebCore {
106
107 static const float cuePointTimerInterval = 0.020f;
108     
109 MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
110     : m_player(player)
111     , m_objcObserver(AdoptNS, [[WebCoreMovieObserver alloc] initWithCallback:this])
112     , m_seekTo(-1)
113     , m_endTime(numeric_limits<float>::infinity())
114     , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired)
115     , m_cuePointTimer(this, &MediaPlayerPrivate::cuePointTimerFired)
116     , m_previousTimeCueTimerFired(0)
117     , m_networkState(MediaPlayer::Empty)
118     , m_readyState(MediaPlayer::DataUnavailable)
119     , m_startedPlaying(false)
120     , m_isStreaming(false)
121 {
122 }
123
124 MediaPlayerPrivate::~MediaPlayerPrivate()
125 {
126     if (m_qtMovieView)
127         [m_qtMovieView.get() removeFromSuperview];
128     [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
129     [m_objcObserver.get() disconnect];
130 }
131
132 void MediaPlayerPrivate::createQTMovie(const String& url)
133 {
134     [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
135     
136     NSError* error = nil;
137     m_qtMovie.adoptNS([[QTMovie alloc] initWithURL:KURL(url.deprecatedString()).getNSURL() error:&error]);
138     
139     // FIXME: Find a proper way to detect streaming content.
140     m_isStreaming = url.startsWith("rtsp:");
141     
142     if (!m_qtMovie)
143         return;
144     
145     [m_qtMovie.get() setVolume:m_player->volume()];
146     [m_qtMovie.get() setMuted:m_player->muted()];
147     
148     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
149                                              selector:@selector(loadStateChanged:) 
150                                                  name:QTMovieLoadStateDidChangeNotification 
151                                                object:m_qtMovie.get()];
152     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
153                                              selector:@selector(rateChanged:) 
154                                                  name:QTMovieRateDidChangeNotification 
155                                                object:m_qtMovie.get()];
156     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
157                                              selector:@selector(sizeChanged:) 
158                                                  name:QTMovieSizeDidChangeNotification 
159                                                object:m_qtMovie.get()];
160     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
161                                              selector:@selector(timeChanged:) 
162                                                  name:QTMovieTimeDidChangeNotification 
163                                                object:m_qtMovie.get()];
164     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
165                                              selector:@selector(volumeChanged:) 
166                                                  name:QTMovieVolumeDidChangeNotification 
167                                                object:m_qtMovie.get()];
168     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()
169                                              selector:@selector(didEnd:) 
170                                                  name:QTMovieDidEndNotification 
171                                                object:m_qtMovie.get()];
172 }
173
174 void MediaPlayerPrivate::createQTMovieView()
175 {
176     if (m_qtMovieView) {
177         [m_qtMovieView.get() removeFromSuperview];
178         m_qtMovieView = nil;
179     }
180     if (!m_player->m_parentWidget || !m_qtMovie)
181         return;
182     m_qtMovieView.adoptNS([[QTMovieView alloc] initWithFrame:m_player->rect()]);
183     NSView* parentView = static_cast<ScrollView*>(m_player->m_parentWidget)->getDocumentView();
184     [parentView addSubview:m_qtMovieView.get()];
185     [m_qtMovieView.get() setMovie:m_qtMovie.get()];
186     [m_qtMovieView.get() setControllerVisible:NO];
187     [m_qtMovieView.get() setPreservesAspectRatio:YES];
188     // the area not covered by video should be transparent
189     [m_qtMovieView.get() setFillColor:[NSColor clearColor]];
190     wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES);
191 }
192
193 QTTime MediaPlayerPrivate::createQTTime(float time) const
194 {
195     if (!m_qtMovie)
196         return QTMakeTime(0, 600);
197     long timeScale = [[m_qtMovie.get() attributeForKey:QTMovieTimeScaleAttribute] longValue];
198     return QTMakeTime(time * timeScale, timeScale);
199 }
200
201 void MediaPlayerPrivate::load(const String& url)
202 {
203     if (m_networkState != MediaPlayer::Loading) {
204         m_networkState = MediaPlayer::Loading;
205         m_player->networkStateChanged();
206     }
207     if (m_readyState != MediaPlayer::DataUnavailable) {
208         m_readyState = MediaPlayer::DataUnavailable;
209         m_player->readyStateChanged();
210     }
211     cancelSeek();
212     m_cuePointTimer.stop();
213     
214     [m_objcObserver.get() setDelayCallbacks:YES];
215
216     createQTMovie(url);
217     if (m_player->visible())
218         createQTMovieView();
219
220     [m_objcObserver.get() loadStateChanged:nil];
221     [m_objcObserver.get() setDelayCallbacks:NO];
222 }
223
224 void MediaPlayerPrivate::play()
225 {
226     if (!m_qtMovie)
227         return;
228     m_startedPlaying = true;
229     [m_objcObserver.get() setDelayCallbacks:YES];
230     [m_qtMovie.get() setRate:m_player->rate()];
231     [m_objcObserver.get() setDelayCallbacks:NO];
232     startCuePointTimerIfNeeded();
233 }
234
235 void MediaPlayerPrivate::pause()
236 {
237     if (!m_qtMovie)
238         return;
239     m_startedPlaying = false;
240     [m_objcObserver.get() setDelayCallbacks:YES];
241     [m_qtMovie.get() stop];
242     [m_objcObserver.get() setDelayCallbacks:NO];
243     m_cuePointTimer.stop();
244 }
245
246 float MediaPlayerPrivate::duration() const
247 {
248     if (!m_qtMovie)
249         return 0;
250     QTTime time = [m_qtMovie.get() duration];
251     if (time.flags == kQTTimeIsIndefinite)
252         return numeric_limits<float>::infinity();
253     return static_cast<float>(time.timeValue) / time.timeScale;
254 }
255
256 float MediaPlayerPrivate::currentTime() const
257 {
258     if (!m_qtMovie)
259         return 0;
260     QTTime time = [m_qtMovie.get() currentTime];
261     return min(static_cast<float>(time.timeValue) / time.timeScale, m_endTime);
262 }
263
264 void MediaPlayerPrivate::seek(float time)
265 {
266     cancelSeek();
267     
268     if (!m_qtMovie)
269         return;
270     
271     if (time > duration())
272         time = duration();
273     
274     m_seekTo = time;
275     if (maxTimeLoaded() >= m_seekTo)
276         doSeek();
277     else 
278         m_seekTimer.start(0, 0.5f);
279 }
280
281 void MediaPlayerPrivate::doSeek() 
282 {
283     QTTime qttime = createQTTime(m_seekTo);
284     // setCurrentTime generates several event callbacks, update afterwards
285     [m_objcObserver.get() setDelayCallbacks:YES];
286     float oldRate = [m_qtMovie.get() rate];
287     [m_qtMovie.get() setRate:0];
288     [m_qtMovie.get() setCurrentTime:qttime];
289     float timeAfterSeek = currentTime();
290     // restore playback only if not at end, othewise QTMovie will loop
291     if (timeAfterSeek < duration() && timeAfterSeek < m_endTime)
292         [m_qtMovie.get() setRate:oldRate];
293     cancelSeek();
294     [m_objcObserver.get() setDelayCallbacks:NO];
295 }
296
297 void MediaPlayerPrivate::cancelSeek()
298 {
299     m_seekTo = -1;
300     m_seekTimer.stop();
301 }
302
303 void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*)
304 {        
305     if (!m_qtMovie || !seeking() || currentTime() == m_seekTo) {
306         cancelSeek();
307         updateStates();
308         m_player->timeChanged(); 
309         return;
310     } 
311     
312     if (maxTimeLoaded() >= m_seekTo)
313         doSeek();
314     else {
315         MediaPlayer::NetworkState state = networkState();
316         if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) {
317             cancelSeek();
318             updateStates();
319             m_player->timeChanged();
320         }
321     }
322 }
323
324 void MediaPlayerPrivate::setEndTime(float time)
325 {
326     m_endTime = time;
327     startCuePointTimerIfNeeded();
328 }
329
330 void MediaPlayerPrivate::addCuePoint(float /*time*/)
331 {
332     // FIXME: Eventually we'd like an approach that doesn't involve a timer.
333     startCuePointTimerIfNeeded();
334 }
335
336 void MediaPlayerPrivate::removeCuePoint(float /*time*/)
337 {
338 }
339
340 void MediaPlayerPrivate::clearCuePoints()
341 {
342 }
343
344 void MediaPlayerPrivate::startCuePointTimerIfNeeded()
345 {
346     if ((m_endTime < duration() || !m_player->m_cuePoints.isEmpty())
347         && m_startedPlaying && !m_cuePointTimer.isActive()) {
348         m_previousTimeCueTimerFired = currentTime();
349         m_cuePointTimer.startRepeating(cuePointTimerInterval);
350     }
351 }
352
353 void MediaPlayerPrivate::cuePointTimerFired(Timer<MediaPlayerPrivate>*)
354 {
355     float time = currentTime();
356     float previousTime = m_previousTimeCueTimerFired;
357     m_previousTimeCueTimerFired = time;
358     
359     // just do end for now
360     if (time >= m_endTime) {
361         pause();
362         didEnd();
363     }
364
365     // Make a copy since m_cuePoints could change as we deliver JavaScript calls.
366     Vector<float> cuePoints;
367     copyToVector(m_player->m_cuePoints, cuePoints);
368     size_t numCuePoints = cuePoints.size();
369     for (size_t i = 0; i < numCuePoints; ++i) {
370         float cueTime = cuePoints[i];
371         if (previousTime < cueTime && cueTime <= time)
372             m_player->cuePointReached(cueTime);
373     }
374 }
375
376 bool MediaPlayerPrivate::paused() const
377 {
378     if (!m_qtMovie)
379         return true;
380     return [m_qtMovie.get() rate] == 0;
381 }
382
383 bool MediaPlayerPrivate::seeking() const
384 {
385     if (!m_qtMovie)
386         return false;
387     return m_seekTo >= 0;
388 }
389
390 IntSize MediaPlayerPrivate::naturalSize() const
391 {
392     if (!m_qtMovie)
393         return IntSize();
394     return IntSize([[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]);
395 }
396
397 bool MediaPlayerPrivate::hasVideo() const
398 {
399     if (!m_qtMovie)
400         return false;
401     return [[m_qtMovie.get() attributeForKey:QTMovieHasVideoAttribute] boolValue];
402 }
403
404 void MediaPlayerPrivate::setVolume(float volume)
405 {
406     if (!m_qtMovie)
407         return;
408     [m_qtMovie.get() setVolume:volume];  
409 }
410
411 void MediaPlayerPrivate::setMuted(bool b)
412 {
413     if (!m_qtMovie)
414         return;
415     [m_qtMovie.get() setMuted:b];
416 }
417
418 void MediaPlayerPrivate::setRate(float rate)
419 {
420     if (!m_qtMovie)
421         return;
422     if (!paused())
423         [m_qtMovie.get() setRate:rate];
424 }
425
426 int MediaPlayerPrivate::dataRate() const
427 {
428     if (!m_qtMovie)
429         return 0;
430     return wkQTMovieDataRate(m_qtMovie.get()); 
431 }
432
433
434 float MediaPlayerPrivate::maxTimeBuffered() const
435 {
436     // rtsp streams are not buffered
437     return m_isStreaming ? 0 : maxTimeLoaded();
438 }
439
440 float MediaPlayerPrivate::maxTimeSeekable() const
441 {
442     // infinite duration means live stream
443     return isinf(duration()) ? 0 : maxTimeLoaded();
444 }
445
446 float MediaPlayerPrivate::maxTimeLoaded() const
447 {
448     if (!m_qtMovie)
449         return 0;
450     return wkQTMovieMaxTimeLoaded(m_qtMovie.get()); 
451 }
452
453 unsigned MediaPlayerPrivate::bytesLoaded() const
454 {
455     float dur = duration();
456     if (!dur)
457         return 0;
458     return totalBytes() * maxTimeLoaded() / dur;
459 }
460
461 bool MediaPlayerPrivate::totalBytesKnown() const
462 {
463     return totalBytes() > 0;
464 }
465
466 unsigned MediaPlayerPrivate::totalBytes() const
467 {
468     if (!m_qtMovie)
469         return 0;
470     return [[m_qtMovie.get() attributeForKey:QTMovieDataSizeAttribute] intValue];
471 }
472
473 void MediaPlayerPrivate::cancelLoad()
474 {
475     // FIXME: Is there a better way to check for this?
476     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
477         return;
478     
479     if (m_qtMovieView) {
480         [m_qtMovieView.get() removeFromSuperview];
481         m_qtMovieView = nil;
482     }
483     m_qtMovie = nil;
484     
485     updateStates();
486 }
487
488 void MediaPlayerPrivate::updateStates()
489 {
490     MediaPlayer::NetworkState oldNetworkState = m_networkState;
491     MediaPlayer::ReadyState oldReadyState = m_readyState;
492     
493     long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : -1;
494     // "Loaded" is reserved for fully buffered movies, never the case when streaming
495     if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) {
496         if (m_networkState < MediaPlayer::Loaded)
497             m_networkState = MediaPlayer::Loaded;
498         m_readyState = MediaPlayer::CanPlayThrough;
499     } else if (loadState >= QTMovieLoadStatePlaythroughOK) {
500         if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking())
501             m_networkState = MediaPlayer::LoadedFirstFrame;
502         m_readyState = ([m_qtMovie.get() rate] == 0 && m_startedPlaying) ? MediaPlayer::DataUnavailable : MediaPlayer::CanPlayThrough;
503     } else if (loadState >= QTMovieLoadStatePlayable) {
504         if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking())
505             m_networkState = MediaPlayer::LoadedFirstFrame;
506         m_readyState = ([m_qtMovie.get() rate] == 0 && m_startedPlaying) ? MediaPlayer::DataUnavailable : MediaPlayer::CanPlay;
507     } else if (loadState >= QTMovieLoadStateLoaded) {
508         if (m_networkState < MediaPlayer::LoadedMetaData)
509             m_networkState = MediaPlayer::LoadedMetaData;
510         m_readyState = MediaPlayer::DataUnavailable;
511     } else if (loadState >= 0) {
512         if (m_networkState < MediaPlayer::Loading)
513             m_networkState = MediaPlayer::Loading;
514         m_readyState = MediaPlayer::DataUnavailable;        
515     } else {
516         m_networkState = MediaPlayer::LoadFailed;
517         m_readyState = MediaPlayer::DataUnavailable; 
518     }
519
520     if (seeking())
521         m_readyState = MediaPlayer::DataUnavailable;
522     
523     if (m_networkState != oldNetworkState)
524         m_player->networkStateChanged();
525     if (m_readyState != oldReadyState)
526         m_player->readyStateChanged();
527 }
528
529 void MediaPlayerPrivate::loadStateChanged()
530 {
531     updateStates();
532 }
533
534 void MediaPlayerPrivate::rateChanged()
535 {
536     updateStates();
537 }
538
539 void MediaPlayerPrivate::sizeChanged()
540 {
541 }
542
543 void MediaPlayerPrivate::timeChanged()
544 {
545     m_previousTimeCueTimerFired = -1;
546     updateStates();
547     m_player->timeChanged();
548 }
549
550 void MediaPlayerPrivate::volumeChanged()
551 {
552     m_player->volumeChanged();
553 }
554
555 void MediaPlayerPrivate::didEnd()
556 {
557     m_cuePointTimer.stop();
558     m_startedPlaying = false;
559     updateStates();
560     m_player->timeChanged();
561 }
562
563 void MediaPlayerPrivate::setRect(const IntRect& r) 
564
565     if (m_qtMovieView)
566         [m_qtMovieView.get() setFrame:r];
567 }
568
569 void MediaPlayerPrivate::setVisible(bool b)
570 {
571     if (b)
572         createQTMovieView();
573     else if (m_qtMovieView) {
574         [m_qtMovieView.get() removeFromSuperview];
575         m_qtMovieView = nil;
576     }
577 }
578
579 void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r)
580 {
581     if (context->paintingDisabled())
582         return;
583     NSView *view = m_qtMovieView.get();
584     if (view == nil)
585         return;
586     [m_objcObserver.get() setDelayCallbacks:YES];
587     BEGIN_BLOCK_OBJC_EXCEPTIONS;
588     [view displayRectIgnoringOpacity:[view convertRect:r fromView:[view superview]]];
589     END_BLOCK_OBJC_EXCEPTIONS;
590     [m_objcObserver.get() setDelayCallbacks:NO];
591 }
592
593 void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types)
594 {
595     NSArray* fileTypes = [QTMovie movieFileTypes:QTIncludeCommonTypes];
596     int count = [fileTypes count];
597     for (int n = 0; n < count; n++) {
598         CFStringRef ext = reinterpret_cast<CFStringRef>([fileTypes objectAtIndex:n]);
599         RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL));
600         if (!uti)
601             continue;
602         RetainPtr<CFStringRef> mime(AdoptCF, UTTypeCopyPreferredTagWithClass(uti.get(), kUTTagClassMIMEType));
603         if (!mime)
604             continue;
605         types.add(mime.get());
606     }
607
608
609 }
610
611 @implementation WebCoreMovieObserver
612
613 - (id)initWithCallback:(MediaPlayerPrivate *)callback
614 {
615     m_callback = callback;
616     return [super init];
617 }
618
619 - (void)disconnect
620 {
621     [NSObject cancelPreviousPerformRequestsWithTarget:self];
622     m_callback = 0;
623 }
624
625 - (void)loadStateChanged:(NSNotification *)notification
626 {
627     if (m_delayCallbacks)
628         [self performSelector:_cmd withObject:nil afterDelay:0];
629     else
630         m_callback->loadStateChanged();
631 }
632
633 - (void)rateChanged:(NSNotification *)notification
634 {
635     if (m_delayCallbacks)
636         [self performSelector:_cmd withObject:nil afterDelay:0];
637     else
638         m_callback->rateChanged();
639 }
640
641 - (void)sizeChanged:(NSNotification *)notification
642 {
643     if (m_delayCallbacks)
644         [self performSelector:_cmd withObject:nil afterDelay:0];
645     else
646         m_callback->sizeChanged();
647 }
648
649 - (void)timeChanged:(NSNotification *)notification
650 {
651     if (m_delayCallbacks)
652         [self performSelector:_cmd withObject:nil afterDelay:0];
653     else
654         m_callback->timeChanged();
655 }
656
657 - (void)volumeChanged:(NSNotification *)notification
658 {
659     if (m_delayCallbacks)
660         [self performSelector:_cmd withObject:nil afterDelay:0];
661     else
662         m_callback->volumeChanged();
663 }
664
665 - (void)didEnd:(NSNotification *)notification
666 {
667     if (m_delayCallbacks)
668         [self performSelector:_cmd withObject:nil afterDelay:0];
669     else
670         m_callback->didEnd();
671 }
672
673 - (void)setDelayCallbacks:(BOOL)shouldDelay
674 {
675     m_delayCallbacks = shouldDelay;
676 }
677
678 @end
679
680 #endif