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