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