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