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