Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / HTMLMediaElement.cpp
index 1cb775d..c1c66cc 100644 (file)
@@ -34,7 +34,6 @@
 #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"
@@ -65,7 +64,6 @@
 // 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"
@@ -242,7 +240,20 @@ WebMimeRegistry::SupportsType HTMLMediaElement::supportsType(const ContentType&
     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)
@@ -264,7 +275,7 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
     , 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())
@@ -285,10 +296,8 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
     , 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)
@@ -306,13 +315,10 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
     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
@@ -454,7 +460,6 @@ void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicStr
 void HTMLMediaElement::finishParsingChildren()
 {
     HTMLElement::finishParsingChildren();
-    m_parsingInProgress = false;
 
     if (!RuntimeEnabledFeatures::videoTrackEnabled())
         return;
@@ -620,12 +625,9 @@ void HTMLMediaElement::load()
     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();
@@ -713,11 +715,6 @@ void HTMLMediaElement::loadInternal()
     // 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()) {
@@ -847,11 +844,6 @@ void HTMLMediaElement::loadResource(const KURL& url, ContentType& contentType, c
 
     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.
@@ -869,16 +861,21 @@ void HTMLMediaElement::loadResource(const KURL& url, ContentType& contentType, c
     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;
+                }
             }
         }
     }
@@ -1507,9 +1504,6 @@ void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
             changeNetworkStateFromLoadingToIdle();
         m_completelyLoaded = true;
     }
-
-    if (hasMediaControls())
-        mediaControls()->updateStatusDisplay();
 }
 
 void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
@@ -1621,7 +1615,7 @@ void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
         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);
@@ -1633,10 +1627,8 @@ void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
 
     if (shouldUpdateDisplayState) {
         updateDisplayState();
-        if (hasMediaControls()) {
+        if (hasMediaControls())
             mediaControls()->refreshClosedCaptionsButtonVisibility();
-            mediaControls()->updateStatusDisplay();
-        }
     }
 
     updatePlayState();
@@ -1716,31 +1708,45 @@ void HTMLMediaElement::mediaPlayerKeyMessage(const String& keySystem, const Stri
     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());
     }
@@ -1844,7 +1850,7 @@ void HTMLMediaElement::seek(double time, ExceptionState& exceptionState)
 
     // 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;
     }
 
@@ -2013,7 +2019,7 @@ double HTMLMediaElement::currentTime() const
 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);
@@ -2125,7 +2131,7 @@ void HTMLMediaElement::play()
 {
     WTF_LOG(Media, "HTMLMediaElement::play()");
 
-    if (userGestureRequiredForRateChange() && !UserGestureIndicator::processingUserGesture())
+    if (userGestureRequiredForPlay() && !UserGestureIndicator::processingUserGesture())
         return;
     if (UserGestureIndicator::processingUserGesture())
         removeBehaviorsRestrictionsAfterFirstUserGesture();
@@ -2167,18 +2173,6 @@ void HTMLMediaElement::pause()
 {
     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);
 
@@ -2210,12 +2204,12 @@ void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, PassRef
         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;
     }
 
@@ -2243,22 +2237,22 @@ void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Arr
         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;
     }
 
@@ -2286,12 +2280,12 @@ void HTMLMediaElement::webkitCancelKeyRequest(const String& keySystem, const Str
         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;
     }
 
@@ -2341,7 +2335,7 @@ void HTMLMediaElement::setVolume(double vol, ExceptionState& exceptionState)
     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;
     }
 
@@ -2372,19 +2366,6 @@ void HTMLMediaElement::setMuted(bool muted)
     }
 }
 
-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()));
@@ -2433,7 +2414,7 @@ void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
         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();
         }
     }
 
@@ -2474,25 +2455,6 @@ bool HTMLMediaElement::canPlay() const
     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())
@@ -2592,7 +2554,7 @@ PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const AtomicString& kind, c
 
     // 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;
     }
 
@@ -2648,7 +2610,7 @@ void HTMLMediaElement::didAddTrack(HTMLTrackElement* trackElement)
 
     // 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())
@@ -2911,7 +2873,6 @@ KURL HTMLMediaElement::selectNextSourceChild(ContentType* contentType, String* k
         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
@@ -2923,19 +2884,6 @@ KURL HTMLMediaElement::selectNextSourceChild(ContentType* contentType, String* k
         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())
@@ -3152,7 +3100,7 @@ void HTMLMediaElement::mediaPlayerPlaybackStateChanged()
         return;
 
     if (m_player->paused())
-        pauseInternal();
+        pause();
     else
         playInternal();
 }
@@ -3849,8 +3797,10 @@ void HTMLMediaElement::setControllerInternal(PassRefPtr<MediaController> control
 
     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));
@@ -3928,9 +3878,10 @@ void HTMLMediaElement::applyMediaFragmentURI()
 
 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;
 }