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