summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/HTMLMediaElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/html/HTMLMediaElement.cpp')
-rw-r--r--Source/WebCore/html/HTMLMediaElement.cpp517
1 files changed, 415 insertions, 102 deletions
diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp
index 8033dc884..1e27cd4a2 100644
--- a/Source/WebCore/html/HTMLMediaElement.cpp
+++ b/Source/WebCore/html/HTMLMediaElement.cpp
@@ -40,6 +40,7 @@
#include "CSSPropertyNames.h"
#include "CSSValueKeywords.h"
#include "DocumentLoader.h"
+#include "ElementShadow.h"
#include "Event.h"
#include "EventNames.h"
#include "ExceptionCode.h"
@@ -58,6 +59,8 @@
#include "MediaDocument.h"
#include "MediaError.h"
#include "MediaFragmentURIParser.h"
+#include "MediaKeyError.h"
+#include "MediaKeyEvent.h"
#include "MediaList.h"
#include "MediaPlayer.h"
#include "MediaQueryEvaluator.h"
@@ -73,7 +76,6 @@
#include "SecurityPolicy.h"
#include "Settings.h"
#include "ShadowRoot.h"
-#include "ShadowTree.h"
#include "TimeRanges.h"
#include "UUID.h"
#include <limits>
@@ -175,6 +177,23 @@ static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* do
map.add(document, set);
}
+#if ENABLE(ENCRYPTED_MEDIA)
+static ExceptionCode exceptionCodeForMediaKeyException(MediaPlayer::MediaKeyException exception)
+{
+ switch (exception) {
+ case MediaPlayer::NoError:
+ return 0;
+ case MediaPlayer::InvalidPlayerState:
+ return INVALID_STATE_ERR;
+ case MediaPlayer::KeySystemNotSupported:
+ return NOT_SUPPORTED_ERR;
+ }
+
+ ASSERT_NOT_REACHED();
+ return INVALID_STATE_ERR;
+}
+#endif
+
HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document, bool createdByParser)
: HTMLElement(tagName, document)
, ActiveDOMObject(document, this)
@@ -182,6 +201,7 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
, m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
, m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
, m_playedTimeRanges()
+ , m_asyncEventQueue(GenericEventQueue::create(this))
, m_playbackRate(1.0f)
, m_defaultPlaybackRate(1.0f)
, m_webkitPreservesPitch(true)
@@ -195,8 +215,6 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
, m_lastTimeUpdateEventWallTime(0)
, m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max())
, m_loadState(WaitingForSource)
- , m_currentSourceNode(0)
- , m_nextChildNodeToConsider(0)
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
, m_proxyWidget(0)
#endif
@@ -251,8 +269,10 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
document->registerForMediaVolumeCallbacks(this);
document->registerForPrivateBrowsingStateChangedCallbacks(this);
- if (document->settings() && document->settings()->mediaPlaybackRequiresUserGesture())
+ if (document->settings() && document->settings()->mediaPlaybackRequiresUserGesture()) {
addBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
+ addBehaviorRestriction(RequireUserGestureForLoadRestriction);
+ }
#if ENABLE(MEDIA_SOURCE)
m_mediaSourceURL.setProtocol(mediaSourceURLProtocol);
@@ -468,25 +488,31 @@ RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
bool HTMLMediaElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const
{
- return childContext.isOnEncapsulationBoundary() && HTMLElement::childShouldCreateRenderer(childContext);
+ return childContext.isOnUpperEncapsulationBoundary() && HTMLElement::childShouldCreateRenderer(childContext);
}
-void HTMLMediaElement::insertedIntoDocument()
+Node::InsertionNotificationRequest HTMLMediaElement::insertedInto(Node* insertionPoint)
{
- LOG(Media, "HTMLMediaElement::insertedIntoDocument");
- HTMLElement::insertedIntoDocument();
- if (!getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY)
+ LOG(Media, "HTMLMediaElement::insertedInto");
+ HTMLElement::insertedInto(insertionPoint);
+ if (insertionPoint->inDocument() && !getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY)
scheduleLoad(MediaResource);
+ configureMediaControls();
+ return InsertionDone;
}
-void HTMLMediaElement::removedFromDocument()
+void HTMLMediaElement::removedFrom(Node* insertionPoint)
{
- LOG(Media, "HTMLMediaElement::removedFromDocument");
- if (m_networkState > NETWORK_EMPTY)
- pause();
- if (m_isFullscreen)
- exitFullscreen();
- HTMLElement::removedFromDocument();
+ if (insertionPoint->inDocument()) {
+ LOG(Media, "HTMLMediaElement::removedFromDocument");
+ configureMediaControls();
+ if (m_networkState > NETWORK_EMPTY)
+ pause();
+ if (m_isFullscreen)
+ exitFullscreen();
+ }
+
+ HTMLElement::removedFrom(insertionPoint);
}
void HTMLMediaElement::attach()
@@ -529,7 +555,7 @@ void HTMLMediaElement::scheduleLoad(LoadType loadType)
}
#if ENABLE(VIDEO_TRACK)
- if (loadType & TextTrackResource)
+ if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (loadType & TextTrackResource))
m_pendingLoadFlags |= TextTrackResource;
#endif
@@ -552,13 +578,15 @@ void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
RefPtr<Event> event = Event::create(eventName, false, true);
event->setTarget(this);
- m_asyncEventQueue.enqueueEvent(event.release());
+ m_asyncEventQueue->enqueueEvent(event.release());
}
void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
{
+ RefPtr<HTMLMediaElement> protect(this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
+
#if ENABLE(VIDEO_TRACK)
- if (m_pendingLoadFlags & TextTrackResource)
+ if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingLoadFlags & TextTrackResource))
configureNewTextTracks();
#endif
@@ -587,9 +615,9 @@ HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
return m_networkState;
}
-String HTMLMediaElement::canPlayType(const String& mimeType) const
+String HTMLMediaElement::canPlayType(const String& mimeType, const String& keySystem) const
{
- MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType));
+ MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType), keySystem);
String canPlay;
// 4.8.10.3
@@ -606,24 +634,27 @@ String HTMLMediaElement::canPlayType(const String& mimeType) const
break;
}
- LOG(Media, "HTMLMediaElement::canPlayType(%s) -> %s", mimeType.utf8().data(), canPlay.utf8().data());
+ LOG(Media, "HTMLMediaElement::canPlayType(%s, %s) -> %s", mimeType.utf8().data(), keySystem.utf8().data(), canPlay.utf8().data());
return canPlay;
}
void HTMLMediaElement::load(ExceptionCode& ec)
{
+ RefPtr<HTMLMediaElement> protect(this); // loadInternal may result in a 'beforeload' event, which can make arbitrary DOM mutations.
+
LOG(Media, "HTMLMediaElement::load()");
-
- if (userGestureRequiredForLoad() && !ScriptController::processingUserGesture())
+
+ if (userGestureRequiredForLoad() && !ScriptController::processingUserGesture()) {
ec = INVALID_STATE_ERR;
- else {
- m_loadInitiatedByUserGesture = ScriptController::processingUserGesture();
- if (m_loadInitiatedByUserGesture)
- removeBehaviorsRestrictionsAfterFirstUserGesture();
- prepareForLoad();
- loadInternal();
+ return;
}
+
+ m_loadInitiatedByUserGesture = ScriptController::processingUserGesture();
+ if (m_loadInitiatedByUserGesture)
+ removeBehaviorsRestrictionsAfterFirstUserGesture();
+ prepareForLoad();
+ loadInternal();
prepareToPlay();
}
@@ -680,7 +711,8 @@ void HTMLMediaElement::prepareForLoad()
scheduleEvent(eventNames().emptiedEvent);
updateMediaController();
#if ENABLE(VIDEO_TRACK)
- updateActiveTextTrackCues(0);
+ if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
+ updateActiveTextTrackCues(0);
#endif
}
@@ -715,6 +747,11 @@ void HTMLMediaElement::prepareForLoad()
void HTMLMediaElement::loadInternal()
{
+ // Some of the code paths below this function dispatch the BeforeLoad event. This ASSERT helps
+ // us catch those bugs more quickly without needing all the branches to align to actually
+ // trigger the event.
+ ASSERT(!eventDispatchForbidden());
+
// If we can't start a load right away, start it later.
Page* page = document()->page();
if (pageConsentRequiredForLoad() && page && !page->canStartMedia()) {
@@ -733,12 +770,14 @@ void HTMLMediaElement::loadInternal()
#if ENABLE(VIDEO_TRACK)
// HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
// disabled state when the element's resource selection algorithm last started".
- m_textTracksWhenResourceSelectionBegan.clear();
- if (m_textTracks) {
- for (unsigned i = 0; i < m_textTracks->length(); ++i) {
- TextTrack* track = m_textTracks->item(i);
- if (track->mode() != TextTrack::DISABLED)
- m_textTracksWhenResourceSelectionBegan.append(track);
+ if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) {
+ m_textTracksWhenResourceSelectionBegan.clear();
+ if (m_textTracks) {
+ for (unsigned i = 0; i < m_textTracks->length(); ++i) {
+ TextTrack* track = m_textTracks->item(i);
+ if (track->mode() != TextTrack::DISABLED)
+ m_textTracksWhenResourceSelectionBegan.append(track);
+ }
}
}
#endif
@@ -766,7 +805,7 @@ void HTMLMediaElement::selectMediaResource()
// source element child in tree order.
if (node) {
mode = children;
- m_nextChildNodeToConsider = 0;
+ m_nextChildNodeToConsider = node;
m_currentSourceNode = 0;
} else {
// Otherwise the media element has neither a src attribute nor a source element
@@ -806,10 +845,11 @@ void HTMLMediaElement::selectMediaResource()
return;
}
- // No type information is available when the url comes from the 'src' attribute so MediaPlayer
+ // No type or key system information is available when the url comes
+ // from the 'src' attribute so MediaPlayer
// will have to pick a media engine based on the file extension.
- ContentType contentType("");
- loadResource(mediaURL, contentType);
+ ContentType contentType((String()));
+ loadResource(mediaURL, contentType, String());
LOG(Media, "HTMLMediaElement::selectMediaResource, using 'src' attribute url");
return;
}
@@ -820,8 +860,9 @@ void HTMLMediaElement::selectMediaResource()
void HTMLMediaElement::loadNextSourceChild()
{
- ContentType contentType("");
- KURL mediaURL = selectNextSourceChild(&contentType, Complain);
+ ContentType contentType((String()));
+ String keySystem;
+ KURL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain);
if (!mediaURL.isValid()) {
waitForSourceChange();
return;
@@ -833,7 +874,7 @@ void HTMLMediaElement::loadNextSourceChild()
#endif
m_loadState = LoadingFromSourceElement;
- loadResource(mediaURL, contentType);
+ loadResource(mediaURL, contentType, keySystem);
}
#if !PLATFORM(CHROMIUM)
@@ -858,11 +899,11 @@ static KURL createFileURLForApplicationCacheResource(const String& path)
}
#endif
-void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType)
+void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType, const String& keySystem)
{
ASSERT(isSafeToLoadURL(initialURL, Complain));
- LOG(Media, "HTMLMediaElement::loadResource(%s, %s)", urlForLogging(initialURL).utf8().data(), contentType.raw().utf8().data());
+ LOG(Media, "HTMLMediaElement::loadResource(%s, %s, %s)", urlForLogging(initialURL).utf8().data(), contentType.raw().utf8().data(), keySystem.utf8().data());
Frame* frame = document()->frame();
if (!frame) {
@@ -933,7 +974,7 @@ void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& content
m_muted = true;
updateVolume();
- if (!m_player->load(url, contentType))
+ if (!m_player->load(url, contentType, keySystem))
mediaLoadingFailed(MediaPlayer::FormatError);
// If there is no poster to display, allow the media engine to render video frames as soon as
@@ -1142,7 +1183,7 @@ void HTMLMediaElement::updateActiveTextTrackCues(float movieTime)
event = Event::create(eventNames().exitEvent, false, false);
event->setTarget(eventTasks[i].second);
- m_asyncEventQueue.enqueueEvent(event.release());
+ m_asyncEventQueue->enqueueEvent(event.release());
}
// 14 - Sort affected tracks in the same order as the text tracks appear in
@@ -1150,18 +1191,23 @@ void HTMLMediaElement::updateActiveTextTrackCues(float movieTime)
nonCopyingSort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare);
// 15 - For each text track in affected tracks, in the list order, queue a
- // task to fire a simple event named cuechange at the TextTrack object, and,
- // if the text track has a corresponding track element, to then fire a
- // simple event named cuechange at the track element as well.
+ // task to fire a simple event named cuechange at the TextTrack object, and, ...
for (size_t i = 0; i < affectedTracks.size(); ++i) {
RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
event->setTarget(affectedTracks[i]);
- m_asyncEventQueue.enqueueEvent(event.release());
+ m_asyncEventQueue->enqueueEvent(event.release());
- // Fire syncronous cue change event for track elements.
- if (affectedTracks[i]->trackType() == TextTrack::TrackElement)
- affectedTracks[i]->fireCueChangeEvent();
+ // ... if the text track has a corresponding track element, to then fire a
+ // simple event named cuechange at the track element as well.
+ if (affectedTracks[i]->trackType() == TextTrack::TrackElement) {
+ RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
+ HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(affectedTracks[i])->trackElement();
+ ASSERT(trackElement);
+ event->setTarget(trackElement);
+
+ m_asyncEventQueue->enqueueEvent(event.release());
+ }
}
// 16 - Set the text track cue active flag of all the cues in the current
@@ -1222,8 +1268,12 @@ void HTMLMediaElement::textTrackModeChanged(TextTrack* track)
// Mark this track as "configured" so configureNewTextTracks won't change the mode again.
trackElement->setHasBeenConfigured(true);
- if (track->mode() != TextTrack::DISABLED && trackElement->readyState() == HTMLTrackElement::NONE)
- trackElement->scheduleLoad();
+ if (track->mode() != TextTrack::DISABLED) {
+ if (trackElement->readyState() == HTMLTrackElement::LOADED)
+ textTrackAddCues(track, track->cues());
+ else if (trackElement->readyState() == HTMLTrackElement::NONE)
+ trackElement->scheduleLoad();
+ }
break;
}
}
@@ -1345,6 +1395,11 @@ void HTMLMediaElement::noneSupported()
// 7 - Queue a task to fire a simple event named error at the media element.
scheduleEvent(eventNames().errorEvent);
+#if ENABLE(MEDIA_SOURCE)
+ if (m_sourceState != SOURCE_CLOSED)
+ setSourceState(SOURCE_CLOSED);
+#endif
+
// 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
setShouldDelayLoadEvent(false);
@@ -1392,7 +1447,7 @@ void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
void HTMLMediaElement::cancelPendingEventsAndCallbacks()
{
LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks");
- m_asyncEventQueue.cancelAllEvents();
+ m_asyncEventQueue->cancelAllEvents();
for (Node* node = firstChild(); node; node = node->nextSibling()) {
if (node->hasTagName(sourceTag))
@@ -1528,7 +1583,7 @@ void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
ReadyState newState = static_cast<ReadyState>(state);
#if ENABLE(VIDEO_TRACK)
- bool tracksAreReady = textTracksAreReady();
+ bool tracksAreReady = !RuntimeEnabledFeatures::webkitVideoTrackEnabled() || textTracksAreReady();
if (newState == oldState && m_tracksAreReady == tracksAreReady)
return;
@@ -1629,7 +1684,8 @@ void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
updatePlayState();
updateMediaController();
#if ENABLE(VIDEO_TRACK)
- updateActiveTextTrackCues(currentTime());
+ if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
+ updateActiveTextTrackCues(currentTime());
#endif
}
@@ -1647,6 +1703,102 @@ String HTMLMediaElement::mediaPlayerSourceURL() const
{
return m_mediaSourceURL.string();
}
+
+bool HTMLMediaElement::isValidSourceId(const String& id, ExceptionCode& ec) const
+{
+ if (id.isNull() || id.isEmpty()) {
+ ec = INVALID_ACCESS_ERR;
+ return false;
+ }
+
+ if (!m_sourceIDs.contains(id)) {
+ ec = SYNTAX_ERR;
+ return false;
+ }
+
+ return true;
+}
+
+#endif
+
+#if ENABLE(ENCRYPTED_MEDIA)
+void HTMLMediaElement::mediaPlayerKeyAdded(MediaPlayer*, const String& keySystem, const String& sessionId)
+{
+ MediaKeyEventInit initializer;
+ initializer.keySystem = keySystem;
+ initializer.sessionId = sessionId;
+ initializer.bubbles = false;
+ initializer.cancelable = false;
+
+ RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeyaddedEvent, initializer);
+ event->setTarget(this);
+ m_asyncEventQueue->enqueueEvent(event.release());
+}
+
+void HTMLMediaElement::mediaPlayerKeyError(MediaPlayer*, const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode)
+{
+ MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
+ switch (errorCode) {
+ case MediaPlayerClient::UnknownError:
+ mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
+ break;
+ case MediaPlayerClient::ClientError:
+ mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
+ break;
+ case MediaPlayerClient::ServiceError:
+ mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE;
+ break;
+ case MediaPlayerClient::OutputError:
+ mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT;
+ break;
+ case MediaPlayerClient::HardwareChangeError:
+ mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE;
+ break;
+ case MediaPlayerClient::DomainError:
+ mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN;
+ break;
+ }
+
+ MediaKeyEventInit initializer;
+ initializer.keySystem = keySystem;
+ initializer.sessionId = sessionId;
+ initializer.errorCode = MediaKeyError::create(mediaKeyErrorCode);
+ initializer.systemCode = systemCode;
+ initializer.bubbles = false;
+ initializer.cancelable = false;
+
+ RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeyerrorEvent, initializer);
+ event->setTarget(this);
+ m_asyncEventQueue->enqueueEvent(event.release());
+}
+
+void HTMLMediaElement::mediaPlayerKeyMessage(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength)
+{
+ MediaKeyEventInit initializer;
+ initializer.keySystem = keySystem;
+ initializer.sessionId = sessionId;
+ initializer.message = Uint8Array::create(message, messageLength);
+ initializer.bubbles = false;
+ initializer.cancelable = false;
+
+ RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeymessageEvent, initializer);
+ event->setTarget(this);
+ m_asyncEventQueue->enqueueEvent(event.release());
+}
+
+void HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength)
+{
+ MediaKeyEventInit initializer;
+ initializer.keySystem = keySystem;
+ initializer.sessionId = sessionId;
+ initializer.initData = Uint8Array::create(initData, initDataLength);
+ initializer.bubbles = false;
+ initializer.cancelable = false;
+
+ RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitneedkeyEvent, initializer);
+ event->setTarget(this);
+ m_asyncEventQueue->enqueueEvent(event.release());
+}
#endif
void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
@@ -2154,6 +2306,59 @@ void HTMLMediaElement::pauseInternal()
}
#if ENABLE(MEDIA_SOURCE)
+void HTMLMediaElement::webkitSourceAddId(const String& id, const String& type, ExceptionCode& ec)
+{
+ if (id.isNull() || id.isEmpty()) {
+ ec = INVALID_ACCESS_ERR;
+ return;
+ }
+
+ if (m_sourceIDs.contains(id)) {
+ ec = INVALID_STATE_ERR;
+ return;
+ }
+
+ if (type.isNull() || type.isEmpty()) {
+ ec = INVALID_ACCESS_ERR;
+ return;
+ }
+
+ if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
+ ec = INVALID_STATE_ERR;
+ return;
+ }
+
+ switch (m_player->sourceAddId(id, type)) {
+ case MediaPlayer::Ok:
+ m_sourceIDs.add(id);
+ return;
+ case MediaPlayer::NotSupported:
+ ec = NOT_SUPPORTED_ERR;
+ return;
+ case MediaPlayer::ReachedIdLimit:
+ ec = QUOTA_EXCEEDED_ERR;
+ return;
+ }
+
+ ASSERT_NOT_REACHED();
+}
+
+void HTMLMediaElement::webkitSourceRemoveId(const String& id, ExceptionCode& ec)
+{
+ if (!isValidSourceId(id, ec))
+ return;
+
+ if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState == SOURCE_CLOSED) {
+ ec = INVALID_STATE_ERR;
+ return;
+ }
+
+ if (!m_player->sourceRemoveId(id))
+ ASSERT_NOT_REACHED();
+
+ m_sourceIDs.remove(id);
+}
+
void HTMLMediaElement::webkitSourceAppend(PassRefPtr<Uint8Array> data, ExceptionCode& ec)
{
if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) {
@@ -2209,6 +2414,7 @@ void HTMLMediaElement::setSourceState(SourceState state)
return;
if (m_sourceState == SOURCE_CLOSED) {
+ m_sourceIDs.clear();
scheduleEvent(eventNames().webkitsourcecloseEvent);
return;
}
@@ -2225,6 +2431,86 @@ void HTMLMediaElement::setSourceState(SourceState state)
}
#endif
+#if ENABLE(ENCRYPTED_MEDIA)
+void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionCode& ec)
+{
+ if (keySystem.isEmpty()) {
+ ec = SYNTAX_ERR;
+ return;
+ }
+
+ if (!m_player) {
+ ec = INVALID_STATE_ERR;
+ return;
+ }
+
+ const unsigned char* initDataPointer = 0;
+ unsigned initDataLength = 0;
+ if (initData) {
+ initDataPointer = initData->data();
+ initDataLength = initData->length();
+ }
+
+ MediaPlayer::MediaKeyException result = m_player->generateKeyRequest(keySystem, initDataPointer, initDataLength);
+ ec = exceptionCodeForMediaKeyException(result);
+}
+
+void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, ExceptionCode& ec)
+{
+ webkitGenerateKeyRequest(keySystem, Uint8Array::create(0), ec);
+}
+
+void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionCode& ec)
+{
+ if (keySystem.isEmpty()) {
+ ec = SYNTAX_ERR;
+ return;
+ }
+
+ if (!key->length()) {
+ ec = TYPE_MISMATCH_ERR;
+ return;
+ }
+
+ if (!m_player) {
+ ec = INVALID_STATE_ERR;
+ return;
+ }
+
+ const unsigned char* initDataPointer = 0;
+ unsigned initDataLength = 0;
+ if (initData) {
+ initDataPointer = initData->data();
+ initDataLength = initData->length();
+ }
+
+ MediaPlayer::MediaKeyException result = m_player->addKey(keySystem, key->data(), key->length(), initDataPointer, initDataLength, sessionId);
+ ec = exceptionCodeForMediaKeyException(result);
+}
+
+void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, ExceptionCode& ec)
+{
+ webkitAddKey(keySystem, key, Uint8Array::create(0), String(), ec);
+}
+
+void HTMLMediaElement::webkitCancelKeyRequest(const String& keySystem, const String& sessionId, ExceptionCode& ec)
+{
+ if (keySystem.isEmpty()) {
+ ec = SYNTAX_ERR;
+ return;
+ }
+
+ if (!m_player) {
+ ec = INVALID_STATE_ERR;
+ return;
+ }
+
+ MediaPlayer::MediaKeyException result = m_player->cancelKeyRequest(keySystem, sessionId);
+ ec = exceptionCodeForMediaKeyException(result);
+}
+
+#endif
+
bool HTMLMediaElement::loop() const
{
return fastHasAttribute(loopAttr);
@@ -2385,7 +2671,8 @@ void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
mediaControls()->playbackProgressed();
#if ENABLE(VIDEO_TRACK)
- updateActiveTextTrackCues(currentTime());
+ if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
+ updateActiveTextTrackCues(currentTime());
#endif
}
@@ -2490,7 +2777,7 @@ HTMLTrackElement* HTMLMediaElement::showingTrackWithSameKind(HTMLTrackElement* t
return 0;
}
-void HTMLMediaElement::trackWasAdded(HTMLTrackElement* trackElement)
+void HTMLMediaElement::didAddTrack(HTMLTrackElement* trackElement)
{
ASSERT(trackElement->hasTagName(trackTag));
@@ -2513,7 +2800,7 @@ void HTMLMediaElement::trackWasAdded(HTMLTrackElement* trackElement)
scheduleLoad(TextTrackResource);
}
-void HTMLMediaElement::trackWasRemoved(HTMLTrackElement* trackElement)
+void HTMLMediaElement::willRemoveTrack(HTMLTrackElement* trackElement)
{
ASSERT(trackElement->hasTagName(trackTag));
@@ -2523,12 +2810,15 @@ void HTMLMediaElement::trackWasRemoved(HTMLTrackElement* trackElement)
#if !LOG_DISABLED
if (trackElement->hasTagName(trackTag)) {
KURL url = trackElement->getNonEmptyURLAttribute(srcAttr);
- LOG(Media, "HTMLMediaElement::trackWasRemoved - 'src' is %s", urlForLogging(url).utf8().data());
+ LOG(Media, "HTMLMediaElement::willRemoveTrack - 'src' is %s", urlForLogging(url).utf8().data());
}
#endif
trackElement->setHasBeenConfigured(false);
+ if (!m_textTracks)
+ return;
+
RefPtr<TextTrack> textTrack = trackElement->track();
if (!textTrack)
return;
@@ -2725,10 +3015,10 @@ bool HTMLMediaElement::havePotentialSourceChild()
{
// Stash the current <source> node and next nodes so we can restore them after checking
// to see there is another potential.
- HTMLSourceElement* currentSourceNode = m_currentSourceNode;
- Node* nextNode = m_nextChildNodeToConsider;
+ RefPtr<HTMLSourceElement> currentSourceNode = m_currentSourceNode;
+ RefPtr<Node> nextNode = m_nextChildNodeToConsider;
- KURL nextURL = selectNextSourceChild(0, DoNothing);
+ KURL nextURL = selectNextSourceChild(0, 0, DoNothing);
m_currentSourceNode = currentSourceNode;
m_nextChildNodeToConsider = nextNode;
@@ -2736,7 +3026,7 @@ bool HTMLMediaElement::havePotentialSourceChild()
return nextURL.isValid();
}
-KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidURLAction actionIfInvalid)
+KURL HTMLMediaElement::selectNextSourceChild(ContentType* contentType, String* keySystem, InvalidURLAction actionIfInvalid)
{
#if !LOG_DISABLED
// Don't log if this was just called to find out if there are any valid <source> elements.
@@ -2745,7 +3035,7 @@ KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidUR
LOG(Media, "HTMLMediaElement::selectNextSourceChild");
#endif
- if (m_nextChildNodeToConsider == sourceChildEndOfListValue()) {
+ if (!m_nextChildNodeToConsider) {
#if !LOG_DISABLED
if (shouldLog)
LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\"");
@@ -2756,17 +3046,25 @@ KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidUR
KURL mediaURL;
Node* node;
HTMLSourceElement* source = 0;
- bool lookingForStartNode = m_nextChildNodeToConsider;
- bool canUse = false;
String type;
+ String system;
+ bool lookingForStartNode = m_nextChildNodeToConsider;
+ bool canUseSourceElement = false;
+ bool okToLoadSourceURL;
+
+ NodeVector potentialSourceNodes;
+ getChildNodes(this, potentialSourceNodes);
- for (node = firstChild(); !canUse && node; node = node->nextSibling()) {
+ for (unsigned i = 0; !canUseSourceElement && i < potentialSourceNodes.size(); ++i) {
+ node = potentialSourceNodes[i].get();
if (lookingForStartNode && m_nextChildNodeToConsider != node)
continue;
lookingForStartNode = false;
-
+
if (!node->hasTagName(sourceTag))
continue;
+ if (node->parentNode() != this)
+ continue;
source = static_cast<HTMLSourceElement*>(node);
@@ -2781,7 +3079,7 @@ KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidUR
if (source->fastHasAttribute(mediaAttr)) {
MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
- RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media());
+ RefPtr<MediaQuerySet> media = MediaQuerySet::createAllowingDescriptionSyntax(source->media());
#if !LOG_DISABLED
if (shouldLog)
LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data());
@@ -2791,46 +3089,56 @@ KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidUR
}
type = source->type();
+ // FIXME(82965): Add support for keySystem in <source> and set system from source.
if (type.isEmpty() && mediaURL.protocolIsData())
type = mimeTypeFromDataURL(mediaURL);
- if (!type.isEmpty()) {
+ if (!type.isEmpty() || !system.isEmpty()) {
#if !LOG_DISABLED
if (shouldLog)
- LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is %s", type.utf8().data());
+ LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is '%s' - key system is '%s'", type.utf8().data(), system.utf8().data());
#endif
- if (!MediaPlayer::supportsType(ContentType(type)))
+ if (!MediaPlayer::supportsType(ContentType(type), system))
goto check_again;
}
// Is it safe to load this url?
- if (!isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string()))
+ okToLoadSourceURL = isSafeToLoadURL(mediaURL, actionIfInvalid) && dispatchBeforeLoadEvent(mediaURL.string());
+
+ // A 'beforeload' event handler can mutate the DOM, so check to see if the source element is still a child node.
+ if (node->parentNode() != this) {
+ LOG(Media, "HTMLMediaElement::selectNextSourceChild : 'beforeload' removed current element");
+ source = 0;
+ goto check_again;
+ }
+
+ if (!okToLoadSourceURL)
goto check_again;
// Making it this far means the <source> looks reasonable.
- canUse = true;
+ canUseSourceElement = true;
check_again:
- if (!canUse && actionIfInvalid == Complain)
+ if (!canUseSourceElement && actionIfInvalid == Complain && source)
source->scheduleErrorEvent();
}
- if (canUse) {
+ if (canUseSourceElement) {
if (contentType)
*contentType = ContentType(type);
+ if (keySystem)
+ *keySystem = system;
m_currentSourceNode = source;
m_nextChildNodeToConsider = source->nextSibling();
- if (!m_nextChildNodeToConsider)
- m_nextChildNodeToConsider = sourceChildEndOfListValue();
} else {
m_currentSourceNode = 0;
- m_nextChildNodeToConsider = sourceChildEndOfListValue();
+ m_nextChildNodeToConsider = 0;
}
#if !LOG_DISABLED
if (shouldLog)
- LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode, canUse ? urlForLogging(mediaURL).utf8().data() : "");
+ LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode.get(), canUseSourceElement ? urlForLogging(mediaURL).utf8().data() : "");
#endif
- return canUse ? mediaURL : KURL();
+ return canUseSourceElement ? mediaURL : KURL();
}
void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
@@ -2853,6 +3161,7 @@ void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
// the media element's resource selection algorithm.
if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
scheduleLoad(MediaResource);
+ m_nextChildNodeToConsider = source;
return;
}
@@ -2862,20 +3171,20 @@ void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
return;
}
- if (m_nextChildNodeToConsider != sourceChildEndOfListValue())
+ if (m_nextChildNodeToConsider)
return;
// 4.8.9.5, resource selection algorithm, source elements section:
- // 20 - Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
- // 21 - Asynchronously await a stable state...
- // 22 - Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case
+ // 21. Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
+ // 22. Asynchronously await a stable state...
+ // 23. Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case
// it hasn't been fired yet).
setShouldDelayLoadEvent(true);
- // 23 - Set the networkState back to NETWORK_LOADING.
+ // 24. Set the networkState back to NETWORK_LOADING.
m_networkState = NETWORK_LOADING;
- // 24 - Jump back to the find next candidate step above.
+ // 25. Jump back to the find next candidate step above.
m_nextChildNodeToConsider = source;
scheduleNextSourceChild();
}
@@ -2897,8 +3206,8 @@ void HTMLMediaElement::sourceWillBeRemoved(HTMLSourceElement* source)
if (source == m_nextChildNodeToConsider) {
m_nextChildNodeToConsider = m_nextChildNodeToConsider->nextSibling();
if (!m_nextChildNodeToConsider)
- m_nextChildNodeToConsider = sourceChildEndOfListValue();
- LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider);
+ m_nextChildNodeToConsider = 0;
+ LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider.get());
} else if (source == m_currentSourceNode) {
// Clear the current source node pointer, but don't change the movie as the spec says:
// 4.8.8 - Dynamically modifying a source element and its attribute when the element is already
@@ -2913,7 +3222,8 @@ void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
#if ENABLE(VIDEO_TRACK)
- updateActiveTextTrackCues(currentTime());
+ if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
+ updateActiveTextTrackCues(currentTime());
#endif
beginProcessingMediaPlayerCallback();
@@ -3377,7 +3687,8 @@ void HTMLMediaElement::userCancelledLoad()
m_readyState = HAVE_NOTHING;
updateMediaController();
#if ENABLE(VIDEO_TRACK)
- updateActiveTextTrackCues(0);
+ if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
+ updateActiveTextTrackCues(0);
#endif
}
@@ -3416,7 +3727,7 @@ void HTMLMediaElement::suspend(ReasonForSuspension why)
break;
case PageWillBeSuspended:
case JavaScriptDebuggerPaused:
- case WillShowDialog:
+ case WillDeferLoading:
// Do nothing, we don't pause media playback in these cases.
break;
}
@@ -3444,7 +3755,7 @@ void HTMLMediaElement::resume()
bool HTMLMediaElement::hasPendingActivity() const
{
- return m_asyncEventQueue.hasPendingEvents();
+ return m_asyncEventQueue->hasPendingEvents();
}
void HTMLMediaElement::mediaVolumeDidChange()
@@ -3495,6 +3806,8 @@ void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy)
void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values)
{
+ RefPtr<HTMLMediaElement> protect(this); // selectNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
+
Frame* frame = document()->frame();
if (isVideo()) {
@@ -3512,7 +3825,7 @@ void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Ve
url = src();
if (!isSafeToLoadURL(url, Complain))
- url = selectNextSourceChild(0, DoNothing);
+ url = selectNextSourceChild(0, 0, DoNothing);
m_currentSrc = url;
if (url.isValid() && frame && frame->loader()->willLoadMediaElementURL(url)) {
@@ -3756,7 +4069,7 @@ void HTMLMediaElement::privateBrowsingStateDidChange()
MediaControls* HTMLMediaElement::mediaControls()
{
- return toMediaControls(shadowTree()->oldestShadowRoot()->firstChild());
+ return toMediaControls(shadow()->oldestShadowRoot()->firstChild());
}
bool HTMLMediaElement::hasMediaControls()
@@ -3764,7 +4077,7 @@ bool HTMLMediaElement::hasMediaControls()
if (!hasShadowRoot())
return false;
- Node* node = shadowTree()->oldestShadowRoot()->firstChild();
+ Node* node = shadow()->oldestShadowRoot()->firstChild();
return node && node->isMediaControls();
}
@@ -3790,7 +4103,7 @@ bool HTMLMediaElement::createMediaControls()
void HTMLMediaElement::configureMediaControls()
{
#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- if (!controls()) {
+ if (!controls() || !inDocument()) {
if (hasMediaControls())
mediaControls()->hide();
return;