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