• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007, 2008, 2009 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 "ContentType.h"
35 #include "DocLoader.h"
36 #include "Event.h"
37 #include "EventNames.h"
38 #include "ExceptionCode.h"
39 #include "Frame.h"
40 #include "FrameLoader.h"
41 #include "HTMLDocument.h"
42 #include "HTMLNames.h"
43 #include "HTMLSourceElement.h"
44 #include "HTMLVideoElement.h"
45 #include "MIMETypeRegistry.h"
46 #include "MappedAttribute.h"
47 #include "MediaDocument.h"
48 #include "MediaError.h"
49 #include "MediaList.h"
50 #include "MediaPlayer.h"
51 #include "MediaQueryEvaluator.h"
52 #include "Page.h"
53 #include "ProgressEvent.h"
54 #include "RenderVideo.h"
55 #include "ScriptEventListener.h"
56 #include "TimeRanges.h"
57 #include <limits>
58 #include <wtf/CurrentTime.h>
59 #include <wtf/MathExtras.h>
60 
61 #if USE(ACCELERATED_COMPOSITING)
62 #include "RenderView.h"
63 #include "RenderLayerCompositor.h"
64 #endif
65 
66 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
67 #include "RenderPartObject.h"
68 #include "Widget.h"
69 #endif
70 
71 using namespace std;
72 
73 namespace WebCore {
74 
75 using namespace HTMLNames;
76 
HTMLMediaElement(const QualifiedName & tagName,Document * doc)77 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc)
78     : HTMLElement(tagName, doc)
79     , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
80     , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired)
81     , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
82     , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
83     , m_playedTimeRanges()
84     , m_playbackRate(1.0f)
85     , m_defaultPlaybackRate(1.0f)
86     , m_webkitPreservesPitch(true)
87     , m_networkState(NETWORK_EMPTY)
88     , m_readyState(HAVE_NOTHING)
89     , m_volume(1.0f)
90     , m_lastSeekTime(0)
91     , m_previousProgress(0)
92     , m_previousProgressTime(numeric_limits<double>::max())
93     , m_lastTimeUpdateEventWallTime(0)
94     , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max())
95     , m_loadState(WaitingForSource)
96     , m_currentSourceNode(0)
97     , m_player(0)
98     , m_restrictions(NoRestrictions)
99     , m_playing(false)
100     , m_processingMediaPlayerCallback(0)
101     , m_processingLoad(false)
102     , m_delayingTheLoadEvent(false)
103     , m_haveFiredLoadedData(false)
104     , m_inActiveDocument(true)
105     , m_autoplaying(true)
106     , m_muted(false)
107     , m_paused(true)
108     , m_seeking(false)
109     , m_sentStalledEvent(false)
110     , m_sentEndEvent(false)
111     , m_pausedInternal(false)
112     , m_sendProgressEvents(true)
113 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
114     , m_needWidgetUpdate(false)
115 #endif
116 {
117     document()->registerForDocumentActivationCallbacks(this);
118     document()->registerForMediaVolumeCallbacks(this);
119 }
120 
~HTMLMediaElement()121 HTMLMediaElement::~HTMLMediaElement()
122 {
123     document()->unregisterForDocumentActivationCallbacks(this);
124     document()->unregisterForMediaVolumeCallbacks(this);
125 }
126 
checkDTD(const Node * newChild)127 bool HTMLMediaElement::checkDTD(const Node* newChild)
128 {
129     return newChild->hasTagName(sourceTag) || HTMLElement::checkDTD(newChild);
130 }
131 
attributeChanged(Attribute * attr,bool preserveDecls)132 void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls)
133 {
134     HTMLElement::attributeChanged(attr, preserveDecls);
135 
136     const QualifiedName& attrName = attr->name();
137     if (attrName == srcAttr) {
138         // don't have a src or any <source> children, trigger load
139         if (inDocument() && m_loadState == WaitingForSource)
140             scheduleLoad();
141     }
142 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
143     else if (attrName == controlsAttr) {
144         if (!isVideo() && attached() && (controls() != (renderer() != 0))) {
145             detach();
146             attach();
147         }
148         if (renderer())
149             renderer()->updateFromElement();
150     }
151 #endif
152 }
153 
parseMappedAttribute(MappedAttribute * attr)154 void HTMLMediaElement::parseMappedAttribute(MappedAttribute* attr)
155 {
156     const QualifiedName& attrName = attr->name();
157 
158     if (attrName == autobufferAttr) {
159         if (m_player)
160             m_player->setAutobuffer(!attr->isNull());
161     } else if (attrName == onabortAttr)
162         setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr));
163     else if (attrName == oncanplayAttr)
164         setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, attr));
165     else if (attrName == oncanplaythroughAttr)
166         setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, attr));
167     else if (attrName == ondurationchangeAttr)
168         setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, attr));
169     else if (attrName == onemptiedAttr)
170         setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, attr));
171     else if (attrName == onendedAttr)
172         setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attr));
173     else if (attrName == onerrorAttr)
174         setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr));
175     else if (attrName == onloadAttr)
176         setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr));
177     else if (attrName == onloadeddataAttr)
178         setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr));
179     else if (attrName == onloadedmetadataAttr)
180         setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attr));
181     else if (attrName == onloadstartAttr)
182         setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attr));
183     else if (attrName == onpauseAttr)
184         setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, attr));
185     else if (attrName == onplayAttr)
186         setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, attr));
187     else if (attrName == onplayingAttr)
188         setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, attr));
189     else if (attrName == onprogressAttr)
190         setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, attr));
191     else if (attrName == onratechangeAttr)
192         setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, attr));
193     else if (attrName == onseekedAttr)
194         setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, attr));
195     else if (attrName == onseekingAttr)
196         setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, attr));
197     else if (attrName == onstalledAttr)
198         setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, attr));
199     else if (attrName == onsuspendAttr)
200         setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, attr));
201     else if (attrName == ontimeupdateAttr)
202         setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, attr));
203     else if (attrName == onvolumechangeAttr)
204         setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, attr));
205     else if (attrName == onwaitingAttr)
206         setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, attr));
207     else
208         HTMLElement::parseMappedAttribute(attr);
209 }
210 
rendererIsNeeded(RenderStyle * style)211 bool HTMLMediaElement::rendererIsNeeded(RenderStyle* style)
212 {
213 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
214     UNUSED_PARAM(style);
215     Frame* frame = document()->frame();
216     if (!frame)
217         return false;
218 
219     return true;
220 #else
221     return controls() ? HTMLElement::rendererIsNeeded(style) : false;
222 #endif
223 }
224 
createRenderer(RenderArena * arena,RenderStyle *)225 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
226 {
227 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
228     return new (arena) RenderPartObject(this);
229 #else
230     return new (arena) RenderMedia(this);
231 #endif
232 }
233 
insertedIntoDocument()234 void HTMLMediaElement::insertedIntoDocument()
235 {
236     HTMLElement::insertedIntoDocument();
237     if (!src().isEmpty() && m_networkState == NETWORK_EMPTY)
238         scheduleLoad();
239 }
240 
removedFromDocument()241 void HTMLMediaElement::removedFromDocument()
242 {
243     if (m_networkState > NETWORK_EMPTY)
244         pause();
245     HTMLElement::removedFromDocument();
246 }
247 
attach()248 void HTMLMediaElement::attach()
249 {
250     ASSERT(!attached());
251 
252 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
253     m_needWidgetUpdate = true;
254 #endif
255 
256     HTMLElement::attach();
257 
258     if (renderer())
259         renderer()->updateFromElement();
260 }
261 
recalcStyle(StyleChange change)262 void HTMLMediaElement::recalcStyle(StyleChange change)
263 {
264     HTMLElement::recalcStyle(change);
265 
266     if (renderer())
267         renderer()->updateFromElement();
268 }
269 
scheduleLoad()270 void HTMLMediaElement::scheduleLoad()
271 {
272     m_loadTimer.startOneShot(0);
273 }
274 
scheduleProgressEvent(const AtomicString & eventName)275 void HTMLMediaElement::scheduleProgressEvent(const AtomicString& eventName)
276 {
277     if (!m_sendProgressEvents)
278         return;
279 
280     // FIXME: don't schedule timeupdate or progress events unless there are registered listeners
281 
282     bool totalKnown = m_player && m_player->totalBytesKnown();
283     unsigned loaded = m_player ? m_player->bytesLoaded() : 0;
284     unsigned total = m_player ? m_player->totalBytes() : 0;
285 
286     RefPtr<ProgressEvent> evt = ProgressEvent::create(eventName, totalKnown, loaded, total);
287     enqueueEvent(evt);
288 
289     if (renderer())
290         renderer()->updateFromElement();
291 }
292 
scheduleEvent(const AtomicString & eventName)293 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
294 {
295     enqueueEvent(Event::create(eventName, false, true));
296 }
297 
enqueueEvent(RefPtr<Event> event)298 void HTMLMediaElement::enqueueEvent(RefPtr<Event> event)
299 {
300     m_pendingEvents.append(event);
301     if (!m_asyncEventTimer.isActive())
302         m_asyncEventTimer.startOneShot(0);
303 }
304 
asyncEventTimerFired(Timer<HTMLMediaElement> *)305 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
306 {
307     Vector<RefPtr<Event> > pendingEvents;
308     ExceptionCode ec = 0;
309 
310     m_pendingEvents.swap(pendingEvents);
311     unsigned count = pendingEvents.size();
312     for (unsigned ndx = 0; ndx < count; ++ndx)
313         dispatchEvent(pendingEvents[ndx].release(), ec);
314 }
315 
loadTimerFired(Timer<HTMLMediaElement> *)316 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
317 {
318     if (m_loadState == LoadingFromSourceElement)
319         loadNextSourceChild();
320     else
321         loadInternal();
322 }
323 
serializeTimeOffset(float time)324 static String serializeTimeOffset(float time)
325 {
326     String timeString = String::number(time);
327     // FIXME serialize time offset values properly (format not specified yet)
328     timeString.append("s");
329     return timeString;
330 }
331 
parseTimeOffset(const String & timeString,bool * ok=0)332 static float parseTimeOffset(const String& timeString, bool* ok = 0)
333 {
334     const UChar* characters = timeString.characters();
335     unsigned length = timeString.length();
336 
337     if (length && characters[length - 1] == 's')
338         length--;
339 
340     // FIXME parse time offset values (format not specified yet)
341     float val = charactersToFloat(characters, length, ok);
342     return val;
343 }
344 
getTimeOffsetAttribute(const QualifiedName & name,float valueOnError) const345 float HTMLMediaElement::getTimeOffsetAttribute(const QualifiedName& name, float valueOnError) const
346 {
347     bool ok;
348     String timeString = getAttribute(name);
349     float result = parseTimeOffset(timeString, &ok);
350     if (ok)
351         return result;
352     return valueOnError;
353 }
354 
setTimeOffsetAttribute(const QualifiedName & name,float value)355 void HTMLMediaElement::setTimeOffsetAttribute(const QualifiedName& name, float value)
356 {
357     setAttribute(name, serializeTimeOffset(value));
358 }
359 
error() const360 PassRefPtr<MediaError> HTMLMediaElement::error() const
361 {
362     return m_error;
363 }
364 
src() const365 KURL HTMLMediaElement::src() const
366 {
367     return document()->completeURL(getAttribute(srcAttr));
368 }
369 
setSrc(const String & url)370 void HTMLMediaElement::setSrc(const String& url)
371 {
372     setAttribute(srcAttr, url);
373 }
374 
currentSrc() const375 String HTMLMediaElement::currentSrc() const
376 {
377     return m_currentSrc;
378 }
379 
networkState() const380 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
381 {
382     return m_networkState;
383 }
384 
canPlayType(const String & mimeType) const385 String HTMLMediaElement::canPlayType(const String& mimeType) const
386 {
387     MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType));
388     String canPlay;
389 
390     // 4.8.10.3
391     switch (support)
392     {
393         case MediaPlayer::IsNotSupported:
394             canPlay = "";
395             break;
396         case MediaPlayer::MayBeSupported:
397             canPlay = "maybe";
398             break;
399         case MediaPlayer::IsSupported:
400             canPlay = "probably";
401             break;
402     }
403 
404     return canPlay;
405 }
406 
load(ExceptionCode & ec)407 void HTMLMediaElement::load(ExceptionCode& ec)
408 {
409     if (m_restrictions & RequireUserGestureForLoadRestriction && !processingUserGesture())
410         ec = INVALID_STATE_ERR;
411     else
412         loadInternal();
413 }
414 
loadInternal()415 void HTMLMediaElement::loadInternal()
416 {
417     // 1 - If the load() method for this element is already being invoked, then abort these steps.
418     if (m_processingLoad)
419         return;
420     m_processingLoad = true;
421 
422     stopPeriodicTimers();
423     m_loadTimer.stop();
424     m_sentStalledEvent = false;
425     m_haveFiredLoadedData = false;
426 
427     // 2 - Abort any already-running instance of the resource selection algorithm for this element.
428     m_currentSourceNode = 0;
429 
430     // 3 - If there are any tasks from the media element's media element event task source in
431     // one of the task queues, then remove those tasks.
432     cancelPendingEventsAndCallbacks();
433 
434     // 4 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, set the
435     // error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED,
436     // and fire a progress event called abort at the media element.
437     if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE) {
438         m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
439 
440         // fire synchronous 'abort'
441         bool totalKnown = m_player && m_player->totalBytesKnown();
442         unsigned loaded = m_player ? m_player->bytesLoaded() : 0;
443         unsigned total = m_player ? m_player->totalBytes() : 0;
444         dispatchProgressEvent(eventNames().abortEvent, totalKnown, loaded, total);
445     }
446 
447     // 5
448     m_error = 0;
449     m_autoplaying = true;
450     m_playedTimeRanges = TimeRanges::create();
451     m_lastSeekTime = 0;
452 
453     // 6
454     setPlaybackRate(defaultPlaybackRate());
455 
456     // 7
457     if (m_networkState != NETWORK_EMPTY) {
458         m_networkState = NETWORK_EMPTY;
459         m_readyState = HAVE_NOTHING;
460         m_paused = true;
461         m_seeking = false;
462         if (m_player) {
463             m_player->pause();
464             m_playing = false;
465             m_player->seek(0);
466         }
467         dispatchEvent(eventNames().emptiedEvent, false, true);
468     }
469 
470     selectMediaResource();
471     m_processingLoad = false;
472 }
473 
selectMediaResource()474 void HTMLMediaElement::selectMediaResource()
475 {
476     // 1 - If the media element has neither a src attribute nor any source element children, run these substeps
477     String mediaSrc = getAttribute(srcAttr);
478     if (!mediaSrc && !havePotentialSourceChild()) {
479         m_loadState = WaitingForSource;
480 
481         // 1 -  Set the networkState to NETWORK_NO_SOURCE
482         m_networkState = NETWORK_NO_SOURCE;
483 
484         // 2 - While the media element has neither a src attribute nor any source element children,
485         // wait. (This steps might wait forever.)
486 
487         m_delayingTheLoadEvent = false;
488         return;
489     }
490 
491     // 2
492     m_delayingTheLoadEvent = true;
493 
494     // 3
495     m_networkState = NETWORK_LOADING;
496 
497     // 4
498     scheduleProgressEvent(eventNames().loadstartEvent);
499 
500     // 5 - If the media element has a src attribute, then run these substeps
501     ContentType contentType("");
502     if (!mediaSrc.isEmpty()) {
503         KURL mediaURL = document()->completeURL(mediaSrc);
504         if (isSafeToLoadURL(mediaURL, Complain)) {
505             m_loadState = LoadingFromSrcAttr;
506             loadResource(mediaURL, contentType);
507         } else
508             noneSupported();
509 
510         return;
511     }
512 
513     // Otherwise, the source elements will be used
514     m_currentSourceNode = 0;
515     loadNextSourceChild();
516 }
517 
loadNextSourceChild()518 void HTMLMediaElement::loadNextSourceChild()
519 {
520     ContentType contentType("");
521     KURL mediaURL = selectNextSourceChild(&contentType, Complain);
522     if (!mediaURL.isValid()) {
523         // It seems wrong to fail silently when we give up because no suitable <source>
524         // element can be found and set the error attribute if the element's 'src' attribute
525         // fails, but that is what the spec says.
526         return;
527     }
528 
529     m_loadState = LoadingFromSourceElement;
530     loadResource(mediaURL, contentType);
531 }
532 
loadResource(const KURL & url,ContentType & contentType)533 void HTMLMediaElement::loadResource(const KURL& url, ContentType& contentType)
534 {
535     ASSERT(isSafeToLoadURL(url, Complain));
536 
537     // The resource fetch algorithm
538     m_networkState = NETWORK_LOADING;
539 
540     m_currentSrc = url;
541 
542     if (m_sendProgressEvents)
543         startProgressEventTimer();
544 
545 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
546     m_player.clear();
547     m_player.set(new MediaPlayer(this));
548 #else
549     if (!m_player)
550         m_player.set(new MediaPlayer(this));
551 #endif
552 
553     m_player->setPreservesPitch(m_webkitPreservesPitch);
554     updateVolume();
555 
556     m_player->load(m_currentSrc, contentType);
557 
558 #if PLATFORM(ANDROID)
559     if (isVideo() && m_player->canLoadPoster()) {
560         KURL posterUrl = static_cast<HTMLVideoElement*>(this)->poster();
561         if (!posterUrl.isEmpty())
562             m_player->setPoster(posterUrl);
563     }
564 #endif
565 
566     if (renderer())
567         renderer()->updateFromElement();
568 }
569 
isSafeToLoadURL(const KURL & url,InvalidSourceAction actionIfInvalid)570 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidSourceAction actionIfInvalid)
571 {
572     Frame* frame = document()->frame();
573     FrameLoader* loader = frame ? frame->loader() : 0;
574 
575     // don't allow remote to local urls
576     if (!loader || !loader->canLoad(url, String(), document())) {
577         if (actionIfInvalid == Complain)
578             FrameLoader::reportLocalLoadFailed(frame, url.string());
579         return false;
580     }
581 
582     return true;
583 }
584 
startProgressEventTimer()585 void HTMLMediaElement::startProgressEventTimer()
586 {
587     if (m_progressEventTimer.isActive())
588         return;
589 
590     m_previousProgressTime = WTF::currentTime();
591     m_previousProgress = 0;
592     // 350ms is not magic, it is in the spec!
593     m_progressEventTimer.startRepeating(0.350);
594 }
595 
noneSupported()596 void HTMLMediaElement::noneSupported()
597 {
598     stopPeriodicTimers();
599     m_loadState = WaitingForSource;
600     m_currentSourceNode = 0;
601 
602     // 3 - Reaching this step indicates that either the URL failed to resolve, or the media
603     // resource failed to load. Set the error attribute to a new MediaError object whose
604     // code attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED.
605     m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
606 
607     // 4- Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
608     m_networkState = NETWORK_NO_SOURCE;
609 
610     // 5 - Queue a task to fire a progress event called error at the media element.
611     scheduleProgressEvent(eventNames().errorEvent);
612 
613     // 6 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
614     m_delayingTheLoadEvent = false;
615 
616     // Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource.
617 
618     if (isVideo())
619         static_cast<HTMLVideoElement*>(this)->updatePosterImage();
620     if (renderer())
621         renderer()->updateFromElement();
622 }
623 
mediaEngineError(PassRefPtr<MediaError> err)624 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
625 {
626     // 1 - The user agent should cancel the fetching process.
627     stopPeriodicTimers();
628     m_loadState = WaitingForSource;
629 
630     // 2 - Set the error attribute to a new MediaError object whose code attribute is
631     // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
632     m_error = err;
633 
634     // 3 - Queue a task to fire a progress event called error at the media element.
635     scheduleProgressEvent(eventNames().errorEvent);
636 
637     // 3 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
638     // task to fire a simple event called emptied at the element.
639     m_networkState = NETWORK_EMPTY;
640     scheduleEvent(eventNames().emptiedEvent);
641 
642     // 4 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
643     m_delayingTheLoadEvent = false;
644 
645     // 5 - Abort the overall resource selection algorithm.
646     m_currentSourceNode = 0;
647 
648 }
649 
cancelPendingEventsAndCallbacks()650 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
651 {
652     m_pendingEvents.clear();
653 
654     for (Node* node = firstChild(); node; node = node->nextSibling()) {
655         if (node->hasTagName(sourceTag))
656             static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent();
657     }
658 }
659 
mediaPlayerNetworkStateChanged(MediaPlayer *)660 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
661 {
662     beginProcessingMediaPlayerCallback();
663     setNetworkState(m_player->networkState());
664     endProcessingMediaPlayerCallback();
665 }
666 
setNetworkState(MediaPlayer::NetworkState state)667 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
668 {
669     if (state == MediaPlayer::Empty) {
670         // just update the cached state and leave, we can't do anything
671         m_networkState = NETWORK_EMPTY;
672         return;
673     }
674 
675     if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
676         stopPeriodicTimers();
677 
678         // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
679         // <source> children, schedule the next one
680         if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
681             m_currentSourceNode->scheduleErrorEvent();
682             if (havePotentialSourceChild())
683                 scheduleLoad();
684             return;
685         }
686 
687         if (state == MediaPlayer::NetworkError)
688             mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
689         else if (state == MediaPlayer::DecodeError)
690             mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
691         else if (state == MediaPlayer::FormatError && m_loadState == LoadingFromSrcAttr)
692             noneSupported();
693 
694         if (isVideo())
695             static_cast<HTMLVideoElement*>(this)->updatePosterImage();
696 
697         return;
698     }
699 
700     if (state == MediaPlayer::Idle) {
701         if (m_networkState > NETWORK_IDLE) {
702             stopPeriodicTimers();
703             scheduleProgressEvent(eventNames().suspendEvent);
704         }
705         m_networkState = NETWORK_IDLE;
706     }
707 
708     if (state == MediaPlayer::Loading) {
709         if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
710             startProgressEventTimer();
711         m_networkState = NETWORK_LOADING;
712     }
713 
714     if (state == MediaPlayer::Loaded) {
715         NetworkState oldState = m_networkState;
716 
717         m_networkState = NETWORK_LOADED;
718         if (oldState < NETWORK_LOADED || oldState == NETWORK_NO_SOURCE) {
719             m_progressEventTimer.stop();
720 
721             // Check to see if readyState changes need to be dealt with before sending the
722             // 'load' event so we report 'canplaythrough' first. This is necessary because a
723             //  media engine reports readyState and networkState changes separately
724             MediaPlayer::ReadyState currentState = m_player->readyState();
725             if (static_cast<ReadyState>(currentState) != m_readyState)
726                 setReadyState(currentState);
727 
728              scheduleProgressEvent(eventNames().loadEvent);
729         }
730     }
731 }
732 
mediaPlayerReadyStateChanged(MediaPlayer *)733 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
734 {
735     beginProcessingMediaPlayerCallback();
736 
737     setReadyState(m_player->readyState());
738 
739     endProcessingMediaPlayerCallback();
740 }
741 
setReadyState(MediaPlayer::ReadyState state)742 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
743 {
744     // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
745     bool wasPotentiallyPlaying = potentiallyPlaying();
746 
747     ReadyState oldState = m_readyState;
748     m_readyState = static_cast<ReadyState>(state);
749 
750     if (m_readyState == oldState)
751         return;
752 
753     if (m_readyState >= HAVE_CURRENT_DATA)
754         m_seeking = false;
755 
756     if (m_networkState == NETWORK_EMPTY)
757         return;
758 
759     if (m_seeking && m_readyState < HAVE_CURRENT_DATA) {
760         // 4.8.10.10, step 9
761         scheduleEvent(eventNames().seekingEvent);
762     }
763 
764     if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
765         // 4.8.10.9
766         scheduleTimeupdateEvent(false);
767         scheduleEvent(eventNames().waitingEvent);
768     }
769 
770     if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
771         scheduleEvent(eventNames().durationchangeEvent);
772         scheduleEvent(eventNames().loadedmetadataEvent);
773 
774 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
775         if (renderer() && renderer()->isVideo()) {
776             toRenderVideo(renderer())->videoSizeChanged();
777         }
778 #endif
779         m_delayingTheLoadEvent = false;
780         m_player->seek(0);
781     }
782 
783     // 4.8.10.7 says loadeddata is sent only when the new state *is* HAVE_CURRENT_DATA: "If the
784     // previous ready state was HAVE_METADATA and the new ready state is HAVE_CURRENT_DATA",
785     // but the event table at the end of the spec says it is sent when: "readyState newly
786     // increased to HAVE_CURRENT_DATA  or greater for the first time"
787     // We go with the later because it seems useful to count on getting this event
788     if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
789         m_haveFiredLoadedData = true;
790         scheduleEvent(eventNames().loadeddataEvent);
791     }
792 
793     bool isPotentiallyPlaying = potentiallyPlaying();
794     if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) {
795         scheduleEvent(eventNames().canplayEvent);
796         if (isPotentiallyPlaying)
797             scheduleEvent(eventNames().playingEvent);
798 
799         if (isVideo())
800             static_cast<HTMLVideoElement*>(this)->updatePosterImage();
801     }
802 
803     if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) {
804         if (oldState <= HAVE_CURRENT_DATA)
805             scheduleEvent(eventNames().canplayEvent);
806 
807         scheduleEvent(eventNames().canplaythroughEvent);
808 
809         if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
810             scheduleEvent(eventNames().playingEvent);
811 
812         if (m_autoplaying && m_paused && autoplay()) {
813             m_paused = false;
814             scheduleEvent(eventNames().playEvent);
815             scheduleEvent(eventNames().playingEvent);
816         }
817 
818         if (isVideo())
819             static_cast<HTMLVideoElement*>(this)->updatePosterImage();
820     }
821 
822     updatePlayState();
823 }
824 
progressEventTimerFired(Timer<HTMLMediaElement> *)825 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
826 {
827     ASSERT(m_player);
828     if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED)
829         return;
830 
831     unsigned progress = m_player->bytesLoaded();
832     double time = WTF::currentTime();
833     double timedelta = time - m_previousProgressTime;
834 
835     if (progress == m_previousProgress) {
836         if (timedelta > 3.0 && !m_sentStalledEvent) {
837             scheduleProgressEvent(eventNames().stalledEvent);
838             m_sentStalledEvent = true;
839         }
840     } else {
841         scheduleProgressEvent(eventNames().progressEvent);
842         m_previousProgress = progress;
843         m_previousProgressTime = time;
844         m_sentStalledEvent = false;
845     }
846 }
847 
rewind(float timeDelta)848 void HTMLMediaElement::rewind(float timeDelta)
849 {
850     ExceptionCode e;
851     setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e);
852 }
853 
returnToRealtime()854 void HTMLMediaElement::returnToRealtime()
855 {
856     ExceptionCode e;
857     setCurrentTime(maxTimeSeekable(), e);
858 }
859 
supportsSave() const860 bool HTMLMediaElement::supportsSave() const
861 {
862     return m_player ? m_player->supportsSave() : false;
863 }
864 
seek(float time,ExceptionCode & ec)865 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
866 {
867     // 4.8.10.10. Seeking
868     // 1
869     if (m_readyState == HAVE_NOTHING || !m_player) {
870         ec = INVALID_STATE_ERR;
871         return;
872     }
873 
874     // 2
875     time = min(time, duration());
876 
877     // 3
878     time = max(time, 0.0f);
879 
880     // 4
881     RefPtr<TimeRanges> seekableRanges = seekable();
882     if (!seekableRanges->contain(time)) {
883         ec = INDEX_SIZE_ERR;
884         return;
885     }
886 
887     // avoid generating events when the time won't actually change
888     float now = currentTime();
889     if (time == now)
890         return;
891 
892     // 5
893     if (m_playing) {
894         if (m_lastSeekTime < now)
895             m_playedTimeRanges->add(m_lastSeekTime, now);
896     }
897     m_lastSeekTime = time;
898 
899     // 6 - set the seeking flag, it will be cleared when the engine tells is the time has actually changed
900     m_seeking = true;
901 
902     // 7
903     scheduleTimeupdateEvent(false);
904 
905     // 8 - this is covered, if necessary, when the engine signals a readystate change
906 
907     // 10
908     m_player->seek(time);
909     m_sentEndEvent = false;
910 }
911 
readyState() const912 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
913 {
914     return m_readyState;
915 }
916 
movieLoadType() const917 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
918 {
919     return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
920 }
921 
seeking() const922 bool HTMLMediaElement::seeking() const
923 {
924     return m_seeking;
925 }
926 
927 // playback state
currentTime() const928 float HTMLMediaElement::currentTime() const
929 {
930     if (!m_player)
931         return 0;
932     if (m_seeking)
933         return m_lastSeekTime;
934     return m_player->currentTime();
935 }
936 
setCurrentTime(float time,ExceptionCode & ec)937 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
938 {
939     seek(time, ec);
940 }
941 
startTime() const942 float HTMLMediaElement::startTime() const
943 {
944     if (!m_player)
945         return 0;
946     return m_player->startTime();
947 }
948 
duration() const949 float HTMLMediaElement::duration() const
950 {
951     if (m_readyState >= HAVE_METADATA)
952         return m_player->duration();
953 
954     return numeric_limits<float>::quiet_NaN();
955 }
956 
paused() const957 bool HTMLMediaElement::paused() const
958 {
959     return m_paused;
960 }
961 
defaultPlaybackRate() const962 float HTMLMediaElement::defaultPlaybackRate() const
963 {
964     return m_defaultPlaybackRate;
965 }
966 
setDefaultPlaybackRate(float rate)967 void HTMLMediaElement::setDefaultPlaybackRate(float rate)
968 {
969     if (m_defaultPlaybackRate != rate) {
970         m_defaultPlaybackRate = rate;
971         scheduleEvent(eventNames().ratechangeEvent);
972     }
973 }
974 
playbackRate() const975 float HTMLMediaElement::playbackRate() const
976 {
977     return m_player ? m_player->rate() : 0;
978 }
979 
setPlaybackRate(float rate)980 void HTMLMediaElement::setPlaybackRate(float rate)
981 {
982     if (m_playbackRate != rate) {
983         m_playbackRate = rate;
984         scheduleEvent(eventNames().ratechangeEvent);
985     }
986     if (m_player && potentiallyPlaying() && m_player->rate() != rate)
987         m_player->setRate(rate);
988 }
989 
webkitPreservesPitch() const990 bool HTMLMediaElement::webkitPreservesPitch() const
991 {
992     return m_webkitPreservesPitch;
993 }
994 
setWebkitPreservesPitch(bool preservesPitch)995 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
996 {
997     m_webkitPreservesPitch = preservesPitch;
998 
999     if (!m_player)
1000         return;
1001 
1002     m_player->setPreservesPitch(preservesPitch);
1003 }
1004 
ended() const1005 bool HTMLMediaElement::ended() const
1006 {
1007     return endedPlayback();
1008 }
1009 
autoplay() const1010 bool HTMLMediaElement::autoplay() const
1011 {
1012     return hasAttribute(autoplayAttr);
1013 }
1014 
setAutoplay(bool b)1015 void HTMLMediaElement::setAutoplay(bool b)
1016 {
1017     setBooleanAttribute(autoplayAttr, b);
1018 }
1019 
autobuffer() const1020 bool HTMLMediaElement::autobuffer() const
1021 {
1022     return hasAttribute(autobufferAttr);
1023 }
1024 
setAutobuffer(bool b)1025 void HTMLMediaElement::setAutobuffer(bool b)
1026 {
1027     setBooleanAttribute(autobufferAttr, b);
1028 }
1029 
play()1030 void HTMLMediaElement::play()
1031 {
1032     if (m_restrictions & RequireUserGestureForRateChangeRestriction && !processingUserGesture())
1033         return;
1034 
1035     playInternal();
1036 }
1037 
playInternal()1038 void HTMLMediaElement::playInternal()
1039 {
1040     // 4.8.10.9. Playing the media resource
1041     if (!m_player || m_networkState == NETWORK_EMPTY)
1042         scheduleLoad();
1043 
1044     if (endedPlayback()) {
1045         ExceptionCode unused;
1046         seek(0, unused);
1047     }
1048 
1049     setPlaybackRate(defaultPlaybackRate());
1050 
1051     if (m_paused) {
1052         m_paused = false;
1053         scheduleEvent(eventNames().playEvent);
1054 
1055         if (m_readyState <= HAVE_CURRENT_DATA)
1056             scheduleEvent(eventNames().waitingEvent);
1057         else if (m_readyState >= HAVE_FUTURE_DATA)
1058             scheduleEvent(eventNames().playingEvent);
1059     }
1060     m_autoplaying = false;
1061 
1062     updatePlayState();
1063 }
1064 
pause()1065 void HTMLMediaElement::pause()
1066 {
1067     if (m_restrictions & RequireUserGestureForRateChangeRestriction && !processingUserGesture())
1068         return;
1069 
1070     pauseInternal();
1071 }
1072 
1073 
pauseInternal()1074 void HTMLMediaElement::pauseInternal()
1075 {
1076     // 4.8.10.9. Playing the media resource
1077     if (!m_player || m_networkState == NETWORK_EMPTY)
1078         scheduleLoad();
1079 
1080     m_autoplaying = false;
1081 
1082     if (!m_paused) {
1083         m_paused = true;
1084         scheduleTimeupdateEvent(false);
1085         scheduleEvent(eventNames().pauseEvent);
1086     }
1087 
1088     updatePlayState();
1089 }
1090 
loop() const1091 bool HTMLMediaElement::loop() const
1092 {
1093     return hasAttribute(loopAttr);
1094 }
1095 
setLoop(bool b)1096 void HTMLMediaElement::setLoop(bool b)
1097 {
1098     setBooleanAttribute(loopAttr, b);
1099 }
1100 
controls() const1101 bool HTMLMediaElement::controls() const
1102 {
1103     Frame* frame = document()->frame();
1104 
1105     // always show controls when scripting is disabled
1106     if (frame && !frame->script()->isEnabled())
1107         return true;
1108 
1109     return hasAttribute(controlsAttr);
1110 }
1111 
setControls(bool b)1112 void HTMLMediaElement::setControls(bool b)
1113 {
1114     setBooleanAttribute(controlsAttr, b);
1115 }
1116 
volume() const1117 float HTMLMediaElement::volume() const
1118 {
1119     return m_volume;
1120 }
1121 
setVolume(float vol,ExceptionCode & ec)1122 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
1123 {
1124     if (vol < 0.0f || vol > 1.0f) {
1125         ec = INDEX_SIZE_ERR;
1126         return;
1127     }
1128 
1129     if (m_volume != vol) {
1130         m_volume = vol;
1131         updateVolume();
1132         scheduleEvent(eventNames().volumechangeEvent);
1133     }
1134 }
1135 
muted() const1136 bool HTMLMediaElement::muted() const
1137 {
1138     return m_muted;
1139 }
1140 
setMuted(bool muted)1141 void HTMLMediaElement::setMuted(bool muted)
1142 {
1143     if (m_muted != muted) {
1144         m_muted = muted;
1145         updateVolume();
1146         scheduleEvent(eventNames().volumechangeEvent);
1147     }
1148 }
1149 
togglePlayState()1150 void HTMLMediaElement::togglePlayState()
1151 {
1152     // We can safely call the internal play/pause methods, which don't check restrictions, because
1153     // this method is only called from the built-in media controller
1154     if (canPlay())
1155         playInternal();
1156     else
1157         pauseInternal();
1158 }
1159 
beginScrubbing()1160 void HTMLMediaElement::beginScrubbing()
1161 {
1162     if (!paused()) {
1163         if (ended()) {
1164             // Because a media element stays in non-paused state when it reaches end, playback resumes
1165             // when the slider is dragged from the end to another position unless we pause first. Do
1166             // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
1167             pause();
1168         } else {
1169             // Not at the end but we still want to pause playback so the media engine doesn't try to
1170             // continue playing during scrubbing. Pause without generating an event as we will
1171             // unpause after scrubbing finishes.
1172             setPausedInternal(true);
1173         }
1174     }
1175 }
1176 
endScrubbing()1177 void HTMLMediaElement::endScrubbing()
1178 {
1179     if (m_pausedInternal)
1180         setPausedInternal(false);
1181 }
1182 
1183 // The spec says to fire periodic timeupdate events (those sent while playing) every
1184 // "15 to 250ms", we choose the slowest frequency
1185 static const double maxTimeupdateEventFrequency = 0.25;
1186 
startPlaybackProgressTimer()1187 void HTMLMediaElement::startPlaybackProgressTimer()
1188 {
1189     if (m_playbackProgressTimer.isActive())
1190         return;
1191 
1192     m_previousProgressTime = WTF::currentTime();
1193     m_previousProgress = 0;
1194     m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
1195 }
1196 
playbackProgressTimerFired(Timer<HTMLMediaElement> *)1197 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
1198 {
1199     ASSERT(m_player);
1200     if (!m_playbackRate)
1201         return;
1202 
1203     scheduleTimeupdateEvent(true);
1204 
1205     // FIXME: deal with cue ranges here
1206 }
1207 
scheduleTimeupdateEvent(bool periodicEvent)1208 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
1209 {
1210     double now = WTF::currentTime();
1211     double timedelta = now - m_lastTimeUpdateEventWallTime;
1212 
1213     // throttle the periodic events
1214     if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
1215         return;
1216 
1217     // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
1218     // event at a given time so filter here
1219     float movieTime = m_player ? m_player->currentTime() : 0;
1220     if (movieTime != m_lastTimeUpdateEventMovieTime) {
1221         scheduleEvent(eventNames().timeupdateEvent);
1222         m_lastTimeUpdateEventWallTime = now;
1223         m_lastTimeUpdateEventMovieTime = movieTime;
1224     }
1225 }
1226 
canPlay() const1227 bool HTMLMediaElement::canPlay() const
1228 {
1229     return paused() || ended() || m_readyState < HAVE_METADATA;
1230 }
1231 
percentLoaded() const1232 float HTMLMediaElement::percentLoaded() const
1233 {
1234     if (!m_player)
1235         return 0;
1236     float duration = m_player->duration();
1237     return duration ? m_player->maxTimeBuffered() / duration : 0;
1238 }
1239 
havePotentialSourceChild()1240 bool HTMLMediaElement::havePotentialSourceChild()
1241 {
1242     // Stash the current <source> node so we can restore it after checking
1243     // to see there is another potential
1244     HTMLSourceElement* currentSourceNode = m_currentSourceNode;
1245     KURL nextURL = selectNextSourceChild(0, DoNothing);
1246     m_currentSourceNode = currentSourceNode;
1247 
1248     return nextURL.isValid();
1249 }
1250 
selectNextSourceChild(ContentType * contentType,InvalidSourceAction actionIfInvalid)1251 KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSourceAction actionIfInvalid)
1252 {
1253     KURL mediaURL;
1254     Node* node;
1255     bool lookingForPreviousNode = m_currentSourceNode;
1256     bool canUse = false;
1257 
1258     for (node = firstChild(); !canUse && node; node = node->nextSibling()) {
1259         if (!node->hasTagName(sourceTag))
1260             continue;
1261 
1262         if (lookingForPreviousNode) {
1263             if (m_currentSourceNode == static_cast<HTMLSourceElement*>(node))
1264                 lookingForPreviousNode = false;
1265             continue;
1266         }
1267 
1268         HTMLSourceElement* source = static_cast<HTMLSourceElement*>(node);
1269         if (!source->hasAttribute(srcAttr))
1270             goto check_again;
1271 
1272         if (source->hasAttribute(mediaAttr)) {
1273             MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
1274             RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media());
1275             if (!screenEval.eval(media.get()))
1276                 goto check_again;
1277         }
1278 
1279         if (source->hasAttribute(typeAttr)) {
1280             if (!MediaPlayer::supportsType(ContentType(source->type())))
1281                 goto check_again;
1282         }
1283 
1284         // Is it safe to load this url?
1285         mediaURL = source->src();
1286         if (!mediaURL.isValid() || !isSafeToLoadURL(mediaURL, actionIfInvalid))
1287             goto check_again;
1288 
1289         // Making it this far means the <source> looks reasonable
1290         canUse = true;
1291         if (contentType)
1292             *contentType = ContentType(source->type());
1293 
1294 check_again:
1295         if (!canUse && actionIfInvalid == Complain)
1296             source->scheduleErrorEvent();
1297         m_currentSourceNode = static_cast<HTMLSourceElement*>(node);
1298     }
1299 
1300     if (!canUse)
1301         m_currentSourceNode = 0;
1302     return canUse ? mediaURL : KURL();
1303 }
1304 
mediaPlayerTimeChanged(MediaPlayer *)1305 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
1306 {
1307     beginProcessingMediaPlayerCallback();
1308 
1309     if (m_readyState >= HAVE_CURRENT_DATA && m_seeking) {
1310         scheduleEvent(eventNames().seekedEvent);
1311         m_seeking = false;
1312     }
1313 
1314     float now = currentTime();
1315     float dur = duration();
1316     if (!isnan(dur) && dur && now >= dur) {
1317         if (loop()) {
1318             ExceptionCode ignoredException;
1319             m_sentEndEvent = false;
1320             seek(0, ignoredException);
1321         } else {
1322             if (!m_sentEndEvent) {
1323                 m_sentEndEvent = true;
1324                 scheduleTimeupdateEvent(false);
1325                 scheduleEvent(eventNames().endedEvent);
1326             }
1327         }
1328     }
1329     else
1330         m_sentEndEvent = false;
1331 
1332     updatePlayState();
1333     endProcessingMediaPlayerCallback();
1334 }
1335 
mediaPlayerVolumeChanged(MediaPlayer *)1336 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
1337 {
1338     beginProcessingMediaPlayerCallback();
1339     updateVolume();
1340     endProcessingMediaPlayerCallback();
1341 }
1342 
mediaPlayerDurationChanged(MediaPlayer *)1343 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*)
1344 {
1345     beginProcessingMediaPlayerCallback();
1346     scheduleEvent(eventNames().durationchangeEvent);
1347 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1348     if (renderer()) {
1349         renderer()->updateFromElement();
1350         if (renderer()->isVideo())
1351             toRenderVideo(renderer())->videoSizeChanged();
1352     }
1353 #endif
1354     endProcessingMediaPlayerCallback();
1355 }
1356 
mediaPlayerRateChanged(MediaPlayer *)1357 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
1358 {
1359     beginProcessingMediaPlayerCallback();
1360     // Stash the rate in case the one we tried to set isn't what the engine is
1361     // using (eg. it can't handle the rate we set)
1362     m_playbackRate = m_player->rate();
1363     endProcessingMediaPlayerCallback();
1364 }
1365 
mediaPlayerSawUnsupportedTracks(MediaPlayer *)1366 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*)
1367 {
1368     // The MediaPlayer came across content it cannot completely handle.
1369     // This is normally acceptable except when we are in a standalone
1370     // MediaDocument. If so, tell the document what has happened.
1371     if (ownerDocument()->isMediaDocument()) {
1372         MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument());
1373         mediaDocument->mediaElementSawUnsupportedTracks();
1374     }
1375 }
1376 
1377 // MediaPlayerPresentation methods
mediaPlayerRepaint(MediaPlayer *)1378 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
1379 {
1380     beginProcessingMediaPlayerCallback();
1381     if (renderer())
1382         renderer()->repaint();
1383     endProcessingMediaPlayerCallback();
1384 }
1385 
mediaPlayerSizeChanged(MediaPlayer *)1386 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*)
1387 {
1388     beginProcessingMediaPlayerCallback();
1389 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1390     if (renderer() && renderer()->isVideo())
1391         toRenderVideo(renderer())->videoSizeChanged();
1392 #endif
1393     endProcessingMediaPlayerCallback();
1394 }
1395 
1396 #if USE(ACCELERATED_COMPOSITING)
mediaPlayerRenderingCanBeAccelerated(MediaPlayer *)1397 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*)
1398 {
1399     if (renderer() && renderer()->isVideo()) {
1400         ASSERT(renderer()->view());
1401         return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer()));
1402     }
1403     return false;
1404 }
1405 
mediaPlayerGraphicsLayer(MediaPlayer *)1406 GraphicsLayer* HTMLMediaElement::mediaPlayerGraphicsLayer(MediaPlayer*)
1407 {
1408     if (renderer() && renderer()->isVideo())
1409         return toRenderVideo(renderer())->videoGraphicsLayer();
1410     return 0;
1411 }
1412 #endif
1413 
buffered() const1414 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
1415 {
1416     // FIXME real ranges support
1417     if (!m_player || !m_player->maxTimeBuffered())
1418         return TimeRanges::create();
1419     return TimeRanges::create(0, m_player->maxTimeBuffered());
1420 }
1421 
played() const1422 PassRefPtr<TimeRanges> HTMLMediaElement::played() const
1423 {
1424     if (!m_playedTimeRanges) {
1425         // We are not yet loaded
1426         return TimeRanges::create();
1427     }
1428     if (m_playing) {
1429         float time = currentTime();
1430         if (m_lastSeekTime < time)
1431             m_playedTimeRanges->add(m_lastSeekTime, time);
1432     }
1433     return m_playedTimeRanges->copy();
1434 }
1435 
seekable() const1436 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
1437 {
1438     // FIXME real ranges support
1439     if (!maxTimeSeekable())
1440         return TimeRanges::create();
1441     return TimeRanges::create(minTimeSeekable(), maxTimeSeekable());
1442 }
1443 
potentiallyPlaying() const1444 bool HTMLMediaElement::potentiallyPlaying() const
1445 {
1446     return !paused() && m_readyState >= HAVE_FUTURE_DATA && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
1447 }
1448 
1449 #if PLATFORM(ANDROID)
couldPlayIfEnoughData() const1450 bool HTMLMediaElement::couldPlayIfEnoughData() const
1451 {
1452     return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
1453 }
1454 #endif
1455 
endedPlayback() const1456 bool HTMLMediaElement::endedPlayback() const
1457 {
1458     if (!m_player || m_readyState < HAVE_METADATA)
1459         return false;
1460 
1461     float dur = duration();
1462     return !isnan(dur) && currentTime() >= dur && !loop();
1463 }
1464 
stoppedDueToErrors() const1465 bool HTMLMediaElement::stoppedDueToErrors() const
1466 {
1467     if (m_readyState >= HAVE_METADATA && m_error) {
1468         RefPtr<TimeRanges> seekableRanges = seekable();
1469         if (!seekableRanges->contain(currentTime()))
1470             return true;
1471     }
1472 
1473     return false;
1474 }
1475 
pausedForUserInteraction() const1476 bool HTMLMediaElement::pausedForUserInteraction() const
1477 {
1478 //    return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user]
1479     return false;
1480 }
1481 
minTimeSeekable() const1482 float HTMLMediaElement::minTimeSeekable() const
1483 {
1484     return 0;
1485 }
1486 
maxTimeSeekable() const1487 float HTMLMediaElement::maxTimeSeekable() const
1488 {
1489     return m_player ? m_player->maxTimeSeekable() : 0;
1490 }
1491 
updateVolume()1492 void HTMLMediaElement::updateVolume()
1493 {
1494     if (!m_player)
1495         return;
1496 
1497     // Avoid recursion when the player reports volume changes.
1498     if (!processingMediaPlayerCallback()) {
1499         Page* page = document()->page();
1500         float volumeMultiplier = page ? page->mediaVolume() : 1;
1501 
1502         m_player->setVolume(m_muted ? 0 : m_volume * volumeMultiplier);
1503     }
1504 
1505     if (renderer())
1506         renderer()->updateFromElement();
1507 }
1508 
updatePlayState()1509 void HTMLMediaElement::updatePlayState()
1510 {
1511     if (!m_player)
1512         return;
1513 
1514     if (m_pausedInternal) {
1515         if (!m_player->paused())
1516             m_player->pause();
1517         m_playbackProgressTimer.stop();
1518         return;
1519     }
1520 
1521     bool shouldBePlaying = potentiallyPlaying();
1522     bool playerPaused = m_player->paused();
1523     if (shouldBePlaying && playerPaused) {
1524         // Set rate before calling play in case the rate was set before the media engine wasn't setup.
1525         // The media engine should just stash the rate since it isn't already playing.
1526         m_player->setRate(m_playbackRate);
1527         m_player->play();
1528         startPlaybackProgressTimer();
1529         m_playing = true;
1530     } else if (!shouldBePlaying && !playerPaused) {
1531         m_player->pause();
1532         m_playbackProgressTimer.stop();
1533         m_playing = false;
1534         float time = currentTime();
1535         if (m_lastSeekTime < time)
1536             m_playedTimeRanges->add(m_lastSeekTime, time);
1537 #if PLATFORM(ANDROID)
1538     } else if (couldPlayIfEnoughData() && playerPaused) {
1539         m_player->prepareToPlay();
1540 #endif
1541     }
1542 
1543     if (renderer())
1544         renderer()->updateFromElement();
1545 }
1546 
setPausedInternal(bool b)1547 void HTMLMediaElement::setPausedInternal(bool b)
1548 {
1549     m_pausedInternal = b;
1550     updatePlayState();
1551 }
1552 
stopPeriodicTimers()1553 void HTMLMediaElement::stopPeriodicTimers()
1554 {
1555     m_progressEventTimer.stop();
1556     m_playbackProgressTimer.stop();
1557 }
1558 
userCancelledLoad()1559 void HTMLMediaElement::userCancelledLoad()
1560 {
1561     if (m_networkState != NETWORK_EMPTY) {
1562 
1563         // If the media data fetching process is aborted by the user:
1564 
1565         // 1 - The user agent should cancel the fetching process.
1566 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1567         m_player.clear();
1568 #endif
1569         stopPeriodicTimers();
1570 
1571         // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORT.
1572         m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
1573 
1574         // 3 - Queue a task to fire a progress event called abort at the media element.
1575         scheduleProgressEvent(eventNames().abortEvent);
1576 
1577         // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
1578         // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
1579         // simple event called emptied at the element. Otherwise, set set the element's networkState
1580         // attribute to the NETWORK_IDLE value.
1581         if (m_networkState >= NETWORK_LOADING) {
1582             m_networkState = NETWORK_EMPTY;
1583             m_readyState = HAVE_NOTHING;
1584             scheduleEvent(eventNames().emptiedEvent);
1585         }
1586 
1587         // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1588         m_delayingTheLoadEvent = false;
1589     }
1590 }
1591 
documentWillBecomeInactive()1592 void HTMLMediaElement::documentWillBecomeInactive()
1593 {
1594     m_inActiveDocument = false;
1595     userCancelledLoad();
1596 
1597     // Stop the playback without generating events
1598     setPausedInternal(true);
1599 
1600     if (renderer())
1601         renderer()->updateFromElement();
1602 
1603     stopPeriodicTimers();
1604     cancelPendingEventsAndCallbacks();
1605 }
1606 
documentDidBecomeActive()1607 void HTMLMediaElement::documentDidBecomeActive()
1608 {
1609     m_inActiveDocument = true;
1610     setPausedInternal(false);
1611 
1612     if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) {
1613         // Restart the load if it was aborted in the middle by moving the document to the page cache.
1614         // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to
1615         //  MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards).
1616         // This behavior is not specified but it seems like a sensible thing to do.
1617         ExceptionCode ec;
1618         load(ec);
1619     }
1620 
1621     if (renderer())
1622         renderer()->updateFromElement();
1623 }
1624 
mediaVolumeDidChange()1625 void HTMLMediaElement::mediaVolumeDidChange()
1626 {
1627     updateVolume();
1628 }
1629 
defaultEventHandler(Event * event)1630 void HTMLMediaElement::defaultEventHandler(Event* event)
1631 {
1632 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1633     RenderObject* r = renderer();
1634     if (!r || !r->isWidget())
1635         return;
1636 
1637     Widget* widget = toRenderWidget(r)->widget();
1638     if (widget)
1639         widget->handleEvent(event);
1640 #else
1641     if (renderer() && renderer()->isMedia())
1642         toRenderMedia(renderer())->forwardEvent(event);
1643     if (event->defaultHandled())
1644         return;
1645     HTMLElement::defaultEventHandler(event);
1646 #endif
1647 }
1648 
processingUserGesture() const1649 bool HTMLMediaElement::processingUserGesture() const
1650 {
1651     Frame* frame = document()->frame();
1652     FrameLoader* loader = frame ? frame->loader() : 0;
1653 
1654     // return 'true' for safety if we don't know the answer
1655     return loader ? loader->isProcessingUserGesture() : true;
1656 }
1657 
1658 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1659 
deliverNotification(MediaPlayerProxyNotificationType notification)1660 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification)
1661 {
1662     if (notification == MediaPlayerNotificationPlayPauseButtonPressed) {
1663         togglePlayState();
1664         return;
1665     }
1666 
1667     if (m_player)
1668         m_player->deliverNotification(notification);
1669 }
1670 
setMediaPlayerProxy(WebMediaPlayerProxy * proxy)1671 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy)
1672 {
1673     if (m_player)
1674         m_player->setMediaPlayerProxy(proxy);
1675 }
1676 
initialURL()1677 String HTMLMediaElement::initialURL()
1678 {
1679     KURL initialSrc = document()->completeURL(getAttribute(srcAttr));
1680 
1681     if (!initialSrc.isValid())
1682         initialSrc = selectNextSourceChild(0, DoNothing);
1683 
1684     m_currentSrc = initialSrc.string();
1685 
1686     return initialSrc;
1687 }
1688 
finishParsingChildren()1689 void HTMLMediaElement::finishParsingChildren()
1690 {
1691     HTMLElement::finishParsingChildren();
1692     if (!m_player)
1693         m_player.set(new MediaPlayer(this));
1694 
1695     document()->updateStyleIfNeeded();
1696     if (m_needWidgetUpdate && renderer())
1697         toRenderPartObject(renderer())->updateWidget(true);
1698 }
1699 
1700 #endif
1701 
1702 }
1703 
1704 #endif
1705