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