diff options
Diffstat (limited to 'Source/WebCore/html/HTMLMediaElement.cpp')
-rw-r--r-- | Source/WebCore/html/HTMLMediaElement.cpp | 517 |
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; |