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