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