WebCore:
[WebKit-https.git] / WebCore / html / HTMLMediaElement.cpp
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 #include "config.h"
27
28 #if ENABLE(VIDEO)
29 #include "HTMLMediaElement.h"
30
31 #include "csshelper.h"
32 #include "CSSPropertyNames.h"
33 #include "CSSValueKeywords.h"
34 #include "EventNames.h"
35 #include "ExceptionCode.h"
36 #include "HTMLDocument.h"
37 #include "HTMLNames.h"
38 #include "HTMLSourceElement.h"
39 #include "HTMLVideoElement.h"
40 #include <limits>
41 #include "MediaError.h"
42 #include "MediaList.h"
43 #include "MediaQueryEvaluator.h"
44 #include "MIMETypeRegistry.h"
45 #include "Movie.h"
46 #include "RenderVideo.h"
47 #include "SystemTime.h"
48 #include "TimeRanges.h"
49 #include "VoidCallback.h"
50
51 using namespace std;
52
53 namespace WebCore {
54
55 using namespace EventNames;
56 using namespace HTMLNames;
57
58 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc)
59     : HTMLElement(tagName, doc)
60     , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
61     , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired)
62     , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
63     , m_defaultPlaybackRate(1.0f)
64     , m_networkState(EMPTY)
65     , m_readyState(DATA_UNAVAILABLE)
66     , m_begun(false)
67     , m_loadedFirstFrame(false)
68     , m_autoplaying(true)
69     , m_wasPlayingBeforeMovingToPageCache(false)
70     , m_currentLoop(0)
71     , m_volume(0.5f)
72     , m_muted(false)
73     , m_previousProgress(0)
74     , m_previousProgressTime(numeric_limits<double>::max())
75     , m_sentStalledEvent(false)
76     , m_bufferingRate(0)
77     , m_loadNestingLevel(0)
78     , m_terminateLoadBelowNestingLevel(0)
79     , m_movie(0)
80 {
81     document()->registerForCacheCallbacks(this);
82 }
83
84 HTMLMediaElement::~HTMLMediaElement()
85 {
86     document()->unregisterForCacheCallbacks(this);
87     delete m_movie;
88     for (HashMap<float, CallbackVector*>::iterator it = m_cuePoints.begin(); it != m_cuePoints.end(); ++it)
89         delete it->second;
90 }
91
92 bool HTMLMediaElement::checkDTD(const Node* newChild)
93 {
94     return newChild->hasTagName(sourceTag) || HTMLElement::checkDTD(newChild);
95 }
96
97 void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls)
98 {
99     HTMLElement::attributeChanged(attr, preserveDecls);
100
101     const QualifiedName& attrName = attr->name();
102     if (attrName == srcAttr) {
103         // 3.14.9.2.
104         // change to src attribute triggers load()
105         if (inDocument() && m_networkState == EMPTY)
106             scheduleLoad();
107     }
108 }
109     
110 void HTMLMediaElement::insertedIntoDocument()
111 {
112     HTMLElement::insertedIntoDocument();
113     if (!src().isEmpty())
114         scheduleLoad();
115 }
116
117 void HTMLMediaElement::removedFromDocument()
118 {
119     delete m_movie;
120     m_movie = 0;
121     HTMLElement::removedFromDocument();
122 }
123
124 void HTMLMediaElement::scheduleLoad()
125 {
126     m_loadTimer.startOneShot(0);
127 }
128
129 void HTMLMediaElement::initAndDispatchProgressEvent(const AtomicString& eventName)
130 {
131     bool totalKnown = m_movie && m_movie->totalBytesKnown();
132     unsigned loaded = m_movie ? m_movie->bytesLoaded() : 0;
133     unsigned total = m_movie ? m_movie->totalBytes() : 0;
134     dispatchProgressEvent(eventName, totalKnown, loaded, total);
135 }
136
137 void HTMLMediaElement::dispatchEventAsync(const AtomicString& eventName)
138 {
139     m_asyncEventsToDispatch.add(eventName);
140     if (!m_asyncEventTimer.isActive())                            
141         m_asyncEventTimer.startOneShot(0);
142 }
143
144 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
145 {
146     ExceptionCode ec;
147     load(ec);
148 }
149
150 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
151 {
152     HashSet<String>::const_iterator end = m_asyncEventsToDispatch.end();
153     for (HashSet<String>::const_iterator it = m_asyncEventsToDispatch.begin(); it != end; ++it)
154         dispatchHTMLEvent(*it, false, true);
155     m_asyncEventsToDispatch.clear();
156 }
157
158 String serializeTimeOffset(float time)
159 {
160     String timeString = String::number(time);
161     // FIXME serialize time offset values properly (format not specified yet)
162     timeString.append("s");
163     return timeString;
164 }
165
166 float parseTimeOffset(String timeString, bool* ok = 0)
167 {
168     if (timeString.endsWith("s"))
169         timeString = timeString.left(timeString.length() - 1);
170     // FIXME parse time offset values (format not specified yet)
171     float val = (float)timeString.toDouble(ok);
172     return val;
173 }
174
175 float HTMLMediaElement::getTimeOffsetAttribute(const QualifiedName& name, float valueOnError) const
176 {
177     bool ok;
178     String timeString = getAttribute(name);
179     float result = parseTimeOffset(timeString, &ok);
180     if (ok)
181         return result;
182     return valueOnError;
183 }
184
185 void HTMLMediaElement::setTimeOffsetAttribute(const QualifiedName& name, float value)
186 {
187     setAttribute(name, serializeTimeOffset(value));
188 }
189
190 PassRefPtr<MediaError> HTMLMediaElement::error() const 
191 {
192     return m_error;
193 }
194
195 String HTMLMediaElement::src() const
196 {
197     return document()->completeURL(getAttribute(srcAttr));
198 }
199
200 void HTMLMediaElement::HTMLMediaElement::setSrc(const String& url)
201 {
202     setAttribute(srcAttr, url);
203 }
204
205 String HTMLMediaElement::currentSrc() const
206 {
207     return m_currentSrc;
208 }
209
210 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
211 {
212     return m_networkState;
213 }
214
215 float HTMLMediaElement::bufferingRate()
216 {
217     if (!m_movie)
218         return 0;
219     return m_bufferingRate;
220     //return m_movie->dataRate();
221 }
222
223 void HTMLMediaElement::load(ExceptionCode& ec)
224 {
225     String mediaSrc;
226     
227     // 3.14.9.4. Loading the media resource
228     // 1
229     // if an event generated during load() ends up re-entering load(), terminate previous instances
230     m_loadNestingLevel++;
231     m_terminateLoadBelowNestingLevel = m_loadNestingLevel;
232     
233     m_progressEventTimer.stop();
234     m_sentStalledEvent = false;
235     m_bufferingRate = 0;
236     
237     m_loadTimer.stop();
238     
239     // 2
240     if (m_begun) {
241         m_begun = false;
242         m_error = new MediaError(MediaError::MEDIA_ERR_ABORTED);
243         initAndDispatchProgressEvent(abortEvent);
244         if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
245             goto end;
246     }
247     
248     // 3
249     m_error = 0;
250     m_loadedFirstFrame = false;
251     m_autoplaying = true;
252     
253     // 4
254     setPlaybackRate(defaultPlaybackRate(), ec);
255     
256     // 5
257     if (networkState() != EMPTY) {
258         m_networkState = EMPTY;
259         m_readyState = DATA_UNAVAILABLE;
260         if (m_movie) {
261             m_movie->pause();
262             m_movie->seek(0);
263         }
264         m_currentLoop = 0;
265         dispatchHTMLEvent(emptiedEvent, false, true);
266         if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
267             goto end;
268     }
269     
270     // 6
271     mediaSrc = pickMedia();
272     if (mediaSrc.isEmpty()) {
273         ec = INVALID_STATE_ERR;
274         goto end;
275     }
276     
277     // 7
278     m_networkState = LOADING;
279     
280     // 8
281     m_currentSrc = mediaSrc;
282     
283     // 9
284     m_begun = true;        
285     dispatchProgressEvent(beginEvent, false, 0, 0); // progress event draft calls this loadstart
286     if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
287         goto end;
288     
289     // 10, 11, 12, 13
290     delete m_movie;
291     m_movie = new Movie(this);
292     m_movie->setVolume(m_volume);
293     m_movie->setMuted(m_muted);
294     for (HashMap<float, CallbackVector*>::iterator it = m_cuePoints.begin(); it != m_cuePoints.end(); ++it)
295         m_movie->addCuePoint(it->first);
296     m_movie->load(m_currentSrc);
297     if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
298         goto end;
299     
300     if (renderer()) {
301         renderer()->updateFromElement();
302         m_movie->setVisible(true);
303     }
304     
305     // 14
306     m_previousProgressTime = WebCore::currentTime();
307     m_previousProgress = 0;
308     if (m_begun)
309         // 350ms is not magic, it is in the spec!
310         m_progressEventTimer.startRepeating(0.350);
311 end:
312     ASSERT(m_loadNestingLevel);
313     m_loadNestingLevel--;
314 }
315
316
317 void HTMLMediaElement::movieNetworkStateChanged(Movie*)
318 {
319     if (!m_begun || m_networkState == EMPTY)
320         return;
321     
322     m_terminateLoadBelowNestingLevel = m_loadNestingLevel;
323
324     Movie::NetworkState state = m_movie->networkState();
325     
326     // 3.14.9.4. Loading the media resource
327     // 14
328     if (state == Movie::LoadFailed) {
329         //delete m_movie;
330         //m_movie = 0;
331         // FIXME better error handling
332         m_error = new MediaError(MediaError::MEDIA_ERR_NETWORK);
333         m_begun = false;
334         m_progressEventTimer.stop();
335         m_bufferingRate = 0;
336         
337         initAndDispatchProgressEvent(errorEvent); 
338         if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
339             return;
340         
341         m_networkState = EMPTY;
342         
343         if (isVideo())
344             static_cast<HTMLVideoElement*>(this)->updatePosterImage();
345
346         dispatchHTMLEvent(emptiedEvent, false, true);
347         return;
348     }
349     
350     if (state >= Movie::Loading && m_networkState < LOADING)
351         m_networkState = LOADING;
352     
353     if (state >= Movie::LoadedMetaData && m_networkState < LOADED_METADATA) {
354         m_movie->seek(effectiveStart());
355         m_movie->setEndTime(currentLoop() == loopCount() - 1 ? effectiveEnd() : effectiveLoopEnd());
356         m_networkState = LOADED_METADATA;
357         
358         dispatchHTMLEvent(durationchangeEvent, false, true);
359         if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
360             return;
361         
362         dispatchHTMLEvent(loadedmetadataEvent, false, true);
363         if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
364             return;
365     }
366     
367     if (state >= Movie::LoadedFirstFrame && m_networkState < LOADED_FIRST_FRAME) {
368         m_networkState = LOADED_FIRST_FRAME;
369         
370         setReadyState(CAN_SHOW_CURRENT_FRAME);
371         
372         if (isVideo())
373             static_cast<HTMLVideoElement*>(this)->updatePosterImage();
374         
375         if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
376             return;
377         
378         m_loadedFirstFrame = true;
379         if (renderer()) {
380             ASSERT(!renderer()->isImage());
381             static_cast<RenderVideo*>(renderer())->videoSizeChanged();
382         }
383         
384         dispatchHTMLEvent(loadedfirstframeEvent, false, true);
385         if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
386             return;
387         
388         dispatchHTMLEvent(canshowcurrentframeEvent, false, true);
389         if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
390             return;
391     }
392     
393     // 15
394     if (state == Movie::Loaded && m_networkState < LOADED) {
395         m_begun = false;
396         m_networkState = LOADED;
397         m_progressEventTimer.stop();
398         m_bufferingRate = 0;
399         initAndDispatchProgressEvent(loadEvent); 
400     }
401 }
402
403 void HTMLMediaElement::movieReadyStateChanged(Movie*)
404 {
405     Movie::ReadyState state = m_movie->readyState();
406     setReadyState((ReadyState)state);
407 }
408
409 void HTMLMediaElement::setReadyState(ReadyState state)
410 {
411     // 3.14.9.6. The ready states
412     if (m_readyState == state)
413         return;
414     
415     bool wasActivelyPlaying = activelyPlaying();
416     m_readyState = state;
417     
418     if (networkState() == EMPTY)
419         return;
420     
421     if (state == DATA_UNAVAILABLE) {
422         dispatchHTMLEvent(dataunavailableEvent, false, true);
423         if (wasActivelyPlaying) {
424             dispatchHTMLEvent(timeupdateEvent, false, true);
425             dispatchHTMLEvent(waitingEvent, false, true);
426         }
427     } else if (state == CAN_SHOW_CURRENT_FRAME) {
428         if (m_loadedFirstFrame)
429             dispatchHTMLEvent(canshowcurrentframeEvent, false, true);
430         if (wasActivelyPlaying) {
431             dispatchHTMLEvent(timeupdateEvent, false, true);
432             dispatchHTMLEvent(waitingEvent, false, true);
433         }
434     } else if (state == CAN_PLAY) {
435         dispatchHTMLEvent(canplayEvent, false, true);
436     } else if (state == CAN_PLAY_THROUGH) {
437         dispatchHTMLEvent(canplaythroughEvent, false, true);
438         if (m_autoplaying && paused() && autoplay()) {
439             m_movie->play();
440             dispatchHTMLEvent(playEvent, false, true);
441         }
442     }
443 }
444
445 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
446 {
447     ASSERT(m_movie);
448     unsigned progress = m_movie->bytesLoaded();
449     double time = WebCore::currentTime();
450     double timedelta = time - m_previousProgressTime;
451     if (timedelta)
452         m_bufferingRate = (float)(0.8 * m_bufferingRate + 0.2 * ((float)(progress - m_previousProgress)) / timedelta);
453     
454     if (progress == m_previousProgress) {
455         if (timedelta > 3.0 && !m_sentStalledEvent) {
456             m_bufferingRate = 0;
457             initAndDispatchProgressEvent(stalledEvent);
458             m_sentStalledEvent = true;
459         }
460     } else {
461         initAndDispatchProgressEvent(progressEvent);
462         m_previousProgress = progress;
463         m_previousProgressTime = time;
464         m_sentStalledEvent = false;
465     }
466 }
467
468 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
469 {
470     // 3.14.9.8. Seeking
471     // 1
472     if (networkState() < LOADED_METADATA) {
473         ec = INVALID_STATE_ERR;
474         return;
475     }
476     
477     // 2
478     float minTime;
479     if (currentLoop() == 0)
480         minTime = effectiveStart();
481     else
482         minTime = effectiveLoopStart();
483  
484     // 3
485     float maxTime = currentLoop() == loopCount() - 1 ? effectiveEnd() : effectiveLoopEnd();
486     
487     // 4
488     time = min(time, maxTime);
489     
490     // 5
491     time = max(time, minTime);
492     
493     // 6
494     RefPtr<TimeRanges> seekableRanges = seekable();
495     if (!seekableRanges->contain(time)) {
496         ec = INDEX_SIZE_ERR;
497         return;
498     }
499     
500     // 7
501     if (m_movie) {
502         m_movie->seek(time);
503         m_movie->setEndTime(maxTime);
504     }
505     
506     // 8
507     // The seeking DOM attribute is implicitly set to true
508     
509     // 9
510     dispatchHTMLEvent(timeupdateEvent, false, true);
511     
512     // 10
513     // As soon as the user agent has established whether or not the media data for the new playback position is available, 
514     // and, if it is, decoded enough data to play back that position, the seeking DOM attribute must be set to false.
515 }
516
517 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
518 {
519     return m_readyState;
520 }
521
522 bool HTMLMediaElement::seeking() const
523 {
524     if (!m_movie)
525         return false;
526     RefPtr<TimeRanges> seekableRanges = seekable();
527     return m_movie->seeking() && seekableRanges->contain(currentTime());
528 }
529
530 // playback state
531 float HTMLMediaElement::currentTime() const
532 {
533     return m_movie ? m_movie->currentTime() : 0;
534 }
535
536 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
537 {
538     seek(time, ec);
539 }
540
541 float HTMLMediaElement::duration() const
542 {
543     return m_movie ? m_movie->duration() : 0;
544 }
545
546 bool HTMLMediaElement::paused() const
547 {
548     return m_movie ? m_movie->paused() : true;
549 }
550
551 float HTMLMediaElement::defaultPlaybackRate() const
552 {
553     return m_defaultPlaybackRate;
554 }
555
556 void HTMLMediaElement::setDefaultPlaybackRate(float rate, ExceptionCode& ec)
557 {
558     if (rate == 0.0f) {
559         ec = NOT_SUPPORTED_ERR;
560         return;
561     }
562     if (m_defaultPlaybackRate != rate) {
563         m_defaultPlaybackRate = rate;
564         dispatchEventAsync(ratechangeEvent);
565     }
566 }
567
568 float HTMLMediaElement::playbackRate() const
569 {
570     return m_movie ? m_movie->rate() : 0;
571 }
572
573 void HTMLMediaElement::setPlaybackRate(float rate, ExceptionCode& ec)
574 {
575     if (rate == 0.0f) {
576         ec = NOT_SUPPORTED_ERR;
577         return;
578     }
579     if (m_movie && m_movie->rate() != rate) {
580         m_movie->setRate(rate);
581         dispatchEventAsync(ratechangeEvent);
582     }
583 }
584
585 bool HTMLMediaElement::ended()
586 {
587     return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == loopCount() - 1;
588 }
589
590 bool HTMLMediaElement::autoplay() const
591 {
592     return hasAttribute(autoplayAttr);
593 }
594
595 void HTMLMediaElement::setAutoplay(bool b)
596 {
597     setBooleanAttribute(autoplayAttr, b);
598 }
599
600 void HTMLMediaElement::play(ExceptionCode& ec)
601 {
602     // 3.14.9.7. Playing the media resource
603     if (!m_movie || networkState() == EMPTY) {
604         load(ec);
605         if (ec)
606             return;
607     }
608     if (endedPlayback()) {
609         m_currentLoop = 0;
610         seek(effectiveStart(), ec);
611         if (ec)
612             return;
613     }
614     setPlaybackRate(defaultPlaybackRate(), ec);
615     if (ec)
616         return;
617     
618     m_autoplaying = false;
619     
620     if (m_movie->paused()) {
621         dispatchHTMLEvent(playEvent, false, true);
622         m_movie->play();
623     }
624 }
625
626 void HTMLMediaElement::pause(ExceptionCode& ec)
627 {
628     // 3.14.9.7. Playing the media resource
629     if (!m_movie || networkState() == EMPTY) {
630         load(ec);
631     }
632
633     m_autoplaying = false;
634
635     if (!m_movie->paused()) {
636         dispatchHTMLEvent(pauseEvent, false, true);
637         m_movie->pause();
638     }
639 }
640
641 unsigned HTMLMediaElement::loopCount() const
642 {
643     String val = getAttribute(loopcountAttr);
644     int count = val.toInt();
645     return max(count, 1); 
646 }
647
648 void HTMLMediaElement::setLoopCount(unsigned count, ExceptionCode& ec)
649 {
650     if (!count) {
651         ec = INDEX_SIZE_ERR;
652         return;
653     }
654     setAttribute(loopcountAttr, String::number(count));
655     checkIfSeekNeeded();
656 }
657
658 float HTMLMediaElement::start() const 
659
660     return getTimeOffsetAttribute(startAttr, 0); 
661 }
662
663 void HTMLMediaElement::setStart(float time) 
664
665     setTimeOffsetAttribute(startAttr, time); 
666     checkIfSeekNeeded();
667 }
668
669 float HTMLMediaElement::end() const 
670
671     return getTimeOffsetAttribute(endAttr, std::numeric_limits<float>::infinity()); 
672 }
673
674 void HTMLMediaElement::setEnd(float time) 
675
676     setTimeOffsetAttribute(endAttr, time); 
677     checkIfSeekNeeded();
678 }
679
680 float HTMLMediaElement::loopStart() const 
681
682     return getTimeOffsetAttribute(loopstartAttr, 0); 
683 }
684
685 void HTMLMediaElement::setLoopStart(float time) 
686 {
687     setTimeOffsetAttribute(loopstartAttr, time); 
688     checkIfSeekNeeded();
689 }
690
691 float HTMLMediaElement::loopEnd() const 
692
693     return getTimeOffsetAttribute(loopendAttr, std::numeric_limits<float>::infinity()); 
694 }
695
696 void HTMLMediaElement::setLoopEnd(float time) 
697
698     setTimeOffsetAttribute(loopendAttr, time); 
699     checkIfSeekNeeded();
700 }
701
702 unsigned HTMLMediaElement::currentLoop() const
703 {
704     return m_currentLoop;
705 }
706
707 void HTMLMediaElement::setCurrentLoop(unsigned currentLoop)
708 {
709     m_currentLoop = currentLoop;
710 }
711
712 bool HTMLMediaElement::controls() const
713 {
714     return hasAttribute(controlsAttr);
715 }
716
717 void HTMLMediaElement::setControls(bool b)
718 {
719     setBooleanAttribute(controlsAttr, b);
720 }
721
722 float HTMLMediaElement::volume() const
723 {
724     return m_volume;
725 }
726
727 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
728 {
729     if (vol < 0.0f || vol > 1.0f) {
730         ec = INDEX_SIZE_ERR;
731         return;
732     }
733     
734     if (m_volume != vol) {
735         m_volume = vol;
736         dispatchEventAsync(volumechangeEvent);
737     
738         if (m_movie)
739             m_movie->setVolume(vol);
740     }
741 }
742
743 bool HTMLMediaElement::muted() const
744 {
745     return m_muted;
746 }
747
748 void HTMLMediaElement::setMuted(bool muted)
749 {
750     if (m_muted != muted) {
751         m_muted = muted;
752         dispatchEventAsync(volumechangeEvent);
753         if (m_movie)
754             m_movie->setMuted(muted);
755     }
756 }
757
758 String HTMLMediaElement::pickMedia()
759 {
760     // 3.14.9.2. Location of the media resource
761     String mediaSrc = getAttribute(srcAttr);
762     if (mediaSrc.isEmpty()) {
763         for (Node* n = firstChild(); n; n = n->nextSibling()) {
764             if (n->hasTagName(sourceTag)) {
765                 HTMLSourceElement* source = static_cast<HTMLSourceElement*>(n);
766                 if (!source->hasAttribute(srcAttr))
767                     continue; 
768                 if (source->hasAttribute(mediaAttr)) {
769                     MediaQueryEvaluator screenEval("screen", document()->page(), renderer() ? renderer()->style() : 0);
770                     RefPtr<MediaList> media = new MediaList((CSSStyleSheet*)0, source->media(), true);
771                     if (!screenEval.eval(media.get()))
772                         continue;
773                 }
774                 if (source->hasAttribute(typeAttr)) {
775                     String type = source->type();
776                     if (!MIMETypeRegistry::isSupportedMovieMIMEType(type))
777                         continue;
778                 }
779                 mediaSrc = source->src();
780                 break;
781             }
782         }
783     }
784     if (!mediaSrc.isEmpty())
785         mediaSrc = document()->completeURL(mediaSrc);
786     return mediaSrc;
787 }
788
789 void HTMLMediaElement::checkIfSeekNeeded()
790 {
791     // 3.14.9.5. Offsets into the media resource
792     // 1
793     if (loopCount() - 1 < m_currentLoop)
794         m_currentLoop = loopCount() - 1;
795     
796     // 2
797     if (networkState() <= LOADING)
798         return;
799     
800     // 3
801     ExceptionCode ec;
802     float time = currentTime();
803     if (!m_currentLoop && time < effectiveStart())
804         seek(effectiveStart(), ec);
805
806     // 4
807     if (m_currentLoop && time < effectiveLoopStart())
808         seek(effectiveLoopStart(), ec);
809         
810     // 5
811     if (m_currentLoop < loopCount() - 1 && time > effectiveLoopEnd()) {
812         seek(effectiveLoopStart(), ec);
813         m_currentLoop++;
814     }
815     
816     // 6
817     if (m_currentLoop == loopCount() - 1 && time > effectiveEnd())
818         seek(effectiveEnd(), ec);
819 }
820
821 void HTMLMediaElement::movieVolumeChanged(Movie*)
822 {
823     if (!m_movie)
824         return;
825     if (m_movie->volume() != m_volume || m_movie->muted() != m_muted) {
826         m_volume = m_movie->volume();
827         m_muted = m_movie->muted();
828         dispatchEventAsync(volumechangeEvent);
829     }
830 }
831
832 void HTMLMediaElement::movieDidEnd(Movie*)
833 {
834     if (m_currentLoop < loopCount() - 1 && currentTime() >= effectiveLoopEnd()) {
835         m_movie->seek(effectiveLoopStart());
836         m_currentLoop++;
837         m_movie->setEndTime(m_currentLoop == loopCount() - 1 ? effectiveEnd() : effectiveLoopEnd());
838         if (m_movie)
839             m_movie->play();
840         dispatchHTMLEvent(timeupdateEvent, false, true);
841     }
842     
843     if (m_currentLoop == loopCount() - 1 && currentTime() >= effectiveEnd()) {
844         dispatchHTMLEvent(timeupdateEvent, false, true);
845         dispatchHTMLEvent(endedEvent, false, true);
846     }
847 }
848
849 void HTMLMediaElement::movieCuePointReached(Movie*, float cueTime)
850 {
851     CallbackVector* callbackVector = m_cuePoints.get(cueTime);
852     if (!callbackVector)
853         return;
854     for (unsigned n = 0; n < callbackVector->size(); n++) {
855         CallbackEntry ce = (*callbackVector)[n];
856         if (ce.m_pause) {
857             ExceptionCode ec;
858             pause(ec);
859             break;
860         }
861     }    
862     
863     dispatchHTMLEvent(timeupdateEvent, false, true);
864     
865     for (unsigned n = 0; n < callbackVector->size(); n++) {
866         CallbackEntry ce = (*callbackVector)[n];
867         if (ce.m_voidCallback) 
868             ce.m_voidCallback->execute(document()->frame());
869     }      
870 }
871
872 void HTMLMediaElement::addCuePoint(float time, VoidCallback* voidCallback, bool pause)
873 {
874     if (time < 0 || !isfinite(time))
875         return;
876     CallbackVector* callbackVector = m_cuePoints.get(time);
877     if (!callbackVector) {
878         callbackVector = new CallbackVector;
879         m_cuePoints.add(time, callbackVector);
880     }
881     callbackVector->append(CallbackEntry(voidCallback, pause));
882     
883     if (m_movie)
884         m_movie->addCuePoint(time);
885 }
886
887 void HTMLMediaElement::removeCuePoint(float time, VoidCallback* callback)
888 {
889     if (time < 0 || !isfinite(time))
890         return;
891     CallbackVector* callbackVector = m_cuePoints.get(time);
892     if (callbackVector) {
893         for (unsigned n = 0; n < callbackVector->size(); n++) {
894             if (*(*callbackVector)[n].m_voidCallback == *callback) {
895                 callbackVector->remove(n);
896                 break;
897             }
898         }
899         if (!callbackVector->size()) {
900             delete callbackVector;
901             m_cuePoints.remove(time);
902             if (m_movie)
903                 m_movie->removeCuePoint(time);
904         }
905     }
906 }
907
908 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
909 {
910     // FIXME real ranges support
911     if (!m_movie || !m_movie->maxTimeBuffered())
912         return new TimeRanges;
913     return new TimeRanges(0, m_movie->maxTimeBuffered());
914 }
915
916 PassRefPtr<TimeRanges> HTMLMediaElement::played() const
917 {
918     // FIXME track played
919     return new TimeRanges;
920 }
921
922 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
923 {
924     // FIXME real ranges support
925     if (!m_movie || !m_movie->maxTimeSeekable())
926         return new TimeRanges;
927     return new TimeRanges(0, m_movie->maxTimeSeekable());
928 }
929
930 float HTMLMediaElement::effectiveStart() const
931 {
932     if (!m_movie)
933         return 0;
934     return min(start(), m_movie->duration());
935 }
936
937 float HTMLMediaElement::effectiveEnd() const
938 {
939     if (!m_movie)
940         return 0;
941     return min(max(end(), max(start(), loopStart())), m_movie->duration());
942 }
943
944 float HTMLMediaElement::effectiveLoopStart() const
945 {
946     if (!m_movie)
947         return 0;
948     return min(loopStart(), m_movie->duration());
949 }
950
951 float HTMLMediaElement::effectiveLoopEnd() const
952 {
953     if (!m_movie)
954         return 0;
955     return min(max(start(), max(loopStart(), loopEnd())), m_movie->duration());
956 }
957
958 bool HTMLMediaElement::activelyPlaying() const
959 {
960     return !paused() && readyState() >= CAN_PLAY && !endedPlayback(); // && !stoppedDueToErrors() && !pausedForUserInteraction();
961 }
962
963 bool HTMLMediaElement::endedPlayback() const
964 {
965     return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == loopCount() - 1;
966 }
967
968 void HTMLMediaElement::willSaveToCache()
969 {
970     // 3.14.9.4. Loading the media resource
971     // 14
972     if (m_begun) {
973         if (m_movie)
974             m_movie->cancelLoad();
975         m_error = new MediaError(MediaError::MEDIA_ERR_ABORTED);
976         m_begun = false;
977         initAndDispatchProgressEvent(abortEvent);
978         if (m_networkState >= LOADING) {
979             m_networkState = EMPTY;
980             dispatchHTMLEvent(emptiedEvent, false, true);
981         }
982     }
983     
984     ExceptionCode ec;
985     m_wasPlayingBeforeMovingToPageCache = !paused();
986     if (m_wasPlayingBeforeMovingToPageCache)
987         pause(ec);
988     if (m_movie)
989         m_movie->setVisible(false);
990 }
991
992 void HTMLMediaElement::didRestoreFromCache()
993 {
994     ExceptionCode ec;
995     if (m_wasPlayingBeforeMovingToPageCache)
996         play(ec);
997     if (renderer())
998         m_movie->setVisible(true);
999 }
1000
1001 }
1002
1003 #endif