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