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