2009-10-05 Pierre d'Herbemont <pdherbemont@webkit.org>
[WebKit.git] / WebCore / html / HTMLMediaElement.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009 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 "ChromeClient.h"
32 #include "CSSHelper.h"
33 #include "CSSPropertyNames.h"
34 #include "CSSValueKeywords.h"
35 #include "ContentType.h"
36 #include "DocLoader.h"
37 #include "Event.h"
38 #include "EventNames.h"
39 #include "ExceptionCode.h"
40 #include "Frame.h"
41 #include "FrameLoader.h"
42 #include "FrameLoaderClient.h"
43 #include "HTMLDocument.h"
44 #include "HTMLNames.h"
45 #include "HTMLSourceElement.h"
46 #include "HTMLVideoElement.h"
47 #include "MIMETypeRegistry.h"
48 #include "MappedAttribute.h"
49 #include "MediaDocument.h"
50 #include "MediaError.h"
51 #include "MediaList.h"
52 #include "MediaPlayer.h"
53 #include "MediaQueryEvaluator.h"
54 #include "Page.h"
55 #include "ProgressEvent.h"
56 #include "RenderVideo.h"
57 #include "ScriptEventListener.h"
58 #include "TimeRanges.h"
59 #include "ClientRect.h"
60 #include "ClientRectList.h"
61 #include <limits>
62 #include <wtf/CurrentTime.h>
63 #include <wtf/MathExtras.h>
64
65 #if USE(ACCELERATED_COMPOSITING)
66 #include "RenderView.h"
67 #include "RenderLayerCompositor.h"
68 #endif
69
70 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
71 #include "RenderPartObject.h"
72 #include "Widget.h"
73 #endif
74
75 using namespace std;
76
77 namespace WebCore {
78
79 using namespace HTMLNames;
80
81 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc)
82     : HTMLElement(tagName, doc)
83     , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
84     , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired)
85     , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
86     , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
87     , m_playedTimeRanges()
88     , m_playbackRate(1.0f)
89     , m_defaultPlaybackRate(1.0f)
90     , m_webkitPreservesPitch(true)
91     , m_networkState(NETWORK_EMPTY)
92     , m_readyState(HAVE_NOTHING)
93     , m_volume(1.0f)
94     , m_lastSeekTime(0)
95     , m_previousProgress(0)
96     , m_previousProgressTime(numeric_limits<double>::max())
97     , m_lastTimeUpdateEventWallTime(0)
98     , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max())
99     , m_loadState(WaitingForSource)
100     , m_currentSourceNode(0)
101     , m_player(0)
102     , m_restrictions(NoRestrictions)
103     , m_playing(false)
104     , m_processingMediaPlayerCallback(0)
105     , m_processingLoad(false)
106     , m_delayingTheLoadEvent(false)
107     , m_haveFiredLoadedData(false)
108     , m_inActiveDocument(true)
109     , m_autoplaying(true)
110     , m_muted(false)
111     , m_paused(true)
112     , m_seeking(false)
113     , m_sentStalledEvent(false)
114     , m_sentEndEvent(false)
115     , m_pausedInternal(false)
116     , m_sendProgressEvents(true)
117     , m_isFullscreen(false)
118 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
119     , m_needWidgetUpdate(false)
120 #endif
121 {
122     document()->registerForDocumentActivationCallbacks(this);
123     document()->registerForMediaVolumeCallbacks(this);
124 }
125
126 HTMLMediaElement::~HTMLMediaElement()
127 {
128     document()->unregisterForDocumentActivationCallbacks(this);
129     document()->unregisterForMediaVolumeCallbacks(this);
130 }
131
132 bool HTMLMediaElement::checkDTD(const Node* newChild)
133 {
134     return newChild->hasTagName(sourceTag) || HTMLElement::checkDTD(newChild);
135 }
136
137 void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls)
138 {
139     HTMLElement::attributeChanged(attr, preserveDecls);
140
141     const QualifiedName& attrName = attr->name();
142     if (attrName == srcAttr) {
143         // don't have a src or any <source> children, trigger load
144         if (inDocument() && m_loadState == WaitingForSource)
145             scheduleLoad();
146     } 
147 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
148     else if (attrName == controlsAttr) {
149         if (!isVideo() && attached() && (controls() != (renderer() != 0))) {
150             detach();
151             attach();
152         }
153         if (renderer())
154             renderer()->updateFromElement();
155     }
156 #endif
157 }
158
159 void HTMLMediaElement::parseMappedAttribute(MappedAttribute* attr)
160 {
161     const QualifiedName& attrName = attr->name();
162
163     if (attrName == autobufferAttr) {
164         if (m_player)
165             m_player->setAutobuffer(!attr->isNull());
166     } else if (attrName == onabortAttr)
167         setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr));
168     else if (attrName == oncanplayAttr)
169         setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, attr));
170     else if (attrName == oncanplaythroughAttr)
171         setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, attr));
172     else if (attrName == ondurationchangeAttr)
173         setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, attr));
174     else if (attrName == onemptiedAttr)
175         setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, attr));
176     else if (attrName == onendedAttr)
177         setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attr));
178     else if (attrName == onerrorAttr)
179         setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr));
180     else if (attrName == onloadAttr)
181         setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr));
182     else if (attrName == onloadeddataAttr)
183         setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr));
184     else if (attrName == onloadedmetadataAttr)
185         setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attr));
186     else if (attrName == onloadendAttr)
187         setAttributeEventListener(eventNames().loadendEvent, createAttributeEventListener(this, attr));
188     else if (attrName == onloadstartAttr)
189         setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attr));
190     else if (attrName == onpauseAttr)
191         setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, attr));
192     else if (attrName == onplayAttr)
193         setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, attr));
194     else if (attrName == onplayingAttr)
195         setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, attr));
196     else if (attrName == onprogressAttr)
197         setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, attr));
198     else if (attrName == onratechangeAttr)
199         setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, attr));
200     else if (attrName == onseekedAttr)
201         setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, attr));
202     else if (attrName == onseekingAttr)
203         setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, attr));
204     else if (attrName == onstalledAttr)
205         setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, attr));
206     else if (attrName == onsuspendAttr)
207         setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, attr));
208     else if (attrName == ontimeupdateAttr)
209         setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, attr));
210     else if (attrName == onvolumechangeAttr)
211         setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, attr));
212     else if (attrName == onwaitingAttr)
213         setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, attr));
214     else
215         HTMLElement::parseMappedAttribute(attr);
216 }
217
218 bool HTMLMediaElement::rendererIsNeeded(RenderStyle* style)
219 {
220 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
221     UNUSED_PARAM(style);
222     Frame* frame = document()->frame();
223     if (!frame)
224         return false;
225
226     return true;
227 #else
228     return controls() ? HTMLElement::rendererIsNeeded(style) : false;
229 #endif
230 }
231
232 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
233 {
234 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
235     return new (arena) RenderPartObject(this);
236 #else
237     return new (arena) RenderMedia(this);
238 #endif
239 }
240  
241 void HTMLMediaElement::insertedIntoDocument()
242 {
243     HTMLElement::insertedIntoDocument();
244     if (!src().isEmpty() && m_networkState == NETWORK_EMPTY)
245         scheduleLoad();
246 }
247
248 void HTMLMediaElement::willRemove()
249 {
250     if (m_isFullscreen)
251         exitFullscreen();
252     HTMLElement::willRemove();
253 }
254 void HTMLMediaElement::removedFromDocument()
255 {
256     if (m_networkState > NETWORK_EMPTY)
257         pause();
258     HTMLElement::removedFromDocument();
259 }
260
261 void HTMLMediaElement::attach()
262 {
263     ASSERT(!attached());
264
265 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
266     m_needWidgetUpdate = true;
267 #endif
268
269     HTMLElement::attach();
270
271     if (renderer())
272         renderer()->updateFromElement();
273 }
274
275 void HTMLMediaElement::recalcStyle(StyleChange change)
276 {
277     HTMLElement::recalcStyle(change);
278
279     if (renderer())
280         renderer()->updateFromElement();
281 }
282
283 void HTMLMediaElement::scheduleLoad()
284 {
285     if (m_loadTimer.isActive())
286         return;
287     prepareForLoad();
288     m_loadTimer.startOneShot(0);
289 }
290
291 void HTMLMediaElement::scheduleNextSourceChild()
292 {
293     // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
294     m_loadTimer.startOneShot(0);
295 }
296
297 void HTMLMediaElement::scheduleProgressEvent(const AtomicString& eventName)
298 {
299     if (!m_sendProgressEvents)
300         return;
301
302     // FIXME: don't schedule timeupdate or progress events unless there are registered listeners
303
304     bool totalKnown = m_player && m_player->totalBytesKnown();
305     unsigned loaded = m_player ? m_player->bytesLoaded() : 0;
306     unsigned total = m_player ? m_player->totalBytes() : 0;
307
308     RefPtr<ProgressEvent> evt = ProgressEvent::create(eventName, totalKnown, loaded, total);
309     enqueueEvent(evt);
310
311     if (renderer())
312         renderer()->updateFromElement();
313 }
314
315 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
316 {
317     enqueueEvent(Event::create(eventName, false, true));
318 }
319
320 void HTMLMediaElement::enqueueEvent(RefPtr<Event> event)
321 {
322     m_pendingEvents.append(event);
323     if (!m_asyncEventTimer.isActive())
324         m_asyncEventTimer.startOneShot(0);
325 }
326
327 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
328 {
329     Vector<RefPtr<Event> > pendingEvents;
330     ExceptionCode ec = 0;
331
332     m_pendingEvents.swap(pendingEvents);
333     unsigned count = pendingEvents.size();
334     for (unsigned ndx = 0; ndx < count; ++ndx) 
335         dispatchEvent(pendingEvents[ndx].release(), ec);
336 }
337
338 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
339 {
340     if (m_loadState == LoadingFromSourceElement)
341         loadNextSourceChild();
342     else
343         loadInternal();
344 }
345
346 static String serializeTimeOffset(float time)
347 {
348     String timeString = String::number(time);
349     // FIXME serialize time offset values properly (format not specified yet)
350     timeString.append("s");
351     return timeString;
352 }
353
354 static float parseTimeOffset(const String& timeString, bool* ok = 0)
355 {
356     const UChar* characters = timeString.characters();
357     unsigned length = timeString.length();
358     
359     if (length && characters[length - 1] == 's')
360         length--;
361     
362     // FIXME parse time offset values (format not specified yet)
363     float val = charactersToFloat(characters, length, ok);
364     return val;
365 }
366
367 float HTMLMediaElement::getTimeOffsetAttribute(const QualifiedName& name, float valueOnError) const
368 {
369     bool ok;
370     String timeString = getAttribute(name);
371     float result = parseTimeOffset(timeString, &ok);
372     if (ok)
373         return result;
374     return valueOnError;
375 }
376
377 void HTMLMediaElement::setTimeOffsetAttribute(const QualifiedName& name, float value)
378 {
379     setAttribute(name, serializeTimeOffset(value));
380 }
381
382 PassRefPtr<MediaError> HTMLMediaElement::error() const 
383 {
384     return m_error;
385 }
386
387 KURL HTMLMediaElement::src() const
388 {
389     return document()->completeURL(getAttribute(srcAttr));
390 }
391
392 void HTMLMediaElement::setSrc(const String& url)
393 {
394     setAttribute(srcAttr, url);
395 }
396
397 String HTMLMediaElement::currentSrc() const
398 {
399     return m_currentSrc;
400 }
401
402 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
403 {
404     return m_networkState;
405 }
406
407 String HTMLMediaElement::canPlayType(const String& mimeType) const
408 {
409     MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType));
410     String canPlay;
411
412     // 4.8.10.3
413     switch (support)
414     {
415         case MediaPlayer::IsNotSupported:
416             canPlay = "";
417             break;
418         case MediaPlayer::MayBeSupported:
419             canPlay = "maybe";
420             break;
421         case MediaPlayer::IsSupported:
422             canPlay = "probably";
423             break;
424     }
425     
426     return canPlay;
427 }
428
429 void HTMLMediaElement::load(ExceptionCode& ec)
430 {
431     if (m_restrictions & RequireUserGestureForLoadRestriction && !processingUserGesture())
432         ec = INVALID_STATE_ERR;
433     else {
434         prepareForLoad();
435         loadInternal();
436     }
437 }
438
439 void HTMLMediaElement::prepareForLoad()
440 {
441     // Perform the cleanup required for the resource load algorithm to run.
442     stopPeriodicTimers();
443     m_loadTimer.stop();
444     m_sentStalledEvent = false;
445     m_haveFiredLoadedData = false;
446
447     // 2 - Abort any already-running instance of the resource selection algorithm for this element.
448     m_currentSourceNode = 0;
449
450     // 3 - If there are any tasks from the media element's media element event task source in 
451     // one of the task queues, then remove those tasks.
452     cancelPendingEventsAndCallbacks();
453 }
454
455 void HTMLMediaElement::loadInternal()
456 {
457     // 1 - If the load() method for this element is already being invoked, then abort these steps.
458     if (m_processingLoad)
459         return;
460     m_processingLoad = true;
461     
462     // Steps 2 and 3 were done in prepareForLoad()
463     
464     // 4 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, set
465     // the error attribute to a new MediaError object whose code attribute is set to
466     // MEDIA_ERR_ABORTED, fire a progress event called abort at the media element, in the
467     // context of the fetching process that is in progress for the element, and fire a progress
468     // event called loadend at the media element, in the context of the same fetching process.
469     if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE) {
470         m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
471
472         // fire synchronous 'abort' and 'loadend'
473         bool totalKnown = m_player && m_player->totalBytesKnown();
474         unsigned loaded = m_player ? m_player->bytesLoaded() : 0;
475         unsigned total = m_player ? m_player->totalBytes() : 0;
476         dispatchEvent(ProgressEvent::create(eventNames().abortEvent, totalKnown, loaded, total));
477         dispatchEvent(ProgressEvent::create(eventNames().loadendEvent, totalKnown, loaded, total));
478     }
479
480     // 5
481     m_error = 0;
482     m_autoplaying = true;
483     m_playedTimeRanges = TimeRanges::create();
484     m_lastSeekTime = 0;
485
486     // 6
487     setPlaybackRate(defaultPlaybackRate());
488
489     // 7
490     if (m_networkState != NETWORK_EMPTY) {
491         m_networkState = NETWORK_EMPTY;
492         m_readyState = HAVE_NOTHING;
493         m_paused = true;
494         m_seeking = false;
495         if (m_player) {
496             m_player->pause();
497             m_playing = false;
498             m_player->seek(0);
499         }
500         dispatchEvent(Event::create(eventNames().emptiedEvent, false, true));
501     }
502
503     selectMediaResource();
504     m_processingLoad = false;
505 }
506
507 void HTMLMediaElement::selectMediaResource()
508 {
509     // 1 - If the media element has neither a src attribute nor any source element children, run these substeps
510     String mediaSrc = getAttribute(srcAttr);
511     if (!mediaSrc && !havePotentialSourceChild()) {
512         m_loadState = WaitingForSource;
513
514         // 1 -  Set the networkState to NETWORK_NO_SOURCE
515         m_networkState = NETWORK_NO_SOURCE;
516         
517         // 2 - While the media element has neither a src attribute nor any source element children, 
518         // wait. (This steps might wait forever.)
519
520         m_delayingTheLoadEvent = false;
521         return;
522     }
523
524     // 2
525     m_delayingTheLoadEvent = true;
526
527     // 3
528     m_networkState = NETWORK_LOADING;
529
530     // 4
531     scheduleProgressEvent(eventNames().loadstartEvent);
532
533     // 5 - If the media element has a src attribute, then run these substeps
534     ContentType contentType("");
535     if (!mediaSrc.isEmpty()) {
536         KURL mediaURL = document()->completeURL(mediaSrc);
537         if (isSafeToLoadURL(mediaURL, Complain)) {
538             m_loadState = LoadingFromSrcAttr;
539             loadResource(mediaURL, contentType);
540         } else 
541             noneSupported();
542
543         return;
544     }
545
546     // Otherwise, the source elements will be used
547     m_currentSourceNode = 0;
548     loadNextSourceChild();
549 }
550
551 void HTMLMediaElement::loadNextSourceChild()
552 {
553     ContentType contentType("");
554     KURL mediaURL = selectNextSourceChild(&contentType, Complain);
555     if (!mediaURL.isValid()) {
556         // It seems wrong to fail silently when we give up because no suitable <source>
557         // element can be found and set the error attribute if the element's 'src' attribute
558         // fails, but that is what the spec says.
559         return;
560     }
561
562     m_loadState = LoadingFromSourceElement;
563     loadResource(mediaURL, contentType);
564 }
565
566 void HTMLMediaElement::loadResource(const KURL& url, ContentType& contentType)
567 {
568     ASSERT(isSafeToLoadURL(url, Complain));
569
570     // The resource fetch algorithm 
571     m_networkState = NETWORK_LOADING;
572
573     m_currentSrc = url;
574
575     if (m_sendProgressEvents) 
576         startProgressEventTimer();
577
578 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
579     m_player.clear();
580     m_player.set(new MediaPlayer(this));
581 #else
582     if (!m_player)
583         m_player.set(new MediaPlayer(this));
584 #endif
585
586     m_player->setPreservesPitch(m_webkitPreservesPitch);
587     updateVolume();
588
589     m_player->load(m_currentSrc, contentType);
590     
591     if (renderer())
592         renderer()->updateFromElement();
593 }
594
595 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidSourceAction actionIfInvalid)
596 {
597     Frame* frame = document()->frame();
598     FrameLoader* loader = frame ? frame->loader() : 0;
599
600     // don't allow remote to local urls, and check with the frame loader client.
601     if (!loader || !loader->canLoad(url, String(), document()) || !loader->client()->shouldLoadMediaElementURL(url)) {
602         if (actionIfInvalid == Complain)
603             FrameLoader::reportLocalLoadFailed(frame, url.string());
604         return false;
605     }
606     
607     return true;
608 }
609
610 void HTMLMediaElement::startProgressEventTimer()
611 {
612     if (m_progressEventTimer.isActive())
613         return;
614
615     m_previousProgressTime = WTF::currentTime();
616     m_previousProgress = 0;
617     // 350ms is not magic, it is in the spec!
618     m_progressEventTimer.startRepeating(0.350);
619 }
620
621 void HTMLMediaElement::noneSupported()
622 {
623     stopPeriodicTimers();
624     m_loadState = WaitingForSource;
625     m_currentSourceNode = 0;
626
627     // 4 - Reaching this step indicates that either the URL failed to resolve, or the media
628     // resource failed to load. Set the error attribute to a new MediaError object whose
629     // code attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED.
630     m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
631
632     // 5 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
633     m_networkState = NETWORK_NO_SOURCE;
634
635     // 6 - Queue a task to fire a progress event called error at the media element, in
636     // the context of the fetching process that was used to try to obtain the media
637     // resource in the resource fetch algorithm.
638     scheduleProgressEvent(eventNames().errorEvent);
639
640     // 7 - Queue a task to fire a progress event called loadend at the media element, in
641     // the context of the fetching process that was used to try to obtain the media
642     // resource in the resource fetch algorithm.
643     scheduleProgressEvent(eventNames().loadendEvent);
644
645     // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
646     m_delayingTheLoadEvent = false;
647
648     // 9 -Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource.
649
650     if (isVideo())
651         static_cast<HTMLVideoElement*>(this)->updatePosterImage();
652     if (renderer())
653         renderer()->updateFromElement();
654 }
655
656 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
657 {
658     // 1 - The user agent should cancel the fetching process.
659     stopPeriodicTimers();
660     m_loadState = WaitingForSource;
661
662     // 2 - Set the error attribute to a new MediaError object whose code attribute is 
663     // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
664     m_error = err;
665
666     // 3 - Queue a task to fire a progress event called error at the media element, in
667     // the context of the fetching process started by this instance of this algorithm.
668     scheduleProgressEvent(eventNames().errorEvent);
669
670     // 4 - Queue a task to fire a progress event called loadend at the media element, in
671     // the context of the fetching process started by this instance of this algorithm.
672     scheduleProgressEvent(eventNames().loadendEvent);
673
674     // 5 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
675     // task to fire a simple event called emptied at the element.
676     m_networkState = NETWORK_EMPTY;
677     scheduleEvent(eventNames().emptiedEvent);
678
679     // 6 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
680     m_delayingTheLoadEvent = false;
681
682     // 7 - Abort the overall resource selection algorithm.
683     m_currentSourceNode = 0;
684 }
685
686 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
687 {
688     m_pendingEvents.clear();
689
690     for (Node* node = firstChild(); node; node = node->nextSibling()) {
691         if (node->hasTagName(sourceTag))
692             static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent();
693     }
694 }
695
696 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
697 {
698     beginProcessingMediaPlayerCallback();
699     setNetworkState(m_player->networkState());
700     endProcessingMediaPlayerCallback();
701 }
702
703 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
704 {
705     if (state == MediaPlayer::Empty) {
706         // just update the cached state and leave, we can't do anything 
707         m_networkState = NETWORK_EMPTY;
708         return;
709     }
710
711     if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
712         stopPeriodicTimers();
713
714         // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
715         // <source> children, schedule the next one
716         if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
717             m_currentSourceNode->scheduleErrorEvent();
718             if (havePotentialSourceChild())
719                 scheduleNextSourceChild();
720             return;
721         }
722
723         if (state == MediaPlayer::NetworkError)
724             mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
725         else if (state == MediaPlayer::DecodeError)
726             mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
727         else if (state == MediaPlayer::FormatError && m_loadState == LoadingFromSrcAttr)
728             noneSupported();
729
730         if (isVideo())
731             static_cast<HTMLVideoElement*>(this)->updatePosterImage();
732
733         return;
734     }
735
736     if (state == MediaPlayer::Idle) {
737         if (m_networkState > NETWORK_IDLE) {
738             stopPeriodicTimers();
739             scheduleProgressEvent(eventNames().suspendEvent);
740         }
741         m_networkState = NETWORK_IDLE;
742     }
743
744     if (state == MediaPlayer::Loading) {
745         if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
746             startProgressEventTimer();
747         m_networkState = NETWORK_LOADING;
748     }
749
750     if (state == MediaPlayer::Loaded) {
751         NetworkState oldState = m_networkState;
752
753         m_networkState = NETWORK_LOADED;
754         if (oldState < NETWORK_LOADED || oldState == NETWORK_NO_SOURCE) {
755             m_progressEventTimer.stop();
756
757             // Schedule one last progress event so we guarantee that at least one is fired
758             // for files that load very quickly.
759             scheduleProgressEvent(eventNames().progressEvent);
760
761             // Check to see if readyState changes need to be dealt with before sending the 
762             // 'load' event so we report 'canplaythrough' first. This is necessary because a
763             //  media engine reports readyState and networkState changes separately
764             MediaPlayer::ReadyState currentState = m_player->readyState();
765             if (static_cast<ReadyState>(currentState) != m_readyState)
766                 setReadyState(currentState);
767
768             scheduleProgressEvent(eventNames().loadEvent);
769             scheduleProgressEvent(eventNames().loadendEvent);
770         }
771     }
772 }
773
774 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
775 {
776     beginProcessingMediaPlayerCallback();
777
778     setReadyState(m_player->readyState());
779
780     endProcessingMediaPlayerCallback();
781 }
782
783 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
784 {
785     // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
786     bool wasPotentiallyPlaying = potentiallyPlaying();
787
788     ReadyState oldState = m_readyState;
789     m_readyState = static_cast<ReadyState>(state);
790
791     if (m_readyState == oldState)
792         return;
793     
794     if (m_networkState == NETWORK_EMPTY)
795         return;
796
797     if (m_seeking) {
798         // 4.8.10.10, step 8
799         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
800             scheduleEvent(eventNames().waitingEvent);
801
802         // 4.8.10.10, step 9
803         if (m_readyState < HAVE_CURRENT_DATA) {
804             if (oldState >= HAVE_CURRENT_DATA)
805                 scheduleEvent(eventNames().seekingEvent);
806         } else {
807             // 4.8.10.10 step 12 & 13.
808             finishSeek();
809         }
810
811     } else {
812         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
813             // 4.8.10.9
814             scheduleTimeupdateEvent(false);
815             scheduleEvent(eventNames().waitingEvent);
816         }
817     }
818
819     if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
820         scheduleEvent(eventNames().durationchangeEvent);
821         scheduleEvent(eventNames().loadedmetadataEvent);
822
823 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
824         if (renderer() && renderer()->isVideo()) {
825             toRenderVideo(renderer())->videoSizeChanged();
826         }
827 #endif        
828         m_delayingTheLoadEvent = false;
829         m_player->seek(0);
830     }
831
832     // 4.8.10.7 says loadeddata is sent only when the new state *is* HAVE_CURRENT_DATA: "If the
833     // previous ready state was HAVE_METADATA and the new ready state is HAVE_CURRENT_DATA", 
834     // but the event table at the end of the spec says it is sent when: "readyState newly 
835     // increased to HAVE_CURRENT_DATA  or greater for the first time"
836     // We go with the later because it seems useful to count on getting this event
837     if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
838         m_haveFiredLoadedData = true;
839         scheduleEvent(eventNames().loadeddataEvent);
840     }
841
842     bool isPotentiallyPlaying = potentiallyPlaying();
843     if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) {
844         scheduleEvent(eventNames().canplayEvent);
845         if (isPotentiallyPlaying)
846             scheduleEvent(eventNames().playingEvent);
847
848         if (isVideo())
849             static_cast<HTMLVideoElement*>(this)->updatePosterImage();
850     }
851
852     if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) {
853         if (oldState <= HAVE_CURRENT_DATA)
854             scheduleEvent(eventNames().canplayEvent);
855
856         scheduleEvent(eventNames().canplaythroughEvent);
857
858         if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
859             scheduleEvent(eventNames().playingEvent);
860
861         if (m_autoplaying && m_paused && autoplay()) {
862             m_paused = false;
863             scheduleEvent(eventNames().playEvent);
864             scheduleEvent(eventNames().playingEvent);
865         }
866
867         if (isVideo())
868             static_cast<HTMLVideoElement*>(this)->updatePosterImage();
869     }
870
871     updatePlayState();
872 }
873
874 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
875 {
876     ASSERT(m_player);
877     if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED)
878         return;
879
880     unsigned progress = m_player->bytesLoaded();
881     double time = WTF::currentTime();
882     double timedelta = time - m_previousProgressTime;
883
884     if (progress == m_previousProgress) {
885         if (timedelta > 3.0 && !m_sentStalledEvent) {
886             scheduleProgressEvent(eventNames().stalledEvent);
887             m_sentStalledEvent = true;
888         }
889     } else {
890         scheduleProgressEvent(eventNames().progressEvent);
891         m_previousProgress = progress;
892         m_previousProgressTime = time;
893         m_sentStalledEvent = false;
894     }
895 }
896
897 void HTMLMediaElement::rewind(float timeDelta)
898 {
899     ExceptionCode e;
900     setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e);
901 }
902
903 void HTMLMediaElement::returnToRealtime()
904 {
905     ExceptionCode e;
906     setCurrentTime(maxTimeSeekable(), e);
907 }  
908
909 void HTMLMediaElement::addPlayedRange(float start, float end)
910 {
911     if (!m_playedTimeRanges)
912         m_playedTimeRanges = TimeRanges::create();
913     m_playedTimeRanges->add(start, end);
914 }  
915
916 bool HTMLMediaElement::supportsSave() const
917 {
918     return m_player ? m_player->supportsSave() : false;
919 }
920     
921 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
922 {
923     // 4.8.10.10. Seeking
924     // 1
925     if (m_readyState == HAVE_NOTHING || !m_player) {
926         ec = INVALID_STATE_ERR;
927         return;
928     }
929
930     // 2
931     time = min(time, duration());
932
933     // 3
934     time = max(time, 0.0f);
935
936     // 4
937     RefPtr<TimeRanges> seekableRanges = seekable();
938     if (!seekableRanges->contain(time)) {
939         ec = INDEX_SIZE_ERR;
940         return;
941     }
942     
943     // avoid generating events when the time won't actually change
944     float now = currentTime();
945     if (time == now)
946         return;
947
948     // 5
949     if (m_playing) {
950         if (m_lastSeekTime < now)
951             addPlayedRange(m_lastSeekTime, now);
952     }
953     m_lastSeekTime = time;
954
955     // 6 - set the seeking flag, it will be cleared when the engine tells is the time has actually changed
956     m_seeking = true;
957
958     // 7
959     scheduleTimeupdateEvent(false);
960
961     // 8 - this is covered, if necessary, when the engine signals a readystate change
962
963     // 10
964     m_player->seek(time);
965     m_sentEndEvent = false;
966 }
967
968 void HTMLMediaElement::finishSeek()
969 {
970     // 4.8.10.10 Seeking step 12
971     m_seeking = false;
972
973     // 4.8.10.10 Seeking step 13
974     scheduleEvent(eventNames().seekedEvent);
975 }
976
977 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
978 {
979     return m_readyState;
980 }
981
982 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
983 {
984     return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
985 }
986
987 bool HTMLMediaElement::hasAudio() const
988 {
989     return m_player ? m_player->hasAudio() : false;
990 }
991
992 bool HTMLMediaElement::seeking() const
993 {
994     return m_seeking;
995 }
996
997 // playback state
998 float HTMLMediaElement::currentTime() const
999 {
1000     if (!m_player)
1001         return 0;
1002     if (m_seeking)
1003         return m_lastSeekTime;
1004     return m_player->currentTime();
1005 }
1006
1007 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
1008 {
1009     seek(time, ec);
1010 }
1011
1012 float HTMLMediaElement::startTime() const
1013 {
1014     if (!m_player)
1015         return 0;
1016     return m_player->startTime();
1017 }
1018
1019 float HTMLMediaElement::duration() const
1020 {
1021     if (m_readyState >= HAVE_METADATA)
1022         return m_player->duration();
1023
1024     return numeric_limits<float>::quiet_NaN();
1025 }
1026
1027 bool HTMLMediaElement::paused() const
1028 {
1029     return m_paused;
1030 }
1031
1032 float HTMLMediaElement::defaultPlaybackRate() const
1033 {
1034     return m_defaultPlaybackRate;
1035 }
1036
1037 void HTMLMediaElement::setDefaultPlaybackRate(float rate)
1038 {
1039     if (m_defaultPlaybackRate != rate) {
1040         m_defaultPlaybackRate = rate;
1041         scheduleEvent(eventNames().ratechangeEvent);
1042     }
1043 }
1044
1045 float HTMLMediaElement::playbackRate() const
1046 {
1047     return m_player ? m_player->rate() : 0;
1048 }
1049
1050 void HTMLMediaElement::setPlaybackRate(float rate)
1051 {
1052     if (m_playbackRate != rate) {
1053         m_playbackRate = rate;
1054         scheduleEvent(eventNames().ratechangeEvent);
1055     }
1056     if (m_player && potentiallyPlaying() && m_player->rate() != rate)
1057         m_player->setRate(rate);
1058 }
1059
1060 bool HTMLMediaElement::webkitPreservesPitch() const
1061 {
1062     return m_webkitPreservesPitch;
1063 }
1064
1065 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
1066 {
1067     m_webkitPreservesPitch = preservesPitch;
1068     
1069     if (!m_player)
1070         return;
1071
1072     m_player->setPreservesPitch(preservesPitch);
1073 }
1074
1075 bool HTMLMediaElement::ended() const
1076 {
1077     return endedPlayback();
1078 }
1079
1080 bool HTMLMediaElement::autoplay() const
1081 {
1082     return hasAttribute(autoplayAttr);
1083 }
1084
1085 void HTMLMediaElement::setAutoplay(bool b)
1086 {
1087     setBooleanAttribute(autoplayAttr, b);
1088 }
1089
1090 bool HTMLMediaElement::autobuffer() const
1091 {
1092     return hasAttribute(autobufferAttr);
1093 }
1094
1095 void HTMLMediaElement::setAutobuffer(bool b)
1096 {
1097     setBooleanAttribute(autobufferAttr, b);
1098 }
1099
1100 void HTMLMediaElement::play()
1101 {
1102     if (m_restrictions & RequireUserGestureForRateChangeRestriction && !processingUserGesture())
1103         return;
1104
1105     playInternal();
1106 }
1107
1108 void HTMLMediaElement::playInternal()
1109 {
1110     // 4.8.10.9. Playing the media resource
1111     if (!m_player || m_networkState == NETWORK_EMPTY)
1112         scheduleLoad();
1113
1114     if (endedPlayback()) {
1115         ExceptionCode unused;
1116         seek(0, unused);
1117     }
1118     
1119     setPlaybackRate(defaultPlaybackRate());
1120     
1121     if (m_paused) {
1122         m_paused = false;
1123         scheduleEvent(eventNames().playEvent);
1124
1125         if (m_readyState <= HAVE_CURRENT_DATA)
1126             scheduleEvent(eventNames().waitingEvent);
1127         else if (m_readyState >= HAVE_FUTURE_DATA)
1128             scheduleEvent(eventNames().playingEvent);
1129     }
1130     m_autoplaying = false;
1131
1132     updatePlayState();
1133 }
1134
1135 void HTMLMediaElement::pause()
1136 {
1137     if (m_restrictions & RequireUserGestureForRateChangeRestriction && !processingUserGesture())
1138         return;
1139
1140     pauseInternal();
1141 }
1142
1143
1144 void HTMLMediaElement::pauseInternal()
1145 {
1146     // 4.8.10.9. Playing the media resource
1147     if (!m_player || m_networkState == NETWORK_EMPTY)
1148         scheduleLoad();
1149
1150     m_autoplaying = false;
1151     
1152     if (!m_paused) {
1153         m_paused = true;
1154         scheduleTimeupdateEvent(false);
1155         scheduleEvent(eventNames().pauseEvent);
1156     }
1157
1158     updatePlayState();
1159 }
1160
1161 bool HTMLMediaElement::loop() const
1162 {
1163     return hasAttribute(loopAttr);
1164 }
1165
1166 void HTMLMediaElement::setLoop(bool b)
1167 {
1168     setBooleanAttribute(loopAttr, b);
1169 }
1170
1171 bool HTMLMediaElement::controls() const
1172 {
1173     Frame* frame = document()->frame();
1174
1175     // always show controls when scripting is disabled
1176     if (frame && !frame->script()->isEnabled())
1177         return true;
1178
1179     return hasAttribute(controlsAttr);
1180 }
1181
1182 void HTMLMediaElement::setControls(bool b)
1183 {
1184     setBooleanAttribute(controlsAttr, b);
1185 }
1186
1187 float HTMLMediaElement::volume() const
1188 {
1189     return m_volume;
1190 }
1191
1192 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
1193 {
1194     if (vol < 0.0f || vol > 1.0f) {
1195         ec = INDEX_SIZE_ERR;
1196         return;
1197     }
1198     
1199     if (m_volume != vol) {
1200         m_volume = vol;
1201         updateVolume();
1202         scheduleEvent(eventNames().volumechangeEvent);
1203     }
1204 }
1205
1206 bool HTMLMediaElement::muted() const
1207 {
1208     return m_muted;
1209 }
1210
1211 void HTMLMediaElement::setMuted(bool muted)
1212 {
1213     if (m_muted != muted) {
1214         m_muted = muted;
1215         updateVolume();
1216         scheduleEvent(eventNames().volumechangeEvent);
1217     }
1218 }
1219
1220 void HTMLMediaElement::togglePlayState()
1221 {
1222     // We can safely call the internal play/pause methods, which don't check restrictions, because
1223     // this method is only called from the built-in media controller
1224     if (canPlay())
1225         playInternal();
1226     else 
1227         pauseInternal();
1228 }
1229
1230 void HTMLMediaElement::beginScrubbing()
1231 {
1232     if (!paused()) {
1233         if (ended()) {
1234             // Because a media element stays in non-paused state when it reaches end, playback resumes 
1235             // when the slider is dragged from the end to another position unless we pause first. Do 
1236             // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
1237             pause();
1238         } else {
1239             // Not at the end but we still want to pause playback so the media engine doesn't try to
1240             // continue playing during scrubbing. Pause without generating an event as we will 
1241             // unpause after scrubbing finishes.
1242             setPausedInternal(true);
1243         }
1244     }
1245 }
1246
1247 void HTMLMediaElement::endScrubbing()
1248 {
1249     if (m_pausedInternal)
1250         setPausedInternal(false);
1251 }
1252
1253 // The spec says to fire periodic timeupdate events (those sent while playing) every
1254 // "15 to 250ms", we choose the slowest frequency
1255 static const double maxTimeupdateEventFrequency = 0.25;
1256
1257 void HTMLMediaElement::startPlaybackProgressTimer()
1258 {
1259     if (m_playbackProgressTimer.isActive())
1260         return;
1261
1262     m_previousProgressTime = WTF::currentTime();
1263     m_previousProgress = 0;
1264     m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
1265 }
1266
1267 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
1268 {
1269     ASSERT(m_player);
1270     if (!m_playbackRate)
1271         return;
1272
1273     scheduleTimeupdateEvent(true);
1274     
1275     // FIXME: deal with cue ranges here
1276 }
1277
1278 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
1279 {
1280     double now = WTF::currentTime();
1281     double timedelta = now - m_lastTimeUpdateEventWallTime;
1282
1283     // throttle the periodic events
1284     if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
1285         return;
1286
1287     // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
1288     // event at a given time so filter here
1289     float movieTime = m_player ? m_player->currentTime() : 0;
1290     if (movieTime != m_lastTimeUpdateEventMovieTime) {
1291         scheduleEvent(eventNames().timeupdateEvent);
1292         m_lastTimeUpdateEventWallTime = now;
1293         m_lastTimeUpdateEventMovieTime = movieTime;
1294     }
1295 }
1296
1297 bool HTMLMediaElement::canPlay() const
1298 {
1299     return paused() || ended() || m_readyState < HAVE_METADATA;
1300 }
1301
1302 float HTMLMediaElement::percentLoaded() const
1303 {
1304     if (!m_player)
1305         return 0;
1306     float duration = m_player->duration();
1307
1308     if (!duration || isinf(duration))
1309         return 0;
1310
1311     float buffered = 0;
1312     RefPtr<TimeRanges> timeRanges = m_player->buffered();
1313     for (unsigned i = 0; i < timeRanges->length(); ++i) {
1314         ExceptionCode ignoredException;
1315         float start = timeRanges->start(i, ignoredException);
1316         float end = timeRanges->end(i, ignoredException);
1317         buffered += end - start;
1318     }
1319     return buffered / duration;
1320 }
1321
1322 bool HTMLMediaElement::havePotentialSourceChild()
1323 {
1324     // Stash the current <source> node so we can restore it after checking
1325     // to see there is another potential
1326     HTMLSourceElement* currentSourceNode = m_currentSourceNode;
1327     KURL nextURL = selectNextSourceChild(0, DoNothing);
1328     m_currentSourceNode = currentSourceNode;
1329
1330     return nextURL.isValid();
1331 }
1332
1333 KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSourceAction actionIfInvalid)
1334 {
1335     KURL mediaURL;
1336     Node* node;
1337     bool lookingForPreviousNode = m_currentSourceNode;
1338     bool canUse = false;
1339
1340     for (node = firstChild(); !canUse && node; node = node->nextSibling()) {
1341         if (!node->hasTagName(sourceTag))
1342             continue;
1343
1344         if (lookingForPreviousNode) {
1345             if (m_currentSourceNode == static_cast<HTMLSourceElement*>(node))
1346                 lookingForPreviousNode = false;
1347             continue;
1348         }
1349
1350         HTMLSourceElement* source = static_cast<HTMLSourceElement*>(node);
1351         if (!source->hasAttribute(srcAttr))
1352             goto check_again; 
1353
1354         if (source->hasAttribute(mediaAttr)) {
1355             MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
1356             RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media());
1357             if (!screenEval.eval(media.get())) 
1358                 goto check_again;
1359         }
1360
1361         if (source->hasAttribute(typeAttr)) {
1362             if (!MediaPlayer::supportsType(ContentType(source->type())))
1363                 goto check_again;
1364         }
1365
1366         // Is it safe to load this url?
1367         mediaURL = source->src();
1368         if (!mediaURL.isValid() || !isSafeToLoadURL(mediaURL, actionIfInvalid))
1369             goto check_again;
1370
1371         // Making it this far means the <source> looks reasonable
1372         canUse = true;
1373         if (contentType)
1374             *contentType = ContentType(source->type());
1375
1376 check_again:
1377         if (!canUse && actionIfInvalid == Complain)
1378             source->scheduleErrorEvent();
1379         m_currentSourceNode = static_cast<HTMLSourceElement*>(node);
1380     }
1381
1382     if (!canUse)
1383         m_currentSourceNode = 0;
1384     return canUse ? mediaURL : KURL();
1385 }
1386
1387 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
1388 {
1389     beginProcessingMediaPlayerCallback();
1390
1391     // 4.8.10.10 step 12 & 13.  Needed if no ReadyState change is associated with the seek.
1392     if (m_readyState >= HAVE_CURRENT_DATA && m_seeking) {
1393         finishSeek();
1394     }
1395     
1396     float now = currentTime();
1397     float dur = duration();
1398     if (!isnan(dur) && dur && now >= dur) {
1399         if (loop()) {
1400             ExceptionCode ignoredException;
1401             m_sentEndEvent = false;
1402             seek(0, ignoredException);
1403         } else {
1404             if (!m_sentEndEvent) {
1405                 m_sentEndEvent = true;
1406                 scheduleTimeupdateEvent(false);
1407                 scheduleEvent(eventNames().endedEvent);
1408             }
1409         }
1410     }
1411     else
1412         m_sentEndEvent = false;
1413
1414     updatePlayState();
1415     endProcessingMediaPlayerCallback();
1416 }
1417
1418 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
1419 {
1420     beginProcessingMediaPlayerCallback();
1421     updateVolume();
1422     endProcessingMediaPlayerCallback();
1423 }
1424
1425 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*)
1426 {
1427     beginProcessingMediaPlayerCallback();
1428     scheduleEvent(eventNames().durationchangeEvent);
1429 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1430     if (renderer()) {
1431         renderer()->updateFromElement();
1432         if (renderer()->isVideo())
1433             toRenderVideo(renderer())->videoSizeChanged();
1434     }
1435 #endif        
1436     endProcessingMediaPlayerCallback();
1437 }
1438
1439 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
1440 {
1441     beginProcessingMediaPlayerCallback();
1442     // Stash the rate in case the one we tried to set isn't what the engine is
1443     // using (eg. it can't handle the rate we set)
1444     m_playbackRate = m_player->rate();
1445     endProcessingMediaPlayerCallback();
1446 }
1447
1448 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*)
1449 {
1450     // The MediaPlayer came across content it cannot completely handle.
1451     // This is normally acceptable except when we are in a standalone
1452     // MediaDocument. If so, tell the document what has happened.
1453     if (ownerDocument()->isMediaDocument()) {
1454         MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument());
1455         mediaDocument->mediaElementSawUnsupportedTracks();
1456     }
1457 }
1458
1459 // MediaPlayerPresentation methods
1460 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
1461 {
1462     beginProcessingMediaPlayerCallback();
1463     if (renderer())
1464         renderer()->repaint();
1465     endProcessingMediaPlayerCallback();
1466 }
1467
1468 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*)
1469 {
1470     beginProcessingMediaPlayerCallback();
1471 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1472     if (renderer() && renderer()->isVideo())
1473         toRenderVideo(renderer())->videoSizeChanged();
1474 #endif        
1475     endProcessingMediaPlayerCallback();
1476 }
1477
1478 #if USE(ACCELERATED_COMPOSITING)
1479 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*)
1480 {
1481     if (renderer() && renderer()->isVideo()) {
1482         ASSERT(renderer()->view());
1483         return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer()));
1484     }
1485     return false;
1486 }
1487
1488 GraphicsLayer* HTMLMediaElement::mediaPlayerGraphicsLayer(MediaPlayer*)
1489 {
1490     if (renderer() && renderer()->isVideo())
1491         return toRenderVideo(renderer())->videoGraphicsLayer();
1492     return 0;
1493 }
1494 #endif
1495
1496 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
1497 {
1498     if (!m_player)
1499         return TimeRanges::create();
1500     return m_player->buffered();
1501 }
1502
1503 PassRefPtr<TimeRanges> HTMLMediaElement::played()
1504 {
1505     if (m_playing) {
1506         float time = currentTime();
1507         if (time > m_lastSeekTime)
1508             addPlayedRange(m_lastSeekTime, time);
1509     }
1510
1511     if (!m_playedTimeRanges)
1512         m_playedTimeRanges = TimeRanges::create();
1513
1514     return m_playedTimeRanges->copy();
1515 }
1516
1517 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
1518 {
1519     // FIXME real ranges support
1520     if (!maxTimeSeekable())
1521         return TimeRanges::create();
1522     return TimeRanges::create(minTimeSeekable(), maxTimeSeekable());
1523 }
1524
1525 bool HTMLMediaElement::potentiallyPlaying() const
1526 {
1527     return !paused() && m_readyState >= HAVE_FUTURE_DATA && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
1528 }
1529
1530 bool HTMLMediaElement::endedPlayback() const
1531 {
1532     if (!m_player || m_readyState < HAVE_METADATA)
1533         return false;
1534     
1535     float dur = duration();
1536     return !isnan(dur) && currentTime() >= dur && !loop();
1537 }
1538
1539 bool HTMLMediaElement::stoppedDueToErrors() const
1540 {
1541     if (m_readyState >= HAVE_METADATA && m_error) {
1542         RefPtr<TimeRanges> seekableRanges = seekable();
1543         if (!seekableRanges->contain(currentTime()))
1544             return true;
1545     }
1546     
1547     return false;
1548 }
1549
1550 bool HTMLMediaElement::pausedForUserInteraction() const
1551 {
1552 //    return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user]
1553     return false;
1554 }
1555
1556 float HTMLMediaElement::minTimeSeekable() const
1557 {
1558     return 0;
1559 }
1560
1561 float HTMLMediaElement::maxTimeSeekable() const
1562 {
1563     return m_player ? m_player->maxTimeSeekable() : 0;
1564 }
1565     
1566 void HTMLMediaElement::updateVolume()
1567 {
1568     if (!m_player)
1569         return;
1570
1571     // Avoid recursion when the player reports volume changes.
1572     if (!processingMediaPlayerCallback()) {
1573         Page* page = document()->page();
1574         float volumeMultiplier = page ? page->mediaVolume() : 1;
1575     
1576         m_player->setVolume(m_muted ? 0 : m_volume * volumeMultiplier);
1577     }
1578     
1579     if (renderer())
1580         renderer()->updateFromElement();
1581 }
1582
1583 void HTMLMediaElement::updatePlayState()
1584 {
1585     if (!m_player)
1586         return;
1587
1588     if (m_pausedInternal) {
1589         if (!m_player->paused())
1590             m_player->pause();
1591         m_playbackProgressTimer.stop();
1592         return;
1593     }
1594     
1595     bool shouldBePlaying = potentiallyPlaying();
1596     bool playerPaused = m_player->paused();
1597     if (shouldBePlaying && playerPaused) {
1598         // Set rate before calling play in case the rate was set before the media engine wasn't setup.
1599         // The media engine should just stash the rate since it isn't already playing.
1600         m_player->setRate(m_playbackRate);
1601         m_player->play();
1602         startPlaybackProgressTimer();
1603         m_playing = true;
1604     } else if (!shouldBePlaying && !playerPaused) {
1605         m_player->pause();
1606         m_playbackProgressTimer.stop();
1607         m_playing = false;
1608         float time = currentTime();
1609         if (time > m_lastSeekTime)
1610             addPlayedRange(m_lastSeekTime, time);
1611     }
1612     
1613     if (renderer())
1614         renderer()->updateFromElement();
1615 }
1616     
1617 void HTMLMediaElement::setPausedInternal(bool b)
1618 {
1619     m_pausedInternal = b;
1620     updatePlayState();
1621 }
1622
1623 void HTMLMediaElement::stopPeriodicTimers()
1624 {
1625     m_progressEventTimer.stop();
1626     m_playbackProgressTimer.stop();
1627 }
1628
1629 void HTMLMediaElement::userCancelledLoad()
1630 {
1631     if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED)
1632         return;
1633
1634     // If the media data fetching process is aborted by the user:
1635
1636     // 1 - The user agent should cancel the fetching process.
1637 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1638     m_player.clear();
1639 #endif
1640     stopPeriodicTimers();
1641
1642     // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORT.
1643     m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
1644
1645     // 3 - Queue a task to fire a progress event called abort at the media element, in the context
1646     // of the fetching process started by this instance of this algorithm.
1647     scheduleProgressEvent(eventNames().abortEvent);
1648
1649     // 4 - Queue a task to fire a progress event called loadend at the media element, in the context
1650     // of the fetching process started by this instance of this algorithm.
1651     scheduleProgressEvent(eventNames().loadendEvent);
1652
1653     // 5 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
1654     // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
1655     // simple event called emptied at the element. Otherwise, set set the element's networkState
1656     // attribute to the NETWORK_IDLE value.
1657     if (m_readyState == HAVE_NOTHING) {
1658         m_networkState = NETWORK_EMPTY;
1659         scheduleEvent(eventNames().emptiedEvent);
1660     }
1661     else
1662         m_networkState = NETWORK_IDLE;
1663
1664     // 6 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1665     m_delayingTheLoadEvent = false;
1666
1667     // 7 - Abort the overall resource selection algorithm.
1668     m_currentSourceNode = 0;
1669 }
1670
1671 void HTMLMediaElement::documentWillBecomeInactive()
1672 {
1673     m_inActiveDocument = false;
1674     userCancelledLoad();
1675
1676     // Stop the playback without generating events
1677     setPausedInternal(true);
1678
1679     if (renderer())
1680         renderer()->updateFromElement();
1681
1682     stopPeriodicTimers();
1683     cancelPendingEventsAndCallbacks();
1684 }
1685
1686 void HTMLMediaElement::documentDidBecomeActive()
1687 {
1688     m_inActiveDocument = true;
1689     setPausedInternal(false);
1690
1691     if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) {
1692         // Restart the load if it was aborted in the middle by moving the document to the page cache.
1693         // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to
1694         //  MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards).
1695         // This behavior is not specified but it seems like a sensible thing to do.
1696         ExceptionCode ec;
1697         load(ec);
1698     }
1699
1700     if (renderer())
1701         renderer()->updateFromElement();
1702 }
1703
1704 void HTMLMediaElement::mediaVolumeDidChange()
1705 {
1706     updateVolume();
1707 }
1708
1709 const IntRect HTMLMediaElement::screenRect()
1710 {
1711     IntRect elementRect;
1712     if (renderer())
1713         elementRect = renderer()->view()->frameView()->contentsToScreen(renderer()->absoluteBoundingBoxRect());
1714     return elementRect;
1715 }
1716     
1717 void HTMLMediaElement::defaultEventHandler(Event* event)
1718 {
1719 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1720     RenderObject* r = renderer();
1721     if (!r || !r->isWidget())
1722         return;
1723
1724     Widget* widget = toRenderWidget(r)->widget();
1725     if (widget)
1726         widget->handleEvent(event);
1727 #else
1728     if (renderer() && renderer()->isMedia())
1729         toRenderMedia(renderer())->forwardEvent(event);
1730     if (event->defaultHandled())
1731         return;
1732     HTMLElement::defaultEventHandler(event);
1733 #endif
1734 }
1735
1736 bool HTMLMediaElement::processingUserGesture() const
1737 {
1738     Frame* frame = document()->frame();
1739     FrameLoader* loader = frame ? frame->loader() : 0;
1740
1741     // return 'true' for safety if we don't know the answer 
1742     return loader ? loader->isProcessingUserGesture() : true;
1743 }
1744
1745 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1746
1747 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification)
1748 {
1749     if (notification == MediaPlayerNotificationPlayPauseButtonPressed) {
1750         togglePlayState();
1751         return;
1752     }
1753
1754     if (m_player)
1755         m_player->deliverNotification(notification);
1756 }
1757
1758 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy)
1759 {
1760     if (m_player)
1761         m_player->setMediaPlayerProxy(proxy);
1762 }
1763
1764 String HTMLMediaElement::initialURL()
1765 {
1766     KURL initialSrc = document()->completeURL(getAttribute(srcAttr));
1767     
1768     if (!initialSrc.isValid())
1769         initialSrc = selectNextSourceChild(0, DoNothing);
1770
1771     m_currentSrc = initialSrc.string();
1772
1773     return initialSrc;
1774 }
1775
1776 void HTMLMediaElement::finishParsingChildren()
1777 {
1778     HTMLElement::finishParsingChildren();
1779     if (!m_player)
1780         m_player.set(new MediaPlayer(this));
1781     
1782     document()->updateStyleIfNeeded();
1783     if (m_needWidgetUpdate && renderer())
1784         toRenderPartObject(renderer())->updateWidget(true);
1785 }
1786
1787 #endif
1788
1789 void HTMLMediaElement::enterFullscreen()
1790 {
1791     ASSERT(!m_isFullscreen);
1792     if (!renderer())
1793         return;
1794     if (document() && document()->page())
1795         document()->page()->chrome()->client()->enterFullscreenForNode(this);
1796     m_isFullscreen = true;
1797 }
1798
1799 void HTMLMediaElement::exitFullscreen()
1800 {
1801     ASSERT(m_isFullscreen);
1802     if (document() && document()->page())
1803         document()->page()->chrome()->client()->exitFullscreenForNode(this);
1804     m_isFullscreen = false;
1805 }
1806
1807 PlatformMedia HTMLMediaElement::platformMedia() const
1808 {
1809     return m_player ? m_player->platformMedia() : NoPlatformMedia;
1810 }        
1811 }
1812
1813 #endif