#include "bindings/v8/ScriptController.h"
#include "bindings/v8/ScriptEventListener.h"
#include "core/css/MediaList.h"
-#include "core/css/MediaQueryEvaluator.h"
#include "core/dom/Attribute.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/FullscreenElementStack.h"
// FIXME: Remove dependency on modules/encryptedmedia (http://crbug.com/242754).
#include "modules/encryptedmedia/MediaKeyNeededEvent.h"
#include "modules/encryptedmedia/MediaKeys.h"
-#include "modules/mediastream/MediaStreamRegistry.h"
#include "platform/ContentType.h"
#include "platform/Language.h"
#include "platform/Logging.h"
return blink::Platform::current()->mimeRegistry()->supportsMediaMIMEType(type, typeCodecs, system);
}
-HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& document, bool createdByParser)
+URLRegistry* HTMLMediaElement::s_mediaStreamRegistry = 0;
+
+void HTMLMediaElement::setMediaStreamRegistry(URLRegistry* registry)
+{
+ ASSERT(!s_mediaStreamRegistry);
+ s_mediaStreamRegistry = registry;
+}
+
+bool HTMLMediaElement::isMediaStreamURL(const String& url)
+{
+ return s_mediaStreamRegistry ? s_mediaStreamRegistry->contains(url) : false;
+}
+
+HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& document)
: HTMLElement(tagName, document)
, ActiveDOMObject(&document)
, m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
, m_loadState(WaitingForSource)
, m_webLayer(0)
, m_opaque(false)
- , m_restrictions(RequirePageConsentToLoadMediaRestriction)
+ , m_restrictions(NoRestrictions)
, m_preload(MediaPlayer::Auto)
, m_displayMode(Unknown)
, m_cachedTime(MediaPlayer::invalidTime())
, m_sentEndEvent(false)
, m_pausedInternal(false)
, m_closedCaptionsVisible(false)
- , m_loadInitiatedByUserGesture(false)
, m_completelyLoaded(false)
, m_havePreparedToPlay(false)
- , m_parsingInProgress(createdByParser)
, m_tracksAreReady(true)
, m_haveVisibleTextTrack(false)
, m_processingPreferenceChange(false)
ScriptWrappable::init(this);
if (document.settings()) {
- if (document.settings()->mediaPlaybackRequiresUserGesture()) {
- addBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
- addBehaviorRestriction(RequireUserGestureForLoadRestriction);
- }
- if (document.settings()->mediaFullscreenRequiresUserGesture()) {
+ if (document.settings()->mediaPlaybackRequiresUserGesture())
+ addBehaviorRestriction(RequireUserGestureForPlayRestriction);
+ if (document.settings()->mediaFullscreenRequiresUserGesture())
addBehaviorRestriction(RequireUserGestureForFullscreenRestriction);
- }
}
// We must always have a ShadowRoot so children like <source> will not render
void HTMLMediaElement::finishParsingChildren()
{
HTMLElement::finishParsingChildren();
- m_parsingInProgress = false;
if (!RuntimeEnabledFeatures::videoTrackEnabled())
return;
if (document().settings() && !document().settings()->mediaEnabled())
return;
- if (userGestureRequiredForLoad() && !UserGestureIndicator::processingUserGesture())
- return;
-
- m_loadInitiatedByUserGesture = UserGestureIndicator::processingUserGesture();
- if (m_loadInitiatedByUserGesture)
+ if (UserGestureIndicator::processingUserGesture())
removeBehaviorsRestrictionsAfterFirstUserGesture();
+
prepareForLoad();
loadInternal();
prepareToPlay();
// trigger the event.
ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
- // Once the page has allowed an element to load media, it is free to load at will. This allows a
- // playlist that starts in a foreground tab to continue automatically if the tab is subsequently
- // put in the the background.
- removeBehaviorRestriction(RequirePageConsentToLoadMediaRestriction);
-
// HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
// disabled state when the element's resource selection algorithm last started".
if (RuntimeEnabledFeatures::videoTrackEnabled()) {
blink::WebMediaPlayer::LoadType loadType = blink::WebMediaPlayer::LoadTypeURL;
- if (MediaStreamRegistry::registry().lookupMediaStreamDescriptor(url.string())) {
- loadType = blink::WebMediaPlayer::LoadTypeMediaStream;
- removeBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
- }
-
startProgressEventTimer();
// Reset display mode to force a recalculation of what to show because we are resetting the player.
bool attemptLoad = true;
if (url.protocolIs(mediaSourceBlobProtocol)) {
- m_mediaSource = HTMLMediaSource::lookup(url.string());
+ if (isMediaStreamURL(url.string())) {
+ loadType = blink::WebMediaPlayer::LoadTypeMediaStream;
+ removeBehaviorRestriction(RequireUserGestureForPlayRestriction);
+ } else {
+ m_mediaSource = HTMLMediaSource::lookup(url.string());
- if (m_mediaSource) {
- loadType = blink::WebMediaPlayer::LoadTypeMediaSource;
+ if (m_mediaSource) {
+ loadType = blink::WebMediaPlayer::LoadTypeMediaSource;
- if (!m_mediaSource->attachToElement(this)) {
- // Forget our reference to the MediaSource, so we leave it alone
- // while processing remainder of load failure.
- m_mediaSource = 0;
- attemptLoad = false;
+ if (!m_mediaSource->attachToElement(this)) {
+ // Forget our reference to the MediaSource, so we leave it alone
+ // while processing remainder of load failure.
+ m_mediaSource = 0;
+ attemptLoad = false;
+ }
}
}
}
changeNetworkStateFromLoadingToIdle();
m_completelyLoaded = true;
}
-
- if (hasMediaControls())
- mediaControls()->updateStatusDisplay();
}
void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
scheduleEvent(EventTypeNames::playing);
- if (m_autoplaying && m_paused && autoplay() && !document().isSandboxed(SandboxAutomaticFeatures) && !userGestureRequiredForRateChange()) {
+ if (m_autoplaying && m_paused && autoplay() && !document().isSandboxed(SandboxAutomaticFeatures) && !userGestureRequiredForPlay()) {
m_paused = false;
invalidateCachedTime();
scheduleEvent(EventTypeNames::play);
if (shouldUpdateDisplayState) {
updateDisplayState();
- if (hasMediaControls()) {
+ if (hasMediaControls())
mediaControls()->refreshClosedCaptionsButtonVisibility();
- mediaControls()->updateStatusDisplay();
- }
}
updatePlayState();
m_asyncEventQueue->enqueueEvent(event.release());
}
-bool HTMLMediaElement::mediaPlayerKeyNeeded(const String& contentType, const unsigned char* initData, unsigned initDataLength)
+// Create a MediaKeyNeededEvent for WD EME.
+static PassRefPtr<Event> createNeedKeyEvent(const String& contentType, const unsigned char* initData, unsigned initDataLength)
{
- WTF_LOG(Media, "HTMLMediaElement::mediaPlayerKeyNeeded: contentType=%s", contentType.utf8().data());
-
- // Send event for WD EME.
MediaKeyNeededEventInit initializer;
initializer.contentType = contentType;
initializer.initData = Uint8Array::create(initData, initDataLength);
initializer.bubbles = false;
initializer.cancelable = false;
- RefPtr<Event> event = MediaKeyNeededEvent::create(EventTypeNames::needkey, initializer);
- event->setTarget(this);
- m_asyncEventQueue->enqueueEvent(event.release());
+ return MediaKeyNeededEvent::create(EventTypeNames::needkey, initializer);
+}
- // Send event for v0.1b EME.
- if (hasEventListeners(EventTypeNames::webkitneedkey)) {
- MediaKeyEventInit webkitInitializer;
- webkitInitializer.keySystem = String();
- webkitInitializer.sessionId = String();
- webkitInitializer.initData = Uint8Array::create(initData, initDataLength);
- webkitInitializer.bubbles = false;
- webkitInitializer.cancelable = false;
+// Create a 'needkey' MediaKeyEvent for v0.1b EME.
+static PassRefPtr<Event> createWebkitNeedKeyEvent(const String& contentType, const unsigned char* initData, unsigned initDataLength)
+{
+ MediaKeyEventInit webkitInitializer;
+ webkitInitializer.keySystem = String();
+ webkitInitializer.sessionId = String();
+ webkitInitializer.initData = Uint8Array::create(initData, initDataLength);
+ webkitInitializer.bubbles = false;
+ webkitInitializer.cancelable = false;
+
+ return MediaKeyEvent::create(EventTypeNames::webkitneedkey, webkitInitializer);
+}
- event = MediaKeyEvent::create(EventTypeNames::webkitneedkey, webkitInitializer);
+bool HTMLMediaElement::mediaPlayerKeyNeeded(const String& contentType, const unsigned char* initData, unsigned initDataLength)
+{
+ WTF_LOG(Media, "HTMLMediaElement::mediaPlayerKeyNeeded: contentType=%s", contentType.utf8().data());
+
+ if (RuntimeEnabledFeatures::encryptedMediaEnabled()) {
+ // Send event for WD EME.
+ RefPtr<Event> event = createNeedKeyEvent(contentType, initData, initDataLength);
+ event->setTarget(this);
+ m_asyncEventQueue->enqueueEvent(event.release());
+ }
+
+ if (RuntimeEnabledFeatures::prefixedEncryptedMediaEnabled()) {
+ // Send event for v0.1b EME.
+ RefPtr<Event> event = createWebkitNeedKeyEvent(contentType, initData, initDataLength);
event->setTarget(this);
m_asyncEventQueue->enqueueEvent(event.release());
}
// 1 - If the media element's readyState is HAVE_NOTHING, then raise an InvalidStateError exception.
if (m_readyState == HAVE_NOTHING || !m_player) {
- exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+ exceptionState.throwDOMException(InvalidStateError, "The element's readyState is HAVE_NOTHING.");
return;
}
void HTMLMediaElement::setCurrentTime(double time, ExceptionState& exceptionState)
{
if (m_mediaController) {
- exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+ exceptionState.throwDOMException(InvalidStateError, "No media controller is available.");
return;
}
seek(time, exceptionState);
{
WTF_LOG(Media, "HTMLMediaElement::play()");
- if (userGestureRequiredForRateChange() && !UserGestureIndicator::processingUserGesture())
+ if (userGestureRequiredForPlay() && !UserGestureIndicator::processingUserGesture())
return;
if (UserGestureIndicator::processingUserGesture())
removeBehaviorsRestrictionsAfterFirstUserGesture();
{
WTF_LOG(Media, "HTMLMediaElement::pause()");
- if (userGestureRequiredForRateChange() && !UserGestureIndicator::processingUserGesture())
- return;
-
- pauseInternal();
-}
-
-
-void HTMLMediaElement::pauseInternal()
-{
- WTF_LOG(Media, "HTMLMediaElement::pauseInternal");
-
- // 4.8.10.9. Playing the media resource
if (!m_player || m_networkState == NETWORK_EMPTY)
scheduleDelayedAction(LoadMediaResource);
return;
if (keySystem.isEmpty()) {
- exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
+ exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
return;
}
if (!m_player) {
- exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+ exceptionState.throwDOMException(InvalidStateError, "No player is available.");
return;
}
return;
if (keySystem.isEmpty()) {
- exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
+ exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
return;
}
if (!key) {
- exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
+ exceptionState.throwDOMException(SyntaxError, "The key provided is invalid.");
return;
}
if (!key->length()) {
- exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
+ exceptionState.throwDOMException(TypeMismatchError, "The key provided is invalid.");
return;
}
if (!m_player) {
- exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+ exceptionState.throwDOMException(InvalidStateError, "No player is available.");
return;
}
return;
if (keySystem.isEmpty()) {
- exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
+ exceptionState.throwDOMException(SyntaxError, "The key system provided is empty.");
return;
}
if (!m_player) {
- exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+ exceptionState.throwDOMException(InvalidStateError, "No player is available.");
return;
}
WTF_LOG(Media, "HTMLMediaElement::setVolume(%f)", vol);
if (vol < 0.0f || vol > 1.0f) {
- exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
+ exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexOutsideRange("volume", vol, 0.0f, ExceptionMessages::ExclusiveBound, 1.0f, ExceptionMessages::ExclusiveBound));
return;
}
}
}
-void HTMLMediaElement::togglePlayState()
-{
- WTF_LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay()));
-
- // We can safely call the internal play/pause methods, which don't check restrictions, because
- // this method is only called from the built-in media controller
- if (canPlay()) {
- updatePlaybackRate();
- playInternal();
- } else
- pauseInternal();
-}
-
void HTMLMediaElement::beginScrubbing()
{
WTF_LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused()));
m_fragmentEndTime = MediaPlayer::invalidTime();
if (!m_mediaController && !m_paused) {
// changes paused to true and fires a simple event named pause at the media element.
- pauseInternal();
+ pause();
}
}
return paused() || ended() || m_readyState < HAVE_METADATA;
}
-double HTMLMediaElement::percentLoaded() const
-{
- if (!m_player)
- return 0;
- double duration = m_player->duration();
-
- if (!duration || std::isinf(duration))
- return 0;
-
- double buffered = 0;
- RefPtr<TimeRanges> timeRanges = m_player->buffered();
- for (unsigned i = 0; i < timeRanges->length(); ++i) {
- double start = timeRanges->start(i, IGNORE_EXCEPTION);
- double end = timeRanges->end(i, IGNORE_EXCEPTION);
- buffered += end - start;
- }
- return buffered / duration;
-}
-
void HTMLMediaElement::mediaPlayerDidAddTrack(WebInbandTextTrack* webTrack)
{
if (!RuntimeEnabledFeatures::videoTrackEnabled())
// 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps
if (!TextTrack::isValidKindKeyword(kind)) {
- exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
+ exceptionState.throwDOMException(SyntaxError, "The 'kind' provided ('" + kind + "') is invalid.");
return 0;
}
// Do not schedule the track loading until parsing finishes so we don't start before all tracks
// in the markup have been added.
- if (!m_parsingInProgress)
+ if (isFinishedParsingChildren())
scheduleDelayedAction(LoadTextTrackResource);
if (hasMediaControls())
if (node->parentNode() != this)
continue;
- UseCounter::count(document(), UseCounter::SourceElementCandidate);
source = toHTMLSourceElement(node);
// 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
if (mediaURL.isEmpty())
goto check_again;
- if (source->fastHasAttribute(mediaAttr)) {
- MediaQueryEvaluator screenEval("screen", document().frame(), renderer() ? renderer()->style() : 0);
- RefPtr<MediaQuerySet> media = MediaQuerySet::create(source->media());
-#if !LOG_DISABLED
- if (shouldLog)
- WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data());
-#endif
- if (!screenEval.eval(media.get())) {
- UseCounter::count(document(), UseCounter::SourceElementNonMatchingMedia);
- goto check_again;
- }
- }
-
type = source->type();
// FIXME(82965): Add support for keySystem in <source> and set system from source.
if (type.isEmpty() && mediaURL.protocolIsData())
return;
if (m_player->paused())
- pauseInternal();
+ pause();
else
playInternal();
}
m_mediaController = controller;
- if (m_mediaController)
+ if (m_mediaController) {
+ UseCounter::count(document(), UseCounter::HTMLMediaElementControllerNotNull);
m_mediaController->addMediaElement(this);
+ }
if (hasMediaControls())
mediaControls()->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this));
MediaPlayerClient::CORSMode HTMLMediaElement::mediaPlayerCORSMode() const
{
- if (!fastHasAttribute(crossoriginAttr))
+ const AtomicString& crossOriginMode = fastGetAttribute(crossoriginAttr);
+ if (crossOriginMode.isNull())
return Unspecified;
- if (equalIgnoringCase(fastGetAttribute(crossoriginAttr), "use-credentials"))
+ if (equalIgnoringCase(crossOriginMode, "use-credentials"))
return UseCredentials;
return Anonymous;
}