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