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