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