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