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