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