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