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);
540 configureMediaControls();
541 return InsertionDone;
544 void HTMLMediaElement::removedFrom(ContainerNode* insertionPoint)
546 if (insertionPoint->inDocument()) {
547 LOG(Media, "HTMLMediaElement::removedFromDocument");
548 configureMediaControls();
549 if (m_networkState > NETWORK_EMPTY)
555 HTMLElement::removedFrom(insertionPoint);
558 void HTMLMediaElement::attach()
562 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
563 m_needWidgetUpdate = true;
566 HTMLElement::attach();
569 renderer()->updateFromElement();
570 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
571 else if (m_proxyWidget) {
572 if (Frame* frame = document()->frame())
573 frame->loader()->client()->hideMediaPlayerProxyPlugin(m_proxyWidget.get());
578 void HTMLMediaElement::didRecalcStyle(StyleChange)
581 renderer()->updateFromElement();
584 void HTMLMediaElement::scheduleLoad(LoadType loadType)
586 LOG(Media, "HTMLMediaElement::scheduleLoad");
588 if ((loadType & MediaResource) && !(m_pendingLoadFlags & MediaResource)) {
589 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
590 createMediaPlayerProxy();
594 m_pendingLoadFlags |= MediaResource;
597 #if ENABLE(VIDEO_TRACK)
598 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (loadType & TextTrackResource))
599 m_pendingLoadFlags |= TextTrackResource;
602 if (!m_loadTimer.isActive())
603 m_loadTimer.startOneShot(0);
606 void HTMLMediaElement::scheduleNextSourceChild()
608 // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
609 m_pendingLoadFlags |= MediaResource;
610 m_loadTimer.startOneShot(0);
613 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
616 LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data());
618 RefPtr<Event> event = Event::create(eventName, false, true);
619 event->setTarget(this);
621 m_asyncEventQueue->enqueueEvent(event.release());
624 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
626 RefPtr<HTMLMediaElement> protect(this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
628 #if ENABLE(VIDEO_TRACK)
629 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingLoadFlags & TextTrackResource))
630 configureTextTracks();
633 if (m_pendingLoadFlags & MediaResource) {
634 if (m_loadState == LoadingFromSourceElement)
635 loadNextSourceChild();
640 m_pendingLoadFlags = 0;
643 PassRefPtr<MediaError> HTMLMediaElement::error() const
648 void HTMLMediaElement::setSrc(const String& url)
650 setAttribute(srcAttr, url);
653 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
655 return m_networkState;
658 String HTMLMediaElement::canPlayType(const String& mimeType, const String& keySystem, const KURL& url) const
660 MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType), keySystem, url, this);
666 case MediaPlayer::IsNotSupported:
669 case MediaPlayer::MayBeSupported:
672 case MediaPlayer::IsSupported:
673 canPlay = "probably";
677 LOG(Media, "HTMLMediaElement::canPlayType(%s, %s, %s) -> %s", mimeType.utf8().data(), keySystem.utf8().data(), url.string().utf8().data(), canPlay.utf8().data());
682 void HTMLMediaElement::load()
684 RefPtr<HTMLMediaElement> protect(this); // loadInternal may result in a 'beforeload' event, which can make arbitrary DOM mutations.
686 LOG(Media, "HTMLMediaElement::load()");
688 if (userGestureRequiredForLoad() && !ScriptController::processingUserGesture())
691 m_loadInitiatedByUserGesture = ScriptController::processingUserGesture();
692 if (m_loadInitiatedByUserGesture)
693 removeBehaviorsRestrictionsAfterFirstUserGesture();
699 void HTMLMediaElement::prepareForLoad()
701 LOG(Media, "HTMLMediaElement::prepareForLoad");
703 // Perform the cleanup required for the resource load algorithm to run.
704 stopPeriodicTimers();
706 m_sentEndEvent = false;
707 m_sentStalledEvent = false;
708 m_haveFiredLoadedData = false;
709 m_completelyLoaded = false;
710 m_havePreparedToPlay = false;
711 m_displayMode = Unknown;
713 // 1 - Abort any already-running instance of the resource selection algorithm for this element.
714 m_loadState = WaitingForSource;
715 m_currentSourceNode = 0;
717 // 2 - If there are any tasks from the media element's media element event task source in
718 // one of the task queues, then remove those tasks.
719 cancelPendingEventsAndCallbacks();
721 // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
722 // a task to fire a simple event named abort at the media element.
723 if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
724 scheduleEvent(eventNames().abortEvent);
726 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
730 m_player->cancelLoad();
732 createMediaPlayerProxy();
735 #if ENABLE(MEDIA_SOURCE)
736 if (m_sourceState != SOURCE_CLOSED)
737 setSourceState(SOURCE_CLOSED);
740 // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
741 if (m_networkState != NETWORK_EMPTY) {
742 m_networkState = NETWORK_EMPTY;
743 m_readyState = HAVE_NOTHING;
744 m_readyStateMaximum = HAVE_NOTHING;
748 invalidateCachedTime();
749 scheduleEvent(eventNames().emptiedEvent);
750 updateMediaController();
751 #if ENABLE(VIDEO_TRACK)
752 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
753 updateActiveTextTrackCues(0);
757 // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
758 setPlaybackRate(defaultPlaybackRate());
760 // 6 - Set the error attribute to null and the autoplaying flag to true.
762 m_autoplaying = true;
764 // 7 - Invoke the media element's resource selection algorithm.
766 // 8 - Note: Playback of any previously playing media resource for this element stops.
768 // The resource selection algorithm
769 // 1 - Set the networkState to NETWORK_NO_SOURCE
770 m_networkState = NETWORK_NO_SOURCE;
772 // 2 - Asynchronously await a stable state.
774 m_playedTimeRanges = TimeRanges::create();
777 // The spec doesn't say to block the load event until we actually run the asynchronous section
778 // algorithm, but do it now because we won't start that until after the timer fires and the
779 // event may have already fired by then.
780 setShouldDelayLoadEvent(true);
782 configureMediaControls();
785 void HTMLMediaElement::loadInternal()
787 // Some of the code paths below this function dispatch the BeforeLoad event. This ASSERT helps
788 // us catch those bugs more quickly without needing all the branches to align to actually
789 // trigger the event.
790 ASSERT(!eventDispatchForbidden());
792 // If we can't start a load right away, start it later.
793 Page* page = document()->page();
794 if (pageConsentRequiredForLoad() && page && !page->canStartMedia()) {
795 setShouldDelayLoadEvent(false);
796 if (m_isWaitingUntilMediaCanStart)
798 document()->addMediaCanStartListener(this);
799 m_isWaitingUntilMediaCanStart = true;
803 // Once the page has allowed an element to load media, it is free to load at will. This allows a
804 // playlist that starts in a foreground tab to continue automatically if the tab is subsequently
805 // put in the the background.
806 removeBehaviorRestriction(RequirePageConsentToLoadMediaRestriction);
808 #if ENABLE(VIDEO_TRACK)
809 // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
810 // disabled state when the element's resource selection algorithm last started".
811 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) {
812 m_textTracksWhenResourceSelectionBegan.clear();
814 for (unsigned i = 0; i < m_textTracks->length(); ++i) {
815 TextTrack* track = m_textTracks->item(i);
816 if (track->mode() != TextTrack::disabledKeyword())
817 m_textTracksWhenResourceSelectionBegan.append(track);
823 selectMediaResource();
826 void HTMLMediaElement::selectMediaResource()
828 LOG(Media, "HTMLMediaElement::selectMediaResource");
830 enum Mode { attribute, children };
832 // 3 - If the media element has a src attribute, then let mode be attribute.
833 Mode mode = attribute;
834 if (!fastHasAttribute(srcAttr)) {
836 for (node = firstChild(); node; node = node->nextSibling()) {
837 if (node->hasTagName(sourceTag))
841 // Otherwise, if the media element does not have a src attribute but has a source
842 // element child, then let mode be children and let candidate be the first such
843 // source element child in tree order.
846 m_nextChildNodeToConsider = node;
847 m_currentSourceNode = 0;
849 // Otherwise the media element has neither a src attribute nor a source element
850 // child: set the networkState to NETWORK_EMPTY, and abort these steps; the
851 // synchronous section ends.
852 m_loadState = WaitingForSource;
853 setShouldDelayLoadEvent(false);
854 m_networkState = NETWORK_EMPTY;
856 LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load");
861 // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event),
862 // and set its networkState to NETWORK_LOADING.
863 setShouldDelayLoadEvent(true);
864 m_networkState = NETWORK_LOADING;
866 // 5 - Queue a task to fire a simple event named loadstart at the media element.
867 scheduleEvent(eventNames().loadstartEvent);
869 // 6 - If mode is attribute, then run these substeps
870 if (mode == attribute) {
871 m_loadState = LoadingFromSrcAttr;
873 // If the src attribute's value is the empty string ... jump down to the failed step below
874 KURL mediaURL = getNonEmptyURLAttribute(srcAttr);
875 if (mediaURL.isEmpty()) {
876 mediaLoadingFailed(MediaPlayer::FormatError);
877 LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'");
881 if (!isSafeToLoadURL(mediaURL, Complain) || !dispatchBeforeLoadEvent(mediaURL.string())) {
882 mediaLoadingFailed(MediaPlayer::FormatError);
886 // No type or key system information is available when the url comes
887 // from the 'src' attribute so MediaPlayer
888 // will have to pick a media engine based on the file extension.
889 ContentType contentType((String()));
890 loadResource(mediaURL, contentType, String());
891 LOG(Media, "HTMLMediaElement::selectMediaResource, using 'src' attribute url");
895 // Otherwise, the source elements will be used
896 loadNextSourceChild();
899 void HTMLMediaElement::loadNextSourceChild()
901 ContentType contentType((String()));
903 KURL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain);
904 if (!mediaURL.isValid()) {
905 waitForSourceChange();
909 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
910 // Recreate the media player for the new url
914 m_loadState = LoadingFromSourceElement;
915 loadResource(mediaURL, contentType, keySystem);
918 #if !PLATFORM(CHROMIUM)
919 static KURL createFileURLForApplicationCacheResource(const String& path)
921 // KURL should have a function to create a url from a path, but it does not. This function
922 // is not suitable because KURL::setPath uses encodeWithURLEscapeSequences, which it notes
923 // does not correctly escape '#' and '?'. This function works for our purposes because
924 // app cache media files are always created with encodeForFileName(createCanonicalUUIDString()).
926 #if USE(CF) && PLATFORM(WIN)
927 RetainPtr<CFStringRef> cfPath(AdoptCF, path.createCFString());
928 RetainPtr<CFURLRef> cfURL(AdoptCF, CFURLCreateWithFileSystemPath(0, cfPath.get(), kCFURLWindowsPathStyle, false));
929 KURL url(cfURL.get());
933 url.setProtocol("file");
940 void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType, const String& keySystem)
942 ASSERT(isSafeToLoadURL(initialURL, Complain));
944 LOG(Media, "HTMLMediaElement::loadResource(%s, %s, %s)", urlForLogging(initialURL).utf8().data(), contentType.raw().utf8().data(), keySystem.utf8().data());
946 Frame* frame = document()->frame();
948 mediaLoadingFailed(MediaPlayer::FormatError);
952 KURL url = initialURL;
953 if (!frame->loader()->willLoadMediaElementURL(url)) {
954 mediaLoadingFailed(MediaPlayer::FormatError);
958 #if ENABLE(MEDIA_SOURCE)
959 // If this is a media source URL, make sure it is the one for this media element.
960 if (url.protocolIs(mediaSourceURLProtocol) && url != m_mediaSourceURL) {
961 mediaLoadingFailed(MediaPlayer::FormatError);
966 // The resource fetch algorithm
967 m_networkState = NETWORK_LOADING;
969 #if !PLATFORM(CHROMIUM)
970 // If the url should be loaded from the application cache, pass the url of the cached file
971 // to the media engine.
972 ApplicationCacheHost* cacheHost = frame->loader()->documentLoader()->applicationCacheHost();
973 ApplicationCacheResource* resource = 0;
974 if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource)) {
975 // Resources that are not present in the manifest will always fail to load (at least, after the
976 // cache has been primed the first time), making the testing of offline applications simpler.
977 if (!resource || resource->path().isEmpty()) {
978 mediaLoadingFailed(MediaPlayer::NetworkError);
984 // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app
985 // cache is an internal detail not exposed through the media element API.
988 #if !PLATFORM(CHROMIUM)
990 url = createFileURLForApplicationCacheResource(resource->path());
991 LOG(Media, "HTMLMediaElement::loadResource - will load from app cache -> %s", urlForLogging(url).utf8().data());
995 LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLogging(m_currentSrc).utf8().data());
997 #if ENABLE(MEDIA_STREAM)
998 if (MediaStreamRegistry::registry().lookupMediaStreamDescriptor(url.string()))
999 removeBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
1002 if (m_sendProgressEvents)
1003 startProgressEventTimer();
1005 Settings* settings = document()->settings();
1006 bool privateMode = !settings || settings->privateBrowsingEnabled();
1007 m_player->setPrivateBrowsingMode(privateMode);
1009 // Reset display mode to force a recalculation of what to show because we are resetting the player.
1010 setDisplayMode(Unknown);
1013 m_player->setPreload(m_preload);
1014 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1017 power_lock_state(POWER_STATE_NORMAL, 0);
1020 m_player->setPreservesPitch(m_webkitPreservesPitch);
1022 #if !ENABLE(TIZEN_GSTREAMER_AUDIO)
1023 if (fastHasAttribute(mutedAttr))
1028 if (!m_player->load(url, contentType, keySystem))
1029 mediaLoadingFailed(MediaPlayer::FormatError);
1031 #if ENABLE(TIZEN_GSTREAMER_AUDIO)
1035 // If there is no poster to display, allow the media engine to render video frames as soon as
1036 // they are available.
1037 updateDisplayState();
1040 renderer()->updateFromElement();
1043 #if ENABLE(VIDEO_TRACK)
1044 static bool trackIndexCompare(TextTrack* a,
1047 return a->trackIndex() - b->trackIndex() < 0;
1050 static bool eventTimeCueCompare(const std::pair<double, TextTrackCue*>& a,
1051 const std::pair<double, TextTrackCue*>& b)
1053 // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1055 if (a.first != b.first)
1056 return a.first - b.first < 0;
1058 // If the cues belong to different text tracks, it doesn't make sense to
1059 // compare the two tracks by the relative cue order, so return the relative
1061 if (a.second->track() != b.second->track())
1062 return trackIndexCompare(a.second->track(), b.second->track());
1064 // 12 - Further sort tasks in events that have the same time by the
1065 // relative text track cue order of the text track cues associated
1066 // with these tasks.
1067 return a.second->cueIndex() - b.second->cueIndex() < 0;
1071 void HTMLMediaElement::updateActiveTextTrackCues(float movieTime)
1073 LOG(Media, "HTMLMediaElement::updateActiveTextTracks");
1075 // 4.8.10.8 Playing the media resource
1077 // If the current playback position changes while the steps are running,
1078 // then the user agent must wait for the steps to complete, and then must
1079 // immediately rerun the steps.
1080 if (ignoreTrackDisplayUpdateRequests())
1083 // 1 - Let current cues be a list of cues, initialized to contain all the
1084 // cues of all the hidden, showing, or showing by default text tracks of the
1085 // media element (not the disabled ones) whose start times are less than or
1086 // equal to the current playback position and whose end times are greater
1087 // than the current playback position.
1088 Vector<CueIntervalTree::IntervalType> currentCues;
1090 // The user agent must synchronously unset [the text track cue active] flag
1091 // whenever ... the media element's readyState is changed back to HAVE_NOTHING.
1092 if (m_readyState != HAVE_NOTHING && m_player)
1093 currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
1095 Vector<CueIntervalTree::IntervalType> affectedCues;
1096 Vector<CueIntervalTree::IntervalType> previousCues;
1097 Vector<CueIntervalTree::IntervalType> missedCues;
1099 // 2 - Let other cues be a list of cues, initialized to contain all the cues
1100 // of hidden, showing, and showing by default text tracks of the media
1101 // element that are not present in current cues.
1102 previousCues = m_currentlyActiveCues;
1104 // 3 - Let last time be the current playback position at the time this
1105 // algorithm was last run for this media element, if this is not the first
1107 float lastTime = m_lastTextTrackUpdateTime;
1109 // 4 - If the current playback position has, since the last time this
1110 // algorithm was run, only changed through its usual monotonic increase
1111 // during normal playback, then let missed cues be the list of cues in other
1112 // cues whose start times are greater than or equal to last time and whose
1113 // end times are less than or equal to the current playback position.
1114 // Otherwise, let missed cues be an empty list.
1115 if (lastTime >= 0 && m_lastSeekTime < movieTime) {
1116 Vector<CueIntervalTree::IntervalType> potentiallySkippedCues =
1117 m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime));
1119 for (size_t i = 0; i < potentiallySkippedCues.size(); ++i) {
1120 float cueStartTime = potentiallySkippedCues[i].low();
1121 float cueEndTime = potentiallySkippedCues[i].high();
1123 // Consider cues that may have been missed since the last seek time.
1124 if (cueStartTime > max(m_lastSeekTime, lastTime) && cueEndTime < movieTime)
1125 missedCues.append(potentiallySkippedCues[i]);
1129 m_lastTextTrackUpdateTime = movieTime;
1131 // 5 - If the time was reached through the usual monotonic increase of the
1132 // current playback position during normal playback, and if the user agent
1133 // has not fired a timeupdate event at the element in the past 15 to 250ms
1134 // and is not still running event handlers for such an event, then the user
1135 // agent must queue a task to fire a simple event named timeupdate at the
1136 // element. (In the other cases, such as explicit seeks, relevant events get
1137 // fired as part of the overall process of changing the current playback
1139 if (m_lastSeekTime <= lastTime)
1140 scheduleTimeupdateEvent(false);
1142 // Explicitly cache vector sizes, as their content is constant from here.
1143 size_t currentCuesSize = currentCues.size();
1144 size_t missedCuesSize = missedCues.size();
1145 size_t previousCuesSize = previousCues.size();
1147 // 6 - If all of the cues in current cues have their text track cue active
1148 // flag set, none of the cues in other cues have their text track cue active
1149 // flag set, and missed cues is empty, then abort these steps.
1150 bool activeSetChanged = missedCuesSize;
1152 for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i)
1153 if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive())
1154 activeSetChanged = true;
1156 for (size_t i = 0; i < currentCuesSize; ++i) {
1157 currentCues[i].data()->updateDisplayTree(movieTime);
1159 if (!currentCues[i].data()->isActive())
1160 activeSetChanged = true;
1163 if (!activeSetChanged) {
1164 // Even though the active set has not changed, it is possible that the
1165 // the mode of a track has changed from 'hidden' to 'showing' and the
1166 // cues have not yet been rendered.
1167 if (hasMediaControls())
1168 mediaControls()->updateTextTrackDisplay();
1173 // 7 - If the time was reached through the usual monotonic increase of the
1174 // current playback position during normal playback, and there are cues in
1175 // other cues that have their text track cue pause-on-exi flag set and that
1176 // either have their text track cue active flag set or are also in missed
1177 // cues, then immediately pause the media element.
1178 for (size_t i = 0; !m_paused && i < previousCuesSize; ++i) {
1179 if (previousCues[i].data()->pauseOnExit()
1180 && previousCues[i].data()->isActive()
1181 && !currentCues.contains(previousCues[i]))
1185 for (size_t i = 0; !m_paused && i < missedCuesSize; ++i) {
1186 if (missedCues[i].data()->pauseOnExit())
1190 // 8 - Let events be a list of tasks, initially empty. Each task in this
1191 // list will be associated with a text track, a text track cue, and a time,
1192 // which are used to sort the list before the tasks are queued.
1193 Vector<std::pair<double, TextTrackCue*> > eventTasks;
1195 // 8 - Let affected tracks be a list of text tracks, initially empty.
1196 Vector<TextTrack*> affectedTracks;
1198 for (size_t i = 0; i < missedCuesSize; ++i) {
1199 // 9 - For each text track cue in missed cues, prepare an event named enter
1200 // for the TextTrackCue object with the text track cue start time.
1201 eventTasks.append(std::make_pair(missedCues[i].data()->startTime(),
1202 missedCues[i].data()));
1204 // 10 - For each text track [...] in missed cues, prepare an event
1205 // named exit for the TextTrackCue object with the with the later of
1206 // the text track cue end time and the text track cue start time.
1208 // Note: An explicit task is added only if the cue is NOT a zero or
1209 // negative length cue. Otherwise, the need for an exit event is
1210 // checked when these tasks are actually queued below. This doesn't
1211 // affect sorting events before dispatch either, because the exit
1212 // event has the same time as the enter event.
1213 if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime())
1214 eventTasks.append(std::make_pair(missedCues[i].data()->endTime(),
1215 missedCues[i].data()));
1218 for (size_t i = 0; i < previousCuesSize; ++i) {
1219 // 10 - For each text track cue in other cues that has its text
1220 // track cue active flag set prepare an event named exit for the
1221 // TextTrackCue object with the text track cue end time.
1222 if (!currentCues.contains(previousCues[i]))
1223 eventTasks.append(std::make_pair(previousCues[i].data()->endTime(),
1224 previousCues[i].data()));
1227 for (size_t i = 0; i < currentCuesSize; ++i) {
1228 // 11 - For each text track cue in current cues that does not have its
1229 // text track cue active flag set, prepare an event named enter for the
1230 // TextTrackCue object with the text track cue start time.
1231 if (!previousCues.contains(currentCues[i]))
1232 eventTasks.append(std::make_pair(currentCues[i].data()->startTime(),
1233 currentCues[i].data()));
1236 // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1238 nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);
1240 for (size_t i = 0; i < eventTasks.size(); ++i) {
1241 if (!affectedTracks.contains(eventTasks[i].second->track()))
1242 affectedTracks.append(eventTasks[i].second->track());
1244 // 13 - Queue each task in events, in list order.
1245 RefPtr<Event> event;
1247 // Each event in eventTasks may be either an enterEvent or an exitEvent,
1248 // depending on the time that is associated with the event. This
1249 // correctly identifies the type of the event, if the startTime is
1250 // less than the endTime in the cue.
1251 if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) {
1252 event = Event::create(eventNames().enterEvent, false, false);
1253 event->setTarget(eventTasks[i].second);
1254 m_asyncEventQueue->enqueueEvent(event.release());
1256 event = Event::create(eventNames().exitEvent, false, false);
1257 event->setTarget(eventTasks[i].second);
1258 m_asyncEventQueue->enqueueEvent(event.release());
1260 if (eventTasks[i].first == eventTasks[i].second->startTime())
1261 event = Event::create(eventNames().enterEvent, false, false);
1263 event = Event::create(eventNames().exitEvent, false, false);
1265 event->setTarget(eventTasks[i].second);
1266 m_asyncEventQueue->enqueueEvent(event.release());
1270 // 14 - Sort affected tracks in the same order as the text tracks appear in
1271 // the media element's list of text tracks, and remove duplicates.
1272 nonCopyingSort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare);
1274 // 15 - For each text track in affected tracks, in the list order, queue a
1275 // task to fire a simple event named cuechange at the TextTrack object, and, ...
1276 for (size_t i = 0; i < affectedTracks.size(); ++i) {
1277 RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
1278 event->setTarget(affectedTracks[i]);
1280 m_asyncEventQueue->enqueueEvent(event.release());
1282 // ... if the text track has a corresponding track element, to then fire a
1283 // simple event named cuechange at the track element as well.
1284 if (affectedTracks[i]->trackType() == TextTrack::TrackElement) {
1285 RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
1286 HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(affectedTracks[i])->trackElement();
1287 ASSERT(trackElement);
1288 event->setTarget(trackElement);
1290 m_asyncEventQueue->enqueueEvent(event.release());
1294 // 16 - Set the text track cue active flag of all the cues in the current
1295 // cues, and unset the text track cue active flag of all the cues in the
1297 for (size_t i = 0; i < currentCuesSize; ++i)
1298 currentCues[i].data()->setIsActive(true);
1300 for (size_t i = 0; i < previousCuesSize; ++i)
1301 if (!currentCues.contains(previousCues[i]))
1302 previousCues[i].data()->setIsActive(false);
1304 // Update the current active cues.
1305 m_currentlyActiveCues = currentCues;
1307 if (activeSetChanged && hasMediaControls())
1308 mediaControls()->updateTextTrackDisplay();
1311 bool HTMLMediaElement::textTracksAreReady() const
1313 // 4.8.10.12.1 Text track model
1315 // The text tracks of a media element are ready if all the text tracks whose mode was not
1316 // in the disabled state when the element's resource selection algorithm last started now
1317 // have a text track readiness state of loaded or failed to load.
1318 for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) {
1319 if (m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::Loading)
1326 void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track)
1328 if (m_player && m_textTracksWhenResourceSelectionBegan.contains(track)) {
1329 if (track->readinessState() != TextTrack::Loading)
1330 setReadyState(m_player->readyState());
1334 void HTMLMediaElement::textTrackModeChanged(TextTrack* track)
1336 if (track->trackType() == TextTrack::TrackElement) {
1337 // 4.8.10.12.3 Sourcing out-of-band text tracks
1338 // ... when a text track corresponding to a track element is created with text track
1339 // mode set to disabled and subsequently changes its text track mode to hidden, showing,
1340 // or showing by default for the first time, the user agent must immediately and synchronously
1341 // run the following algorithm ...
1343 for (Node* node = firstChild(); node; node = node->nextSibling()) {
1344 if (!node->hasTagName(trackTag))
1346 HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node);
1347 if (trackElement->track() != track)
1350 // Mark this track as "configured" so configureTextTracks won't change the mode again.
1351 trackElement->setHasBeenConfigured(true);
1352 if (track->mode() != TextTrack::disabledKeyword()) {
1353 if (trackElement->readyState() == HTMLTrackElement::LOADED)
1354 textTrackAddCues(track, track->cues());
1355 else if (trackElement->readyState() == HTMLTrackElement::NONE)
1356 trackElement->scheduleLoad();
1358 // If this is the first added track, create the list of text tracks.
1360 m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
1366 configureTextTrackDisplay();
1367 updateActiveTextTrackCues(currentTime());
1370 void HTMLMediaElement::textTrackKindChanged(TextTrack* track)
1372 if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword() && track->mode() == TextTrack::showingKeyword())
1373 track->setMode(TextTrack::hiddenKeyword());
1376 void HTMLMediaElement::textTrackAddCues(TextTrack*, const TextTrackCueList* cues)
1378 beginIgnoringTrackDisplayUpdateRequests();
1379 for (size_t i = 0; i < cues->length(); ++i)
1380 textTrackAddCue(cues->item(i)->track(), cues->item(i));
1381 endIgnoringTrackDisplayUpdateRequests();
1382 updateActiveTextTrackCues(currentTime());
1385 void HTMLMediaElement::textTrackRemoveCues(TextTrack*, const TextTrackCueList* cues)
1387 beginIgnoringTrackDisplayUpdateRequests();
1388 for (size_t i = 0; i < cues->length(); ++i)
1389 textTrackRemoveCue(cues->item(i)->track(), cues->item(i));
1390 endIgnoringTrackDisplayUpdateRequests();
1391 updateActiveTextTrackCues(currentTime());
1394 void HTMLMediaElement::textTrackAddCue(TextTrack*, PassRefPtr<TextTrackCue> cue)
1396 // Negative duration cues need be treated in the interval tree as
1397 // zero-length cues.
1398 double endTime = max(cue->startTime(), cue->endTime());
1400 m_cueTree.add(m_cueTree.createInterval(cue->startTime(), endTime, cue.get()));
1401 updateActiveTextTrackCues(currentTime());
1404 void HTMLMediaElement::textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue> cue)
1406 // Negative duration cues need to be treated in the interval tree as
1407 // zero-length cues.
1408 double endTime = max(cue->startTime(), cue->endTime());
1410 m_cueTree.remove(m_cueTree.createInterval(cue->startTime(), endTime, cue.get()));
1411 updateActiveTextTrackCues(currentTime());
1416 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidURLAction actionIfInvalid)
1418 if (!url.isValid()) {
1419 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLogging(url).utf8().data());
1423 Frame* frame = document()->frame();
1424 if (!frame || !document()->securityOrigin()->canDisplay(url)) {
1425 if (actionIfInvalid == Complain)
1426 FrameLoader::reportLocalLoadFailed(frame, url.string());
1427 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLogging(url).utf8().data());
1431 if (!document()->contentSecurityPolicy()->allowMediaFromSource(url)) {
1432 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> rejected by Content Security Policy", urlForLogging(url).utf8().data());
1439 void HTMLMediaElement::startProgressEventTimer()
1441 if (m_progressEventTimer.isActive())
1444 m_previousProgressTime = WTF::currentTime();
1445 // 350ms is not magic, it is in the spec!
1446 m_progressEventTimer.startRepeating(0.350);
1449 void HTMLMediaElement::waitForSourceChange()
1451 LOG(Media, "HTMLMediaElement::waitForSourceChange");
1453 stopPeriodicTimers();
1454 m_loadState = WaitingForSource;
1456 // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
1457 m_networkState = NETWORK_NO_SOURCE;
1459 // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1460 setShouldDelayLoadEvent(false);
1462 updateDisplayState();
1465 renderer()->updateFromElement();
1468 void HTMLMediaElement::noneSupported()
1470 LOG(Media, "HTMLMediaElement::noneSupported");
1472 stopPeriodicTimers();
1473 m_loadState = WaitingForSource;
1474 m_currentSourceNode = 0;
1477 // 6 - Reaching this step indicates that the media resource failed to load or that the given
1478 // URL could not be resolved. In one atomic operation, run the following steps:
1480 // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to
1481 // MEDIA_ERR_SRC_NOT_SUPPORTED.
1482 m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
1484 // 6.2 - Forget the media element's media-resource-specific text tracks.
1486 // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
1487 m_networkState = NETWORK_NO_SOURCE;
1489 // 7 - Queue a task to fire a simple event named error at the media element.
1490 scheduleEvent(eventNames().errorEvent);
1492 #if ENABLE(MEDIA_SOURCE)
1493 if (m_sourceState != SOURCE_CLOSED)
1494 setSourceState(SOURCE_CLOSED);
1497 // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1498 setShouldDelayLoadEvent(false);
1500 // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed,
1501 // the element won't attempt to load another resource.
1503 updateDisplayState();
1506 renderer()->updateFromElement();
1509 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
1511 LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code()));
1513 // 1 - The user agent should cancel the fetching process.
1514 stopPeriodicTimers();
1515 m_loadState = WaitingForSource;
1517 // 2 - Set the error attribute to a new MediaError object whose code attribute is
1518 // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
1521 // 3 - Queue a task to fire a simple event named error at the media element.
1522 scheduleEvent(eventNames().errorEvent);
1524 #if ENABLE(MEDIA_SOURCE)
1525 if (m_sourceState != SOURCE_CLOSED)
1526 setSourceState(SOURCE_CLOSED);
1529 // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
1530 // task to fire a simple event called emptied at the element.
1531 m_networkState = NETWORK_EMPTY;
1532 scheduleEvent(eventNames().emptiedEvent);
1534 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1535 setShouldDelayLoadEvent(false);
1537 // 6 - Abort the overall resource selection algorithm.
1538 m_currentSourceNode = 0;
1539 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1541 power_unlock_state(POWER_STATE_NORMAL);
1545 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
1547 LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks");
1548 m_asyncEventQueue->cancelAllEvents();
1550 for (Node* node = firstChild(); node; node = node->nextSibling()) {
1551 if (node->hasTagName(sourceTag))
1552 static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent();
1556 Document* HTMLMediaElement::mediaPlayerOwningDocument()
1558 Document* d = document();
1561 d = ownerDocument();
1566 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
1568 beginProcessingMediaPlayerCallback();
1569 setNetworkState(m_player->networkState());
1570 endProcessingMediaPlayerCallback();
1573 static String stringForNetworkState(MediaPlayer::NetworkState state)
1576 case MediaPlayer::Empty: return "Empty";
1577 case MediaPlayer::Idle: return "Idle";
1578 case MediaPlayer::Loading: return "Loading";
1579 case MediaPlayer::Loaded: return "Loaded";
1580 case MediaPlayer::FormatError: return "FormatError";
1581 case MediaPlayer::NetworkError: return "NetworkError";
1582 case MediaPlayer::DecodeError: return "DecodeError";
1583 default: return emptyString();
1587 void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error)
1589 stopPeriodicTimers();
1591 // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
1592 // <source> children, schedule the next one
1593 if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
1595 if (m_currentSourceNode)
1596 m_currentSourceNode->scheduleErrorEvent();
1598 LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed");
1600 if (havePotentialSourceChild()) {
1601 LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>");
1602 scheduleNextSourceChild();
1604 LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting");
1605 waitForSourceChange();
1611 if (error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA)
1612 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
1613 else if (error == MediaPlayer::DecodeError)
1614 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
1615 else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr)
1618 updateDisplayState();
1619 if (hasMediaControls()) {
1620 mediaControls()->reset();
1621 mediaControls()->reportedError();
1624 if (document()->page() && document()->page()->settings()->diagnosticLoggingEnabled())
1625 document()->page()->chrome()->client()->logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadingFailedKey(), stringForNetworkState(error), DiagnosticLoggingKeys::failKey());
1626 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
1628 power_unlock_state(POWER_STATE_NORMAL);
1632 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
1634 LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState));
1636 if (state == MediaPlayer::Empty) {
1637 // Just update the cached state and leave, we can't do anything.
1638 m_networkState = NETWORK_EMPTY;
1642 if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
1643 mediaLoadingFailed(state);
1647 if (state == MediaPlayer::Idle) {
1648 if (m_networkState > NETWORK_IDLE) {
1649 changeNetworkStateFromLoadingToIdle();
1650 setShouldDelayLoadEvent(false);
1652 m_networkState = NETWORK_IDLE;
1656 if (state == MediaPlayer::Loading) {
1657 if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
1658 startProgressEventTimer();
1659 m_networkState = NETWORK_LOADING;
1662 if (state == MediaPlayer::Loaded) {
1663 if (m_networkState != NETWORK_IDLE)
1664 changeNetworkStateFromLoadingToIdle();
1665 m_completelyLoaded = true;
1668 if (hasMediaControls())
1669 mediaControls()->updateStatusDisplay();
1672 void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
1674 m_progressEventTimer.stop();
1675 if (hasMediaControls() && m_player->didLoadingProgress())
1676 mediaControls()->bufferingProgressed();
1678 // Schedule one last progress event so we guarantee that at least one is fired
1679 // for files that load very quickly.
1680 scheduleEvent(eventNames().progressEvent);
1681 scheduleEvent(eventNames().suspendEvent);
1682 m_networkState = NETWORK_IDLE;
1685 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
1687 beginProcessingMediaPlayerCallback();
1689 setReadyState(m_player->readyState());
1691 endProcessingMediaPlayerCallback();
1694 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
1696 LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState));
1698 // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
1699 bool wasPotentiallyPlaying = potentiallyPlaying();
1701 ReadyState oldState = m_readyState;
1702 ReadyState newState = static_cast<ReadyState>(state);
1704 #if ENABLE(VIDEO_TRACK)
1705 bool tracksAreReady = !RuntimeEnabledFeatures::webkitVideoTrackEnabled() || textTracksAreReady();
1707 if (newState == oldState && m_tracksAreReady == tracksAreReady)
1710 m_tracksAreReady = tracksAreReady;
1712 if (newState == oldState)
1714 bool tracksAreReady = true;
1718 m_readyState = newState;
1720 // If a media file has text tracks the readyState may not progress beyond HAVE_FUTURE_DATA until
1721 // the text tracks are ready, regardless of the state of the media file.
1722 if (newState <= HAVE_METADATA)
1723 m_readyState = newState;
1725 m_readyState = HAVE_CURRENT_DATA;
1728 if (oldState > m_readyStateMaximum)
1729 m_readyStateMaximum = oldState;
1731 if (m_networkState == NETWORK_EMPTY)
1735 // 4.8.10.9, step 11
1736 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
1737 scheduleEvent(eventNames().waitingEvent);
1739 // 4.8.10.10 step 14 & 15.
1740 if (m_readyState >= HAVE_CURRENT_DATA)
1743 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
1745 scheduleTimeupdateEvent(false);
1746 scheduleEvent(eventNames().waitingEvent);
1750 if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
1751 prepareMediaFragmentURI();
1752 scheduleEvent(eventNames().durationchangeEvent);
1753 scheduleEvent(eventNames().loadedmetadataEvent);
1754 if (hasMediaControls())
1755 mediaControls()->loadedMetadata();
1757 renderer()->updateFromElement();
1759 if (document()->page() && document()->page()->settings()->diagnosticLoggingEnabled())
1760 document()->page()->chrome()->client()->logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadedKey(), m_player->engineDescription(), DiagnosticLoggingKeys::noopKey());
1763 bool shouldUpdateDisplayState = false;
1765 if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
1766 m_haveFiredLoadedData = true;
1767 shouldUpdateDisplayState = true;
1768 scheduleEvent(eventNames().loadeddataEvent);
1769 setShouldDelayLoadEvent(false);
1770 applyMediaFragmentURI();
1773 bool isPotentiallyPlaying = potentiallyPlaying();
1774 if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) {
1775 scheduleEvent(eventNames().canplayEvent);
1776 if (isPotentiallyPlaying)
1777 scheduleEvent(eventNames().playingEvent);
1778 shouldUpdateDisplayState = true;
1781 if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) {
1782 if (oldState <= HAVE_CURRENT_DATA)
1783 scheduleEvent(eventNames().canplayEvent);
1785 scheduleEvent(eventNames().canplaythroughEvent);
1787 if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
1788 scheduleEvent(eventNames().playingEvent);
1790 if (m_autoplaying && m_paused && autoplay() && !document()->isSandboxed(SandboxAutomaticFeatures) && !userGestureRequiredForRateChange()) {
1792 invalidateCachedTime();
1793 scheduleEvent(eventNames().playEvent);
1794 scheduleEvent(eventNames().playingEvent);
1797 shouldUpdateDisplayState = true;
1800 if (shouldUpdateDisplayState) {
1801 updateDisplayState();
1802 if (hasMediaControls())
1803 mediaControls()->updateStatusDisplay();
1807 updateMediaController();
1808 #if ENABLE(VIDEO_TRACK)
1809 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
1810 updateActiveTextTrackCues(currentTime());
1814 #if ENABLE(MEDIA_SOURCE)
1815 void HTMLMediaElement::mediaPlayerSourceOpened()
1817 beginProcessingMediaPlayerCallback();
1819 setSourceState(SOURCE_OPEN);
1821 endProcessingMediaPlayerCallback();
1824 String HTMLMediaElement::mediaPlayerSourceURL() const
1826 return m_mediaSourceURL.string();
1829 bool HTMLMediaElement::isValidSourceId(const String& id, ExceptionCode& ec) const
1831 if (id.isNull() || id.isEmpty()) {
1832 ec = INVALID_ACCESS_ERR;
1836 if (!m_sourceIDs.contains(id)) {
1846 #if ENABLE(ENCRYPTED_MEDIA)
1847 void HTMLMediaElement::mediaPlayerKeyAdded(MediaPlayer*, const String& keySystem, const String& sessionId)
1849 MediaKeyEventInit initializer;
1850 initializer.keySystem = keySystem;
1851 initializer.sessionId = sessionId;
1852 initializer.bubbles = false;
1853 initializer.cancelable = false;
1855 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeyaddedEvent, initializer);
1856 event->setTarget(this);
1857 m_asyncEventQueue->enqueueEvent(event.release());
1860 void HTMLMediaElement::mediaPlayerKeyError(MediaPlayer*, const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode)
1862 MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
1863 switch (errorCode) {
1864 case MediaPlayerClient::UnknownError:
1865 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
1867 case MediaPlayerClient::ClientError:
1868 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
1870 case MediaPlayerClient::ServiceError:
1871 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE;
1873 case MediaPlayerClient::OutputError:
1874 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT;
1876 case MediaPlayerClient::HardwareChangeError:
1877 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE;
1879 case MediaPlayerClient::DomainError:
1880 mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN;
1884 MediaKeyEventInit initializer;
1885 initializer.keySystem = keySystem;
1886 initializer.sessionId = sessionId;
1887 initializer.errorCode = MediaKeyError::create(mediaKeyErrorCode);
1888 initializer.systemCode = systemCode;
1889 initializer.bubbles = false;
1890 initializer.cancelable = false;
1892 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeyerrorEvent, initializer);
1893 event->setTarget(this);
1894 m_asyncEventQueue->enqueueEvent(event.release());
1897 void HTMLMediaElement::mediaPlayerKeyMessage(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength)
1899 MediaKeyEventInit initializer;
1900 initializer.keySystem = keySystem;
1901 initializer.sessionId = sessionId;
1902 initializer.message = Uint8Array::create(message, messageLength);
1903 initializer.bubbles = false;
1904 initializer.cancelable = false;
1906 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeymessageEvent, initializer);
1907 event->setTarget(this);
1908 m_asyncEventQueue->enqueueEvent(event.release());
1911 void HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength)
1913 MediaKeyEventInit initializer;
1914 initializer.keySystem = keySystem;
1915 initializer.sessionId = sessionId;
1916 initializer.initData = Uint8Array::create(initData, initDataLength);
1917 initializer.bubbles = false;
1918 initializer.cancelable = false;
1920 RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitneedkeyEvent, initializer);
1921 event->setTarget(this);
1922 m_asyncEventQueue->enqueueEvent(event.release());
1926 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
1929 if (m_networkState != NETWORK_LOADING)
1932 double time = WTF::currentTime();
1933 double timedelta = time - m_previousProgressTime;
1935 if (m_player->didLoadingProgress()) {
1936 scheduleEvent(eventNames().progressEvent);
1937 m_previousProgressTime = time;
1938 m_sentStalledEvent = false;
1940 renderer()->updateFromElement();
1941 if (hasMediaControls())
1942 mediaControls()->bufferingProgressed();
1943 } else if (timedelta > 3.0 && !m_sentStalledEvent) {
1944 scheduleEvent(eventNames().stalledEvent);
1945 m_sentStalledEvent = true;
1946 setShouldDelayLoadEvent(false);
1950 void HTMLMediaElement::createShadowSubtree()
1952 ASSERT(!userAgentShadowRoot());
1953 ShadowRoot::create(this, ShadowRoot::UserAgentShadowRoot);
1956 void HTMLMediaElement::willAddAuthorShadowRoot()
1958 if (!userAgentShadowRoot())
1959 createShadowSubtree();
1962 void HTMLMediaElement::rewind(float timeDelta)
1964 LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta);
1967 setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e);
1970 void HTMLMediaElement::returnToRealtime()
1972 LOG(Media, "HTMLMediaElement::returnToRealtime");
1974 setCurrentTime(maxTimeSeekable(), e);
1977 void HTMLMediaElement::addPlayedRange(float start, float end)
1979 LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end);
1980 if (!m_playedTimeRanges)
1981 m_playedTimeRanges = TimeRanges::create();
1982 m_playedTimeRanges->add(start, end);
1985 bool HTMLMediaElement::supportsSave() const
1987 return m_player ? m_player->supportsSave() : false;
1990 bool HTMLMediaElement::supportsScanning() const
1992 return m_player ? m_player->supportsScanning() : false;
1995 void HTMLMediaElement::prepareToPlay()
1997 LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this);
1998 if (m_havePreparedToPlay)
2000 m_havePreparedToPlay = true;
2001 m_player->prepareToPlay();
2004 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
2006 LOG(Media, "HTMLMediaElement::seek(%f)", time);
2010 // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception.
2011 if (m_readyState == HAVE_NOTHING || !m_player) {
2012 ec = INVALID_STATE_ERR;
2016 // If the media engine has been told to postpone loading data, let it go ahead now.
2017 if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
2020 // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
2021 refreshCachedTime();
2022 float now = currentTime();
2024 // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
2025 // already running. Abort that other instance of the algorithm without waiting for the step that
2026 // it is running to complete.
2027 // Nothing specific to be done here.
2029 // 3 - Set the seeking IDL attribute to true.
2030 // The flag will be cleared when the engine tells us the time has actually changed.
2033 // 5 - If the new playback position is later than the end of the media resource, then let it be the end
2034 // of the media resource instead.
2035 time = min(time, duration());
2037 // 6 - If the new playback position is less than the earliest possible position, let it be that position instead.
2038 float earliestTime = m_player->startTime();
2039 time = max(time, earliestTime);
2041 // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
2042 // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
2043 // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
2044 // not generate a timechanged callback. This means m_seeking will never be cleared and we will never
2045 // fire a 'seeked' event.
2047 float mediaTime = m_player->mediaTimeForTimeValue(time);
2048 if (time != mediaTime)
2049 LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime);
2051 time = m_player->mediaTimeForTimeValue(time);
2053 // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the
2054 // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute
2055 // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
2056 // attribute then set the seeking IDL attribute to false and abort these steps.
2057 RefPtr<TimeRanges> seekableRanges = seekable();
2059 // Short circuit seeking to the current time by just firing the events if no seek is required.
2060 // Don't skip calling the media engine if we are in poster mode because a seek should always
2061 // cancel poster display.
2062 bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster);
2064 #if ENABLE(MEDIA_SOURCE)
2065 // Always notify the media engine of a seek if the source is not closed. This ensures that the source is
2066 // always in a flushed state when the 'seeking' event fires.
2067 if (m_sourceState != SOURCE_CLOSED)
2068 noSeekRequired = false;
2071 if (noSeekRequired) {
2073 scheduleEvent(eventNames().seekingEvent);
2074 scheduleTimeupdateEvent(false);
2075 scheduleEvent(eventNames().seekedEvent);
2080 time = seekableRanges->nearest(time);
2083 if (m_lastSeekTime < now)
2084 addPlayedRange(m_lastSeekTime, now);
2086 m_lastSeekTime = time;
2087 m_sentEndEvent = false;
2089 #if ENABLE(MEDIA_SOURCE)
2090 if (m_sourceState == SOURCE_ENDED)
2091 setSourceState(SOURCE_OPEN);
2094 // 8 - Set the current playback position to the given new playback position
2095 m_player->seek(time);
2097 // 9 - Queue a task to fire a simple event named seeking at the element.
2098 scheduleEvent(eventNames().seekingEvent);
2100 // 10 - Queue a task to fire a simple event named timeupdate at the element.
2101 scheduleTimeupdateEvent(false);
2103 // 11-15 are handled, if necessary, when the engine signals a readystate change.
2106 void HTMLMediaElement::finishSeek()
2108 LOG(Media, "HTMLMediaElement::finishSeek");
2110 // 4.8.10.9 Seeking step 14
2113 // 4.8.10.9 Seeking step 15
2114 scheduleEvent(eventNames().seekedEvent);
2116 setDisplayMode(Video);
2119 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
2121 return m_readyState;
2124 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
2126 return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
2129 bool HTMLMediaElement::hasAudio() const
2131 return m_player ? m_player->hasAudio() : false;
2134 bool HTMLMediaElement::seeking() const
2139 void HTMLMediaElement::refreshCachedTime() const
2141 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2142 if (m_player->currentTime() > 0.0f || m_cachedTime == MediaPlayer::invalidTime())
2144 m_cachedTime = m_player->currentTime();
2145 m_cachedTimeWallClockUpdateTime = WTF::currentTime();
2148 void HTMLMediaElement::invalidateCachedTime()
2150 LOG(Media, "HTMLMediaElement::invalidateCachedTime");
2152 // Don't try to cache movie time when playback first starts as the time reported by the engine
2153 // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
2155 static const double minimumTimePlayingBeforeCacheSnapshot = 0.5;
2157 m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot;
2158 #if !ENABLE(TIZEN_GSTREAMER_VIDEO)
2159 m_cachedTime = MediaPlayer::invalidTime();
2164 float HTMLMediaElement::currentTime() const
2166 #if LOG_CACHED_TIME_WARNINGS
2167 static const double minCachedDeltaForWarning = 0.01;
2174 LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime);
2175 return m_lastSeekTime;
2178 if (m_cachedTime != MediaPlayer::invalidTime() && m_paused) {
2179 #if LOG_CACHED_TIME_WARNINGS
2180 float delta = m_cachedTime - m_player->currentTime();
2181 if (delta > minCachedDeltaForWarning)
2182 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta);
2184 return m_cachedTime;
2187 // Is it too soon use a cached time?
2188 double now = WTF::currentTime();
2189 double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
2191 if (maximumDurationToCacheMediaTime && m_cachedTime != MediaPlayer::invalidTime() && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) {
2192 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime;
2194 // Not too soon, use the cached time only if it hasn't expired.
2195 if (wallClockDelta < maximumDurationToCacheMediaTime) {
2196 float adjustedCacheTime = static_cast<float>(m_cachedTime + (m_playbackRate * wallClockDelta));
2198 #if LOG_CACHED_TIME_WARNINGS
2199 float delta = adjustedCacheTime - m_player->currentTime();
2200 if (delta > minCachedDeltaForWarning)
2201 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when playing", delta);
2203 return adjustedCacheTime;
2207 #if LOG_CACHED_TIME_WARNINGS
2208 if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != MediaPlayer::invalidTime()) {
2209 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime;
2210 float delta = m_cachedTime + (m_playbackRate * wallClockDelta) - m_player->currentTime();
2211 LOG(Media, "HTMLMediaElement::currentTime - cached time was %f seconds off of media time when it expired", delta);
2215 refreshCachedTime();
2217 return m_cachedTime;
2220 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
2222 if (m_mediaController) {
2223 ec = INVALID_STATE_ERR;
2229 float HTMLMediaElement::startTime() const
2233 return m_player->startTime();
2236 double HTMLMediaElement::initialTime() const
2238 if (m_fragmentStartTime != MediaPlayer::invalidTime())
2239 return m_fragmentStartTime;
2244 return m_player->initialTime();
2247 float HTMLMediaElement::duration() const
2249 if (m_player && m_readyState >= HAVE_METADATA)
2250 return m_player->duration();
2252 return numeric_limits<float>::quiet_NaN();
2255 bool HTMLMediaElement::paused() const
2260 float HTMLMediaElement::defaultPlaybackRate() const
2262 return m_defaultPlaybackRate;
2265 void HTMLMediaElement::setDefaultPlaybackRate(float rate)
2267 if (m_defaultPlaybackRate != rate) {
2268 m_defaultPlaybackRate = rate;
2269 scheduleEvent(eventNames().ratechangeEvent);
2273 float HTMLMediaElement::playbackRate() const
2275 return m_playbackRate;
2278 void HTMLMediaElement::setPlaybackRate(float rate)
2280 LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate);
2282 if (m_playbackRate != rate) {
2283 m_playbackRate = rate;
2284 invalidateCachedTime();
2285 scheduleEvent(eventNames().ratechangeEvent);
2288 if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController)
2289 m_player->setRate(rate);
2292 void HTMLMediaElement::updatePlaybackRate()
2294 float effectiveRate = m_mediaController ? m_mediaController->playbackRate() : m_playbackRate;
2295 if (m_player && potentiallyPlaying() && m_player->rate() != effectiveRate)
2296 m_player->setRate(effectiveRate);
2299 bool HTMLMediaElement::webkitPreservesPitch() const
2301 return m_webkitPreservesPitch;
2304 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
2306 LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch));
2308 m_webkitPreservesPitch = preservesPitch;
2313 m_player->setPreservesPitch(preservesPitch);
2316 bool HTMLMediaElement::ended() const
2318 // 4.8.10.8 Playing the media resource
2319 // The ended attribute must return true if the media element has ended
2320 // playback and the direction of playback is forwards, and false otherwise.
2321 return endedPlayback() && m_playbackRate > 0;
2324 bool HTMLMediaElement::autoplay() const
2326 return fastHasAttribute(autoplayAttr);
2329 void HTMLMediaElement::setAutoplay(bool b)
2331 LOG(Media, "HTMLMediaElement::setAutoplay(%s)", boolString(b));
2332 setBooleanAttribute(autoplayAttr, b);
2335 String HTMLMediaElement::preload() const
2337 switch (m_preload) {
2338 case MediaPlayer::None:
2341 case MediaPlayer::MetaData:
2344 case MediaPlayer::Auto:
2349 ASSERT_NOT_REACHED();
2353 void HTMLMediaElement::setPreload(const String& preload)
2355 LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data());
2356 setAttribute(preloadAttr, preload);
2359 void HTMLMediaElement::play()
2361 LOG(Media, "HTMLMediaElement::play()");
2363 if (userGestureRequiredForRateChange() && !ScriptController::processingUserGesture())
2365 if (ScriptController::processingUserGesture())
2366 removeBehaviorsRestrictionsAfterFirstUserGesture();
2368 Settings* settings = document()->settings();
2369 if (settings && settings->needsSiteSpecificQuirks() && m_dispatchingCanPlayEvent && !m_loadInitiatedByUserGesture) {
2370 // It should be impossible to be processing the canplay event while handling a user gesture
2371 // since it is dispatched asynchronously.
2372 ASSERT(!ScriptController::processingUserGesture());
2373 String host = document()->baseURL().host();
2374 if (host.endsWith(".npr.org", false) || equalIgnoringCase(host, "npr.org"))
2378 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2380 power_lock_state(POWER_STATE_NORMAL, 0);
2385 void HTMLMediaElement::playInternal()
2387 LOG(Media, "HTMLMediaElement::playInternal");
2389 // 4.8.10.9. Playing the media resource
2390 if (!m_player || m_networkState == NETWORK_EMPTY)
2391 scheduleLoad(MediaResource);
2393 if (endedPlayback()) {
2394 ExceptionCode unused;
2398 if (m_mediaController)
2399 m_mediaController->bringElementUpToSpeed(this);
2403 invalidateCachedTime();
2404 scheduleEvent(eventNames().playEvent);
2406 if (m_readyState <= HAVE_CURRENT_DATA)
2407 scheduleEvent(eventNames().waitingEvent);
2408 else if (m_readyState >= HAVE_FUTURE_DATA)
2409 scheduleEvent(eventNames().playingEvent);
2411 m_autoplaying = false;
2414 updateMediaController();
2417 void HTMLMediaElement::pause()
2419 LOG(Media, "HTMLMediaElement::pause()");
2421 if (userGestureRequiredForRateChange() && !ScriptController::processingUserGesture())
2424 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
2426 power_unlock_state(POWER_STATE_NORMAL);
2432 void HTMLMediaElement::pauseInternal()
2434 LOG(Media, "HTMLMediaElement::pauseInternal");
2436 // 4.8.10.9. Playing the media resource
2437 if (!m_player || m_networkState == NETWORK_EMPTY)
2438 scheduleLoad(MediaResource);
2440 m_autoplaying = false;
2444 scheduleTimeupdateEvent(false);
2445 scheduleEvent(eventNames().pauseEvent);
2451 #if ENABLE(MEDIA_SOURCE)
2453 void HTMLMediaElement::webkitSourceAddId(const String& id, const String& type, ExceptionCode& ec)
2455 if (id.isNull() || id.isEmpty()) {
2456 ec = INVALID_ACCESS_ERR;
2460 if (m_sourceIDs.contains(id)) {
2461 ec = INVALID_STATE_ERR;
2465 if (type.isNull() || type.isEmpty()) {
2466 ec = INVALID_ACCESS_ERR;
2470 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
2471 ec = INVALID_STATE_ERR;
2475 ContentType contentType(type);
2476 Vector<String> codecs = contentType.codecs();
2478 if (!codecs.size()) {
2479 ec = NOT_SUPPORTED_ERR;
2483 switch (m_player->sourceAddId(id, contentType.type(), codecs)) {
2484 case MediaPlayer::Ok:
2485 m_sourceIDs.add(id);
2487 case MediaPlayer::NotSupported:
2488 ec = NOT_SUPPORTED_ERR;
2490 case MediaPlayer::ReachedIdLimit:
2491 ec = QUOTA_EXCEEDED_ERR;
2495 ASSERT_NOT_REACHED();
2498 void HTMLMediaElement::webkitSourceRemoveId(const String& id, ExceptionCode& ec)
2500 if (!isValidSourceId(id, ec))
2503 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState == SOURCE_CLOSED) {
2504 ec = INVALID_STATE_ERR;
2508 if (!m_player->sourceRemoveId(id))
2509 ASSERT_NOT_REACHED();
2511 m_sourceIDs.remove(id);
2514 PassRefPtr<TimeRanges> HTMLMediaElement::webkitSourceBuffered(const String& id, ExceptionCode& ec)
2516 if (!isValidSourceId(id, ec))
2519 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState == SOURCE_CLOSED) {
2520 ec = INVALID_STATE_ERR;
2524 return m_player->sourceBuffered(id);
2527 void HTMLMediaElement::webkitSourceAppend(const String& id, PassRefPtr<Uint8Array> data, ExceptionCode& ec)
2529 if (!isValidSourceId(id, ec))
2532 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
2533 ec = INVALID_STATE_ERR;
2538 ec = INVALID_ACCESS_ERR;
2542 if (!data->length())
2545 if (!m_player->sourceAppend(id, data->data(), data->length())) {
2551 void HTMLMediaElement::webkitSourceAbort(const String& id, ExceptionCode& ec)
2553 if (!isValidSourceId(id, ec))
2556 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
2557 ec = INVALID_STATE_ERR;
2561 if (!m_player->sourceAbort(id))
2562 ASSERT_NOT_REACHED();
2565 void HTMLMediaElement::webkitSourceEndOfStream(unsigned short status, ExceptionCode& ec)
2567 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
2568 ec = INVALID_STATE_ERR;
2572 MediaPlayer::EndOfStreamStatus eosStatus = MediaPlayer::EosNoError;
2576 eosStatus = MediaPlayer::EosNoError;
2578 case EOS_NETWORK_ERR:
2579 eosStatus = MediaPlayer::EosNetworkError;
2581 case EOS_DECODE_ERR:
2582 eosStatus = MediaPlayer::EosDecodeError;
2589 setSourceState(SOURCE_ENDED);
2590 m_player->sourceEndOfStream(eosStatus);
2593 HTMLMediaElement::SourceState HTMLMediaElement::webkitSourceState() const
2595 return m_sourceState;
2598 void HTMLMediaElement::setSourceState(SourceState state)
2600 SourceState oldState = m_sourceState;
2601 m_sourceState = static_cast<SourceState>(state);
2603 if (m_sourceState == oldState)
2606 if (m_sourceState == SOURCE_CLOSED) {
2607 m_sourceIDs.clear();
2608 scheduleEvent(eventNames().webkitsourcecloseEvent);
2612 if (oldState == SOURCE_OPEN && m_sourceState == SOURCE_ENDED) {
2613 scheduleEvent(eventNames().webkitsourceendedEvent);
2617 if (m_sourceState == SOURCE_OPEN) {
2618 scheduleEvent(eventNames().webkitsourceopenEvent);
2624 #if ENABLE(ENCRYPTED_MEDIA)
2625 void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionCode& ec)
2627 if (keySystem.isEmpty()) {
2633 ec = INVALID_STATE_ERR;
2637 const unsigned char* initDataPointer = 0;
2638 unsigned initDataLength = 0;
2640 initDataPointer = initData->data();
2641 initDataLength = initData->length();
2644 MediaPlayer::MediaKeyException result = m_player->generateKeyRequest(keySystem, initDataPointer, initDataLength);
2645 ec = exceptionCodeForMediaKeyException(result);
2648 void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, ExceptionCode& ec)
2650 webkitGenerateKeyRequest(keySystem, Uint8Array::create(0), ec);
2653 void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionCode& ec)
2655 if (keySystem.isEmpty()) {
2665 if (!key->length()) {
2666 ec = TYPE_MISMATCH_ERR;
2671 ec = INVALID_STATE_ERR;
2675 const unsigned char* initDataPointer = 0;
2676 unsigned initDataLength = 0;
2678 initDataPointer = initData->data();
2679 initDataLength = initData->length();
2682 MediaPlayer::MediaKeyException result = m_player->addKey(keySystem, key->data(), key->length(), initDataPointer, initDataLength, sessionId);
2683 ec = exceptionCodeForMediaKeyException(result);
2686 void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, ExceptionCode& ec)
2688 webkitAddKey(keySystem, key, Uint8Array::create(0), String(), ec);
2691 void HTMLMediaElement::webkitCancelKeyRequest(const String& keySystem, const String& sessionId, ExceptionCode& ec)
2693 if (keySystem.isEmpty()) {
2699 ec = INVALID_STATE_ERR;
2703 MediaPlayer::MediaKeyException result = m_player->cancelKeyRequest(keySystem, sessionId);
2704 ec = exceptionCodeForMediaKeyException(result);
2709 bool HTMLMediaElement::loop() const
2711 return fastHasAttribute(loopAttr);
2714 void HTMLMediaElement::setLoop(bool b)
2716 LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b));
2717 setBooleanAttribute(loopAttr, b);
2719 updateDisableSleep();
2723 bool HTMLMediaElement::controls() const
2725 Frame* frame = document()->frame();
2727 // always show controls when scripting is disabled
2728 if (frame && !frame->script()->canExecuteScripts(NotAboutToExecuteScript))
2731 // always show controls for video when fullscreen playback is required.
2732 if (isVideo() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback())
2735 // Always show controls when in full screen mode.
2739 return fastHasAttribute(controlsAttr);
2742 void HTMLMediaElement::setControls(bool b)
2744 LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b));
2745 setBooleanAttribute(controlsAttr, b);
2748 float HTMLMediaElement::volume() const
2753 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
2755 LOG(Media, "HTMLMediaElement::setVolume(%f)", vol);
2757 if (vol < 0.0f || vol > 1.0f) {
2758 ec = INDEX_SIZE_ERR;
2762 if (m_volume != vol) {
2765 scheduleEvent(eventNames().volumechangeEvent);
2769 bool HTMLMediaElement::muted() const
2774 void HTMLMediaElement::setMuted(bool muted)
2776 LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted));
2778 if (m_muted != muted) {
2780 // Avoid recursion when the player reports volume changes.
2781 if (!processingMediaPlayerCallback()) {
2783 m_player->setMuted(m_muted);
2784 if (hasMediaControls())
2785 mediaControls()->changedMute();
2788 scheduleEvent(eventNames().volumechangeEvent);
2792 void HTMLMediaElement::togglePlayState()
2794 LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay()));
2796 // We can safely call the internal play/pause methods, which don't check restrictions, because
2797 // this method is only called from the built-in media controller
2799 updatePlaybackRate();
2805 void HTMLMediaElement::beginScrubbing()
2807 LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused()));
2811 // Because a media element stays in non-paused state when it reaches end, playback resumes
2812 // when the slider is dragged from the end to another position unless we pause first. Do
2813 // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
2816 // Not at the end but we still want to pause playback so the media engine doesn't try to
2817 // continue playing during scrubbing. Pause without generating an event as we will
2818 // unpause after scrubbing finishes.
2819 setPausedInternal(true);
2824 void HTMLMediaElement::endScrubbing()
2826 LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal));
2828 if (m_pausedInternal)
2829 setPausedInternal(false);
2832 // The spec says to fire periodic timeupdate events (those sent while playing) every
2833 // "15 to 250ms", we choose the slowest frequency
2834 static const double maxTimeupdateEventFrequency = 0.25;
2836 void HTMLMediaElement::startPlaybackProgressTimer()
2838 if (m_playbackProgressTimer.isActive())
2841 m_previousProgressTime = WTF::currentTime();
2842 m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
2845 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
2849 if (m_fragmentEndTime != MediaPlayer::invalidTime() && currentTime() >= m_fragmentEndTime && m_playbackRate > 0) {
2850 m_fragmentEndTime = MediaPlayer::invalidTime();
2851 if (!m_mediaController && !m_paused) {
2852 // changes paused to true and fires a simple event named pause at the media element.
2857 scheduleTimeupdateEvent(true);
2859 if (!m_playbackRate)
2862 if (!m_paused && hasMediaControls())
2863 mediaControls()->playbackProgressed();
2865 #if ENABLE(VIDEO_TRACK)
2866 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2867 updateActiveTextTrackCues(currentTime());
2871 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
2873 double now = WTF::currentTime();
2874 double timedelta = now - m_lastTimeUpdateEventWallTime;
2876 // throttle the periodic events
2877 if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
2880 // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
2881 // event at a given time so filter here
2882 float movieTime = currentTime();
2883 if (movieTime != m_lastTimeUpdateEventMovieTime) {
2884 scheduleEvent(eventNames().timeupdateEvent);
2885 m_lastTimeUpdateEventWallTime = now;
2886 m_lastTimeUpdateEventMovieTime = movieTime;
2890 bool HTMLMediaElement::canPlay() const
2892 return paused() || ended() || m_readyState < HAVE_METADATA;
2895 float HTMLMediaElement::percentLoaded() const
2899 float duration = m_player->duration();
2901 if (!duration || isinf(duration))
2905 RefPtr<TimeRanges> timeRanges = m_player->buffered();
2906 for (unsigned i = 0; i < timeRanges->length(); ++i) {
2907 ExceptionCode ignoredException;
2908 float start = timeRanges->start(i, ignoredException);
2909 float end = timeRanges->end(i, ignoredException);
2910 buffered += end - start;
2912 return buffered / duration;
2915 #if ENABLE(VIDEO_TRACK)
2916 PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const String& kind, const String& label, const String& language, ExceptionCode& ec)
2918 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2921 // 4.8.10.12.4 Text track API
2922 // The addTextTrack(kind, label, language) method of media elements, when invoked, must run the following steps:
2924 // 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps
2925 if (!TextTrack::isValidKindKeyword(kind)) {
2930 // 2. If the label argument was omitted, let label be the empty string.
2931 // 3. If the language argument was omitted, let language be the empty string.
2932 // 4. Create a new TextTrack object.
2934 // 5. Create a new text track corresponding to the new object, and set its text track kind to kind, its text
2935 // track label to label, its text track language to language...
2936 RefPtr<TextTrack> textTrack = TextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, kind, label, language);
2938 // Note, due to side effects when changing track parameters, we have to
2939 // first append the track to the text track list.
2941 // 6. Add the new text track to the media element's list of text tracks.
2942 textTracks()->append(textTrack);
2944 // ... its text track readiness state to the text track loaded state ...
2945 textTrack->setReadinessState(TextTrack::Loaded);
2947 // ... its text track mode to the text track hidden mode, and its text track list of cues to an empty list ...
2948 textTrack->setMode(TextTrack::hiddenKeyword());
2950 return textTrack.release();
2953 TextTrackList* HTMLMediaElement::textTracks()
2955 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2959 m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
2961 return m_textTracks.get();
2964 HTMLTrackElement* HTMLMediaElement::showingTrackWithSameKind(HTMLTrackElement* trackElement) const
2966 for (Node* node = firstChild(); node; node = node->nextSibling()) {
2967 if (trackElement == node)
2969 if (!node->hasTagName(trackTag))
2972 HTMLTrackElement* showingTrack = static_cast<HTMLTrackElement*>(node);
2973 if (showingTrack->kind() == trackElement->kind() && showingTrack->track()->mode() == TextTrack::showingKeyword())
2974 return showingTrack;
2980 void HTMLMediaElement::didAddTrack(HTMLTrackElement* trackElement)
2982 ASSERT(trackElement->hasTagName(trackTag));
2984 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2987 // 4.8.10.12.3 Sourcing out-of-band text tracks
2988 // When a track element's parent element changes and the new parent is a media element,
2989 // then the user agent must add the track element's corresponding text track to the
2990 // media element's list of text tracks ... [continues in TextTrackList::append]
2991 RefPtr<TextTrack> textTrack = trackElement->track();
2995 textTracks()->append(textTrack);
2997 // Do not schedule the track loading until parsing finishes so we don't start before all tracks
2998 // in the markup have been added.
2999 if (!m_parsingInProgress)
3000 scheduleLoad(TextTrackResource);
3003 void HTMLMediaElement::willRemoveTrack(HTMLTrackElement* trackElement)
3005 ASSERT(trackElement->hasTagName(trackTag));
3007 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3011 if (trackElement->hasTagName(trackTag)) {
3012 KURL url = trackElement->getNonEmptyURLAttribute(srcAttr);
3013 LOG(Media, "HTMLMediaElement::willRemoveTrack - 'src' is %s", urlForLogging(url).utf8().data());
3017 trackElement->setHasBeenConfigured(false);
3022 RefPtr<TextTrack> textTrack = trackElement->track();
3026 // 4.8.10.12.3 Sourcing out-of-band text tracks
3027 // When a track element's parent element changes and the old parent was a media element,
3028 // then the user agent must remove the track element's corresponding text track from the
3029 // media element's list of text tracks.
3030 m_textTracks->remove(textTrack.get());
3031 size_t index = m_textTracksWhenResourceSelectionBegan.find(textTrack.get());
3032 if (index != notFound)
3033 m_textTracksWhenResourceSelectionBegan.remove(index);
3036 bool HTMLMediaElement::userIsInterestedInThisLanguage(const String&) const
3038 // FIXME: check the user's language preference - bugs.webkit.org/show_bug.cgi?id=74121
3042 bool HTMLMediaElement::userIsInterestedInThisTrackKind(String kind) const
3044 // If ... the user has indicated an interest in having a track with this text track kind, text track language, ...
3045 if (m_disableCaptions)
3048 Settings* settings = document()->settings();
3052 if (kind == TextTrack::subtitlesKeyword())
3053 return settings->shouldDisplaySubtitles() || m_closedCaptionsVisible;
3054 if (kind == TextTrack::captionsKeyword())
3055 return settings->shouldDisplayCaptions() || m_closedCaptionsVisible;
3056 if (kind == TextTrack::descriptionsKeyword())
3057 return settings->shouldDisplayTextDescriptions() || m_closedCaptionsVisible;
3062 void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group) const
3064 ASSERT(group.tracks.size());
3066 String bestMatchingLanguage;
3067 if (group.hasSrcLang) {
3068 Vector<String> languages;
3069 languages.reserveInitialCapacity(group.tracks.size());
3070 for (size_t i = 0; i < group.tracks.size(); ++i) {
3071 String srcLanguage = group.tracks[i]->track()->language();
3072 if (srcLanguage.length())
3073 languages.append(srcLanguage);
3075 bestMatchingLanguage = preferredLanguageFromList(languages);
3078 // First, find the track in the group that should be enabled (if any).
3079 HTMLTrackElement* trackElementToEnable = 0;
3080 HTMLTrackElement* defaultTrack = 0;
3081 HTMLTrackElement* fallbackTrack = 0;
3082 for (size_t i = 0; !trackElementToEnable && i < group.tracks.size(); ++i) {
3083 HTMLTrackElement* trackElement = group.tracks[i];
3084 RefPtr<TextTrack> textTrack = trackElement->track();
3086 if (userIsInterestedInThisTrackKind(textTrack->kind())) {
3087 // * If the text track kind is { [subtitles or captions] [descriptions] } and the user has indicated an interest in having a
3088 // track with this text track kind, text track language, and text track label enabled, and there is no
3089 // other text track in the media element's list of text tracks with a text track kind of either subtitles
3090 // or captions whose text track mode is showing
3092 // * If the text track kind is chapters and the text track language is one that the user agent has reason
3093 // to believe is appropriate for the user, and there is no other text track in the media element's list of
3094 // text tracks with a text track kind of chapters whose text track mode is showing
3095 // Let the text track mode be showing.
3096 if (bestMatchingLanguage.length()) {
3097 if (textTrack->language() == bestMatchingLanguage)
3098 trackElementToEnable = trackElement;
3099 } else if (trackElement->isDefault()) {
3100 // The user is interested in this type of track, but their language preference doesn't match any track so we will
3101 // enable the 'default' track.
3102 defaultTrack = trackElement;
3105 // Remember the first track that doesn't match language or have 'default' to potentially use as fallback.
3107 fallbackTrack = trackElement;
3108 } else if (!group.visibleTrack && !defaultTrack && trackElement->isDefault()) {
3109 // * If the track element has a default attribute specified, and there is no other text track in the media
3110 // element's list of text tracks whose text track mode is showing or showing by default
3111 // Let the text track mode be showing by default.
3112 defaultTrack = trackElement;
3116 if (!trackElementToEnable && defaultTrack)
3117 trackElementToEnable = defaultTrack;
3119 // If no track matches the user's preferred language and non was marked 'default', enable the first track
3120 // because the user has explicitly stated a preference for this kind of track.
3121 if (!trackElementToEnable && fallbackTrack)
3122 trackElementToEnable = fallbackTrack;
3124 for (size_t i = 0; i < group.tracks.size(); ++i) {
3125 HTMLTrackElement* trackElement = group.tracks[i];
3126 RefPtr<TextTrack> textTrack = trackElement->track();
3128 if (trackElementToEnable == trackElement) {
3129 textTrack->setMode(TextTrack::showingKeyword());
3130 if (defaultTrack == trackElement)
3131 textTrack->setShowingByDefault(true);
3133 if (textTrack->showingByDefault()) {
3134 // If there is a text track in the media element's list of text tracks whose text track
3135 // mode is showing by default, the user agent must furthermore change that text track's
3136 // text track mode to hidden.
3137 textTrack->setShowingByDefault(false);
3138 textTrack->setMode(TextTrack::hiddenKeyword());
3140 textTrack->setMode(TextTrack::disabledKeyword());
3144 if (trackElementToEnable && group.defaultTrack && group.defaultTrack != trackElementToEnable) {
3145 RefPtr<TextTrack> textTrack = group.defaultTrack->track();
3146 if (textTrack && textTrack->showingByDefault()) {
3147 textTrack->setShowingByDefault(false);
3148 textTrack->setMode(TextTrack::hiddenKeyword());
3153 void HTMLMediaElement::configureTextTracks()
3155 TrackGroup captionAndSubtitleTracks(TrackGroup::CaptionsAndSubtitles);
3156 TrackGroup descriptionTracks(TrackGroup::Description);
3157 TrackGroup chapterTracks(TrackGroup::Chapter);
3158 TrackGroup metadataTracks(TrackGroup::Metadata);
3159 TrackGroup otherTracks(TrackGroup::Other);
3161 for (Node* node = firstChild(); node; node = node->nextSibling()) {
3162 if (!node->hasTagName(trackTag))
3165 HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node);
3166 RefPtr<TextTrack> textTrack = trackElement->track();
3170 String kind = textTrack->kind();
3171 TrackGroup* currentGroup;
3172 if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword())
3173 currentGroup = &captionAndSubtitleTracks;
3174 else if (kind == TextTrack::descriptionsKeyword())
3175 currentGroup = &descriptionTracks;
3176 else if (kind == TextTrack::chaptersKeyword())
3177 currentGroup = &chapterTracks;
3178 else if (kind == TextTrack::metadataKeyword())
3179 currentGroup = &metadataTracks;
3181 currentGroup = &otherTracks;
3183 if (!currentGroup->visibleTrack && textTrack->mode() == TextTrack::showingKeyword())
3184 currentGroup->visibleTrack = trackElement;
3185 if (!currentGroup->defaultTrack && trackElement->isDefault())
3186 currentGroup->defaultTrack = trackElement;
3188 // Do not add this track to the group if it has already been automatically configured
3189 // as we only want to call configureTextTrack once per track so that adding another
3190 // track after the initial configuration doesn't reconfigure every track - only those
3191 // that should be changed by the new addition. For example all metadata tracks are
3192 // disabled by default, and we don't want a track that has been enabled by script
3193 // to be disabled automatically when a new metadata track is added later.
3194 if (trackElement->hasBeenConfigured())
3197 if (textTrack->language().length())
3198 currentGroup->hasSrcLang = true;
3199 currentGroup->tracks.append(trackElement);
3202 if (captionAndSubtitleTracks.tracks.size())
3203 configureTextTrackGroup(captionAndSubtitleTracks);
3204 if (descriptionTracks.tracks.size())
3205 configureTextTrackGroup(descriptionTracks);
3206 if (chapterTracks.tracks.size())
3207 configureTextTrackGroup(chapterTracks);
3208 if (metadataTracks.tracks.size())
3209 configureTextTrackGroup(metadataTracks);
3210 if (otherTracks.tracks.size())
3211 configureTextTrackGroup(otherTracks);
3215 bool HTMLMediaElement::havePotentialSourceChild()
3217 // Stash the current <source> node and next nodes so we can restore them after checking
3218 // to see there is another potential.
3219 RefPtr<HTMLSourceElement> currentSourceNode = m_currentSourceNode;
3220 RefPtr<Node> nextNode = m_nextChildNodeToConsider;
3222 KURL nextURL = selectNextSourceChild(0, 0, DoNothing);
3224 m_currentSourceNode = currentSourceNode;
3225 m_nextChildNodeToConsider = nextNode;
3227 return nextURL.isValid();
3230 KURL HTMLMediaElement::selectNextSourceChild(ContentType* contentType, String* keySystem, InvalidURLAction actionIfInvalid)
3233 // Don't log if this was just called to find out if there are any valid <source> elements.
3234 bool shouldLog = actionIfInvalid != DoNothing;
3236 LOG(Media, "HTMLMediaElement::selectNextSourceChild");
3239 if (!m_nextChildNodeToConsider) {
3242 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\"");
3249 HTMLSourceElement* source = 0;
3252 bool lookingForStartNode = m_nextChildNodeToConsider;
3253 bool canUseSourceElement = false;
3254 bool okToLoadSourceURL;
3256 NodeVector potentialSourceNodes;
3257 getChildNodes(this, potentialSourceNodes);
3259 for (unsigned i = 0; !canUseSourceElement && i < potentialSourceNodes.size(); ++i) {
3260 node = potentialSourceNodes[i].get();
3261 if (lookingForStartNode && m_nextChildNodeToConsider != node)
3263 lookingForStartNode = false;
3265 if (!node->hasTagName(sourceTag))
3267 if (node->parentNode() != this)
3270 source = static_cast<HTMLSourceElement*>(node);
3272 // 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
3273 mediaURL = source->getNonEmptyURLAttribute(srcAttr);
3276 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLogging(mediaURL).utf8().data());
3278 if (mediaURL.isEmpty())
3281 if (source->fastHasAttribute(mediaAttr)) {
3282 MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
3283 RefPtr<MediaQuerySet> media = MediaQuerySet::createAllowingDescriptionSyntax(source->media());
3286 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data());
3288 if (!screenEval.eval(media.get()))
3292 type = source->type();
3293 // FIXME(82965): Add support for keySystem in <source> and set system from source.
3294 if (type.isEmpty() && mediaURL.protocolIsData())
3295 type = mimeTypeFromDataURL(mediaURL);
3296 if (!type.isEmpty() || !system.isEmpty()) {
3299 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is '%s' - key system is '%s'", type.utf8().data(), system.utf8().data());
3301 if (!MediaPlayer::supportsType(ContentType(type), system, mediaURL, this))
3305 // Is it safe to load this url?
3306 okToLoadSourceURL = isSafeToLoadURL(mediaURL, actionIfInvalid) && dispatchBeforeLoadEvent(mediaURL.string());
3308 // A 'beforeload' event handler can mutate the DOM, so check to see if the source element is still a child node.
3309 if (node->parentNode() != this) {
3310 LOG(Media, "HTMLMediaElement::selectNextSourceChild : 'beforeload' removed current element");
3315 if (!okToLoadSourceURL)
3318 // Making it this far means the <source> looks reasonable.
3319 canUseSourceElement = true;
3322 if (!canUseSourceElement && actionIfInvalid == Complain && source)
3323 source->scheduleErrorEvent();
3326 if (canUseSourceElement) {
3328 *contentType = ContentType(type);
3330 *keySystem = system;
3331 m_currentSourceNode = source;
3332 m_nextChildNodeToConsider = source->nextSibling();
3334 m_currentSourceNode = 0;
3335 m_nextChildNodeToConsider = 0;
3340 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode.get(), canUseSourceElement ? urlForLogging(mediaURL).utf8().data() : "");
3342 return canUseSourceElement ? mediaURL : KURL();
3345 void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
3347 LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source);
3350 if (source->hasTagName(sourceTag)) {
3351 KURL url = source->getNonEmptyURLAttribute(srcAttr);
3352 LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLogging(url).utf8().data());
3356 // We should only consider a <source> element when there is not src attribute at all.
3357 if (fastHasAttribute(srcAttr))
3360 // 4.8.8 - If a source element is inserted as a child of a media element that has no src
3361 // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke
3362 // the media element's resource selection algorithm.
3363 if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
3364 scheduleLoad(MediaResource);
3365 m_nextChildNodeToConsider = source;
3369 if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) {
3370 LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source");
3371 m_nextChildNodeToConsider = source;
3375 if (m_nextChildNodeToConsider)
3378 // 4.8.9.5, resource selection algorithm, source elements section:
3379 // 21. Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
3380 // 22. Asynchronously await a stable state...
3381 // 23. Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case
3382 // it hasn't been fired yet).
3383 setShouldDelayLoadEvent(true);
3385 // 24. Set the networkState back to NETWORK_LOADING.
3386 m_networkState = NETWORK_LOADING;
3388 // 25. Jump back to the find next candidate step above.
3389 m_nextChildNodeToConsider = source;
3390 scheduleNextSourceChild();
3393 void HTMLMediaElement::sourceWasRemoved(HTMLSourceElement* source)
3395 LOG(Media, "HTMLMediaElement::sourceWasRemoved(%p)", source);
3398 if (source->hasTagName(sourceTag)) {
3399 KURL url = source->getNonEmptyURLAttribute(srcAttr);
3400 LOG(Media, "HTMLMediaElement::sourceWasRemoved - 'src' is %s", urlForLogging(url).utf8().data());
3404 if (source != m_currentSourceNode && source != m_nextChildNodeToConsider)
3407 if (source == m_nextChildNodeToConsider) {
3408 if (m_currentSourceNode)
3409 m_nextChildNodeToConsider = m_currentSourceNode->nextSibling();
3410 LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider.get());
3411 } else if (source == m_currentSourceNode) {
3412 // Clear the current source node pointer, but don't change the movie as the spec says:
3413 // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already
3414 // inserted in a video or audio element will have no effect.
3415 m_currentSourceNode = 0;
3416 LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0");
3420 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
3422 LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
3424 #if ENABLE(VIDEO_TRACK)
3425 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3426 updateActiveTextTrackCues(currentTime());
3429 beginProcessingMediaPlayerCallback();
3431 invalidateCachedTime();
3433 // 4.8.10.9 step 14 & 15. Needed if no ReadyState change is associated with the seek.
3434 if (m_seeking && m_readyState >= HAVE_CURRENT_DATA)
3437 // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity,
3438 // it will only queue a 'timeupdate' event if we haven't already posted one at the current
3440 scheduleTimeupdateEvent(false);
3442 float now = currentTime();
3443 float dur = duration();
3445 // When the current playback position reaches the end of the media resource when the direction of
3446 // playback is forwards, then the user agent must follow these steps:
3447 if (!isnan(dur) && dur && now >= dur && m_playbackRate > 0) {
3448 // If the media element has a loop attribute specified and does not have a current media controller,
3449 if (loop() && !m_mediaController) {
3450 ExceptionCode ignoredException;
3451 m_sentEndEvent = false;
3452 // then seek to the earliest possible position of the media resource and abort these steps.
3453 seek(startTime(), ignoredException);
3455 // If the media element does not have a current media controller, and the media element
3456 // has still ended playback, and the direction of playback is still forwards, and paused
3458 if (!m_mediaController && !m_paused) {
3459 // changes paused to true and fires a simple event named pause at the media element.
3461 scheduleEvent(eventNames().pauseEvent);
3463 // Queue a task to fire a simple event named ended at the media element.
3464 if (!m_sentEndEvent) {
3465 m_sentEndEvent = true;
3466 scheduleEvent(eventNames().endedEvent);
3468 // If the media element has a current media controller, then report the controller state
3469 // for the media element's current media controller.
3470 updateMediaController();
3474 m_sentEndEvent = false;
3477 endProcessingMediaPlayerCallback();
3480 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
3482 LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged");
3484 beginProcessingMediaPlayerCallback();
3486 float vol = m_player->volume();
3487 if (vol != m_volume) {
3490 scheduleEvent(eventNames().volumechangeEvent);
3493 endProcessingMediaPlayerCallback();
3496 void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*)
3498 LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged");
3500 beginProcessingMediaPlayerCallback();
3502 setMuted(m_player->muted());
3503 endProcessingMediaPlayerCallback();
3506 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer* player)
3508 LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged");
3510 beginProcessingMediaPlayerCallback();
3511 scheduleEvent(eventNames().durationchangeEvent);
3512 mediaPlayerCharacteristicChanged(player);
3513 endProcessingMediaPlayerCallback();
3516 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
3518 LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged");
3520 beginProcessingMediaPlayerCallback();
3522 // Stash the rate in case the one we tried to set isn't what the engine is
3523 // using (eg. it can't handle the rate we set)
3524 m_playbackRate = m_player->rate();
3526 invalidateCachedTime();
3529 updateDisableSleep();
3532 endProcessingMediaPlayerCallback();
3535 void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*)
3537 LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged");
3539 if (!m_player || m_pausedInternal)
3542 beginProcessingMediaPlayerCallback();
3543 if (m_player->paused())
3547 endProcessingMediaPlayerCallback();
3550 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*)
3552 LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks");
3554 // The MediaPlayer came across content it cannot completely handle.
3555 // This is normally acceptable except when we are in a standalone
3556 // MediaDocument. If so, tell the document what has happened.
3557 if (ownerDocument()->isMediaDocument()) {
3558 MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument());
3559 mediaDocument->mediaElementSawUnsupportedTracks();
3563 void HTMLMediaElement::mediaPlayerResourceNotSupported(MediaPlayer*)
3565 LOG(Media, "HTMLMediaElement::mediaPlayerResourceNotSupported");
3567 // The MediaPlayer came across content which no installed engine supports.
3568 mediaLoadingFailed(MediaPlayer::FormatError);
3571 // MediaPlayerPresentation methods
3572 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
3574 beginProcessingMediaPlayerCallback();
3575 updateDisplayState();
3577 renderer()->repaint();
3578 endProcessingMediaPlayerCallback();
3581 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*)
3583 LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged");
3585 beginProcessingMediaPlayerCallback();
3587 renderer()->updateFromElement();
3588 endProcessingMediaPlayerCallback();
3591 #if USE(ACCELERATED_COMPOSITING)
3592 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*)
3594 if (renderer() && renderer()->isVideo()) {
3595 ASSERT(renderer()->view());
3596 return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer()));
3601 void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*)
3603 LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged");
3605 // Kick off a fake recalcStyle that will update the compositing tree.
3606 setNeedsStyleRecalc(SyntheticStyleChange);
3610 #if PLATFORM(WIN) && USE(AVFOUNDATION)
3611 GraphicsDeviceAdapter* HTMLMediaElement::mediaPlayerGraphicsDeviceAdapter(const MediaPlayer*) const
3613 if (!document() || !document()->page())
3616 return document()->page()->chrome()->client()->graphicsDeviceAdapter();
3620 void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*)
3622 LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated");
3623 beginProcessingMediaPlayerCallback();
3625 renderer()->updateFromElement();
3626 endProcessingMediaPlayerCallback();
3629 void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*)
3631 LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable");
3632 beginProcessingMediaPlayerCallback();
3633 if (displayMode() == PosterWaitingForVideo) {
3634 setDisplayMode(Video);
3635 #if USE(ACCELERATED_COMPOSITING)
3636 mediaPlayerRenderingModeChanged(m_player.get());
3639 endProcessingMediaPlayerCallback();
3642 void HTMLMediaElement::mediaPlayerCharacteristicChanged(MediaPlayer*)
3644 LOG(Media, "HTMLMediaElement::mediaPlayerCharacteristicChanged");
3646 beginProcessingMediaPlayerCallback();
3647 if (hasMediaControls())
3648 mediaControls()->reset();
3650 renderer()->updateFromElement();
3651 endProcessingMediaPlayerCallback();
3654 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
3657 return TimeRanges::create();
3658 return m_player->buffered();
3661 PassRefPtr<TimeRanges> HTMLMediaElement::played()
3664 float time = currentTime();
3665 if (time > m_lastSeekTime)
3666 addPlayedRange(m_lastSeekTime, time);
3669 if (!m_playedTimeRanges)
3670 m_playedTimeRanges = TimeRanges::create();
3672 return m_playedTimeRanges->copy();
3675 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
3677 return m_player ? m_player->seekable() : TimeRanges::create();
3680 bool HTMLMediaElement::potentiallyPlaying() const
3682 // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing
3683 // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the
3684 // checks in couldPlayIfEnoughData().
3685 bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA;
3686 return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData() && !isBlockedOnMediaController();
3689 bool HTMLMediaElement::couldPlayIfEnoughData() const
3691 return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
3694 bool HTMLMediaElement::endedPlayback() const
3696 float dur = duration();
3697 if (!m_player || isnan(dur))
3700 // 4.8.10.8 Playing the media resource
3702 // A media element is said to have ended playback when the element's
3703 // readyState attribute is HAVE_METADATA or greater,
3704 if (m_readyState < HAVE_METADATA)
3707 // and the current playback position is the end of the media resource and the direction
3708 // of playback is forwards, Either the media element does not have a loop attribute specified,
3709 // or the media element has a current media controller.
3710 float now = currentTime();
3711 if (m_playbackRate > 0)
3712 return dur > 0 && now >= dur && (!loop() || m_mediaController);
3714 // or the current playback position is the earliest possible position and the direction
3715 // of playback is backwards
3716 if (m_playbackRate < 0)
3722 bool HTMLMediaElement::stoppedDueToErrors() const
3724 if (m_readyState >= HAVE_METADATA && m_error) {
3725 RefPtr<TimeRanges> seekableRanges = seekable();
3726 if (!seekableRanges->contain(currentTime()))
3733 bool HTMLMediaElement::pausedForUserInteraction() const
3735 // return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user]
3739 float HTMLMediaElement::minTimeSeekable() const
3744 float HTMLMediaElement::maxTimeSeekable() const
3746 return m_player ? m_player->maxTimeSeekable() : 0;
3749 void HTMLMediaElement::updateVolume()
3754 // Avoid recursion when the player reports volume changes.
3755 if (!processingMediaPlayerCallback()) {
3756 Page* page = document()->page();
3757 float volumeMultiplier = page ? page->mediaVolume() : 1;
3758 bool shouldMute = m_muted;
3760 if (m_mediaController) {
3761 volumeMultiplier *= m_mediaController->volume();
3762 shouldMute = m_mediaController->muted();
3765 m_player->setMuted(shouldMute);
3766 m_player->setVolume(m_volume * volumeMultiplier);
3769 if (hasMediaControls())
3770 mediaControls()->changedVolume();
3773 void HTMLMediaElement::updatePlayState()
3778 if (m_pausedInternal) {
3779 if (!m_player->paused())
3781 refreshCachedTime();
3782 m_playbackProgressTimer.stop();
3783 if (hasMediaControls())
3784 mediaControls()->playbackStopped();
3788 bool shouldBePlaying = potentiallyPlaying();
3789 bool playerPaused = m_player->paused();
3791 LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s",
3792 boolString(shouldBePlaying), boolString(playerPaused));
3794 if (shouldBePlaying) {
3795 setDisplayMode(Video);
3796 invalidateCachedTime();
3799 if (!m_isFullscreen && isVideo() && document() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback())
3802 // Set rate, muted before calling play in case they were set before the media engine was setup.
3803 // The media engine should just stash the rate and muted values since it isn't already playing.
3804 m_player->setRate(m_playbackRate);
3805 m_player->setMuted(m_muted);
3810 if (hasMediaControls())
3811 mediaControls()->playbackStarted();
3812 startPlaybackProgressTimer();
3814 } else { // Should not be playing right now
3817 refreshCachedTime();
3819 m_playbackProgressTimer.stop();
3821 float time = currentTime();
3822 if (time > m_lastSeekTime)
3823 addPlayedRange(m_lastSeekTime, time);
3825 if (couldPlayIfEnoughData())
3828 if (hasMediaControls())
3829 mediaControls()->playbackStopped();
3832 updateMediaController();
3835 renderer()->updateFromElement();
3838 void HTMLMediaElement::setPausedInternal(bool b)
3840 m_pausedInternal = b;
3844 void HTMLMediaElement::stopPeriodicTimers()
3846 m_progressEventTimer.stop();
3847 m_playbackProgressTimer.stop();
3850 void HTMLMediaElement::userCancelledLoad()
3852 LOG(Media, "HTMLMediaElement::userCancelledLoad");
3854 if (m_networkState == NETWORK_EMPTY || m_completelyLoaded)
3857 // If the media data fetching process is aborted by the user:
3859 // 1 - The user agent should cancel the fetching process.
3860 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
3863 stopPeriodicTimers();
3865 m_loadState = WaitingForSource;
3866 m_pendingLoadFlags = 0;
3868 // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED.
3869 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
3871 // 3 - Queue a task to fire a simple event named error at the media element.
3872 scheduleEvent(eventNames().abortEvent);
3874 #if ENABLE(MEDIA_SOURCE)
3875 if (m_sourceState != SOURCE_CLOSED)
3876 setSourceState(SOURCE_CLOSED);
3879 // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
3880 // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
3881 // simple event named emptied at the element. Otherwise, set the element's networkState
3882 // attribute to the NETWORK_IDLE value.
3883 if (m_readyState == HAVE_NOTHING) {
3884 m_networkState = NETWORK_EMPTY;
3885 scheduleEvent(eventNames().emptiedEvent);
3888 m_networkState = NETWORK_IDLE;
3890 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
3891 setShouldDelayLoadEvent(false);
3893 // 6 - Abort the overall resource selection algorithm.
3894 m_currentSourceNode = 0;
3896 // Reset m_readyState since m_player is gone.
3897 m_readyState = HAVE_NOTHING;
3898 updateMediaController();
3899 #if ENABLE(VIDEO_TRACK)
3900 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3901 updateActiveTextTrackCues(0);
3905 bool HTMLMediaElement::canSuspend() const
3910 void HTMLMediaElement::stop()
3912 LOG(Media, "HTMLMediaElement::stop");
3916 m_inActiveDocument = false;
3917 userCancelledLoad();
3919 // Stop the playback without generating events
3920 setPausedInternal(true);
3923 renderer()->updateFromElement();
3925 stopPeriodicTimers();
3926 cancelPendingEventsAndCallbacks();
3927 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
3929 power_unlock_state(POWER_STATE_NORMAL);
3933 void HTMLMediaElement::suspend(ReasonForSuspension why)
3935 LOG(Media, "HTMLMediaElement::suspend");
3936 #if ENABLE(TIZEN_DLOG_SUPPORT)
3937 TIZEN_LOGI("Why: %d", why);
3942 case DocumentWillBecomeInactive:
3945 case PageWillBeSuspended:
3946 case JavaScriptDebuggerPaused:
3947 case WillDeferLoading:
3948 // Do nothing, we don't pause media playback in these cases.
3949 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
3950 if (shouldSuspendMedia()) {
3952 m_player->suspend();
3960 void HTMLMediaElement::resume()
3962 LOG(Media, "HTMLMediaElement::resume");
3964 m_inActiveDocument = true;
3965 setPausedInternal(false);
3967 if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) {
3968 // Restart the load if it was aborted in the middle by moving the document to the page cache.
3969 // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to
3970 // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards).
3971 // This behavior is not specified but it seems like a sensible thing to do.
3972 // As it is not safe to immedately start loading now, let's schedule a load.
3973 scheduleLoad(MediaResource);
3977 renderer()->updateFromElement();
3979 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
3980 // Player will resume asynchronously.
3981 // Set m_suspended to false in setSuspended().
3987 bool HTMLMediaElement::hasPendingActivity() const
3989 return m_asyncEventQueue->hasPendingEvents();
3992 void HTMLMediaElement::mediaVolumeDidChange()
3994 LOG(Media, "HTMLMediaElement::mediaVolumeDidChange");
3998 void HTMLMediaElement::defaultEventHandler(Event* event)
4000 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
4001 RenderObject* r = renderer();
4002 if (!r || !r->isWidget())
4005 Widget* widget = toRenderWidget(r)->widget();
4007 widget->handleEvent(event);
4009 HTMLElement::defaultEventHandler(event);
4013 bool HTMLMediaElement::willRespondToMouseClickEvents()
4015 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
4018 return HTMLElement::willRespondToMouseClickEvents();
4022 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
4024 void HTMLMediaElement::ensureMediaPlayer()
4027 createMediaPlayer();
4030 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification)
4032 if (notification == MediaPlayerNotificationPlayPauseButtonPressed) {
4038 m_player->deliverNotification(notification);
4041 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy)
4043 ensureMediaPlayer();
4044 m_player->setMediaPlayerProxy(proxy);
4047 void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values)
4049 RefPtr<HTMLMediaElement> protect(this); // selectNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
4051 Frame* frame = document()->frame();
4054 KURL posterURL = getNonEmptyURLAttribute(posterAttr);
4055 if (!posterURL.isEmpty() && frame && frame->loader()->willLoadMediaElementURL(posterURL)) {
4056 names.append("_media_element_poster_");
4057 values.append(posterURL.string());
4062 names.append("_media_element_controls_");
4063 values.append("true");
4067 if (!isSafeToLoadURL(url, Complain))
4068 url = selectNextSourceChild(0, 0, DoNothing);
4071 if (url.isValid() && frame && frame->loader()->willLoadMediaElementURL(url)) {
4072 names.append("_media_element_src_");
4073 values.append(m_currentSrc.string());
4077 void HTMLMediaElement::createMediaPlayerProxy()
4079 ensureMediaPlayer();
4081 if (m_proxyWidget || (inDocument() && !m_needWidgetUpdate))
4084 Frame* frame = document()->frame();
4088 LOG(Media, "HTMLMediaElement::createMediaPlayerProxy");
4091 Vector<String> paramNames;
4092 Vector<String> paramValues;
4094 getPluginProxyParams(url, paramNames, paramValues);
4096 // Hang onto the proxy widget so it won't be destroyed if the plug-in is set to
4098 m_proxyWidget = frame->loader()->subframeLoader()->loadMediaPlayerProxyPlugin(this, url, paramNames, paramValues);
4100 m_needWidgetUpdate = false;
4103 void HTMLMediaElement::updateWidget(PluginCreationOption)
4105 mediaElement->setNeedWidgetUpdate(false);
4107 Vector<String> paramNames;
4108 Vector<String> paramValues;
4109 // FIXME: Rename kurl to something more sensible.
4112 mediaElement->getPluginProxyParams(kurl, paramNames, paramValues);
4113 // FIXME: What if document()->frame() is 0?
4114 SubframeLoader* loader = document()->frame()->loader()->subframeLoader();
4115 loader->loadMediaPlayerProxyPlugin(mediaElement, kurl, paramNames, paramValues);
4118 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
4120 bool HTMLMediaElement::isFullscreen() const
4125 #if ENABLE(FULLSCREEN_API)
4126 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this)
4133 void HTMLMediaElement::enterFullscreen()
4135 LOG(Media, "HTMLMediaElement::enterFullscreen");
4137 #if ENABLE(FULLSCREEN_API)
4138 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) {
4139 document()->requestFullScreenForElement(this, 0, Document::ExemptIFrameAllowFullScreenRequirement);
4140 #if ENABLE(TIZEN_FULLSCREEN_API)
4141 if (hasMediaControls())
4142 mediaControls()->updateMediaControlScale();
4147 ASSERT(!m_isFullscreen);
4148 m_isFullscreen = true;
4149 if (hasMediaControls())
4150 mediaControls()->enteredFullscreen();
4151 if (document() && document()->page()) {
4152 document()->page()->chrome()->client()->enterFullscreenForNode(this);
4153 scheduleEvent(eventNames().webkitbeginfullscreenEvent);
4158 void HTMLMediaElement::exitFullscreen()
4160 LOG(Media, "HTMLMediaElement::exitFullscreen");
4162 #if ENABLE(FULLSCREEN_API)
4163 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) {
4164 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this)
4165 document()->webkitCancelFullScreen();
4169 ASSERT(m_isFullscreen);
4170 m_isFullscreen = false;
4171 if (hasMediaControls())
4172 mediaControls()->exitedFullscreen();
4173 if (document() && document()->page()) {
4174 if (document()->page()->chrome()->requiresFullscreenForVideoPlayback())
4176 document()->page()->chrome()->client()->exitFullscreenForNode(this);
4177 scheduleEvent(eventNames().webkitendfullscreenEvent);
4181 #if ENABLE(TIZEN_FULLSCREEN_API)
4182 void HTMLMediaElement::updateMediaControlsStyle(bool needsRecalc)
4184 if (hasMediaControls())
4185 mediaControls()->updateMediaControlScale();
4188 recalcStyle(Node::Force);
4192 void HTMLMediaElement::didBecomeFullscreenElement()
4194 if (hasMediaControls())
4195 mediaControls()->enteredFullscreen();
4198 void HTMLMediaElement::willStopBeingFullscreenElement()
4200 if (hasMediaControls())
4201 mediaControls()->exitedFullscreen();
4204 PlatformMedia HTMLMediaElement::platformMedia() const
4206 return m_player ? m_player->platformMedia() : NoPlatformMedia;
4209 #if USE(ACCELERATED_COMPOSITING)
4210 PlatformLayer* HTMLMediaElement::platformLayer() const
4212 return m_player ? m_player->platformLayer() : 0;
4216 bool HTMLMediaElement::hasClosedCaptions() const
4218 if (m_player && m_player->hasClosedCaptions())
4221 #if ENABLE(VIDEO_TRACK)
4222 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && m_textTracks)
4223 for (unsigned i = 0; i < m_textTracks->length(); ++i) {
4224 if (m_textTracks->item(i)->kind() == TextTrack::captionsKeyword()
4225 || m_textTracks->item(i)->kind() == TextTrack::subtitlesKeyword())
4232 bool HTMLMediaElement::closedCaptionsVisible() const
4234 return m_closedCaptionsVisible;
4237 void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible)
4239 LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible));
4241 if (!m_player || !hasClosedCaptions())
4244 m_closedCaptionsVisible = closedCaptionVisible;
4245 m_player->setClosedCaptionsVisible(closedCaptionVisible);
4247 #if ENABLE(VIDEO_TRACK)
4248 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) {
4249 m_disableCaptions = !m_closedCaptionsVisible;
4251 // Mark all track elements as not "configured" so that configureTextTracks()
4252 // will reconsider which tracks to display in light of new user preferences
4253 // (e.g. default tracks should not be displayed if the user has turned off
4254 // captions and non-default tracks should be displayed based on language
4255 // preferences if the user has turned captions on).
4256 for (Node* node = firstChild(); node; node = node->nextSibling()) {
4257 if (!node->hasTagName(trackTag))
4259 HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node);
4260 if (trackElement->kind() == TextTrack::captionsKeyword()
4261 || trackElement->kind() == TextTrack::subtitlesKeyword())
4262 trackElement->setHasBeenConfigured(false);
4265 configureTextTracks();
4268 if (hasMediaControls())
4269 mediaControls()->changedClosedCaptionsVisibility();
4273 void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible)
4275 setClosedCaptionsVisible(visible);
4278 bool HTMLMediaElement::webkitClosedCaptionsVisible() const
4280 return closedCaptionsVisible();
4284 bool HTMLMediaElement::webkitHasClosedCaptions() const
4286 return hasClosedCaptions();
4289 #if ENABLE(MEDIA_STATISTICS)
4290 unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const
4294 return m_player->audioDecodedByteCount();
4297 unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const
4301 return m_player->videoDecodedByteCount();
4305 void HTMLMediaElement::mediaCanStart()
4307 LOG(Media, "HTMLMediaElement::mediaCanStart");
4309 ASSERT(m_isWaitingUntilMediaCanStart);
4310 m_isWaitingUntilMediaCanStart = false;
4314 bool HTMLMediaElement::isURLAttribute(const Attribute& attribute) const
4316 return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute);
4319 void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay)
4321 if (m_shouldDelayLoadEvent == shouldDelay)
4324 LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay));
4326 m_shouldDelayLoadEvent = shouldDelay;
4328 document()->incrementLoadEventDelayCount();
4330 document()->decrementLoadEventDelayCount();
4334 void HTMLMediaElement::getSitesInMediaCache(Vector<String>& sites)
4336 MediaPlayer::getSitesInMediaCache(sites);
4339 void HTMLMediaElement::clearMediaCache()
4341 MediaPlayer::clearMediaCache();
4344 void HTMLMediaElement::clearMediaCacheForSite(const String& site)
4346 MediaPlayer::clearMediaCacheForSite(site);
4349 void HTMLMediaElement::privateBrowsingStateDidChange()
4354 Settings* settings = document()->settings();
4355 bool privateMode = !settings || settings->privateBrowsingEnabled();
4356 LOG(Media, "HTMLMediaElement::privateBrowsingStateDidChange(%s)", boolString(privateMode));
4357 m_player->setPrivateBrowsingMode(privateMode);
4360 MediaControls* HTMLMediaElement::mediaControls() const
4362 return toMediaControls(userAgentShadowRoot()->firstChild());
4365 bool HTMLMediaElement::hasMediaControls() const
4367 if (ShadowRoot* userAgent = userAgentShadowRoot()) {
4368 Node* node = userAgent->firstChild();
4369 ASSERT(!node || node->isMediaControls());
4376 bool HTMLMediaElement::createMediaControls()
4378 if (hasMediaControls())
4382 RefPtr<MediaControls> controls = MediaControls::create(document());
4386 controls->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this));
4388 #if ENABLE(TIZEN_FULLSCREEN_API)
4390 controls->updateMediaControlScale();
4393 controls->enteredFullscreen();
4396 createShadowSubtree();
4398 ASSERT(userAgentShadowRoot());
4399 userAgentShadowRoot()->appendChild(controls, ec);
4403 void HTMLMediaElement::configureMediaControls()
4405 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
4406 if (!controls() || !inDocument()) {
4407 if (hasMediaControls())
4408 mediaControls()->hide();
4412 if (!hasMediaControls() && !createMediaControls())
4415 mediaControls()->show();
4418 m_player->setControls(controls());
4422 #if ENABLE(VIDEO_TRACK)
4423 void HTMLMediaElement::configureTextTrackDisplay()
4425 ASSERT(m_textTracks);
4427 bool haveVisibleTextTrack = false;
4428 for (unsigned i = 0; i < m_textTracks->length(); ++i) {
4429 if (m_textTracks->item(i)->mode() == TextTrack::showingKeyword()) {
4430 haveVisibleTextTrack = true;
4435 if (m_haveVisibleTextTrack == haveVisibleTextTrack)
4437 m_haveVisibleTextTrack = haveVisibleTextTrack;
4438 m_closedCaptionsVisible = m_haveVisibleTextTrack;
4440 if (!m_haveVisibleTextTrack && !hasMediaControls())
4442 if (!hasMediaControls() && !createMediaControls())
4445 updateClosedCaptionsControls();
4448 void HTMLMediaElement::updateClosedCaptionsControls()
4450 if (hasMediaControls()) {
4451 mediaControls()->changedClosedCaptionsVisibility();
4453 if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
4454 mediaControls()->updateTextTrackDisplay();
4459 void* HTMLMediaElement::preDispatchEventHandler(Event* event)
4461 if (event && event->type() == eventNames().webkitfullscreenchangeEvent) {
4462 configureMediaControls();
4463 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
4464 if (!isFullscreen())
4465 scheduleEvent(eventNames().webkitendfullscreenEvent);
4472 void HTMLMediaElement::createMediaPlayer()
4474 #if ENABLE(WEB_AUDIO)
4475 if (m_audioSourceNode)
4476 m_audioSourceNode->lock();
4479 m_player = MediaPlayer::create(this);
4481 #if ENABLE(WEB_AUDIO)
4482 if (m_audioSourceNode) {
4483 // When creating the player, make sure its AudioSourceProvider knows about the MediaElementAudioSourceNode.
4484 if (audioSourceProvider())
4485 audioSourceProvider()->setClient(m_audioSourceNode);
4487 m_audioSourceNode->unlock();
4492 #if ENABLE(WEB_AUDIO)
4493 void HTMLMediaElement::setAudioSourceNode(MediaElementAudioSourceNode* sourceNode)
4495 m_audioSourceNode = sourceNode;
4497 if (audioSourceProvider())
4498 audioSourceProvider()->setClient(m_audioSourceNode);
4501 AudioSourceProvider* HTMLMediaElement::audioSourceProvider()
4504 return m_player->audioSourceProvider();
4510 #if ENABLE(MICRODATA)
4511 String HTMLMediaElement::itemValueText() const
4513 return getURLAttribute(srcAttr);
4516 void HTMLMediaElement::setItemValueText(const String& value, ExceptionCode&)
4518 setAttribute(srcAttr, value);
4522 const String& HTMLMediaElement::mediaGroup() const
4524 return m_mediaGroup;
4527 void HTMLMediaElement::setMediaGroup(const String& group)
4529 if (m_mediaGroup == group)
4531 m_mediaGroup = group;
4533 // When a media element is created with a mediagroup attribute, and when a media element's mediagroup
4534 // attribute is set, changed, or removed, the user agent must run the following steps:
4535 // 1. Let m [this] be the media element in question.
4536 // 2. Let m have no current media controller, if it currently has one.
4539 // 3. If m's mediagroup attribute is being removed, then abort these steps.
4540 if (group.isNull() || group.isEmpty())
4543 // 4. If there is another media element whose Document is the same as m's Document (even if one or both
4544 // of these elements are not actually in the Document),
4545 HashSet<HTMLMediaElement*> elements = documentToElementSetMap().get(document());
4546 for (HashSet<HTMLMediaElement*>::iterator i = elements.begin(); i != elements.end(); ++i) {
4550 // and which also has a mediagroup attribute, and whose mediagroup attribute has the same value as
4551 // the new value of m's mediagroup attribute,
4552 if ((*i)->mediaGroup() == group) {
4553 // then let controller be that media element's current media controller.
4554 setController((*i)->controller());
4559 // Otherwise, let controller be a newly created MediaController.
4560 setController(MediaController::create(Node::scriptExecutionContext()));
4563 MediaController* HTMLMediaElement::controller() const
4565 return m_mediaController.get();
4568 void HTMLMediaElement::setController(PassRefPtr<MediaController> controller)
4570 if (m_mediaController)
4571 m_mediaController->removeMediaElement(this);
4573 m_mediaController = controller;
4575 if (m_mediaController)
4576 m_mediaController->addMediaElement(this);
4578 if (hasMediaControls())
4579 mediaControls()->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this));
4582 void HTMLMediaElement::updateMediaController()
4584 if (m_mediaController)
4585 m_mediaController->reportControllerState();
4588 bool HTMLMediaElement::dispatchEvent(PassRefPtr<Event> event)
4590 bool dispatchResult;
4591 bool isCanPlayEvent;
4593 isCanPlayEvent = (event->type() == eventNames().canplayEvent);
4596 m_dispatchingCanPlayEvent = true;
4598 dispatchResult = HTMLElement::dispatchEvent(event);
4601 m_dispatchingCanPlayEvent = false;
4603 return dispatchResult;
4606 bool HTMLMediaElement::isBlocked() const
4608 // A media element is a blocked media element if its readyState attribute is in the
4609 // HAVE_NOTHING state, the HAVE_METADATA state, or the HAVE_CURRENT_DATA state,
4610 if (m_readyState <= HAVE_CURRENT_DATA)
4613 // or if the element has paused for user interaction.
4614 return pausedForUserInteraction();
4617 bool HTMLMediaElement::isBlockedOnMediaController() const
4619 if (!m_mediaController)
4622 // A media element is blocked on its media controller if the MediaController is a blocked
4623 // media controller,
4624 if (m_mediaController->isBlocked())
4627 // or if its media controller position is either before the media resource's earliest possible
4628 // position relative to the MediaController's timeline or after the end of the media resource
4629 // relative to the MediaController's timeline.
4630 float mediaControllerPosition = m_mediaController->currentTime();
4631 if (mediaControllerPosition < startTime() || mediaControllerPosition > startTime() + duration())
4637 void HTMLMediaElement::prepareMediaFragmentURI()
4639 MediaFragmentURIParser fragmentParser(m_currentSrc);
4640 float dur = duration();
4642 double start = fragmentParser.startTime();
4643 if (start != MediaFragmentURIParser::invalidTimeValue() && start > 0) {
4644 m_fragmentStartTime = start;
4645 if (m_fragmentStartTime > dur)
4646 m_fragmentStartTime = dur;
4648 m_fragmentStartTime = MediaPlayer::invalidTime();
4650 double end = fragmentParser.endTime();
4651 if (end != MediaFragmentURIParser::invalidTimeValue() && end > 0 && end > m_fragmentStartTime) {
4652 m_fragmentEndTime = end;
4653 if (m_fragmentEndTime > dur)
4654 m_fragmentEndTime = dur;
4656 m_fragmentEndTime = MediaPlayer::invalidTime();
4658 if (m_fragmentStartTime != MediaPlayer::invalidTime() && m_readyState < HAVE_FUTURE_DATA)
4662 void HTMLMediaElement::applyMediaFragmentURI()
4664 if (m_fragmentStartTime != MediaPlayer::invalidTime()) {
4665 ExceptionCode ignoredException;
4666 m_sentEndEvent = false;
4667 seek(m_fragmentStartTime, ignoredException);
4672 void HTMLMediaElement::updateDisableSleep()
4674 if (!shouldDisableSleep() && m_sleepDisabler)
4675 m_sleepDisabler = nullptr;
4676 else if (shouldDisableSleep() && !m_sleepDisabler)
4677 m_sleepDisabler = DisplaySleepDisabler::create("com.apple.WebCore: HTMLMediaElement playback");
4680 bool HTMLMediaElement::shouldDisableSleep() const
4682 return m_player && !m_player->paused() && hasVideo() && hasAudio() && !loop();
4686 String HTMLMediaElement::mediaPlayerReferrer() const
4688 Frame* frame = document()->frame();
4692 return SecurityPolicy::generateReferrerHeader(document()->referrerPolicy(), m_currentSrc, frame->loader()->outgoingReferrer());
4695 String HTMLMediaElement::mediaPlayerUserAgent() const
4697 Frame* frame = document()->frame();
4701 return frame->loader()->userAgent(m_currentSrc);
4705 MediaPlayerClient::CORSMode HTMLMediaElement::mediaPlayerCORSMode() const
4707 if (!fastHasAttribute(HTMLNames::crossoriginAttr))
4709 if (equalIgnoringCase(fastGetAttribute(HTMLNames::crossoriginAttr), "use-credentials"))
4710 return UseCredentials;
4714 bool HTMLMediaElement::mediaPlayerNeedsSiteSpecificHacks() const
4716 Settings* settings = document()->settings();
4717 return settings && settings->needsSiteSpecificQuirks();
4720 String HTMLMediaElement::mediaPlayerDocumentHost() const
4722 return document()->url().host();
4725 void HTMLMediaElement::mediaPlayerExitFullscreen()
4730 bool HTMLMediaElement::mediaPlayerIsVideo() const
4735 LayoutRect HTMLMediaElement::mediaPlayerContentBoxRect() const
4738 return renderer()->enclosingBox()->contentBoxRect();
4739 return LayoutRect();
4742 void HTMLMediaElement::mediaPlayerSetSize(const IntSize& size)
4744 setAttribute(widthAttr, String::number(size.width()));
4745 setAttribute(heightAttr, String::number(size.height()));
4748 void HTMLMediaElement::mediaPlayerPause()
4753 void HTMLMediaElement::mediaPlayerPlay()
4758 bool HTMLMediaElement::mediaPlayerIsPaused() const
4763 bool HTMLMediaElement::mediaPlayerIsLooping() const
4768 HostWindow* HTMLMediaElement::mediaPlayerHostWindow()
4770 return mediaPlayerOwningDocument()->view()->hostWindow();
4773 IntRect HTMLMediaElement::mediaPlayerWindowClipRect()
4775 return mediaPlayerOwningDocument()->view()->windowClipRect();
4778 void HTMLMediaElement::removeBehaviorsRestrictionsAfterFirstUserGesture()
4780 m_restrictions = NoRestrictions;
4783 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
4784 bool HTMLMediaElement::shouldSuspendMedia()
4792 #if ENABLE(TIZEN_EXTENSIBLE_API)
4793 if (!TizenExtensibleAPI::extensibleAPI().backgroundMusic())
4800 void HTMLMediaElement::setSuspended(bool suspended)
4802 m_suspended = suspended;