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