2 * Copyright (C) 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "HTMLMediaElement.h"
31 #include "CSSHelper.h"
32 #include "CSSPropertyNames.h"
33 #include "CSSValueKeywords.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"
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>
57 using namespace EventNames;
58 using namespace HTMLNames;
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)
69 , m_loadedFirstFrame(false)
71 , m_wasPlayingBeforeMovingToPageCache(false)
77 , m_currentTimeDuringSeek(0)
78 , m_previousProgress(0)
79 , m_previousProgressTime(numeric_limits<double>::max())
80 , m_sentStalledEvent(false)
82 , m_loadNestingLevel(0)
83 , m_terminateLoadBelowNestingLevel(0)
86 document()->registerForCacheCallbacks(this);
89 HTMLMediaElement::~HTMLMediaElement()
91 document()->unregisterForCacheCallbacks(this);
93 for (HashMap<float, CallbackVector*>::iterator it = m_cuePoints.begin(); it != m_cuePoints.end(); ++it)
97 bool HTMLMediaElement::checkDTD(const Node* newChild)
99 return newChild->hasTagName(sourceTag) || HTMLElement::checkDTD(newChild);
102 void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls)
104 HTMLElement::attributeChanged(attr, preserveDecls);
106 const QualifiedName& attrName = attr->name();
107 if (attrName == srcAttr) {
109 // change to src attribute triggers load()
110 if (inDocument() && m_networkState == EMPTY)
112 } if (attrName == controlsAttr) {
113 if (!isVideo() && attached() && (controls() != (renderer() != 0))) {
118 renderer()->updateFromElement();
122 bool HTMLMediaElement::rendererIsNeeded(RenderStyle* style)
124 return controls() ? HTMLElement::rendererIsNeeded(style) : false;
127 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
129 return new (arena) RenderMedia(this);
132 void HTMLMediaElement::insertedIntoDocument()
134 HTMLElement::insertedIntoDocument();
135 if (!src().isEmpty())
139 void HTMLMediaElement::removedFromDocument()
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.
145 HTMLElement::removedFromDocument();
148 void HTMLMediaElement::scheduleLoad()
150 m_loadTimer.startOneShot(0);
153 void HTMLMediaElement::initAndDispatchProgressEvent(const AtomicString& eventName)
155 bool totalKnown = m_player && m_player->totalBytesKnown();
156 unsigned loaded = m_player ? m_player->bytesLoaded() : 0;
157 unsigned total = m_player ? m_player->totalBytes() : 0;
158 dispatchProgressEvent(eventName, totalKnown, loaded, total);
161 void HTMLMediaElement::dispatchEventAsync(const AtomicString& eventName)
163 m_asyncEventsToDispatch.append(eventName);
164 if (!m_asyncEventTimer.isActive())
165 m_asyncEventTimer.startOneShot(0);
168 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
174 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
176 Vector<AtomicString> asyncEventsToDispatch;
177 m_asyncEventsToDispatch.swap(asyncEventsToDispatch);
178 unsigned count = asyncEventsToDispatch.size();
179 for (unsigned n = 0; n < count; ++n)
180 dispatchHTMLEvent(asyncEventsToDispatch[n], false, true);
183 String serializeTimeOffset(float time)
185 String timeString = String::number(time);
186 // FIXME serialize time offset values properly (format not specified yet)
187 timeString.append("s");
191 float parseTimeOffset(String timeString, bool* ok = 0)
193 if (timeString.endsWith("s"))
194 timeString = timeString.left(timeString.length() - 1);
195 // FIXME parse time offset values (format not specified yet)
196 float val = (float)timeString.toDouble(ok);
200 float HTMLMediaElement::getTimeOffsetAttribute(const QualifiedName& name, float valueOnError) const
203 String timeString = getAttribute(name);
204 float result = parseTimeOffset(timeString, &ok);
210 void HTMLMediaElement::setTimeOffsetAttribute(const QualifiedName& name, float value)
212 setAttribute(name, serializeTimeOffset(value));
215 PassRefPtr<MediaError> HTMLMediaElement::error() const
220 String HTMLMediaElement::src() const
222 return document()->completeURL(getAttribute(srcAttr));
225 void HTMLMediaElement::HTMLMediaElement::setSrc(const String& url)
227 setAttribute(srcAttr, url);
230 String HTMLMediaElement::currentSrc() const
235 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
237 return m_networkState;
240 float HTMLMediaElement::bufferingRate()
244 return m_bufferingRate;
245 //return m_player->dataRate();
248 void HTMLMediaElement::load(ExceptionCode& ec)
252 // 3.14.9.4. Loading the media resource
254 // if an event generated during load() ends up re-entering load(), terminate previous instances
255 m_loadNestingLevel++;
256 m_terminateLoadBelowNestingLevel = m_loadNestingLevel;
258 m_progressEventTimer.stop();
259 m_sentStalledEvent = false;
267 m_error = new MediaError(MediaError::MEDIA_ERR_ABORTED);
268 initAndDispatchProgressEvent(abortEvent);
269 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
275 m_loadedFirstFrame = false;
276 m_autoplaying = true;
279 setPlaybackRate(defaultPlaybackRate(), ec);
282 if (networkState() != EMPTY) {
283 m_networkState = EMPTY;
284 m_readyState = DATA_UNAVAILABLE;
292 dispatchHTMLEvent(emptiedEvent, false, true);
293 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
298 mediaSrc = pickMedia();
299 if (mediaSrc.isEmpty()) {
300 ec = INVALID_STATE_ERR;
305 m_networkState = LOADING;
308 m_currentSrc = mediaSrc;
312 dispatchProgressEvent(beginEvent, false, 0, 0); // progress event draft calls this loadstart
313 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
318 m_player = new MediaPlayer(this);
319 m_player->setVolume(m_volume);
320 m_player->setMuted(m_muted);
321 for (HashMap<float, CallbackVector*>::iterator it = m_cuePoints.begin(); it != m_cuePoints.end(); ++it)
322 m_player->addCuePoint(it->first);
323 m_player->load(m_currentSrc);
324 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
328 renderer()->updateFromElement();
331 m_previousProgressTime = WebCore::currentTime();
332 m_previousProgress = 0;
334 // 350ms is not magic, it is in the spec!
335 m_progressEventTimer.startRepeating(0.350);
337 ASSERT(m_loadNestingLevel);
338 m_loadNestingLevel--;
342 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
344 if (!m_begun || m_networkState == EMPTY)
347 m_terminateLoadBelowNestingLevel = m_loadNestingLevel;
349 MediaPlayer::NetworkState state = m_player->networkState();
351 // 3.14.9.4. Loading the media resource
353 if (state == MediaPlayer::LoadFailed) {
356 // FIXME better error handling
357 m_error = new MediaError(MediaError::MEDIA_ERR_NETWORK);
359 m_progressEventTimer.stop();
362 initAndDispatchProgressEvent(errorEvent);
363 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
366 m_networkState = EMPTY;
369 static_cast<HTMLVideoElement*>(this)->updatePosterImage();
371 dispatchHTMLEvent(emptiedEvent, false, true);
375 if (state >= MediaPlayer::Loading && m_networkState < LOADING)
376 m_networkState = LOADING;
378 if (state >= MediaPlayer::LoadedMetaData && m_networkState < LOADED_METADATA) {
379 m_player->seek(effectiveStart());
380 m_networkState = LOADED_METADATA;
382 dispatchHTMLEvent(durationchangeEvent, false, true);
383 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
386 dispatchHTMLEvent(loadedmetadataEvent, false, true);
387 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
391 if (state >= MediaPlayer::LoadedFirstFrame && m_networkState < LOADED_FIRST_FRAME) {
392 m_networkState = LOADED_FIRST_FRAME;
394 setReadyState(CAN_SHOW_CURRENT_FRAME);
397 static_cast<HTMLVideoElement*>(this)->updatePosterImage();
399 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
402 m_loadedFirstFrame = true;
404 ASSERT(!renderer()->isImage());
405 static_cast<RenderVideo*>(renderer())->videoSizeChanged();
408 dispatchHTMLEvent(loadedfirstframeEvent, false, true);
409 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
412 dispatchHTMLEvent(canshowcurrentframeEvent, false, true);
413 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
418 if (state == MediaPlayer::Loaded && m_networkState < LOADED) {
420 m_networkState = LOADED;
421 m_progressEventTimer.stop();
423 initAndDispatchProgressEvent(loadEvent);
427 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
429 MediaPlayer::ReadyState state = m_player->readyState();
430 setReadyState((ReadyState)state);
433 void HTMLMediaElement::setReadyState(ReadyState state)
435 // 3.14.9.6. The ready states
436 if (m_readyState == state)
439 bool wasActivelyPlaying = activelyPlaying();
440 m_readyState = state;
442 if (state >= CAN_PLAY)
445 if (networkState() == EMPTY)
448 if (state == DATA_UNAVAILABLE) {
449 dispatchHTMLEvent(dataunavailableEvent, false, true);
450 if (wasActivelyPlaying) {
451 dispatchHTMLEvent(timeupdateEvent, false, true);
452 dispatchHTMLEvent(waitingEvent, false, true);
454 } else if (state == CAN_SHOW_CURRENT_FRAME) {
455 if (m_loadedFirstFrame)
456 dispatchHTMLEvent(canshowcurrentframeEvent, false, true);
457 if (wasActivelyPlaying) {
458 dispatchHTMLEvent(timeupdateEvent, false, true);
459 dispatchHTMLEvent(waitingEvent, false, true);
461 } else if (state == CAN_PLAY) {
462 dispatchHTMLEvent(canplayEvent, false, true);
463 } else if (state == CAN_PLAY_THROUGH) {
464 dispatchHTMLEvent(canplaythroughEvent, false, true);
465 if (m_autoplaying && m_paused && autoplay()) {
467 dispatchHTMLEvent(playEvent, false, true);
473 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
476 unsigned progress = m_player->bytesLoaded();
477 double time = WebCore::currentTime();
478 double timedelta = time - m_previousProgressTime;
480 m_bufferingRate = (float)(0.8 * m_bufferingRate + 0.2 * ((float)(progress - m_previousProgress)) / timedelta);
482 if (progress == m_previousProgress) {
483 if (timedelta > 3.0 && !m_sentStalledEvent) {
485 initAndDispatchProgressEvent(stalledEvent);
486 m_sentStalledEvent = true;
489 initAndDispatchProgressEvent(progressEvent);
490 m_previousProgress = progress;
491 m_previousProgressTime = time;
492 m_sentStalledEvent = false;
496 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
500 if (networkState() < LOADED_METADATA) {
501 ec = INVALID_STATE_ERR;
507 if (currentLoop() == 0)
508 minTime = effectiveStart();
510 minTime = effectiveLoopStart();
513 float maxTime = currentLoop() == playCount() - 1 ? effectiveEnd() : effectiveLoopEnd();
516 time = min(time, maxTime);
519 time = max(time, minTime);
522 RefPtr<TimeRanges> seekableRanges = seekable();
523 if (!seekableRanges->contain(time)) {
529 m_currentTimeDuringSeek = time;
535 dispatchHTMLEvent(timeupdateEvent, false, true);
538 // As soon as the user agent has established whether or not the media data for the new playback position is available,
539 // and, if it is, decoded enough data to play back that position, the seeking DOM attribute must be set to false.
541 m_player->setEndTime(maxTime);
542 m_player->seek(time);
546 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
551 bool HTMLMediaElement::seeking() const
557 float HTMLMediaElement::currentTime() const
562 return m_currentTimeDuringSeek;
563 return m_player->currentTime();
566 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
571 float HTMLMediaElement::duration() const
573 return m_player ? m_player->duration() : 0;
576 bool HTMLMediaElement::paused() const
581 float HTMLMediaElement::defaultPlaybackRate() const
583 return m_defaultPlaybackRate;
586 void HTMLMediaElement::setDefaultPlaybackRate(float rate, ExceptionCode& ec)
589 ec = NOT_SUPPORTED_ERR;
592 if (m_defaultPlaybackRate != rate) {
593 m_defaultPlaybackRate = rate;
594 dispatchEventAsync(ratechangeEvent);
598 float HTMLMediaElement::playbackRate() const
600 return m_player ? m_player->rate() : 0;
603 void HTMLMediaElement::setPlaybackRate(float rate, ExceptionCode& ec)
606 ec = NOT_SUPPORTED_ERR;
609 if (m_player && m_player->rate() != rate) {
610 m_player->setRate(rate);
611 dispatchEventAsync(ratechangeEvent);
615 bool HTMLMediaElement::ended()
617 return endedPlayback();
620 bool HTMLMediaElement::autoplay() const
622 return hasAttribute(autoplayAttr);
625 void HTMLMediaElement::setAutoplay(bool b)
627 setBooleanAttribute(autoplayAttr, b);
630 void HTMLMediaElement::play(ExceptionCode& ec)
632 // 3.14.9.7. Playing the media resource
633 if (!m_player || networkState() == EMPTY) {
639 ExceptionCode unused;
640 if (endedPlayback()) {
642 seek(effectiveStart(), unused);
644 setPlaybackRate(defaultPlaybackRate(), unused);
648 dispatchEventAsync(playEvent);
651 m_autoplaying = false;
656 void HTMLMediaElement::pause(ExceptionCode& ec)
658 // 3.14.9.7. Playing the media resource
659 if (!m_player || networkState() == EMPTY) {
668 dispatchEventAsync(timeupdateEvent);
669 dispatchEventAsync(pauseEvent);
672 m_autoplaying = false;
677 unsigned HTMLMediaElement::playCount() const
679 String val = getAttribute(playcountAttr);
680 int count = val.toInt();
681 return max(count, 1);
684 void HTMLMediaElement::setPlayCount(unsigned count, ExceptionCode& ec)
690 setAttribute(playcountAttr, String::number(count));
694 float HTMLMediaElement::start() const
696 return getTimeOffsetAttribute(startAttr, 0);
699 void HTMLMediaElement::setStart(float time)
701 setTimeOffsetAttribute(startAttr, time);
705 float HTMLMediaElement::end() const
707 return getTimeOffsetAttribute(endAttr, std::numeric_limits<float>::infinity());
710 void HTMLMediaElement::setEnd(float time)
712 setTimeOffsetAttribute(endAttr, time);
716 float HTMLMediaElement::loopStart() const
718 return getTimeOffsetAttribute(loopstartAttr, 0);
721 void HTMLMediaElement::setLoopStart(float time)
723 setTimeOffsetAttribute(loopstartAttr, time);
727 float HTMLMediaElement::loopEnd() const
729 return getTimeOffsetAttribute(loopendAttr, std::numeric_limits<float>::infinity());
732 void HTMLMediaElement::setLoopEnd(float time)
734 setTimeOffsetAttribute(loopendAttr, time);
738 unsigned HTMLMediaElement::currentLoop() const
740 return m_currentLoop;
743 void HTMLMediaElement::setCurrentLoop(unsigned currentLoop)
745 m_currentLoop = currentLoop;
748 bool HTMLMediaElement::controls() const
750 return hasAttribute(controlsAttr);
753 void HTMLMediaElement::setControls(bool b)
755 setBooleanAttribute(controlsAttr, b);
758 float HTMLMediaElement::volume() const
763 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
765 if (vol < 0.0f || vol > 1.0f) {
770 if (m_volume != vol) {
772 dispatchEventAsync(volumechangeEvent);
775 m_player->setVolume(vol);
779 bool HTMLMediaElement::muted() const
784 void HTMLMediaElement::setMuted(bool muted)
786 if (m_muted != muted) {
788 dispatchEventAsync(volumechangeEvent);
790 m_player->setMuted(muted);
794 String HTMLMediaElement::pickMedia()
796 // 3.14.9.2. Location of the media resource
797 String mediaSrc = getAttribute(srcAttr);
798 if (mediaSrc.isEmpty()) {
799 for (Node* n = firstChild(); n; n = n->nextSibling()) {
800 if (n->hasTagName(sourceTag)) {
801 HTMLSourceElement* source = static_cast<HTMLSourceElement*>(n);
802 if (!source->hasAttribute(srcAttr))
804 if (source->hasAttribute(mediaAttr)) {
805 MediaQueryEvaluator screenEval("screen", document()->page(), renderer() ? renderer()->style() : 0);
806 RefPtr<MediaList> media = new MediaList((CSSStyleSheet*)0, source->media(), true);
807 if (!screenEval.eval(media.get()))
810 if (source->hasAttribute(typeAttr)) {
811 String type = source->type();
812 if (!MIMETypeRegistry::isSupportedMediaMIMEType(type))
815 mediaSrc = source->src();
820 if (!mediaSrc.isEmpty())
821 mediaSrc = document()->completeURL(mediaSrc);
825 void HTMLMediaElement::checkIfSeekNeeded()
827 // 3.14.9.5. Offsets into the media resource
829 if (playCount() <= m_currentLoop)
830 m_currentLoop = playCount() - 1;
833 if (networkState() <= LOADING)
838 float time = currentTime();
839 if (!m_currentLoop && time < effectiveStart())
840 seek(effectiveStart(), ec);
843 if (m_currentLoop && time < effectiveLoopStart())
844 seek(effectiveLoopStart(), ec);
847 if (m_currentLoop < playCount() - 1 && time > effectiveLoopEnd()) {
848 seek(effectiveLoopStart(), ec);
853 if (m_currentLoop == playCount() - 1 && time > effectiveEnd())
854 seek(effectiveEnd(), ec);
859 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
863 if (m_player->volume() != m_volume || m_player->muted() != m_muted) {
864 m_volume = m_player->volume();
865 m_muted = m_player->muted();
866 dispatchEventAsync(volumechangeEvent);
870 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
872 if (readyState() >= CAN_PLAY)
875 if (m_currentLoop < playCount() - 1 && currentTime() >= effectiveLoopEnd()) {
877 seek(effectiveLoopStart(), ec);
879 dispatchHTMLEvent(timeupdateEvent, false, true);
882 if (m_currentLoop == playCount() - 1 && currentTime() >= effectiveEnd()) {
883 dispatchHTMLEvent(timeupdateEvent, false, true);
884 dispatchHTMLEvent(endedEvent, false, true);
890 void HTMLMediaElement::mediaPlayerCuePointReached(MediaPlayer*, float cueTime)
892 CallbackVector* callbackVector = m_cuePoints.get(cueTime);
895 for (unsigned n = 0; n < callbackVector->size(); n++) {
896 CallbackEntry ce = (*callbackVector)[n];
904 dispatchHTMLEvent(timeupdateEvent, false, true);
906 for (unsigned n = 0; n < callbackVector->size(); n++) {
907 CallbackEntry ce = (*callbackVector)[n];
908 if (ce.m_voidCallback)
909 ce.m_voidCallback->handleEvent();
913 void HTMLMediaElement::addCuePoint(float time, VoidCallback* voidCallback, bool pause)
915 if (time < 0 || !isfinite(time))
917 CallbackVector* callbackVector = m_cuePoints.get(time);
918 if (!callbackVector) {
919 callbackVector = new CallbackVector;
920 m_cuePoints.add(time, callbackVector);
922 callbackVector->append(CallbackEntry(voidCallback, pause));
925 m_player->addCuePoint(time);
928 void HTMLMediaElement::removeCuePoint(float time, VoidCallback* callback)
930 // FIXME: This method be removed entirely. The body has been removed
931 // because it used to contain code that compared VoidCallbacks for equality
932 // and the new VoidCallback interface doesn't allow that.
935 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
937 // FIXME real ranges support
938 if (!m_player || !m_player->maxTimeBuffered())
939 return new TimeRanges;
940 return new TimeRanges(0, m_player->maxTimeBuffered());
943 PassRefPtr<TimeRanges> HTMLMediaElement::played() const
945 // FIXME track played
946 return new TimeRanges;
949 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
951 // FIXME real ranges support
952 if (!m_player || !m_player->maxTimeSeekable())
953 return new TimeRanges;
954 return new TimeRanges(0, m_player->maxTimeSeekable());
957 float HTMLMediaElement::effectiveStart() const
961 return min(start(), m_player->duration());
964 float HTMLMediaElement::effectiveEnd() const
968 return min(max(end(), max(start(), loopStart())), m_player->duration());
971 float HTMLMediaElement::effectiveLoopStart() const
975 return min(loopStart(), m_player->duration());
978 float HTMLMediaElement::effectiveLoopEnd() const
982 return min(max(start(), max(loopStart(), loopEnd())), m_player->duration());
985 bool HTMLMediaElement::activelyPlaying() const
987 return !paused() && readyState() >= CAN_PLAY && !endedPlayback(); // && !stoppedDueToErrors() && !pausedForUserInteraction();
990 bool HTMLMediaElement::endedPlayback() const
992 return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == playCount() - 1;
995 void HTMLMediaElement::updateMediaPlayer()
1000 m_player->setEndTime(currentLoop() == playCount() - 1 ? effectiveEnd() : effectiveLoopEnd());
1002 bool shouldBePlaying = activelyPlaying() && currentTime() < effectiveEnd();
1003 if (shouldBePlaying && m_player->paused())
1005 else if (!shouldBePlaying && !m_player->paused())
1009 renderer()->updateFromElement();
1012 void HTMLMediaElement::willSaveToCache()
1014 // 3.14.9.4. Loading the media resource
1018 m_player->cancelLoad();
1019 m_error = new MediaError(MediaError::MEDIA_ERR_ABORTED);
1021 initAndDispatchProgressEvent(abortEvent);
1022 if (m_networkState >= LOADING) {
1023 m_networkState = EMPTY;
1024 dispatchHTMLEvent(emptiedEvent, false, true);
1029 m_wasPlayingBeforeMovingToPageCache = !paused();
1030 if (m_wasPlayingBeforeMovingToPageCache)
1033 m_player->setVisible(false);
1036 void HTMLMediaElement::didRestoreFromCache()
1039 if (m_wasPlayingBeforeMovingToPageCache)
1042 renderer()->updateFromElement();
1045 void HTMLMediaElement::defaultEventHandler(Event* event)
1047 if (renderer() && renderer()->isMedia())
1048 static_cast<RenderMedia*>(renderer())->forwardEvent(event);
1049 HTMLElement::defaultEventHandler(event);