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