Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "core/html/HTMLMediaElement.h"
     28 
     29 #include <limits>
     30 #include "HTMLNames.h"
     31 #include "RuntimeEnabledFeatures.h"
     32 #include "bindings/v8/ExceptionState.h"
     33 #include "bindings/v8/ExceptionStatePlaceholder.h"
     34 #include "bindings/v8/ScriptController.h"
     35 #include "bindings/v8/ScriptEventListener.h"
     36 #include "core/css/MediaList.h"
     37 #include "core/css/MediaQueryEvaluator.h"
     38 #include "core/dom/Attribute.h"
     39 #include "core/dom/ExceptionCode.h"
     40 #include "core/dom/FullscreenElementStack.h"
     41 #include "core/dom/shadow/ShadowRoot.h"
     42 #include "core/events/Event.h"
     43 #include "core/events/ThreadLocalEventNames.h"
     44 #include "core/frame/ContentSecurityPolicy.h"
     45 #include "core/frame/Frame.h"
     46 #include "core/frame/Settings.h"
     47 #include "core/frame/UseCounter.h"
     48 #include "core/html/HTMLMediaSource.h"
     49 #include "core/html/HTMLSourceElement.h"
     50 #include "core/html/HTMLTrackElement.h"
     51 #include "core/html/MediaController.h"
     52 #include "core/html/MediaError.h"
     53 #include "core/html/MediaFragmentURIParser.h"
     54 #include "core/html/MediaKeyError.h"
     55 #include "core/html/MediaKeyEvent.h"
     56 #include "core/html/TimeRanges.h"
     57 #include "core/html/shadow/MediaControls.h"
     58 #include "core/html/track/InbandTextTrack.h"
     59 #include "core/html/track/TextTrackCueList.h"
     60 #include "core/html/track/TextTrackList.h"
     61 #include "core/loader/FrameLoader.h"
     62 #include "core/rendering/RenderLayerCompositor.h"
     63 #include "core/rendering/RenderVideo.h"
     64 #include "core/rendering/RenderView.h"
     65 // FIXME: Remove dependency on modules/encryptedmedia (http://crbug.com/242754).
     66 #include "modules/encryptedmedia/MediaKeyNeededEvent.h"
     67 #include "modules/encryptedmedia/MediaKeys.h"
     68 #include "modules/mediastream/MediaStreamRegistry.h"
     69 #include "platform/ContentType.h"
     70 #include "platform/Language.h"
     71 #include "platform/Logging.h"
     72 #include "platform/MIMETypeFromURL.h"
     73 #include "platform/MIMETypeRegistry.h"
     74 #include "platform/NotImplemented.h"
     75 #include "platform/UserGestureIndicator.h"
     76 #include "platform/graphics/GraphicsLayer.h"
     77 #include "platform/weborigin/SecurityOrigin.h"
     78 #include "public/platform/Platform.h"
     79 #include "public/platform/WebInbandTextTrack.h"
     80 #include "wtf/CurrentTime.h"
     81 #include "wtf/MathExtras.h"
     82 #include "wtf/NonCopyingSort.h"
     83 #include "wtf/Uint8Array.h"
     84 #include "wtf/text/CString.h"
     85 
     86 #if ENABLE(WEB_AUDIO)
     87 #include "platform/audio/AudioSourceProvider.h"
     88 #include "modules/webaudio/MediaElementAudioSourceNode.h"
     89 #endif
     90 
     91 using namespace std;
     92 using blink::WebInbandTextTrack;
     93 using blink::WebMimeRegistry;
     94 
     95 namespace WebCore {
     96 
     97 #if !LOG_DISABLED
     98 static String urlForLoggingMedia(const KURL& url)
     99 {
    100     static const unsigned maximumURLLengthForLogging = 128;
    101 
    102     if (url.string().length() < maximumURLLengthForLogging)
    103         return url.string();
    104     return url.string().substring(0, maximumURLLengthForLogging) + "...";
    105 }
    106 
    107 static const char* boolString(bool val)
    108 {
    109     return val ? "true" : "false";
    110 }
    111 #endif
    112 
    113 #ifndef LOG_MEDIA_EVENTS
    114 // Default to not logging events because so many are generated they can overwhelm the rest of
    115 // the logging.
    116 #define LOG_MEDIA_EVENTS 0
    117 #endif
    118 
    119 #ifndef LOG_CACHED_TIME_WARNINGS
    120 // Default to not logging warnings about excessive drift in the cached media time because it adds a
    121 // fair amount of overhead and logging.
    122 #define LOG_CACHED_TIME_WARNINGS 0
    123 #endif
    124 
    125 // URL protocol used to signal that the media source API is being used.
    126 static const char mediaSourceBlobProtocol[] = "blob";
    127 
    128 using namespace HTMLNames;
    129 using namespace std;
    130 
    131 typedef HashMap<Document*, HashSet<HTMLMediaElement*> > DocumentElementSetMap;
    132 static DocumentElementSetMap& documentToElementSetMap()
    133 {
    134     DEFINE_STATIC_LOCAL(DocumentElementSetMap, map, ());
    135     return map;
    136 }
    137 
    138 static void addElementToDocumentMap(HTMLMediaElement* element, Document* document)
    139 {
    140     DocumentElementSetMap& map = documentToElementSetMap();
    141     HashSet<HTMLMediaElement*> set = map.take(document);
    142     set.add(element);
    143     map.add(document, set);
    144 }
    145 
    146 static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* document)
    147 {
    148     DocumentElementSetMap& map = documentToElementSetMap();
    149     HashSet<HTMLMediaElement*> set = map.take(document);
    150     set.remove(element);
    151     if (!set.isEmpty())
    152         map.add(document, set);
    153 }
    154 
    155 static void throwExceptionForMediaKeyException(MediaPlayer::MediaKeyException exception, ExceptionState& exceptionState)
    156 {
    157     switch (exception) {
    158     case MediaPlayer::NoError:
    159         return;
    160     case MediaPlayer::InvalidPlayerState:
    161         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
    162         return;
    163     case MediaPlayer::KeySystemNotSupported:
    164         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
    165         return;
    166     case MediaPlayer::InvalidAccess:
    167         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
    168         return;
    169     }
    170 
    171     ASSERT_NOT_REACHED();
    172     return;
    173 }
    174 
    175 class TrackDisplayUpdateScope {
    176 public:
    177     TrackDisplayUpdateScope(HTMLMediaElement* mediaElement)
    178     {
    179         m_mediaElement = mediaElement;
    180         m_mediaElement->beginIgnoringTrackDisplayUpdateRequests();
    181     }
    182     ~TrackDisplayUpdateScope()
    183     {
    184         ASSERT(m_mediaElement);
    185         m_mediaElement->endIgnoringTrackDisplayUpdateRequests();
    186     }
    187 
    188 private:
    189     HTMLMediaElement* m_mediaElement;
    190 };
    191 
    192 static bool canLoadURL(const KURL& url, const ContentType& contentType, const String& keySystem)
    193 {
    194     DEFINE_STATIC_LOCAL(const String, codecs, ("codecs"));
    195 
    196     String contentMIMEType = contentType.type().lower();
    197     String contentTypeCodecs = contentType.parameter(codecs);
    198 
    199     // If the MIME type is missing or is not meaningful, try to figure it out from the URL.
    200     if (contentMIMEType.isEmpty() || contentMIMEType == "application/octet-stream" || contentMIMEType == "text/plain") {
    201         if (url.protocolIsData())
    202             contentMIMEType = mimeTypeFromDataURL(url.string());
    203     }
    204 
    205     // If no MIME type is specified, always attempt to load.
    206     if (contentMIMEType.isEmpty())
    207         return true;
    208 
    209     // 4.8.10.3 MIME types - In the absence of a specification to the contrary, the MIME type "application/octet-stream"
    210     // when used with parameters, e.g. "application/octet-stream;codecs=theora", is a type that the user agent knows
    211     // it cannot render.
    212     if (contentMIMEType != "application/octet-stream" || contentTypeCodecs.isEmpty()) {
    213         WebMimeRegistry::SupportsType supported = blink::Platform::current()->mimeRegistry()->supportsMediaMIMEType(contentMIMEType, contentTypeCodecs, keySystem.lower());
    214         return supported > WebMimeRegistry::IsNotSupported;
    215     }
    216 
    217     return false;
    218 }
    219 
    220 WebMimeRegistry::SupportsType HTMLMediaElement::supportsType(const ContentType& contentType, const String& keySystem)
    221 {
    222     DEFINE_STATIC_LOCAL(const String, codecs, ("codecs"));
    223 
    224     if (!RuntimeEnabledFeatures::mediaEnabled())
    225         return WebMimeRegistry::IsNotSupported;
    226 
    227     String type = contentType.type().lower();
    228     // The codecs string is not lower-cased because MP4 values are case sensitive
    229     // per http://tools.ietf.org/html/rfc4281#page-7.
    230     String typeCodecs = contentType.parameter(codecs);
    231     String system = keySystem.lower();
    232 
    233     if (type.isEmpty())
    234         return WebMimeRegistry::IsNotSupported;
    235 
    236     // 4.8.10.3 MIME types - The canPlayType(type) method must return the empty string if type is a type that the
    237     // user agent knows it cannot render or is the type "application/octet-stream"
    238     if (type == "application/octet-stream")
    239         return WebMimeRegistry::IsNotSupported;
    240 
    241     return blink::Platform::current()->mimeRegistry()->supportsMediaMIMEType(type, typeCodecs, system);
    242 }
    243 
    244 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& document, bool createdByParser)
    245     : HTMLElement(tagName, document)
    246     , ActiveDOMObject(&document)
    247     , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
    248     , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
    249     , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
    250     , m_playedTimeRanges()
    251     , m_asyncEventQueue(GenericEventQueue::create(this))
    252     , m_playbackRate(1.0f)
    253     , m_defaultPlaybackRate(1.0f)
    254     , m_networkState(NETWORK_EMPTY)
    255     , m_readyState(HAVE_NOTHING)
    256     , m_readyStateMaximum(HAVE_NOTHING)
    257     , m_volume(1.0f)
    258     , m_lastSeekTime(0)
    259     , m_previousProgressTime(numeric_limits<double>::max())
    260     , m_duration(numeric_limits<double>::quiet_NaN())
    261     , m_lastTimeUpdateEventWallTime(0)
    262     , m_lastTimeUpdateEventMovieTime(numeric_limits<double>::max())
    263     , m_loadState(WaitingForSource)
    264     , m_webLayer(0)
    265     , m_opaque(false)
    266     , m_restrictions(RequirePageConsentToLoadMediaRestriction)
    267     , m_preload(MediaPlayer::Auto)
    268     , m_displayMode(Unknown)
    269     , m_cachedTime(MediaPlayer::invalidTime())
    270     , m_cachedTimeWallClockUpdateTime(0)
    271     , m_minimumWallClockTimeToCacheMediaTime(0)
    272     , m_fragmentStartTime(MediaPlayer::invalidTime())
    273     , m_fragmentEndTime(MediaPlayer::invalidTime())
    274     , m_pendingActionFlags(0)
    275     , m_playing(false)
    276     , m_shouldDelayLoadEvent(false)
    277     , m_haveFiredLoadedData(false)
    278     , m_active(true)
    279     , m_autoplaying(true)
    280     , m_muted(false)
    281     , m_paused(true)
    282     , m_seeking(false)
    283     , m_sentStalledEvent(false)
    284     , m_sentEndEvent(false)
    285     , m_pausedInternal(false)
    286     , m_closedCaptionsVisible(false)
    287     , m_loadInitiatedByUserGesture(false)
    288     , m_completelyLoaded(false)
    289     , m_havePreparedToPlay(false)
    290     , m_parsingInProgress(createdByParser)
    291     , m_tracksAreReady(true)
    292     , m_haveVisibleTextTrack(false)
    293     , m_processingPreferenceChange(false)
    294     , m_lastTextTrackUpdateTime(-1)
    295     , m_textTracks(0)
    296     , m_ignoreTrackDisplayUpdate(0)
    297 #if ENABLE(WEB_AUDIO)
    298     , m_audioSourceNode(0)
    299 #endif
    300 {
    301     ASSERT(RuntimeEnabledFeatures::mediaEnabled());
    302 
    303     WTF_LOG(Media, "HTMLMediaElement::HTMLMediaElement");
    304     ScriptWrappable::init(this);
    305 
    306     if (document.settings()) {
    307         if (document.settings()->mediaPlaybackRequiresUserGesture()) {
    308             addBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
    309             addBehaviorRestriction(RequireUserGestureForLoadRestriction);
    310         }
    311         if (document.settings()->mediaFullscreenRequiresUserGesture()) {
    312             addBehaviorRestriction(RequireUserGestureForFullscreenRestriction);
    313         }
    314     }
    315 
    316     setHasCustomStyleCallbacks();
    317     addElementToDocumentMap(this, &document);
    318 
    319 }
    320 
    321 HTMLMediaElement::~HTMLMediaElement()
    322 {
    323     WTF_LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
    324 
    325     m_asyncEventQueue->close();
    326 
    327     setShouldDelayLoadEvent(false);
    328     if (m_textTracks)
    329         m_textTracks->clearOwner();
    330     if (m_textTracks) {
    331         for (unsigned i = 0; i < m_textTracks->length(); ++i)
    332             m_textTracks->item(i)->clearClient();
    333     }
    334 
    335     if (m_mediaController) {
    336         m_mediaController->removeMediaElement(this);
    337         m_mediaController = 0;
    338     }
    339 
    340     closeMediaSource();
    341 
    342     setMediaKeys(0);
    343 
    344     removeElementFromDocumentMap(this, &document());
    345 
    346     // Destroying the player may cause a resource load to be canceled,
    347     // which could result in userCancelledLoad() being called back.
    348     // Setting m_completelyLoaded ensures that such a call will not cause
    349     // us to dispatch an abort event, which would result in a crash.
    350     // See http://crbug.com/233654 for more details.
    351     m_completelyLoaded = true;
    352 
    353     // Destroying the player may cause a resource load to be canceled,
    354     // which could result in Document::dispatchWindowLoadEvent() being
    355     // called via ResourceFetch::didLoadResource() then
    356     // FrameLoader::loadDone(). To prevent load event dispatching during
    357     // object destruction, we use Document::incrementLoadEventDelayCount().
    358     // See http://crbug.com/275223 for more details.
    359     document().incrementLoadEventDelayCount();
    360 
    361     clearMediaPlayerAndAudioSourceProviderClient();
    362 
    363     document().decrementLoadEventDelayCount();
    364 }
    365 
    366 void HTMLMediaElement::didMoveToNewDocument(Document& oldDocument)
    367 {
    368     WTF_LOG(Media, "HTMLMediaElement::didMoveToNewDocument");
    369 
    370     if (m_shouldDelayLoadEvent) {
    371         document().incrementLoadEventDelayCount();
    372         // Note: Keeping the load event delay count increment on oldDocument that was added
    373         // when m_shouldDelayLoadEvent was set so that destruction of m_player can not
    374         // cause load event dispatching in oldDocument.
    375     } else {
    376         // Incrementing the load event delay count so that destruction of m_player can not
    377         // cause load event dispatching in oldDocument.
    378         oldDocument.incrementLoadEventDelayCount();
    379     }
    380 
    381     removeElementFromDocumentMap(this, &oldDocument);
    382     addElementToDocumentMap(this, &document());
    383 
    384     // FIXME: This is a temporary fix to prevent this object from causing the
    385     // MediaPlayer to dereference Frame and FrameLoader pointers from the
    386     // previous document. A proper fix would provide a mechanism to allow this
    387     // object to refresh the MediaPlayer's Frame and FrameLoader references on
    388     // document changes so that playback can be resumed properly.
    389     userCancelledLoad();
    390 
    391     // Decrement the load event delay count on oldDocument now that m_player has been destroyed
    392     // and there is no risk of dispatching a load event from within the destructor.
    393     oldDocument.decrementLoadEventDelayCount();
    394 
    395     HTMLElement::didMoveToNewDocument(oldDocument);
    396 }
    397 
    398 bool HTMLMediaElement::hasCustomFocusLogic() const
    399 {
    400     return true;
    401 }
    402 
    403 bool HTMLMediaElement::supportsFocus() const
    404 {
    405     if (ownerDocument()->isMediaDocument())
    406         return false;
    407 
    408     // If no controls specified, we should still be able to focus the element if it has tabIndex.
    409     return controls() || HTMLElement::supportsFocus();
    410 }
    411 
    412 bool HTMLMediaElement::isMouseFocusable() const
    413 {
    414     return false;
    415 }
    416 
    417 void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    418 {
    419     if (name == srcAttr) {
    420         // Trigger a reload, as long as the 'src' attribute is present.
    421         if (!value.isNull()) {
    422             clearMediaPlayer(LoadMediaResource);
    423             scheduleDelayedAction(LoadMediaResource);
    424         }
    425     } else if (name == controlsAttr)
    426         configureMediaControls();
    427     else if (name == preloadAttr) {
    428         if (equalIgnoringCase(value, "none"))
    429             m_preload = MediaPlayer::None;
    430         else if (equalIgnoringCase(value, "metadata"))
    431             m_preload = MediaPlayer::MetaData;
    432         else {
    433             // The spec does not define an "invalid value default" but "auto" is suggested as the
    434             // "missing value default", so use it for everything except "none" and "metadata"
    435             m_preload = MediaPlayer::Auto;
    436         }
    437 
    438         // The attribute must be ignored if the autoplay attribute is present
    439         if (!autoplay() && m_player)
    440             m_player->setPreload(m_preload);
    441 
    442     } else if (name == mediagroupAttr)
    443         setMediaGroup(value);
    444     else if (name == onbeforeloadAttr)
    445         setAttributeEventListener(EventTypeNames::beforeload, createAttributeEventListener(this, name, value));
    446     else
    447         HTMLElement::parseAttribute(name, value);
    448 }
    449 
    450 void HTMLMediaElement::finishParsingChildren()
    451 {
    452     HTMLElement::finishParsingChildren();
    453     m_parsingInProgress = false;
    454 
    455     if (!RuntimeEnabledFeatures::videoTrackEnabled())
    456         return;
    457 
    458     for (Node* node = firstChild(); node; node = node->nextSibling()) {
    459         if (node->hasTagName(trackTag)) {
    460             scheduleDelayedAction(LoadTextTrackResource);
    461             break;
    462         }
    463     }
    464 }
    465 
    466 bool HTMLMediaElement::rendererIsNeeded(const RenderStyle& style)
    467 {
    468     return controls() ? HTMLElement::rendererIsNeeded(style) : false;
    469 }
    470 
    471 RenderObject* HTMLMediaElement::createRenderer(RenderStyle*)
    472 {
    473     return new RenderMedia(this);
    474 }
    475 
    476 bool HTMLMediaElement::childShouldCreateRenderer(const Node& child) const
    477 {
    478     return hasMediaControls() && HTMLElement::childShouldCreateRenderer(child);
    479 }
    480 
    481 Node::InsertionNotificationRequest HTMLMediaElement::insertedInto(ContainerNode* insertionPoint)
    482 {
    483     WTF_LOG(Media, "HTMLMediaElement::insertedInto");
    484 
    485     HTMLElement::insertedInto(insertionPoint);
    486     if (insertionPoint->inDocument()) {
    487         m_active = true;
    488 
    489         if (!getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY)
    490             scheduleDelayedAction(LoadMediaResource);
    491     }
    492 
    493     configureMediaControls();
    494     return InsertionDone;
    495 }
    496 
    497 void HTMLMediaElement::removedFrom(ContainerNode* insertionPoint)
    498 {
    499     WTF_LOG(Media, "HTMLMediaElement::removedFrom");
    500 
    501     m_active = false;
    502     if (insertionPoint->inDocument()) {
    503         configureMediaControls();
    504         if (m_networkState > NETWORK_EMPTY)
    505             pause();
    506     }
    507 
    508     HTMLElement::removedFrom(insertionPoint);
    509 }
    510 
    511 void HTMLMediaElement::attach(const AttachContext& context)
    512 {
    513     HTMLElement::attach(context);
    514 
    515     if (renderer())
    516         renderer()->updateFromElement();
    517 }
    518 
    519 void HTMLMediaElement::didRecalcStyle(StyleRecalcChange)
    520 {
    521     if (renderer())
    522         renderer()->updateFromElement();
    523 }
    524 
    525 void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType)
    526 {
    527     WTF_LOG(Media, "HTMLMediaElement::scheduleDelayedAction");
    528 
    529     if ((actionType & LoadMediaResource) && !(m_pendingActionFlags & LoadMediaResource)) {
    530         prepareForLoad();
    531         m_pendingActionFlags |= LoadMediaResource;
    532     }
    533 
    534     if (RuntimeEnabledFeatures::videoTrackEnabled() && (actionType & LoadTextTrackResource))
    535         m_pendingActionFlags |= LoadTextTrackResource;
    536 
    537     if (!m_loadTimer.isActive())
    538         m_loadTimer.startOneShot(0);
    539 }
    540 
    541 void HTMLMediaElement::scheduleNextSourceChild()
    542 {
    543     // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
    544     m_pendingActionFlags |= LoadMediaResource;
    545     m_loadTimer.startOneShot(0);
    546 }
    547 
    548 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
    549 {
    550 #if LOG_MEDIA_EVENTS
    551     WTF_LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data());
    552 #endif
    553     m_asyncEventQueue->enqueueEvent(Event::createCancelable(eventName));
    554 }
    555 
    556 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
    557 {
    558     RefPtr<HTMLMediaElement> protect(this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
    559 
    560     if (RuntimeEnabledFeatures::videoTrackEnabled() && (m_pendingActionFlags & LoadTextTrackResource))
    561         configureTextTracks();
    562 
    563     if (m_pendingActionFlags & LoadMediaResource) {
    564         if (m_loadState == LoadingFromSourceElement)
    565             loadNextSourceChild();
    566         else
    567             loadInternal();
    568     }
    569 
    570     m_pendingActionFlags = 0;
    571 }
    572 
    573 PassRefPtr<MediaError> HTMLMediaElement::error() const
    574 {
    575     return m_error;
    576 }
    577 
    578 void HTMLMediaElement::setSrc(const AtomicString& url)
    579 {
    580     setAttribute(srcAttr, url);
    581 }
    582 
    583 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
    584 {
    585     return m_networkState;
    586 }
    587 
    588 String HTMLMediaElement::canPlayType(const String& mimeType, const String& keySystem, const KURL& url) const
    589 {
    590     WebMimeRegistry::SupportsType support = supportsType(ContentType(mimeType), keySystem);
    591     String canPlay;
    592 
    593     // 4.8.10.3
    594     switch (support)
    595     {
    596         case WebMimeRegistry::IsNotSupported:
    597             canPlay = emptyString();
    598             break;
    599         case WebMimeRegistry::MayBeSupported:
    600             canPlay = "maybe";
    601             break;
    602         case WebMimeRegistry::IsSupported:
    603             canPlay = "probably";
    604             break;
    605     }
    606 
    607     WTF_LOG(Media, "HTMLMediaElement::canPlayType(%s, %s, %s) -> %s", mimeType.utf8().data(), keySystem.utf8().data(), url.elidedString().utf8().data(), canPlay.utf8().data());
    608 
    609     return canPlay;
    610 }
    611 
    612 void HTMLMediaElement::load()
    613 {
    614     RefPtr<HTMLMediaElement> protect(this); // loadInternal may result in a 'beforeload' event, which can make arbitrary DOM mutations.
    615 
    616     WTF_LOG(Media, "HTMLMediaElement::load()");
    617 
    618     if (document().settings() && !document().settings()->mediaEnabled())
    619         return;
    620 
    621     if (userGestureRequiredForLoad() && !UserGestureIndicator::processingUserGesture())
    622         return;
    623 
    624     m_loadInitiatedByUserGesture = UserGestureIndicator::processingUserGesture();
    625     if (m_loadInitiatedByUserGesture)
    626         removeBehaviorsRestrictionsAfterFirstUserGesture();
    627     prepareForLoad();
    628     loadInternal();
    629     prepareToPlay();
    630 }
    631 
    632 void HTMLMediaElement::prepareForLoad()
    633 {
    634     WTF_LOG(Media, "HTMLMediaElement::prepareForLoad");
    635 
    636     // Perform the cleanup required for the resource load algorithm to run.
    637     stopPeriodicTimers();
    638     m_loadTimer.stop();
    639     m_sentEndEvent = false;
    640     m_sentStalledEvent = false;
    641     m_haveFiredLoadedData = false;
    642     m_completelyLoaded = false;
    643     m_havePreparedToPlay = false;
    644     m_displayMode = Unknown;
    645 
    646     // 1 - Abort any already-running instance of the resource selection algorithm for this element.
    647     m_loadState = WaitingForSource;
    648     m_currentSourceNode = 0;
    649 
    650     // 2 - If there are any tasks from the media element's media element event task source in
    651     // one of the task queues, then remove those tasks.
    652     cancelPendingEventsAndCallbacks();
    653 
    654     // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
    655     // a task to fire a simple event named abort at the media element.
    656     if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
    657         scheduleEvent(EventTypeNames::abort);
    658 
    659     closeMediaSource();
    660 
    661     createMediaPlayer();
    662 
    663     // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
    664     if (m_networkState != NETWORK_EMPTY) {
    665         m_networkState = NETWORK_EMPTY;
    666         m_readyState = HAVE_NOTHING;
    667         m_readyStateMaximum = HAVE_NOTHING;
    668         refreshCachedTime();
    669         m_paused = true;
    670         m_seeking = false;
    671         invalidateCachedTime();
    672         scheduleEvent(EventTypeNames::emptied);
    673         updateMediaController();
    674         if (RuntimeEnabledFeatures::videoTrackEnabled())
    675             updateActiveTextTrackCues(0);
    676     }
    677 
    678     // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
    679     setPlaybackRate(defaultPlaybackRate());
    680 
    681     // 6 - Set the error attribute to null and the autoplaying flag to true.
    682     m_error = 0;
    683     m_autoplaying = true;
    684 
    685     // 7 - Invoke the media element's resource selection algorithm.
    686 
    687     // 8 - Note: Playback of any previously playing media resource for this element stops.
    688 
    689     // The resource selection algorithm
    690     // 1 - Set the networkState to NETWORK_NO_SOURCE
    691     m_networkState = NETWORK_NO_SOURCE;
    692 
    693     // 2 - Asynchronously await a stable state.
    694 
    695     m_playedTimeRanges = TimeRanges::create();
    696     m_lastSeekTime = 0;
    697     m_duration = numeric_limits<double>::quiet_NaN();
    698 
    699     // The spec doesn't say to block the load event until we actually run the asynchronous section
    700     // algorithm, but do it now because we won't start that until after the timer fires and the
    701     // event may have already fired by then.
    702     setShouldDelayLoadEvent(true);
    703 
    704     configureMediaControls();
    705 }
    706 
    707 void HTMLMediaElement::loadInternal()
    708 {
    709     // Some of the code paths below this function dispatch the BeforeLoad event. This ASSERT helps
    710     // us catch those bugs more quickly without needing all the branches to align to actually
    711     // trigger the event.
    712     ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
    713 
    714     // Once the page has allowed an element to load media, it is free to load at will. This allows a
    715     // playlist that starts in a foreground tab to continue automatically if the tab is subsequently
    716     // put in the the background.
    717     removeBehaviorRestriction(RequirePageConsentToLoadMediaRestriction);
    718 
    719     // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
    720     // disabled state when the element's resource selection algorithm last started".
    721     if (RuntimeEnabledFeatures::videoTrackEnabled()) {
    722         m_textTracksWhenResourceSelectionBegan.clear();
    723         if (m_textTracks) {
    724             for (unsigned i = 0; i < m_textTracks->length(); ++i) {
    725                 TextTrack* track = m_textTracks->item(i);
    726                 if (track->mode() != TextTrack::disabledKeyword())
    727                     m_textTracksWhenResourceSelectionBegan.append(track);
    728             }
    729         }
    730     }
    731 
    732     selectMediaResource();
    733 }
    734 
    735 void HTMLMediaElement::selectMediaResource()
    736 {
    737     WTF_LOG(Media, "HTMLMediaElement::selectMediaResource");
    738 
    739     enum Mode { attribute, children };
    740 
    741     // 3 - If the media element has a src attribute, then let mode be attribute.
    742     Mode mode = attribute;
    743     if (!fastHasAttribute(srcAttr)) {
    744         Node* node;
    745         for (node = firstChild(); node; node = node->nextSibling()) {
    746             if (node->hasTagName(sourceTag))
    747                 break;
    748         }
    749 
    750         // Otherwise, if the media element does not have a src attribute but has a source
    751         // element child, then let mode be children and let candidate be the first such
    752         // source element child in tree order.
    753         if (node) {
    754             mode = children;
    755             m_nextChildNodeToConsider = node;
    756             m_currentSourceNode = 0;
    757         } else {
    758             // Otherwise the media element has neither a src attribute nor a source element
    759             // child: set the networkState to NETWORK_EMPTY, and abort these steps; the
    760             // synchronous section ends.
    761             m_loadState = WaitingForSource;
    762             setShouldDelayLoadEvent(false);
    763             m_networkState = NETWORK_EMPTY;
    764 
    765             WTF_LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load");
    766             return;
    767         }
    768     }
    769 
    770     // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event),
    771     // and set its networkState to NETWORK_LOADING.
    772     setShouldDelayLoadEvent(true);
    773     m_networkState = NETWORK_LOADING;
    774 
    775     // 5 - Queue a task to fire a simple event named loadstart at the media element.
    776     scheduleEvent(EventTypeNames::loadstart);
    777 
    778     // 6 - If mode is attribute, then run these substeps
    779     if (mode == attribute) {
    780         m_loadState = LoadingFromSrcAttr;
    781 
    782         // If the src attribute's value is the empty string ... jump down to the failed step below
    783         KURL mediaURL = getNonEmptyURLAttribute(srcAttr);
    784         if (mediaURL.isEmpty()) {
    785             mediaLoadingFailed(MediaPlayer::FormatError);
    786             WTF_LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'");
    787             return;
    788         }
    789 
    790         if (!isSafeToLoadURL(mediaURL, Complain) || !dispatchBeforeLoadEvent(mediaURL.string())) {
    791             mediaLoadingFailed(MediaPlayer::FormatError);
    792             return;
    793         }
    794 
    795         // No type or key system information is available when the url comes
    796         // from the 'src' attribute so MediaPlayer
    797         // will have to pick a media engine based on the file extension.
    798         ContentType contentType((String()));
    799         loadResource(mediaURL, contentType, String());
    800         WTF_LOG(Media, "HTMLMediaElement::selectMediaResource, using 'src' attribute url");
    801         return;
    802     }
    803 
    804     // Otherwise, the source elements will be used
    805     loadNextSourceChild();
    806 }
    807 
    808 void HTMLMediaElement::loadNextSourceChild()
    809 {
    810     ContentType contentType((String()));
    811     String keySystem;
    812     KURL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain);
    813     if (!mediaURL.isValid()) {
    814         waitForSourceChange();
    815         return;
    816     }
    817 
    818     // Recreate the media player for the new url
    819     createMediaPlayer();
    820 
    821     m_loadState = LoadingFromSourceElement;
    822     loadResource(mediaURL, contentType, keySystem);
    823 }
    824 
    825 void HTMLMediaElement::loadResource(const KURL& url, ContentType& contentType, const String& keySystem)
    826 {
    827     ASSERT(isSafeToLoadURL(url, Complain));
    828 
    829     WTF_LOG(Media, "HTMLMediaElement::loadResource(%s, %s, %s)", urlForLoggingMedia(url).utf8().data(), contentType.raw().utf8().data(), keySystem.utf8().data());
    830 
    831     Frame* frame = document().frame();
    832     if (!frame) {
    833         mediaLoadingFailed(MediaPlayer::FormatError);
    834         return;
    835     }
    836 
    837     // The resource fetch algorithm
    838     m_networkState = NETWORK_LOADING;
    839 
    840     // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app
    841     // cache is an internal detail not exposed through the media element API.
    842     m_currentSrc = url;
    843 
    844     WTF_LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLoggingMedia(m_currentSrc).utf8().data());
    845 
    846     if (MediaStreamRegistry::registry().lookupMediaStreamDescriptor(url.string()))
    847       removeBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
    848 
    849     startProgressEventTimer();
    850 
    851     // Reset display mode to force a recalculation of what to show because we are resetting the player.
    852     setDisplayMode(Unknown);
    853 
    854     if (!autoplay())
    855         m_player->setPreload(m_preload);
    856 
    857     if (fastHasAttribute(mutedAttr))
    858         m_muted = true;
    859     updateVolume();
    860 
    861     ASSERT(!m_mediaSource);
    862 
    863     if (url.protocolIs(mediaSourceBlobProtocol))
    864         m_mediaSource = HTMLMediaSource::lookup(url.string());
    865 
    866     if (m_mediaSource) {
    867         if (m_mediaSource->attachToElement(this)) {
    868             m_player->load(url, m_mediaSource);
    869         } else {
    870             // Forget our reference to the MediaSource, so we leave it alone
    871             // while processing remainder of load failure.
    872             m_mediaSource = 0;
    873             mediaLoadingFailed(MediaPlayer::FormatError);
    874         }
    875     } else if (canLoadURL(url, contentType, keySystem)) {
    876         m_player->load(url);
    877     } else {
    878         mediaLoadingFailed(MediaPlayer::FormatError);
    879     }
    880 
    881     // If there is no poster to display, allow the media engine to render video frames as soon as
    882     // they are available.
    883     updateDisplayState();
    884 
    885     if (renderer())
    886         renderer()->updateFromElement();
    887 }
    888 
    889 static bool trackIndexCompare(TextTrack* a,
    890                               TextTrack* b)
    891 {
    892     return a->trackIndex() - b->trackIndex() < 0;
    893 }
    894 
    895 static bool eventTimeCueCompare(const std::pair<double, TextTrackCue*>& a,
    896                                 const std::pair<double, TextTrackCue*>& b)
    897 {
    898     // 12 - Sort the tasks in events in ascending time order (tasks with earlier
    899     // times first).
    900     if (a.first != b.first)
    901         return a.first - b.first < 0;
    902 
    903     // If the cues belong to different text tracks, it doesn't make sense to
    904     // compare the two tracks by the relative cue order, so return the relative
    905     // track order.
    906     if (a.second->track() != b.second->track())
    907         return trackIndexCompare(a.second->track(), b.second->track());
    908 
    909     // 12 - Further sort tasks in events that have the same time by the
    910     // relative text track cue order of the text track cues associated
    911     // with these tasks.
    912     return a.second->cueIndex() - b.second->cueIndex() < 0;
    913 }
    914 
    915 
    916 void HTMLMediaElement::updateActiveTextTrackCues(double movieTime)
    917 {
    918     // 4.8.10.8 Playing the media resource
    919 
    920     //  If the current playback position changes while the steps are running,
    921     //  then the user agent must wait for the steps to complete, and then must
    922     //  immediately rerun the steps.
    923     if (ignoreTrackDisplayUpdateRequests())
    924         return;
    925 
    926     WTF_LOG(Media, "HTMLMediaElement::updateActiveTextTrackCues");
    927 
    928     // 1 - Let current cues be a list of cues, initialized to contain all the
    929     // cues of all the hidden, showing, or showing by default text tracks of the
    930     // media element (not the disabled ones) whose start times are less than or
    931     // equal to the current playback position and whose end times are greater
    932     // than the current playback position.
    933     CueList currentCues;
    934 
    935     // The user agent must synchronously unset [the text track cue active] flag
    936     // whenever ... the media element's readyState is changed back to HAVE_NOTHING.
    937     if (m_readyState != HAVE_NOTHING && m_player)
    938         currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
    939 
    940     CueList previousCues;
    941     CueList missedCues;
    942 
    943     // 2 - Let other cues be a list of cues, initialized to contain all the cues
    944     // of hidden, showing, and showing by default text tracks of the media
    945     // element that are not present in current cues.
    946     previousCues = m_currentlyActiveCues;
    947 
    948     // 3 - Let last time be the current playback position at the time this
    949     // algorithm was last run for this media element, if this is not the first
    950     // time it has run.
    951     double lastTime = m_lastTextTrackUpdateTime;
    952 
    953     // 4 - If the current playback position has, since the last time this
    954     // algorithm was run, only changed through its usual monotonic increase
    955     // during normal playback, then let missed cues be the list of cues in other
    956     // cues whose start times are greater than or equal to last time and whose
    957     // end times are less than or equal to the current playback position.
    958     // Otherwise, let missed cues be an empty list.
    959     if (lastTime >= 0 && m_lastSeekTime < movieTime) {
    960         CueList potentiallySkippedCues =
    961             m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime));
    962 
    963         for (size_t i = 0; i < potentiallySkippedCues.size(); ++i) {
    964             double cueStartTime = potentiallySkippedCues[i].low();
    965             double cueEndTime = potentiallySkippedCues[i].high();
    966 
    967             // Consider cues that may have been missed since the last seek time.
    968             if (cueStartTime > max(m_lastSeekTime, lastTime) && cueEndTime < movieTime)
    969                 missedCues.append(potentiallySkippedCues[i]);
    970         }
    971     }
    972 
    973     m_lastTextTrackUpdateTime = movieTime;
    974 
    975     // 5 - If the time was reached through the usual monotonic increase of the
    976     // current playback position during normal playback, and if the user agent
    977     // has not fired a timeupdate event at the element in the past 15 to 250ms
    978     // and is not still running event handlers for such an event, then the user
    979     // agent must queue a task to fire a simple event named timeupdate at the
    980     // element. (In the other cases, such as explicit seeks, relevant events get
    981     // fired as part of the overall process of changing the current playback
    982     // position.)
    983     if (!m_seeking && m_lastSeekTime <= lastTime)
    984         scheduleTimeupdateEvent(true);
    985 
    986     // Explicitly cache vector sizes, as their content is constant from here.
    987     size_t currentCuesSize = currentCues.size();
    988     size_t missedCuesSize = missedCues.size();
    989     size_t previousCuesSize = previousCues.size();
    990 
    991     // 6 - If all of the cues in current cues have their text track cue active
    992     // flag set, none of the cues in other cues have their text track cue active
    993     // flag set, and missed cues is empty, then abort these steps.
    994     bool activeSetChanged = missedCuesSize;
    995 
    996     for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i)
    997         if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive())
    998             activeSetChanged = true;
    999 
   1000     for (size_t i = 0; i < currentCuesSize; ++i) {
   1001         currentCues[i].data()->updateDisplayTree(movieTime);
   1002 
   1003         if (!currentCues[i].data()->isActive())
   1004             activeSetChanged = true;
   1005     }
   1006 
   1007     if (!activeSetChanged)
   1008         return;
   1009 
   1010     // 7 - If the time was reached through the usual monotonic increase of the
   1011     // current playback position during normal playback, and there are cues in
   1012     // other cues that have their text track cue pause-on-exi flag set and that
   1013     // either have their text track cue active flag set or are also in missed
   1014     // cues, then immediately pause the media element.
   1015     for (size_t i = 0; !m_paused && i < previousCuesSize; ++i) {
   1016         if (previousCues[i].data()->pauseOnExit()
   1017             && previousCues[i].data()->isActive()
   1018             && !currentCues.contains(previousCues[i]))
   1019             pause();
   1020     }
   1021 
   1022     for (size_t i = 0; !m_paused && i < missedCuesSize; ++i) {
   1023         if (missedCues[i].data()->pauseOnExit())
   1024             pause();
   1025     }
   1026 
   1027     // 8 - Let events be a list of tasks, initially empty. Each task in this
   1028     // list will be associated with a text track, a text track cue, and a time,
   1029     // which are used to sort the list before the tasks are queued.
   1030     Vector<std::pair<double, TextTrackCue*> > eventTasks;
   1031 
   1032     // 8 - Let affected tracks be a list of text tracks, initially empty.
   1033     Vector<TextTrack*> affectedTracks;
   1034 
   1035     for (size_t i = 0; i < missedCuesSize; ++i) {
   1036         // 9 - For each text track cue in missed cues, prepare an event named enter
   1037         // for the TextTrackCue object with the text track cue start time.
   1038         eventTasks.append(std::make_pair(missedCues[i].data()->startTime(),
   1039                                          missedCues[i].data()));
   1040 
   1041         // 10 - For each text track [...] in missed cues, prepare an event
   1042         // named exit for the TextTrackCue object with the  with the later of
   1043         // the text track cue end time and the text track cue start time.
   1044 
   1045         // Note: An explicit task is added only if the cue is NOT a zero or
   1046         // negative length cue. Otherwise, the need for an exit event is
   1047         // checked when these tasks are actually queued below. This doesn't
   1048         // affect sorting events before dispatch either, because the exit
   1049         // event has the same time as the enter event.
   1050         if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime())
   1051             eventTasks.append(std::make_pair(missedCues[i].data()->endTime(),
   1052                                              missedCues[i].data()));
   1053     }
   1054 
   1055     for (size_t i = 0; i < previousCuesSize; ++i) {
   1056         // 10 - For each text track cue in other cues that has its text
   1057         // track cue active flag set prepare an event named exit for the
   1058         // TextTrackCue object with the text track cue end time.
   1059         if (!currentCues.contains(previousCues[i]))
   1060             eventTasks.append(std::make_pair(previousCues[i].data()->endTime(),
   1061                                              previousCues[i].data()));
   1062     }
   1063 
   1064     for (size_t i = 0; i < currentCuesSize; ++i) {
   1065         // 11 - For each text track cue in current cues that does not have its
   1066         // text track cue active flag set, prepare an event named enter for the
   1067         // TextTrackCue object with the text track cue start time.
   1068         if (!previousCues.contains(currentCues[i]))
   1069             eventTasks.append(std::make_pair(currentCues[i].data()->startTime(),
   1070                                              currentCues[i].data()));
   1071     }
   1072 
   1073     // 12 - Sort the tasks in events in ascending time order (tasks with earlier
   1074     // times first).
   1075     nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);
   1076 
   1077     for (size_t i = 0; i < eventTasks.size(); ++i) {
   1078         if (!affectedTracks.contains(eventTasks[i].second->track()))
   1079             affectedTracks.append(eventTasks[i].second->track());
   1080 
   1081         // 13 - Queue each task in events, in list order.
   1082         RefPtr<Event> event;
   1083 
   1084         // Each event in eventTasks may be either an enterEvent or an exitEvent,
   1085         // depending on the time that is associated with the event. This
   1086         // correctly identifies the type of the event, if the startTime is
   1087         // less than the endTime in the cue.
   1088         if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) {
   1089             event = Event::create(EventTypeNames::enter);
   1090             event->setTarget(eventTasks[i].second);
   1091             m_asyncEventQueue->enqueueEvent(event.release());
   1092 
   1093             event = Event::create(EventTypeNames::exit);
   1094             event->setTarget(eventTasks[i].second);
   1095             m_asyncEventQueue->enqueueEvent(event.release());
   1096         } else {
   1097             if (eventTasks[i].first == eventTasks[i].second->startTime())
   1098                 event = Event::create(EventTypeNames::enter);
   1099             else
   1100                 event = Event::create(EventTypeNames::exit);
   1101 
   1102             event->setTarget(eventTasks[i].second);
   1103             m_asyncEventQueue->enqueueEvent(event.release());
   1104         }
   1105     }
   1106 
   1107     // 14 - Sort affected tracks in the same order as the text tracks appear in
   1108     // the media element's list of text tracks, and remove duplicates.
   1109     nonCopyingSort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare);
   1110 
   1111     // 15 - For each text track in affected tracks, in the list order, queue a
   1112     // task to fire a simple event named cuechange at the TextTrack object, and, ...
   1113     for (size_t i = 0; i < affectedTracks.size(); ++i) {
   1114         RefPtr<Event> event = Event::create(EventTypeNames::cuechange);
   1115         event->setTarget(affectedTracks[i]);
   1116 
   1117         m_asyncEventQueue->enqueueEvent(event.release());
   1118 
   1119         // ... if the text track has a corresponding track element, to then fire a
   1120         // simple event named cuechange at the track element as well.
   1121         if (affectedTracks[i]->trackType() == TextTrack::TrackElement) {
   1122             RefPtr<Event> event = Event::create(EventTypeNames::cuechange);
   1123             HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(affectedTracks[i])->trackElement();
   1124             ASSERT(trackElement);
   1125             event->setTarget(trackElement);
   1126 
   1127             m_asyncEventQueue->enqueueEvent(event.release());
   1128         }
   1129     }
   1130 
   1131     // 16 - Set the text track cue active flag of all the cues in the current
   1132     // cues, and unset the text track cue active flag of all the cues in the
   1133     // other cues.
   1134     for (size_t i = 0; i < currentCuesSize; ++i)
   1135         currentCues[i].data()->setIsActive(true);
   1136 
   1137     for (size_t i = 0; i < previousCuesSize; ++i)
   1138         if (!currentCues.contains(previousCues[i]))
   1139             previousCues[i].data()->setIsActive(false);
   1140 
   1141     // Update the current active cues.
   1142     m_currentlyActiveCues = currentCues;
   1143 
   1144     if (activeSetChanged)
   1145         updateTextTrackDisplay();
   1146 }
   1147 
   1148 bool HTMLMediaElement::textTracksAreReady() const
   1149 {
   1150     // 4.8.10.12.1 Text track model
   1151     // ...
   1152     // The text tracks of a media element are ready if all the text tracks whose mode was not
   1153     // in the disabled state when the element's resource selection algorithm last started now
   1154     // have a text track readiness state of loaded or failed to load.
   1155     for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) {
   1156         if (m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::Loading
   1157             || m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::NotLoaded)
   1158             return false;
   1159     }
   1160 
   1161     return true;
   1162 }
   1163 
   1164 void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track)
   1165 {
   1166     if (m_player && m_textTracksWhenResourceSelectionBegan.contains(track)) {
   1167         if (track->readinessState() != TextTrack::Loading)
   1168             setReadyState(m_player->readyState());
   1169     } else {
   1170         // The track readiness state might have changed as a result of the user
   1171         // clicking the captions button. In this case, a check whether all the
   1172         // resources have failed loading should be done in order to hide the CC button.
   1173         if (hasMediaControls() && track->readinessState() == TextTrack::FailedToLoad)
   1174             mediaControls()->refreshClosedCaptionsButtonVisibility();
   1175     }
   1176 }
   1177 
   1178 void HTMLMediaElement::textTrackModeChanged(TextTrack* track)
   1179 {
   1180     if (track->trackType() == TextTrack::TrackElement) {
   1181         // 4.8.10.12.3 Sourcing out-of-band text tracks
   1182         // ... when a text track corresponding to a track element is created with text track
   1183         // mode set to disabled and subsequently changes its text track mode to hidden, showing,
   1184         // or showing by default for the first time, the user agent must immediately and synchronously
   1185         // run the following algorithm ...
   1186 
   1187         for (Node* node = firstChild(); node; node = node->nextSibling()) {
   1188             if (!node->hasTagName(trackTag))
   1189                 continue;
   1190             HTMLTrackElement* trackElement = toHTMLTrackElement(node);
   1191             if (trackElement->track() != track)
   1192                 continue;
   1193 
   1194             // Mark this track as "configured" so configureTextTracks won't change the mode again.
   1195             track->setHasBeenConfigured(true);
   1196             if (track->mode() != TextTrack::disabledKeyword()) {
   1197                 if (trackElement->readyState() == HTMLTrackElement::LOADED)
   1198                     textTrackAddCues(track, track->cues());
   1199 
   1200                 // If this is the first added track, create the list of text tracks.
   1201                 if (!m_textTracks)
   1202                     m_textTracks = TextTrackList::create(this);
   1203             }
   1204             break;
   1205         }
   1206     } else if (track->trackType() == TextTrack::AddTrack && track->mode() != TextTrack::disabledKeyword())
   1207         textTrackAddCues(track, track->cues());
   1208 
   1209     configureTextTrackDisplay(AssumeVisibleChange);
   1210 
   1211     ASSERT(textTracks()->contains(track));
   1212     textTracks()->scheduleChangeEvent();
   1213 }
   1214 
   1215 void HTMLMediaElement::textTrackKindChanged(TextTrack* track)
   1216 {
   1217     if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword() && track->mode() == TextTrack::showingKeyword())
   1218         track->setMode(TextTrack::hiddenKeyword());
   1219 }
   1220 
   1221 void HTMLMediaElement::beginIgnoringTrackDisplayUpdateRequests()
   1222 {
   1223     ++m_ignoreTrackDisplayUpdate;
   1224 }
   1225 
   1226 void HTMLMediaElement::endIgnoringTrackDisplayUpdateRequests()
   1227 {
   1228     ASSERT(m_ignoreTrackDisplayUpdate);
   1229     --m_ignoreTrackDisplayUpdate;
   1230     if (!m_ignoreTrackDisplayUpdate && m_active)
   1231         updateActiveTextTrackCues(currentTime());
   1232 }
   1233 
   1234 void HTMLMediaElement::textTrackAddCues(TextTrack* track, const TextTrackCueList* cues)
   1235 {
   1236     WTF_LOG(Media, "HTMLMediaElement::textTrackAddCues");
   1237     if (track->mode() == TextTrack::disabledKeyword())
   1238         return;
   1239 
   1240     TrackDisplayUpdateScope scope(this);
   1241     for (size_t i = 0; i < cues->length(); ++i)
   1242         textTrackAddCue(cues->item(i)->track(), cues->item(i));
   1243 }
   1244 
   1245 void HTMLMediaElement::textTrackRemoveCues(TextTrack*, const TextTrackCueList* cues)
   1246 {
   1247     WTF_LOG(Media, "HTMLMediaElement::textTrackRemoveCues");
   1248 
   1249     TrackDisplayUpdateScope scope(this);
   1250     for (size_t i = 0; i < cues->length(); ++i)
   1251         textTrackRemoveCue(cues->item(i)->track(), cues->item(i));
   1252 }
   1253 
   1254 void HTMLMediaElement::textTrackAddCue(TextTrack* track, PassRefPtr<TextTrackCue> cue)
   1255 {
   1256     if (track->mode() == TextTrack::disabledKeyword())
   1257         return;
   1258 
   1259     // Negative duration cues need be treated in the interval tree as
   1260     // zero-length cues.
   1261     double endTime = max(cue->startTime(), cue->endTime());
   1262 
   1263     CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, cue.get());
   1264     if (!m_cueTree.contains(interval))
   1265         m_cueTree.add(interval);
   1266     updateActiveTextTrackCues(currentTime());
   1267 }
   1268 
   1269 void HTMLMediaElement::textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue> cue)
   1270 {
   1271     // Negative duration cues need to be treated in the interval tree as
   1272     // zero-length cues.
   1273     double endTime = max(cue->startTime(), cue->endTime());
   1274 
   1275     CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, cue.get());
   1276     m_cueTree.remove(interval);
   1277 
   1278     // Since the cue will be removed from the media element and likely the
   1279     // TextTrack might also be destructed, notifying the region of the cue
   1280     // removal shouldn't be done.
   1281     cue->notifyRegionWhenRemovingDisplayTree(false);
   1282 
   1283     size_t index = m_currentlyActiveCues.find(interval);
   1284     if (index != kNotFound) {
   1285         m_currentlyActiveCues.remove(index);
   1286         cue->setIsActive(false);
   1287     }
   1288     cue->removeDisplayTree();
   1289     updateActiveTextTrackCues(currentTime());
   1290 
   1291     cue->notifyRegionWhenRemovingDisplayTree(true);
   1292 }
   1293 
   1294 
   1295 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidURLAction actionIfInvalid)
   1296 {
   1297     if (!url.isValid()) {
   1298         WTF_LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLoggingMedia(url).utf8().data());
   1299         return false;
   1300     }
   1301 
   1302     Frame* frame = document().frame();
   1303     if (!frame || !document().securityOrigin()->canDisplay(url)) {
   1304         if (actionIfInvalid == Complain)
   1305             FrameLoader::reportLocalLoadFailed(frame, url.elidedString());
   1306         WTF_LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLoggingMedia(url).utf8().data());
   1307         return false;
   1308     }
   1309 
   1310     if (!document().contentSecurityPolicy()->allowMediaFromSource(url)) {
   1311         WTF_LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> rejected by Content Security Policy", urlForLoggingMedia(url).utf8().data());
   1312         return false;
   1313     }
   1314 
   1315     return true;
   1316 }
   1317 
   1318 void HTMLMediaElement::startProgressEventTimer()
   1319 {
   1320     if (m_progressEventTimer.isActive())
   1321         return;
   1322 
   1323     m_previousProgressTime = WTF::currentTime();
   1324     // 350ms is not magic, it is in the spec!
   1325     m_progressEventTimer.startRepeating(0.350);
   1326 }
   1327 
   1328 void HTMLMediaElement::waitForSourceChange()
   1329 {
   1330     WTF_LOG(Media, "HTMLMediaElement::waitForSourceChange");
   1331 
   1332     stopPeriodicTimers();
   1333     m_loadState = WaitingForSource;
   1334 
   1335     // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
   1336     m_networkState = NETWORK_NO_SOURCE;
   1337 
   1338     // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
   1339     setShouldDelayLoadEvent(false);
   1340 
   1341     updateDisplayState();
   1342 
   1343     if (renderer())
   1344         renderer()->updateFromElement();
   1345 }
   1346 
   1347 void HTMLMediaElement::noneSupported()
   1348 {
   1349     WTF_LOG(Media, "HTMLMediaElement::noneSupported");
   1350 
   1351     stopPeriodicTimers();
   1352     m_loadState = WaitingForSource;
   1353     m_currentSourceNode = 0;
   1354 
   1355     // 4.8.10.5
   1356     // 6 - Reaching this step indicates that the media resource failed to load or that the given
   1357     // URL could not be resolved. In one atomic operation, run the following steps:
   1358 
   1359     // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to
   1360     // MEDIA_ERR_SRC_NOT_SUPPORTED.
   1361     m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
   1362 
   1363     // 6.2 - Forget the media element's media-resource-specific text tracks.
   1364 
   1365     // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
   1366     m_networkState = NETWORK_NO_SOURCE;
   1367 
   1368     // 7 - Queue a task to fire a simple event named error at the media element.
   1369     scheduleEvent(EventTypeNames::error);
   1370 
   1371     closeMediaSource();
   1372 
   1373     // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
   1374     setShouldDelayLoadEvent(false);
   1375 
   1376     // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed,
   1377     // the element won't attempt to load another resource.
   1378 
   1379     updateDisplayState();
   1380 
   1381     if (renderer())
   1382         renderer()->updateFromElement();
   1383 }
   1384 
   1385 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
   1386 {
   1387     WTF_LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code()));
   1388 
   1389     // 1 - The user agent should cancel the fetching process.
   1390     stopPeriodicTimers();
   1391     m_loadState = WaitingForSource;
   1392 
   1393     // 2 - Set the error attribute to a new MediaError object whose code attribute is
   1394     // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
   1395     m_error = err;
   1396 
   1397     // 3 - Queue a task to fire a simple event named error at the media element.
   1398     scheduleEvent(EventTypeNames::error);
   1399 
   1400     closeMediaSource();
   1401 
   1402     // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
   1403     // task to fire a simple event called emptied at the element.
   1404     m_networkState = NETWORK_EMPTY;
   1405     scheduleEvent(EventTypeNames::emptied);
   1406 
   1407     // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
   1408     setShouldDelayLoadEvent(false);
   1409 
   1410     // 6 - Abort the overall resource selection algorithm.
   1411     m_currentSourceNode = 0;
   1412 }
   1413 
   1414 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
   1415 {
   1416     WTF_LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks");
   1417     m_asyncEventQueue->cancelAllEvents();
   1418 
   1419     for (Node* node = firstChild(); node; node = node->nextSibling()) {
   1420         if (node->hasTagName(sourceTag))
   1421             toHTMLSourceElement(node)->cancelPendingErrorEvent();
   1422     }
   1423 }
   1424 
   1425 void HTMLMediaElement::mediaPlayerNetworkStateChanged()
   1426 {
   1427     setNetworkState(m_player->networkState());
   1428 }
   1429 
   1430 void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error)
   1431 {
   1432     stopPeriodicTimers();
   1433 
   1434     // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
   1435     // <source> children, schedule the next one
   1436     if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
   1437 
   1438         if (m_currentSourceNode)
   1439             m_currentSourceNode->scheduleErrorEvent();
   1440         else
   1441             WTF_LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed");
   1442 
   1443         if (havePotentialSourceChild()) {
   1444             WTF_LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>");
   1445             scheduleNextSourceChild();
   1446         } else {
   1447             WTF_LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting");
   1448             waitForSourceChange();
   1449         }
   1450 
   1451         return;
   1452     }
   1453 
   1454     if (error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA)
   1455         mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
   1456     else if (error == MediaPlayer::DecodeError)
   1457         mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
   1458     else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr)
   1459         noneSupported();
   1460 
   1461     updateDisplayState();
   1462     if (hasMediaControls()) {
   1463         mediaControls()->reset();
   1464         mediaControls()->reportedError();
   1465     }
   1466 }
   1467 
   1468 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
   1469 {
   1470     WTF_LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState));
   1471 
   1472     if (state == MediaPlayer::Empty) {
   1473         // Just update the cached state and leave, we can't do anything.
   1474         m_networkState = NETWORK_EMPTY;
   1475         return;
   1476     }
   1477 
   1478     if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
   1479         mediaLoadingFailed(state);
   1480         return;
   1481     }
   1482 
   1483     if (state == MediaPlayer::Idle) {
   1484         if (m_networkState > NETWORK_IDLE) {
   1485             changeNetworkStateFromLoadingToIdle();
   1486             setShouldDelayLoadEvent(false);
   1487         } else {
   1488             m_networkState = NETWORK_IDLE;
   1489         }
   1490     }
   1491 
   1492     if (state == MediaPlayer::Loading) {
   1493         if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
   1494             startProgressEventTimer();
   1495         m_networkState = NETWORK_LOADING;
   1496     }
   1497 
   1498     if (state == MediaPlayer::Loaded) {
   1499         if (m_networkState != NETWORK_IDLE)
   1500             changeNetworkStateFromLoadingToIdle();
   1501         m_completelyLoaded = true;
   1502     }
   1503 
   1504     if (hasMediaControls())
   1505         mediaControls()->updateStatusDisplay();
   1506 }
   1507 
   1508 void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
   1509 {
   1510     m_progressEventTimer.stop();
   1511     if (hasMediaControls() && m_player->didLoadingProgress())
   1512         mediaControls()->bufferingProgressed();
   1513 
   1514     // Schedule one last progress event so we guarantee that at least one is fired
   1515     // for files that load very quickly.
   1516     scheduleEvent(EventTypeNames::progress);
   1517     scheduleEvent(EventTypeNames::suspend);
   1518     m_networkState = NETWORK_IDLE;
   1519 }
   1520 
   1521 void HTMLMediaElement::mediaPlayerReadyStateChanged()
   1522 {
   1523     setReadyState(m_player->readyState());
   1524 }
   1525 
   1526 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
   1527 {
   1528     WTF_LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState));
   1529 
   1530     // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
   1531     bool wasPotentiallyPlaying = potentiallyPlaying();
   1532 
   1533     ReadyState oldState = m_readyState;
   1534     ReadyState newState = static_cast<ReadyState>(state);
   1535 
   1536     bool tracksAreReady = !RuntimeEnabledFeatures::videoTrackEnabled() || textTracksAreReady();
   1537 
   1538     if (newState == oldState && m_tracksAreReady == tracksAreReady)
   1539         return;
   1540 
   1541     m_tracksAreReady = tracksAreReady;
   1542 
   1543     if (tracksAreReady)
   1544         m_readyState = newState;
   1545     else {
   1546         // If a media file has text tracks the readyState may not progress beyond HAVE_FUTURE_DATA until
   1547         // the text tracks are ready, regardless of the state of the media file.
   1548         if (newState <= HAVE_METADATA)
   1549             m_readyState = newState;
   1550         else
   1551             m_readyState = HAVE_CURRENT_DATA;
   1552     }
   1553 
   1554     if (oldState > m_readyStateMaximum)
   1555         m_readyStateMaximum = oldState;
   1556 
   1557     if (m_networkState == NETWORK_EMPTY)
   1558         return;
   1559 
   1560     if (m_seeking) {
   1561         // 4.8.10.9, step 9 note: If the media element was potentially playing immediately before
   1562         // it started seeking, but seeking caused its readyState attribute to change to a value
   1563         // lower than HAVE_FUTURE_DATA, then a waiting will be fired at the element.
   1564         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
   1565             scheduleEvent(EventTypeNames::waiting);
   1566 
   1567         // 4.8.10.9 steps 12-14
   1568         if (m_readyState >= HAVE_CURRENT_DATA)
   1569             finishSeek();
   1570     } else {
   1571         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
   1572             // 4.8.10.8
   1573             scheduleTimeupdateEvent(false);
   1574             scheduleEvent(EventTypeNames::waiting);
   1575         }
   1576     }
   1577 
   1578     if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
   1579         prepareMediaFragmentURI();
   1580         scheduleEvent(EventTypeNames::durationchange);
   1581         scheduleEvent(EventTypeNames::loadedmetadata);
   1582         if (hasMediaControls())
   1583             mediaControls()->loadedMetadata();
   1584         if (renderer())
   1585             renderer()->updateFromElement();
   1586     }
   1587 
   1588     bool shouldUpdateDisplayState = false;
   1589 
   1590     if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
   1591         m_haveFiredLoadedData = true;
   1592         shouldUpdateDisplayState = true;
   1593         scheduleEvent(EventTypeNames::loadeddata);
   1594         setShouldDelayLoadEvent(false);
   1595         applyMediaFragmentURI();
   1596     }
   1597 
   1598     bool isPotentiallyPlaying = potentiallyPlaying();
   1599     if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) {
   1600         scheduleEvent(EventTypeNames::canplay);
   1601         if (isPotentiallyPlaying)
   1602             scheduleEvent(EventTypeNames::playing);
   1603         shouldUpdateDisplayState = true;
   1604     }
   1605 
   1606     if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) {
   1607         if (oldState <= HAVE_CURRENT_DATA)
   1608             scheduleEvent(EventTypeNames::canplay);
   1609 
   1610         scheduleEvent(EventTypeNames::canplaythrough);
   1611 
   1612         if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
   1613             scheduleEvent(EventTypeNames::playing);
   1614 
   1615         if (m_autoplaying && m_paused && autoplay() && !document().isSandboxed(SandboxAutomaticFeatures) && !userGestureRequiredForRateChange()) {
   1616             m_paused = false;
   1617             invalidateCachedTime();
   1618             scheduleEvent(EventTypeNames::play);
   1619             scheduleEvent(EventTypeNames::playing);
   1620         }
   1621 
   1622         shouldUpdateDisplayState = true;
   1623     }
   1624 
   1625     if (shouldUpdateDisplayState) {
   1626         updateDisplayState();
   1627         if (hasMediaControls()) {
   1628             mediaControls()->refreshClosedCaptionsButtonVisibility();
   1629             mediaControls()->updateStatusDisplay();
   1630         }
   1631     }
   1632 
   1633     updatePlayState();
   1634     updateMediaController();
   1635     if (RuntimeEnabledFeatures::videoTrackEnabled())
   1636         updateActiveTextTrackCues(currentTime());
   1637 }
   1638 
   1639 void HTMLMediaElement::mediaPlayerKeyAdded(const String& keySystem, const String& sessionId)
   1640 {
   1641     MediaKeyEventInit initializer;
   1642     initializer.keySystem = keySystem;
   1643     initializer.sessionId = sessionId;
   1644     initializer.bubbles = false;
   1645     initializer.cancelable = false;
   1646 
   1647     RefPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeyadded, initializer);
   1648     event->setTarget(this);
   1649     m_asyncEventQueue->enqueueEvent(event.release());
   1650 }
   1651 
   1652 void HTMLMediaElement::mediaPlayerKeyError(const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode)
   1653 {
   1654     MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
   1655     switch (errorCode) {
   1656     case MediaPlayerClient::UnknownError:
   1657         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
   1658         break;
   1659     case MediaPlayerClient::ClientError:
   1660         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
   1661         break;
   1662     case MediaPlayerClient::ServiceError:
   1663         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE;
   1664         break;
   1665     case MediaPlayerClient::OutputError:
   1666         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT;
   1667         break;
   1668     case MediaPlayerClient::HardwareChangeError:
   1669         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE;
   1670         break;
   1671     case MediaPlayerClient::DomainError:
   1672         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN;
   1673         break;
   1674     }
   1675 
   1676     MediaKeyEventInit initializer;
   1677     initializer.keySystem = keySystem;
   1678     initializer.sessionId = sessionId;
   1679     initializer.errorCode = MediaKeyError::create(mediaKeyErrorCode);
   1680     initializer.systemCode = systemCode;
   1681     initializer.bubbles = false;
   1682     initializer.cancelable = false;
   1683 
   1684     RefPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeyerror, initializer);
   1685     event->setTarget(this);
   1686     m_asyncEventQueue->enqueueEvent(event.release());
   1687 }
   1688 
   1689 void HTMLMediaElement::mediaPlayerKeyMessage(const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const KURL& defaultURL)
   1690 {
   1691     MediaKeyEventInit initializer;
   1692     initializer.keySystem = keySystem;
   1693     initializer.sessionId = sessionId;
   1694     initializer.message = Uint8Array::create(message, messageLength);
   1695     initializer.defaultURL = defaultURL;
   1696     initializer.bubbles = false;
   1697     initializer.cancelable = false;
   1698 
   1699     RefPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitkeymessage, initializer);
   1700     event->setTarget(this);
   1701     m_asyncEventQueue->enqueueEvent(event.release());
   1702 }
   1703 
   1704 bool HTMLMediaElement::mediaPlayerKeyNeeded(const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength)
   1705 {
   1706     if (!hasEventListeners(EventTypeNames::webkitneedkey)) {
   1707         m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED);
   1708         scheduleEvent(EventTypeNames::error);
   1709         return false;
   1710     }
   1711 
   1712     MediaKeyEventInit initializer;
   1713     initializer.keySystem = keySystem;
   1714     initializer.sessionId = sessionId;
   1715     initializer.initData = Uint8Array::create(initData, initDataLength);
   1716     initializer.bubbles = false;
   1717     initializer.cancelable = false;
   1718 
   1719     RefPtr<Event> event = MediaKeyEvent::create(EventTypeNames::webkitneedkey, initializer);
   1720     event->setTarget(this);
   1721     m_asyncEventQueue->enqueueEvent(event.release());
   1722     return true;
   1723 }
   1724 
   1725 bool HTMLMediaElement::mediaPlayerKeyNeeded(Uint8Array* initData)
   1726 {
   1727     if (!hasEventListeners("webkitneedkey")) {
   1728         m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED);
   1729         scheduleEvent(EventTypeNames::error);
   1730         return false;
   1731     }
   1732 
   1733     MediaKeyNeededEventInit initializer;
   1734     initializer.initData = initData;
   1735     initializer.bubbles = false;
   1736     initializer.cancelable = false;
   1737 
   1738     RefPtr<Event> event = MediaKeyNeededEvent::create(EventTypeNames::webkitneedkey, initializer);
   1739     event->setTarget(this);
   1740     m_asyncEventQueue->enqueueEvent(event.release());
   1741 
   1742     return true;
   1743 }
   1744 
   1745 void HTMLMediaElement::setMediaKeys(MediaKeys* mediaKeys)
   1746 {
   1747     if (m_mediaKeys == mediaKeys)
   1748         return;
   1749 
   1750     if (m_mediaKeys)
   1751         m_mediaKeys->setMediaElement(0);
   1752     m_mediaKeys = mediaKeys;
   1753     if (m_mediaKeys)
   1754         m_mediaKeys->setMediaElement(this);
   1755 }
   1756 
   1757 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
   1758 {
   1759     ASSERT(m_player);
   1760     if (m_networkState != NETWORK_LOADING)
   1761         return;
   1762 
   1763     double time = WTF::currentTime();
   1764     double timedelta = time - m_previousProgressTime;
   1765 
   1766     if (m_player->didLoadingProgress()) {
   1767         scheduleEvent(EventTypeNames::progress);
   1768         m_previousProgressTime = time;
   1769         m_sentStalledEvent = false;
   1770         if (renderer())
   1771             renderer()->updateFromElement();
   1772         if (hasMediaControls())
   1773             mediaControls()->bufferingProgressed();
   1774     } else if (timedelta > 3.0 && !m_sentStalledEvent) {
   1775         scheduleEvent(EventTypeNames::stalled);
   1776         m_sentStalledEvent = true;
   1777         setShouldDelayLoadEvent(false);
   1778     }
   1779 }
   1780 
   1781 void HTMLMediaElement::addPlayedRange(double start, double end)
   1782 {
   1783     WTF_LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end);
   1784     if (!m_playedTimeRanges)
   1785         m_playedTimeRanges = TimeRanges::create();
   1786     m_playedTimeRanges->add(start, end);
   1787 }
   1788 
   1789 bool HTMLMediaElement::supportsSave() const
   1790 {
   1791     return m_player ? m_player->supportsSave() : false;
   1792 }
   1793 
   1794 void HTMLMediaElement::prepareToPlay()
   1795 {
   1796     WTF_LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this);
   1797     if (m_havePreparedToPlay)
   1798         return;
   1799     m_havePreparedToPlay = true;
   1800     m_player->prepareToPlay();
   1801 }
   1802 
   1803 void HTMLMediaElement::seek(double time, ExceptionState& exceptionState)
   1804 {
   1805     WTF_LOG(Media, "HTMLMediaElement::seek(%f)", time);
   1806 
   1807     // 4.8.10.9 Seeking
   1808 
   1809     // 1 - If the media element's readyState is HAVE_NOTHING, then raise an InvalidStateError exception.
   1810     if (m_readyState == HAVE_NOTHING || !m_player) {
   1811         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
   1812         return;
   1813     }
   1814 
   1815     // If the media engine has been told to postpone loading data, let it go ahead now.
   1816     if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
   1817         prepareToPlay();
   1818 
   1819     // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
   1820     refreshCachedTime();
   1821     double now = currentTime();
   1822 
   1823     // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
   1824     // already running. Abort that other instance of the algorithm without waiting for the step that
   1825     // it is running to complete.
   1826     // Nothing specific to be done here.
   1827 
   1828     // 3 - Set the seeking IDL attribute to true.
   1829     // The flag will be cleared when the engine tells us the time has actually changed.
   1830     m_seeking = true;
   1831 
   1832     // 5 - If the new playback position is later than the end of the media resource, then let it be the end
   1833     // of the media resource instead.
   1834     time = min(time, duration());
   1835 
   1836     // 6 - If the new playback position is less than the earliest possible position, let it be that position instead.
   1837     time = max(time, 0.0);
   1838 
   1839     // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
   1840     // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
   1841     // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
   1842     // not generate a timechanged callback. This means m_seeking will never be cleared and we will never
   1843     // fire a 'seeked' event.
   1844 #if !LOG_DISABLED
   1845     double mediaTime = m_player->mediaTimeForTimeValue(time);
   1846     if (time != mediaTime)
   1847         WTF_LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime);
   1848 #endif
   1849     time = m_player->mediaTimeForTimeValue(time);
   1850 
   1851     // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the
   1852     // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute
   1853     // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
   1854     // attribute then set the seeking IDL attribute to false and abort these steps.
   1855     RefPtr<TimeRanges> seekableRanges = seekable();
   1856 
   1857     // Short circuit seeking to the current time by just firing the events if no seek is required.
   1858     // Don't skip calling the media engine if we are in poster mode because a seek should always
   1859     // cancel poster display.
   1860     bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster);
   1861 
   1862     // Always notify the media engine of a seek if the source is not closed. This ensures that the source is
   1863     // always in a flushed state when the 'seeking' event fires.
   1864     if (m_mediaSource && m_mediaSource->isClosed())
   1865         noSeekRequired = false;
   1866 
   1867     if (noSeekRequired) {
   1868         if (time == now) {
   1869             scheduleEvent(EventTypeNames::seeking);
   1870             // FIXME: There must be a stable state before timeupdate+seeked are dispatched and seeking
   1871             // is reset to false. See http://crbug.com/266631
   1872             scheduleTimeupdateEvent(false);
   1873             scheduleEvent(EventTypeNames::seeked);
   1874         }
   1875         m_seeking = false;
   1876         return;
   1877     }
   1878     time = seekableRanges->nearest(time);
   1879 
   1880     if (m_playing) {
   1881         if (m_lastSeekTime < now)
   1882             addPlayedRange(m_lastSeekTime, now);
   1883     }
   1884     m_lastSeekTime = time;
   1885     m_sentEndEvent = false;
   1886 
   1887     // 8 - Queue a task to fire a simple event named seeking at the element.
   1888     scheduleEvent(EventTypeNames::seeking);
   1889 
   1890     // 9 - Set the current playback position to the given new playback position
   1891     m_player->seek(time);
   1892 
   1893     // 10-14 are handled, if necessary, when the engine signals a readystate change or otherwise
   1894     // satisfies seek completion and signals a time change.
   1895 }
   1896 
   1897 void HTMLMediaElement::finishSeek()
   1898 {
   1899     WTF_LOG(Media, "HTMLMediaElement::finishSeek");
   1900 
   1901     // 4.8.10.9 Seeking completion
   1902     // 12 - Set the seeking IDL attribute to false.
   1903     m_seeking = false;
   1904 
   1905     // 13 - Queue a task to fire a simple event named timeupdate at the element.
   1906     scheduleTimeupdateEvent(false);
   1907 
   1908     // 14 - Queue a task to fire a simple event named seeked at the element.
   1909     scheduleEvent(EventTypeNames::seeked);
   1910 
   1911     setDisplayMode(Video);
   1912 }
   1913 
   1914 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
   1915 {
   1916     return m_readyState;
   1917 }
   1918 
   1919 bool HTMLMediaElement::hasAudio() const
   1920 {
   1921     return m_player ? m_player->hasAudio() : false;
   1922 }
   1923 
   1924 bool HTMLMediaElement::seeking() const
   1925 {
   1926     return m_seeking;
   1927 }
   1928 
   1929 void HTMLMediaElement::refreshCachedTime() const
   1930 {
   1931     m_cachedTime = m_player->currentTime();
   1932     m_cachedTimeWallClockUpdateTime = WTF::currentTime();
   1933 }
   1934 
   1935 void HTMLMediaElement::invalidateCachedTime()
   1936 {
   1937     WTF_LOG(Media, "HTMLMediaElement::invalidateCachedTime");
   1938 
   1939     // Don't try to cache movie time when playback first starts as the time reported by the engine
   1940     // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
   1941     // too early.
   1942     static const double minimumTimePlayingBeforeCacheSnapshot = 0.5;
   1943 
   1944     m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot;
   1945     m_cachedTime = MediaPlayer::invalidTime();
   1946 }
   1947 
   1948 // playback state
   1949 double HTMLMediaElement::currentTime() const
   1950 {
   1951 #if LOG_CACHED_TIME_WARNINGS
   1952     static const double minCachedDeltaForWarning = 0.01;
   1953 #endif
   1954 
   1955     if (!m_player)
   1956         return 0;
   1957 
   1958     if (m_seeking) {
   1959         WTF_LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime);
   1960         return m_lastSeekTime;
   1961     }
   1962 
   1963     if (m_cachedTime != MediaPlayer::invalidTime() && m_paused) {
   1964 #if LOG_CACHED_TIME_WARNINGS
   1965         double delta = m_cachedTime - m_player->currentTime();
   1966         if (delta > minCachedDeltaForWarning)
   1967             WTF_LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta);
   1968 #endif
   1969         return m_cachedTime;
   1970     }
   1971 
   1972     refreshCachedTime();
   1973 
   1974     return m_cachedTime;
   1975 }
   1976 
   1977 void HTMLMediaElement::setCurrentTime(double time, ExceptionState& exceptionState)
   1978 {
   1979     if (m_mediaController) {
   1980         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
   1981         return;
   1982     }
   1983     seek(time, exceptionState);
   1984 }
   1985 
   1986 double HTMLMediaElement::duration() const
   1987 {
   1988     if (!m_player || m_readyState < HAVE_METADATA)
   1989         return numeric_limits<double>::quiet_NaN();
   1990 
   1991     // FIXME: Refactor so m_duration is kept current (in both MSE and
   1992     // non-MSE cases) once we have transitioned from HAVE_NOTHING ->
   1993     // HAVE_METADATA. Currently, m_duration may be out of date for at least MSE
   1994     // case because MediaSourceBase and SourceBuffer do not notify the element
   1995     // directly upon duration changes caused by endOfStream, remove, or append
   1996     // operations; rather the notification is triggered by the WebMediaPlayer
   1997     // implementation observing that the underlying engine has updated duration
   1998     // and notifying the element to consult its MediaSource for current
   1999     // duration. See http://crbug.com/266644
   2000 
   2001     if (m_mediaSource)
   2002         return m_mediaSource->duration();
   2003 
   2004     return m_player->duration();
   2005 }
   2006 
   2007 bool HTMLMediaElement::paused() const
   2008 {
   2009     return m_paused;
   2010 }
   2011 
   2012 double HTMLMediaElement::defaultPlaybackRate() const
   2013 {
   2014     return m_defaultPlaybackRate;
   2015 }
   2016 
   2017 void HTMLMediaElement::setDefaultPlaybackRate(double rate)
   2018 {
   2019     if (m_defaultPlaybackRate != rate) {
   2020         m_defaultPlaybackRate = rate;
   2021         scheduleEvent(EventTypeNames::ratechange);
   2022     }
   2023 }
   2024 
   2025 double HTMLMediaElement::playbackRate() const
   2026 {
   2027     return m_playbackRate;
   2028 }
   2029 
   2030 void HTMLMediaElement::setPlaybackRate(double rate)
   2031 {
   2032     WTF_LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate);
   2033 
   2034     if (m_playbackRate != rate) {
   2035         m_playbackRate = rate;
   2036         invalidateCachedTime();
   2037         scheduleEvent(EventTypeNames::ratechange);
   2038     }
   2039 
   2040     if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController)
   2041         m_player->setRate(rate);
   2042 }
   2043 
   2044 void HTMLMediaElement::updatePlaybackRate()
   2045 {
   2046     double effectiveRate = m_mediaController ? m_mediaController->playbackRate() : m_playbackRate;
   2047     if (m_player && potentiallyPlaying() && m_player->rate() != effectiveRate)
   2048         m_player->setRate(effectiveRate);
   2049 }
   2050 
   2051 bool HTMLMediaElement::ended() const
   2052 {
   2053     // 4.8.10.8 Playing the media resource
   2054     // The ended attribute must return true if the media element has ended
   2055     // playback and the direction of playback is forwards, and false otherwise.
   2056     return endedPlayback() && m_playbackRate > 0;
   2057 }
   2058 
   2059 bool HTMLMediaElement::autoplay() const
   2060 {
   2061     return fastHasAttribute(autoplayAttr);
   2062 }
   2063 
   2064 String HTMLMediaElement::preload() const
   2065 {
   2066     switch (m_preload) {
   2067     case MediaPlayer::None:
   2068         return "none";
   2069         break;
   2070     case MediaPlayer::MetaData:
   2071         return "metadata";
   2072         break;
   2073     case MediaPlayer::Auto:
   2074         return "auto";
   2075         break;
   2076     }
   2077 
   2078     ASSERT_NOT_REACHED();
   2079     return String();
   2080 }
   2081 
   2082 void HTMLMediaElement::setPreload(const String& preload)
   2083 {
   2084     WTF_LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data());
   2085     setAttribute(preloadAttr, preload);
   2086 }
   2087 
   2088 void HTMLMediaElement::play()
   2089 {
   2090     WTF_LOG(Media, "HTMLMediaElement::play()");
   2091 
   2092     if (userGestureRequiredForRateChange() && !UserGestureIndicator::processingUserGesture())
   2093         return;
   2094     if (UserGestureIndicator::processingUserGesture())
   2095         removeBehaviorsRestrictionsAfterFirstUserGesture();
   2096 
   2097     playInternal();
   2098 }
   2099 
   2100 void HTMLMediaElement::playInternal()
   2101 {
   2102     WTF_LOG(Media, "HTMLMediaElement::playInternal");
   2103 
   2104     // 4.8.10.9. Playing the media resource
   2105     if (!m_player || m_networkState == NETWORK_EMPTY)
   2106         scheduleDelayedAction(LoadMediaResource);
   2107 
   2108     if (endedPlayback())
   2109         seek(0, IGNORE_EXCEPTION);
   2110 
   2111     if (m_mediaController)
   2112         m_mediaController->bringElementUpToSpeed(this);
   2113 
   2114     if (m_paused) {
   2115         m_paused = false;
   2116         invalidateCachedTime();
   2117         scheduleEvent(EventTypeNames::play);
   2118 
   2119         if (m_readyState <= HAVE_CURRENT_DATA)
   2120             scheduleEvent(EventTypeNames::waiting);
   2121         else if (m_readyState >= HAVE_FUTURE_DATA)
   2122             scheduleEvent(EventTypeNames::playing);
   2123     }
   2124     m_autoplaying = false;
   2125 
   2126     updatePlayState();
   2127     updateMediaController();
   2128 }
   2129 
   2130 void HTMLMediaElement::pause()
   2131 {
   2132     WTF_LOG(Media, "HTMLMediaElement::pause()");
   2133 
   2134     if (userGestureRequiredForRateChange() && !UserGestureIndicator::processingUserGesture())
   2135         return;
   2136 
   2137     pauseInternal();
   2138 }
   2139 
   2140 
   2141 void HTMLMediaElement::pauseInternal()
   2142 {
   2143     WTF_LOG(Media, "HTMLMediaElement::pauseInternal");
   2144 
   2145     // 4.8.10.9. Playing the media resource
   2146     if (!m_player || m_networkState == NETWORK_EMPTY)
   2147         scheduleDelayedAction(LoadMediaResource);
   2148 
   2149     m_autoplaying = false;
   2150 
   2151     if (!m_paused) {
   2152         m_paused = true;
   2153         scheduleTimeupdateEvent(false);
   2154         scheduleEvent(EventTypeNames::pause);
   2155     }
   2156 
   2157     updatePlayState();
   2158 }
   2159 
   2160 void HTMLMediaElement::closeMediaSource()
   2161 {
   2162     if (!m_mediaSource)
   2163         return;
   2164 
   2165     m_mediaSource->close();
   2166     m_mediaSource = 0;
   2167 }
   2168 
   2169 void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionState& exceptionState)
   2170 {
   2171     if (keySystem.isEmpty()) {
   2172         exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
   2173         return;
   2174     }
   2175 
   2176     if (!m_player) {
   2177         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
   2178         return;
   2179     }
   2180 
   2181     const unsigned char* initDataPointer = 0;
   2182     unsigned initDataLength = 0;
   2183     if (initData) {
   2184         initDataPointer = initData->data();
   2185         initDataLength = initData->length();
   2186     }
   2187 
   2188     MediaPlayer::MediaKeyException result = m_player->generateKeyRequest(keySystem, initDataPointer, initDataLength);
   2189     throwExceptionForMediaKeyException(result, exceptionState);
   2190 }
   2191 
   2192 void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, ExceptionState& exceptionState)
   2193 {
   2194     webkitGenerateKeyRequest(keySystem, Uint8Array::create(0), exceptionState);
   2195 }
   2196 
   2197 void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionState& exceptionState)
   2198 {
   2199     if (keySystem.isEmpty()) {
   2200         exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
   2201         return;
   2202     }
   2203 
   2204     if (!key) {
   2205         exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
   2206         return;
   2207     }
   2208 
   2209     if (!key->length()) {
   2210         exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
   2211         return;
   2212     }
   2213 
   2214     if (!m_player) {
   2215         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
   2216         return;
   2217     }
   2218 
   2219     const unsigned char* initDataPointer = 0;
   2220     unsigned initDataLength = 0;
   2221     if (initData) {
   2222         initDataPointer = initData->data();
   2223         initDataLength = initData->length();
   2224     }
   2225 
   2226     MediaPlayer::MediaKeyException result = m_player->addKey(keySystem, key->data(), key->length(), initDataPointer, initDataLength, sessionId);
   2227     throwExceptionForMediaKeyException(result, exceptionState);
   2228 }
   2229 
   2230 void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, ExceptionState& exceptionState)
   2231 {
   2232     webkitAddKey(keySystem, key, Uint8Array::create(0), String(), exceptionState);
   2233 }
   2234 
   2235 void HTMLMediaElement::webkitCancelKeyRequest(const String& keySystem, const String& sessionId, ExceptionState& exceptionState)
   2236 {
   2237     if (keySystem.isEmpty()) {
   2238         exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
   2239         return;
   2240     }
   2241 
   2242     if (!m_player) {
   2243         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
   2244         return;
   2245     }
   2246 
   2247     MediaPlayer::MediaKeyException result = m_player->cancelKeyRequest(keySystem, sessionId);
   2248     throwExceptionForMediaKeyException(result, exceptionState);
   2249 }
   2250 
   2251 bool HTMLMediaElement::loop() const
   2252 {
   2253     return fastHasAttribute(loopAttr);
   2254 }
   2255 
   2256 void HTMLMediaElement::setLoop(bool b)
   2257 {
   2258     WTF_LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b));
   2259     setBooleanAttribute(loopAttr, b);
   2260 }
   2261 
   2262 bool HTMLMediaElement::controls() const
   2263 {
   2264     Frame* frame = document().frame();
   2265 
   2266     // always show controls when scripting is disabled
   2267     if (frame && !frame->script().canExecuteScripts(NotAboutToExecuteScript))
   2268         return true;
   2269 
   2270     // Always show controls when in full screen mode.
   2271     if (isFullscreen())
   2272         return true;
   2273 
   2274     return fastHasAttribute(controlsAttr);
   2275 }
   2276 
   2277 void HTMLMediaElement::setControls(bool b)
   2278 {
   2279     WTF_LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b));
   2280     setBooleanAttribute(controlsAttr, b);
   2281 }
   2282 
   2283 double HTMLMediaElement::volume() const
   2284 {
   2285     return m_volume;
   2286 }
   2287 
   2288 void HTMLMediaElement::setVolume(double vol, ExceptionState& exceptionState)
   2289 {
   2290     WTF_LOG(Media, "HTMLMediaElement::setVolume(%f)", vol);
   2291 
   2292     if (vol < 0.0f || vol > 1.0f) {
   2293         exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
   2294         return;
   2295     }
   2296 
   2297     if (m_volume != vol) {
   2298         m_volume = vol;
   2299         updateVolume();
   2300         scheduleEvent(EventTypeNames::volumechange);
   2301     }
   2302 }
   2303 
   2304 bool HTMLMediaElement::muted() const
   2305 {
   2306     return m_muted;
   2307 }
   2308 
   2309 void HTMLMediaElement::setMuted(bool muted)
   2310 {
   2311     WTF_LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted));
   2312 
   2313     if (m_muted != muted) {
   2314         m_muted = muted;
   2315         if (m_player) {
   2316             m_player->setMuted(m_muted);
   2317             if (hasMediaControls())
   2318                 mediaControls()->changedMute();
   2319         }
   2320         scheduleEvent(EventTypeNames::volumechange);
   2321     }
   2322 }
   2323 
   2324 void HTMLMediaElement::togglePlayState()
   2325 {
   2326     WTF_LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay()));
   2327 
   2328     // We can safely call the internal play/pause methods, which don't check restrictions, because
   2329     // this method is only called from the built-in media controller
   2330     if (canPlay()) {
   2331         updatePlaybackRate();
   2332         playInternal();
   2333     } else
   2334         pauseInternal();
   2335 }
   2336 
   2337 void HTMLMediaElement::beginScrubbing()
   2338 {
   2339     WTF_LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused()));
   2340 
   2341     if (!paused()) {
   2342         if (ended()) {
   2343             // Because a media element stays in non-paused state when it reaches end, playback resumes
   2344             // when the slider is dragged from the end to another position unless we pause first. Do
   2345             // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
   2346             pause();
   2347         } else {
   2348             // Not at the end but we still want to pause playback so the media engine doesn't try to
   2349             // continue playing during scrubbing. Pause without generating an event as we will
   2350             // unpause after scrubbing finishes.
   2351             setPausedInternal(true);
   2352         }
   2353     }
   2354 }
   2355 
   2356 void HTMLMediaElement::endScrubbing()
   2357 {
   2358     WTF_LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal));
   2359 
   2360     if (m_pausedInternal)
   2361         setPausedInternal(false);
   2362 }
   2363 
   2364 // The spec says to fire periodic timeupdate events (those sent while playing) every
   2365 // "15 to 250ms", we choose the slowest frequency
   2366 static const double maxTimeupdateEventFrequency = 0.25;
   2367 
   2368 void HTMLMediaElement::startPlaybackProgressTimer()
   2369 {
   2370     if (m_playbackProgressTimer.isActive())
   2371         return;
   2372 
   2373     m_previousProgressTime = WTF::currentTime();
   2374     m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
   2375 }
   2376 
   2377 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
   2378 {
   2379     ASSERT(m_player);
   2380 
   2381     if (m_fragmentEndTime != MediaPlayer::invalidTime() && currentTime() >= m_fragmentEndTime && m_playbackRate > 0) {
   2382         m_fragmentEndTime = MediaPlayer::invalidTime();
   2383         if (!m_mediaController && !m_paused) {
   2384             // changes paused to true and fires a simple event named pause at the media element.
   2385             pauseInternal();
   2386         }
   2387     }
   2388 
   2389     if (!m_seeking)
   2390         scheduleTimeupdateEvent(true);
   2391 
   2392     if (!m_playbackRate)
   2393         return;
   2394 
   2395     if (!m_paused && hasMediaControls())
   2396         mediaControls()->playbackProgressed();
   2397 
   2398     if (RuntimeEnabledFeatures::videoTrackEnabled())
   2399         updateActiveTextTrackCues(currentTime());
   2400 }
   2401 
   2402 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
   2403 {
   2404     double now = WTF::currentTime();
   2405     double timedelta = now - m_lastTimeUpdateEventWallTime;
   2406 
   2407     // throttle the periodic events
   2408     if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
   2409         return;
   2410 
   2411     // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
   2412     // event at a given time so filter here
   2413     double movieTime = currentTime();
   2414     if (movieTime != m_lastTimeUpdateEventMovieTime) {
   2415         scheduleEvent(EventTypeNames::timeupdate);
   2416         m_lastTimeUpdateEventWallTime = now;
   2417         m_lastTimeUpdateEventMovieTime = movieTime;
   2418     }
   2419 }
   2420 
   2421 bool HTMLMediaElement::canPlay() const
   2422 {
   2423     return paused() || ended() || m_readyState < HAVE_METADATA;
   2424 }
   2425 
   2426 double HTMLMediaElement::percentLoaded() const
   2427 {
   2428     if (!m_player)
   2429         return 0;
   2430     double duration = m_player->duration();
   2431 
   2432     if (!duration || std::isinf(duration))
   2433         return 0;
   2434 
   2435     double buffered = 0;
   2436     RefPtr<TimeRanges> timeRanges = m_player->buffered();
   2437     for (unsigned i = 0; i < timeRanges->length(); ++i) {
   2438         double start = timeRanges->start(i, IGNORE_EXCEPTION);
   2439         double end = timeRanges->end(i, IGNORE_EXCEPTION);
   2440         buffered += end - start;
   2441     }
   2442     return buffered / duration;
   2443 }
   2444 
   2445 void HTMLMediaElement::mediaPlayerDidAddTrack(WebInbandTextTrack* webTrack)
   2446 {
   2447     if (!RuntimeEnabledFeatures::videoTrackEnabled())
   2448         return;
   2449 
   2450     // 4.8.10.12.2 Sourcing in-band text tracks
   2451     // 1. Associate the relevant data with a new text track and its corresponding new TextTrack object.
   2452     RefPtr<InbandTextTrack> textTrack = InbandTextTrack::create(document(), this, webTrack);
   2453 
   2454     // 2. Set the new text track's kind, label, and language based on the semantics of the relevant data,
   2455     // as defined by the relevant specification. If there is no label in that data, then the label must
   2456     // be set to the empty string.
   2457     // 3. Associate the text track list of cues with the rules for updating the text track rendering appropriate
   2458     // for the format in question.
   2459     // 4. If the new text track's kind is metadata, then set the text track in-band metadata track dispatch type
   2460     // as follows, based on the type of the media resource:
   2461     // 5. Populate the new text track's list of cues with the cues parsed so far, folllowing the guidelines for exposing
   2462     // cues, and begin updating it dynamically as necessary.
   2463     //   - Thess are all done by the media engine.
   2464 
   2465     // 6. Set the new text track's readiness state to loaded.
   2466     textTrack->setReadinessState(TextTrack::Loaded);
   2467 
   2468     // 7. Set the new text track's mode to the mode consistent with the user's preferences and the requirements of
   2469     // the relevant specification for the data.
   2470     //  - This will happen in configureTextTracks()
   2471     scheduleDelayedAction(LoadTextTrackResource);
   2472 
   2473     // 8. Add the new text track to the media element's list of text tracks.
   2474     // 9. Fire an event with the name addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent
   2475     // interface, with the track attribute initialized to the text track's TextTrack object, at the media element's
   2476     // textTracks attribute's TextTrackList object.
   2477     addTrack(textTrack.get());
   2478 }
   2479 
   2480 void HTMLMediaElement::mediaPlayerDidRemoveTrack(WebInbandTextTrack* webTrack)
   2481 {
   2482     if (!RuntimeEnabledFeatures::videoTrackEnabled())
   2483         return;
   2484 
   2485     if (!m_textTracks)
   2486         return;
   2487 
   2488     // This cast is safe because we created the InbandTextTrack with the WebInbandTextTrack
   2489     // passed to mediaPlayerDidAddTrack.
   2490     RefPtr<InbandTextTrack> textTrack = static_cast<InbandTextTrack*>(webTrack->client());
   2491     if (!textTrack)
   2492         return;
   2493 
   2494     removeTrack(textTrack.get());
   2495 }
   2496 
   2497 void HTMLMediaElement::closeCaptionTracksChanged()
   2498 {
   2499     if (hasMediaControls())
   2500         mediaControls()->closedCaptionTracksChanged();
   2501 }
   2502 
   2503 void HTMLMediaElement::addTrack(TextTrack* track)
   2504 {
   2505     textTracks()->append(track);
   2506 
   2507     closeCaptionTracksChanged();
   2508 }
   2509 
   2510 void HTMLMediaElement::removeTrack(TextTrack* track)
   2511 {
   2512     TrackDisplayUpdateScope scope(this);
   2513     TextTrackCueList* cues = track->cues();
   2514     if (cues)
   2515         textTrackRemoveCues(track, cues);
   2516     m_textTracks->remove(track);
   2517 
   2518     closeCaptionTracksChanged();
   2519 }
   2520 
   2521 void HTMLMediaElement::removeAllInbandTracks()
   2522 {
   2523     if (!m_textTracks)
   2524         return;
   2525 
   2526     TrackDisplayUpdateScope scope(this);
   2527     for (int i = m_textTracks->length() - 1; i >= 0; --i) {
   2528         TextTrack* track = m_textTracks->item(i);
   2529 
   2530         if (track->trackType() == TextTrack::InBand)
   2531             removeTrack(track);
   2532     }
   2533 }
   2534 
   2535 PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const String& kind, const String& label, const String& language, ExceptionState& exceptionState)
   2536 {
   2537     ASSERT(RuntimeEnabledFeatures::videoTrackEnabled());
   2538 
   2539     // 4.8.10.12.4 Text track API
   2540     // The addTextTrack(kind, label, language) method of media elements, when invoked, must run the following steps:
   2541 
   2542     // 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps
   2543     if (!TextTrack::isValidKindKeyword(kind)) {
   2544         exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
   2545         return 0;
   2546     }
   2547 
   2548     // 2. If the label argument was omitted, let label be the empty string.
   2549     // 3. If the language argument was omitted, let language be the empty string.
   2550     // 4. Create a new TextTrack object.
   2551 
   2552     // 5. Create a new text track corresponding to the new object, and set its text track kind to kind, its text
   2553     // track label to label, its text track language to language...
   2554     RefPtr<TextTrack> textTrack = TextTrack::create(document(), this, kind, label, language);
   2555 
   2556     // Note, due to side effects when changing track parameters, we have to
   2557     // first append the track to the text track list.
   2558 
   2559     // 6. Add the new text track to the media element's list of text tracks.
   2560     addTrack(textTrack.get());
   2561 
   2562     // ... its text track readiness state to the text track loaded state ...
   2563     textTrack->setReadinessState(TextTrack::Loaded);
   2564 
   2565     // ... its text track mode to the text track hidden mode, and its text track list of cues to an empty list ...
   2566     textTrack->setMode(TextTrack::hiddenKeyword());
   2567 
   2568     return textTrack.release();
   2569 }
   2570 
   2571 TextTrackList* HTMLMediaElement::textTracks()
   2572 {
   2573     ASSERT(RuntimeEnabledFeatures::videoTrackEnabled());
   2574 
   2575     if (!m_textTracks)
   2576         m_textTracks = TextTrackList::create(this);
   2577 
   2578     return m_textTracks.get();
   2579 }
   2580 
   2581 void HTMLMediaElement::didAddTrack(HTMLTrackElement* trackElement)
   2582 {
   2583     ASSERT(trackElement->hasTagName(trackTag));
   2584 
   2585     if (!RuntimeEnabledFeatures::videoTrackEnabled())
   2586         return;
   2587 
   2588     // 4.8.10.12.3 Sourcing out-of-band text tracks
   2589     // When a track element's parent element changes and the new parent is a media element,
   2590     // then the user agent must add the track element's corresponding text track to the
   2591     // media element's list of text tracks ... [continues in TextTrackList::append]
   2592     RefPtr<TextTrack> textTrack = trackElement->track();
   2593     if (!textTrack)
   2594         return;
   2595 
   2596     addTrack(textTrack.get());
   2597 
   2598     // Do not schedule the track loading until parsing finishes so we don't start before all tracks
   2599     // in the markup have been added.
   2600     if (!m_parsingInProgress)
   2601         scheduleDelayedAction(LoadTextTrackResource);
   2602 
   2603     if (hasMediaControls())
   2604         mediaControls()->closedCaptionTracksChanged();
   2605 }
   2606 
   2607 void HTMLMediaElement::didRemoveTrack(HTMLTrackElement* trackElement)
   2608 {
   2609     ASSERT(trackElement->hasTagName(trackTag));
   2610 
   2611     if (!RuntimeEnabledFeatures::videoTrackEnabled())
   2612         return;
   2613 
   2614 #if !LOG_DISABLED
   2615     if (trackElement->hasTagName(trackTag)) {
   2616         KURL url = trackElement->getNonEmptyURLAttribute(srcAttr);
   2617         WTF_LOG(Media, "HTMLMediaElement::didRemoveTrack - 'src' is %s", urlForLoggingMedia(url).utf8().data());
   2618     }
   2619 #endif
   2620 
   2621     RefPtr<TextTrack> textTrack = trackElement->track();
   2622     if (!textTrack)
   2623         return;
   2624 
   2625     textTrack->setHasBeenConfigured(false);
   2626 
   2627     if (!m_textTracks)
   2628         return;
   2629 
   2630     // 4.8.10.12.3 Sourcing out-of-band text tracks
   2631     // When a track element's parent element changes and the old parent was a media element,
   2632     // then the user agent must remove the track element's corresponding text track from the
   2633     // media element's list of text tracks.
   2634     removeTrack(textTrack.get());
   2635 
   2636     size_t index = m_textTracksWhenResourceSelectionBegan.find(textTrack.get());
   2637     if (index != kNotFound)
   2638         m_textTracksWhenResourceSelectionBegan.remove(index);
   2639 }
   2640 
   2641 static int textTrackLanguageSelectionScore(const TextTrack& track)
   2642 {
   2643     if (track.language().isEmpty())
   2644         return 0;
   2645 
   2646     Vector<String> languages = userPreferredLanguages();
   2647     size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track.language(), languages);
   2648     if (languageMatchIndex >= languages.size())
   2649         return 0;
   2650 
   2651     // Matching a track language is more important than matching track type, so this multiplier must be
   2652     // greater than the maximum value returned by textTrackSelectionScore.
   2653     return (languages.size() - languageMatchIndex) * 10;
   2654 }
   2655 
   2656 static int textTrackSelectionScore(const TextTrack& track, Settings* settings)
   2657 {
   2658     int trackScore = 0;
   2659 
   2660     if (!settings)
   2661         return trackScore;
   2662 
   2663     if (track.kind() != TextTrack::captionsKeyword() && track.kind() != TextTrack::subtitlesKeyword())
   2664         return trackScore;
   2665 
   2666     if (track.kind() == TextTrack::subtitlesKeyword() && settings->shouldDisplaySubtitles())
   2667         trackScore = 1;
   2668     else if (track.kind() == TextTrack::captionsKeyword() && settings->shouldDisplayCaptions())
   2669         trackScore = 1;
   2670 
   2671     return trackScore + textTrackLanguageSelectionScore(track);
   2672 }
   2673 
   2674 void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
   2675 {
   2676     ASSERT(group.tracks.size());
   2677 
   2678     WTF_LOG(Media, "HTMLMediaElement::configureTextTrackGroup(%d)", group.kind);
   2679 
   2680     Settings* settings = document().settings();
   2681 
   2682     // First, find the track in the group that should be enabled (if any).
   2683     Vector<RefPtr<TextTrack> > currentlyEnabledTracks;
   2684     RefPtr<TextTrack> trackToEnable;
   2685     RefPtr<TextTrack> defaultTrack;
   2686     RefPtr<TextTrack> fallbackTrack;
   2687     int highestTrackScore = 0;
   2688     for (size_t i = 0; i < group.tracks.size(); ++i) {
   2689         RefPtr<TextTrack> textTrack = group.tracks[i];
   2690 
   2691         if (m_processingPreferenceChange && textTrack->mode() == TextTrack::showingKeyword())
   2692             currentlyEnabledTracks.append(textTrack);
   2693 
   2694         int trackScore = textTrackSelectionScore(*textTrack, settings);
   2695         if (trackScore) {
   2696             // * If the text track kind is { [subtitles or captions] [descriptions] } and the user has indicated an interest in having a
   2697             // track with this text track kind, text track language, and text track label enabled, and there is no
   2698             // other text track in the media element's list of text tracks with a text track kind of either subtitles
   2699             // or captions whose text track mode is showing
   2700             // ...
   2701             // * If the text track kind is chapters and the text track language is one that the user agent has reason
   2702             // to believe is appropriate for the user, and there is no other text track in the media element's list of
   2703             // text tracks with a text track kind of chapters whose text track mode is showing
   2704             //    Let the text track mode be showing.
   2705             if (trackScore > highestTrackScore) {
   2706                 highestTrackScore = trackScore;
   2707                 trackToEnable = textTrack;
   2708             }
   2709 
   2710             if (!defaultTrack && textTrack->isDefault())
   2711                 defaultTrack = textTrack;
   2712             if (!defaultTrack && !fallbackTrack)
   2713                 fallbackTrack = textTrack;
   2714         } else if (!group.visibleTrack && !defaultTrack && textTrack->isDefault()) {
   2715             // * If the track element has a default attribute specified, and there is no other text track in the media
   2716             // element's list of text tracks whose text track mode is showing or showing by default
   2717             //    Let the text track mode be showing by default.
   2718             defaultTrack = textTrack;
   2719         }
   2720     }
   2721 
   2722     if (!trackToEnable && defaultTrack)
   2723         trackToEnable = defaultTrack;
   2724 
   2725     // If no track matches the user's preferred language and non was marked 'default', enable the first track
   2726     // because the user has explicitly stated a preference for this kind of track.
   2727     if (!fallbackTrack && m_closedCaptionsVisible && group.kind == TrackGroup::CaptionsAndSubtitles)
   2728         fallbackTrack = group.tracks[0];
   2729 
   2730     if (!trackToEnable && fallbackTrack)
   2731         trackToEnable = fallbackTrack;
   2732 
   2733     if (currentlyEnabledTracks.size()) {
   2734         for (size_t i = 0; i < currentlyEnabledTracks.size(); ++i) {
   2735             RefPtr<TextTrack> textTrack = currentlyEnabledTracks[i];
   2736             if (textTrack != trackToEnable)
   2737                 textTrack->setMode(TextTrack::disabledKeyword());
   2738         }
   2739     }
   2740 
   2741     if (trackToEnable)
   2742         trackToEnable->setMode(TextTrack::showingKeyword());
   2743 }
   2744 
   2745 void HTMLMediaElement::configureTextTracks()
   2746 {
   2747     TrackGroup captionAndSubtitleTracks(TrackGroup::CaptionsAndSubtitles);
   2748     TrackGroup descriptionTracks(TrackGroup::Description);
   2749     TrackGroup chapterTracks(TrackGroup::Chapter);
   2750     TrackGroup metadataTracks(TrackGroup::Metadata);
   2751     TrackGroup otherTracks(TrackGroup::Other);
   2752 
   2753     if (!m_textTracks)
   2754         return;
   2755 
   2756     for (size_t i = 0; i < m_textTracks->length(); ++i) {
   2757         RefPtr<TextTrack> textTrack = m_textTracks->item(i);
   2758         if (!textTrack)
   2759             continue;
   2760 
   2761         String kind = textTrack->kind();
   2762         TrackGroup* currentGroup;
   2763         if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword())
   2764             currentGroup = &captionAndSubtitleTracks;
   2765         else if (kind == TextTrack::descriptionsKeyword())
   2766             currentGroup = &descriptionTracks;
   2767         else if (kind == TextTrack::chaptersKeyword())
   2768             currentGroup = &chapterTracks;
   2769         else if (kind == TextTrack::metadataKeyword())
   2770             currentGroup = &metadataTracks;
   2771         else
   2772             currentGroup = &otherTracks;
   2773 
   2774         if (!currentGroup->visibleTrack && textTrack->mode() == TextTrack::showingKeyword())
   2775             currentGroup->visibleTrack = textTrack;
   2776         if (!currentGroup->defaultTrack && textTrack->isDefault())
   2777             currentGroup->defaultTrack = textTrack;
   2778 
   2779         // Do not add this track to the group if it has already been automatically configured
   2780         // as we only want to call configureTextTrack once per track so that adding another
   2781         // track after the initial configuration doesn't reconfigure every track - only those
   2782         // that should be changed by the new addition. For example all metadata tracks are
   2783         // disabled by default, and we don't want a track that has been enabled by script
   2784         // to be disabled automatically when a new metadata track is added later.
   2785         if (textTrack->hasBeenConfigured())
   2786             continue;
   2787 
   2788         if (textTrack->language().length())
   2789             currentGroup->hasSrcLang = true;
   2790         currentGroup->tracks.append(textTrack);
   2791     }
   2792 
   2793     if (captionAndSubtitleTracks.tracks.size())
   2794         configureTextTrackGroup(captionAndSubtitleTracks);
   2795     if (descriptionTracks.tracks.size())
   2796         configureTextTrackGroup(descriptionTracks);
   2797     if (chapterTracks.tracks.size())
   2798         configureTextTrackGroup(chapterTracks);
   2799     if (metadataTracks.tracks.size())
   2800         configureTextTrackGroup(metadataTracks);
   2801     if (otherTracks.tracks.size())
   2802         configureTextTrackGroup(otherTracks);
   2803 
   2804     if (hasMediaControls())
   2805         mediaControls()->closedCaptionTracksChanged();
   2806 }
   2807 
   2808 bool HTMLMediaElement::havePotentialSourceChild()
   2809 {
   2810     // Stash the current <source> node and next nodes so we can restore them after checking
   2811     // to see there is another potential.
   2812     RefPtr<HTMLSourceElement> currentSourceNode = m_currentSourceNode;
   2813     RefPtr<Node> nextNode = m_nextChildNodeToConsider;
   2814 
   2815     KURL nextURL = selectNextSourceChild(0, 0, DoNothing);
   2816 
   2817     m_currentSourceNode = currentSourceNode;
   2818     m_nextChildNodeToConsider = nextNode;
   2819 
   2820     return nextURL.isValid();
   2821 }
   2822 
   2823 KURL HTMLMediaElement::selectNextSourceChild(ContentType* contentType, String* keySystem, InvalidURLAction actionIfInvalid)
   2824 {
   2825 #if !LOG_DISABLED
   2826     // Don't log if this was just called to find out if there are any valid <source> elements.
   2827     bool shouldLog = actionIfInvalid != DoNothing;
   2828     if (shouldLog)
   2829         WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild");
   2830 #endif
   2831 
   2832     if (!m_nextChildNodeToConsider) {
   2833 #if !LOG_DISABLED
   2834         if (shouldLog)
   2835             WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\"");
   2836 #endif
   2837         return KURL();
   2838     }
   2839 
   2840     KURL mediaURL;
   2841     Node* node;
   2842     HTMLSourceElement* source = 0;
   2843     String type;
   2844     String system;
   2845     bool lookingForStartNode = m_nextChildNodeToConsider;
   2846     bool canUseSourceElement = false;
   2847     bool okToLoadSourceURL;
   2848 
   2849     NodeVector potentialSourceNodes;
   2850     getChildNodes(*this, potentialSourceNodes);
   2851 
   2852     for (unsigned i = 0; !canUseSourceElement && i < potentialSourceNodes.size(); ++i) {
   2853         node = potentialSourceNodes[i].get();
   2854         if (lookingForStartNode && m_nextChildNodeToConsider != node)
   2855             continue;
   2856         lookingForStartNode = false;
   2857 
   2858         if (!node->hasTagName(sourceTag))
   2859             continue;
   2860         if (node->parentNode() != this)
   2861             continue;
   2862 
   2863         UseCounter::count(document(), UseCounter::SourceElementCandidate);
   2864         source = toHTMLSourceElement(node);
   2865 
   2866         // 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
   2867         mediaURL = source->getNonEmptyURLAttribute(srcAttr);
   2868 #if !LOG_DISABLED
   2869         if (shouldLog)
   2870             WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLoggingMedia(mediaURL).utf8().data());
   2871 #endif
   2872         if (mediaURL.isEmpty())
   2873             goto check_again;
   2874 
   2875         if (source->fastHasAttribute(mediaAttr)) {
   2876             MediaQueryEvaluator screenEval("screen", document().frame(), renderer() ? renderer()->style() : 0);
   2877             RefPtr<MediaQuerySet> media = MediaQuerySet::create(source->media());
   2878 #if !LOG_DISABLED
   2879             if (shouldLog)
   2880                 WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().string().utf8().data());
   2881 #endif
   2882             if (!screenEval.eval(media.get())) {
   2883                 UseCounter::count(document(), UseCounter::SourceElementNonMatchingMedia);
   2884                 goto check_again;
   2885             }
   2886         }
   2887 
   2888         type = source->type();
   2889         // FIXME(82965): Add support for keySystem in <source> and set system from source.
   2890         if (type.isEmpty() && mediaURL.protocolIsData())
   2891             type = mimeTypeFromDataURL(mediaURL);
   2892         if (!type.isEmpty() || !system.isEmpty()) {
   2893 #if !LOG_DISABLED
   2894             if (shouldLog)
   2895                 WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is '%s' - key system is '%s'", type.utf8().data(), system.utf8().data());
   2896 #endif
   2897             if (!supportsType(ContentType(type), system))
   2898                 goto check_again;
   2899         }
   2900 
   2901         // Is it safe to load this url?
   2902         okToLoadSourceURL = isSafeToLoadURL(mediaURL, actionIfInvalid) && dispatchBeforeLoadEvent(mediaURL.string());
   2903 
   2904         // A 'beforeload' event handler can mutate the DOM, so check to see if the source element is still a child node.
   2905         if (node->parentNode() != this) {
   2906             WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild : 'beforeload' removed current element");
   2907             source = 0;
   2908             goto check_again;
   2909         }
   2910 
   2911         if (!okToLoadSourceURL)
   2912             goto check_again;
   2913 
   2914         // Making it this far means the <source> looks reasonable.
   2915         canUseSourceElement = true;
   2916 
   2917 check_again:
   2918         if (!canUseSourceElement && actionIfInvalid == Complain && source)
   2919             source->scheduleErrorEvent();
   2920     }
   2921 
   2922     if (canUseSourceElement) {
   2923         if (contentType)
   2924             *contentType = ContentType(type);
   2925         if (keySystem)
   2926             *keySystem = system;
   2927         m_currentSourceNode = source;
   2928         m_nextChildNodeToConsider = source->nextSibling();
   2929     } else {
   2930         m_currentSourceNode = 0;
   2931         m_nextChildNodeToConsider = 0;
   2932     }
   2933 
   2934 #if !LOG_DISABLED
   2935     if (shouldLog)
   2936         WTF_LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode.get(), canUseSourceElement ? urlForLoggingMedia(mediaURL).utf8().data() : "");
   2937 #endif
   2938     return canUseSourceElement ? mediaURL : KURL();
   2939 }
   2940 
   2941 void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
   2942 {
   2943     WTF_LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source);
   2944 
   2945 #if !LOG_DISABLED
   2946     if (source->hasTagName(sourceTag)) {
   2947         KURL url = source->getNonEmptyURLAttribute(srcAttr);
   2948         WTF_LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLoggingMedia(url).utf8().data());
   2949     }
   2950 #endif
   2951 
   2952     // We should only consider a <source> element when there is not src attribute at all.
   2953     if (fastHasAttribute(srcAttr))
   2954         return;
   2955 
   2956     // 4.8.8 - If a source element is inserted as a child of a media element that has no src
   2957     // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke
   2958     // the media element's resource selection algorithm.
   2959     if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
   2960         scheduleDelayedAction(LoadMediaResource);
   2961         m_nextChildNodeToConsider = source;
   2962         return;
   2963     }
   2964 
   2965     if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) {
   2966         WTF_LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source");
   2967         m_nextChildNodeToConsider = source;
   2968         return;
   2969     }
   2970 
   2971     if (m_nextChildNodeToConsider)
   2972         return;
   2973 
   2974     // 4.8.9.5, resource selection algorithm, source elements section:
   2975     // 21. Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
   2976     // 22. Asynchronously await a stable state...
   2977     // 23. Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case
   2978     // it hasn't been fired yet).
   2979     setShouldDelayLoadEvent(true);
   2980 
   2981     // 24. Set the networkState back to NETWORK_LOADING.
   2982     m_networkState = NETWORK_LOADING;
   2983 
   2984     // 25. Jump back to the find next candidate step above.
   2985     m_nextChildNodeToConsider = source;
   2986     scheduleNextSourceChild();
   2987 }
   2988 
   2989 void HTMLMediaElement::sourceWasRemoved(HTMLSourceElement* source)
   2990 {
   2991     WTF_LOG(Media, "HTMLMediaElement::sourceWasRemoved(%p)", source);
   2992 
   2993 #if !LOG_DISABLED
   2994     if (source->hasTagName(sourceTag)) {
   2995         KURL url = source->getNonEmptyURLAttribute(srcAttr);
   2996         WTF_LOG(Media, "HTMLMediaElement::sourceWasRemoved - 'src' is %s", urlForLoggingMedia(url).utf8().data());
   2997     }
   2998 #endif
   2999 
   3000     if (source != m_currentSourceNode && source != m_nextChildNodeToConsider)
   3001         return;
   3002 
   3003     if (source == m_nextChildNodeToConsider) {
   3004         if (m_currentSourceNode)
   3005             m_nextChildNodeToConsider = m_currentSourceNode->nextSibling();
   3006         WTF_LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider.get());
   3007     } else if (source == m_currentSourceNode) {
   3008         // Clear the current source node pointer, but don't change the movie as the spec says:
   3009         // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already
   3010         // inserted in a video or audio element will have no effect.
   3011         m_currentSourceNode = 0;
   3012         WTF_LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0");
   3013     }
   3014 }
   3015 
   3016 void HTMLMediaElement::mediaPlayerTimeChanged()
   3017 {
   3018     WTF_LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
   3019 
   3020     if (RuntimeEnabledFeatures::videoTrackEnabled())
   3021         updateActiveTextTrackCues(currentTime());
   3022 
   3023     invalidateCachedTime();
   3024 
   3025     // 4.8.10.9 steps 12-14. Needed if no ReadyState change is associated with the seek.
   3026     if (m_seeking && m_readyState >= HAVE_CURRENT_DATA && !m_player->seeking())
   3027         finishSeek();
   3028 
   3029     // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity,
   3030     // it will only queue a 'timeupdate' event if we haven't already posted one at the current
   3031     // movie time.
   3032     scheduleTimeupdateEvent(false);
   3033 
   3034     double now = currentTime();
   3035     double dur = duration();
   3036 
   3037     // When the current playback position reaches the end of the media resource when the direction of
   3038     // playback is forwards, then the user agent must follow these steps:
   3039     if (!std::isnan(dur) && dur && now >= dur && m_playbackRate > 0) {
   3040         // If the media element has a loop attribute specified and does not have a current media controller,
   3041         if (loop() && !m_mediaController) {
   3042             m_sentEndEvent = false;
   3043             //  then seek to the earliest possible position of the media resource and abort these steps.
   3044             seek(0, IGNORE_EXCEPTION);
   3045         } else {
   3046             // If the media element does not have a current media controller, and the media element
   3047             // has still ended playback, and the direction of playback is still forwards, and paused
   3048             // is false,
   3049             if (!m_mediaController && !m_paused) {
   3050                 // changes paused to true and fires a simple event named pause at the media element.
   3051                 m_paused = true;
   3052                 scheduleEvent(EventTypeNames::pause);
   3053             }
   3054             // Queue a task to fire a simple event named ended at the media element.
   3055             if (!m_sentEndEvent) {
   3056                 m_sentEndEvent = true;
   3057                 scheduleEvent(EventTypeNames::ended);
   3058             }
   3059             // If the media element has a current media controller, then report the controller state
   3060             // for the media element's current media controller.
   3061             updateMediaController();
   3062         }
   3063     }
   3064     else
   3065         m_sentEndEvent = false;
   3066 
   3067     updatePlayState();
   3068 }
   3069 
   3070 void HTMLMediaElement::mediaPlayerDurationChanged()
   3071 {
   3072     WTF_LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged");
   3073     durationChanged(duration());
   3074 }
   3075 
   3076 void HTMLMediaElement::durationChanged(double duration)
   3077 {
   3078     WTF_LOG(Media, "HTMLMediaElement::durationChanged(%f)", duration);
   3079 
   3080     // Abort if duration unchanged.
   3081     if (m_duration == duration)
   3082         return;
   3083 
   3084     m_duration = duration;
   3085     scheduleEvent(EventTypeNames::durationchange);
   3086 
   3087     if (hasMediaControls())
   3088         mediaControls()->reset();
   3089     if (renderer())
   3090         renderer()->updateFromElement();
   3091 
   3092     if (currentTime() > duration)
   3093         seek(duration, IGNORE_EXCEPTION);
   3094 }
   3095 
   3096 void HTMLMediaElement::mediaPlayerPlaybackStateChanged()
   3097 {
   3098     WTF_LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged");
   3099 
   3100     if (!m_player || m_pausedInternal)
   3101         return;
   3102 
   3103     if (m_player->paused())
   3104         pauseInternal();
   3105     else
   3106         playInternal();
   3107 }
   3108 
   3109 void HTMLMediaElement::mediaPlayerRequestFullscreen()
   3110 {
   3111     WTF_LOG(Media, "HTMLMediaElement::mediaPlayerRequestFullscreen");
   3112     enterFullscreen();
   3113 }
   3114 
   3115 void HTMLMediaElement::mediaPlayerRequestSeek(double time)
   3116 {
   3117     // The player is the source of this seek request.
   3118     if (m_mediaController) {
   3119         m_mediaController->setCurrentTime(time, IGNORE_EXCEPTION);
   3120         return;
   3121     }
   3122     setCurrentTime(time, IGNORE_EXCEPTION);
   3123 }
   3124 
   3125 // MediaPlayerPresentation methods
   3126 void HTMLMediaElement::mediaPlayerRepaint()
   3127 {
   3128     if (m_webLayer)
   3129         m_webLayer->invalidate();
   3130 
   3131     updateDisplayState();
   3132     if (renderer())
   3133         renderer()->repaint();
   3134 }
   3135 
   3136 void HTMLMediaElement::mediaPlayerSizeChanged()
   3137 {
   3138     WTF_LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged");
   3139 
   3140     if (m_readyState > HAVE_NOTHING)
   3141         scheduleEvent(EventTypeNames::resize);
   3142 
   3143     if (renderer())
   3144         renderer()->updateFromElement();
   3145 }
   3146 
   3147 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
   3148 {
   3149     if (!m_player)
   3150         return TimeRanges::create();
   3151 
   3152     if (m_mediaSource)
   3153         return m_mediaSource->buffered();
   3154 
   3155     return m_player->buffered();
   3156 }
   3157 
   3158 PassRefPtr<TimeRanges> HTMLMediaElement::played()
   3159 {
   3160     if (m_playing) {
   3161         double time = currentTime();
   3162         if (time > m_lastSeekTime)
   3163             addPlayedRange(m_lastSeekTime, time);
   3164     }
   3165 
   3166     if (!m_playedTimeRanges)
   3167         m_playedTimeRanges = TimeRanges::create();
   3168 
   3169     return m_playedTimeRanges->copy();
   3170 }
   3171 
   3172 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
   3173 {
   3174     if (m_player) {
   3175         double maxTimeSeekable = m_player->maxTimeSeekable();
   3176         if (maxTimeSeekable)
   3177             return TimeRanges::create(0, maxTimeSeekable);
   3178     }
   3179     return TimeRanges::create();
   3180 }
   3181 
   3182 bool HTMLMediaElement::potentiallyPlaying() const
   3183 {
   3184     // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing
   3185     // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the
   3186     // checks in couldPlayIfEnoughData().
   3187     bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA;
   3188     return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData() && !isBlockedOnMediaController();
   3189 }
   3190 
   3191 bool HTMLMediaElement::couldPlayIfEnoughData() const
   3192 {
   3193     return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
   3194 }
   3195 
   3196 bool HTMLMediaElement::endedPlayback() const
   3197 {
   3198     double dur = duration();
   3199     if (!m_player || std::isnan(dur))
   3200         return false;
   3201 
   3202     // 4.8.10.8 Playing the media resource
   3203 
   3204     // A media element is said to have ended playback when the element's
   3205     // readyState attribute is HAVE_METADATA or greater,
   3206     if (m_readyState < HAVE_METADATA)
   3207         return false;
   3208 
   3209     // and the current playback position is the end of the media resource and the direction
   3210     // of playback is forwards, Either the media element does not have a loop attribute specified,
   3211     // or the media element has a current media controller.
   3212     double now = currentTime();
   3213     if (m_playbackRate > 0)
   3214         return dur > 0 && now >= dur && (!loop() || m_mediaController);
   3215 
   3216     // or the current playback position is the earliest possible position and the direction
   3217     // of playback is backwards
   3218     if (m_playbackRate < 0)
   3219         return now <= 0;
   3220 
   3221     return false;
   3222 }
   3223 
   3224 bool HTMLMediaElement::stoppedDueToErrors() const
   3225 {
   3226     if (m_readyState >= HAVE_METADATA && m_error) {
   3227         RefPtr<TimeRanges> seekableRanges = seekable();
   3228         if (!seekableRanges->contain(currentTime()))
   3229             return true;
   3230     }
   3231 
   3232     return false;
   3233 }
   3234 
   3235 bool HTMLMediaElement::pausedForUserInteraction() const
   3236 {
   3237 //    return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user]
   3238     return false;
   3239 }
   3240 
   3241 void HTMLMediaElement::updateVolume()
   3242 {
   3243     if (!m_player)
   3244         return;
   3245 
   3246     double volumeMultiplier = 1;
   3247     bool shouldMute = m_muted;
   3248 
   3249     if (m_mediaController) {
   3250         volumeMultiplier *= m_mediaController->volume();
   3251         shouldMute = m_mediaController->muted();
   3252     }
   3253 
   3254     m_player->setMuted(shouldMute);
   3255     m_player->setVolume(m_volume * volumeMultiplier);
   3256 
   3257     if (hasMediaControls())
   3258         mediaControls()->changedVolume();
   3259 }
   3260 
   3261 void HTMLMediaElement::updatePlayState()
   3262 {
   3263     if (!m_player)
   3264         return;
   3265 
   3266     if (m_pausedInternal) {
   3267         if (!m_player->paused())
   3268             m_player->pause();
   3269         refreshCachedTime();
   3270         m_playbackProgressTimer.stop();
   3271         if (hasMediaControls())
   3272             mediaControls()->playbackStopped();
   3273         return;
   3274     }
   3275 
   3276     bool shouldBePlaying = potentiallyPlaying();
   3277     bool playerPaused = m_player->paused();
   3278 
   3279     WTF_LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s",
   3280         boolString(shouldBePlaying), boolString(playerPaused));
   3281 
   3282     if (shouldBePlaying) {
   3283         setDisplayMode(Video);
   3284         invalidateCachedTime();
   3285 
   3286         if (playerPaused) {
   3287             // Set rate, muted before calling play in case they were set before the media engine was setup.
   3288             // The media engine should just stash the rate and muted values since it isn't already playing.
   3289             m_player->setRate(m_playbackRate);
   3290             m_player->setMuted(m_muted);
   3291 
   3292             m_player->play();
   3293         }
   3294 
   3295         if (hasMediaControls())
   3296             mediaControls()->playbackStarted();
   3297         startPlaybackProgressTimer();
   3298         m_playing = true;
   3299 
   3300     } else { // Should not be playing right now
   3301         if (!playerPaused)
   3302             m_player->pause();
   3303         refreshCachedTime();
   3304 
   3305         m_playbackProgressTimer.stop();
   3306         m_playing = false;
   3307         double time = currentTime();
   3308         if (time > m_lastSeekTime)
   3309             addPlayedRange(m_lastSeekTime, time);
   3310 
   3311         if (couldPlayIfEnoughData())
   3312             prepareToPlay();
   3313 
   3314         if (hasMediaControls())
   3315             mediaControls()->playbackStopped();
   3316     }
   3317 
   3318     updateMediaController();
   3319 
   3320     if (renderer())
   3321         renderer()->updateFromElement();
   3322 }
   3323 
   3324 void HTMLMediaElement::setPausedInternal(bool b)
   3325 {
   3326     m_pausedInternal = b;
   3327     updatePlayState();
   3328 }
   3329 
   3330 void HTMLMediaElement::stopPeriodicTimers()
   3331 {
   3332     m_progressEventTimer.stop();
   3333     m_playbackProgressTimer.stop();
   3334 }
   3335 
   3336 void HTMLMediaElement::userCancelledLoad()
   3337 {
   3338     WTF_LOG(Media, "HTMLMediaElement::userCancelledLoad");
   3339 
   3340     // If the media data fetching process is aborted by the user:
   3341 
   3342     // 1 - The user agent should cancel the fetching process.
   3343     clearMediaPlayer(-1);
   3344 
   3345     if (m_networkState == NETWORK_EMPTY || m_completelyLoaded)
   3346         return;
   3347 
   3348     // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED.
   3349     m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
   3350 
   3351     // 3 - Queue a task to fire a simple event named error at the media element.
   3352     scheduleEvent(EventTypeNames::abort);
   3353 
   3354     closeMediaSource();
   3355 
   3356     // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
   3357     // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
   3358     // simple event named emptied at the element. Otherwise, set the element's networkState
   3359     // attribute to the NETWORK_IDLE value.
   3360     if (m_readyState == HAVE_NOTHING) {
   3361         m_networkState = NETWORK_EMPTY;
   3362         scheduleEvent(EventTypeNames::emptied);
   3363     }
   3364     else
   3365         m_networkState = NETWORK_IDLE;
   3366 
   3367     // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
   3368     setShouldDelayLoadEvent(false);
   3369 
   3370     // 6 - Abort the overall resource selection algorithm.
   3371     m_currentSourceNode = 0;
   3372 
   3373     // Reset m_readyState since m_player is gone.
   3374     m_readyState = HAVE_NOTHING;
   3375     updateMediaController();
   3376     if (RuntimeEnabledFeatures::videoTrackEnabled())
   3377         updateActiveTextTrackCues(0);
   3378 }
   3379 
   3380 void HTMLMediaElement::clearMediaPlayerAndAudioSourceProviderClient()
   3381 {
   3382 #if ENABLE(WEB_AUDIO)
   3383     if (m_audioSourceNode)
   3384         m_audioSourceNode->lock();
   3385 
   3386     if (audioSourceProvider())
   3387         audioSourceProvider()->setClient(0);
   3388 #endif
   3389 
   3390     m_player.clear();
   3391 
   3392 #if ENABLE(WEB_AUDIO)
   3393     if (m_audioSourceNode)
   3394         m_audioSourceNode->unlock();
   3395 #endif
   3396 }
   3397 
   3398 void HTMLMediaElement::clearMediaPlayer(int flags)
   3399 {
   3400     removeAllInbandTracks();
   3401 
   3402     closeMediaSource();
   3403 
   3404     clearMediaPlayerAndAudioSourceProviderClient();
   3405 
   3406     stopPeriodicTimers();
   3407     m_loadTimer.stop();
   3408 
   3409     m_pendingActionFlags &= ~flags;
   3410     m_loadState = WaitingForSource;
   3411 
   3412     if (m_textTracks)
   3413         configureTextTrackDisplay(AssumeNoVisibleChange);
   3414 }
   3415 
   3416 void HTMLMediaElement::stop()
   3417 {
   3418     WTF_LOG(Media, "HTMLMediaElement::stop");
   3419 
   3420     m_active = false;
   3421     userCancelledLoad();
   3422 
   3423     // Stop the playback without generating events
   3424     m_playing = false;
   3425     setPausedInternal(true);
   3426 
   3427     if (renderer())
   3428         renderer()->updateFromElement();
   3429 
   3430     stopPeriodicTimers();
   3431     cancelPendingEventsAndCallbacks();
   3432 
   3433     m_asyncEventQueue->close();
   3434 }
   3435 
   3436 bool HTMLMediaElement::hasPendingActivity() const
   3437 {
   3438     return (hasAudio() && isPlaying()) || m_asyncEventQueue->hasPendingEvents();
   3439 }
   3440 
   3441 void HTMLMediaElement::contextDestroyed()
   3442 {
   3443     if (m_mediaController)
   3444         m_mediaController->clearExecutionContext();
   3445     ActiveDOMObject::contextDestroyed();
   3446 }
   3447 
   3448 bool HTMLMediaElement::isFullscreen() const
   3449 {
   3450     return FullscreenElementStack::isActiveFullScreenElement(this);
   3451 }
   3452 
   3453 void HTMLMediaElement::enterFullscreen()
   3454 {
   3455     WTF_LOG(Media, "HTMLMediaElement::enterFullscreen");
   3456 
   3457     if (document().settings() && document().settings()->fullScreenEnabled())
   3458         FullscreenElementStack::from(&document())->requestFullScreenForElement(this, 0, FullscreenElementStack::ExemptIFrameAllowFullScreenRequirement);
   3459 }
   3460 
   3461 void HTMLMediaElement::exitFullscreen()
   3462 {
   3463     WTF_LOG(Media, "HTMLMediaElement::exitFullscreen");
   3464 
   3465     if (document().settings() && document().settings()->fullScreenEnabled() && isFullscreen())
   3466         FullscreenElementStack::from(&document())->webkitCancelFullScreen();
   3467 }
   3468 
   3469 void HTMLMediaElement::didBecomeFullscreenElement()
   3470 {
   3471     if (hasMediaControls())
   3472         mediaControls()->enteredFullscreen();
   3473     if (RuntimeEnabledFeatures::overlayFullscreenVideoEnabled() && isVideo())
   3474         document().renderView()->compositor()->setCompositingLayersNeedRebuild(true);
   3475 }
   3476 
   3477 void HTMLMediaElement::willStopBeingFullscreenElement()
   3478 {
   3479     if (hasMediaControls())
   3480         mediaControls()->exitedFullscreen();
   3481     if (RuntimeEnabledFeatures::overlayFullscreenVideoEnabled() && isVideo())
   3482         document().renderView()->compositor()->setCompositingLayersNeedRebuild(true);
   3483 }
   3484 
   3485 blink::WebLayer* HTMLMediaElement::platformLayer() const
   3486 {
   3487     return m_webLayer;
   3488 }
   3489 
   3490 bool HTMLMediaElement::hasClosedCaptions() const
   3491 {
   3492     if (RuntimeEnabledFeatures::videoTrackEnabled() && m_textTracks) {
   3493         for (unsigned i = 0; i < m_textTracks->length(); ++i) {
   3494             if (m_textTracks->item(i)->readinessState() == TextTrack::FailedToLoad)
   3495                 continue;
   3496 
   3497             if (m_textTracks->item(i)->kind() == TextTrack::captionsKeyword()
   3498                 || m_textTracks->item(i)->kind() == TextTrack::subtitlesKeyword())
   3499                 return true;
   3500         }
   3501     }
   3502     return false;
   3503 }
   3504 
   3505 bool HTMLMediaElement::closedCaptionsVisible() const
   3506 {
   3507     return m_closedCaptionsVisible;
   3508 }
   3509 
   3510 void HTMLMediaElement::updateTextTrackDisplay()
   3511 {
   3512     WTF_LOG(Media, "HTMLMediaElement::updateTextTrackDisplay");
   3513 
   3514     if (!hasMediaControls() && !createMediaControls())
   3515         return;
   3516 
   3517     mediaControls()->updateTextTrackDisplay();
   3518 }
   3519 
   3520 void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible)
   3521 {
   3522     WTF_LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible));
   3523 
   3524     if (!m_player || !hasClosedCaptions())
   3525         return;
   3526 
   3527     m_closedCaptionsVisible = closedCaptionVisible;
   3528 
   3529     if (RuntimeEnabledFeatures::videoTrackEnabled()) {
   3530         m_processingPreferenceChange = true;
   3531         markCaptionAndSubtitleTracksAsUnconfigured();
   3532         m_processingPreferenceChange = false;
   3533 
   3534         updateTextTrackDisplay();
   3535     }
   3536 }
   3537 
   3538 unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const
   3539 {
   3540     if (!m_player)
   3541         return 0;
   3542     return m_player->audioDecodedByteCount();
   3543 }
   3544 
   3545 unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const
   3546 {
   3547     if (!m_player)
   3548         return 0;
   3549     return m_player->videoDecodedByteCount();
   3550 }
   3551 
   3552 bool HTMLMediaElement::isURLAttribute(const Attribute& attribute) const
   3553 {
   3554     return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute);
   3555 }
   3556 
   3557 void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay)
   3558 {
   3559     if (m_shouldDelayLoadEvent == shouldDelay)
   3560         return;
   3561 
   3562     WTF_LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay));
   3563 
   3564     m_shouldDelayLoadEvent = shouldDelay;
   3565     if (shouldDelay)
   3566         document().incrementLoadEventDelayCount();
   3567     else
   3568         document().decrementLoadEventDelayCount();
   3569 }
   3570 
   3571 
   3572 MediaControls* HTMLMediaElement::mediaControls() const
   3573 {
   3574     return toMediaControls(userAgentShadowRoot()->firstChild());
   3575 }
   3576 
   3577 bool HTMLMediaElement::hasMediaControls() const
   3578 {
   3579     if (ShadowRoot* userAgent = userAgentShadowRoot()) {
   3580         Node* node = userAgent->firstChild();
   3581         ASSERT_WITH_SECURITY_IMPLICATION(!node || node->isMediaControls());
   3582         return node;
   3583     }
   3584 
   3585     return false;
   3586 }
   3587 
   3588 bool HTMLMediaElement::createMediaControls()
   3589 {
   3590     if (hasMediaControls())
   3591         return true;
   3592 
   3593     RefPtr<MediaControls> mediaControls = MediaControls::create(document());
   3594     if (!mediaControls)
   3595         return false;
   3596 
   3597     mediaControls->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this));
   3598     mediaControls->reset();
   3599     if (isFullscreen())
   3600         mediaControls->enteredFullscreen();
   3601 
   3602     ensureUserAgentShadowRoot().appendChild(mediaControls);
   3603 
   3604     if (!controls() || !inDocument())
   3605         mediaControls->hide();
   3606 
   3607     return true;
   3608 }
   3609 
   3610 void HTMLMediaElement::configureMediaControls()
   3611 {
   3612     if (!controls() || !inDocument()) {
   3613         if (hasMediaControls())
   3614             mediaControls()->hide();
   3615         return;
   3616     }
   3617 
   3618     if (!hasMediaControls() && !createMediaControls())
   3619         return;
   3620 
   3621     mediaControls()->show();
   3622 }
   3623 
   3624 void HTMLMediaElement::configureTextTrackDisplay(VisibilityChangeAssumption assumption)
   3625 {
   3626     ASSERT(m_textTracks);
   3627     WTF_LOG(Media, "HTMLMediaElement::configureTextTrackDisplay");
   3628 
   3629     if (m_processingPreferenceChange)
   3630         return;
   3631 
   3632     bool haveVisibleTextTrack = false;
   3633     for (unsigned i = 0; i < m_textTracks->length(); ++i) {
   3634         if (m_textTracks->item(i)->mode() == TextTrack::showingKeyword()) {
   3635             haveVisibleTextTrack = true;
   3636             break;
   3637         }
   3638     }
   3639 
   3640     if (assumption == AssumeNoVisibleChange
   3641         && m_haveVisibleTextTrack == haveVisibleTextTrack) {
   3642         updateActiveTextTrackCues(currentTime());
   3643         return;
   3644     }
   3645     m_haveVisibleTextTrack = haveVisibleTextTrack;
   3646     m_closedCaptionsVisible = m_haveVisibleTextTrack;
   3647 
   3648     if (!m_haveVisibleTextTrack && !hasMediaControls())
   3649         return;
   3650     if (!hasMediaControls() && !createMediaControls())
   3651         return;
   3652 
   3653     mediaControls()->changedClosedCaptionsVisibility();
   3654 
   3655     if (RuntimeEnabledFeatures::videoTrackEnabled()) {
   3656         updateActiveTextTrackCues(currentTime());
   3657         updateTextTrackDisplay();
   3658     }
   3659 }
   3660 
   3661 void HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured()
   3662 {
   3663     if (!m_textTracks)
   3664         return;
   3665 
   3666     // Mark all tracks as not "configured" so that configureTextTracks()
   3667     // will reconsider which tracks to display in light of new user preferences
   3668     // (e.g. default tracks should not be displayed if the user has turned off
   3669     // captions and non-default tracks should be displayed based on language
   3670     // preferences if the user has turned captions on).
   3671     for (unsigned i = 0; i < m_textTracks->length(); ++i) {
   3672         RefPtr<TextTrack> textTrack = m_textTracks->item(i);
   3673         String kind = textTrack->kind();
   3674 
   3675         if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword())
   3676             textTrack->setHasBeenConfigured(false);
   3677     }
   3678     configureTextTracks();
   3679 }
   3680 
   3681 
   3682 void* HTMLMediaElement::preDispatchEventHandler(Event* event)
   3683 {
   3684     if (event && event->type() == EventTypeNames::webkitfullscreenchange)
   3685         configureMediaControls();
   3686 
   3687     return 0;
   3688 }
   3689 
   3690 void HTMLMediaElement::createMediaPlayer()
   3691 {
   3692 #if ENABLE(WEB_AUDIO)
   3693     if (m_audioSourceNode)
   3694         m_audioSourceNode->lock();
   3695 #endif
   3696 
   3697     if (m_mediaSource)
   3698         closeMediaSource();
   3699 
   3700     m_player = MediaPlayer::create(this);
   3701 
   3702 #if ENABLE(WEB_AUDIO)
   3703     if (m_audioSourceNode) {
   3704         // When creating the player, make sure its AudioSourceProvider knows about the MediaElementAudioSourceNode.
   3705         if (audioSourceProvider())
   3706             audioSourceProvider()->setClient(m_audioSourceNode);
   3707 
   3708         m_audioSourceNode->unlock();
   3709     }
   3710 #endif
   3711 }
   3712 
   3713 #if ENABLE(WEB_AUDIO)
   3714 void HTMLMediaElement::setAudioSourceNode(MediaElementAudioSourceNode* sourceNode)
   3715 {
   3716     m_audioSourceNode = sourceNode;
   3717 
   3718     if (m_audioSourceNode)
   3719         m_audioSourceNode->lock();
   3720 
   3721     if (audioSourceProvider())
   3722         audioSourceProvider()->setClient(m_audioSourceNode);
   3723 
   3724     if (m_audioSourceNode)
   3725         m_audioSourceNode->unlock();
   3726 }
   3727 
   3728 AudioSourceProvider* HTMLMediaElement::audioSourceProvider()
   3729 {
   3730     if (m_player)
   3731         return m_player->audioSourceProvider();
   3732 
   3733     return 0;
   3734 }
   3735 #endif
   3736 
   3737 const AtomicString& HTMLMediaElement::mediaGroup() const
   3738 {
   3739     return fastGetAttribute(mediagroupAttr);
   3740 }
   3741 
   3742 void HTMLMediaElement::setMediaGroup(const AtomicString& group)
   3743 {
   3744     // When a media element is created with a mediagroup attribute, and when a media element's mediagroup
   3745     // attribute is set, changed, or removed, the user agent must run the following steps:
   3746     // 1. Let m [this] be the media element in question.
   3747     // 2. Let m have no current media controller, if it currently has one.
   3748     setControllerInternal(0);
   3749 
   3750     // 3. If m's mediagroup attribute is being removed, then abort these steps.
   3751     if (group.isNull() || group.isEmpty())
   3752         return;
   3753 
   3754     // 4. If there is another media element whose Document is the same as m's Document (even if one or both
   3755     // of these elements are not actually in the Document),
   3756     HashSet<HTMLMediaElement*> elements = documentToElementSetMap().get(&document());
   3757     for (HashSet<HTMLMediaElement*>::iterator i = elements.begin(); i != elements.end(); ++i) {
   3758         if (*i == this)
   3759             continue;
   3760 
   3761         // and which also has a mediagroup attribute, and whose mediagroup attribute has the same value as
   3762         // the new value of m's mediagroup attribute,
   3763         if ((*i)->mediaGroup() == group) {
   3764             //  then let controller be that media element's current media controller.
   3765             setControllerInternal((*i)->controller());
   3766             return;
   3767         }
   3768     }
   3769 
   3770     // Otherwise, let controller be a newly created MediaController.
   3771     setControllerInternal(MediaController::create(Node::executionContext()));
   3772 }
   3773 
   3774 MediaController* HTMLMediaElement::controller() const
   3775 {
   3776     return m_mediaController.get();
   3777 }
   3778 
   3779 void HTMLMediaElement::setController(PassRefPtr<MediaController> controller)
   3780 {
   3781     // 4.8.10.11.2 Media controllers: controller attribute.
   3782     // On setting, it must first remove the element's mediagroup attribute, if any,
   3783     removeAttribute(mediagroupAttr);
   3784     // and then set the current media controller to the given value.
   3785     setControllerInternal(controller);
   3786 }
   3787 
   3788 void HTMLMediaElement::setControllerInternal(PassRefPtr<MediaController> controller)
   3789 {
   3790     if (m_mediaController)
   3791         m_mediaController->removeMediaElement(this);
   3792 
   3793     m_mediaController = controller;
   3794 
   3795     if (m_mediaController)
   3796         m_mediaController->addMediaElement(this);
   3797 
   3798     if (hasMediaControls())
   3799         mediaControls()->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this));
   3800 }
   3801 
   3802 void HTMLMediaElement::updateMediaController()
   3803 {
   3804     if (m_mediaController)
   3805         m_mediaController->reportControllerState();
   3806 }
   3807 
   3808 bool HTMLMediaElement::isBlocked() const
   3809 {
   3810     // A media element is a blocked media element if its readyState attribute is in the
   3811     // HAVE_NOTHING state, the HAVE_METADATA state, or the HAVE_CURRENT_DATA state,
   3812     if (m_readyState <= HAVE_CURRENT_DATA)
   3813         return true;
   3814 
   3815     // or if the element has paused for user interaction.
   3816     return pausedForUserInteraction();
   3817 }
   3818 
   3819 bool HTMLMediaElement::isBlockedOnMediaController() const
   3820 {
   3821     if (!m_mediaController)
   3822         return false;
   3823 
   3824     // A media element is blocked on its media controller if the MediaController is a blocked
   3825     // media controller,
   3826     if (m_mediaController->isBlocked())
   3827         return true;
   3828 
   3829     // or if its media controller position is either before the media resource's earliest possible
   3830     // position relative to the MediaController's timeline or after the end of the media resource
   3831     // relative to the MediaController's timeline.
   3832     double mediaControllerPosition = m_mediaController->currentTime();
   3833     if (mediaControllerPosition < 0 || mediaControllerPosition > duration())
   3834         return true;
   3835 
   3836     return false;
   3837 }
   3838 
   3839 void HTMLMediaElement::prepareMediaFragmentURI()
   3840 {
   3841     MediaFragmentURIParser fragmentParser(m_currentSrc);
   3842     double dur = duration();
   3843 
   3844     double start = fragmentParser.startTime();
   3845     if (start != MediaFragmentURIParser::invalidTimeValue() && start > 0) {
   3846         m_fragmentStartTime = start;
   3847         if (m_fragmentStartTime > dur)
   3848             m_fragmentStartTime = dur;
   3849     } else
   3850         m_fragmentStartTime = MediaPlayer::invalidTime();
   3851 
   3852     double end = fragmentParser.endTime();
   3853     if (end != MediaFragmentURIParser::invalidTimeValue() && end > 0 && end > m_fragmentStartTime) {
   3854         m_fragmentEndTime = end;
   3855         if (m_fragmentEndTime > dur)
   3856             m_fragmentEndTime = dur;
   3857     } else
   3858         m_fragmentEndTime = MediaPlayer::invalidTime();
   3859 
   3860     if (m_fragmentStartTime != MediaPlayer::invalidTime() && m_readyState < HAVE_FUTURE_DATA)
   3861         prepareToPlay();
   3862 }
   3863 
   3864 void HTMLMediaElement::applyMediaFragmentURI()
   3865 {
   3866     if (m_fragmentStartTime != MediaPlayer::invalidTime()) {
   3867         m_sentEndEvent = false;
   3868         seek(m_fragmentStartTime, IGNORE_EXCEPTION);
   3869     }
   3870 }
   3871 
   3872 MediaPlayerClient::CORSMode HTMLMediaElement::mediaPlayerCORSMode() const
   3873 {
   3874     if (!fastHasAttribute(crossoriginAttr))
   3875         return Unspecified;
   3876     if (equalIgnoringCase(fastGetAttribute(crossoriginAttr), "use-credentials"))
   3877         return UseCredentials;
   3878     return Anonymous;
   3879 }
   3880 
   3881 void HTMLMediaElement::removeBehaviorsRestrictionsAfterFirstUserGesture()
   3882 {
   3883     m_restrictions = NoRestrictions;
   3884 }
   3885 
   3886 void HTMLMediaElement::mediaPlayerSetWebLayer(blink::WebLayer* webLayer)
   3887 {
   3888     if (webLayer == m_webLayer)
   3889         return;
   3890 
   3891     // If either of the layers is null we need to enable or disable compositing. This is done by triggering a style recalc.
   3892     if (!m_webLayer || !webLayer)
   3893         scheduleLayerUpdate();
   3894 
   3895     if (m_webLayer)
   3896         GraphicsLayer::unregisterContentsLayer(m_webLayer);
   3897     m_webLayer = webLayer;
   3898     if (m_webLayer) {
   3899         m_webLayer->setOpaque(m_opaque);
   3900         GraphicsLayer::registerContentsLayer(m_webLayer);
   3901     }
   3902 }
   3903 
   3904 void HTMLMediaElement::mediaPlayerSetOpaque(bool opaque)
   3905 {
   3906     m_opaque = opaque;
   3907     if (m_webLayer)
   3908         m_webLayer->setOpaque(m_opaque);
   3909 }
   3910 
   3911 bool HTMLMediaElement::isInteractiveContent() const
   3912 {
   3913     return fastHasAttribute(controlsAttr);
   3914 }
   3915 
   3916 }
   3917