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