2 * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "HTMLMediaElement.h"
31 #include "ApplicationCacheHost.h"
32 #include "ApplicationCacheResource.h"
33 #include "Attribute.h"
35 #include "ChromeClient.h"
36 #include "ClientRect.h"
37 #include "ClientRectList.h"
38 #include "ContentSecurityPolicy.h"
39 #include "ContentType.h"
40 #include "CSSPropertyNames.h"
41 #include "CSSValueKeywords.h"
42 #include "DiagnosticLoggingKeys.h"
43 #include "DocumentLoader.h"
44 #include "ElementShadow.h"
46 #include "EventNames.h"
47 #include "ExceptionCode.h"
49 #include "FrameLoader.h"
50 #include "FrameLoaderClient.h"
51 #include "FrameView.h"
52 #include "HTMLDocument.h"
53 #include "HTMLNames.h"
54 #include "HTMLSourceElement.h"
55 #include "HTMLVideoElement.h"
58 #include "MediaController.h"
59 #include "MediaControls.h"
60 #include "MediaDocument.h"
61 #include "MediaError.h"
62 #include "MediaFragmentURIParser.h"
63 #include "MediaKeyError.h"
64 #include "MediaKeyEvent.h"
65 #include "MediaList.h"
66 #include "MediaPlayer.h"
67 #include "MediaQueryEvaluator.h"
68 #include "MouseEvent.h"
69 #include "MIMETypeRegistry.h"
70 #include "NodeRenderingContext.h"
72 #include "RenderVideo.h"
73 #include "RenderView.h"
74 #include "ScriptController.h"
75 #include "ScriptEventListener.h"
76 #include "SecurityOrigin.h"
77 #include "SecurityPolicy.h"
79 #include "ShadowRoot.h"
80 #include "TimeRanges.h"
83 #include <wtf/CurrentTime.h>
84 #include <wtf/MathExtras.h>
85 #include <wtf/NonCopyingSort.h>
86 #include <wtf/Uint8Array.h>
87 #include <wtf/text/CString.h>
89 #if USE(ACCELERATED_COMPOSITING)
90 #include "RenderLayerCompositor.h"
93 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
94 #include "RenderEmbeddedObject.h"
98 #if ENABLE(VIDEO_TRACK)
99 #include "HTMLTrackElement.h"
100 #include "RuntimeEnabledFeatures.h"
101 #include "TextTrackCueList.h"
102 #include "TextTrackList.h"
105 #if ENABLE(WEB_AUDIO)
106 #include "AudioSourceProvider.h"
107 #include "MediaElementAudioSourceNode.h"
111 #include "DisplaySleepDisabler.h"
114 #if ENABLE(MEDIA_STREAM)
115 #include "MediaStreamRegistry.h"
118 #if ENABLE(TIZEN_EXTENSIBLE_API)
119 #include "TizenExtensibleAPI.h"
122 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
131 static String urlForLogging(const KURL& url)
133 static const unsigned maximumURLLengthForLogging = 128;
135 if (url.string().length() < maximumURLLengthForLogging)
137 return url.string().substring(0, maximumURLLengthForLogging) + "...";
140 static const char* boolString(bool val)
142 return val ? "true" : "false";
146 #ifndef LOG_MEDIA_EVENTS
147 // Default to not logging events because so many are generated they can overwhelm the rest of
149 #define LOG_MEDIA_EVENTS 0
152 #ifndef LOG_CACHED_TIME_WARNINGS
153 // Default to not logging warnings about excessive drift in the cached media time because it adds a
154 // fair amount of overhead and logging.
155 #define LOG_CACHED_TIME_WARNINGS 0
158 #if ENABLE(MEDIA_SOURCE)
159 // URL protocol used to signal that the media source API is being used.
160 static const char* mediaSourceURLProtocol = "x-media-source";
163 using namespace HTMLNames;
166 typedef HashMap<Document*, HashSet<HTMLMediaElement*> > DocumentElementSetMap;
167 static DocumentElementSetMap& documentToElementSetMap()
169 DEFINE_STATIC_LOCAL(DocumentElementSetMap, map, ());
173 static void addElementToDocumentMap(HTMLMediaElement* element, Document* document)
175 DocumentElementSetMap& map = documentToElementSetMap();
176 HashSet<HTMLMediaElement*> set = map.take(document);
178 map.add(document, set);
181 static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* document)
183 DocumentElementSetMap& map = documentToElementSetMap();
184 HashSet<HTMLMediaElement*> set = map.take(document);
187 map.add(document, set);
190 #if ENABLE(ENCRYPTED_MEDIA)
191 static ExceptionCode exceptionCodeForMediaKeyException(MediaPlayer::MediaKeyException exception)
194 case MediaPlayer::NoError:
196 case MediaPlayer::InvalidPlayerState:
197 return INVALID_STATE_ERR;
198 case MediaPlayer::KeySystemNotSupported:
199 return NOT_SUPPORTED_ERR;
202 ASSERT_NOT_REACHED();
203 return INVALID_STATE_ERR;
207 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document, bool createdByParser)
208 : HTMLElement(tagName, document)
209 , ActiveDOMObject(document, this)
210 , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
211 , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
212 , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
213 , m_playedTimeRanges()
214 , m_asyncEventQueue(GenericEventQueue::create(this))
215 , m_playbackRate(1.0f)
216 , m_defaultPlaybackRate(1.0f)
217 , m_webkitPreservesPitch(true)
218 , m_networkState(NETWORK_EMPTY)
219 , m_readyState(HAVE_NOTHING)
220 , m_readyStateMaximum(HAVE_NOTHING)
223 , m_previousProgressTime(numeric_limits<double>::max())
224 , m_lastTimeUpdateEventWallTime(0)
225 , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max())
226 , m_loadState(WaitingForSource)
227 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
230 , m_restrictions(RequireUserGestureForFullscreenRestriction | RequirePageConsentToLoadMediaRestriction)
231 , m_preload(MediaPlayer::Auto)
232 , m_displayMode(Unknown)
233 , m_processingMediaPlayerCallback(0)
234 #if ENABLE(MEDIA_SOURCE)
235 , m_sourceState(SOURCE_CLOSED)
237 , m_cachedTime(MediaPlayer::invalidTime())
238 , m_cachedTimeWallClockUpdateTime(0)
239 , m_minimumWallClockTimeToCacheMediaTime(0)
240 , m_fragmentStartTime(MediaPlayer::invalidTime())
241 , m_fragmentEndTime(MediaPlayer::invalidTime())
242 , m_pendingLoadFlags(0)
244 , m_isWaitingUntilMediaCanStart(false)
245 , m_shouldDelayLoadEvent(false)
246 , m_haveFiredLoadedData(false)
247 , m_inActiveDocument(true)
248 , m_autoplaying(true)
252 , m_sentStalledEvent(false)
253 , m_sentEndEvent(false)
254 , m_pausedInternal(false)
255 , m_sendProgressEvents(true)
256 , m_isFullscreen(false)
257 , m_closedCaptionsVisible(false)
258 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
259 , m_needWidgetUpdate(false)
261 , m_dispatchingCanPlayEvent(false)
262 , m_loadInitiatedByUserGesture(false)
263 , m_completelyLoaded(false)
264 , m_havePreparedToPlay(false)
265 , m_parsingInProgress(createdByParser)
266 #if ENABLE(VIDEO_TRACK)
267 , m_tracksAreReady(true)
268 , m_haveVisibleTextTrack(false)
269 , m_lastTextTrackUpdateTime(-1)
271 , m_ignoreTrackDisplayUpdate(0)
272 , m_disableCaptions(false)
274 #if ENABLE(WEB_AUDIO)
275 , m_audioSourceNode(0)
277 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
281 LOG(Media, "HTMLMediaElement::HTMLMediaElement");
282 document->registerForMediaVolumeCallbacks(this);
283 document->registerForPrivateBrowsingStateChangedCallbacks(this);
285 if (document->settings() && document->settings()->mediaPlaybackRequiresUserGesture()) {
286 addBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
287 addBehaviorRestriction(RequireUserGestureForLoadRestriction);
290 #if ENABLE(MEDIA_SOURCE)
291 m_mediaSourceURL.setProtocol(mediaSourceURLProtocol);
292 m_mediaSourceURL.setPath(createCanonicalUUIDString());
295 setHasCustomCallbacks();
296 addElementToDocumentMap(this, document);
298 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
299 document->incrementActiveMediaObjectCount();
303 HTMLMediaElement::~HTMLMediaElement()
305 LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
306 if (m_isWaitingUntilMediaCanStart)
307 document()->removeMediaCanStartListener(this);
308 setShouldDelayLoadEvent(false);
309 document()->unregisterForMediaVolumeCallbacks(this);
310 document()->unregisterForPrivateBrowsingStateChangedCallbacks(this);
311 #if ENABLE(VIDEO_TRACK)
313 m_textTracks->clearOwner();
315 for (unsigned i = 0; i < m_textTracks->length(); ++i)
316 m_textTracks->item(i)->clearClient();
320 if (m_mediaController)
321 m_mediaController->removeMediaElement(this);
323 removeElementFromDocumentMap(this, document());
325 m_completelyLoaded = true;
327 m_player->clearMediaPlayerClient();
329 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
330 document()->decrementActiveMediaObjectCount();
334 void HTMLMediaElement::didMoveToNewDocument(Document* oldDocument)
336 if (m_isWaitingUntilMediaCanStart) {
338 oldDocument->removeMediaCanStartListener(this);
339 document()->addMediaCanStartListener(this);
342 if (m_shouldDelayLoadEvent) {
344 oldDocument->decrementLoadEventDelayCount();
345 document()->incrementLoadEventDelayCount();
349 oldDocument->unregisterForMediaVolumeCallbacks(this);
350 removeElementFromDocumentMap(this, oldDocument);
353 document()->registerForMediaVolumeCallbacks(this);
354 addElementToDocumentMap(this, document());
356 HTMLElement::didMoveToNewDocument(oldDocument);
359 bool HTMLMediaElement::supportsFocus() const
361 if (ownerDocument()->isMediaDocument())
364 // If no controls specified, we should still be able to focus the element if it has tabIndex.
365 return controls() || HTMLElement::supportsFocus();
368 bool HTMLMediaElement::isMouseFocusable() const
373 void HTMLMediaElement::parseAttribute(const Attribute& attribute)
375 if (attribute.name() == srcAttr) {
376 // Trigger a reload, as long as the 'src' attribute is present.
377 if (fastHasAttribute(srcAttr))
378 scheduleLoad(MediaResource);
379 } else if (attribute.name() == controlsAttr)
380 configureMediaControls();
382 else if (attribute.name() == loopAttr)
383 updateDisableSleep();
385 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
386 else if (attribute.name() == mutedAttr)
389 else if (attribute.name() == preloadAttr) {
390 if (equalIgnoringCase(attribute.value(), "none"))
391 m_preload = MediaPlayer::None;
392 else if (equalIgnoringCase(attribute.value(), "metadata"))
393 m_preload = MediaPlayer::MetaData;
395 // The spec does not define an "invalid value default" but "auto" is suggested as the
396 // "missing value default", so use it for everything except "none" and "metadata"
397 m_preload = MediaPlayer::Auto;
400 // The attribute must be ignored if the autoplay attribute is present
401 if (!autoplay() && m_player)
402 m_player->setPreload(m_preload);
404 } else if (attribute.name() == mediagroupAttr)
405 setMediaGroup(attribute.value());
406 else if (attribute.name() == onabortAttr)
407 setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attribute));
408 else if (attribute.name() == onbeforeloadAttr)
409 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attribute));
410 else if (attribute.name() == oncanplayAttr)
411 setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, attribute));
412 else if (attribute.name() == oncanplaythroughAttr)
413 setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, attribute));
414 else if (attribute.name() == ondurationchangeAttr)
415 setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, attribute));
416 else if (attribute.name() == onemptiedAttr)
417 setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, attribute));
418 else if (attribute.name() == onendedAttr)
419 setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attribute));
420 else if (attribute.name() == onerrorAttr)
421 setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attribute));
422 else if (attribute.name() == onloadeddataAttr)
423 setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attribute));
424 else if (attribute.name() == onloadedmetadataAttr)
425 setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attribute));
426 else if (attribute.name() == onloadstartAttr)
427 setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attribute));
428 else if (attribute.name() == onpauseAttr)
429 setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, attribute));
430 else if (attribute.name() == onplayAttr)
431 setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, attribute));
432 else if (attribute.name() == onplayingAttr)
433 setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, attribute));
434 else if (attribute.name() == onprogressAttr)
435 setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, attribute));
436 else if (attribute.name() == onratechangeAttr)
437 setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, attribute));
438 else if (attribute.name() == onseekedAttr)
439 setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, attribute));
440 else if (attribute.name() == onseekingAttr)
441 setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, attribute));
442 else if (attribute.name() == onstalledAttr)
443 setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, attribute));
444 else if (attribute.name() == onsuspendAttr)
445 setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, attribute));
446 else if (attribute.name() == ontimeupdateAttr)
447 setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, attribute));
448 else if (attribute.name() == onvolumechangeAttr)
449 setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, attribute));
450 else if (attribute.name() == onwaitingAttr)
451 setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, attribute));
452 else if (attribute.name() == onwebkitbeginfullscreenAttr)
453 setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, attribute));
454 else if (attribute.name() == onwebkitendfullscreenAttr)
455 setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, attribute));
456 #if ENABLE(MEDIA_SOURCE)
457 else if (attribute.name() == onwebkitsourcecloseAttr)
458 setAttributeEventListener(eventNames().webkitsourcecloseEvent, createAttributeEventListener(this, attribute));
459 else if (attribute.name() == onwebkitsourceendedAttr)
460 setAttributeEventListener(eventNames().webkitsourceendedEvent, createAttributeEventListener(this, attribute));
461 else if (attribute.name() == onwebkitsourceopenAttr)
462 setAttributeEventListener(eventNames().webkitsourceopenEvent, createAttributeEventListener(this, attribute));
465 HTMLElement::parseAttribute(attribute);
468 void HTMLMediaElement::finishParsingChildren()
470 HTMLElement::finishParsingChildren();
471 m_parsingInProgress = false;
473 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
474 document()->updateStyleIfNeeded();
475 createMediaPlayerProxy();
478 #if ENABLE(VIDEO_TRACK)
479 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
482 for (Node* node = firstChild(); node; node = node->nextSibling()) {
483 if (node->hasTagName(trackTag)) {
484 scheduleLoad(TextTrackResource);
491 bool HTMLMediaElement::rendererIsNeeded(const NodeRenderingContext& context)
493 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
494 UNUSED_PARAM(context);
495 Frame* frame = document()->frame();
501 return controls() ? HTMLElement::rendererIsNeeded(context) : false;
505 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
507 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
508 // Setup the renderer if we already have a proxy widget.
509 RenderEmbeddedObject* mediaRenderer = new (arena) RenderEmbeddedObject(this);
511 mediaRenderer->setWidget(m_proxyWidget);
513 if (Frame* frame = document()->frame())
514 frame->loader()->client()->showMediaPlayerProxyPlugin(m_proxyWidget.get());
516 return mediaRenderer;
518 return new (arena) RenderMedia(this);
522 bool HTMLMediaElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const
524 if (!hasMediaControls())
526 // <media> doesn't allow its content, including shadow subtree, to
527 // be rendered. So this should return false for most of the children.
528 // One exception is a shadow tree built for rendering controls which should be visible.
529 // So we let them go here by comparing its subtree root with one of the controls.
530 return (mediaControls()->treeScope() == childContext.node()->treeScope()
531 && childContext.isOnUpperEncapsulationBoundary() && HTMLElement::childShouldCreateRenderer(childContext));
534 Node::InsertionNotificationRequest HTMLMediaElement::insertedInto(ContainerNode* insertionPoint)
536 LOG(Media, "HTMLMediaElement::insertedInto");
537 HTMLElement::insertedInto(insertionPoint);
538 if (insertionPoint->inDocument() && !getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY)
539 scheduleLoad(MediaResource);
541 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
544 configureMediaControls();
545 return InsertionDone;
548 void HTMLMediaElement::removedFrom(ContainerNode* insertionPoint)
550 if (insertionPoint->inDocument()) {
551 LOG(Media, "HTMLMediaElement::removedFromDocument");
552 configureMediaControls();
553 if (m_networkState > NETWORK_EMPTY)
554 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
555 suspend(PageWillBeSuspended);
563 HTMLElement::removedFrom(insertionPoint);
566 void HTMLMediaElement::attach()
570 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
571 m_needWidgetUpdate = true;
574 HTMLElement::attach();
577 renderer()->updateFromElement();
578 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
579 else if (m_proxyWidget) {
580 if (Frame* frame = document()->frame())
581 frame->loader()->client()->hideMediaPlayerProxyPlugin(m_proxyWidget.get());
586 void HTMLMediaElement::didRecalcStyle(StyleChange)
589 renderer()->updateFromElement();
592 void HTMLMediaElement::scheduleLoad(LoadType loadType)
594 LOG(Media, "HTMLMediaElement::scheduleLoad");
596 if ((loadType & MediaResource) && !(m_pendingLoadFlags & MediaResource)) {
597 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
598 createMediaPlayerProxy();
602 m_pendingLoadFlags |= MediaResource;
605 #if ENABLE(VIDEO_TRACK)
606 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (loadType & TextTrackResource))
607 m_pendingLoadFlags |= TextTrackResource;
610 if (!m_loadTimer.isActive())
611 m_loadTimer.startOneShot(0);
614 void HTMLMediaElement::scheduleNextSourceChild()
616 // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
617 m_pendingLoadFlags |= MediaResource;
618 m_loadTimer.startOneShot(0);
621 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
624 LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data());
626 RefPtr<Event> event = Event::create(eventName, false, true);
627 event->setTarget(this);
629 m_asyncEventQueue->enqueueEvent(event.release());
632 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
634 RefPtr<HTMLMediaElement> protect(this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
636 #if ENABLE(VIDEO_TRACK)
637 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingLoadFlags & TextTrackResource))
638 configureTextTracks();
641 if (m_pendingLoadFlags & MediaResource) {
642 if (m_loadState == LoadingFromSourceElement)
643 loadNextSourceChild();
648 m_pendingLoadFlags = 0;
651 PassRefPtr<MediaError> HTMLMediaElement::error() const
656 void HTMLMediaElement::setSrc(const String& url)
658 setAttribute(srcAttr, url);
661 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
663 return m_networkState;
666 String HTMLMediaElement::canPlayType(const String& mimeType, const String& keySystem, const KURL& url) const
668 MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType), keySystem, url, this);
674 case MediaPlayer::IsNotSupported:
677 case MediaPlayer::MayBeSupported:
680 case MediaPlayer::IsSupported:
681 canPlay = "probably";
685 LOG(Media, "HTMLMediaElement::canPlayType(%s, %s, %s) -> %s", mimeType.utf8().data(), keySystem.utf8().data(), url.string().utf8().data(), canPlay.utf8().data());
690 void HTMLMediaElement::load()
692 RefPtr<HTMLMediaElement> protect(this); // loadInternal may result in a 'beforeload' event, which can make arbitrary DOM mutations.
694 LOG(Media, "HTMLMediaElement::load()");
696 if (userGestureRequiredForLoad() && !ScriptController::processingUserGesture())
699 m_loadInitiatedByUserGesture = ScriptController::processingUserGesture();
700 if (m_loadInitiatedByUserGesture)
701 removeBehaviorsRestrictionsAfterFirstUserGesture();
707 void HTMLMediaElement::prepareForLoad()
709 LOG(Media, "HTMLMediaElement::prepareForLoad");
711 // Perform the cleanup required for the resource load algorithm to run.
712 stopPeriodicTimers();
714 m_sentEndEvent = false;
715 m_sentStalledEvent = false;
716 m_haveFiredLoadedData = false;
717 m_completelyLoaded = false;
718 m_havePreparedToPlay = false;
719 m_displayMode = Unknown;
721 // 1 - Abort any already-running instance of the resource selection algorithm for this element.
722 m_loadState = WaitingForSource;
723 m_currentSourceNode = 0;
725 // 2 - If there are any tasks from the media element's media element event task source in
726 // one of the task queues, then remove those tasks.
727 cancelPendingEventsAndCallbacks();
729 // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
730 // a task to fire a simple event named abort at the media element.
731 if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
732 scheduleEvent(eventNames().abortEvent);
734 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
738 m_player->cancelLoad();
740 createMediaPlayerProxy();
743 #if ENABLE(MEDIA_SOURCE)
744 if (m_sourceState != SOURCE_CLOSED)
745 setSourceState(SOURCE_CLOSED);
748 // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
749 if (m_networkState != NETWORK_EMPTY) {
750 m_networkState = NETWORK_EMPTY;
751 m_readyState = HAVE_NOTHING;
752 m_readyStateMaximum = HAVE_NOTHING;
756 invalidateCachedTime();
757 scheduleEvent(eventNames().emptiedEvent);
758 updateMediaController();
759 #if ENABLE(VIDEO_TRACK)
760 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
761 updateActiveTextTrackCues(0);
765 // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
766 setPlaybackRate(defaultPlaybackRate());
768 // 6 - Set the error attribute to null and the autoplaying flag to true.
770 m_autoplaying = true;
772 // 7 - Invoke the media element's resource selection algorithm.
774 // 8 - Note: Playback of any previously playing media resource for this element stops.
776 // The resource selection algorithm
777 // 1 - Set the networkState to NETWORK_NO_SOURCE
778 m_networkState = NETWORK_NO_SOURCE;
780 // 2 - Asynchronously await a stable state.
782 m_playedTimeRanges = TimeRanges::create();
785 // The spec doesn't say to block the load event until we actually run the asynchronous section
786 // algorithm, but do it now because we won't start that until after the timer fires and the
787 // event may have already fired by then.
788 setShouldDelayLoadEvent(true);
790 configureMediaControls();
793 void HTMLMediaElement::loadInternal()
795 // Some of the code paths below this function dispatch the BeforeLoad event. This ASSERT helps
796 // us catch those bugs more quickly without needing all the branches to align to actually
797 // trigger the event.
798 ASSERT(!eventDispatchForbidden());
800 // If we can't start a load right away, start it later.
801 Page* page = document()->page();
802 if (pageConsentRequiredForLoad() && page && !page->canStartMedia()) {
803 setShouldDelayLoadEvent(false);
804 if (m_isWaitingUntilMediaCanStart)
806 document()->addMediaCanStartListener(this);
807 m_isWaitingUntilMediaCanStart = true;
811 // Once the page has allowed an element to load media, it is free to load at will. This allows a
812 // playlist that starts in a foreground tab to continue automatically if the tab is subsequently
813 // put in the the background.
814 removeBehaviorRestriction(RequirePageConsentToLoadMediaRestriction);
816 #if ENABLE(VIDEO_TRACK)
817 // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
818 // disabled state when the element's resource selection algorithm last started".
819 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) {
820 m_textTracksWhenResourceSelectionBegan.clear();
822 for (unsigned i = 0; i < m_textTracks->length(); ++i) {
823 TextTrack* track = m_textTracks->item(i);
824 if (track->mode() != TextTrack::disabledKeyword())
825 m_textTracksWhenResourceSelectionBegan.append(track);
831 selectMediaResource();
834 void HTMLMediaElement::selectMediaResource()
836 LOG(Media, "HTMLMediaElement::selectMediaResource");
838 enum Mode { attribute, children };
840 // 3 - If the media element has a src attribute, then let mode be attribute.
841 Mode mode = attribute;
842 if (!fastHasAttribute(srcAttr)) {
844 for (node = firstChild(); node; node = node->nextSibling()) {
845 if (node->hasTagName(sourceTag))
849 // Otherwise, if the media element does not have a src attribute but has a source
850 // element child, then let mode be children and let candidate be the first such
851 // source element child in tree order.
854 m_nextChildNodeToConsider = node;
855 m_currentSourceNode = 0;
857 // Otherwise the media element has neither a src attribute nor a source element
858 // child: set the networkState to NETWORK_EMPTY, and abort these steps; the
859 // synchronous section ends.
860 m_loadState = WaitingForSource;
861 setShouldDelayLoadEvent(false);
862 m_networkState = NETWORK_EMPTY;
864 LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load");
869 // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event),
870 // and set its networkState to NETWORK_LOADING.
871 setShouldDelayLoadEvent(true);
872 m_networkState = NETWORK_LOADING;
874 // 5 - Queue a task to fire a simple event named loadstart at the media element.
875 scheduleEvent(eventNames().loadstartEvent);
877 // 6 - If mode is attribute, then run these substeps
878 if (mode == attribute) {
879 m_loadState = LoadingFromSrcAttr;
881 // If the src attribute's value is the empty string ... jump down to the failed step below
882 KURL mediaURL = getNonEmptyURLAttribute(srcAttr);
883 if (mediaURL.isEmpty()) {
884 mediaLoadingFailed(MediaPlayer::FormatError);
885 LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'");
889 if (!isSafeToLoadURL(mediaURL, Complain) || !dispatchBeforeLoadEvent(mediaURL.string())) {
890 mediaLoadingFailed(MediaPlayer::FormatError);
894 // No type or key system information is available when the url comes
895 // from the 'src' attribute so MediaPlayer
896 // will have to pick a media engine based on the file extension.
897 ContentType contentType((String()));
898 loadResource(mediaURL, contentType, String());
899 LOG(Media, "HTMLMediaElement::selectMediaResource, using 'src' attribute url");
903 // Otherwise, the source elements will be used
904 loadNextSourceChild();
907 void HTMLMediaElement::loadNextSourceChild()
909 ContentType contentType((String()));
911 KURL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain);
912 if (!mediaURL.isValid()) {
913 waitForSourceChange();
917 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
918 // Recreate the media player for the new url
922 m_loadState = LoadingFromSourceElement;
923 loadResource(mediaURL, contentType, keySystem);
926 #if !PLATFORM(CHROMIUM)
927 static KURL createFileURLForApplicationCacheResource(const String& path)
929 // KURL should have a function to create a url from a path, but it does not. This function
930 // is not suitable because KURL::setPath uses encodeWithURLEscapeSequences, which it notes
931 // does not correctly escape '#' and '?'. This function works for our purposes because
932 // app cache media files are always created with encodeForFileName(createCanonicalUUIDString()).
934 #if USE(CF) && PLATFORM(WIN)
935 RetainPtr<CFStringRef> cfPath(AdoptCF, path.createCFString());
936 RetainPtr<CFURLRef> cfURL(AdoptCF, CFURLCreateWithFileSystemPath(0, cfPath.get(), kCFURLWindowsPathStyle, false));
937 KURL url(cfURL.get());
941 url.setProtocol("file");
948 void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType, const String& keySystem)
950 ASSERT(isSafeToLoadURL(initialURL, Complain));
952 LOG(Media, "HTMLMediaElement::loadResource(%s, %s, %s)", urlForLogging(initialURL).utf8().data(), contentType.raw().utf8().data(), keySystem.utf8().data());
954 Frame* frame = document()->frame();
956 mediaLoadingFailed(MediaPlayer::FormatError);
960 KURL url = initialURL;
961 if (!frame->loader()->willLoadMediaElementURL(url)) {
962 mediaLoadingFailed(MediaPlayer::FormatError);
966 #if ENABLE(MEDIA_SOURCE)
967 // If this is a media source URL, make sure it is the one for this media element.
968 if (url.protocolIs(mediaSourceURLProtocol) && url != m_mediaSourceURL) {
969 mediaLoadingFailed(MediaPlayer::FormatError);
974 // The resource fetch algorithm
975 m_networkState = NETWORK_LOADING;
977 #if !PLATFORM(CHROMIUM)
978 // If the url should be loaded from the application cache, pass the url of the cached file
979 // to the media engine.
980 ApplicationCacheHost* cacheHost = frame->loader()->documentLoader()->applicationCacheHost();
981 ApplicationCacheResource* resource = 0;
982 if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource)) {
983 // Resources that are not present in the manifest will always fail to load (at least, after the
984 // cache has been primed the first time), making the testing of offline applications simpler.
985 if (!resource || resource->path().isEmpty()) {
986 mediaLoadingFailed(MediaPlayer::NetworkError);
992 // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app
993 // cache is an internal detail not exposed through the media element API.
996 #if !PLATFORM(CHROMIUM)
998 url = createFileURLForApplicationCacheResource(resource->path());
999 LOG(Media, "HTMLMediaElement::loadResource - will load from app cache -> %s", urlForLogging(url).utf8().data());
1003 LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLogging(m_currentSrc).utf8().data());
1005 #if ENABLE(MEDIA_STREAM)
1006 if (MediaStreamRegistry::registry().lookupMediaStreamDescriptor(url.string()))
1007 removeBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
1010 if (m_sendProgressEvents)
1011 startProgressEventTimer();
1013 Settings* settings = document()->settings();
1014 bool privateMode = !settings || settings->privateBrowsingEnabled();
1015 m_player->setPrivateBrowsingMode(privateMode);
1017 // Reset display mode to force a recalculation of what to show because we are resetting the player.
1018 setDisplayMode(Unknown);
1021 m_player->setPreload(m_preload);
1022 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1025 power_lock_state(POWER_STATE_NORMAL, 0);
1028 m_player->setPreservesPitch(m_webkitPreservesPitch);
1030 #if !ENABLE(TIZEN_GSTREAMER_AUDIO)
1031 if (fastHasAttribute(mutedAttr))
1036 if (!m_player->load(url, contentType, keySystem))
1037 mediaLoadingFailed(MediaPlayer::FormatError);
1039 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1043 // If there is no poster to display, allow the media engine to render video frames as soon as
1044 // they are available.
1045 updateDisplayState();
1048 renderer()->updateFromElement();
1051 #if ENABLE(VIDEO_TRACK)
1052 static bool trackIndexCompare(TextTrack* a,
1055 return a->trackIndex() - b->trackIndex() < 0;
1058 static bool eventTimeCueCompare(const std::pair<double, TextTrackCue*>& a,
1059 const std::pair<double, TextTrackCue*>& b)
1061 // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1063 if (a.first != b.first)
1064 return a.first - b.first < 0;
1066 // If the cues belong to different text tracks, it doesn't make sense to
1067 // compare the two tracks by the relative cue order, so return the relative
1069 if (a.second->track() != b.second->track())
1070 return trackIndexCompare(a.second->track(), b.second->track());
1072 // 12 - Further sort tasks in events that have the same time by the
1073 // relative text track cue order of the text track cues associated
1074 // with these tasks.
1075 return a.second->cueIndex() - b.second->cueIndex() < 0;
1079 void HTMLMediaElement::updateActiveTextTrackCues(float movieTime)
1081 LOG(Media, "HTMLMediaElement::updateActiveTextTracks");
1083 // 4.8.10.8 Playing the media resource
1085 // If the current playback position changes while the steps are running,
1086 // then the user agent must wait for the steps to complete, and then must
1087 // immediately rerun the steps.
1088 if (ignoreTrackDisplayUpdateRequests())
1091 // 1 - Let current cues be a list of cues, initialized to contain all the
1092 // cues of all the hidden, showing, or showing by default text tracks of the
1093 // media element (not the disabled ones) whose start times are less than or
1094 // equal to the current playback position and whose end times are greater
1095 // than the current playback position.
1096 Vector<CueIntervalTree::IntervalType> currentCues;
1098 // The user agent must synchronously unset [the text track cue active] flag
1099 // whenever ... the media element's readyState is changed back to HAVE_NOTHING.
1100 if (m_readyState != HAVE_NOTHING && m_player)
1101 currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
1103 Vector<CueIntervalTree::IntervalType> affectedCues;
1104 Vector<CueIntervalTree::IntervalType> previousCues;
1105 Vector<CueIntervalTree::IntervalType> missedCues;
1107 // 2 - Let other cues be a list of cues, initialized to contain all the cues
1108 // of hidden, showing, and showing by default text tracks of the media
1109 // element that are not present in current cues.
1110 previousCues = m_currentlyActiveCues;
1112 // 3 - Let last time be the current playback position at the time this
1113 // algorithm was last run for this media element, if this is not the first
1115 float lastTime = m_lastTextTrackUpdateTime;
1117 // 4 - If the current playback position has, since the last time this
1118 // algorithm was run, only changed through its usual monotonic increase
1119 // during normal playback, then let missed cues be the list of cues in other
1120 // cues whose start times are greater than or equal to last time and whose
1121 // end times are less than or equal to the current playback position.
1122 // Otherwise, let missed cues be an empty list.
1123 if (lastTime >= 0 && m_lastSeekTime < movieTime) {
1124 Vector<CueIntervalTree::IntervalType> potentiallySkippedCues =
1125 m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime));
1127 for (size_t i = 0; i < potentiallySkippedCues.size(); ++i) {
1128 float cueStartTime = potentiallySkippedCues[i].low();
1129 float cueEndTime = potentiallySkippedCues[i].high();
1131 // Consider cues that may have been missed since the last seek time.
1132 if (cueStartTime > max(m_lastSeekTime, lastTime) && cueEndTime < movieTime)
1133 missedCues.append(potentiallySkippedCues[i]);
1137 m_lastTextTrackUpdateTime = movieTime;
1139 // 5 - If the time was reached through the usual monotonic increase of the
1140 // current playback position during normal playback, and if the user agent
1141 // has not fired a timeupdate event at the element in the past 15 to 250ms
1142 // and is not still running event handlers for such an event, then the user
1143 // agent must queue a task to fire a simple event named timeupdate at the
1144 // element. (In the other cases, such as explicit seeks, relevant events get
1145 // fired as part of the overall process of changing the current playback
1147 if (m_lastSeekTime <= lastTime)
1148 scheduleTimeupdateEvent(false);
1150 // Explicitly cache vector sizes, as their content is constant from here.
1151 size_t currentCuesSize = currentCues.size();
1152 size_t missedCuesSize = missedCues.size();
1153 size_t previousCuesSize = previousCues.size();
1155 // 6 - If all of the cues in current cues have their text track cue active
1156 // flag set, none of the cues in other cues have their text track cue active
1157 // flag set, and missed cues is empty, then abort these steps.
1158 bool activeSetChanged = missedCuesSize;
1160 for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i)
1161 if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive())
1162 activeSetChanged = true;
1164 for (size_t i = 0; i < currentCuesSize; ++i) {
1165 currentCues[i].data()->updateDisplayTree(movieTime);
1167 if (!currentCues[i].data()->isActive())
1168 activeSetChanged = true;
1171 if (!activeSetChanged) {
1172 // Even though the active set has not changed, it is possible that the
1173 // the mode of a track has changed from 'hidden' to 'showing' and the
1174 // cues have not yet been rendered.
1175 if (hasMediaControls())
1176 mediaControls()->updateTextTrackDisplay();
1181 // 7 - If the time was reached through the usual monotonic increase of the
1182 // current playback position during normal playback, and there are cues in
1183 // other cues that have their text track cue pause-on-exi flag set and that
1184 // either have their text track cue active flag set or are also in missed
1185 // cues, then immediately pause the media element.
1186 for (size_t i = 0; !m_paused && i < previousCuesSize; ++i) {
1187 if (previousCues[i].data()->pauseOnExit()
1188 && previousCues[i].data()->isActive()
1189 && !currentCues.contains(previousCues[i]))
1193 for (size_t i = 0; !m_paused && i < missedCuesSize; ++i) {
1194 if (missedCues[i].data()->pauseOnExit())
1198 // 8 - Let events be a list of tasks, initially empty. Each task in this
1199 // list will be associated with a text track, a text track cue, and a time,
1200 // which are used to sort the list before the tasks are queued.
1201 Vector<std::pair<double, TextTrackCue*> > eventTasks;
1203 // 8 - Let affected tracks be a list of text tracks, initially empty.
1204 Vector<TextTrack*> affectedTracks;
1206 for (size_t i = 0; i < missedCuesSize; ++i) {
1207 // 9 - For each text track cue in missed cues, prepare an event named enter
1208 // for the TextTrackCue object with the text track cue start time.
1209 eventTasks.append(std::make_pair(missedCues[i].data()->startTime(),
1210 missedCues[i].data()));
1212 // 10 - For each text track [...] in missed cues, prepare an event
1213 // named exit for the TextTrackCue object with the with the later of
1214 // the text track cue end time and the text track cue start time.
1216 // Note: An explicit task is added only if the cue is NOT a zero or
1217 // negative length cue. Otherwise, the need for an exit event is
1218 // checked when these tasks are actually queued below. This doesn't
1219 // affect sorting events before dispatch either, because the exit
1220 // event has the same time as the enter event.
1221 if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime())
1222 eventTasks.append(std::make_pair(missedCues[i].data()->endTime(),
1223 missedCues[i].data()));
1226 for (size_t i = 0; i < previousCuesSize; ++i) {
1227 // 10 - For each text track cue in other cues that has its text
1228 // track cue active flag set prepare an event named exit for the
1229 // TextTrackCue object with the text track cue end time.
1230 if (!currentCues.contains(previousCues[i]))
1231 eventTasks.append(std::make_pair(previousCues[i].data()->endTime(),
1232 previousCues[i].data()));
1235 for (size_t i = 0; i < currentCuesSize; ++i) {
1236 // 11 - For each text track cue in current cues that does not have its
1237 // text track cue active flag set, prepare an event named enter for the
1238 // TextTrackCue object with the text track cue start time.
1239 if (!previousCues.contains(currentCues[i]))
1240 eventTasks.append(std::make_pair(currentCues[i].data()->startTime(),
1241 currentCues[i].data()));
1244 // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1246 nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);
1248 for (size_t i = 0; i < eventTasks.size(); ++i) {
1249 if (!affectedTracks.contains(eventTasks[i].second->track()))
1250 affectedTracks.append(eventTasks[i].second->track());
1252 // 13 - Queue each task in events, in list order.
1253 RefPtr<Event> event;
1255 // Each event in eventTasks may be either an enterEvent or an exitEvent,
1256 // depending on the time that is associated with the event. This
1257 // correctly identifies the type of the event, if the startTime is
1258 // less than the endTime in the cue.
1259 if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) {
1260 event = Event::create(eventNames().enterEvent, false, false);
1261 event->setTarget(eventTasks[i].second);
1262 m_asyncEventQueue->enqueueEvent(event.release());
1264 event = Event::create(eventNames().exitEvent, false, false);
1265 event->setTarget(eventTasks[i].second);
1266 m_asyncEventQueue->enqueueEvent(event.release());
1268 if (eventTasks[i].first == eventTasks[i].second->startTime())
1269 event = Event::create(eventNames().enterEvent, false, false);
1271 event = Event::create(eventNames().exitEvent, false, false);
1273 event->setTarget(eventTasks[i].second);
1274 m_asyncEventQueue->enqueueEvent(event.release());
1278 // 14 - Sort affected tracks in the same order as the text tracks appear in
1279 // the media element's list of text tracks, and remove duplicates.
1280 nonCopyingSort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare);
1282 // 15 - For each text track in affected tracks, in the list order, queue a
1283 // task to fire a simple event named cuechange at the TextTrack object, and, ...
1284 for (size_t i = 0; i < affectedTracks.size(); ++i) {
1285 RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
1286 event->setTarget(affectedTracks[i]);
1288 m_asyncEventQueue->enqueueEvent(event.release());
1290 // ... if the text track has a corresponding track element, to then fire a
1291 // simple event named cuechange at the track element as well.
1292 if (affectedTracks[i]->trackType() == TextTrack::TrackElement) {
1293 RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
1294 HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(affectedTracks[i])->trackElement();
1295 ASSERT(trackElement);
1296 event->setTarget(trackElement);
1298 m_asyncEventQueue->enqueueEvent(event.release());
1302 // 16 - Set the text track cue active flag of all the cues in the current
1303 // cues, and unset the text track cue active flag of all the cues in the
1305 for (size_t i = 0; i < currentCuesSize; ++i)
1306 currentCues[i].data()->setIsActive(true);
1308 for (size_t i = 0; i < previousCuesSize; ++i)
1309 if (!currentCues.contains(previousCues[i]))
1310 previousCues[i].data()->setIsActive(false);
1312 // Update the current active cues.
1313 m_currentlyActiveCues = currentCues;
1315 if (activeSetChanged && hasMediaControls())
1316 mediaControls()->updateTextTrackDisplay();
1319 bool HTMLMediaElement::textTracksAreReady() const
1321 // 4.8.10.12.1 Text track model
1323 // The text tracks of a media element are ready if all the text tracks whose mode was not
1324 // in the disabled state when the element's resource selection algorithm last started now
1325 // have a text track readiness state of loaded or failed to load.
1326 for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) {
1327 if (m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::Loading)
1334 void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track)
1336 if (m_player && m_textTracksWhenResourceSelectionBegan.contains(track)) {
1337 if (track->readinessState() != TextTrack::Loading)
1338 setReadyState(m_player->readyState());
1342 void HTMLMediaElement::textTrackModeChanged(TextTrack* track)
1344 if (track->trackType() == TextTrack::TrackElement) {
1345 // 4.8.10.12.3 Sourcing out-of-band text tracks
1346 // ... when a text track corresponding to a track element is created with text track
1347 // mode set to disabled and subsequently changes its text track mode to hidden, showing,
1348 // or showing by default for the first time, the user agent must immediately and synchronously
1349 // run the following algorithm ...
1351 for (Node* node = firstChild(); node; node = node->nextSibling()) {
1352 if (!node->hasTagName(trackTag))
1354 HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node);
1355 if (trackElement->track() != track)
1358 // Mark this track as "configured" so configureTextTracks won't change the mode again.
1359 trackElement->setHasBeenConfigured(true);
1360 if (track->mode() != TextTrack::disabledKeyword()) {
1361 if (trackElement->readyState() == HTMLTrackElement::LOADED)
1362 textTrackAddCues(track, track->cues());
1363 else if (trackElement->readyState() == HTMLTrackElement::NONE)
1364 trackElement->scheduleLoad();
1366 // If this is the first added track, create the list of text tracks.
1368 m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
1374 configureTextTrackDisplay();
1375 updateActiveTextTrackCues(currentTime());
1378 void HTMLMediaElement::textTrackKindChanged(TextTrack* track)
1380 if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword() && track->mode() == TextTrack::showingKeyword())
1381 track->setMode(TextTrack::hiddenKeyword());
1384 void HTMLMediaElement::textTrackAddCues(TextTrack*, const TextTrackCueList* cues)
1386 beginIgnoringTrackDisplayUpdateRequests();
1387 for (size_t i = 0; i < cues->length(); ++i)
1388 textTrackAddCue(cues->item(i)->track(), cues->item(i));
1389 endIgnoringTrackDisplayUpdateRequests();
1390 updateActiveTextTrackCues(currentTime());
1393 void HTMLMediaElement::textTrackRemoveCues(TextTrack*, const TextTrackCueList* cues)
1395 beginIgnoringTrackDisplayUpdateRequests();
1396 for (size_t i = 0; i < cues->length(); ++i)
1397 textTrackRemoveCue(cues->item(i)->track(), cues->item(i));
1398 endIgnoringTrackDisplayUpdateRequests();
1399 updateActiveTextTrackCues(currentTime());
1402 void HTMLMediaElement::textTrackAddCue(TextTrack*, PassRefPtr<TextTrackCue> cue)
1404 // Negative duration cues need be treated in the interval tree as
1405 // zero-length cues.
1406 double endTime = max(cue->startTime(), cue->endTime());
1408 m_cueTree.add(m_cueTree.createInterval(cue->startTime(), endTime, cue.get()));
1409 updateActiveTextTrackCues(currentTime());
1412 void HTMLMediaElement::textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue> cue)
1414 // Negative duration cues need to be treated in the interval tree as
1415 // zero-length cues.
1416 double endTime = max(cue->startTime(), cue->endTime());
1418 m_cueTree.remove(m_cueTree.createInterval(cue->startTime(), endTime, cue.get()));
1419 updateActiveTextTrackCues(currentTime());
1424 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidURLAction actionIfInvalid)
1426 if (!url.isValid()) {
1427 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLogging(url).utf8().data());
1431 Frame* frame = document()->frame();
1432 if (!frame || !document()->securityOrigin()->canDisplay(url)) {
1433 if (actionIfInvalid == Complain)
1434 FrameLoader::reportLocalLoadFailed(frame, url.string());
1435 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLogging(url).utf8().data());
1439 if (!document()->contentSecurityPolicy()->allowMediaFromSource(url)) {
1440 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> rejected by Content Security Policy", urlForLogging(url).utf8().data());
1447 void HTMLMediaElement::startProgressEventTimer()
1449 if (m_progressEventTimer.isActive())
1452 m_previousProgressTime = WTF::currentTime();
1453 // 350ms is not magic, it is in the spec!
1454 m_progressEventTimer.startRepeating(0.350);
1457 void HTMLMediaElement::waitForSourceChange()
1459 LOG(Media, "HTMLMediaElement::waitForSourceChange");
1461 stopPeriodicTimers();
1462 m_loadState = WaitingForSource;
1464 // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
1465 m_networkState = NETWORK_NO_SOURCE;
1467 // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1468 setShouldDelayLoadEvent(false);
1470 updateDisplayState();
1473 renderer()->updateFromElement();
1476 void HTMLMediaElement::noneSupported()
1478 LOG(Media, "HTMLMediaElement::noneSupported");
1480 stopPeriodicTimers();
1481 m_loadState = WaitingForSource;
1482 m_currentSourceNode = 0;
1485 // 6 - Reaching this step indicates that the media resource failed to load or that the given
1486 // URL could not be resolved. In one atomic operation, run the following steps:
1488 // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to
1489 // MEDIA_ERR_SRC_NOT_SUPPORTED.
1490 m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
1492 // 6.2 - Forget the media element's media-resource-specific text tracks.
1494 // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
1495 m_networkState = NETWORK_NO_SOURCE;
1497 // 7 - Queue a task to fire a simple event named error at the media element.
1498 scheduleEvent(eventNames().errorEvent);
1500 #if ENABLE(MEDIA_SOURCE)
1501 if (m_sourceState != SOURCE_CLOSED)
1502 setSourceState(SOURCE_CLOSED);
1505 // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1506 setShouldDelayLoadEvent(false);
1508 // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed,
1509 // the element won't attempt to load another resource.
1511 updateDisplayState();
1514 renderer()->updateFromElement();
1517 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
1519 LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code()));
1521 // 1 - The user agent should cancel the fetching process.
1522 stopPeriodicTimers();
1523 m_loadState = WaitingForSource;
1525 // 2 - Set the error attribute to a new MediaError object whose code attribute is
1526 // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
1529 // 3 - Queue a task to fire a simple event named error at the media element.
1530 scheduleEvent(eventNames().errorEvent);
1532 #if ENABLE(MEDIA_SOURCE)
1533 if (m_sourceState != SOURCE_CLOSED)
1534 setSourceState(SOURCE_CLOSED);
1537 // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
1538 // task to fire a simple event called emptied at the element.
1539 m_networkState = NETWORK_EMPTY;
1540 scheduleEvent(eventNames().emptiedEvent);
1542 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1543 setShouldDelayLoadEvent(false);
1545 // 6 - Abort the overall resource selection algorithm.
1546 m_currentSourceNode = 0;
1547 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1549 power_unlock_state(POWER_STATE_NORMAL);
1553 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
1555 LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks");
1556 m_asyncEventQueue->cancelAllEvents();
1558 for (Node* node = firstChild(); node; node = node->nextSibling()) {
1559 if (node->hasTagName(sourceTag))
1560 static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent();
1564 Document* HTMLMediaElement::mediaPlayerOwningDocument()
1566 Document* d = document();
1569 d = ownerDocument();
1574 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
1576 beginProcessingMediaPlayerCallback();
1577 setNetworkState(m_player->networkState());
1578 endProcessingMediaPlayerCallback();
1581 static String stringForNetworkState(MediaPlayer::NetworkState state)
1584 case MediaPlayer::Empty: return "Empty";
1585 case MediaPlayer::Idle: return "Idle";
1586 case MediaPlayer::Loading: return "Loading";
1587 case MediaPlayer::Loaded: return "Loaded";
1588 case MediaPlayer::FormatError: return "FormatError";
1589 case MediaPlayer::NetworkError: return "NetworkError";
1590 case MediaPlayer::DecodeError: return "DecodeError";
1591 default: return emptyString();
1595 void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error)
1597 stopPeriodicTimers();
1599 // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
1600 // <source> children, schedule the next one
1601 if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
1603 if (m_currentSourceNode)
1604 m_currentSourceNode->scheduleErrorEvent();
1606 LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed");
1608 if (havePotentialSourceChild()) {
1609 LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>");
1610 scheduleNextSourceChild();
1612 LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting");
1613 waitForSourceChange();
1619 if (error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA)
1620 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
1621 else if (error == MediaPlayer::DecodeError)
1622 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
1623 else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr)
1626 updateDisplayState();
1627 if (hasMediaControls()) {
1628 mediaControls()->reset();
1629 mediaControls()->reportedError();
1632 if (document()->page() && document()->page()->settings()->diagnosticLoggingEnabled())
1633 document()->page()->chrome()->client()->logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadingFailedKey(), stringForNetworkState(error), DiagnosticLoggingKeys::failKey());
1634 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1636 power_unlock_state(POWER_STATE_NORMAL);
1640 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
1642 LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState));
1644 if (state == MediaPlayer::Empty) {
1645 // Just update the cached state and leave, we can't do anything.
1646 m_networkState = NETWORK_EMPTY;
1650 if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
1651 mediaLoadingFailed(state);
1655 if (state == MediaPlayer::Idle) {
1656 if (m_networkState > NETWORK_IDLE) {
1657 changeNetworkStateFromLoadingToIdle();
1658 setShouldDelayLoadEvent(false);
1660 m_networkState = NETWORK_IDLE;
1664 if (state == MediaPlayer::Loading) {
1665 if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
1666 startProgressEventTimer();
1667 m_networkState = NETWORK_LOADING;
1670 if (state == MediaPlayer::Loaded) {
1671 if (m_networkState != NETWORK_IDLE)
1672 changeNetworkStateFromLoadingToIdle();
1673 m_completelyLoaded = true;
1676 if (hasMediaControls())
1677 mediaControls()->updateStatusDisplay();
1680 void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
1682 m_progressEventTimer.stop();
1683 if (hasMediaControls() && m_player->didLoadingProgress())
1684 mediaControls()->bufferingProgressed();
1686 // Schedule one last progress event so we guarantee that at least one is fired
1687 // for files that load very quickly.
1688 scheduleEvent(eventNames().progressEvent);
1689 scheduleEvent(eventNames().suspendEvent);
1690 m_networkState = NETWORK_IDLE;
1693 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
1695 beginProcessingMediaPlayerCallback();
1697 setReadyState(m_player->readyState());
1699 endProcessingMediaPlayerCallback();
1702 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
1704 LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState));
1706 // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
1707 bool wasPotentiallyPlaying = potentiallyPlaying();
1709 ReadyState oldState = m_readyState;
1710 ReadyState newState = static_cast<ReadyState>(state);
1712 #if ENABLE(VIDEO_TRACK)
1713 bool tracksAreReady = !RuntimeEnabledFeatures::webkitVideoTrackEnabled() || textTracksAreReady();
1715 if (newState == oldState && m_tracksAreReady == tracksAreReady)
1718 m_tracksAreReady = tracksAreReady;
1720 if (newState == oldState)
1722 bool tracksAreReady = true;
1726 m_readyState = newState;
1728 // If a media file has text tracks the readyState may not progress beyond HAVE_FUTURE_DATA until
1729 // the text tracks are ready, regardless of the state of the media file.
1730 if (newState <= HAVE_METADATA)
1731 m_readyState = newState;
1733 m_readyState = HAVE_CURRENT_DATA;
1736 if (oldState > m_readyStateMaximum)
1737 m_readyStateMaximum = oldState;
1739 if (m_networkState == NETWORK_EMPTY)
1743 // 4.8.10.9, step 11
1744 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
1745 scheduleEvent(eventNames().waitingEvent);
1747 // 4.8.10.10 step 14 & 15.
1748 if (m_readyState >= HAVE_CURRENT_DATA)
1751 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
1753 scheduleTimeupdateEvent(false);
1754 scheduleEvent(eventNames().waitingEvent);
1758 if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
1759 prepareMediaFragmentURI();
1760 scheduleEvent(eventNames().durationchangeEvent);
1761 scheduleEvent(eventNames().loadedmetadataEvent);
1762 if (hasMediaControls())
1763 mediaControls()->loadedMetadata();
1765 renderer()->updateFromElement();
1767 if (document()->page() && document()->page()->settings()->diagnosticLoggingEnabled())
1768 document()->page()->chrome()->client()->logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadedKey(), m_player->engineDescription(), DiagnosticLoggingKeys::noopKey());
1771 bool shouldUpdateDisplayState = false;
1773 if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
1774 m_haveFiredLoadedData = true;
1775 shouldUpdateDisplayState = true;
1776 scheduleEvent(eventNames().loadeddataEvent);
1777 setShouldDelayLoadEvent(false);
1778 applyMediaFragmentURI();
1781 bool isPotentiallyPlaying = potentiallyPlaying();
1782 if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) {
1783 scheduleEvent(eventNames().canplayEvent);
1784 if (isPotentiallyPlaying)
1785 scheduleEvent(eventNames().playingEvent);
1786 shouldUpdateDisplayState = true;
1789 if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) {
1790 if (oldState <= HAVE_CURRENT_DATA)
1791 scheduleEvent(eventNames().canplayEvent);
1793 scheduleEvent(eventNames().canplaythroughEvent);
1795 if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
1796 scheduleEvent(eventNames().playingEvent);
1798 if (m_autoplaying && m_paused && autoplay() && !document()->isSandboxed(SandboxAutomaticFeatures) && !userGestureRequiredForRateChange()) {
1800 invalidateCachedTime();
1801 scheduleEvent(eventNames().playEvent);
1802 scheduleEvent(eventNames().playingEvent);
1805 shouldUpdateDisplayState = true;
1808 if (shouldUpdateDisplayState) {
1809 updateDisplayState();
1810 if (hasMediaControls())
1811 mediaControls()->updateStatusDisplay();
1815 updateMediaController();
1816 #if ENABLE(VIDEO_TRACK)
1817 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
1818 updateActiveTextTrackCues(currentTime());
1822 #if ENABLE(MEDIA_SOURCE)
1823 void HTMLMediaElement::mediaPlayerSourceOpened()
1825 beginProcessingMediaPlayerCallback();
1827 setSourceState(SOURCE_OPEN);
1829 endProcessingMediaPlayerCallback();
1832 String HTMLMediaElement::mediaPlayerSourceURL() const
1834 return m_mediaSourceURL.string();
1837 bool HTMLMediaElement::isValidSourceId(const String& id, ExceptionCode& ec) const
1839 if (id.isNull() || id.isEmpty()) {
1840 ec = INVALID_ACCESS_ERR;
1844 if (!m_sourceIDs.contains(id)) {
1854 #if ENABLE(ENCRYPTED_MEDIA)
1855 void HTMLMediaElement::mediaPlayerKeyAdded(MediaPlayer*, const String& keySystem, const String& sessionId)
1857 MediaKeyEventInit initializer;
1858 initializer.keySystem = keySystem;
1859 initializer.sessionId = sessionId;
1860 initializer.bubbles = false;
1861 initializer.cancelable = false;
1863 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeyaddedEvent, initializer);
1864 event->setTarget(this);
1865 m_asyncEventQueue->enqueueEvent(event.release());
1868 void HTMLMediaElement::mediaPlayerKeyError(MediaPlayer*, const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode)
1870 MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
1871 switch (errorCode) {
1872 case MediaPlayerClient::UnknownError:
1873 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
1875 case MediaPlayerClient::ClientError:
1876 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
1878 case MediaPlayerClient::ServiceError:
1879 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE;
1881 case MediaPlayerClient::OutputError:
1882 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT;
1884 case MediaPlayerClient::HardwareChangeError:
1885 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE;
1887 case MediaPlayerClient::DomainError:
1888 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN;
1892 MediaKeyEventInit initializer;
1893 initializer.keySystem = keySystem;
1894 initializer.sessionId = sessionId;
1895 initializer.errorCode = MediaKeyError::create(mediaKeyErrorCode);
1896 initializer.systemCode = systemCode;
1897 initializer.bubbles = false;
1898 initializer.cancelable = false;
1900 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeyerrorEvent, initializer);
1901 event->setTarget(this);
1902 m_asyncEventQueue->enqueueEvent(event.release());
1905 void HTMLMediaElement::mediaPlayerKeyMessage(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength)
1907 MediaKeyEventInit initializer;
1908 initializer.keySystem = keySystem;
1909 initializer.sessionId = sessionId;
1910 initializer.message = Uint8Array::create(message, messageLength);
1911 initializer.bubbles = false;
1912 initializer.cancelable = false;
1914 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeymessageEvent, initializer);
1915 event->setTarget(this);
1916 m_asyncEventQueue->enqueueEvent(event.release());
1919 void HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength)
1921 MediaKeyEventInit initializer;
1922 initializer.keySystem = keySystem;
1923 initializer.sessionId = sessionId;
1924 initializer.initData = Uint8Array::create(initData, initDataLength);
1925 initializer.bubbles = false;
1926 initializer.cancelable = false;
1928 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitneedkeyEvent, initializer);
1929 event->setTarget(this);
1930 m_asyncEventQueue->enqueueEvent(event.release());
1934 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
1937 if (m_networkState != NETWORK_LOADING)
1940 double time = WTF::currentTime();
1941 double timedelta = time - m_previousProgressTime;
1943 if (m_player->didLoadingProgress()) {
1944 scheduleEvent(eventNames().progressEvent);
1945 m_previousProgressTime = time;
1946 m_sentStalledEvent = false;
1948 renderer()->updateFromElement();
1949 if (hasMediaControls())
1950 mediaControls()->bufferingProgressed();
1951 } else if (timedelta > 3.0 && !m_sentStalledEvent) {
1952 scheduleEvent(eventNames().stalledEvent);
1953 m_sentStalledEvent = true;
1954 setShouldDelayLoadEvent(false);
1958 void HTMLMediaElement::createShadowSubtree()
1960 ASSERT(!userAgentShadowRoot());
1961 ShadowRoot::create(this, ShadowRoot::UserAgentShadowRoot);
1964 void HTMLMediaElement::willAddAuthorShadowRoot()
1966 if (!userAgentShadowRoot())
1967 createShadowSubtree();
1970 void HTMLMediaElement::rewind(float timeDelta)
1972 LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta);
1975 setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e);
1978 void HTMLMediaElement::returnToRealtime()
1980 LOG(Media, "HTMLMediaElement::returnToRealtime");
1982 setCurrentTime(maxTimeSeekable(), e);
1985 void HTMLMediaElement::addPlayedRange(float start, float end)
1987 LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end);
1988 if (!m_playedTimeRanges)
1989 m_playedTimeRanges = TimeRanges::create();
1990 m_playedTimeRanges->add(start, end);
1993 bool HTMLMediaElement::supportsSave() const
1995 return m_player ? m_player->supportsSave() : false;
1998 bool HTMLMediaElement::supportsScanning() const
2000 return m_player ? m_player->supportsScanning() : false;
2003 void HTMLMediaElement::prepareToPlay()
2005 LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this);
2006 if (m_havePreparedToPlay)
2008 m_havePreparedToPlay = true;
2009 m_player->prepareToPlay();
2012 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
2014 LOG(Media, "HTMLMediaElement::seek(%f)", time);
2018 // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception.
2019 if (m_readyState == HAVE_NOTHING || !m_player) {
2020 ec = INVALID_STATE_ERR;
2024 // If the media engine has been told to postpone loading data, let it go ahead now.
2025 if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
2028 // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
2029 refreshCachedTime();
2030 float now = currentTime();
2032 // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
2033 // already running. Abort that other instance of the algorithm without waiting for the step that
2034 // it is running to complete.
2035 // Nothing specific to be done here.
2037 // 3 - Set the seeking IDL attribute to true.
2038 // The flag will be cleared when the engine tells us the time has actually changed.
2041 // 5 - If the new playback position is later than the end of the media resource, then let it be the end
2042 // of the media resource instead.
2043 time = min(time, duration());
2045 // 6 - If the new playback position is less than the earliest possible position, let it be that position instead.
2046 float earliestTime = m_player->startTime();
2047 time = max(time, earliestTime);
2049 // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
2050 // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
2051 // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
2052 // not generate a timechanged callback. This means m_seeking will never be cleared and we will never
2053 // fire a 'seeked' event.
2055 float mediaTime = m_player->mediaTimeForTimeValue(time);
2056 if (time != mediaTime)
2057 LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime);
2059 time = m_player->mediaTimeForTimeValue(time);
2061 // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the
2062 // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute
2063 // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
2064 // attribute then set the seeking IDL attribute to false and abort these steps.
2065 RefPtr<TimeRanges> seekableRanges = seekable();
2067 // Short circuit seeking to the current time by just firing the events if no seek is required.
2068 // Don't skip calling the media engine if we are in poster mode because a seek should always
2069 // cancel poster display.
2070 bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster);
2072 #if ENABLE(MEDIA_SOURCE)
2073 // Always notify the media engine of a seek if the source is not closed. This ensures that the source is
2074 // always in a flushed state when the 'seeking' event fires.
2075 if (m_sourceState != SOURCE_CLOSED)
2076 noSeekRequired = false;
2079 if (noSeekRequired) {
2081 scheduleEvent(eventNames().seekingEvent);
2082 scheduleTimeupdateEvent(false);
2083 scheduleEvent(eventNames().seekedEvent);
2088 time = seekableRanges->nearest(time);
2091 if (m_lastSeekTime < now)
2092 addPlayedRange(m_lastSeekTime, now);
2094 m_lastSeekTime = time;
2095 m_sentEndEvent = false;
2097 #if ENABLE(MEDIA_SOURCE)
2098 if (m_sourceState == SOURCE_ENDED)
2099 setSourceState(SOURCE_OPEN);
2102 // 8 - Set the current playback position to the given new playback position
2103 m_player->seek(time);
2105 // 9 - Queue a task to fire a simple event named seeking at the element.
2106 scheduleEvent(eventNames().seekingEvent);
2108 // 10 - Queue a task to fire a simple event named timeupdate at the element.
2109 scheduleTimeupdateEvent(false);
2111 // 11-15 are handled, if necessary, when the engine signals a readystate change.
2114 void HTMLMediaElement::finishSeek()
2116 LOG(Media, "HTMLMediaElement::finishSeek");
2118 // 4.8.10.9 Seeking step 14
2121 // 4.8.10.9 Seeking step 15
2122 scheduleEvent(eventNames().seekedEvent);
2124 setDisplayMode(Video);
2127 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
2129 return m_readyState;
2132 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
2134 return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
2137 bool HTMLMediaElement::hasAudio() const
2139 return m_player ? m_player->hasAudio() : false;
2142 bool HTMLMediaElement::seeking() const
2147 void HTMLMediaElement::refreshCachedTime() const
2149 m_cachedTime = m_player->currentTime();
2150 m_cachedTimeWallClockUpdateTime = WTF::currentTime();
2153 void HTMLMediaElement::invalidateCachedTime()
2155 LOG(Media, "HTMLMediaElement::invalidateCachedTime");
2157 // Don't try to cache movie time when playback first starts as the time reported by the engine
2158 // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
2160 static const double minimumTimePlayingBeforeCacheSnapshot = 0.5;
2162 m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot;
2163 m_cachedTime = MediaPlayer::invalidTime();
2167 float HTMLMediaElement::currentTime() const
2169 #if LOG_CACHED_TIME_WARNINGS
2170 static const double minCachedDeltaForWarning = 0.01;
2177 LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime);
2178 return m_lastSeekTime;
2181 if (m_cachedTime != MediaPlayer::invalidTime() && m_paused) {
2182 #if LOG_CACHED_TIME_WARNINGS
2183 float delta = m_cachedTime - m_player->currentTime();
2184 if (delta > minCachedDeltaForWarning)
2185 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta);
2187 return m_cachedTime;
2190 // Is it too soon use a cached time?
2191 double now = WTF::currentTime();
2192 double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
2194 if (maximumDurationToCacheMediaTime && m_cachedTime != MediaPlayer::invalidTime() && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) {
2195 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime;
2197 // Not too soon, use the cached time only if it hasn't expired.
2198 if (wallClockDelta < maximumDurationToCacheMediaTime) {
2199 float adjustedCacheTime = static_cast<float>(m_cachedTime + (m_playbackRate * wallClockDelta));
2201 #if LOG_CACHED_TIME_WARNINGS
2202 float delta = adjustedCacheTime - m_player->currentTime();
2203 if (delta > minCachedDeltaForWarning)
2204 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when playing", delta);
2206 return adjustedCacheTime;
2210 #if LOG_CACHED_TIME_WARNINGS
2211 if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != MediaPlayer::invalidTime()) {
2212 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime;
2213 float delta = m_cachedTime + (m_playbackRate * wallClockDelta) - m_player->currentTime();
2214 LOG(Media, "HTMLMediaElement::currentTime - cached time was %f seconds off of media time when it expired", delta);
2218 refreshCachedTime();
2220 return m_cachedTime;
2223 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
2225 if (m_mediaController) {
2226 ec = INVALID_STATE_ERR;
2232 float HTMLMediaElement::startTime() const
2236 return m_player->startTime();
2239 double HTMLMediaElement::initialTime() const
2241 if (m_fragmentStartTime != MediaPlayer::invalidTime())
2242 return m_fragmentStartTime;
2247 return m_player->initialTime();
2250 float HTMLMediaElement::duration() const
2252 if (m_player && m_readyState >= HAVE_METADATA)
2253 return m_player->duration();
2255 return numeric_limits<float>::quiet_NaN();
2258 bool HTMLMediaElement::paused() const
2263 float HTMLMediaElement::defaultPlaybackRate() const
2265 return m_defaultPlaybackRate;
2268 void HTMLMediaElement::setDefaultPlaybackRate(float rate)
2270 if (m_defaultPlaybackRate != rate) {
2271 m_defaultPlaybackRate = rate;
2272 scheduleEvent(eventNames().ratechangeEvent);
2276 float HTMLMediaElement::playbackRate() const
2278 return m_playbackRate;
2281 void HTMLMediaElement::setPlaybackRate(float rate)
2283 LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate);
2285 if (m_playbackRate != rate) {
2286 m_playbackRate = rate;
2287 invalidateCachedTime();
2288 scheduleEvent(eventNames().ratechangeEvent);
2291 if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController)
2292 m_player->setRate(rate);
2295 void HTMLMediaElement::updatePlaybackRate()
2297 float effectiveRate = m_mediaController ? m_mediaController->playbackRate() : m_playbackRate;
2298 if (m_player && potentiallyPlaying() && m_player->rate() != effectiveRate)
2299 m_player->setRate(effectiveRate);
2302 bool HTMLMediaElement::webkitPreservesPitch() const
2304 return m_webkitPreservesPitch;
2307 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
2309 LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch));
2311 m_webkitPreservesPitch = preservesPitch;
2316 m_player->setPreservesPitch(preservesPitch);
2319 bool HTMLMediaElement::ended() const
2321 // 4.8.10.8 Playing the media resource
2322 // The ended attribute must return true if the media element has ended
2323 // playback and the direction of playback is forwards, and false otherwise.
2324 return endedPlayback() && m_playbackRate > 0;
2327 bool HTMLMediaElement::autoplay() const
2329 return fastHasAttribute(autoplayAttr);
2332 void HTMLMediaElement::setAutoplay(bool b)
2334 LOG(Media, "HTMLMediaElement::setAutoplay(%s)", boolString(b));
2335 setBooleanAttribute(autoplayAttr, b);
2338 String HTMLMediaElement::preload() const
2340 switch (m_preload) {
2341 case MediaPlayer::None:
2344 case MediaPlayer::MetaData:
2347 case MediaPlayer::Auto:
2352 ASSERT_NOT_REACHED();
2356 void HTMLMediaElement::setPreload(const String& preload)
2358 LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data());
2359 setAttribute(preloadAttr, preload);
2362 void HTMLMediaElement::play()
2364 LOG(Media, "HTMLMediaElement::play()");
2366 if (userGestureRequiredForRateChange() && !ScriptController::processingUserGesture())
2368 if (ScriptController::processingUserGesture())
2369 removeBehaviorsRestrictionsAfterFirstUserGesture();
2371 Settings* settings = document()->settings();
2372 if (settings && settings->needsSiteSpecificQuirks() && m_dispatchingCanPlayEvent && !m_loadInitiatedByUserGesture) {
2373 // It should be impossible to be processing the canplay event while handling a user gesture
2374 // since it is dispatched asynchronously.
2375 ASSERT(!ScriptController::processingUserGesture());
2376 String host = document()->baseURL().host();
2377 if (host.endsWith(".npr.org", false) || equalIgnoringCase(host, "npr.org"))
2381 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2383 power_lock_state(POWER_STATE_NORMAL, 0);
2388 void HTMLMediaElement::playInternal()
2390 LOG(Media, "HTMLMediaElement::playInternal");
2392 // 4.8.10.9. Playing the media resource
2393 if (!m_player || m_networkState == NETWORK_EMPTY)
2394 scheduleLoad(MediaResource);
2396 if (endedPlayback()) {
2397 ExceptionCode unused;
2401 if (m_mediaController)
2402 m_mediaController->bringElementUpToSpeed(this);
2406 invalidateCachedTime();
2407 scheduleEvent(eventNames().playEvent);
2409 if (m_readyState <= HAVE_CURRENT_DATA)
2410 scheduleEvent(eventNames().waitingEvent);
2411 else if (m_readyState >= HAVE_FUTURE_DATA)
2412 scheduleEvent(eventNames().playingEvent);
2414 m_autoplaying = false;
2417 updateMediaController();
2420 void HTMLMediaElement::pause()
2422 LOG(Media, "HTMLMediaElement::pause()");
2424 if (userGestureRequiredForRateChange() && !ScriptController::processingUserGesture())
2427 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2429 power_unlock_state(POWER_STATE_NORMAL);
2435 void HTMLMediaElement::pauseInternal()
2437 LOG(Media, "HTMLMediaElement::pauseInternal");
2439 // 4.8.10.9. Playing the media resource
2440 if (!m_player || m_networkState == NETWORK_EMPTY)
2441 scheduleLoad(MediaResource);
2443 m_autoplaying = false;
2447 scheduleTimeupdateEvent(false);
2448 scheduleEvent(eventNames().pauseEvent);
2454 #if ENABLE(MEDIA_SOURCE)
2456 void HTMLMediaElement::webkitSourceAddId(const String& id, const String& type, ExceptionCode& ec)
2458 if (id.isNull() || id.isEmpty()) {
2459 ec = INVALID_ACCESS_ERR;
2463 if (m_sourceIDs.contains(id)) {
2464 ec = INVALID_STATE_ERR;
2468 if (type.isNull() || type.isEmpty()) {
2469 ec = INVALID_ACCESS_ERR;
2473 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
2474 ec = INVALID_STATE_ERR;
2478 ContentType contentType(type);
2479 Vector<String> codecs = contentType.codecs();
2481 if (!codecs.size()) {
2482 ec = NOT_SUPPORTED_ERR;
2486 switch (m_player->sourceAddId(id, contentType.type(), codecs)) {
2487 case MediaPlayer::Ok:
2488 m_sourceIDs.add(id);
2490 case MediaPlayer::NotSupported:
2491 ec = NOT_SUPPORTED_ERR;
2493 case MediaPlayer::ReachedIdLimit:
2494 ec = QUOTA_EXCEEDED_ERR;
2498 ASSERT_NOT_REACHED();
2501 void HTMLMediaElement::webkitSourceRemoveId(const String& id, ExceptionCode& ec)
2503 if (!isValidSourceId(id, ec))
2506 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState == SOURCE_CLOSED) {
2507 ec = INVALID_STATE_ERR;
2511 if (!m_player->sourceRemoveId(id))
2512 ASSERT_NOT_REACHED();
2514 m_sourceIDs.remove(id);
2517 PassRefPtr<TimeRanges> HTMLMediaElement::webkitSourceBuffered(const String& id, ExceptionCode& ec)
2519 if (!isValidSourceId(id, ec))
2522 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState == SOURCE_CLOSED) {
2523 ec = INVALID_STATE_ERR;
2527 return m_player->sourceBuffered(id);
2530 void HTMLMediaElement::webkitSourceAppend(const String& id, PassRefPtr<Uint8Array> data, ExceptionCode& ec)
2532 if (!isValidSourceId(id, ec))
2535 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
2536 ec = INVALID_STATE_ERR;
2541 ec = INVALID_ACCESS_ERR;
2545 if (!data->length())
2548 if (!m_player->sourceAppend(id, data->data(), data->length())) {
2554 void HTMLMediaElement::webkitSourceAbort(const String& id, ExceptionCode& ec)
2556 if (!isValidSourceId(id, ec))
2559 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
2560 ec = INVALID_STATE_ERR;
2564 if (!m_player->sourceAbort(id))
2565 ASSERT_NOT_REACHED();
2568 void HTMLMediaElement::webkitSourceEndOfStream(unsigned short status, ExceptionCode& ec)
2570 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
2571 ec = INVALID_STATE_ERR;
2575 MediaPlayer::EndOfStreamStatus eosStatus = MediaPlayer::EosNoError;
2579 eosStatus = MediaPlayer::EosNoError;
2581 case EOS_NETWORK_ERR:
2582 eosStatus = MediaPlayer::EosNetworkError;
2584 case EOS_DECODE_ERR:
2585 eosStatus = MediaPlayer::EosDecodeError;
2592 setSourceState(SOURCE_ENDED);
2593 m_player->sourceEndOfStream(eosStatus);
2596 HTMLMediaElement::SourceState HTMLMediaElement::webkitSourceState() const
2598 return m_sourceState;
2601 void HTMLMediaElement::setSourceState(SourceState state)
2603 SourceState oldState = m_sourceState;
2604 m_sourceState = static_cast<SourceState>(state);
2606 if (m_sourceState == oldState)
2609 if (m_sourceState == SOURCE_CLOSED) {
2610 m_sourceIDs.clear();
2611 scheduleEvent(eventNames().webkitsourcecloseEvent);
2615 if (oldState == SOURCE_OPEN && m_sourceState == SOURCE_ENDED) {
2616 scheduleEvent(eventNames().webkitsourceendedEvent);
2620 if (m_sourceState == SOURCE_OPEN) {
2621 scheduleEvent(eventNames().webkitsourceopenEvent);
2627 #if ENABLE(ENCRYPTED_MEDIA)
2628 void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionCode& ec)
2630 if (keySystem.isEmpty()) {
2636 ec = INVALID_STATE_ERR;
2640 const unsigned char* initDataPointer = 0;
2641 unsigned initDataLength = 0;
2643 initDataPointer = initData->data();
2644 initDataLength = initData->length();
2647 MediaPlayer::MediaKeyException result = m_player->generateKeyRequest(keySystem, initDataPointer, initDataLength);
2648 ec = exceptionCodeForMediaKeyException(result);
2651 void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, ExceptionCode& ec)
2653 webkitGenerateKeyRequest(keySystem, Uint8Array::create(0), ec);
2656 void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionCode& ec)
2658 if (keySystem.isEmpty()) {
2668 if (!key->length()) {
2669 ec = TYPE_MISMATCH_ERR;
2674 ec = INVALID_STATE_ERR;
2678 const unsigned char* initDataPointer = 0;
2679 unsigned initDataLength = 0;
2681 initDataPointer = initData->data();
2682 initDataLength = initData->length();
2685 MediaPlayer::MediaKeyException result = m_player->addKey(keySystem, key->data(), key->length(), initDataPointer, initDataLength, sessionId);
2686 ec = exceptionCodeForMediaKeyException(result);
2689 void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, ExceptionCode& ec)
2691 webkitAddKey(keySystem, key, Uint8Array::create(0), String(), ec);
2694 void HTMLMediaElement::webkitCancelKeyRequest(const String& keySystem, const String& sessionId, ExceptionCode& ec)
2696 if (keySystem.isEmpty()) {
2702 ec = INVALID_STATE_ERR;
2706 MediaPlayer::MediaKeyException result = m_player->cancelKeyRequest(keySystem, sessionId);
2707 ec = exceptionCodeForMediaKeyException(result);
2712 bool HTMLMediaElement::loop() const
2714 return fastHasAttribute(loopAttr);
2717 void HTMLMediaElement::setLoop(bool b)
2719 LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b));
2720 setBooleanAttribute(loopAttr, b);
2722 updateDisableSleep();
2726 bool HTMLMediaElement::controls() const
2728 Frame* frame = document()->frame();
2730 // always show controls when scripting is disabled
2731 if (frame && !frame->script()->canExecuteScripts(NotAboutToExecuteScript))
2734 // always show controls for video when fullscreen playback is required.
2735 if (isVideo() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback())
2738 // Always show controls when in full screen mode.
2742 return fastHasAttribute(controlsAttr);
2745 void HTMLMediaElement::setControls(bool b)
2747 LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b));
2748 setBooleanAttribute(controlsAttr, b);
2751 float HTMLMediaElement::volume() const
2756 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
2758 LOG(Media, "HTMLMediaElement::setVolume(%f)", vol);
2760 if (vol < 0.0f || vol > 1.0f) {
2761 ec = INDEX_SIZE_ERR;
2765 if (m_volume != vol) {
2768 scheduleEvent(eventNames().volumechangeEvent);
2772 bool HTMLMediaElement::muted() const
2777 void HTMLMediaElement::setMuted(bool muted)
2779 LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted));
2781 if (m_muted != muted) {
2783 // Avoid recursion when the player reports volume changes.
2784 if (!processingMediaPlayerCallback()) {
2786 m_player->setMuted(m_muted);
2787 if (hasMediaControls())
2788 mediaControls()->changedMute();
2791 scheduleEvent(eventNames().volumechangeEvent);
2795 void HTMLMediaElement::togglePlayState()
2797 LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay()));
2799 // We can safely call the internal play/pause methods, which don't check restrictions, because
2800 // this method is only called from the built-in media controller
2802 updatePlaybackRate();
2808 void HTMLMediaElement::beginScrubbing()
2810 LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused()));
2814 // Because a media element stays in non-paused state when it reaches end, playback resumes
2815 // when the slider is dragged from the end to another position unless we pause first. Do
2816 // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
2819 // Not at the end but we still want to pause playback so the media engine doesn't try to
2820 // continue playing during scrubbing. Pause without generating an event as we will
2821 // unpause after scrubbing finishes.
2822 setPausedInternal(true);
2827 void HTMLMediaElement::endScrubbing()
2829 LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal));
2831 if (m_pausedInternal)
2832 setPausedInternal(false);
2835 // The spec says to fire periodic timeupdate events (those sent while playing) every
2836 // "15 to 250ms", we choose the slowest frequency
2837 static const double maxTimeupdateEventFrequency = 0.25;
2839 void HTMLMediaElement::startPlaybackProgressTimer()
2841 if (m_playbackProgressTimer.isActive())
2844 m_previousProgressTime = WTF::currentTime();
2845 m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
2848 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
2852 if (m_fragmentEndTime != MediaPlayer::invalidTime() && currentTime() >= m_fragmentEndTime && m_playbackRate > 0) {
2853 m_fragmentEndTime = MediaPlayer::invalidTime();
2854 if (!m_mediaController && !m_paused) {
2855 // changes paused to true and fires a simple event named pause at the media element.
2860 scheduleTimeupdateEvent(true);
2862 if (!m_playbackRate)
2865 if (!m_paused && hasMediaControls())
2866 mediaControls()->playbackProgressed();
2868 #if ENABLE(VIDEO_TRACK)
2869 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2870 updateActiveTextTrackCues(currentTime());
2874 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
2876 double now = WTF::currentTime();
2877 double timedelta = now - m_lastTimeUpdateEventWallTime;
2879 // throttle the periodic events
2880 if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
2883 // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
2884 // event at a given time so filter here
2885 float movieTime = currentTime();
2886 if (movieTime != m_lastTimeUpdateEventMovieTime) {
2887 scheduleEvent(eventNames().timeupdateEvent);
2888 m_lastTimeUpdateEventWallTime = now;
2889 m_lastTimeUpdateEventMovieTime = movieTime;
2893 bool HTMLMediaElement::canPlay() const
2895 return paused() || ended() || m_readyState < HAVE_METADATA;
2898 float HTMLMediaElement::percentLoaded() const
2902 float duration = m_player->duration();
2904 if (!duration || isinf(duration))
2908 RefPtr<TimeRanges> timeRanges = m_player->buffered();
2909 for (unsigned i = 0; i < timeRanges->length(); ++i) {
2910 ExceptionCode ignoredException;
2911 float start = timeRanges->start(i, ignoredException);
2912 float end = timeRanges->end(i, ignoredException);
2913 buffered += end - start;
2915 return buffered / duration;
2918 #if ENABLE(VIDEO_TRACK)
2919 PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const String& kind, const String& label, const String& language, ExceptionCode& ec)
2921 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2924 // 4.8.10.12.4 Text track API
2925 // The addTextTrack(kind, label, language) method of media elements, when invoked, must run the following steps:
2927 // 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps
2928 if (!TextTrack::isValidKindKeyword(kind)) {
2933 // 2. If the label argument was omitted, let label be the empty string.
2934 // 3. If the language argument was omitted, let language be the empty string.
2935 // 4. Create a new TextTrack object.
2937 // 5. Create a new text track corresponding to the new object, and set its text track kind to kind, its text
2938 // track label to label, its text track language to language...
2939 RefPtr<TextTrack> textTrack = TextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, kind, label, language);
2941 // Note, due to side effects when changing track parameters, we have to
2942 // first append the track to the text track list.
2944 // 6. Add the new text track to the media element's list of text tracks.
2945 textTracks()->append(textTrack);
2947 // ... its text track readiness state to the text track loaded state ...
2948 textTrack->setReadinessState(TextTrack::Loaded);
2950 // ... its text track mode to the text track hidden mode, and its text track list of cues to an empty list ...
2951 textTrack->setMode(TextTrack::hiddenKeyword());
2953 return textTrack.release();
2956 TextTrackList* HTMLMediaElement::textTracks()
2958 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2962 m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
2964 return m_textTracks.get();
2967 HTMLTrackElement* HTMLMediaElement::showingTrackWithSameKind(HTMLTrackElement* trackElement) const
2969 for (Node* node = firstChild(); node; node = node->nextSibling()) {
2970 if (trackElement == node)
2972 if (!node->hasTagName(trackTag))
2975 HTMLTrackElement* showingTrack = static_cast<HTMLTrackElement*>(node);
2976 if (showingTrack->kind() == trackElement->kind() && showingTrack->track()->mode() == TextTrack::showingKeyword())
2977 return showingTrack;
2983 void HTMLMediaElement::didAddTrack(HTMLTrackElement* trackElement)
2985 ASSERT(trackElement->hasTagName(trackTag));
2987 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2990 // 4.8.10.12.3 Sourcing out-of-band text tracks
2991 // When a track element's parent element changes and the new parent is a media element,
2992 // then the user agent must add the track element's corresponding text track to the
2993 // media element's list of text tracks ... [continues in TextTrackList::append]
2994 RefPtr<TextTrack> textTrack = trackElement->track();
2998 textTracks()->append(textTrack);
3000 // Do not schedule the track loading until parsing finishes so we don't start before all tracks
3001 // in the markup have been added.
3002 if (!m_parsingInProgress)
3003 scheduleLoad(TextTrackResource);
3006 void HTMLMediaElement::willRemoveTrack(HTMLTrackElement* trackElement)
3008 ASSERT(trackElement->hasTagName(trackTag));
3010 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3014 if (trackElement->hasTagName(trackTag)) {
3015 KURL url = trackElement->getNonEmptyURLAttribute(srcAttr);
3016 LOG(Media, "HTMLMediaElement::willRemoveTrack - 'src' is %s", urlForLogging(url).utf8().data());
3020 trackElement->setHasBeenConfigured(false);
3025 RefPtr<TextTrack> textTrack = trackElement->track();
3029 // 4.8.10.12.3 Sourcing out-of-band text tracks
3030 // When a track element's parent element changes and the old parent was a media element,
3031 // then the user agent must remove the track element's corresponding text track from the
3032 // media element's list of text tracks.
3033 m_textTracks->remove(textTrack.get());
3034 size_t index = m_textTracksWhenResourceSelectionBegan.find(textTrack.get());
3035 if (index != notFound)
3036 m_textTracksWhenResourceSelectionBegan.remove(index);
3039 bool HTMLMediaElement::userIsInterestedInThisLanguage(const String&) const
3041 // FIXME: check the user's language preference - bugs.webkit.org/show_bug.cgi?id=74121
3045 bool HTMLMediaElement::userIsInterestedInThisTrackKind(String kind) const
3047 // If ... the user has indicated an interest in having a track with this text track kind, text track language, ...
3048 if (m_disableCaptions)
3051 Settings* settings = document()->settings();
3055 if (kind == TextTrack::subtitlesKeyword())
3056 return settings->shouldDisplaySubtitles() || m_closedCaptionsVisible;
3057 if (kind == TextTrack::captionsKeyword())
3058 return settings->shouldDisplayCaptions() || m_closedCaptionsVisible;
3059 if (kind == TextTrack::descriptionsKeyword())
3060 return settings->shouldDisplayTextDescriptions() || m_closedCaptionsVisible;
3065 void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group) const
3067 ASSERT(group.tracks.size());
3069 String bestMatchingLanguage;
3070 if (group.hasSrcLang) {
3071 Vector<String> languages;
3072 languages.reserveInitialCapacity(group.tracks.size());
3073 for (size_t i = 0; i < group.tracks.size(); ++i) {
3074 String srcLanguage = group.tracks[i]->track()->language();
3075 if (srcLanguage.length())
3076 languages.append(srcLanguage);
3078 bestMatchingLanguage = preferredLanguageFromList(languages);
3081 // First, find the track in the group that should be enabled (if any).
3082 HTMLTrackElement* trackElementToEnable = 0;
3083 HTMLTrackElement* defaultTrack = 0;
3084 HTMLTrackElement* fallbackTrack = 0;
3085 for (size_t i = 0; !trackElementToEnable && i < group.tracks.size(); ++i) {
3086 HTMLTrackElement* trackElement = group.tracks[i];
3087 RefPtr<TextTrack> textTrack = trackElement->track();
3089 if (userIsInterestedInThisTrackKind(textTrack->kind())) {
3090 // * If the text track kind is { [subtitles or captions] [descriptions] } and the user has indicated an interest in having a
3091 // track with this text track kind, text track language, and text track label enabled, and there is no
3092 // other text track in the media element's list of text tracks with a text track kind of either subtitles
3093 // or captions whose text track mode is showing
3095 // * If the text track kind is chapters and the text track language is one that the user agent has reason
3096 // to believe is appropriate for the user, and there is no other text track in the media element's list of
3097 // text tracks with a text track kind of chapters whose text track mode is showing
3098 // Let the text track mode be showing.
3099 if (bestMatchingLanguage.length()) {
3100 if (textTrack->language() == bestMatchingLanguage)
3101 trackElementToEnable = trackElement;
3102 } else if (trackElement->isDefault()) {
3103 // The user is interested in this type of track, but their language preference doesn't match any track so we will
3104 // enable the 'default' track.
3105 defaultTrack = trackElement;
3108 // Remember the first track that doesn't match language or have 'default' to potentially use as fallback.
3110 fallbackTrack = trackElement;
3111 } else if (!group.visibleTrack && !defaultTrack && trackElement->isDefault()) {
3112 // * If the track element has a default attribute specified, and there is no other text track in the media
3113 // element's list of text tracks whose text track mode is showing or showing by default
3114 // Let the text track mode be showing by default.
3115 defaultTrack = trackElement;
3119 if (!trackElementToEnable && defaultTrack)
3120 trackElementToEnable = defaultTrack;
3122 // If no track matches the user's preferred language and non was marked 'default', enable the first track
3123 // because the user has explicitly stated a preference for this kind of track.
3124 if (!trackElementToEnable && fallbackTrack)
3125 trackElementToEnable = fallbackTrack;
3127 for (size_t i = 0; i < group.tracks.size(); ++i) {
3128 HTMLTrackElement* trackElement = group.tracks[i];
3129 RefPtr<TextTrack> textTrack = trackElement->track();
3131 if (trackElementToEnable == trackElement) {
3132 textTrack->setMode(TextTrack::showingKeyword());
3133 if (defaultTrack == trackElement)
3134 textTrack->setShowingByDefault(true);
3136 if (textTrack->showingByDefault()) {
3137 // If there is a text track in the media element's list of text tracks whose text track
3138 // mode is showing by default, the user agent must furthermore change that text track's
3139 // text track mode to hidden.
3140 textTrack->setShowingByDefault(false);
3141 textTrack->setMode(TextTrack::hiddenKeyword());
3143 textTrack->setMode(TextTrack::disabledKeyword());
3147 if (trackElementToEnable && group.defaultTrack && group.defaultTrack != trackElementToEnable) {
3148 RefPtr<TextTrack> textTrack = group.defaultTrack->track();
3149 if (textTrack && textTrack->showingByDefault()) {
3150 textTrack->setShowingByDefault(false);
3151 textTrack->setMode(TextTrack::hiddenKeyword());
3156 void HTMLMediaElement::configureTextTracks()
3158 TrackGroup captionAndSubtitleTracks(TrackGroup::CaptionsAndSubtitles);
3159 TrackGroup descriptionTracks(TrackGroup::Description);
3160 TrackGroup chapterTracks(TrackGroup::Chapter);
3161 TrackGroup metadataTracks(TrackGroup::Metadata);
3162 TrackGroup otherTracks(TrackGroup::Other);
3164 for (Node* node = firstChild(); node; node = node->nextSibling()) {
3165 if (!node->hasTagName(trackTag))
3168 HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node);
3169 RefPtr<TextTrack> textTrack = trackElement->track();
3173 String kind = textTrack->kind();
3174 TrackGroup* currentGroup;
3175 if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword())
3176 currentGroup = &captionAndSubtitleTracks;
3177 else if (kind == TextTrack::descriptionsKeyword())
3178 currentGroup = &descriptionTracks;
3179 else if (kind == TextTrack::chaptersKeyword())
3180 currentGroup = &chapterTracks;
3181 else if (kind == TextTrack::metadataKeyword())
3182 currentGroup = &metadataTracks;
3184 currentGroup = &otherTracks;
3186 if (!currentGroup->visibleTrack && textTrack->mode() == TextTrack::showingKeyword())
3187 currentGroup->visibleTrack = trackElement;
3188 if (!currentGroup->defaultTrack && trackElement->isDefault())
3189 currentGroup->defaultTrack = trackElement;
3191 // Do not add this track to the group if it has already been automatically configured
3192 // as we only want to call configureTextTrack once per track so that adding another
3193 // track after the initial configuration doesn't reconfigure every track - only those
3194 // that should be changed by the new addition. For example all metadata tracks are
3195 // disabled by default, and we don't want a track that has been enabled by script
3196 // to be disabled automatically when a new metadata track is added later.
3197 if (trackElement->hasBeenConfigured())
3200 if (textTrack->language().length())
3201 currentGroup->hasSrcLang = true;
3202 currentGroup->tracks.append(trackElement);
3205 if (captionAndSubtitleTracks.tracks.size())
3206 configureTextTrackGroup(captionAndSubtitleTracks);
3207 if (descriptionTracks.tracks.size())
3208 configureTextTrackGroup(descriptionTracks);
3209 if (chapterTracks.tracks.size())
3210 configureTextTrackGroup(chapterTracks);
3211 if (metadataTracks.tracks.size())
3212 configureTextTrackGroup(metadataTracks);
3213 if (otherTracks.tracks.size())
3214 configureTextTrackGroup(otherTracks);
3218 bool HTMLMediaElement::havePotentialSourceChild()
3220 // Stash the current <source> node and next nodes so we can restore them after checking
3221 // to see there is another potential.
3222 RefPtr<HTMLSourceElement> currentSourceNode = m_currentSourceNode;
3223 RefPtr<Node> nextNode = m_nextChildNodeToConsider;
3225 KURL nextURL = selectNextSourceChild(0, 0, DoNothing);
3227 m_currentSourceNode = currentSourceNode;
3228 m_nextChildNodeToConsider = nextNode;
3230 return nextURL.isValid();
3233 KURL HTMLMediaElement::selectNextSourceChild(ContentType* contentType, String* keySystem, InvalidURLAction actionIfInvalid)
3236 // Don't log if this was just called to find out if there are any valid <source> elements.
3237 bool shouldLog = actionIfInvalid != DoNothing;
3239 LOG(Media, "HTMLMediaElement::selectNextSourceChild");
3242 if (!m_nextChildNodeToConsider) {
3245 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\"");
3252 HTMLSourceElement* source = 0;
3255 bool lookingForStartNode = m_nextChildNodeToConsider;
3256 bool canUseSourceElement = false;
3257 bool okToLoadSourceURL;
3259 NodeVector potentialSourceNodes;
3260 getChildNodes(this, potentialSourceNodes);
3262 for (unsigned i = 0; !canUseSourceElement && i < potentialSourceNodes.size(); ++i) {
3263 node = potentialSourceNodes[i].get();
3264 if (lookingForStartNode && m_nextChildNodeToConsider != node)
3266 lookingForStartNode = false;
3268 if (!node->hasTagName(sourceTag))
3270 if (node->parentNode() != this)
3273 source = static_cast<HTMLSourceElement*>(node);
3275 // If candidate does not have a src attribute, or if its src attribute's value is the empty string ... jump down to the failed step below
3276 mediaURL = source->getNonEmptyURLAttribute(srcAttr);
3279 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLogging(mediaURL).utf8().data());
3281 if (mediaURL.isEmpty())
3284 if (source->fastHasAttribute(mediaAttr)) {
3285 MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
3286 RefPtr<MediaQuerySet> media = MediaQuerySet::createAllowingDescriptionSyntax(source->media());
3289 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data());
3291 if (!screenEval.eval(media.get()))
3295 type = source->type();
3296 // FIXME(82965): Add support for keySystem in <source> and set system from source.
3297 if (type.isEmpty() && mediaURL.protocolIsData())
3298 type = mimeTypeFromDataURL(mediaURL);
3299 if (!type.isEmpty() || !system.isEmpty()) {
3302 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is '%s' - key system is '%s'", type.utf8().data(), system.utf8().data());
3304 if (!MediaPlayer::supportsType(ContentType(type), system, mediaURL, this))
3308 // Is it safe to load this url?
3309 okToLoadSourceURL = isSafeToLoadURL(mediaURL, actionIfInvalid) && dispatchBeforeLoadEvent(mediaURL.string());
3311 // A 'beforeload' event handler can mutate the DOM, so check to see if the source element is still a child node.
3312 if (node->parentNode() != this) {
3313 LOG(Media, "HTMLMediaElement::selectNextSourceChild : 'beforeload' removed current element");
3318 if (!okToLoadSourceURL)
3321 // Making it this far means the <source> looks reasonable.
3322 canUseSourceElement = true;
3325 if (!canUseSourceElement && actionIfInvalid == Complain && source)
3326 source->scheduleErrorEvent();
3329 if (canUseSourceElement) {
3331 *contentType = ContentType(type);
3333 *keySystem = system;
3334 m_currentSourceNode = source;
3335 m_nextChildNodeToConsider = source->nextSibling();
3337 m_currentSourceNode = 0;
3338 m_nextChildNodeToConsider = 0;
3343 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode.get(), canUseSourceElement ? urlForLogging(mediaURL).utf8().data() : "");
3345 return canUseSourceElement ? mediaURL : KURL();
3348 void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
3350 LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source);
3353 if (source->hasTagName(sourceTag)) {
3354 KURL url = source->getNonEmptyURLAttribute(srcAttr);
3355 LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLogging(url).utf8().data());
3359 // We should only consider a <source> element when there is not src attribute at all.
3360 if (fastHasAttribute(srcAttr))
3363 // 4.8.8 - If a source element is inserted as a child of a media element that has no src
3364 // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke
3365 // the media element's resource selection algorithm.
3366 if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
3367 scheduleLoad(MediaResource);
3368 m_nextChildNodeToConsider = source;
3372 if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) {
3373 LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source");
3374 m_nextChildNodeToConsider = source;
3378 if (m_nextChildNodeToConsider)
3381 // 4.8.9.5, resource selection algorithm, source elements section:
3382 // 21. Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
3383 // 22. Asynchronously await a stable state...
3384 // 23. Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case
3385 // it hasn't been fired yet).
3386 setShouldDelayLoadEvent(true);
3388 // 24. Set the networkState back to NETWORK_LOADING.
3389 m_networkState = NETWORK_LOADING;
3391 // 25. Jump back to the find next candidate step above.
3392 m_nextChildNodeToConsider = source;
3393 scheduleNextSourceChild();
3396 void HTMLMediaElement::sourceWasRemoved(HTMLSourceElement* source)
3398 LOG(Media, "HTMLMediaElement::sourceWasRemoved(%p)", source);
3401 if (source->hasTagName(sourceTag)) {
3402 KURL url = source->getNonEmptyURLAttribute(srcAttr);
3403 LOG(Media, "HTMLMediaElement::sourceWasRemoved - 'src' is %s", urlForLogging(url).utf8().data());
3407 if (source != m_currentSourceNode && source != m_nextChildNodeToConsider)
3410 if (source == m_nextChildNodeToConsider) {
3411 if (m_currentSourceNode)
3412 m_nextChildNodeToConsider = m_currentSourceNode->nextSibling();
3413 LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider.get());
3414 } else if (source == m_currentSourceNode) {
3415 // Clear the current source node pointer, but don't change the movie as the spec says:
3416 // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already
3417 // inserted in a video or audio element will have no effect.
3418 m_currentSourceNode = 0;
3419 LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0");
3423 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
3425 LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
3427 #if ENABLE(VIDEO_TRACK)
3428 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3429 updateActiveTextTrackCues(currentTime());
3432 beginProcessingMediaPlayerCallback();
3434 invalidateCachedTime();
3436 // 4.8.10.9 step 14 & 15. Needed if no ReadyState change is associated with the seek.
3437 if (m_seeking && m_readyState >= HAVE_CURRENT_DATA)
3440 // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity,
3441 // it will only queue a 'timeupdate' event if we haven't already posted one at the current
3443 scheduleTimeupdateEvent(false);
3445 float now = currentTime();
3446 float dur = duration();
3448 // When the current playback position reaches the end of the media resource when the direction of
3449 // playback is forwards, then the user agent must follow these steps:
3450 if (!isnan(dur) && dur && now >= dur && m_playbackRate > 0) {
3451 // If the media element has a loop attribute specified and does not have a current media controller,
3452 if (loop() && !m_mediaController) {
3453 ExceptionCode ignoredException;
3454 m_sentEndEvent = false;
3455 // then seek to the earliest possible position of the media resource and abort these steps.
3456 seek(startTime(), ignoredException);
3458 // If the media element does not have a current media controller, and the media element
3459 // has still ended playback, and the direction of playback is still forwards, and paused
3461 if (!m_mediaController && !m_paused) {
3462 // changes paused to true and fires a simple event named pause at the media element.
3464 scheduleEvent(eventNames().pauseEvent);
3466 // Queue a task to fire a simple event named ended at the media element.
3467 if (!m_sentEndEvent) {
3468 m_sentEndEvent = true;
3469 scheduleEvent(eventNames().endedEvent);
3471 // If the media element has a current media controller, then report the controller state
3472 // for the media element's current media controller.
3473 updateMediaController();
3477 m_sentEndEvent = false;
3480 endProcessingMediaPlayerCallback();
3483 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
3485 LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged");
3487 beginProcessingMediaPlayerCallback();
3489 float vol = m_player->volume();
3490 if (vol != m_volume) {
3493 scheduleEvent(eventNames().volumechangeEvent);
3496 endProcessingMediaPlayerCallback();
3499 void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*)
3501 LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged");
3503 beginProcessingMediaPlayerCallback();
3505 setMuted(m_player->muted());
3506 endProcessingMediaPlayerCallback();
3509 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer* player)
3511 LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged");
3513 beginProcessingMediaPlayerCallback();
3514 scheduleEvent(eventNames().durationchangeEvent);
3515 mediaPlayerCharacteristicChanged(player);
3516 endProcessingMediaPlayerCallback();
3519 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
3521 LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged");
3523 beginProcessingMediaPlayerCallback();
3525 // Stash the rate in case the one we tried to set isn't what the engine is
3526 // using (eg. it can't handle the rate we set)
3527 m_playbackRate = m_player->rate();
3529 invalidateCachedTime();
3532 updateDisableSleep();
3535 endProcessingMediaPlayerCallback();
3538 void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*)
3540 LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged");
3542 if (!m_player || m_pausedInternal)
3545 beginProcessingMediaPlayerCallback();
3546 if (m_player->paused())
3550 endProcessingMediaPlayerCallback();
3553 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*)
3555 LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks");
3557 // The MediaPlayer came across content it cannot completely handle.
3558 // This is normally acceptable except when we are in a standalone
3559 // MediaDocument. If so, tell the document what has happened.
3560 if (ownerDocument()->isMediaDocument()) {
3561 MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument());
3562 mediaDocument->mediaElementSawUnsupportedTracks();
3566 void HTMLMediaElement::mediaPlayerResourceNotSupported(MediaPlayer*)
3568 LOG(Media, "HTMLMediaElement::mediaPlayerResourceNotSupported");
3570 // The MediaPlayer came across content which no installed engine supports.
3571 mediaLoadingFailed(MediaPlayer::FormatError);
3574 // MediaPlayerPresentation methods
3575 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
3577 beginProcessingMediaPlayerCallback();
3578 updateDisplayState();
3580 renderer()->repaint();
3581 endProcessingMediaPlayerCallback();
3584 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*)
3586 LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged");
3588 beginProcessingMediaPlayerCallback();
3590 renderer()->updateFromElement();
3591 endProcessingMediaPlayerCallback();
3594 #if USE(ACCELERATED_COMPOSITING)
3595 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*)
3597 if (renderer() && renderer()->isVideo()) {
3598 ASSERT(renderer()->view());
3599 return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer()));
3604 void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*)
3606 LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged");
3608 // Kick off a fake recalcStyle that will update the compositing tree.
3609 setNeedsStyleRecalc(SyntheticStyleChange);
3613 #if PLATFORM(WIN) && USE(AVFOUNDATION)
3614 GraphicsDeviceAdapter* HTMLMediaElement::mediaPlayerGraphicsDeviceAdapter(const MediaPlayer*) const
3616 if (!document() || !document()->page())
3619 return document()->page()->chrome()->client()->graphicsDeviceAdapter();
3623 void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*)
3625 LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated");
3626 beginProcessingMediaPlayerCallback();
3628 renderer()->updateFromElement();
3629 endProcessingMediaPlayerCallback();
3632 void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*)
3634 LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable");
3635 beginProcessingMediaPlayerCallback();
3636 if (displayMode() == PosterWaitingForVideo) {
3637 setDisplayMode(Video);
3638 #if USE(ACCELERATED_COMPOSITING)
3639 mediaPlayerRenderingModeChanged(m_player.get());
3642 endProcessingMediaPlayerCallback();
3645 void HTMLMediaElement::mediaPlayerCharacteristicChanged(MediaPlayer*)
3647 LOG(Media, "HTMLMediaElement::mediaPlayerCharacteristicChanged");
3649 beginProcessingMediaPlayerCallback();
3650 if (hasMediaControls())
3651 mediaControls()->reset();
3653 renderer()->updateFromElement();
3654 endProcessingMediaPlayerCallback();
3657 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
3660 return TimeRanges::create();
3661 return m_player->buffered();
3664 PassRefPtr<TimeRanges> HTMLMediaElement::played()
3667 float time = currentTime();
3668 if (time > m_lastSeekTime)
3669 addPlayedRange(m_lastSeekTime, time);
3672 if (!m_playedTimeRanges)
3673 m_playedTimeRanges = TimeRanges::create();
3675 return m_playedTimeRanges->copy();
3678 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
3680 return m_player ? m_player->seekable() : TimeRanges::create();
3683 bool HTMLMediaElement::potentiallyPlaying() const
3685 // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing
3686 // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the
3687 // checks in couldPlayIfEnoughData().
3688 bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA;
3689 return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData() && !isBlockedOnMediaController();
3692 bool HTMLMediaElement::couldPlayIfEnoughData() const
3694 return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
3697 bool HTMLMediaElement::endedPlayback() const
3699 float dur = duration();
3700 if (!m_player || isnan(dur))
3703 // 4.8.10.8 Playing the media resource
3705 // A media element is said to have ended playback when the element's
3706 // readyState attribute is HAVE_METADATA or greater,
3707 if (m_readyState < HAVE_METADATA)
3710 // and the current playback position is the end of the media resource and the direction
3711 // of playback is forwards, Either the media element does not have a loop attribute specified,
3712 // or the media element has a current media controller.
3713 float now = currentTime();
3714 if (m_playbackRate > 0)
3715 return dur > 0 && now >= dur && (!loop() || m_mediaController);
3717 // or the current playback position is the earliest possible position and the direction
3718 // of playback is backwards
3719 if (m_playbackRate < 0)
3725 bool HTMLMediaElement::stoppedDueToErrors() const
3727 if (m_readyState >= HAVE_METADATA && m_error) {
3728 RefPtr<TimeRanges> seekableRanges = seekable();
3729 if (!seekableRanges->contain(currentTime()))
3736 bool HTMLMediaElement::pausedForUserInteraction() const
3738 // return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user]
3742 float HTMLMediaElement::minTimeSeekable() const
3747 float HTMLMediaElement::maxTimeSeekable() const
3749 return m_player ? m_player->maxTimeSeekable() : 0;
3752 void HTMLMediaElement::updateVolume()
3757 // Avoid recursion when the player reports volume changes.
3758 if (!processingMediaPlayerCallback()) {
3759 Page* page = document()->page();
3760 float volumeMultiplier = page ? page->mediaVolume() : 1;
3761 bool shouldMute = m_muted;
3763 if (m_mediaController) {
3764 volumeMultiplier *= m_mediaController->volume();
3765 shouldMute = m_mediaController->muted();
3768 m_player->setMuted(shouldMute);
3769 m_player->setVolume(m_volume * volumeMultiplier);
3772 if (hasMediaControls())
3773 mediaControls()->changedVolume();
3776 void HTMLMediaElement::updatePlayState()
3781 if (m_pausedInternal) {
3782 if (!m_player->paused())
3784 refreshCachedTime();
3785 m_playbackProgressTimer.stop();
3786 if (hasMediaControls())
3787 mediaControls()->playbackStopped();
3791 bool shouldBePlaying = potentiallyPlaying();
3792 bool playerPaused = m_player->paused();
3794 LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s",
3795 boolString(shouldBePlaying), boolString(playerPaused));
3797 if (shouldBePlaying) {
3798 setDisplayMode(Video);
3799 invalidateCachedTime();
3802 if (!m_isFullscreen && isVideo() && document() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback())
3805 // Set rate, muted before calling play in case they were set before the media engine was setup.
3806 // The media engine should just stash the rate and muted values since it isn't already playing.
3807 m_player->setRate(m_playbackRate);
3808 m_player->setMuted(m_muted);
3813 if (hasMediaControls())
3814 mediaControls()->playbackStarted();
3815 startPlaybackProgressTimer();
3817 } else { // Should not be playing right now
3820 refreshCachedTime();
3822 m_playbackProgressTimer.stop();
3824 float time = currentTime();
3825 if (time > m_lastSeekTime)
3826 addPlayedRange(m_lastSeekTime, time);
3828 if (couldPlayIfEnoughData())
3831 if (hasMediaControls())
3832 mediaControls()->playbackStopped();
3835 updateMediaController();
3838 renderer()->updateFromElement();
3841 void HTMLMediaElement::setPausedInternal(bool b)
3843 m_pausedInternal = b;
3847 void HTMLMediaElement::stopPeriodicTimers()
3849 m_progressEventTimer.stop();
3850 m_playbackProgressTimer.stop();
3853 void HTMLMediaElement::userCancelledLoad()
3855 LOG(Media, "HTMLMediaElement::userCancelledLoad");
3857 if (m_networkState == NETWORK_EMPTY || m_completelyLoaded)
3860 // If the media data fetching process is aborted by the user:
3862 // 1 - The user agent should cancel the fetching process.
3863 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
3866 stopPeriodicTimers();
3868 m_loadState = WaitingForSource;
3869 m_pendingLoadFlags = 0;
3871 // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED.
3872 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
3874 // 3 - Queue a task to fire a simple event named error at the media element.
3875 scheduleEvent(eventNames().abortEvent);
3877 #if ENABLE(MEDIA_SOURCE)
3878 if (m_sourceState != SOURCE_CLOSED)
3879 setSourceState(SOURCE_CLOSED);
3882 // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
3883 // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
3884 // simple event named emptied at the element. Otherwise, set the element's networkState
3885 // attribute to the NETWORK_IDLE value.
3886 if (m_readyState == HAVE_NOTHING) {
3887 m_networkState = NETWORK_EMPTY;
3888 scheduleEvent(eventNames().emptiedEvent);
3891 m_networkState = NETWORK_IDLE;
3893 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
3894 setShouldDelayLoadEvent(false);
3896 // 6 - Abort the overall resource selection algorithm.
3897 m_currentSourceNode = 0;
3899 // Reset m_readyState since m_player is gone.
3900 m_readyState = HAVE_NOTHING;
3901 updateMediaController();
3902 #if ENABLE(VIDEO_TRACK)
3903 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3904 updateActiveTextTrackCues(0);
3908 bool HTMLMediaElement::canSuspend() const
3913 void HTMLMediaElement::stop()
3915 LOG(Media, "HTMLMediaElement::stop");
3919 m_inActiveDocument = false;
3920 userCancelledLoad();
3922 // Stop the playback without generating events
3923 setPausedInternal(true);
3926 renderer()->updateFromElement();
3928 stopPeriodicTimers();
3929 cancelPendingEventsAndCallbacks();
3930 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
3932 m_player->suspend();
3934 power_unlock_state(POWER_STATE_NORMAL);
3938 void HTMLMediaElement::suspend(ReasonForSuspension why)
3940 LOG(Media, "HTMLMediaElement::suspend");
3941 #if ENABLE(TIZEN_DLOG_SUPPORT)
3942 TIZEN_LOGI("Why: %d", why);
3947 case DocumentWillBecomeInactive:
3950 case PageWillBeSuspended:
3951 case JavaScriptDebuggerPaused:
3952 case WillDeferLoading:
3953 // Do nothing, we don't pause media playback in these cases.
3954 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
3955 if (shouldSuspendMedia()) {
3957 m_player->suspend();
3965 void HTMLMediaElement::resume()
3967 LOG(Media, "HTMLMediaElement::resume");
3969 m_inActiveDocument = true;
3970 setPausedInternal(false);
3972 if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) {
3973 // Restart the load if it was aborted in the middle by moving the document to the page cache.
3974 // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to
3975 // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards).
3976 // This behavior is not specified but it seems like a sensible thing to do.
3977 // As it is not safe to immedately start loading now, let's schedule a load.
3978 scheduleLoad(MediaResource);
3982 renderer()->updateFromElement();
3984 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
3985 // Player will resume asynchronously.
3986 // Set m_suspended to false in setSuspended().
3992 bool HTMLMediaElement::hasPendingActivity() const
3994 return m_asyncEventQueue->hasPendingEvents();
3997 void HTMLMediaElement::mediaVolumeDidChange()
3999 LOG(Media, "HTMLMediaElement::mediaVolumeDidChange");
4003 void HTMLMediaElement::defaultEventHandler(Event* event)
4005 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
4006 RenderObject* r = renderer();
4007 if (!r || !r->isWidget())
4010 Widget* widget = toRenderWidget(r)->widget();
4012 widget->handleEvent(event);
4014 HTMLElement::defaultEventHandler(event);
4018 bool HTMLMediaElement::willRespondToMouseClickEvents()
4020 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
4023 return HTMLElement::willRespondToMouseClickEvents();
4027 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
4029 void HTMLMediaElement::ensureMediaPlayer()
4032 createMediaPlayer();
4035 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification)
4037 if (notification == MediaPlayerNotificationPlayPauseButtonPressed) {
4043 m_player->deliverNotification(notification);
4046 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy)
4048 ensureMediaPlayer();
4049 m_player->setMediaPlayerProxy(proxy);
4052 void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values)
4054 RefPtr<HTMLMediaElement> protect(this); // selectNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
4056 Frame* frame = document()->frame();
4059 KURL posterURL = getNonEmptyURLAttribute(posterAttr);
4060 if (!posterURL.isEmpty() && frame && frame->loader()->willLoadMediaElementURL(posterURL)) {
4061 names.append("_media_element_poster_");
4062 values.append(posterURL.string());
4067 names.append("_media_element_controls_");
4068 values.append("true");
4072 if (!isSafeToLoadURL(url, Complain))
4073 url = selectNextSourceChild(0, 0, DoNothing);
4076 if (url.isValid() && frame && frame->loader()->willLoadMediaElementURL(url)) {
4077 names.append("_media_element_src_");
4078 values.append(m_currentSrc.string());
4082 void HTMLMediaElement::createMediaPlayerProxy()
4084 ensureMediaPlayer();
4086 if (m_proxyWidget || (inDocument() && !m_needWidgetUpdate))
4089 Frame* frame = document()->frame();
4093 LOG(Media, "HTMLMediaElement::createMediaPlayerProxy");
4096 Vector<String> paramNames;
4097 Vector<String> paramValues;
4099 getPluginProxyParams(url, paramNames, paramValues);
4101 // Hang onto the proxy widget so it won't be destroyed if the plug-in is set to
4103 m_proxyWidget = frame->loader()->subframeLoader()->loadMediaPlayerProxyPlugin(this, url, paramNames, paramValues);
4105 m_needWidgetUpdate = false;
4108 void HTMLMediaElement::updateWidget(PluginCreationOption)
4110 mediaElement->setNeedWidgetUpdate(false);
4112 Vector<String> paramNames;
4113 Vector<String> paramValues;
4114 // FIXME: Rename kurl to something more sensible.
4117 mediaElement->getPluginProxyParams(kurl, paramNames, paramValues);
4118 // FIXME: What if document()->frame() is 0?
4119 SubframeLoader* loader = document()->frame()->loader()->subframeLoader();
4120 loader->loadMediaPlayerProxyPlugin(mediaElement, kurl, paramNames, paramValues);
4123 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
4125 bool HTMLMediaElement::isFullscreen() const
4130 #if ENABLE(FULLSCREEN_API)
4131 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this)
4138 void HTMLMediaElement::enterFullscreen()
4140 LOG(Media, "HTMLMediaElement::enterFullscreen");
4142 #if ENABLE(FULLSCREEN_API)
4143 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) {
4144 document()->requestFullScreenForElement(this, 0, Document::ExemptIFrameAllowFullScreenRequirement);
4145 #if ENABLE(TIZEN_FULLSCREEN_API)
4146 if (hasMediaControls())
4147 mediaControls()->updateMediaControlScale();
4152 ASSERT(!m_isFullscreen);
4153 m_isFullscreen = true;
4154 if (hasMediaControls())
4155 mediaControls()->enteredFullscreen();
4156 if (document() && document()->page()) {
4157 document()->page()->chrome()->client()->enterFullscreenForNode(this);
4158 scheduleEvent(eventNames().webkitbeginfullscreenEvent);
4163 void HTMLMediaElement::exitFullscreen()
4165 LOG(Media, "HTMLMediaElement::exitFullscreen");
4167 #if ENABLE(FULLSCREEN_API)
4168 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) {
4169 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this)
4170 document()->webkitCancelFullScreen();
4174 ASSERT(m_isFullscreen);
4175 m_isFullscreen = false;
4176 if (hasMediaControls())
4177 mediaControls()->exitedFullscreen();
4178 if (document() && document()->page()) {
4179 if (document()->page()->chrome()->requiresFullscreenForVideoPlayback())
4181 document()->page()->chrome()->client()->exitFullscreenForNode(this);
4182 scheduleEvent(eventNames().webkitendfullscreenEvent);
4186 #if ENABLE(TIZEN_FULLSCREEN_API)
4187 void HTMLMediaElement::updateMediaControlsStyle(bool needsRecalc)
4189 if (hasMediaControls())
4190 mediaControls()->updateMediaControlScale();
4193 recalcStyle(Node::Force);
4197 void HTMLMediaElement::didBecomeFullscreenElement()
4199 if (hasMediaControls())
4200 mediaControls()->enteredFullscreen();
4203 void HTMLMediaElement::willStopBeingFullscreenElement()
4205 if (hasMediaControls())
4206 mediaControls()->exitedFullscreen();
4209 PlatformMedia HTMLMediaElement::platformMedia() const
4211 return m_player ? m_player->platformMedia() : NoPlatformMedia;
4214 #if USE(ACCELERATED_COMPOSITING)
4215 PlatformLayer* HTMLMediaElement::platformLayer() const
4217 return m_player ? m_player->platformLayer() : 0;
4221 bool HTMLMediaElement::hasClosedCaptions() const
4223 if (m_player && m_player->hasClosedCaptions())
4226 #if ENABLE(VIDEO_TRACK)
4227 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && m_textTracks)
4228 for (unsigned i = 0; i < m_textTracks->length(); ++i) {
4229 if (m_textTracks->item(i)->kind() == TextTrack::captionsKeyword()
4230 || m_textTracks->item(i)->kind() == TextTrack::subtitlesKeyword())
4237 bool HTMLMediaElement::closedCaptionsVisible() const
4239 return m_closedCaptionsVisible;
4242 void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible)
4244 LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible));
4246 if (!m_player || !hasClosedCaptions())
4249 m_closedCaptionsVisible = closedCaptionVisible;
4250 m_player->setClosedCaptionsVisible(closedCaptionVisible);
4252 #if ENABLE(VIDEO_TRACK)
4253 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) {
4254 m_disableCaptions = !m_closedCaptionsVisible;
4256 // Mark all track elements as not "configured" so that configureTextTracks()
4257 // will reconsider which tracks to display in light of new user preferences
4258 // (e.g. default tracks should not be displayed if the user has turned off
4259 // captions and non-default tracks should be displayed based on language
4260 // preferences if the user has turned captions on).
4261 for (Node* node = firstChild(); node; node = node->nextSibling()) {
4262 if (!node->hasTagName(trackTag))
4264 HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node);
4265 if (trackElement->kind() == TextTrack::captionsKeyword()
4266 || trackElement->kind() == TextTrack::subtitlesKeyword())
4267 trackElement->setHasBeenConfigured(false);
4270 configureTextTracks();
4273 if (hasMediaControls())
4274 mediaControls()->changedClosedCaptionsVisibility();
4278 void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible)
4280 setClosedCaptionsVisible(visible);
4283 bool HTMLMediaElement::webkitClosedCaptionsVisible() const
4285 return closedCaptionsVisible();
4289 bool HTMLMediaElement::webkitHasClosedCaptions() const
4291 return hasClosedCaptions();
4294 #if ENABLE(MEDIA_STATISTICS)
4295 unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const
4299 return m_player->audioDecodedByteCount();
4302 unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const
4306 return m_player->videoDecodedByteCount();
4310 void HTMLMediaElement::mediaCanStart()
4312 LOG(Media, "HTMLMediaElement::mediaCanStart");
4314 ASSERT(m_isWaitingUntilMediaCanStart);
4315 m_isWaitingUntilMediaCanStart = false;
4319 bool HTMLMediaElement::isURLAttribute(const Attribute& attribute) const
4321 return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute);
4324 void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay)
4326 if (m_shouldDelayLoadEvent == shouldDelay)
4329 LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay));
4331 m_shouldDelayLoadEvent = shouldDelay;
4333 document()->incrementLoadEventDelayCount();
4335 document()->decrementLoadEventDelayCount();
4339 void HTMLMediaElement::getSitesInMediaCache(Vector<String>& sites)
4341 MediaPlayer::getSitesInMediaCache(sites);
4344 void HTMLMediaElement::clearMediaCache()
4346 MediaPlayer::clearMediaCache();
4349 void HTMLMediaElement::clearMediaCacheForSite(const String& site)
4351 MediaPlayer::clearMediaCacheForSite(site);
4354 void HTMLMediaElement::privateBrowsingStateDidChange()
4359 Settings* settings = document()->settings();
4360 bool privateMode = !settings || settings->privateBrowsingEnabled();
4361 LOG(Media, "HTMLMediaElement::privateBrowsingStateDidChange(%s)", boolString(privateMode));
4362 m_player->setPrivateBrowsingMode(privateMode);
4365 MediaControls* HTMLMediaElement::mediaControls() const
4367 return toMediaControls(userAgentShadowRoot()->firstChild());
4370 bool HTMLMediaElement::hasMediaControls() const
4372 if (ShadowRoot* userAgent = userAgentShadowRoot()) {
4373 Node* node = userAgent->firstChild();
4374 ASSERT(!node || node->isMediaControls());
4381 bool HTMLMediaElement::createMediaControls()
4383 if (hasMediaControls())
4387 RefPtr<MediaControls> controls = MediaControls::create(document());
4391 controls->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this));
4393 #if ENABLE(TIZEN_FULLSCREEN_API)
4395 controls->updateMediaControlScale();
4398 controls->enteredFullscreen();
4401 createShadowSubtree();
4403 ASSERT(userAgentShadowRoot());
4404 userAgentShadowRoot()->appendChild(controls, ec);
4408 void HTMLMediaElement::configureMediaControls()
4410 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
4411 if (!controls() || !inDocument()) {
4412 if (hasMediaControls())
4413 mediaControls()->hide();
4417 if (!hasMediaControls() && !createMediaControls())
4420 mediaControls()->show();
4423 m_player->setControls(controls());
4427 #if ENABLE(VIDEO_TRACK)
4428 void HTMLMediaElement::configureTextTrackDisplay()
4430 ASSERT(m_textTracks);
4432 bool haveVisibleTextTrack = false;
4433 for (unsigned i = 0; i < m_textTracks->length(); ++i) {
4434 if (m_textTracks->item(i)->mode() == TextTrack::showingKeyword()) {
4435 haveVisibleTextTrack = true;
4440 if (m_haveVisibleTextTrack == haveVisibleTextTrack)
4442 m_haveVisibleTextTrack = haveVisibleTextTrack;
4443 m_closedCaptionsVisible = m_haveVisibleTextTrack;
4445 if (!m_haveVisibleTextTrack && !hasMediaControls())
4447 if (!hasMediaControls() && !createMediaControls())
4450 updateClosedCaptionsControls();
4453 void HTMLMediaElement::updateClosedCaptionsControls()
4455 if (hasMediaControls()) {
4456 mediaControls()->changedClosedCaptionsVisibility();
4458 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
4459 mediaControls()->updateTextTrackDisplay();
4464 void* HTMLMediaElement::preDispatchEventHandler(Event* event)
4466 if (event && event->type() == eventNames().webkitfullscreenchangeEvent) {
4467 configureMediaControls();
4468 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
4469 if (!isFullscreen())
4470 scheduleEvent(eventNames().webkitendfullscreenEvent);
4477 void HTMLMediaElement::createMediaPlayer()
4479 #if ENABLE(WEB_AUDIO)
4480 if (m_audioSourceNode)
4481 m_audioSourceNode->lock();
4484 m_player = MediaPlayer::create(this);
4486 #if ENABLE(WEB_AUDIO)
4487 if (m_audioSourceNode) {
4488 // When creating the player, make sure its AudioSourceProvider knows about the MediaElementAudioSourceNode.
4489 if (audioSourceProvider())
4490 audioSourceProvider()->setClient(m_audioSourceNode);
4492 m_audioSourceNode->unlock();
4497 #if ENABLE(WEB_AUDIO)
4498 void HTMLMediaElement::setAudioSourceNode(MediaElementAudioSourceNode* sourceNode)
4500 m_audioSourceNode = sourceNode;
4502 if (audioSourceProvider())
4503 audioSourceProvider()->setClient(m_audioSourceNode);
4506 AudioSourceProvider* HTMLMediaElement::audioSourceProvider()
4509 return m_player->audioSourceProvider();
4515 #if ENABLE(MICRODATA)
4516 String HTMLMediaElement::itemValueText() const
4518 return getURLAttribute(srcAttr);
4521 void HTMLMediaElement::setItemValueText(const String& value, ExceptionCode&)
4523 setAttribute(srcAttr, value);
4527 const String& HTMLMediaElement::mediaGroup() const
4529 return m_mediaGroup;
4532 void HTMLMediaElement::setMediaGroup(const String& group)
4534 if (m_mediaGroup == group)
4536 m_mediaGroup = group;
4538 // When a media element is created with a mediagroup attribute, and when a media element's mediagroup
4539 // attribute is set, changed, or removed, the user agent must run the following steps:
4540 // 1. Let m [this] be the media element in question.
4541 // 2. Let m have no current media controller, if it currently has one.
4544 // 3. If m's mediagroup attribute is being removed, then abort these steps.
4545 if (group.isNull() || group.isEmpty())
4548 // 4. If there is another media element whose Document is the same as m's Document (even if one or both
4549 // of these elements are not actually in the Document),
4550 HashSet<HTMLMediaElement*> elements = documentToElementSetMap().get(document());
4551 for (HashSet<HTMLMediaElement*>::iterator i = elements.begin(); i != elements.end(); ++i) {
4555 // and which also has a mediagroup attribute, and whose mediagroup attribute has the same value as
4556 // the new value of m's mediagroup attribute,
4557 if ((*i)->mediaGroup() == group) {
4558 // then let controller be that media element's current media controller.
4559 setController((*i)->controller());
4564 // Otherwise, let controller be a newly created MediaController.
4565 setController(MediaController::create(Node::scriptExecutionContext()));
4568 MediaController* HTMLMediaElement::controller() const
4570 return m_mediaController.get();
4573 void HTMLMediaElement::setController(PassRefPtr<MediaController> controller)
4575 if (m_mediaController)
4576 m_mediaController->removeMediaElement(this);
4578 m_mediaController = controller;
4580 if (m_mediaController)
4581 m_mediaController->addMediaElement(this);
4583 if (hasMediaControls())
4584 mediaControls()->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this));
4587 void HTMLMediaElement::updateMediaController()
4589 if (m_mediaController)
4590 m_mediaController->reportControllerState();
4593 bool HTMLMediaElement::dispatchEvent(PassRefPtr<Event> event)
4595 bool dispatchResult;
4596 bool isCanPlayEvent;
4598 isCanPlayEvent = (event->type() == eventNames().canplayEvent);
4601 m_dispatchingCanPlayEvent = true;
4603 dispatchResult = HTMLElement::dispatchEvent(event);
4606 m_dispatchingCanPlayEvent = false;
4608 return dispatchResult;
4611 bool HTMLMediaElement::isBlocked() const
4613 // A media element is a blocked media element if its readyState attribute is in the
4614 // HAVE_NOTHING state, the HAVE_METADATA state, or the HAVE_CURRENT_DATA state,
4615 if (m_readyState <= HAVE_CURRENT_DATA)
4618 // or if the element has paused for user interaction.
4619 return pausedForUserInteraction();
4622 bool HTMLMediaElement::isBlockedOnMediaController() const
4624 if (!m_mediaController)
4627 // A media element is blocked on its media controller if the MediaController is a blocked
4628 // media controller,
4629 if (m_mediaController->isBlocked())
4632 // or if its media controller position is either before the media resource's earliest possible
4633 // position relative to the MediaController's timeline or after the end of the media resource
4634 // relative to the MediaController's timeline.
4635 float mediaControllerPosition = m_mediaController->currentTime();
4636 if (mediaControllerPosition < startTime() || mediaControllerPosition > startTime() + duration())
4642 void HTMLMediaElement::prepareMediaFragmentURI()
4644 MediaFragmentURIParser fragmentParser(m_currentSrc);
4645 float dur = duration();
4647 double start = fragmentParser.startTime();
4648 if (start != MediaFragmentURIParser::invalidTimeValue() && start > 0) {
4649 m_fragmentStartTime = start;
4650 if (m_fragmentStartTime > dur)
4651 m_fragmentStartTime = dur;
4653 m_fragmentStartTime = MediaPlayer::invalidTime();
4655 double end = fragmentParser.endTime();
4656 if (end != MediaFragmentURIParser::invalidTimeValue() && end > 0 && end > m_fragmentStartTime) {
4657 m_fragmentEndTime = end;
4658 if (m_fragmentEndTime > dur)
4659 m_fragmentEndTime = dur;
4661 m_fragmentEndTime = MediaPlayer::invalidTime();
4663 if (m_fragmentStartTime != MediaPlayer::invalidTime() && m_readyState < HAVE_FUTURE_DATA)
4667 void HTMLMediaElement::applyMediaFragmentURI()
4669 if (m_fragmentStartTime != MediaPlayer::invalidTime()) {
4670 ExceptionCode ignoredException;
4671 m_sentEndEvent = false;
4672 seek(m_fragmentStartTime, ignoredException);
4677 void HTMLMediaElement::updateDisableSleep()
4679 if (!shouldDisableSleep() && m_sleepDisabler)
4680 m_sleepDisabler = nullptr;
4681 else if (shouldDisableSleep() && !m_sleepDisabler)
4682 m_sleepDisabler = DisplaySleepDisabler::create("com.apple.WebCore: HTMLMediaElement playback");
4685 bool HTMLMediaElement::shouldDisableSleep() const
4687 return m_player && !m_player->paused() && hasVideo() && hasAudio() && !loop();
4691 String HTMLMediaElement::mediaPlayerReferrer() const
4693 Frame* frame = document()->frame();
4697 return SecurityPolicy::generateReferrerHeader(document()->referrerPolicy(), m_currentSrc, frame->loader()->outgoingReferrer());
4700 String HTMLMediaElement::mediaPlayerUserAgent() const
4702 Frame* frame = document()->frame();
4706 return frame->loader()->userAgent(m_currentSrc);
4710 MediaPlayerClient::CORSMode HTMLMediaElement::mediaPlayerCORSMode() const
4712 if (!fastHasAttribute(HTMLNames::crossoriginAttr))
4714 if (equalIgnoringCase(fastGetAttribute(HTMLNames::crossoriginAttr), "use-credentials"))
4715 return UseCredentials;
4719 bool HTMLMediaElement::mediaPlayerNeedsSiteSpecificHacks() const
4721 Settings* settings = document()->settings();
4722 return settings && settings->needsSiteSpecificQuirks();
4725 String HTMLMediaElement::mediaPlayerDocumentHost() const
4727 return document()->url().host();
4730 void HTMLMediaElement::mediaPlayerExitFullscreen()
4735 bool HTMLMediaElement::mediaPlayerIsVideo() const
4740 LayoutRect HTMLMediaElement::mediaPlayerContentBoxRect() const
4743 return renderer()->enclosingBox()->contentBoxRect();
4744 return LayoutRect();
4747 void HTMLMediaElement::mediaPlayerSetSize(const IntSize& size)
4749 setAttribute(widthAttr, String::number(size.width()));
4750 setAttribute(heightAttr, String::number(size.height()));
4753 void HTMLMediaElement::mediaPlayerPause()
4758 void HTMLMediaElement::mediaPlayerPlay()
4763 bool HTMLMediaElement::mediaPlayerIsPaused() const
4768 bool HTMLMediaElement::mediaPlayerIsLooping() const
4773 HostWindow* HTMLMediaElement::mediaPlayerHostWindow()
4775 return mediaPlayerOwningDocument()->view()->hostWindow();
4778 IntRect HTMLMediaElement::mediaPlayerWindowClipRect()
4780 return mediaPlayerOwningDocument()->view()->windowClipRect();
4783 void HTMLMediaElement::removeBehaviorsRestrictionsAfterFirstUserGesture()
4785 m_restrictions = NoRestrictions;
4788 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
4789 bool HTMLMediaElement::shouldSuspendMedia()
4797 if (m_player->hasVideo())
4800 #if ENABLE(TIZEN_EXTENSIBLE_API)
4801 if (!TizenExtensibleAPI::extensibleAPI().backgroundMusic())
4808 void HTMLMediaElement::setSuspended(bool suspended)
4810 m_suspended = suspended;