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