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