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