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