2 * Copyright (C) 2007, 2008, 2009, 2010, 2011 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 "DocumentLoader.h"
44 #include "EventNames.h"
45 #include "ExceptionCode.h"
47 #include "FrameLoader.h"
48 #include "FrameLoaderClient.h"
49 #include "FrameView.h"
50 #include "HTMLDocument.h"
51 #include "HTMLNames.h"
52 #include "HTMLSourceElement.h"
53 #include "HTMLVideoElement.h"
55 #include "MediaController.h"
56 #include "MediaControls.h"
57 #include "MediaDocument.h"
58 #include "MediaError.h"
59 #include "MediaList.h"
60 #include "MediaPlayer.h"
61 #include "MediaQueryEvaluator.h"
62 #include "MouseEvent.h"
63 #include "MIMETypeRegistry.h"
65 #include "RenderVideo.h"
66 #include "RenderView.h"
67 #include "ScriptController.h"
68 #include "ScriptEventListener.h"
69 #include "SecurityOrigin.h"
71 #include "ShadowRoot.h"
72 #include "TimeRanges.h"
75 #include <wtf/CurrentTime.h>
76 #include <wtf/MathExtras.h>
77 #include <wtf/Uint8Array.h>
78 #include <wtf/text/CString.h>
80 #if ENABLE(TIZEN_WAC_CAMERA_SUPPORT)
81 #include "CameraManager.h"
84 #if USE(ACCELERATED_COMPOSITING)
85 #include "RenderView.h"
86 #include "RenderLayerCompositor.h"
89 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
90 #include "RenderEmbeddedObject.h"
94 #if ENABLE(VIDEO_TRACK)
95 #include "HTMLTrackElement.h"
96 #include "RuntimeEnabledFeatures.h"
97 #include "TextTrackCueList.h"
98 #include "TextTrackList.h"
101 #if ENABLE(WEB_AUDIO)
102 #include "AudioSourceProvider.h"
103 #include "MediaElementAudioSourceNode.h"
111 static String urlForLogging(const KURL& url)
113 static const unsigned maximumURLLengthForLogging = 128;
115 if (url.string().length() < maximumURLLengthForLogging)
117 return url.string().substring(0, maximumURLLengthForLogging) + "...";
120 static const char* boolString(bool val)
122 return val ? "true" : "false";
126 #ifndef LOG_MEDIA_EVENTS
127 // Default to not logging events because so many are generated they can overwhelm the rest of
129 #define LOG_MEDIA_EVENTS 0
132 #ifndef LOG_CACHED_TIME_WARNINGS
133 // Default to not logging warnings about excessive drift in the cached media time because it adds a
134 // fair amount of overhead and logging.
135 #define LOG_CACHED_TIME_WARNINGS 0
138 static const float invalidMediaTime = -1;
140 #if ENABLE(MEDIA_SOURCE)
141 // URL protocol used to signal that the media source API is being used.
142 static const char* mediaSourceURLProtocol = "x-media-source";
145 using namespace HTMLNames;
148 typedef HashMap<Document*, HashSet<HTMLMediaElement*> > DocumentElementSetMap;
149 static DocumentElementSetMap& documentToElementSetMap()
151 DEFINE_STATIC_LOCAL(DocumentElementSetMap, map, ());
155 static void addElementToDocumentMap(HTMLMediaElement* element, Document* document)
157 DocumentElementSetMap& map = documentToElementSetMap();
158 HashSet<HTMLMediaElement*> set = map.take(document);
160 map.add(document, set);
163 static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* document)
165 DocumentElementSetMap& map = documentToElementSetMap();
166 HashSet<HTMLMediaElement*> set = map.take(document);
169 map.add(document, set);
172 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document)
173 : HTMLElement(tagName, document)
174 , ActiveDOMObject(document, this)
175 , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
176 , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired)
177 , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
178 , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
179 , m_playedTimeRanges()
180 , m_playbackRate(1.0f)
181 , m_defaultPlaybackRate(1.0f)
182 , m_webkitPreservesPitch(true)
183 , m_networkState(NETWORK_EMPTY)
184 , m_readyState(HAVE_NOTHING)
185 , m_readyStateMaximum(HAVE_NOTHING)
188 , m_previousProgress(0)
189 , m_previousProgressTime(numeric_limits<double>::max())
190 , m_lastTimeUpdateEventWallTime(0)
191 , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max())
192 , m_loadState(WaitingForSource)
193 , m_currentSourceNode(0)
194 , m_nextChildNodeToConsider(0)
195 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
198 , m_restrictions(RequireUserGestureForFullscreenRestriction | RequirePageConsentToLoadMediaRestriction)
199 , m_preload(MediaPlayer::Auto)
200 , m_displayMode(Unknown)
201 , m_processingMediaPlayerCallback(0)
202 #if ENABLE(MEDIA_SOURCE)
203 , m_sourceState(SOURCE_CLOSED)
205 , m_cachedTime(invalidMediaTime)
206 , m_cachedTimeWallClockUpdateTime(0)
207 , m_minimumWallClockTimeToCacheMediaTime(0)
208 , m_pendingLoadFlags(0)
210 , m_isWaitingUntilMediaCanStart(false)
211 , m_shouldDelayLoadEvent(false)
212 , m_haveFiredLoadedData(false)
213 , m_inActiveDocument(true)
214 , m_autoplaying(true)
218 , m_sentStalledEvent(false)
219 , m_sentEndEvent(false)
220 , m_pausedInternal(false)
221 , m_sendProgressEvents(true)
222 , m_isFullscreen(false)
223 , m_closedCaptionsVisible(false)
224 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
225 , m_needWidgetUpdate(false)
227 #if ENABLE(TIZEN_WAC_CAMERA_SUPPORT)
230 , m_dispatchingCanPlayEvent(false)
231 , m_loadInitiatedByUserGesture(false)
232 , m_completelyLoaded(false)
233 , m_havePreparedToPlay(false)
234 #if ENABLE(WEB_AUDIO)
235 , m_audioSourceNode(0)
237 #if ENABLE(VIDEO_TRACK)
241 LOG(Media, "HTMLMediaElement::HTMLMediaElement");
242 document->registerForDocumentActivationCallbacks(this);
243 document->registerForMediaVolumeCallbacks(this);
244 document->registerForPrivateBrowsingStateChangedCallbacks(this);
246 if (document->settings() && document->settings()->mediaPlaybackRequiresUserGesture())
247 addBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
249 #if ENABLE(MEDIA_SOURCE)
250 m_mediaSourceURL.setProtocol(mediaSourceURLProtocol);
251 m_mediaSourceURL.setPath(createCanonicalUUIDString());
254 setHasCustomWillOrDidRecalcStyle();
255 addElementToDocumentMap(this, document);
258 HTMLMediaElement::~HTMLMediaElement()
260 LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
261 if (m_isWaitingUntilMediaCanStart)
262 document()->removeMediaCanStartListener(this);
263 setShouldDelayLoadEvent(false);
264 document()->unregisterForDocumentActivationCallbacks(this);
265 document()->unregisterForMediaVolumeCallbacks(this);
266 document()->unregisterForPrivateBrowsingStateChangedCallbacks(this);
267 #if ENABLE(TIZEN_WAC_CAMERA_SUPPORT)
269 m_camera->removeHTMLElement(this);
271 #if ENABLE(VIDEO_TRACK)
273 m_textTracks->clearOwner();
275 for (unsigned i = 0; i < m_textTracks->length(); ++i)
276 m_textTracks->item(i)->clearClient();
280 if (m_mediaController)
281 m_mediaController->removeMediaElement(this);
283 removeElementFromDocumentMap(this, document());
286 void HTMLMediaElement::willMoveToNewOwnerDocument()
288 if (m_isWaitingUntilMediaCanStart)
289 document()->removeMediaCanStartListener(this);
290 setShouldDelayLoadEvent(false);
291 document()->unregisterForDocumentActivationCallbacks(this);
292 document()->unregisterForMediaVolumeCallbacks(this);
293 removeElementFromDocumentMap(this, document());
294 HTMLElement::willMoveToNewOwnerDocument();
297 void HTMLMediaElement::didMoveToNewOwnerDocument()
299 if (m_isWaitingUntilMediaCanStart)
300 document()->addMediaCanStartListener(this);
301 if (m_readyState < HAVE_CURRENT_DATA)
302 setShouldDelayLoadEvent(true);
303 document()->registerForDocumentActivationCallbacks(this);
304 document()->registerForMediaVolumeCallbacks(this);
305 addElementToDocumentMap(this, document());
306 HTMLElement::didMoveToNewOwnerDocument();
309 bool HTMLMediaElement::supportsFocus() const
311 if (ownerDocument()->isMediaDocument())
314 // If no controls specified, we should still be able to focus the element if it has tabIndex.
315 return controls() || HTMLElement::supportsFocus();
318 void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls)
320 HTMLElement::attributeChanged(attr, preserveDecls);
322 const QualifiedName& attrName = attr->name();
323 if (attrName == srcAttr) {
324 // Trigger a reload, as long as the 'src' attribute is present.
325 if (!getAttribute(srcAttr).isEmpty())
326 scheduleLoad(MediaResource);
327 } else if (attrName == controlsAttr)
328 configureMediaControls();
331 void HTMLMediaElement::parseMappedAttribute(Attribute* attr)
333 const QualifiedName& attrName = attr->name();
335 if (attrName == preloadAttr) {
336 String value = attr->value();
338 if (equalIgnoringCase(value, "none"))
339 m_preload = MediaPlayer::None;
340 else if (equalIgnoringCase(value, "metadata"))
341 m_preload = MediaPlayer::MetaData;
343 // The spec does not define an "invalid value default" but "auto" is suggested as the
344 // "missing value default", so use it for everything except "none" and "metadata"
345 m_preload = MediaPlayer::Auto;
348 // The attribute must be ignored if the autoplay attribute is present
349 if (!autoplay() && m_player)
350 m_player->setPreload(m_preload);
352 } else if (attrName == mediagroupAttr)
353 setMediaGroup(attr->value());
354 else if (attrName == onabortAttr)
355 setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr));
356 else if (attrName == onbeforeloadAttr)
357 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr));
358 else if (attrName == oncanplayAttr)
359 setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, attr));
360 else if (attrName == oncanplaythroughAttr)
361 setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, attr));
362 else if (attrName == ondurationchangeAttr)
363 setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, attr));
364 else if (attrName == onemptiedAttr)
365 setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, attr));
366 else if (attrName == onendedAttr)
367 setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attr));
368 else if (attrName == onerrorAttr)
369 setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr));
370 else if (attrName == onloadeddataAttr)
371 setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr));
372 else if (attrName == onloadedmetadataAttr)
373 setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attr));
374 else if (attrName == onloadstartAttr)
375 setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attr));
376 else if (attrName == onpauseAttr)
377 setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, attr));
378 else if (attrName == onplayAttr)
379 setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, attr));
380 else if (attrName == onplayingAttr)
381 setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, attr));
382 else if (attrName == onprogressAttr)
383 setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, attr));
384 else if (attrName == onratechangeAttr)
385 setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, attr));
386 else if (attrName == onseekedAttr)
387 setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, attr));
388 else if (attrName == onseekingAttr)
389 setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, attr));
390 else if (attrName == onstalledAttr)
391 setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, attr));
392 else if (attrName == onsuspendAttr)
393 setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, attr));
394 else if (attrName == ontimeupdateAttr)
395 setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, attr));
396 else if (attrName == onvolumechangeAttr)
397 setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, attr));
398 else if (attrName == onwaitingAttr)
399 setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, attr));
400 else if (attrName == onwebkitbeginfullscreenAttr)
401 setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, attr));
402 else if (attrName == onwebkitendfullscreenAttr)
403 setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, attr));
405 HTMLElement::parseMappedAttribute(attr);
408 bool HTMLMediaElement::rendererIsNeeded(const NodeRenderingContext& context)
410 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
411 UNUSED_PARAM(context);
412 Frame* frame = document()->frame();
418 return controls() ? HTMLElement::rendererIsNeeded(context) : false;
422 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
424 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
425 // Setup the renderer if we already have a proxy widget.
426 RenderEmbeddedObject* mediaRenderer = new (arena) RenderEmbeddedObject(this);
428 mediaRenderer->setWidget(m_proxyWidget);
430 if (Frame* frame = document()->frame())
431 frame->loader()->client()->showMediaPlayerProxyPlugin(m_proxyWidget.get());
433 return mediaRenderer;
435 return new (arena) RenderMedia(this);
439 void HTMLMediaElement::insertedIntoDocument()
441 LOG(Media, "HTMLMediaElement::insertedIntoDocument");
442 HTMLElement::insertedIntoDocument();
443 if (!getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY)
444 scheduleLoad(MediaResource);
447 void HTMLMediaElement::removedFromDocument()
449 LOG(Media, "HTMLMediaElement::removedFromDocument");
450 if (m_networkState > NETWORK_EMPTY)
454 #if ENABLE(TIZEN_MM_PLAYER)
459 #if ENABLE(TIZEN_WAC_CAMERA_SUPPORT)
461 m_camera->removeHTMLElement(this);
463 HTMLElement::removedFromDocument();
466 void HTMLMediaElement::attach()
470 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
471 m_needWidgetUpdate = true;
474 HTMLElement::attach();
477 renderer()->updateFromElement();
478 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
479 else if (m_proxyWidget) {
480 if (Frame* frame = document()->frame())
481 frame->loader()->client()->hideMediaPlayerProxyPlugin(m_proxyWidget.get());
486 void HTMLMediaElement::didRecalcStyle(StyleChange)
489 renderer()->updateFromElement();
492 void HTMLMediaElement::scheduleLoad(LoadType loadType)
494 LOG(Media, "HTMLMediaElement::scheduleLoad");
496 if ((loadType & MediaResource) && !(m_pendingLoadFlags & MediaResource)) {
497 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
498 createMediaPlayerProxy();
502 m_pendingLoadFlags |= MediaResource;
505 #if ENABLE(VIDEO_TRACK)
506 if (loadType & TextTrackResource)
507 m_pendingLoadFlags |= TextTrackResource;
510 if (!m_loadTimer.isActive())
511 m_loadTimer.startOneShot(0);
514 void HTMLMediaElement::scheduleNextSourceChild()
516 // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
517 m_pendingLoadFlags |= MediaResource;
518 m_loadTimer.startOneShot(0);
521 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
524 LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data());
526 m_pendingEvents.append(Event::create(eventName, false, true));
527 if (!m_asyncEventTimer.isActive())
528 m_asyncEventTimer.startOneShot(0);
531 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
533 Vector<RefPtr<Event> > pendingEvents;
534 ExceptionCode ec = 0;
536 m_pendingEvents.swap(pendingEvents);
537 unsigned count = pendingEvents.size();
538 for (unsigned ndx = 0; ndx < count; ++ndx) {
540 LOG(Media, "HTMLMediaElement::asyncEventTimerFired - dispatching '%s'", pendingEvents[ndx]->type().string().ascii().data());
542 if (pendingEvents[ndx]->type() == eventNames().canplayEvent) {
543 m_dispatchingCanPlayEvent = true;
544 dispatchEvent(pendingEvents[ndx].release(), ec);
545 m_dispatchingCanPlayEvent = false;
547 dispatchEvent(pendingEvents[ndx].release(), ec);
551 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
553 if (m_pendingLoadFlags & MediaResource) {
554 if (m_loadState == LoadingFromSourceElement)
555 loadNextSourceChild();
560 #if ENABLE(VIDEO_TRACK)
561 if (m_pendingLoadFlags & TextTrackResource)
562 scheduleLoad(TextTrackResource);
565 m_pendingLoadFlags = 0;
568 PassRefPtr<MediaError> HTMLMediaElement::error() const
573 void HTMLMediaElement::setSrc(const String& url)
575 setAttribute(srcAttr, url);
578 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
580 return m_networkState;
583 String HTMLMediaElement::canPlayType(const String& mimeType) const
585 MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType));
591 case MediaPlayer::IsNotSupported:
594 case MediaPlayer::MayBeSupported:
597 case MediaPlayer::IsSupported:
598 canPlay = "probably";
602 LOG(Media, "HTMLMediaElement::canPlayType(%s) -> %s", mimeType.utf8().data(), canPlay.utf8().data());
607 void HTMLMediaElement::load(ExceptionCode& ec)
609 LOG(Media, "HTMLMediaElement::load()");
611 if (userGestureRequiredForLoad() && !ScriptController::processingUserGesture())
612 ec = INVALID_STATE_ERR;
614 m_loadInitiatedByUserGesture = ScriptController::processingUserGesture();
621 void HTMLMediaElement::prepareForLoad()
623 LOG(Media, "HTMLMediaElement::prepareForLoad");
625 // Perform the cleanup required for the resource load algorithm to run.
626 stopPeriodicTimers();
628 m_sentEndEvent = false;
629 m_sentStalledEvent = false;
630 m_haveFiredLoadedData = false;
631 m_completelyLoaded = false;
632 m_havePreparedToPlay = false;
633 m_displayMode = Unknown;
635 // 1 - Abort any already-running instance of the resource selection algorithm for this element.
636 m_loadState = WaitingForSource;
637 m_currentSourceNode = 0;
639 // 2 - If there are any tasks from the media element's media element event task source in
640 // one of the task queues, then remove those tasks.
641 cancelPendingEventsAndCallbacks();
643 // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
644 // a task to fire a simple event named abort at the media element.
645 if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
646 scheduleEvent(eventNames().abortEvent);
648 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
652 m_player->cancelLoad();
654 createMediaPlayerProxy();
657 #if ENABLE(MEDIA_SOURCE)
658 if (m_sourceState != SOURCE_CLOSED)
659 setSourceState(SOURCE_CLOSED);
662 // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
663 if (m_networkState != NETWORK_EMPTY) {
664 m_networkState = NETWORK_EMPTY;
665 m_readyState = HAVE_NOTHING;
666 m_readyStateMaximum = HAVE_NOTHING;
670 invalidateCachedTime();
671 scheduleEvent(eventNames().emptiedEvent);
672 updateMediaController();
675 // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
676 setPlaybackRate(defaultPlaybackRate());
678 // 6 - Set the error attribute to null and the autoplaying flag to true.
680 m_autoplaying = true;
682 // 7 - Invoke the media element's resource selection algorithm.
684 // 8 - Note: Playback of any previously playing media resource for this element stops.
686 // The resource selection algorithm
687 // 1 - Set the networkState to NETWORK_NO_SOURCE
688 m_networkState = NETWORK_NO_SOURCE;
690 // 2 - Asynchronously await a stable state.
692 m_playedTimeRanges = TimeRanges::create();
694 m_closedCaptionsVisible = false;
696 // The spec doesn't say to block the load event until we actually run the asynchronous section
697 // algorithm, but do it now because we won't start that until after the timer fires and the
698 // event may have already fired by then.
699 setShouldDelayLoadEvent(true);
701 configureMediaControls();
704 void HTMLMediaElement::loadInternal()
706 // If we can't start a load right away, start it later.
707 Page* page = document()->page();
708 if (pageConsentRequiredForLoad() && page && !page->canStartMedia()) {
709 if (m_isWaitingUntilMediaCanStart)
711 document()->addMediaCanStartListener(this);
712 m_isWaitingUntilMediaCanStart = true;
716 // Once the page has allowed an element to load media, it is free to load at will. This allows a
717 // playlist that starts in a foreground tab to continue automatically if the tab is subsequently
718 // put in the the background.
719 removeBehaviorRestriction(RequirePageConsentToLoadMediaRestriction);
721 selectMediaResource();
724 void HTMLMediaElement::selectMediaResource()
726 LOG(Media, "HTMLMediaElement::selectMediaResource");
728 enum Mode { attribute, children };
730 // 3 - If the media element has a src attribute, then let mode be attribute.
731 Mode mode = attribute;
732 if (!fastHasAttribute(srcAttr)) {
734 for (node = firstChild(); node; node = node->nextSibling()) {
735 if (node->hasTagName(sourceTag))
739 // Otherwise, if the media element does not have a src attribute but has a source
740 // element child, then let mode be children and let candidate be the first such
741 // source element child in tree order.
744 m_nextChildNodeToConsider = 0;
745 m_currentSourceNode = 0;
747 // Otherwise the media element has neither a src attribute nor a source element
748 // child: set the networkState to NETWORK_EMPTY, and abort these steps; the
749 // synchronous section ends.
750 m_loadState = WaitingForSource;
751 setShouldDelayLoadEvent(false);
752 m_networkState = NETWORK_EMPTY;
754 LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load");
759 // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event),
760 // and set its networkState to NETWORK_LOADING.
761 setShouldDelayLoadEvent(true);
762 m_networkState = NETWORK_LOADING;
764 // 5 - Queue a task to fire a simple event named loadstart at the media element.
765 scheduleEvent(eventNames().loadstartEvent);
767 // 6 - If mode is attribute, then run these substeps
768 if (mode == attribute) {
769 m_loadState = LoadingFromSrcAttr;
771 // If the src attribute's value is the empty string ... jump down to the failed step below
772 KURL mediaURL = getNonEmptyURLAttribute(srcAttr);
773 if (mediaURL.isEmpty()) {
774 mediaLoadingFailed(MediaPlayer::FormatError);
775 LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'");
779 if (!isSafeToLoadURL(mediaURL, Complain) || !dispatchBeforeLoadEvent(mediaURL.string())) {
780 mediaLoadingFailed(MediaPlayer::FormatError);
784 // No type information is available when the url comes from the 'src' attribute so MediaPlayer
785 // will have to pick a media engine based on the file extension.
786 ContentType contentType("");
787 loadResource(mediaURL, contentType);
788 LOG(Media, "HTMLMediaElement::selectMediaResource, using 'src' attribute url");
792 // Otherwise, the source elements will be used
793 loadNextSourceChild();
796 void HTMLMediaElement::loadNextSourceChild()
798 ContentType contentType("");
799 KURL mediaURL = selectNextSourceChild(&contentType, Complain);
800 if (!mediaURL.isValid()) {
801 waitForSourceChange();
805 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
806 // Recreate the media player for the new url
810 m_loadState = LoadingFromSourceElement;
811 loadResource(mediaURL, contentType);
814 #if !PLATFORM(CHROMIUM)
815 static KURL createFileURLForApplicationCacheResource(const String& path)
817 // KURL should have a function to create a url from a path, but it does not. This function
818 // is not suitable because KURL::setPath uses encodeWithURLEscapeSequences, which it notes
819 // does not correctly escape '#' and '?'. This function works for our purposes because
820 // app cache media files are always created with encodeForFileName(createCanonicalUUIDString()).
822 #if USE(CF) && PLATFORM(WIN)
823 RetainPtr<CFStringRef> cfPath(AdoptCF, path.createCFString());
824 RetainPtr<CFURLRef> cfURL(AdoptCF, CFURLCreateWithFileSystemPath(0, cfPath.get(), kCFURLWindowsPathStyle, false));
825 KURL url(cfURL.get());
829 url.setProtocol("file");
836 void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType)
838 ASSERT(isSafeToLoadURL(initialURL, Complain));
840 LOG(Media, "HTMLMediaElement::loadResource(%s, %s)", urlForLogging(initialURL).utf8().data(), contentType.raw().utf8().data());
842 Frame* frame = document()->frame();
844 mediaLoadingFailed(MediaPlayer::FormatError);
848 KURL url = initialURL;
849 if (!frame->loader()->willLoadMediaElementURL(url)) {
850 mediaLoadingFailed(MediaPlayer::FormatError);
854 #if ENABLE(MEDIA_SOURCE)
855 // If this is a media source URL, make sure it is the one for this media element.
856 if (url.protocolIs(mediaSourceURLProtocol) && url != m_mediaSourceURL) {
857 mediaLoadingFailed(MediaPlayer::FormatError);
862 // The resource fetch algorithm
863 m_networkState = NETWORK_LOADING;
865 #if !PLATFORM(CHROMIUM)
866 // If the url should be loaded from the application cache, pass the url of the cached file
867 // to the media engine.
868 ApplicationCacheHost* cacheHost = frame->loader()->documentLoader()->applicationCacheHost();
869 ApplicationCacheResource* resource = 0;
870 if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource)) {
871 // Resources that are not present in the manifest will always fail to load (at least, after the
872 // cache has been primed the first time), making the testing of offline applications simpler.
873 if (!resource || resource->path().isEmpty()) {
874 mediaLoadingFailed(MediaPlayer::NetworkError);
880 // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app
881 // cache is an internal detail not exposed through the media element API.
884 #if !PLATFORM(CHROMIUM)
886 url = createFileURLForApplicationCacheResource(resource->path());
887 LOG(Media, "HTMLMediaElement::loadResource - will load from app cache -> %s", urlForLogging(url).utf8().data());
891 LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLogging(m_currentSrc).utf8().data());
893 if (m_sendProgressEvents)
894 startProgressEventTimer();
896 Settings* settings = document()->settings();
897 bool privateMode = !settings || settings->privateBrowsingEnabled();
898 m_player->setPrivateBrowsingMode(privateMode);
900 // Reset display mode to force a recalculation of what to show because we are resetting the player.
901 setDisplayMode(Unknown);
904 m_player->setPreload(m_preload);
905 m_player->setPreservesPitch(m_webkitPreservesPitch);
907 if (fastHasAttribute(mutedAttr))
911 #if ENABLE(TIZEN_MM_PLAYER)
912 // frameView is required for both video and audio to register evas callback.
913 // set frameView even though renderer is null. (in case of hidden video and audio)
914 m_player->setFrameView(document()->view());
917 if (!m_player->load(url.string(), contentType))
918 mediaLoadingFailed(MediaPlayer::FormatError);
920 // If there is no poster to display, allow the media engine to render video frames as soon as
921 // they are available.
922 updateDisplayState();
925 renderer()->updateFromElement();
928 #if ENABLE(VIDEO_TRACK)
929 void HTMLMediaElement::updateActiveTextTrackCues(float movieTime)
931 Vector<CueIntervalTree::IntervalType> previouslyVisibleCues = m_currentlyVisibleCues;
933 m_currentlyVisibleCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
935 // FIXME(72171): Events need to be sorted and filtered before dispatching.
937 for (size_t i = 0; i < previouslyVisibleCues.size(); ++i) {
938 if (!m_currentlyVisibleCues.contains(previouslyVisibleCues[i]))
939 previouslyVisibleCues[i].data()->setIsActive(false);
941 for (size_t i = 0; i < m_currentlyVisibleCues.size(); ++i) {
942 if (!previouslyVisibleCues.contains(m_currentlyVisibleCues[i]))
943 m_currentlyVisibleCues[i].data()->setIsActive(true);
946 // FIXME(72173): Pause the media element for cues going past their endTime
947 // during a monotonic time increase.
950 void HTMLMediaElement::textTrackReadyStateChanged(TextTrack*)
952 // FIXME(62885): Implement.
955 void HTMLMediaElement::textTrackModeChanged(TextTrack*)
957 // FIXME(62885): Implement.
960 void HTMLMediaElement::textTrackKindChanged(TextTrack*)
962 // FIXME(62885): Implement.
965 void HTMLMediaElement::textTrackAddCues(TextTrack*, const TextTrackCueList* cues)
967 for (size_t i = 0; i < cues->length(); ++i)
968 textTrackAddCue(cues->item(i)->track(), cues->item(i));
971 void HTMLMediaElement::textTrackRemoveCues(TextTrack*, const TextTrackCueList* cues)
973 for (size_t i = 0; i < cues->length(); ++i)
974 textTrackRemoveCue(cues->item(i)->track(), cues->item(i));
977 void HTMLMediaElement::textTrackAddCue(TextTrack*, PassRefPtr<TextTrackCue> cue)
979 m_cueTree.add(m_cueTree.createInterval(cue->startTime(), cue->endTime(), cue.get()));
982 void HTMLMediaElement::textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue> cue)
984 m_cueTree.remove(m_cueTree.createInterval(cue->startTime(), cue->endTime(), cue.get()));
989 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidURLAction actionIfInvalid)
991 if (!url.isValid()) {
992 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLogging(url).utf8().data());
996 Frame* frame = document()->frame();
997 if (!frame || !document()->securityOrigin()->canDisplay(url)) {
998 if (actionIfInvalid == Complain)
999 FrameLoader::reportLocalLoadFailed(frame, url.string());
1000 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLogging(url).utf8().data());
1004 if (!document()->contentSecurityPolicy()->allowMediaFromSource(url)) {
1005 LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> rejected by Content Security Policy", urlForLogging(url).utf8().data());
1012 void HTMLMediaElement::startProgressEventTimer()
1014 if (m_progressEventTimer.isActive())
1017 m_previousProgressTime = WTF::currentTime();
1018 m_previousProgress = 0;
1019 // 350ms is not magic, it is in the spec!
1020 m_progressEventTimer.startRepeating(0.350);
1023 void HTMLMediaElement::waitForSourceChange()
1025 LOG(Media, "HTMLMediaElement::waitForSourceChange");
1027 stopPeriodicTimers();
1028 m_loadState = WaitingForSource;
1030 // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
1031 m_networkState = NETWORK_NO_SOURCE;
1033 // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1034 setShouldDelayLoadEvent(false);
1036 updateDisplayState();
1039 renderer()->updateFromElement();
1042 void HTMLMediaElement::noneSupported()
1044 LOG(Media, "HTMLMediaElement::noneSupported");
1046 stopPeriodicTimers();
1047 m_loadState = WaitingForSource;
1048 m_currentSourceNode = 0;
1051 // 6 - Reaching this step indicates that the media resource failed to load or that the given
1052 // URL could not be resolved. In one atomic operation, run the following steps:
1054 // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to
1055 // MEDIA_ERR_SRC_NOT_SUPPORTED.
1056 m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
1058 // 6.2 - Forget the media element's media-resource-specific text tracks.
1060 // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
1061 m_networkState = NETWORK_NO_SOURCE;
1063 // 7 - Queue a task to fire a simple event named error at the media element.
1064 scheduleEvent(eventNames().errorEvent);
1066 // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1067 setShouldDelayLoadEvent(false);
1069 // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed,
1070 // the element won't attempt to load another resource.
1072 updateDisplayState();
1075 renderer()->updateFromElement();
1078 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
1080 LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code()));
1082 // 1 - The user agent should cancel the fetching process.
1083 stopPeriodicTimers();
1084 m_loadState = WaitingForSource;
1086 // 2 - Set the error attribute to a new MediaError object whose code attribute is
1087 // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
1090 // 3 - Queue a task to fire a simple event named error at the media element.
1091 scheduleEvent(eventNames().errorEvent);
1093 #if ENABLE(MEDIA_SOURCE)
1094 if (m_sourceState != SOURCE_CLOSED)
1095 setSourceState(SOURCE_CLOSED);
1098 // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
1099 // task to fire a simple event called emptied at the element.
1100 m_networkState = NETWORK_EMPTY;
1101 scheduleEvent(eventNames().emptiedEvent);
1103 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1104 setShouldDelayLoadEvent(false);
1106 // 6 - Abort the overall resource selection algorithm.
1107 m_currentSourceNode = 0;
1110 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
1112 LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks");
1114 m_pendingEvents.clear();
1116 for (Node* node = firstChild(); node; node = node->nextSibling()) {
1117 if (node->hasTagName(sourceTag))
1118 static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent();
1122 Document* HTMLMediaElement::mediaPlayerOwningDocument()
1124 Document* d = document();
1127 d = ownerDocument();
1132 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
1134 beginProcessingMediaPlayerCallback();
1135 setNetworkState(m_player->networkState());
1136 endProcessingMediaPlayerCallback();
1139 void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error)
1141 stopPeriodicTimers();
1143 // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
1144 // <source> children, schedule the next one
1145 if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
1147 if (m_currentSourceNode)
1148 m_currentSourceNode->scheduleErrorEvent();
1150 LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed");
1152 if (havePotentialSourceChild()) {
1153 LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>");
1154 scheduleNextSourceChild();
1156 LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting");
1157 waitForSourceChange();
1163 if (error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA)
1164 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
1165 else if (error == MediaPlayer::DecodeError)
1166 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
1167 else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr)
1170 updateDisplayState();
1171 if (hasMediaControls()) {
1172 mediaControls()->reset();
1173 mediaControls()->reportedError();
1177 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
1179 LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState));
1181 if (state == MediaPlayer::Empty) {
1182 // Just update the cached state and leave, we can't do anything.
1183 m_networkState = NETWORK_EMPTY;
1187 if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
1188 mediaLoadingFailed(state);
1192 if (state == MediaPlayer::Idle) {
1193 if (m_networkState > NETWORK_IDLE) {
1194 m_progressEventTimer.stop();
1195 scheduleEvent(eventNames().suspendEvent);
1196 setShouldDelayLoadEvent(false);
1198 m_networkState = NETWORK_IDLE;
1201 if (state == MediaPlayer::Loading) {
1202 if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
1203 startProgressEventTimer();
1204 m_networkState = NETWORK_LOADING;
1207 if (state == MediaPlayer::Loaded) {
1208 if (m_networkState != NETWORK_IDLE) {
1209 m_progressEventTimer.stop();
1211 // Schedule one last progress event so we guarantee that at least one is fired
1212 // for files that load very quickly.
1213 scheduleEvent(eventNames().progressEvent);
1215 m_networkState = NETWORK_IDLE;
1216 m_completelyLoaded = true;
1219 if (hasMediaControls())
1220 mediaControls()->updateStatusDisplay();
1223 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
1225 beginProcessingMediaPlayerCallback();
1227 setReadyState(m_player->readyState());
1229 endProcessingMediaPlayerCallback();
1232 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
1234 LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState));
1236 // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
1237 bool wasPotentiallyPlaying = potentiallyPlaying();
1239 ReadyState oldState = m_readyState;
1240 m_readyState = static_cast<ReadyState>(state);
1242 if (m_readyState == oldState)
1245 if (oldState > m_readyStateMaximum)
1246 m_readyStateMaximum = oldState;
1248 if (m_networkState == NETWORK_EMPTY)
1252 // 4.8.10.9, step 11
1253 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
1254 scheduleEvent(eventNames().waitingEvent);
1256 // 4.8.10.10 step 14 & 15.
1257 if (m_readyState >= HAVE_CURRENT_DATA)
1260 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
1262 scheduleTimeupdateEvent(false);
1263 scheduleEvent(eventNames().waitingEvent);
1267 if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
1268 scheduleEvent(eventNames().durationchangeEvent);
1269 scheduleEvent(eventNames().loadedmetadataEvent);
1270 if (hasMediaControls())
1271 mediaControls()->loadedMetadata();
1273 renderer()->updateFromElement();
1276 bool shouldUpdateDisplayState = false;
1278 if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
1279 m_haveFiredLoadedData = true;
1280 shouldUpdateDisplayState = true;
1281 scheduleEvent(eventNames().loadeddataEvent);
1282 setShouldDelayLoadEvent(false);
1285 bool isPotentiallyPlaying = potentiallyPlaying();
1286 if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) {
1287 scheduleEvent(eventNames().canplayEvent);
1288 if (isPotentiallyPlaying)
1289 scheduleEvent(eventNames().playingEvent);
1290 shouldUpdateDisplayState = true;
1293 if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) {
1294 if (oldState <= HAVE_CURRENT_DATA)
1295 scheduleEvent(eventNames().canplayEvent);
1297 scheduleEvent(eventNames().canplaythroughEvent);
1299 if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
1300 scheduleEvent(eventNames().playingEvent);
1302 if (m_autoplaying && m_paused && autoplay()) {
1304 invalidateCachedTime();
1305 scheduleEvent(eventNames().playEvent);
1306 scheduleEvent(eventNames().playingEvent);
1309 shouldUpdateDisplayState = true;
1312 if (shouldUpdateDisplayState) {
1313 updateDisplayState();
1314 if (hasMediaControls())
1315 mediaControls()->updateStatusDisplay();
1319 updateMediaController();
1322 #if ENABLE(MEDIA_SOURCE)
1323 void HTMLMediaElement::mediaPlayerSourceOpened()
1325 beginProcessingMediaPlayerCallback();
1327 setSourceState(SOURCE_OPEN);
1329 endProcessingMediaPlayerCallback();
1332 String HTMLMediaElement::mediaPlayerSourceURL() const
1334 return m_mediaSourceURL.string();
1338 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
1341 if (m_networkState != NETWORK_LOADING)
1344 unsigned progress = m_player->bytesLoaded();
1345 double time = WTF::currentTime();
1346 double timedelta = time - m_previousProgressTime;
1348 if (progress == m_previousProgress) {
1349 if (timedelta > 3.0 && !m_sentStalledEvent) {
1350 scheduleEvent(eventNames().stalledEvent);
1351 m_sentStalledEvent = true;
1352 setShouldDelayLoadEvent(false);
1355 scheduleEvent(eventNames().progressEvent);
1356 m_previousProgress = progress;
1357 m_previousProgressTime = time;
1358 m_sentStalledEvent = false;
1360 renderer()->updateFromElement();
1364 void HTMLMediaElement::rewind(float timeDelta)
1366 LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta);
1369 setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e);
1372 void HTMLMediaElement::returnToRealtime()
1374 LOG(Media, "HTMLMediaElement::returnToRealtime");
1376 setCurrentTime(maxTimeSeekable(), e);
1379 void HTMLMediaElement::addPlayedRange(float start, float end)
1381 LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end);
1382 if (!m_playedTimeRanges)
1383 m_playedTimeRanges = TimeRanges::create();
1384 m_playedTimeRanges->add(start, end);
1387 bool HTMLMediaElement::supportsSave() const
1389 return m_player ? m_player->supportsSave() : false;
1392 bool HTMLMediaElement::supportsScanning() const
1394 return m_player ? m_player->supportsScanning() : false;
1397 void HTMLMediaElement::prepareToPlay()
1399 LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this);
1400 if (m_havePreparedToPlay)
1402 m_havePreparedToPlay = true;
1403 m_player->prepareToPlay();
1406 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
1408 LOG(Media, "HTMLMediaElement::seek(%f)", time);
1412 // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception.
1413 if (m_readyState == HAVE_NOTHING || !m_player) {
1414 ec = INVALID_STATE_ERR;
1418 // If the media engine has been told to postpone loading data, let it go ahead now.
1419 if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
1422 // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
1423 refreshCachedTime();
1424 float now = currentTime();
1426 // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
1427 // already running. Abort that other instance of the algorithm without waiting for the step that
1428 // it is running to complete.
1429 // Nothing specific to be done here.
1431 // 3 - Set the seeking IDL attribute to true.
1432 // The flag will be cleared when the engine tells us the time has actually changed.
1435 // 5 - If the new playback position is later than the end of the media resource, then let it be the end
1436 // of the media resource instead.
1437 time = min(time, duration());
1439 // 6 - If the new playback position is less than the earliest possible position, let it be that position instead.
1440 float earliestTime = m_player->startTime();
1441 time = max(time, earliestTime);
1443 // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
1444 // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
1445 // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
1446 // not generate a timechanged callback. This means m_seeking will never be cleared and we will never
1447 // fire a 'seeked' event.
1449 float mediaTime = m_player->mediaTimeForTimeValue(time);
1450 if (time != mediaTime)
1451 LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime);
1453 time = m_player->mediaTimeForTimeValue(time);
1455 // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the
1456 // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute
1457 // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
1458 // attribute then set the seeking IDL attribute to false and abort these steps.
1459 RefPtr<TimeRanges> seekableRanges = seekable();
1461 // Short circuit seeking to the current time by just firing the events if no seek is required.
1462 // Don't skip calling the media engine if we are in poster mode because a seek should always
1463 // cancel poster display.
1464 bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster);
1466 #if ENABLE(MEDIA_SOURCE)
1467 // Always notify the media engine of a seek if the source is not closed. This ensures that the source is
1468 // always in a flushed state when the 'seeking' event fires.
1469 if (m_sourceState != SOURCE_CLOSED)
1470 noSeekRequired = false;
1473 if (noSeekRequired) {
1475 scheduleEvent(eventNames().seekingEvent);
1476 scheduleTimeupdateEvent(false);
1477 scheduleEvent(eventNames().seekedEvent);
1482 time = seekableRanges->nearest(time);
1485 if (m_lastSeekTime < now)
1486 addPlayedRange(m_lastSeekTime, now);
1488 m_lastSeekTime = time;
1489 m_sentEndEvent = false;
1491 #if ENABLE(MEDIA_SOURCE)
1492 if (m_sourceState == SOURCE_ENDED)
1493 setSourceState(SOURCE_OPEN);
1496 // 8 - Set the current playback position to the given new playback position
1497 m_player->seek(time);
1499 // 9 - Queue a task to fire a simple event named seeking at the element.
1500 scheduleEvent(eventNames().seekingEvent);
1502 // 10 - Queue a task to fire a simple event named timeupdate at the element.
1503 scheduleTimeupdateEvent(false);
1505 // 11-15 are handled, if necessary, when the engine signals a readystate change.
1508 void HTMLMediaElement::finishSeek()
1510 LOG(Media, "HTMLMediaElement::finishSeek");
1512 // 4.8.10.9 Seeking step 14
1515 // 4.8.10.9 Seeking step 15
1516 scheduleEvent(eventNames().seekedEvent);
1518 setDisplayMode(Video);
1521 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
1523 return m_readyState;
1526 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
1528 return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
1531 bool HTMLMediaElement::hasAudio() const
1533 return m_player ? m_player->hasAudio() : false;
1536 bool HTMLMediaElement::seeking() const
1541 void HTMLMediaElement::refreshCachedTime() const
1543 m_cachedTime = m_player->currentTime();
1544 m_cachedTimeWallClockUpdateTime = WTF::currentTime();
1547 void HTMLMediaElement::invalidateCachedTime()
1549 LOG(Media, "HTMLMediaElement::invalidateCachedTime");
1551 // Don't try to cache movie time when playback first starts as the time reported by the engine
1552 // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
1554 static const double minimumTimePlayingBeforeCacheSnapshot = 0.5;
1556 m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot;
1557 m_cachedTime = invalidMediaTime;
1561 float HTMLMediaElement::currentTime() const
1563 #if LOG_CACHED_TIME_WARNINGS
1564 static const double minCachedDeltaForWarning = 0.01;
1571 LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime);
1572 return m_lastSeekTime;
1575 if (m_cachedTime != invalidMediaTime && m_paused) {
1576 #if LOG_CACHED_TIME_WARNINGS
1577 float delta = m_cachedTime - m_player->currentTime();
1578 if (delta > minCachedDeltaForWarning)
1579 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta);
1581 return m_cachedTime;
1584 // Is it too soon use a cached time?
1585 double now = WTF::currentTime();
1586 double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
1588 if (maximumDurationToCacheMediaTime && m_cachedTime != invalidMediaTime && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) {
1589 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime;
1591 // Not too soon, use the cached time only if it hasn't expired.
1592 if (wallClockDelta < maximumDurationToCacheMediaTime) {
1593 float adjustedCacheTime = static_cast<float>(m_cachedTime + (m_playbackRate * wallClockDelta));
1595 #if LOG_CACHED_TIME_WARNINGS
1596 float delta = adjustedCacheTime - m_player->currentTime();
1597 if (delta > minCachedDeltaForWarning)
1598 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when playing", delta);
1600 return adjustedCacheTime;
1604 #if LOG_CACHED_TIME_WARNINGS
1605 if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != invalidMediaTime) {
1606 double wallClockDelta = now - m_cachedTimeWallClockUpdateTime;
1607 float delta = m_cachedTime + (m_playbackRate * wallClockDelta) - m_player->currentTime();
1608 LOG(Media, "HTMLMediaElement::currentTime - cached time was %f seconds off of media time when it expired", delta);
1612 refreshCachedTime();
1614 return m_cachedTime;
1617 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
1619 if (m_mediaController) {
1620 ec = INVALID_STATE_ERR;
1626 float HTMLMediaElement::startTime() const
1630 return m_player->startTime();
1633 double HTMLMediaElement::initialTime() const
1637 return m_player->initialTime();
1640 float HTMLMediaElement::duration() const
1642 if (m_player && m_readyState >= HAVE_METADATA)
1643 return m_player->duration();
1645 return numeric_limits<float>::quiet_NaN();
1648 bool HTMLMediaElement::paused() const
1653 float HTMLMediaElement::defaultPlaybackRate() const
1655 return m_defaultPlaybackRate;
1658 void HTMLMediaElement::setDefaultPlaybackRate(float rate)
1660 if (m_defaultPlaybackRate != rate) {
1661 m_defaultPlaybackRate = rate;
1662 scheduleEvent(eventNames().ratechangeEvent);
1666 float HTMLMediaElement::playbackRate() const
1668 return m_playbackRate;
1671 void HTMLMediaElement::setPlaybackRate(float rate)
1673 LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate);
1675 if (m_playbackRate != rate) {
1676 m_playbackRate = rate;
1677 invalidateCachedTime();
1678 scheduleEvent(eventNames().ratechangeEvent);
1681 if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController)
1682 m_player->setRate(rate);
1685 void HTMLMediaElement::updatePlaybackRate()
1687 float effectiveRate = m_mediaController ? m_mediaController->playbackRate() : m_playbackRate;
1688 if (m_player && potentiallyPlaying() && m_player->rate() != effectiveRate && !m_mediaController)
1689 m_player->setRate(effectiveRate);
1692 bool HTMLMediaElement::webkitPreservesPitch() const
1694 return m_webkitPreservesPitch;
1697 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
1699 LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch));
1701 m_webkitPreservesPitch = preservesPitch;
1706 m_player->setPreservesPitch(preservesPitch);
1709 bool HTMLMediaElement::ended() const
1711 // 4.8.10.8 Playing the media resource
1712 // The ended attribute must return true if the media element has ended
1713 // playback and the direction of playback is forwards, and false otherwise.
1714 return endedPlayback() && m_playbackRate > 0;
1717 bool HTMLMediaElement::autoplay() const
1719 #if ENABLE(TIZEN_MM_PLAYER)
1720 if (hasVideo()) // disable video autoplay
1723 return fastHasAttribute(autoplayAttr);
1726 void HTMLMediaElement::setAutoplay(bool b)
1728 LOG(Media, "HTMLMediaElement::setAutoplay(%s)", boolString(b));
1729 setBooleanAttribute(autoplayAttr, b);
1732 String HTMLMediaElement::preload() const
1734 switch (m_preload) {
1735 case MediaPlayer::None:
1738 case MediaPlayer::MetaData:
1741 case MediaPlayer::Auto:
1746 ASSERT_NOT_REACHED();
1750 void HTMLMediaElement::setPreload(const String& preload)
1752 LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data());
1753 setAttribute(preloadAttr, preload);
1756 void HTMLMediaElement::play()
1758 LOG(Media, "HTMLMediaElement::play()");
1760 if (userGestureRequiredForRateChange() && !ScriptController::processingUserGesture())
1763 Settings* settings = document()->settings();
1764 if (settings && settings->needsSiteSpecificQuirks() && m_dispatchingCanPlayEvent && !m_loadInitiatedByUserGesture) {
1765 // It should be impossible to be processing the canplay event while handling a user gesture
1766 // since it is dispatched asynchronously.
1767 ASSERT(!ScriptController::processingUserGesture());
1768 String host = document()->baseURL().host();
1769 if (host.endsWith(".npr.org", false) || equalIgnoringCase(host, "npr.org"))
1776 void HTMLMediaElement::playInternal()
1778 LOG(Media, "HTMLMediaElement::playInternal");
1780 // 4.8.10.9. Playing the media resource
1781 if (!m_player || m_networkState == NETWORK_EMPTY)
1782 scheduleLoad(MediaResource);
1784 if (endedPlayback()) {
1785 ExceptionCode unused;
1789 if (m_mediaController)
1790 m_mediaController->bringElementUpToSpeed(this);
1794 invalidateCachedTime();
1795 scheduleEvent(eventNames().playEvent);
1797 if (m_readyState <= HAVE_CURRENT_DATA)
1798 scheduleEvent(eventNames().waitingEvent);
1799 else if (m_readyState >= HAVE_FUTURE_DATA)
1800 scheduleEvent(eventNames().playingEvent);
1802 m_autoplaying = false;
1805 updateMediaController();
1808 void HTMLMediaElement::pause()
1810 LOG(Media, "HTMLMediaElement::pause()");
1812 if (userGestureRequiredForRateChange() && !ScriptController::processingUserGesture())
1819 void HTMLMediaElement::pauseInternal()
1821 LOG(Media, "HTMLMediaElement::pauseInternal");
1823 // 4.8.10.9. Playing the media resource
1824 if (!m_player || m_networkState == NETWORK_EMPTY)
1825 scheduleLoad(MediaResource);
1827 m_autoplaying = false;
1831 scheduleTimeupdateEvent(false);
1832 scheduleEvent(eventNames().pauseEvent);
1838 #if ENABLE(MEDIA_SOURCE)
1839 void HTMLMediaElement::webkitSourceAppend(PassRefPtr<Uint8Array> data, ExceptionCode& ec)
1841 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
1842 ec = INVALID_STATE_ERR;
1846 if (!data.get() || !m_player->sourceAppend(data->data(), data->length())) {
1852 void HTMLMediaElement::webkitSourceEndOfStream(unsigned short status, ExceptionCode& ec)
1854 if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
1855 ec = INVALID_STATE_ERR;
1859 MediaPlayer::EndOfStreamStatus eosStatus = MediaPlayer::EosNoError;
1863 eosStatus = MediaPlayer::EosNoError;
1865 case EOS_NETWORK_ERR:
1866 eosStatus = MediaPlayer::EosNetworkError;
1868 case EOS_DECODE_ERR:
1869 eosStatus = MediaPlayer::EosDecodeError;
1876 setSourceState(SOURCE_ENDED);
1877 m_player->sourceEndOfStream(eosStatus);
1880 HTMLMediaElement::SourceState HTMLMediaElement::webkitSourceState() const
1882 return m_sourceState;
1885 void HTMLMediaElement::setSourceState(SourceState state)
1887 SourceState oldState = m_sourceState;
1888 m_sourceState = static_cast<SourceState>(state);
1890 if (m_sourceState == oldState)
1893 if (m_sourceState == SOURCE_CLOSED) {
1894 scheduleEvent(eventNames().webkitsourcecloseEvent);
1898 if (oldState == SOURCE_OPEN && m_sourceState == SOURCE_ENDED) {
1899 scheduleEvent(eventNames().webkitsourceendedEvent);
1903 if (m_sourceState == SOURCE_OPEN) {
1904 scheduleEvent(eventNames().webkitsourceopenEvent);
1910 bool HTMLMediaElement::loop() const
1912 return fastHasAttribute(loopAttr);
1915 void HTMLMediaElement::setLoop(bool b)
1917 LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b));
1918 setBooleanAttribute(loopAttr, b);
1921 bool HTMLMediaElement::controls() const
1923 Frame* frame = document()->frame();
1925 // always show controls when scripting is disabled
1926 if (frame && !frame->script()->canExecuteScripts(NotAboutToExecuteScript))
1929 // always show controls for video when fullscreen playback is required.
1930 if (isVideo() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback())
1933 // Always show controls when in full screen mode.
1937 return fastHasAttribute(controlsAttr);
1940 void HTMLMediaElement::setControls(bool b)
1942 LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b));
1943 setBooleanAttribute(controlsAttr, b);
1946 float HTMLMediaElement::volume() const
1951 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
1953 LOG(Media, "HTMLMediaElement::setVolume(%f)", vol);
1955 if (vol < 0.0f || vol > 1.0f) {
1956 ec = INDEX_SIZE_ERR;
1960 if (m_volume != vol) {
1963 scheduleEvent(eventNames().volumechangeEvent);
1967 bool HTMLMediaElement::muted() const
1972 void HTMLMediaElement::setMuted(bool muted)
1974 LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted));
1976 if (m_muted != muted) {
1978 // Avoid recursion when the player reports volume changes.
1979 if (!processingMediaPlayerCallback()) {
1981 m_player->setMuted(m_muted);
1982 if (hasMediaControls())
1983 mediaControls()->changedMute();
1986 scheduleEvent(eventNames().volumechangeEvent);
1990 void HTMLMediaElement::togglePlayState()
1992 LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay()));
1994 // We can safely call the internal play/pause methods, which don't check restrictions, because
1995 // this method is only called from the built-in media controller
1997 updatePlaybackRate();
2003 void HTMLMediaElement::beginScrubbing()
2005 LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused()));
2009 // Because a media element stays in non-paused state when it reaches end, playback resumes
2010 // when the slider is dragged from the end to another position unless we pause first. Do
2011 // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
2014 // Not at the end but we still want to pause playback so the media engine doesn't try to
2015 // continue playing during scrubbing. Pause without generating an event as we will
2016 // unpause after scrubbing finishes.
2017 setPausedInternal(true);
2022 void HTMLMediaElement::endScrubbing()
2024 LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal));
2026 if (m_pausedInternal)
2027 setPausedInternal(false);
2030 // The spec says to fire periodic timeupdate events (those sent while playing) every
2031 // "15 to 250ms", we choose the slowest frequency
2032 static const double maxTimeupdateEventFrequency = 0.25;
2034 static const double timeWithoutMouseMovementBeforeHidingControls = 3;
2036 void HTMLMediaElement::startPlaybackProgressTimer()
2038 if (m_playbackProgressTimer.isActive())
2041 m_previousProgressTime = WTF::currentTime();
2042 m_previousProgress = 0;
2043 m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
2046 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
2049 if (!m_playbackRate)
2052 scheduleTimeupdateEvent(true);
2053 if (hasMediaControls())
2054 mediaControls()->playbackProgressed();
2055 // FIXME: deal with cue ranges here
2057 #if ENABLE(VIDEO_TRACK)
2058 updateActiveTextTrackCues(currentTime());
2062 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
2064 double now = WTF::currentTime();
2065 double timedelta = now - m_lastTimeUpdateEventWallTime;
2067 // throttle the periodic events
2068 if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
2071 // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
2072 // event at a given time so filter here
2073 float movieTime = currentTime();
2074 if (movieTime != m_lastTimeUpdateEventMovieTime) {
2075 scheduleEvent(eventNames().timeupdateEvent);
2076 m_lastTimeUpdateEventWallTime = now;
2077 m_lastTimeUpdateEventMovieTime = movieTime;
2081 bool HTMLMediaElement::canPlay() const
2083 return paused() || ended() || m_readyState < HAVE_METADATA;
2086 float HTMLMediaElement::percentLoaded() const
2090 float duration = m_player->duration();
2092 if (!duration || isinf(duration))
2096 RefPtr<TimeRanges> timeRanges = m_player->buffered();
2097 for (unsigned i = 0; i < timeRanges->length(); ++i) {
2098 ExceptionCode ignoredException;
2099 float start = timeRanges->start(i, ignoredException);
2100 float end = timeRanges->end(i, ignoredException);
2101 buffered += end - start;
2103 return buffered / duration;
2106 #if ENABLE(VIDEO_TRACK)
2107 PassRefPtr<TextTrack> HTMLMediaElement::addTrack(const String& kind, const String& label, const String& language, ExceptionCode& ec)
2109 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2112 // 4.8.10.12.4 Text track API
2113 // The addTextTrack(kind, label, language) method of media elements, when invoked, must run the following steps:
2115 // 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps
2116 if (!TextTrack::isValidKindKeyword(kind)) {
2121 // 2. If the label argument was omitted, let label be the empty string.
2122 // 3. If the language argument was omitted, let language be the empty string.
2123 // 4. Create a new TextTrack object.
2124 RefPtr<TextTrack> textTrack = TextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, kind, label, language);
2126 // 5. Create a new text track corresponding to the new object, and set its text track kind to kind, its text
2127 // track label to label, its text track language to language, its text track readiness state to the text track
2128 // loaded state, its text track mode to the text track hidden mode, and its text track list of cues to an empty list.
2130 // 6. Add the new text track to the media element's list of text tracks.
2131 addTextTrack(textTrack);
2133 return textTrack.release();
2136 void HTMLMediaElement::addTextTrack(PassRefPtr<TextTrack> track)
2138 textTracks()->append(track);
2139 configureTextTracks();
2142 void HTMLMediaElement::configureTextTracks()
2144 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2147 // 4.8.10.12.3 Sourcing out-of-band text tracks
2149 // When a text track corresponding to a track element is added to a media element's list of text tracks,
2150 // the user agent must set the text track mode appropriately, as determined by the following conditions:
2152 // * If the text track kind is subtitles or captions and the user has indicated an interest in having a
2153 // track with this text track kind, text track language, and text track label enabled, and there is no
2154 // other text track in the media element's list of text tracks with a text track kind of either subtitles
2155 // or captions whose text track mode is showing
2156 // * If the text track kind is descriptions and the user has indicated an interest in having text
2157 // descriptions with this text track language and text track label enabled, and there is no other text
2158 // track in the media element's list of text tracks with a text track kind of descriptions whose text
2159 // track mode is showing
2160 // Let the text track mode be showing.
2161 // If there is a text track in the media element's list of text tracks whose text track mode is showing
2162 // by default, the user agent must furthermore change that text track's text track mode to hidden.
2164 // * If the text track kind is chapters and the text track language is one that the user agent has reason
2165 // to believe is appropriate for the user, and there is no other text track in the media element's list of
2166 // text tracks with a text track kind of chapters whose text track mode is showing
2167 // Let the text track mode be showing.
2169 // * If the track element has a default attribute specified, and there is no other text track in the media
2170 // element's list of text tracks whose text track mode is showing or showing by default
2171 // Let the text track mode be showing by default.
2174 // Let the text track mode be disabled.
2176 // FIXME(71123): Until the above logic has been implemented, just tell all text tracks to load.
2177 for (Node* node = firstChild(); node; node = node->nextSibling()) {
2178 if (node->hasTagName(trackTag))
2179 static_cast<HTMLTrackElement*>(node)->scheduleLoad();
2183 TextTrackList* HTMLMediaElement::textTracks()
2185 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2189 m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
2191 return m_textTracks.get();
2194 void HTMLMediaElement::trackWasAdded(HTMLTrackElement* trackElement)
2196 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2200 if (trackElement->hasTagName(trackTag)) {
2201 KURL url = trackElement->getNonEmptyURLAttribute(srcAttr);
2202 LOG(Media, "HTMLMediaElement::trackWasAdded - 'src' is %s", urlForLogging(url).utf8().data());
2206 // 4.8.10.12.3 Sourcing out-of-band text tracks
2207 // When a track element's parent element changes and the new parent is a media element,
2208 // then the user agent must add the track element's corresponding text track to the
2209 // media element's list of text tracks ... [continues in TextTrackList::append]
2210 RefPtr<TextTrack> textTrack = trackElement->track();
2213 addTextTrack(textTrack);
2214 scheduleLoad(TextTrackResource);
2217 void HTMLMediaElement::trackWillBeRemoved(HTMLTrackElement* trackElement)
2219 if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
2223 if (trackElement->hasTagName(trackTag)) {
2224 KURL url = trackElement->getNonEmptyURLAttribute(srcAttr);
2225 LOG(Media, "HTMLMediaElement::trackWillBeRemoved - 'src' is %s", urlForLogging(url).utf8().data());
2229 RefPtr<TextTrack> textTrack = trackElement->track();
2233 // 4.8.10.12.3 Sourcing out-of-band text tracks
2234 // When a track element's parent element changes and the old parent was a media element,
2235 // then the user agent must remove the track element's corresponding text track from the
2236 // media element's list of text tracks.
2237 m_textTracks->remove(textTrack);
2241 bool HTMLMediaElement::havePotentialSourceChild()
2243 // Stash the current <source> node and next nodes so we can restore them after checking
2244 // to see there is another potential.
2245 HTMLSourceElement* currentSourceNode = m_currentSourceNode;
2246 Node* nextNode = m_nextChildNodeToConsider;
2248 KURL nextURL = selectNextSourceChild(0, DoNothing);
2250 m_currentSourceNode = currentSourceNode;
2251 m_nextChildNodeToConsider = nextNode;
2253 return nextURL.isValid();
2256 KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidURLAction actionIfInvalid)
2259 // Don't log if this was just called to find out if there are any valid <source> elements.
2260 bool shouldLog = actionIfInvalid != DoNothing;
2262 LOG(Media, "HTMLMediaElement::selectNextSourceChild");
2265 if (m_nextChildNodeToConsider == sourceChildEndOfListValue()) {
2268 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\"");
2275 HTMLSourceElement* source = 0;
2276 bool lookingForStartNode = m_nextChildNodeToConsider;
2277 bool canUse = false;
2279 for (node = firstChild(); !canUse && node; node = node->nextSibling()) {
2280 if (lookingForStartNode && m_nextChildNodeToConsider != node)
2282 lookingForStartNode = false;
2284 if (!node->hasTagName(sourceTag))
2287 source = static_cast<HTMLSourceElement*>(node);
2289 // 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
2290 mediaURL = source->getNonEmptyURLAttribute(srcAttr);
2293 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLogging(mediaURL).utf8().data());
2295 if (mediaURL.isEmpty())
2298 if (source->fastHasAttribute(mediaAttr)) {
2299 MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
2300 RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media());
2303 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data());
2305 if (!screenEval.eval(media.get()))
2309 if (source->fastHasAttribute(typeAttr)) {
2312 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is %s", source->type().utf8().data());
2314 if (!MediaPlayer::supportsType(ContentType(source->type())))
2318 // Is it safe to load this url?
2319 if (!isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string()))
2322 // Making it this far means the <source> looks reasonable.
2326 if (!canUse && actionIfInvalid == Complain)
2327 source->scheduleErrorEvent();
2332 *contentType = ContentType(source->type());
2333 m_currentSourceNode = source;
2334 m_nextChildNodeToConsider = source->nextSibling();
2335 if (!m_nextChildNodeToConsider)
2336 m_nextChildNodeToConsider = sourceChildEndOfListValue();
2338 m_currentSourceNode = 0;
2339 m_nextChildNodeToConsider = sourceChildEndOfListValue();
2344 LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode, canUse ? urlForLogging(mediaURL).utf8().data() : "");
2346 return canUse ? mediaURL : KURL();
2349 void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
2351 LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source);
2354 if (source->hasTagName(sourceTag)) {
2355 KURL url = source->getNonEmptyURLAttribute(srcAttr);
2356 LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLogging(url).utf8().data());
2360 // We should only consider a <source> element when there is not src attribute at all.
2361 if (fastHasAttribute(srcAttr))
2364 // 4.8.8 - If a source element is inserted as a child of a media element that has no src
2365 // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke
2366 // the media element's resource selection algorithm.
2367 if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
2368 scheduleLoad(MediaResource);
2372 if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) {
2373 LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source");
2374 m_nextChildNodeToConsider = source;
2378 if (m_nextChildNodeToConsider != sourceChildEndOfListValue())
2381 // 4.8.9.5, resource selection algorithm, source elements section:
2382 // 20 - Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
2383 // 21 - Asynchronously await a stable state...
2384 // 22 - Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case
2385 // it hasn't been fired yet).
2386 setShouldDelayLoadEvent(true);
2388 // 23 - Set the networkState back to NETWORK_LOADING.
2389 m_networkState = NETWORK_LOADING;
2391 // 24 - Jump back to the find next candidate step above.
2392 m_nextChildNodeToConsider = source;
2393 scheduleNextSourceChild();
2396 void HTMLMediaElement::sourceWillBeRemoved(HTMLSourceElement* source)
2398 LOG(Media, "HTMLMediaElement::sourceWillBeRemoved(%p)", source);
2401 if (source->hasTagName(sourceTag)) {
2402 KURL url = source->getNonEmptyURLAttribute(srcAttr);
2403 LOG(Media, "HTMLMediaElement::sourceWillBeRemoved - 'src' is %s", urlForLogging(url).utf8().data());
2407 if (source != m_currentSourceNode && source != m_nextChildNodeToConsider)
2410 if (source == m_nextChildNodeToConsider) {
2411 m_nextChildNodeToConsider = m_nextChildNodeToConsider->nextSibling();
2412 if (!m_nextChildNodeToConsider)
2413 m_nextChildNodeToConsider = sourceChildEndOfListValue();
2414 LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider);
2415 } else if (source == m_currentSourceNode) {
2416 // Clear the current source node pointer, but don't change the movie as the spec says:
2417 // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already
2418 // inserted in a video or audio element will have no effect.
2419 m_currentSourceNode = 0;
2420 LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0");
2424 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
2426 LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
2428 beginProcessingMediaPlayerCallback();
2430 invalidateCachedTime();
2432 // 4.8.10.9 step 14 & 15. Needed if no ReadyState change is associated with the seek.
2433 if (m_seeking && m_readyState >= HAVE_CURRENT_DATA)
2436 // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity,
2437 // it will only queue a 'timeupdate' event if we haven't already posted one at the current
2439 scheduleTimeupdateEvent(false);
2441 float now = currentTime();
2442 float dur = duration();
2444 // When the current playback position reaches the end of the media resource when the direction of
2445 // playback is forwards, then the user agent must follow these steps:
2446 if (!isnan(dur) && dur && now >= dur && m_playbackRate > 0) {
2447 // If the media element has a loop attribute specified and does not have a current media controller,
2448 if (loop() && !m_mediaController) {
2449 ExceptionCode ignoredException;
2450 m_sentEndEvent = false;
2451 // then seek to the earliest possible position of the media resource and abort these steps.
2452 seek(startTime(), ignoredException);
2454 // If the media element does not have a current media controller, and the media element
2455 // has still ended playback, and the direction of playback is still forwards, and paused
2457 if (!m_mediaController && !m_paused) {
2458 // changes paused to true and fires a simple event named pause at the media element.
2460 scheduleEvent(eventNames().pauseEvent);
2462 // Queue a task to fire a simple event named ended at the media element.
2463 if (!m_sentEndEvent) {
2464 m_sentEndEvent = true;
2465 scheduleEvent(eventNames().endedEvent);
2467 #if !ENABLE(TIZEN_DAILY_UPVERSIONING)
2468 #if ENABLE(TIZEN_MM_PLAYER)
2470 LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged reset controls");
2471 m_paused = true; /* reset to play icon */
2473 renderer()->updateFromElement();
2476 // If the media element has a current media controller, then report the controller state
2477 // for the media element's current media controller.
2478 updateMediaController();
2483 m_sentEndEvent = false;
2486 #if ENABLE(VIDEO_TRACK)
2487 updateActiveTextTrackCues(now);
2489 endProcessingMediaPlayerCallback();
2492 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
2494 LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged");
2496 beginProcessingMediaPlayerCallback();
2498 float vol = m_player->volume();
2499 if (vol != m_volume) {
2502 scheduleEvent(eventNames().volumechangeEvent);
2505 endProcessingMediaPlayerCallback();
2508 void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*)
2510 LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged");
2512 beginProcessingMediaPlayerCallback();
2514 setMuted(m_player->muted());
2515 endProcessingMediaPlayerCallback();
2518 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer* player)
2520 LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged");
2522 beginProcessingMediaPlayerCallback();
2523 scheduleEvent(eventNames().durationchangeEvent);
2524 mediaPlayerCharacteristicChanged(player);
2525 endProcessingMediaPlayerCallback();
2528 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
2530 LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged");
2532 beginProcessingMediaPlayerCallback();
2534 // Stash the rate in case the one we tried to set isn't what the engine is
2535 // using (eg. it can't handle the rate we set)
2536 m_playbackRate = m_player->rate();
2538 invalidateCachedTime();
2539 endProcessingMediaPlayerCallback();
2542 void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*)
2544 LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged");
2546 if (!m_player || m_pausedInternal)
2549 beginProcessingMediaPlayerCallback();
2550 if (m_player->paused())
2554 endProcessingMediaPlayerCallback();
2557 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*)
2559 LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks");
2561 // The MediaPlayer came across content it cannot completely handle.
2562 // This is normally acceptable except when we are in a standalone
2563 // MediaDocument. If so, tell the document what has happened.
2564 if (ownerDocument()->isMediaDocument()) {
2565 MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument());
2566 mediaDocument->mediaElementSawUnsupportedTracks();
2570 // MediaPlayerPresentation methods
2571 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
2573 beginProcessingMediaPlayerCallback();
2574 updateDisplayState();
2576 renderer()->repaint();
2577 endProcessingMediaPlayerCallback();
2580 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*)
2582 LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged");
2584 beginProcessingMediaPlayerCallback();
2586 renderer()->updateFromElement();
2587 endProcessingMediaPlayerCallback();
2590 #if USE(ACCELERATED_COMPOSITING)
2591 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*)
2593 if (renderer() && renderer()->isVideo()) {
2594 ASSERT(renderer()->view());
2595 return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer()));
2600 void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*)
2602 LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged");
2604 // Kick off a fake recalcStyle that will update the compositing tree.
2605 setNeedsStyleRecalc(SyntheticStyleChange);
2609 void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*)
2611 LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated");
2612 beginProcessingMediaPlayerCallback();
2614 renderer()->updateFromElement();
2615 endProcessingMediaPlayerCallback();
2618 void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*)
2620 LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable");
2621 beginProcessingMediaPlayerCallback();
2622 if (displayMode() == PosterWaitingForVideo) {
2623 setDisplayMode(Video);
2624 #if USE(ACCELERATED_COMPOSITING)
2625 mediaPlayerRenderingModeChanged(m_player.get());
2628 endProcessingMediaPlayerCallback();
2631 void HTMLMediaElement::mediaPlayerCharacteristicChanged(MediaPlayer*)
2633 LOG(Media, "HTMLMediaElement::mediaPlayerCharacteristicChanged");
2635 beginProcessingMediaPlayerCallback();
2636 if (hasMediaControls())
2637 mediaControls()->reset();
2639 renderer()->updateFromElement();
2640 endProcessingMediaPlayerCallback();
2643 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
2646 return TimeRanges::create();
2647 return m_player->buffered();
2650 PassRefPtr<TimeRanges> HTMLMediaElement::played()
2653 float time = currentTime();
2654 if (time > m_lastSeekTime)
2655 addPlayedRange(m_lastSeekTime, time);
2658 if (!m_playedTimeRanges)
2659 m_playedTimeRanges = TimeRanges::create();
2661 return m_playedTimeRanges->copy();
2664 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
2666 return m_player ? m_player->seekable() : TimeRanges::create();
2669 bool HTMLMediaElement::potentiallyPlaying() const
2671 // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing
2672 // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the
2673 // checks in couldPlayIfEnoughData().
2674 bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA;
2675 return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData() && !isBlockedOnMediaController();
2678 bool HTMLMediaElement::couldPlayIfEnoughData() const
2680 return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
2683 bool HTMLMediaElement::endedPlayback() const
2685 float dur = duration();
2686 if (!m_player || isnan(dur))
2689 // 4.8.10.8 Playing the media resource
2691 // A media element is said to have ended playback when the element's
2692 // readyState attribute is HAVE_METADATA or greater,
2693 if (m_readyState < HAVE_METADATA)
2696 // and the current playback position is the end of the media resource and the direction
2697 // of playback is forwards, Either the media element does not have a loop attribute specified,
2698 // or the media element has a current media controller.
2699 float now = currentTime();
2700 if (m_playbackRate > 0)
2701 return dur > 0 && now >= dur && (!loop() || m_mediaController);
2703 // or the current playback position is the earliest possible position and the direction
2704 // of playback is backwards
2705 if (m_playbackRate < 0)
2711 bool HTMLMediaElement::stoppedDueToErrors() const
2713 if (m_readyState >= HAVE_METADATA && m_error) {
2714 RefPtr<TimeRanges> seekableRanges = seekable();
2715 if (!seekableRanges->contain(currentTime()))
2722 bool HTMLMediaElement::pausedForUserInteraction() const
2724 // return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user]
2728 float HTMLMediaElement::minTimeSeekable() const
2733 float HTMLMediaElement::maxTimeSeekable() const
2735 return m_player ? m_player->maxTimeSeekable() : 0;
2738 void HTMLMediaElement::updateVolume()
2743 // Avoid recursion when the player reports volume changes.
2744 if (!processingMediaPlayerCallback()) {
2745 Page* page = document()->page();
2746 float volumeMultiplier = page ? page->mediaVolume() : 1;
2747 bool shouldMute = m_muted;
2749 if (m_mediaController) {
2750 volumeMultiplier *= m_mediaController->volume();
2751 shouldMute = m_mediaController->muted();
2754 m_player->setMuted(shouldMute);
2755 m_player->setVolume(m_volume * volumeMultiplier);
2758 if (hasMediaControls())
2759 mediaControls()->changedVolume();
2762 void HTMLMediaElement::updatePlayState()
2767 if (m_pausedInternal) {
2768 if (!m_player->paused())
2770 refreshCachedTime();
2771 m_playbackProgressTimer.stop();
2772 if (hasMediaControls())
2773 mediaControls()->playbackStopped();
2777 bool shouldBePlaying = potentiallyPlaying();
2778 bool playerPaused = m_player->paused();
2780 LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s",
2781 boolString(shouldBePlaying), boolString(playerPaused));
2783 if (shouldBePlaying) {
2784 setDisplayMode(Video);
2785 invalidateCachedTime();
2788 if (!m_isFullscreen && isVideo() && document() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback())
2791 // Set rate, muted before calling play in case they were set before the media engine was setup.
2792 // The media engine should just stash the rate and muted values since it isn't already playing.
2793 m_player->setRate(m_playbackRate);
2794 m_player->setMuted(m_muted);
2799 if (hasMediaControls())
2800 mediaControls()->playbackStarted();
2801 startPlaybackProgressTimer();
2803 } else { // Should not be playing right now
2806 refreshCachedTime();
2808 m_playbackProgressTimer.stop();
2810 float time = currentTime();
2811 if (time > m_lastSeekTime)
2812 addPlayedRange(m_lastSeekTime, time);
2814 if (couldPlayIfEnoughData())
2817 if (hasMediaControls())
2818 mediaControls()->playbackStopped();
2821 updateMediaController();
2824 renderer()->updateFromElement();
2827 void HTMLMediaElement::setPausedInternal(bool b)
2829 m_pausedInternal = b;
2833 void HTMLMediaElement::stopPeriodicTimers()
2835 m_progressEventTimer.stop();
2836 m_playbackProgressTimer.stop();
2839 void HTMLMediaElement::userCancelledLoad()
2841 LOG(Media, "HTMLMediaElement::userCancelledLoad");
2843 if (m_networkState == NETWORK_EMPTY || m_completelyLoaded)
2846 // If the media data fetching process is aborted by the user:
2848 // 1 - The user agent should cancel the fetching process.
2849 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
2852 stopPeriodicTimers();
2854 m_loadState = WaitingForSource;
2856 // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED.
2857 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
2859 // 3 - Queue a task to fire a simple event named error at the media element.
2860 scheduleEvent(eventNames().abortEvent);
2862 #if ENABLE(MEDIA_SOURCE)
2863 if (m_sourceState != SOURCE_CLOSED)
2864 setSourceState(SOURCE_CLOSED);
2867 // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
2868 // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
2869 // simple event named emptied at the element. Otherwise, set the element's networkState
2870 // attribute to the NETWORK_IDLE value.
2871 if (m_readyState == HAVE_NOTHING) {
2872 m_networkState = NETWORK_EMPTY;
2873 scheduleEvent(eventNames().emptiedEvent);
2876 m_networkState = NETWORK_IDLE;
2878 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
2879 setShouldDelayLoadEvent(false);
2881 // 6 - Abort the overall resource selection algorithm.
2882 m_currentSourceNode = 0;
2884 // Reset m_readyState since m_player is gone.
2885 m_readyState = HAVE_NOTHING;
2886 updateMediaController();
2889 bool HTMLMediaElement::canSuspend() const
2894 void HTMLMediaElement::stop()
2896 LOG(Media, "HTMLMediaElement::stop");
2900 m_inActiveDocument = false;
2901 userCancelledLoad();
2903 #if ENABLE(TIZEN_MM_PLAYER) // instead of pause , call stop directly.
2904 m_pausedInternal = true;
2905 m_playbackProgressTimer.stop();
2909 // Stop the playback without generating events
2910 setPausedInternal(true);
2914 renderer()->updateFromElement();
2916 stopPeriodicTimers();
2917 cancelPendingEventsAndCallbacks();
2920 void HTMLMediaElement::suspend(ReasonForSuspension why)
2922 LOG(Media, "HTMLMediaElement::suspend");
2926 case DocumentWillBecomeInactive:
2929 case JavaScriptDebuggerPaused:
2930 case WillShowDialog:
2931 // Do nothing, we don't pause media playback in these cases.
2936 void HTMLMediaElement::resume()
2938 LOG(Media, "HTMLMediaElement::resume");
2940 #if ENABLE(TIZEN_MM_PLAYER)
2941 // divide long press and double tab... when double tab... resume will called.
2943 m_player->catchMouseUp();
2944 bool isEmbedVideo = document()->page()->settings()->acceleratedCompositingForVideoEnabled() &&
2945 document()->page()->settings()->acceleratedCompositingEnabled();
2946 if (!isEmbedVideo && hasVideo() && !m_paused) {
2947 // make video stop if player is not paused
2948 LOG(Media, "%s() pause video\n", __FUNCTION__);
2949 pause(); // pause instead of ended
2951 if (m_isFullscreen) // ended playback of external player
2955 m_inActiveDocument = true;
2956 setPausedInternal(false);
2958 if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) {
2959 // Restart the load if it was aborted in the middle by moving the document to the page cache.
2960 // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to
2961 // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards).
2962 // This behavior is not specified but it seems like a sensible thing to do.
2968 renderer()->updateFromElement();
2971 bool HTMLMediaElement::hasPendingActivity() const
2973 // Return true when we have pending events so we can't fire events after the JS
2974 // object gets collected.
2975 bool pending = m_pendingEvents.size();
2976 LOG(Media, "HTMLMediaElement::hasPendingActivity -> %s", boolString(pending));
2980 void HTMLMediaElement::mediaVolumeDidChange()
2982 LOG(Media, "HTMLMediaElement::mediaVolumeDidChange");
2986 void HTMLMediaElement::defaultEventHandler(Event* event)
2988 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
2989 RenderObject* r = renderer();
2990 if (!r || !r->isWidget())
2993 Widget* widget = toRenderWidget(r)->widget();
2995 widget->handleEvent(event);
2997 #if ENABLE(TIZEN_MM_PLAYER)
2998 /* When touch beside controlbar, do play or pause. need the check whether controlbar was touched or not.*/
2999 if (m_player && event->isMouseEvent() ) {
3000 if ( m_player && event->type() == eventNames().mousedownEvent ) {
3001 m_player->catchMouseDown();
3003 if ( m_player && event->type() == eventNames().mouseupEvent ) {
3004 m_player->catchMouseUp();
3006 if ( event->type() == eventNames().clickEvent && hasVideo()) {
3007 /* it should be placed in clickEvent . it does not operate for controlbar.*/
3012 HTMLElement::defaultEventHandler(event);
3016 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
3018 void HTMLMediaElement::ensureMediaPlayer()
3021 createMediaPlayer();
3024 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification)
3026 if (notification == MediaPlayerNotificationPlayPauseButtonPressed) {
3032 m_player->deliverNotification(notification);
3035 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy)
3037 ensureMediaPlayer();
3038 m_player->setMediaPlayerProxy(proxy);
3041 void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values)
3043 Frame* frame = document()->frame();
3046 KURL posterURL = getNonEmptyURLAttribute(posterAttr);
3047 if (!posterURL.isEmpty() && frame && frame->loader()->willLoadMediaElementURL(posterURL)) {
3048 names.append("_media_element_poster_");
3049 values.append(posterURL.string());
3054 names.append("_media_element_controls_");
3055 values.append("true");
3059 if (!isSafeToLoadURL(url, Complain))
3060 url = selectNextSourceChild(0, DoNothing);
3063 if (url.isValid() && frame && frame->loader()->willLoadMediaElementURL(url)) {
3064 names.append("_media_element_src_");
3065 values.append(m_currentSrc.string());
3069 void HTMLMediaElement::finishParsingChildren()
3071 HTMLElement::finishParsingChildren();
3072 document()->updateStyleIfNeeded();
3073 createMediaPlayerProxy();
3076 void HTMLMediaElement::createMediaPlayerProxy()
3078 ensureMediaPlayer();
3080 if (m_proxyWidget || (inDocument() && !m_needWidgetUpdate))
3083 Frame* frame = document()->frame();
3087 LOG(Media, "HTMLMediaElement::createMediaPlayerProxy");
3090 Vector<String> paramNames;
3091 Vector<String> paramValues;
3093 getPluginProxyParams(url, paramNames, paramValues);
3095 // Hang onto the proxy widget so it won't be destroyed if the plug-in is set to
3097 m_proxyWidget = frame->loader()->subframeLoader()->loadMediaPlayerProxyPlugin(this, url, paramNames, paramValues);
3099 m_needWidgetUpdate = false;
3102 void HTMLMediaElement::updateWidget(PluginCreationOption)
3104 mediaElement->setNeedWidgetUpdate(false);
3106 Vector<String> paramNames;
3107 Vector<String> paramValues;
3108 // FIXME: Rename kurl to something more sensible.
3111 mediaElement->getPluginProxyParams(kurl, paramNames, paramValues);
3112 // FIXME: What if document()->frame() is 0?
3113 SubframeLoader* loader = document()->frame()->loader()->subframeLoader();
3114 loader->loadMediaPlayerProxyPlugin(mediaElement, kurl, paramNames, paramValues);
3117 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
3119 bool HTMLMediaElement::isFullscreen() const
3124 #if ENABLE(FULLSCREEN_API)
3125 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this)
3132 void HTMLMediaElement::enterFullscreen()
3134 LOG(Media, "HTMLMediaElement::enterFullscreen");
3136 #if ENABLE(FULLSCREEN_API)
3137 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) {
3138 document()->requestFullScreenForElement(this, 0, Document::ExemptIFrameAllowFulScreenRequirement);
3142 ASSERT(!m_isFullscreen);
3143 m_isFullscreen = true;
3144 if (hasMediaControls())
3145 mediaControls()->enteredFullscreen();
3146 if (document() && document()->page()) {
3147 document()->page()->chrome()->client()->enterFullscreenForNode(this);
3148 scheduleEvent(eventNames().webkitbeginfullscreenEvent);
3151 #if ENABLE(TIZEN_MM_PLAYER)
3153 m_player->enterFullscreen();
3157 void HTMLMediaElement::exitFullscreen()
3159 LOG(Media, "HTMLMediaElement::exitFullscreen");
3161 #if ENABLE(FULLSCREEN_API)
3162 if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) {
3163 if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this)
3164 document()->webkitCancelFullScreen();
3168 ASSERT(m_isFullscreen);
3169 m_isFullscreen = false;
3170 if (hasMediaControls())
3171 mediaControls()->exitedFullscreen();
3172 if (document() && document()->page()) {
3173 if (document()->page()->chrome()->requiresFullscreenForVideoPlayback())
3175 document()->page()->chrome()->client()->exitFullscreenForNode(this);
3176 scheduleEvent(eventNames().webkitendfullscreenEvent);
3180 void HTMLMediaElement::didBecomeFullscreenElement()
3182 if (hasMediaControls())
3183 mediaControls()->enteredFullscreen();
3186 void HTMLMediaElement::willStopBeingFullscreenElement()
3188 if (hasMediaControls())
3189 mediaControls()->exitedFullscreen();
3192 PlatformMedia HTMLMediaElement::platformMedia() const
3194 return m_player ? m_player->platformMedia() : NoPlatformMedia;
3197 #if USE(ACCELERATED_COMPOSITING)
3198 PlatformLayer* HTMLMediaElement::platformLayer() const
3200 return m_player ? m_player->platformLayer() : 0;
3204 bool HTMLMediaElement::hasClosedCaptions() const
3206 return m_player && m_player->hasClosedCaptions();
3209 bool HTMLMediaElement::closedCaptionsVisible() const
3211 return m_closedCaptionsVisible;
3214 void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible)
3216 LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible));
3218 if (!m_player ||!hasClosedCaptions())
3221 m_closedCaptionsVisible = closedCaptionVisible;
3222 m_player->setClosedCaptionsVisible(closedCaptionVisible);
3223 if (hasMediaControls())
3224 mediaControls()->changedClosedCaptionsVisibility();
3227 void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible)
3229 setClosedCaptionsVisible(visible);
3232 bool HTMLMediaElement::webkitClosedCaptionsVisible() const
3234 return closedCaptionsVisible();
3238 bool HTMLMediaElement::webkitHasClosedCaptions() const
3240 return hasClosedCaptions();
3243 #if ENABLE(MEDIA_STATISTICS)
3244 unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const
3248 return m_player->audioDecodedByteCount();
3251 unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const
3255 return m_player->videoDecodedByteCount();
3259 void HTMLMediaElement::mediaCanStart()
3261 LOG(Media, "HTMLMediaElement::mediaCanStart");
3263 ASSERT(m_isWaitingUntilMediaCanStart);
3264 m_isWaitingUntilMediaCanStart = false;
3268 bool HTMLMediaElement::isURLAttribute(Attribute* attribute) const
3270 return attribute->name() == srcAttr || HTMLElement::isURLAttribute(attribute);
3273 void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay)
3275 if (m_shouldDelayLoadEvent == shouldDelay)
3278 LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay));
3280 m_shouldDelayLoadEvent = shouldDelay;
3282 document()->incrementLoadEventDelayCount();
3284 document()->decrementLoadEventDelayCount();
3288 void HTMLMediaElement::getSitesInMediaCache(Vector<String>& sites)
3290 MediaPlayer::getSitesInMediaCache(sites);
3293 void HTMLMediaElement::clearMediaCache()
3295 MediaPlayer::clearMediaCache();
3298 void HTMLMediaElement::clearMediaCacheForSite(const String& site)
3300 MediaPlayer::clearMediaCacheForSite(site);
3303 void HTMLMediaElement::privateBrowsingStateDidChange()
3308 Settings* settings = document()->settings();
3309 bool privateMode = !settings || settings->privateBrowsingEnabled();
3310 LOG(Media, "HTMLMediaElement::privateBrowsingStateDidChange(%s)", boolString(privateMode));
3311 m_player->setPrivateBrowsingMode(privateMode);
3314 MediaControls* HTMLMediaElement::mediaControls()
3316 return toMediaControls(shadowRoot()->firstChild());
3319 bool HTMLMediaElement::hasMediaControls()
3324 Node* node = shadowRoot()->firstChild();
3325 return node && node->isMediaControls();
3328 bool HTMLMediaElement::createMediaControls()
3330 if (hasMediaControls())
3334 RefPtr<MediaControls> controls = MediaControls::create(this);
3338 ensureShadowRoot()->appendChild(controls, ec);
3342 void HTMLMediaElement::configureMediaControls()
3344 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
3346 if (hasMediaControls())
3347 mediaControls()->hide();
3351 if (!hasMediaControls()) {
3352 if (!createMediaControls())
3354 mediaControls()->reset();
3356 mediaControls()->show();
3359 m_player->setControls(controls());
3363 void* HTMLMediaElement::preDispatchEventHandler(Event* event)
3365 if (event && event->type() == eventNames().webkitfullscreenchangeEvent)
3366 configureMediaControls();
3371 void HTMLMediaElement::createMediaPlayer()
3373 #if ENABLE(WEB_AUDIO)
3374 if (m_audioSourceNode)
3375 m_audioSourceNode->lock();
3378 m_player = MediaPlayer::create(this);
3380 #if ENABLE(WEB_AUDIO)
3381 if (m_audioSourceNode) {
3382 // When creating the player, make sure its AudioSourceProvider knows about the MediaElementAudioSourceNode.
3383 if (audioSourceProvider())
3384 audioSourceProvider()->setClient(m_audioSourceNode);
3386 m_audioSourceNode->unlock();
3391 #if ENABLE(WEB_AUDIO)
3392 void HTMLMediaElement::setAudioSourceNode(MediaElementAudioSourceNode* sourceNode)
3394 m_audioSourceNode = sourceNode;
3396 if (audioSourceProvider())
3397 audioSourceProvider()->setClient(m_audioSourceNode);
3400 AudioSourceProvider* HTMLMediaElement::audioSourceProvider()
3403 return m_player->audioSourceProvider();
3409 #if ENABLE(MICRODATA)
3410 String HTMLMediaElement::itemValueText() const
3412 return getURLAttribute(srcAttr);
3415 void HTMLMediaElement::setItemValueText(const String& value, ExceptionCode& ec)
3417 setAttribute(srcAttr, value, ec);
3421 const String& HTMLMediaElement::mediaGroup() const
3423 return m_mediaGroup;
3426 void HTMLMediaElement::setMediaGroup(const String& group)
3428 if (m_mediaGroup == group)
3430 m_mediaGroup = group;
3432 // When a media element is created with a mediagroup attribute, and when a media element's mediagroup
3433 // attribute is set, changed, or removed, the user agent must run the following steps:
3434 // 1. Let m [this] be the media element in question.
3435 // 2. Let m have no current media controller, if it currently has one.
3438 // 3. If m's mediagroup attribute is being removed, then abort these steps.
3439 if (group.isNull() || group.isEmpty())
3442 // 4. If there is another media element whose Document is the same as m's Document (even if one or both
3443 // of these elements are not actually in the Document),
3444 HashSet<HTMLMediaElement*> elements = documentToElementSetMap().get(document());
3445 for (HashSet<HTMLMediaElement*>::iterator i = elements.begin(); i != elements.end(); ++i) {
3449 // and which also has a mediagroup attribute, and whose mediagroup attribute has the same value as
3450 // the new value of m's mediagroup attribute,
3451 if ((*i)->mediaGroup() == group) {
3452 // then let controller be that media element's current media controller.
3453 setController((*i)->controller());
3458 // Otherwise, let controller be a newly created MediaController.
3459 setController(MediaController::create(Node::scriptExecutionContext()));
3462 MediaController* HTMLMediaElement::controller() const
3464 return m_mediaController.get();
3467 void HTMLMediaElement::setController(PassRefPtr<MediaController> controller)
3469 if (m_mediaController)
3470 m_mediaController->removeMediaElement(this);
3472 m_mediaController = controller;
3474 if (m_mediaController)
3475 m_mediaController->addMediaElement(this);
3478 void HTMLMediaElement::updateMediaController()
3480 if (m_mediaController)
3481 m_mediaController->reportControllerState();
3484 bool HTMLMediaElement::isBlocked() const
3486 // A media element is a blocked media element if its readyState attribute is in the
3487 // HAVE_NOTHING state, the HAVE_METADATA state, or the HAVE_CURRENT_DATA state,
3488 if (m_readyState <= HAVE_CURRENT_DATA)
3491 // or if the element has paused for user interaction.
3492 return pausedForUserInteraction();
3495 bool HTMLMediaElement::isBlockedOnMediaController() const
3497 if (!m_mediaController)
3500 // A media element is blocked on its media controller if the MediaController is a blocked
3501 // media controller,
3502 if (m_mediaController->isBlocked())
3505 // or if its media controller position is either before the media resource's earliest possible
3506 // position relative to the MediaController's timeline or after the end of the media resource
3507 // relative to the MediaController's timeline.
3508 float mediaControllerPosition = m_mediaController->currentTime();
3509 if (mediaControllerPosition < startTime() || mediaControllerPosition > startTime() + duration())