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"
34 #include "EventNames.h"
35 #include "ExceptionCode.h"
36 #include "HTMLDocument.h"
37 #include "HTMLNames.h"
38 #include "HTMLSourceElement.h"
39 #include "HTMLVideoElement.h"
41 #include "MediaError.h"
42 #include "MediaList.h"
43 #include "MediaQueryEvaluator.h"
44 #include "MIMETypeRegistry.h"
46 #include "RenderVideo.h"
47 #include "SystemTime.h"
48 #include "TimeRanges.h"
49 #include "VoidCallback.h"
55 using namespace EventNames;
56 using namespace HTMLNames;
58 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc)
59 : HTMLElement(tagName, doc)
60 , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
61 , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired)
62 , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
63 , m_defaultPlaybackRate(1.0f)
64 , m_networkState(EMPTY)
65 , m_readyState(DATA_UNAVAILABLE)
67 , m_loadedFirstFrame(false)
69 , m_wasPlayingBeforeMovingToPageCache(false)
73 , m_previousProgress(0)
74 , m_previousProgressTime(numeric_limits<double>::max())
75 , m_sentStalledEvent(false)
77 , m_loadNestingLevel(0)
78 , m_terminateLoadBelowNestingLevel(0)
81 document()->registerForCacheCallbacks(this);
84 HTMLMediaElement::~HTMLMediaElement()
86 document()->unregisterForCacheCallbacks(this);
88 for (HashMap<float, CallbackVector*>::iterator it = m_cuePoints.begin(); it != m_cuePoints.end(); ++it)
92 bool HTMLMediaElement::checkDTD(const Node* newChild)
94 return newChild->hasTagName(sourceTag) || HTMLElement::checkDTD(newChild);
97 void HTMLMediaElement::insertedIntoDocument()
99 HTMLElement::insertedIntoDocument();
100 if (!src().isEmpty())
104 void HTMLMediaElement::removedFromDocument()
108 HTMLElement::removedFromDocument();
111 void HTMLMediaElement::scheduleLoad()
113 m_loadTimer.startOneShot(0);
116 void HTMLMediaElement::initAndDispatchProgressEvent(const AtomicString& eventName)
118 bool totalKnown = m_movie && m_movie->totalBytesKnown();
119 unsigned loaded = m_movie ? m_movie->bytesLoaded() : 0;
120 unsigned total = m_movie ? m_movie->totalBytes() : 0;
121 dispatchProgressEvent(eventName, totalKnown, loaded, total);
124 void HTMLMediaElement::dispatchEventAsync(const AtomicString& eventName)
126 m_asyncEventsToDispatch.add(eventName);
127 if (!m_asyncEventTimer.isActive())
128 m_asyncEventTimer.startOneShot(0);
131 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
137 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
139 HashSet<String>::const_iterator end = m_asyncEventsToDispatch.end();
140 for (HashSet<String>::const_iterator it = m_asyncEventsToDispatch.begin(); it != end; ++it)
141 dispatchHTMLEvent(*it, false, true);
142 m_asyncEventsToDispatch.clear();
145 String serializeTimeOffset(float time)
147 String timeString = String::number(time);
148 // FIXME serialize time offset values properly (format not specified yet)
149 timeString.append("s");
153 float parseTimeOffset(String timeString, bool* ok = 0)
155 if (timeString.endsWith("s"))
156 timeString = timeString.left(timeString.length() - 1);
157 // FIXME parse time offset values (format not specified yet)
158 float val = (float)timeString.toDouble(ok);
162 float HTMLMediaElement::getTimeOffsetAttribute(const QualifiedName& name, float valueOnError) const
165 String timeString = getAttribute(name);
166 float result = parseTimeOffset(timeString, &ok);
172 void HTMLMediaElement::setTimeOffsetAttribute(const QualifiedName& name, float value)
174 setAttribute(name, serializeTimeOffset(value));
177 PassRefPtr<MediaError> HTMLMediaElement::error() const
182 String HTMLMediaElement::src() const
184 return document()->completeURL(getAttribute(srcAttr));
187 void HTMLMediaElement::HTMLMediaElement::setSrc(const String& url)
189 setAttribute(srcAttr, url);
192 String HTMLMediaElement::currentSrc() const
197 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
199 return m_networkState;
202 float HTMLMediaElement::bufferingRate()
206 return m_bufferingRate;
207 //return m_movie->dataRate();
210 void HTMLMediaElement::load(ExceptionCode& ec)
214 // 3.14.9.4. Loading the media resource
216 // if an event generated during load() ends up re-entering load(), terminate previous instances
217 m_loadNestingLevel++;
218 m_terminateLoadBelowNestingLevel = m_loadNestingLevel;
220 m_progressEventTimer.stop();
221 m_sentStalledEvent = false;
229 m_error = new MediaError(MediaError::MEDIA_ERR_ABORTED);
230 initAndDispatchProgressEvent(abortEvent);
231 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
237 m_loadedFirstFrame = false;
238 m_autoplaying = true;
241 setPlaybackRate(defaultPlaybackRate(), ec);
244 if (networkState() != EMPTY) {
245 m_networkState = EMPTY;
246 m_readyState = DATA_UNAVAILABLE;
252 dispatchHTMLEvent(emptiedEvent, false, true);
253 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
258 mediaSrc = pickMedia();
259 if (mediaSrc.isEmpty()) {
260 ec = INVALID_STATE_ERR;
265 m_networkState = LOADING;
268 m_currentSrc = mediaSrc;
272 dispatchProgressEvent(beginEvent, false, 0, 0); // progress event draft calls this loadstart
273 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
278 m_movie = new Movie(this);
279 m_movie->setVolume(m_volume);
280 m_movie->setMuted(m_muted);
281 for (HashMap<float, CallbackVector*>::iterator it = m_cuePoints.begin(); it != m_cuePoints.end(); ++it)
282 m_movie->addCuePoint(it->first);
283 m_movie->load(m_currentSrc);
284 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
288 renderer()->updateFromElement();
289 m_movie->setVisible(true);
293 m_previousProgressTime = WebCore::currentTime();
294 m_previousProgress = 0;
296 // 350ms is not magic, it is in the spec!
297 m_progressEventTimer.startRepeating(0.350);
299 ASSERT(m_loadNestingLevel);
300 m_loadNestingLevel--;
304 void HTMLMediaElement::movieNetworkStateChanged(Movie*)
306 if (!m_begun || m_networkState == EMPTY)
309 m_terminateLoadBelowNestingLevel = m_loadNestingLevel;
311 Movie::NetworkState state = m_movie->networkState();
313 // 3.14.9.4. Loading the media resource
315 if (state == Movie::LoadFailed) {
318 // FIXME better error handling
319 m_error = new MediaError(MediaError::MEDIA_ERR_NETWORK);
321 m_progressEventTimer.stop();
324 initAndDispatchProgressEvent(errorEvent);
325 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
328 m_networkState = EMPTY;
331 static_cast<HTMLVideoElement*>(this)->updatePosterImage();
333 dispatchHTMLEvent(emptiedEvent, false, true);
337 if (state >= Movie::Loading && m_networkState < LOADING)
338 m_networkState = LOADING;
340 if (state >= Movie::LoadedMetaData && m_networkState < LOADED_METADATA) {
341 m_movie->seek(effectiveStart());
342 m_movie->setEndTime(currentLoop() == loopCount() - 1 ? effectiveEnd() : effectiveLoopEnd());
343 m_networkState = LOADED_METADATA;
345 dispatchHTMLEvent(durationchangeEvent, false, true);
346 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
349 dispatchHTMLEvent(loadedmetadataEvent, false, true);
350 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
354 if (state >= Movie::LoadedFirstFrame && m_networkState < LOADED_FIRST_FRAME) {
355 m_networkState = LOADED_FIRST_FRAME;
357 setReadyState(CAN_SHOW_CURRENT_FRAME);
360 static_cast<HTMLVideoElement*>(this)->updatePosterImage();
362 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
365 m_loadedFirstFrame = true;
367 ASSERT(!renderer()->isImage());
368 static_cast<RenderVideo*>(renderer())->videoSizeChanged();
371 dispatchHTMLEvent(loadedfirstframeEvent, false, true);
372 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
375 dispatchHTMLEvent(canshowcurrentframeEvent, false, true);
376 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
381 if (state == Movie::Loaded && m_networkState < LOADED) {
383 m_networkState = LOADED;
384 m_progressEventTimer.stop();
386 initAndDispatchProgressEvent(loadEvent);
390 void HTMLMediaElement::movieReadyStateChanged(Movie*)
392 Movie::ReadyState state = m_movie->readyState();
393 setReadyState((ReadyState)state);
396 void HTMLMediaElement::setReadyState(ReadyState state)
398 // 3.14.9.6. The ready states
399 if (m_readyState == state)
402 bool wasActivelyPlaying = activelyPlaying();
403 m_readyState = state;
405 if (networkState() == EMPTY)
408 if (state == DATA_UNAVAILABLE) {
409 dispatchHTMLEvent(dataunavailableEvent, false, true);
410 if (wasActivelyPlaying) {
411 dispatchHTMLEvent(timeupdateEvent, false, true);
412 dispatchHTMLEvent(waitingEvent, false, true);
414 } else if (state == CAN_SHOW_CURRENT_FRAME) {
415 if (m_loadedFirstFrame)
416 dispatchHTMLEvent(canshowcurrentframeEvent, false, true);
417 if (wasActivelyPlaying) {
418 dispatchHTMLEvent(timeupdateEvent, false, true);
419 dispatchHTMLEvent(waitingEvent, false, true);
421 } else if (state == CAN_PLAY) {
422 dispatchHTMLEvent(canplayEvent, false, true);
423 } else if (state == CAN_PLAY_THROUGH) {
424 dispatchHTMLEvent(canplaythroughEvent, false, true);
425 if (m_autoplaying && paused() && autoplay()) {
427 dispatchHTMLEvent(playEvent, false, true);
432 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
435 unsigned progress = m_movie->bytesLoaded();
436 double time = WebCore::currentTime();
437 double timedelta = time - m_previousProgressTime;
439 m_bufferingRate = (float)(0.8 * m_bufferingRate + 0.2 * ((float)(progress - m_previousProgress)) / timedelta);
441 if (progress == m_previousProgress) {
442 if (timedelta > 3.0 && !m_sentStalledEvent) {
444 initAndDispatchProgressEvent(stalledEvent);
445 m_sentStalledEvent = true;
448 initAndDispatchProgressEvent(progressEvent);
449 m_previousProgress = progress;
450 m_previousProgressTime = time;
451 m_sentStalledEvent = false;
455 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
459 if (networkState() < LOADED_METADATA) {
460 ec = INVALID_STATE_ERR;
466 if (currentLoop() == 0)
467 minTime = effectiveStart();
469 minTime = effectiveLoopStart();
472 float maxTime = currentLoop() == loopCount() - 1 ? effectiveEnd() : effectiveLoopEnd();
475 time = min(time, maxTime);
478 time = max(time, minTime);
481 RefPtr<TimeRanges> seekableRanges = seekable();
482 if (!seekableRanges->contain(time)) {
490 m_movie->setEndTime(maxTime);
494 // The seeking DOM attribute is implicitly set to true
497 dispatchHTMLEvent(timeupdateEvent, false, true);
500 // As soon as the user agent has established whether or not the media data for the new playback position is available,
501 // and, if it is, decoded enough data to play back that position, the seeking DOM attribute must be set to false.
504 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
509 bool HTMLMediaElement::seeking() const
513 RefPtr<TimeRanges> seekableRanges = seekable();
514 return m_movie->seeking() && seekableRanges->contain(currentTime());
518 float HTMLMediaElement::currentTime() const
520 return m_movie ? m_movie->currentTime() : 0;
523 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
528 float HTMLMediaElement::duration() const
530 return m_movie ? m_movie->duration() : 0;
533 bool HTMLMediaElement::paused() const
535 return m_movie ? m_movie->paused() : true;
538 float HTMLMediaElement::defaultPlaybackRate() const
540 return m_defaultPlaybackRate;
543 void HTMLMediaElement::setDefaultPlaybackRate(float rate, ExceptionCode& ec)
546 ec = NOT_SUPPORTED_ERR;
549 if (m_defaultPlaybackRate != rate) {
550 m_defaultPlaybackRate = rate;
551 dispatchEventAsync(ratechangeEvent);
555 float HTMLMediaElement::playbackRate() const
557 return m_movie ? m_movie->rate() : 0;
560 void HTMLMediaElement::setPlaybackRate(float rate, ExceptionCode& ec)
563 ec = NOT_SUPPORTED_ERR;
566 if (m_movie && m_movie->rate() != rate) {
567 m_movie->setRate(rate);
568 dispatchEventAsync(ratechangeEvent);
572 bool HTMLMediaElement::ended()
574 return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == loopCount() - 1;
577 bool HTMLMediaElement::autoplay() const
579 return hasAttribute(autoplayAttr);
582 void HTMLMediaElement::setAutoplay(bool b)
584 setBooleanAttribute(autoplayAttr, b);
587 void HTMLMediaElement::play(ExceptionCode& ec)
589 // 3.14.9.7. Playing the media resource
590 if (!m_movie || networkState() == EMPTY) {
595 if (endedPlayback()) {
597 seek(effectiveStart(), ec);
601 setPlaybackRate(defaultPlaybackRate(), ec);
605 m_autoplaying = false;
607 if (m_movie->paused()) {
608 dispatchHTMLEvent(playEvent, false, true);
613 void HTMLMediaElement::pause(ExceptionCode& ec)
615 // 3.14.9.7. Playing the media resource
616 if (!m_movie || networkState() == EMPTY) {
620 m_autoplaying = false;
622 if (!m_movie->paused()) {
623 dispatchHTMLEvent(pauseEvent, false, true);
628 unsigned HTMLMediaElement::loopCount() const
630 String val = getAttribute(loopcountAttr);
631 int count = val.toInt();
632 return max(count, 1);
635 void HTMLMediaElement::setLoopCount(unsigned count, ExceptionCode& ec)
641 setAttribute(loopcountAttr, String::number(count));
645 float HTMLMediaElement::start() const
647 return getTimeOffsetAttribute(startAttr, 0);
650 void HTMLMediaElement::setStart(float time)
652 setTimeOffsetAttribute(startAttr, time);
656 float HTMLMediaElement::end() const
658 return getTimeOffsetAttribute(endAttr, std::numeric_limits<float>::infinity());
661 void HTMLMediaElement::setEnd(float time)
663 setTimeOffsetAttribute(endAttr, time);
667 float HTMLMediaElement::loopStart() const
669 return getTimeOffsetAttribute(loopstartAttr, 0);
672 void HTMLMediaElement::setLoopStart(float time)
674 setTimeOffsetAttribute(loopstartAttr, time);
678 float HTMLMediaElement::loopEnd() const
680 return getTimeOffsetAttribute(loopendAttr, std::numeric_limits<float>::infinity());
683 void HTMLMediaElement::setLoopEnd(float time)
685 setTimeOffsetAttribute(loopendAttr, time);
689 unsigned HTMLMediaElement::currentLoop() const
691 return m_currentLoop;
694 void HTMLMediaElement::setCurrentLoop(unsigned currentLoop)
696 m_currentLoop = currentLoop;
699 bool HTMLMediaElement::controls() const
701 return hasAttribute(controlsAttr);
704 void HTMLMediaElement::setControls(bool b)
706 setBooleanAttribute(controlsAttr, b);
709 float HTMLMediaElement::volume() const
714 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
716 if (vol < 0.0f || vol > 1.0f) {
721 if (m_volume != vol) {
723 dispatchEventAsync(volumechangeEvent);
726 m_movie->setVolume(vol);
730 bool HTMLMediaElement::muted() const
735 void HTMLMediaElement::setMuted(bool muted)
737 if (m_muted != muted) {
739 dispatchEventAsync(volumechangeEvent);
741 m_movie->setMuted(muted);
745 String HTMLMediaElement::pickMedia()
747 // 3.14.9.2. Location of the media resource
748 String mediaSrc = getAttribute(srcAttr);
749 if (mediaSrc.isEmpty()) {
750 for (Node* n = firstChild(); n; n = n->nextSibling()) {
751 if (n->hasTagName(sourceTag)) {
752 HTMLSourceElement* source = static_cast<HTMLSourceElement*>(n);
753 if (!source->hasAttribute(srcAttr))
755 if (source->hasAttribute(mediaAttr)) {
756 MediaQueryEvaluator screenEval("screen", document()->page(), renderer() ? renderer()->style() : 0);
757 RefPtr<MediaList> media = new MediaList((CSSStyleSheet*)0, source->media(), true);
758 if (!screenEval.eval(media.get()))
761 if (source->hasAttribute(typeAttr)) {
762 String type = source->type();
763 if (!MIMETypeRegistry::isSupportedMovieMIMEType(type))
766 mediaSrc = source->src();
771 if (!mediaSrc.isEmpty())
772 mediaSrc = document()->completeURL(mediaSrc);
776 void HTMLMediaElement::checkIfSeekNeeded()
778 // 3.14.9.5. Offsets into the media resource
780 if (loopCount() - 1 < m_currentLoop)
781 m_currentLoop = loopCount() - 1;
784 if (networkState() <= LOADING)
789 float time = currentTime();
790 if (!m_currentLoop && time < effectiveStart())
791 seek(effectiveStart(), ec);
794 if (m_currentLoop && time < effectiveLoopStart())
795 seek(effectiveLoopStart(), ec);
798 if (m_currentLoop < loopCount() - 1 && time > effectiveLoopEnd()) {
799 seek(effectiveLoopStart(), ec);
804 if (m_currentLoop == loopCount() - 1 && time > effectiveEnd())
805 seek(effectiveEnd(), ec);
808 void HTMLMediaElement::movieVolumeChanged(Movie*)
812 if (m_movie->volume() != m_volume || m_movie->muted() != m_muted) {
813 m_volume = m_movie->volume();
814 m_muted = m_movie->muted();
815 dispatchEventAsync(volumechangeEvent);
819 void HTMLMediaElement::movieDidEnd(Movie*)
821 if (m_currentLoop < loopCount() - 1 && currentTime() >= effectiveLoopEnd()) {
822 m_movie->seek(effectiveLoopStart());
824 m_movie->setEndTime(m_currentLoop == loopCount() - 1 ? effectiveEnd() : effectiveLoopEnd());
827 dispatchHTMLEvent(timeupdateEvent, false, true);
830 if (m_currentLoop == loopCount() - 1 && currentTime() >= effectiveEnd()) {
831 dispatchHTMLEvent(timeupdateEvent, false, true);
832 dispatchHTMLEvent(endedEvent, false, true);
836 void HTMLMediaElement::movieCuePointReached(Movie*, float cueTime)
838 CallbackVector* callbackVector = m_cuePoints.get(cueTime);
841 for (unsigned n = 0; n < callbackVector->size(); n++) {
842 CallbackEntry ce = (*callbackVector)[n];
850 dispatchHTMLEvent(timeupdateEvent, false, true);
852 for (unsigned n = 0; n < callbackVector->size(); n++) {
853 CallbackEntry ce = (*callbackVector)[n];
854 if (ce.m_voidCallback)
855 ce.m_voidCallback->execute(document()->frame());
859 void HTMLMediaElement::addCuePoint(float time, VoidCallback* voidCallback, bool pause)
861 if (time < 0 || !isfinite(time))
863 CallbackVector* callbackVector = m_cuePoints.get(time);
864 if (!callbackVector) {
865 callbackVector = new CallbackVector;
866 m_cuePoints.add(time, callbackVector);
868 callbackVector->append(CallbackEntry(voidCallback, pause));
871 m_movie->addCuePoint(time);
874 void HTMLMediaElement::removeCuePoint(float time, VoidCallback* callback)
876 if (time < 0 || !isfinite(time))
878 CallbackVector* callbackVector = m_cuePoints.get(time);
879 if (callbackVector) {
880 for (unsigned n = 0; n < callbackVector->size(); n++) {
881 if (*(*callbackVector)[n].m_voidCallback == *callback) {
882 callbackVector->remove(n);
886 if (!callbackVector->size()) {
887 delete callbackVector;
888 m_cuePoints.remove(time);
890 m_movie->removeCuePoint(time);
895 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
897 // FIXME real ranges support
898 if (!m_movie || !m_movie->maxTimeBuffered())
899 return new TimeRanges;
900 return new TimeRanges(0, m_movie->maxTimeBuffered());
903 PassRefPtr<TimeRanges> HTMLMediaElement::played() const
905 // FIXME track played
906 return new TimeRanges;
909 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
911 // FIXME real ranges support
912 if (!m_movie || !m_movie->maxTimeSeekable())
913 return new TimeRanges;
914 return new TimeRanges(0, m_movie->maxTimeSeekable());
917 float HTMLMediaElement::effectiveStart() const
921 return min(start(), m_movie->duration());
924 float HTMLMediaElement::effectiveEnd() const
928 return min(max(end(), max(start(), loopStart())), m_movie->duration());
931 float HTMLMediaElement::effectiveLoopStart() const
935 return min(loopStart(), m_movie->duration());
938 float HTMLMediaElement::effectiveLoopEnd() const
942 return min(max(start(), max(loopStart(), loopEnd())), m_movie->duration());
945 bool HTMLMediaElement::activelyPlaying() const
947 return !paused() && readyState() >= CAN_PLAY && !endedPlayback(); // && !stoppedDueToErrors() && !pausedForUserInteraction();
950 bool HTMLMediaElement::endedPlayback() const
952 return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == loopCount() - 1;
955 void HTMLMediaElement::willSaveToCache()
957 // 3.14.9.4. Loading the media resource
961 m_movie->cancelLoad();
962 m_error = new MediaError(MediaError::MEDIA_ERR_ABORTED);
964 initAndDispatchProgressEvent(abortEvent);
965 if (m_networkState >= LOADING) {
966 m_networkState = EMPTY;
967 dispatchHTMLEvent(emptiedEvent, false, true);
972 m_wasPlayingBeforeMovingToPageCache = !paused();
973 if (m_wasPlayingBeforeMovingToPageCache)
976 m_movie->setVisible(false);
979 void HTMLMediaElement::didRestoreFromCache()
982 if (m_wasPlayingBeforeMovingToPageCache)
985 m_movie->setVisible(true);