1/*
2 * Copyright (C) 2007, 2008, 2009, 2010, 2011 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 "Attribute.h"
32#include "Chrome.h"
33#include "ChromeClient.h"
34#include "ClientRect.h"
35#include "ClientRectList.h"
36#include "ContentSecurityPolicy.h"
37#include "ContentType.h"
38#include "CSSPropertyNames.h"
39#include "CSSValueKeywords.h"
40#include "Event.h"
41#include "EventNames.h"
42#include "ExceptionCode.h"
43#include "Frame.h"
44#include "FrameLoader.h"
45#include "FrameLoaderClient.h"
46#include "FrameView.h"
47#include "HTMLDocument.h"
48#include "HTMLNames.h"
49#include "HTMLSourceElement.h"
50#include "HTMLVideoElement.h"
51#include "Logging.h"
52#include "MediaControls.h"
53#include "MediaDocument.h"
54#include "MediaError.h"
55#include "MediaList.h"
56#include "MediaPlayer.h"
57#include "MediaQueryEvaluator.h"
58#include "MouseEvent.h"
59#include "MIMETypeRegistry.h"
60#include "Page.h"
61#include "RenderVideo.h"
62#include "RenderView.h"
63#include "ScriptEventListener.h"
64#include "Settings.h"
65#include "ShadowRoot.h"
66#include "TimeRanges.h"
67#include <limits>
68#include <wtf/CurrentTime.h>
69#include <wtf/MathExtras.h>
70#include <wtf/text/CString.h>
71
72#if USE(ACCELERATED_COMPOSITING)
73#include "RenderView.h"
74#include "RenderLayerCompositor.h"
75#endif
76
77#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
78#include "RenderEmbeddedObject.h"
79#include "Widget.h"
80#endif
81
82#if PLATFORM(ANDROID)
83// For every touch, show the media control for 4 seconds.
84#define TOUCH_DELAY 4
85#endif
86
87using namespace std;
88
89namespace WebCore {
90
91#if !LOG_DISABLED
92static String urlForLogging(const String& url)
93{
94    static const unsigned maximumURLLengthForLogging = 128;
95
96    if (url.length() < maximumURLLengthForLogging)
97        return url;
98    return url.substring(0, maximumURLLengthForLogging) + "...";
99}
100
101static const char *boolString(bool val)
102{
103    return val ? "true" : "false";
104}
105#endif
106
107#ifndef LOG_MEDIA_EVENTS
108// Default to not logging events because so many are generated they can overwhelm the rest of
109// the logging.
110#define LOG_MEDIA_EVENTS 0
111#endif
112
113#ifndef LOG_CACHED_TIME_WARNINGS
114// Default to not logging warnings about excessive drift in the cached media time because it adds a
115// fair amount of overhead and logging.
116#define LOG_CACHED_TIME_WARNINGS 0
117#endif
118
119static const float invalidMediaTime = -1;
120
121using namespace HTMLNames;
122
123HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document)
124    : HTMLElement(tagName, document)
125    , ActiveDOMObject(document, this)
126    , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
127    , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired)
128    , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
129    , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
130    , m_playedTimeRanges()
131    , m_playbackRate(1.0f)
132    , m_defaultPlaybackRate(1.0f)
133    , m_webkitPreservesPitch(true)
134    , m_networkState(NETWORK_EMPTY)
135    , m_readyState(HAVE_NOTHING)
136    , m_readyStateMaximum(HAVE_NOTHING)
137    , m_volume(1.0f)
138    , m_lastSeekTime(0)
139    , m_previousProgress(0)
140    , m_previousProgressTime(numeric_limits<double>::max())
141    , m_lastTimeUpdateEventWallTime(0)
142    , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max())
143    , m_loadState(WaitingForSource)
144    , m_currentSourceNode(0)
145    , m_nextChildNodeToConsider(0)
146    , m_player(0)
147#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
148    , m_proxyWidget(0)
149#endif
150    , m_restrictions(RequireUserGestureForFullScreenRestriction)
151    , m_preload(MediaPlayer::Auto)
152    , m_displayMode(Unknown)
153    , m_processingMediaPlayerCallback(0)
154    , m_cachedTime(invalidMediaTime)
155    , m_cachedTimeWallClockUpdateTime(0)
156    , m_minimumWallClockTimeToCacheMediaTime(0)
157    , m_playing(false)
158    , m_isWaitingUntilMediaCanStart(false)
159    , m_shouldDelayLoadEvent(false)
160    , m_haveFiredLoadedData(false)
161    , m_inActiveDocument(true)
162    , m_autoplaying(true)
163    , m_muted(false)
164    , m_paused(true)
165    , m_seeking(false)
166    , m_sentStalledEvent(false)
167    , m_sentEndEvent(false)
168    , m_pausedInternal(false)
169    , m_sendProgressEvents(true)
170    , m_isFullscreen(false)
171    , m_closedCaptionsVisible(false)
172    , m_mouseOver(false)
173#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
174    , m_needWidgetUpdate(false)
175#endif
176    , m_dispatchingCanPlayEvent(false)
177    , m_loadInitiatedByUserGesture(false)
178    , m_completelyLoaded(false)
179#if PLATFORM(ANDROID)
180    , m_lastTouch(0)
181    , m_userGestureInitiated(false)
182#endif
183{
184    LOG(Media, "HTMLMediaElement::HTMLMediaElement");
185    document->registerForDocumentActivationCallbacks(this);
186    document->registerForMediaVolumeCallbacks(this);
187    document->registerForPrivateBrowsingStateChangedCallbacks(this);
188#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS)
189    m_restrictions |= RequireUserGestureForRateChangeRestriction;
190#endif
191}
192
193HTMLMediaElement::~HTMLMediaElement()
194{
195    LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
196    if (m_isWaitingUntilMediaCanStart)
197        document()->removeMediaCanStartListener(this);
198    setShouldDelayLoadEvent(false);
199    document()->unregisterForDocumentActivationCallbacks(this);
200    document()->unregisterForMediaVolumeCallbacks(this);
201    document()->unregisterForPrivateBrowsingStateChangedCallbacks(this);
202}
203
204void HTMLMediaElement::willMoveToNewOwnerDocument()
205{
206    if (m_isWaitingUntilMediaCanStart)
207        document()->removeMediaCanStartListener(this);
208    setShouldDelayLoadEvent(false);
209    document()->unregisterForDocumentActivationCallbacks(this);
210    document()->unregisterForMediaVolumeCallbacks(this);
211    HTMLElement::willMoveToNewOwnerDocument();
212}
213
214void HTMLMediaElement::didMoveToNewOwnerDocument()
215{
216    if (m_isWaitingUntilMediaCanStart)
217        document()->addMediaCanStartListener(this);
218    if (m_readyState < HAVE_CURRENT_DATA)
219        setShouldDelayLoadEvent(true);
220    document()->registerForDocumentActivationCallbacks(this);
221    document()->registerForMediaVolumeCallbacks(this);
222    HTMLElement::didMoveToNewOwnerDocument();
223}
224
225void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls)
226{
227    HTMLElement::attributeChanged(attr, preserveDecls);
228
229    const QualifiedName& attrName = attr->name();
230    if (attrName == srcAttr) {
231        // Trigger a reload, as long as the 'src' attribute is present.
232        if (!getAttribute(srcAttr).isEmpty())
233            scheduleLoad();
234    }
235    else if (attrName == controlsAttr) {
236#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
237        if (controls()) {
238            if (!hasMediaControls()) {
239                if (!createMediaControls())
240                    return;
241
242                mediaControls()->reset();
243            }
244            mediaControls()->show();
245        } else if (hasMediaControls())
246            mediaControls()->hide();
247#else
248        if (m_player)
249            m_player->setControls(controls());
250#endif
251    }
252}
253
254void HTMLMediaElement::parseMappedAttribute(Attribute* attr)
255{
256    const QualifiedName& attrName = attr->name();
257
258    if (attrName == preloadAttr) {
259        String value = attr->value();
260
261        if (equalIgnoringCase(value, "none"))
262            m_preload = MediaPlayer::None;
263        else if (equalIgnoringCase(value, "metadata"))
264            m_preload = MediaPlayer::MetaData;
265        else {
266            // The spec does not define an "invalid value default" but "auto" is suggested as the
267            // "missing value default", so use it for everything except "none" and "metadata"
268            m_preload = MediaPlayer::Auto;
269        }
270
271        // The attribute must be ignored if the autoplay attribute is present
272        if (!autoplay() && m_player)
273            m_player->setPreload(m_preload);
274
275    } else if (attrName == onabortAttr)
276        setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr));
277    else if (attrName == onbeforeloadAttr)
278        setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr));
279    else if (attrName == oncanplayAttr)
280        setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, attr));
281    else if (attrName == oncanplaythroughAttr)
282        setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, attr));
283    else if (attrName == ondurationchangeAttr)
284        setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, attr));
285    else if (attrName == onemptiedAttr)
286        setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, attr));
287    else if (attrName == onendedAttr)
288        setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attr));
289    else if (attrName == onerrorAttr)
290        setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr));
291    else if (attrName == onloadeddataAttr)
292        setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr));
293    else if (attrName == onloadedmetadataAttr)
294        setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attr));
295    else if (attrName == onloadstartAttr)
296        setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attr));
297    else if (attrName == onpauseAttr)
298        setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, attr));
299    else if (attrName == onplayAttr)
300        setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, attr));
301    else if (attrName == onplayingAttr)
302        setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, attr));
303    else if (attrName == onprogressAttr)
304        setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, attr));
305    else if (attrName == onratechangeAttr)
306        setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, attr));
307    else if (attrName == onseekedAttr)
308        setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, attr));
309    else if (attrName == onseekingAttr)
310        setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, attr));
311    else if (attrName == onstalledAttr)
312        setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, attr));
313    else if (attrName == onsuspendAttr)
314        setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, attr));
315    else if (attrName == ontimeupdateAttr)
316        setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, attr));
317    else if (attrName == onvolumechangeAttr)
318        setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, attr));
319    else if (attrName == onwaitingAttr)
320        setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, attr));
321    else if (attrName == onwebkitbeginfullscreenAttr)
322        setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, attr));
323    else if (attrName == onwebkitendfullscreenAttr)
324        setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, attr));
325    else
326        HTMLElement::parseMappedAttribute(attr);
327}
328
329bool HTMLMediaElement::rendererIsNeeded(RenderStyle* style)
330{
331#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
332    UNUSED_PARAM(style);
333    Frame* frame = document()->frame();
334    if (!frame)
335        return false;
336
337    return true;
338#else
339    return controls() ? HTMLElement::rendererIsNeeded(style) : false;
340#endif
341}
342
343RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
344{
345#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
346    // Setup the renderer if we already have a proxy widget.
347    RenderEmbeddedObject* mediaRenderer = new (arena) RenderEmbeddedObject(this);
348    if (m_proxyWidget) {
349        mediaRenderer->setWidget(m_proxyWidget);
350
351        Frame* frame = document()->frame();
352        FrameLoader* loader = frame ? frame->loader() : 0;
353        if (loader)
354            loader->showMediaPlayerProxyPlugin(m_proxyWidget.get());
355    }
356    return mediaRenderer;
357#else
358    return new (arena) RenderMedia(this);
359#endif
360}
361
362void HTMLMediaElement::insertedIntoDocument()
363{
364    LOG(Media, "HTMLMediaElement::removedFromDocument");
365    HTMLElement::insertedIntoDocument();
366    if (!getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY)
367        scheduleLoad();
368}
369
370void HTMLMediaElement::removedFromDocument()
371{
372    LOG(Media, "HTMLMediaElement::removedFromDocument");
373    if (m_networkState > NETWORK_EMPTY)
374        pause(processingUserGesture());
375    if (m_isFullscreen)
376        exitFullscreen();
377    HTMLElement::removedFromDocument();
378}
379
380void HTMLMediaElement::attach()
381{
382    ASSERT(!attached());
383
384#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
385    m_needWidgetUpdate = true;
386#endif
387
388    HTMLElement::attach();
389
390    if (renderer())
391        renderer()->updateFromElement();
392#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
393    else if (m_proxyWidget) {
394        Frame* frame = document()->frame();
395        FrameLoader* loader = frame ? frame->loader() : 0;
396        if (loader)
397            loader->hideMediaPlayerProxyPlugin(m_proxyWidget.get());
398    }
399#endif
400}
401
402void HTMLMediaElement::recalcStyle(StyleChange change)
403{
404    HTMLElement::recalcStyle(change);
405
406    if (renderer())
407        renderer()->updateFromElement();
408}
409
410void HTMLMediaElement::scheduleLoad()
411{
412    LOG(Media, "HTMLMediaElement::scheduleLoad");
413#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
414    createMediaPlayerProxy();
415#endif
416
417    if (m_loadTimer.isActive())
418        return;
419    prepareForLoad();
420    m_loadTimer.startOneShot(0);
421}
422
423void HTMLMediaElement::scheduleNextSourceChild()
424{
425    // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
426    m_loadTimer.startOneShot(0);
427}
428
429void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
430{
431#if LOG_MEDIA_EVENTS
432    LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data());
433#endif
434    m_pendingEvents.append(Event::create(eventName, false, true));
435    if (!m_asyncEventTimer.isActive())
436        m_asyncEventTimer.startOneShot(0);
437}
438
439void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
440{
441    Vector<RefPtr<Event> > pendingEvents;
442    ExceptionCode ec = 0;
443
444    m_pendingEvents.swap(pendingEvents);
445    unsigned count = pendingEvents.size();
446    for (unsigned ndx = 0; ndx < count; ++ndx) {
447#if LOG_MEDIA_EVENTS
448        LOG(Media, "HTMLMediaElement::asyncEventTimerFired - dispatching '%s'", pendingEvents[ndx]->type().string().ascii().data());
449#endif
450        if (pendingEvents[ndx]->type() == eventNames().canplayEvent) {
451            m_dispatchingCanPlayEvent = true;
452            dispatchEvent(pendingEvents[ndx].release(), ec);
453            m_dispatchingCanPlayEvent = false;
454        } else
455            dispatchEvent(pendingEvents[ndx].release(), ec);
456    }
457}
458
459void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
460{
461    if (m_loadState == LoadingFromSourceElement)
462        loadNextSourceChild();
463    else
464        loadInternal();
465}
466
467PassRefPtr<MediaError> HTMLMediaElement::error() const
468{
469    return m_error;
470}
471
472void HTMLMediaElement::setSrc(const String& url)
473{
474    setAttribute(srcAttr, url);
475}
476
477String HTMLMediaElement::currentSrc() const
478{
479    return m_currentSrc;
480}
481
482HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
483{
484    return m_networkState;
485}
486
487String HTMLMediaElement::canPlayType(const String& mimeType) const
488{
489    MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType));
490    String canPlay;
491
492    // 4.8.10.3
493    switch (support)
494    {
495        case MediaPlayer::IsNotSupported:
496            canPlay = "";
497            break;
498        case MediaPlayer::MayBeSupported:
499            canPlay = "maybe";
500            break;
501        case MediaPlayer::IsSupported:
502            canPlay = "probably";
503            break;
504    }
505
506    LOG(Media, "HTMLMediaElement::canPlayType(%s) -> %s", mimeType.utf8().data(), canPlay.utf8().data());
507
508    return canPlay;
509}
510
511void HTMLMediaElement::load(bool isUserGesture, ExceptionCode& ec)
512{
513    LOG(Media, "HTMLMediaElement::load(isUserGesture : %s)", boolString(isUserGesture));
514
515    if (m_restrictions & RequireUserGestureForLoadRestriction && !isUserGesture)
516        ec = INVALID_STATE_ERR;
517    else {
518        m_loadInitiatedByUserGesture = isUserGesture;
519#if PLATFORM(ANDROID)
520        m_userGestureInitiated |= isUserGesture;
521#endif
522        prepareForLoad();
523        loadInternal();
524    }
525}
526
527void HTMLMediaElement::prepareForLoad()
528{
529    LOG(Media, "HTMLMediaElement::prepareForLoad");
530
531    // Perform the cleanup required for the resource load algorithm to run.
532    stopPeriodicTimers();
533    m_loadTimer.stop();
534    m_sentStalledEvent = false;
535    m_haveFiredLoadedData = false;
536    m_completelyLoaded = false;
537    m_displayMode = Unknown;
538
539    // 1 - Abort any already-running instance of the resource selection algorithm for this element.
540    m_loadState = WaitingForSource;
541    m_currentSourceNode = 0;
542
543    // 2 - If there are any tasks from the media element's media element event task source in
544    // one of the task queues, then remove those tasks.
545    cancelPendingEventsAndCallbacks();
546
547    // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
548    // a task to fire a simple event named abort at the media element.
549    if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
550        scheduleEvent(eventNames().abortEvent);
551
552#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
553    m_player = MediaPlayer::create(this);
554#else
555    if (m_player)
556        m_player->cancelLoad();
557    else
558        createMediaPlayerProxy();
559#endif
560
561    // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
562    if (m_networkState != NETWORK_EMPTY) {
563        m_networkState = NETWORK_EMPTY;
564        m_readyState = HAVE_NOTHING;
565        m_readyStateMaximum = HAVE_NOTHING;
566        refreshCachedTime();
567        m_paused = true;
568        m_seeking = false;
569        invalidateCachedTime();
570        scheduleEvent(eventNames().emptiedEvent);
571    }
572
573    // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
574    setPlaybackRate(defaultPlaybackRate());
575
576    // 6 - Set the error attribute to null and the autoplaying flag to true.
577    m_error = 0;
578    m_autoplaying = true;
579
580    // 7 - Invoke the media element's resource selection algorithm.
581
582    // 8 - Note: Playback of any previously playing media resource for this element stops.
583
584    // The resource selection algorithm
585    // 1 - Set the networkState to NETWORK_NO_SOURCE
586    m_networkState = NETWORK_NO_SOURCE;
587
588    // 2 - Asynchronously await a stable state.
589
590    m_playedTimeRanges = TimeRanges::create();
591    m_lastSeekTime = 0;
592    m_closedCaptionsVisible = false;
593
594    // The spec doesn't say to block the load event until we actually run the asynchronous section
595    // algorithm, but do it now because we won't start that until after the timer fires and the
596    // event may have already fired by then.
597    setShouldDelayLoadEvent(true);
598}
599
600void HTMLMediaElement::loadInternal()
601{
602    // If we can't start a load right away, start it later.
603    Page* page = document()->page();
604    if (page && !page->canStartMedia()) {
605        if (m_isWaitingUntilMediaCanStart)
606            return;
607        document()->addMediaCanStartListener(this);
608        m_isWaitingUntilMediaCanStart = true;
609        return;
610    }
611
612    selectMediaResource();
613}
614
615void HTMLMediaElement::selectMediaResource()
616{
617    LOG(Media, "HTMLMediaElement::selectMediaResource");
618
619    enum Mode { attribute, children };
620    Mode mode = attribute;
621
622    // 3 - ... the media element has neither a src attribute ...
623    if (!hasAttribute(srcAttr)) {
624        // ... nor a source element child: ...
625        Node* node;
626        for (node = firstChild(); node; node = node->nextSibling()) {
627            if (node->hasTagName(sourceTag))
628                break;
629        }
630
631        if (!node) {
632            m_loadState = WaitingForSource;
633            setShouldDelayLoadEvent(false);
634
635            // ... set the networkState to NETWORK_EMPTY, and abort these steps
636            m_networkState = NETWORK_EMPTY;
637
638            LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load");
639            return;
640        }
641
642        mode = children;
643    }
644
645    // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event),
646    // and set its networkState to NETWORK_LOADING.
647    setShouldDelayLoadEvent(true);
648    m_networkState = NETWORK_LOADING;
649
650    // 5
651    scheduleEvent(eventNames().loadstartEvent);
652
653    // 6 - If mode is attribute, then run these substeps
654    if (mode == attribute) {
655        // If the src attribute's value is the empty string ... jump down to the failed step below
656        KURL mediaURL = getNonEmptyURLAttribute(srcAttr);
657        if (mediaURL.isEmpty()) {
658            noneSupported();
659            LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'");
660            return;
661        }
662
663        if (isSafeToLoadURL(mediaURL, Complain) && dispatchBeforeLoadEvent(mediaURL.string())) {
664            ContentType contentType("");
665            m_loadState = LoadingFromSrcAttr;
666            loadResource(mediaURL, contentType);
667        } else
668            noneSupported();
669
670        LOG(Media, "HTMLMediaElement::selectMediaResource, 'src' not used");
671        return;
672    }
673
674    // Otherwise, the source elements will be used
675    m_currentSourceNode = 0;
676    loadNextSourceChild();
677}
678
679void HTMLMediaElement::loadNextSourceChild()
680{
681    ContentType contentType("");
682    KURL mediaURL = selectNextSourceChild(&contentType, Complain);
683    if (!mediaURL.isValid()) {
684        waitForSourceChange();
685        return;
686    }
687
688#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
689    // Recreate the media player for the new url
690    m_player = MediaPlayer::create(this);
691#endif
692
693    m_loadState = LoadingFromSourceElement;
694    loadResource(mediaURL, contentType);
695}
696
697void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType)
698{
699    ASSERT(isSafeToLoadURL(initialURL, Complain));
700
701    LOG(Media, "HTMLMediaElement::loadResource(%s, %s)", urlForLogging(initialURL.string()).utf8().data(), contentType.raw().utf8().data());
702
703    Frame* frame = document()->frame();
704    if (!frame)
705        return;
706    FrameLoader* loader = frame->loader();
707    if (!loader)
708        return;
709
710    KURL url(initialURL);
711    if (!loader->willLoadMediaElementURL(url))
712        return;
713
714    // The resource fetch algorithm
715    m_networkState = NETWORK_LOADING;
716
717    m_currentSrc = url;
718
719    LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLogging(m_currentSrc).utf8().data());
720
721    if (m_sendProgressEvents)
722        startProgressEventTimer();
723
724    Settings* settings = document()->settings();
725    bool privateMode = !settings || settings->privateBrowsingEnabled();
726    m_player->setPrivateBrowsingMode(privateMode);
727
728    if (!autoplay())
729        m_player->setPreload(m_preload);
730    m_player->setPreservesPitch(m_webkitPreservesPitch);
731    updateVolume();
732
733#if PLATFORM(ANDROID)
734    if (isVideo())
735        m_player->setMediaElementType(MediaPlayer::Video);
736    else
737        m_player->setMediaElementType(MediaPlayer::Audio);
738#endif
739    m_player->load(m_currentSrc, contentType);
740
741    // If there is no poster to display, allow the media engine to render video frames as soon as
742    // they are available.
743    updateDisplayState();
744
745    if (renderer())
746        renderer()->updateFromElement();
747}
748
749bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidSourceAction actionIfInvalid)
750{
751    if (!url.isValid()) {
752        LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLogging(url.string()).utf8().data());
753        return false;
754    }
755
756    Frame* frame = document()->frame();
757    if (!frame || !document()->securityOrigin()->canDisplay(url)) {
758        if (actionIfInvalid == Complain)
759            FrameLoader::reportLocalLoadFailed(frame, url.string());
760        LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLogging(url.string()).utf8().data());
761        return false;
762    }
763
764    if (!document()->contentSecurityPolicy()->allowMediaFromSource(url))
765        return false;
766
767    return true;
768}
769
770void HTMLMediaElement::startProgressEventTimer()
771{
772    if (m_progressEventTimer.isActive())
773        return;
774
775    m_previousProgressTime = WTF::currentTime();
776    m_previousProgress = 0;
777    // 350ms is not magic, it is in the spec!
778    m_progressEventTimer.startRepeating(0.350);
779}
780
781void HTMLMediaElement::waitForSourceChange()
782{
783    LOG(Media, "HTMLMediaElement::waitForSourceChange");
784
785    stopPeriodicTimers();
786    m_loadState = WaitingForSource;
787
788    // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
789    m_networkState = NETWORK_NO_SOURCE;
790
791    // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
792    setShouldDelayLoadEvent(false);
793}
794
795void HTMLMediaElement::noneSupported()
796{
797    LOG(Media, "HTMLMediaElement::noneSupported");
798
799    stopPeriodicTimers();
800    m_loadState = WaitingForSource;
801    m_currentSourceNode = 0;
802
803    // 5 - Reaching this step indicates that either the URL failed to resolve, or the media
804    // resource failed to load. Set the error attribute to a new MediaError object whose
805    // code attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED.
806    m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
807
808    // 6 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
809    m_networkState = NETWORK_NO_SOURCE;
810
811    // 7 - Queue a task to fire a progress event called error at the media element, in
812    // the context of the fetching process that was used to try to obtain the media
813    // resource in the resource fetch algorithm.
814    scheduleEvent(eventNames().errorEvent);
815
816    // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
817    setShouldDelayLoadEvent(false);
818
819    // 9 -Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource.
820
821    updateDisplayState();
822
823    if (renderer())
824        renderer()->updateFromElement();
825}
826
827void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
828{
829    LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code()));
830
831    // 1 - The user agent should cancel the fetching process.
832    stopPeriodicTimers();
833    m_loadState = WaitingForSource;
834
835    // 2 - Set the error attribute to a new MediaError object whose code attribute is
836    // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
837    m_error = err;
838
839    // 3 - Queue a task to fire a simple event named error at the media element.
840    scheduleEvent(eventNames().errorEvent);
841
842    // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
843    // task to fire a simple event called emptied at the element.
844    m_networkState = NETWORK_EMPTY;
845    scheduleEvent(eventNames().emptiedEvent);
846
847    // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
848    setShouldDelayLoadEvent(false);
849
850    // 6 - Abort the overall resource selection algorithm.
851    m_currentSourceNode = 0;
852}
853
854void HTMLMediaElement::cancelPendingEventsAndCallbacks()
855{
856    LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks");
857
858    m_pendingEvents.clear();
859
860    for (Node* node = firstChild(); node; node = node->nextSibling()) {
861        if (node->hasTagName(sourceTag))
862            static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent();
863    }
864}
865
866Document* HTMLMediaElement::mediaPlayerOwningDocument()
867{
868    Document* d = document();
869
870    if (!d)
871        d = ownerDocument();
872
873    return d;
874}
875
876void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
877{
878    beginProcessingMediaPlayerCallback();
879    setNetworkState(m_player->networkState());
880    endProcessingMediaPlayerCallback();
881}
882
883void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
884{
885    LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState));
886
887    if (state == MediaPlayer::Empty) {
888        // Just update the cached state and leave, we can't do anything.
889        m_networkState = NETWORK_EMPTY;
890        return;
891    }
892
893    if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
894        stopPeriodicTimers();
895
896        // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
897        // <source> children, schedule the next one
898        if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
899
900            if (m_currentSourceNode)
901                m_currentSourceNode->scheduleErrorEvent();
902            else
903                LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed");
904
905            if (havePotentialSourceChild()) {
906                LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>");
907                scheduleNextSourceChild();
908            } else {
909                LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting");
910                waitForSourceChange();
911            }
912
913            return;
914        }
915
916        if (state == MediaPlayer::NetworkError)
917            mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
918        else if (state == MediaPlayer::DecodeError)
919            mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
920        else if (state == MediaPlayer::FormatError && m_loadState == LoadingFromSrcAttr)
921            noneSupported();
922
923        updateDisplayState();
924        if (hasMediaControls())
925            mediaControls()->reportedError();
926        return;
927    }
928
929    if (state == MediaPlayer::Idle) {
930        if (m_networkState > NETWORK_IDLE) {
931            m_progressEventTimer.stop();
932            scheduleEvent(eventNames().suspendEvent);
933            setShouldDelayLoadEvent(false);
934        }
935        m_networkState = NETWORK_IDLE;
936    }
937
938    if (state == MediaPlayer::Loading) {
939        if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
940            startProgressEventTimer();
941        m_networkState = NETWORK_LOADING;
942    }
943
944    if (state == MediaPlayer::Loaded) {
945        if (m_networkState != NETWORK_IDLE) {
946            m_progressEventTimer.stop();
947
948            // Schedule one last progress event so we guarantee that at least one is fired
949            // for files that load very quickly.
950            scheduleEvent(eventNames().progressEvent);
951        }
952        m_networkState = NETWORK_IDLE;
953        m_completelyLoaded = true;
954    }
955
956    if (hasMediaControls())
957        mediaControls()->changedNetworkState();
958}
959
960void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
961{
962    beginProcessingMediaPlayerCallback();
963
964    setReadyState(m_player->readyState());
965
966    endProcessingMediaPlayerCallback();
967}
968
969void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
970{
971    LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState));
972
973    // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
974    bool wasPotentiallyPlaying = potentiallyPlaying();
975
976    ReadyState oldState = m_readyState;
977    m_readyState = static_cast<ReadyState>(state);
978
979    if (m_readyState == oldState)
980        return;
981
982    if (oldState > m_readyStateMaximum)
983        m_readyStateMaximum = oldState;
984
985    if (m_networkState == NETWORK_EMPTY)
986        return;
987
988    if (m_seeking) {
989        // 4.8.10.9, step 11
990        if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
991            scheduleEvent(eventNames().waitingEvent);
992
993        // 4.8.10.10 step 14 & 15.
994        if (m_readyState >= HAVE_CURRENT_DATA)
995            finishSeek();
996    } else {
997        if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
998            // 4.8.10.8
999            scheduleTimeupdateEvent(false);
1000            scheduleEvent(eventNames().waitingEvent);
1001        }
1002    }
1003
1004    if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
1005        scheduleEvent(eventNames().durationchangeEvent);
1006        scheduleEvent(eventNames().loadedmetadataEvent);
1007        if (hasMediaControls())
1008            mediaControls()->loadedMetadata();
1009        if (renderer())
1010            renderer()->updateFromElement();
1011        m_player->seek(0);
1012    }
1013
1014    bool shouldUpdateDisplayState = false;
1015
1016    if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
1017        m_haveFiredLoadedData = true;
1018        shouldUpdateDisplayState = true;
1019        scheduleEvent(eventNames().loadeddataEvent);
1020        setShouldDelayLoadEvent(false);
1021    }
1022
1023    bool isPotentiallyPlaying = potentiallyPlaying();
1024    if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) {
1025        scheduleEvent(eventNames().canplayEvent);
1026        if (isPotentiallyPlaying)
1027            scheduleEvent(eventNames().playingEvent);
1028        shouldUpdateDisplayState = true;
1029    }
1030
1031    if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) {
1032        if (oldState <= HAVE_CURRENT_DATA)
1033            scheduleEvent(eventNames().canplayEvent);
1034
1035        scheduleEvent(eventNames().canplaythroughEvent);
1036
1037        if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
1038            scheduleEvent(eventNames().playingEvent);
1039
1040        if (m_autoplaying && m_paused && autoplay()) {
1041            m_paused = false;
1042            invalidateCachedTime();
1043            scheduleEvent(eventNames().playEvent);
1044            scheduleEvent(eventNames().playingEvent);
1045        }
1046
1047        shouldUpdateDisplayState = true;
1048    }
1049
1050    if (shouldUpdateDisplayState)
1051        updateDisplayState();
1052
1053    updatePlayState();
1054}
1055
1056void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
1057{
1058    ASSERT(m_player);
1059    if (m_networkState != NETWORK_LOADING)
1060        return;
1061
1062    unsigned progress = m_player->bytesLoaded();
1063    double time = WTF::currentTime();
1064    double timedelta = time - m_previousProgressTime;
1065
1066    if (progress == m_previousProgress) {
1067        if (timedelta > 3.0 && !m_sentStalledEvent) {
1068            scheduleEvent(eventNames().stalledEvent);
1069            m_sentStalledEvent = true;
1070            setShouldDelayLoadEvent(false);
1071        }
1072    } else {
1073        scheduleEvent(eventNames().progressEvent);
1074        m_previousProgress = progress;
1075        m_previousProgressTime = time;
1076        m_sentStalledEvent = false;
1077        if (renderer())
1078            renderer()->updateFromElement();
1079    }
1080}
1081
1082void HTMLMediaElement::rewind(float timeDelta)
1083{
1084    LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta);
1085
1086    ExceptionCode e;
1087    setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e);
1088}
1089
1090void HTMLMediaElement::returnToRealtime()
1091{
1092    LOG(Media, "HTMLMediaElement::returnToRealtime");
1093    ExceptionCode e;
1094    setCurrentTime(maxTimeSeekable(), e);
1095}
1096
1097void HTMLMediaElement::addPlayedRange(float start, float end)
1098{
1099    LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end);
1100    if (!m_playedTimeRanges)
1101        m_playedTimeRanges = TimeRanges::create();
1102    m_playedTimeRanges->add(start, end);
1103}
1104
1105bool HTMLMediaElement::supportsSave() const
1106{
1107    return m_player ? m_player->supportsSave() : false;
1108}
1109
1110void HTMLMediaElement::seek(float time, ExceptionCode& ec)
1111{
1112    LOG(Media, "HTMLMediaElement::seek(%f)", time);
1113
1114    // 4.8.9.9 Seeking
1115
1116    // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception.
1117    if (m_readyState == HAVE_NOTHING || !m_player) {
1118        ec = INVALID_STATE_ERR;
1119        return;
1120    }
1121
1122    // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
1123    refreshCachedTime();
1124    float now = currentTime();
1125
1126    // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
1127    // already running. Abort that other instance of the algorithm without waiting for the step that
1128    // it is running to complete.
1129    // Nothing specific to be done here.
1130
1131    // 3 - Set the seeking IDL attribute to true.
1132    // The flag will be cleared when the engine tells us the time has actually changed.
1133    m_seeking = true;
1134
1135    // 5 - If the new playback position is later than the end of the media resource, then let it be the end
1136    // of the media resource instead.
1137    time = min(time, duration());
1138
1139    // 6 - If the new playback position is less than the earliest possible position, let it be that position instead.
1140    float earliestTime = m_player->startTime();
1141    time = max(time, earliestTime);
1142
1143    // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
1144    // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
1145    // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
1146    // not generate a timechanged callback. This means m_seeking will never be cleared and we will never
1147    // fire a 'seeked' event.
1148#if !LOG_DISABLED
1149    float mediaTime = m_player->mediaTimeForTimeValue(time);
1150    if (time != mediaTime)
1151        LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime);
1152#endif
1153    time = m_player->mediaTimeForTimeValue(time);
1154
1155    // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the
1156    // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute
1157    // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
1158    // attribute then set the seeking IDL attribute to false and abort these steps.
1159    RefPtr<TimeRanges> seekableRanges = seekable();
1160
1161    // Short circuit seeking to the current time by just firing the events if no seek is required.
1162    // Don't skip calling the media engine if we are in poster mode because a seek should always
1163    // cancel poster display.
1164    bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster);
1165    if (noSeekRequired) {
1166        if (time == now) {
1167            scheduleEvent(eventNames().seekingEvent);
1168            scheduleTimeupdateEvent(false);
1169            scheduleEvent(eventNames().seekedEvent);
1170        }
1171        m_seeking = false;
1172        return;
1173    }
1174    time = seekableRanges->nearest(time);
1175
1176    if (m_playing) {
1177        if (m_lastSeekTime < now)
1178            addPlayedRange(m_lastSeekTime, now);
1179    }
1180    m_lastSeekTime = time;
1181    m_sentEndEvent = false;
1182
1183    // 8 - Set the current playback position to the given new playback position
1184    m_player->seek(time);
1185
1186    // 9 - Queue a task to fire a simple event named seeking at the element.
1187    scheduleEvent(eventNames().seekingEvent);
1188
1189    // 10 - Queue a task to fire a simple event named timeupdate at the element.
1190    scheduleTimeupdateEvent(false);
1191
1192    // 11-15 are handled, if necessary, when the engine signals a readystate change.
1193}
1194
1195void HTMLMediaElement::finishSeek()
1196{
1197    LOG(Media, "HTMLMediaElement::finishSeek");
1198
1199    // 4.8.10.9 Seeking step 14
1200    m_seeking = false;
1201
1202    // 4.8.10.9 Seeking step 15
1203    scheduleEvent(eventNames().seekedEvent);
1204
1205    setDisplayMode(Video);
1206}
1207
1208HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
1209{
1210    return m_readyState;
1211}
1212
1213MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
1214{
1215    return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
1216}
1217
1218bool HTMLMediaElement::hasAudio() const
1219{
1220    return m_player ? m_player->hasAudio() : false;
1221}
1222
1223bool HTMLMediaElement::seeking() const
1224{
1225    return m_seeking;
1226}
1227
1228void HTMLMediaElement::refreshCachedTime() const
1229{
1230    m_cachedTime = m_player->currentTime();
1231    m_cachedTimeWallClockUpdateTime = WTF::currentTime();
1232}
1233
1234void HTMLMediaElement::invalidateCachedTime()
1235{
1236    LOG(Media, "HTMLMediaElement::invalidateCachedTime");
1237
1238    // Don't try to cache movie time when playback first starts as the time reported by the engine
1239    // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
1240    // too early.
1241    static const double minimumTimePlayingBeforeCacheSnapshot = 0.5;
1242
1243    m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot;
1244    m_cachedTime = invalidMediaTime;
1245}
1246
1247// playback state
1248float HTMLMediaElement::currentTime() const
1249{
1250#if LOG_CACHED_TIME_WARNINGS
1251    static const double minCachedDeltaForWarning = 0.01;
1252#endif
1253
1254    if (!m_player)
1255        return 0;
1256
1257    if (m_seeking) {
1258        LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime);
1259        return m_lastSeekTime;
1260    }
1261
1262    if (m_cachedTime != invalidMediaTime && m_paused) {
1263#if LOG_CACHED_TIME_WARNINGS
1264        float delta = m_cachedTime - m_player->currentTime();
1265        if (delta > minCachedDeltaForWarning)
1266            LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta);
1267#endif
1268        return m_cachedTime;
1269    }
1270
1271    // Is it too soon use a cached time?
1272    double now = WTF::currentTime();
1273    double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
1274
1275    if (maximumDurationToCacheMediaTime && m_cachedTime != invalidMediaTime && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) {
1276        double wallClockDelta = now - m_cachedTimeWallClockUpdateTime;
1277
1278        // Not too soon, use the cached time only if it hasn't expired.
1279        if (wallClockDelta < maximumDurationToCacheMediaTime) {
1280            float adjustedCacheTime = static_cast<float>(m_cachedTime + (m_playbackRate * wallClockDelta));
1281
1282#if LOG_CACHED_TIME_WARNINGS
1283            float delta = adjustedCacheTime - m_player->currentTime();
1284            if (delta > minCachedDeltaForWarning)
1285                LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when playing", delta);
1286#endif
1287            return adjustedCacheTime;
1288        }
1289    }
1290
1291#if LOG_CACHED_TIME_WARNINGS
1292    if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != invalidMediaTime) {
1293        double wallClockDelta = now - m_cachedTimeWallClockUpdateTime;
1294        float delta = m_cachedTime + (m_playbackRate * wallClockDelta) - m_player->currentTime();
1295        LOG(Media, "HTMLMediaElement::currentTime - cached time was %f seconds off of media time when it expired", delta);
1296    }
1297#endif
1298
1299    refreshCachedTime();
1300
1301    return m_cachedTime;
1302}
1303
1304void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
1305{
1306    seek(time, ec);
1307}
1308
1309float HTMLMediaElement::startTime() const
1310{
1311    if (!m_player)
1312        return 0;
1313    return m_player->startTime();
1314}
1315
1316float HTMLMediaElement::duration() const
1317{
1318    if (m_player && m_readyState >= HAVE_METADATA)
1319        return m_player->duration();
1320
1321    return numeric_limits<float>::quiet_NaN();
1322}
1323
1324bool HTMLMediaElement::paused() const
1325{
1326    return m_paused;
1327}
1328
1329float HTMLMediaElement::defaultPlaybackRate() const
1330{
1331    return m_defaultPlaybackRate;
1332}
1333
1334void HTMLMediaElement::setDefaultPlaybackRate(float rate)
1335{
1336    if (m_defaultPlaybackRate != rate) {
1337        m_defaultPlaybackRate = rate;
1338        scheduleEvent(eventNames().ratechangeEvent);
1339    }
1340}
1341
1342float HTMLMediaElement::playbackRate() const
1343{
1344    return m_playbackRate;
1345}
1346
1347void HTMLMediaElement::setPlaybackRate(float rate)
1348{
1349    LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate);
1350
1351    if (m_playbackRate != rate) {
1352        m_playbackRate = rate;
1353        invalidateCachedTime();
1354        scheduleEvent(eventNames().ratechangeEvent);
1355    }
1356    if (m_player && potentiallyPlaying() && m_player->rate() != rate)
1357        m_player->setRate(rate);
1358}
1359
1360bool HTMLMediaElement::webkitPreservesPitch() const
1361{
1362    return m_webkitPreservesPitch;
1363}
1364
1365void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
1366{
1367    LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch));
1368
1369    m_webkitPreservesPitch = preservesPitch;
1370
1371    if (!m_player)
1372        return;
1373
1374    m_player->setPreservesPitch(preservesPitch);
1375}
1376
1377bool HTMLMediaElement::ended() const
1378{
1379    // 4.8.10.8 Playing the media resource
1380    // The ended attribute must return true if the media element has ended
1381    // playback and the direction of playback is forwards, and false otherwise.
1382    return endedPlayback() && m_playbackRate > 0;
1383}
1384
1385bool HTMLMediaElement::autoplay() const
1386{
1387    return hasAttribute(autoplayAttr);
1388}
1389
1390void HTMLMediaElement::setAutoplay(bool b)
1391{
1392    LOG(Media, "HTMLMediaElement::setAutoplay(%s)", boolString(b));
1393    setBooleanAttribute(autoplayAttr, b);
1394}
1395
1396String HTMLMediaElement::preload() const
1397{
1398    switch (m_preload) {
1399    case MediaPlayer::None:
1400        return "none";
1401        break;
1402    case MediaPlayer::MetaData:
1403        return "metadata";
1404        break;
1405    case MediaPlayer::Auto:
1406        return "auto";
1407        break;
1408    }
1409
1410    ASSERT_NOT_REACHED();
1411    return String();
1412}
1413
1414void HTMLMediaElement::setPreload(const String& preload)
1415{
1416    LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data());
1417    setAttribute(preloadAttr, preload);
1418}
1419
1420void HTMLMediaElement::play(bool isUserGesture)
1421{
1422    LOG(Media, "HTMLMediaElement::play(isUserGesture : %s)", boolString(isUserGesture));
1423
1424    if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture
1425#if PLATFORM(ANDROID)
1426        && !m_userGestureInitiated
1427#endif
1428        )
1429        return;
1430
1431#if PLATFORM(ANDROID)
1432    // B/c we set the restriction to require gesture for rate change for
1433    // Android, when we don't early return, we can safely set this to true.
1434    m_userGestureInitiated = true;
1435#endif
1436
1437    Document* doc = document();
1438    Settings* settings = doc->settings();
1439    if (settings && settings->needsSiteSpecificQuirks() && m_dispatchingCanPlayEvent && !m_loadInitiatedByUserGesture) {
1440        // It should be impossible to be processing the canplay event while handling a user gesture
1441        // since it is dispatched asynchronously.
1442        ASSERT(!isUserGesture);
1443        String host = doc->baseURL().host();
1444        if (host.endsWith(".npr.org", false) || equalIgnoringCase(host, "npr.org"))
1445            return;
1446    }
1447
1448    playInternal();
1449}
1450
1451void HTMLMediaElement::playInternal()
1452{
1453    LOG(Media, "HTMLMediaElement::playInternal");
1454
1455    // 4.8.10.9. Playing the media resource
1456    if (!m_player || m_networkState == NETWORK_EMPTY)
1457        scheduleLoad();
1458
1459    if (endedPlayback()) {
1460        ExceptionCode unused;
1461        seek(0, unused);
1462    }
1463
1464    if (m_paused) {
1465        m_paused = false;
1466        invalidateCachedTime();
1467        scheduleEvent(eventNames().playEvent);
1468
1469        if (m_readyState <= HAVE_CURRENT_DATA)
1470            scheduleEvent(eventNames().waitingEvent);
1471        else if (m_readyState >= HAVE_FUTURE_DATA)
1472            scheduleEvent(eventNames().playingEvent);
1473    }
1474    m_autoplaying = false;
1475
1476    updatePlayState();
1477}
1478
1479void HTMLMediaElement::pause(bool isUserGesture)
1480{
1481    LOG(Media, "HTMLMediaElement::pause(isUserGesture : %s)", boolString(isUserGesture));
1482
1483    if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture
1484#if PLATFORM(ANDROID)
1485        && !m_userGestureInitiated
1486#endif
1487        )
1488        return;
1489#if PLATFORM(ANDROID)
1490    // B/c we set the restriction to require gesture for rate change for
1491    // Android, when we don't early return, we can safely set this to true.
1492    m_userGestureInitiated = true;
1493#endif
1494    pauseInternal();
1495}
1496
1497
1498void HTMLMediaElement::pauseInternal()
1499{
1500    LOG(Media, "HTMLMediaElement::pauseInternal");
1501
1502    // 4.8.10.9. Playing the media resource
1503    if (!m_player || m_networkState == NETWORK_EMPTY)
1504        scheduleLoad();
1505
1506    m_autoplaying = false;
1507
1508    if (!m_paused) {
1509        m_paused = true;
1510        scheduleTimeupdateEvent(false);
1511        scheduleEvent(eventNames().pauseEvent);
1512    }
1513
1514    updatePlayState();
1515}
1516
1517bool HTMLMediaElement::loop() const
1518{
1519    return hasAttribute(loopAttr);
1520}
1521
1522void HTMLMediaElement::setLoop(bool b)
1523{
1524    LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b));
1525    setBooleanAttribute(loopAttr, b);
1526}
1527
1528bool HTMLMediaElement::controls() const
1529{
1530    Frame* frame = document()->frame();
1531
1532    // always show controls when scripting is disabled
1533    if (frame && !frame->script()->canExecuteScripts(NotAboutToExecuteScript))
1534        return true;
1535
1536    // always show controls for video when fullscreen playback is required.
1537    if (isVideo() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback())
1538        return true;
1539
1540    // Always show controls when in full screen mode.
1541    if (isFullscreen())
1542        return true;
1543
1544    return hasAttribute(controlsAttr);
1545}
1546
1547void HTMLMediaElement::setControls(bool b)
1548{
1549    LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b));
1550    setBooleanAttribute(controlsAttr, b);
1551}
1552
1553float HTMLMediaElement::volume() const
1554{
1555    return m_volume;
1556}
1557
1558void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
1559{
1560    LOG(Media, "HTMLMediaElement::setVolume(%f)", vol);
1561
1562    if (vol < 0.0f || vol > 1.0f) {
1563        ec = INDEX_SIZE_ERR;
1564        return;
1565    }
1566
1567    if (m_volume != vol) {
1568        m_volume = vol;
1569        updateVolume();
1570        scheduleEvent(eventNames().volumechangeEvent);
1571    }
1572}
1573
1574bool HTMLMediaElement::muted() const
1575{
1576    return m_muted;
1577}
1578
1579void HTMLMediaElement::setMuted(bool muted)
1580{
1581    LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted));
1582
1583    if (m_muted != muted) {
1584        m_muted = muted;
1585        // Avoid recursion when the player reports volume changes.
1586        if (!processingMediaPlayerCallback()) {
1587            if (m_player) {
1588                m_player->setMuted(m_muted);
1589                if (hasMediaControls())
1590                    mediaControls()->changedMute();
1591            }
1592        }
1593        scheduleEvent(eventNames().volumechangeEvent);
1594    }
1595}
1596
1597void HTMLMediaElement::togglePlayState()
1598{
1599    LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay()));
1600
1601    // We can safely call the internal play/pause methods, which don't check restrictions, because
1602    // this method is only called from the built-in media controller
1603    if (canPlay()) {
1604        setPlaybackRate(defaultPlaybackRate());
1605        playInternal();
1606    } else
1607        pauseInternal();
1608}
1609
1610void HTMLMediaElement::beginScrubbing()
1611{
1612    LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused()));
1613
1614    if (!paused()) {
1615        if (ended()) {
1616            // Because a media element stays in non-paused state when it reaches end, playback resumes
1617            // when the slider is dragged from the end to another position unless we pause first. Do
1618            // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
1619            pause(processingUserGesture());
1620        } else {
1621            // Not at the end but we still want to pause playback so the media engine doesn't try to
1622            // continue playing during scrubbing. Pause without generating an event as we will
1623            // unpause after scrubbing finishes.
1624            setPausedInternal(true);
1625        }
1626    }
1627}
1628
1629void HTMLMediaElement::endScrubbing()
1630{
1631    LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal));
1632
1633    if (m_pausedInternal)
1634        setPausedInternal(false);
1635}
1636
1637// The spec says to fire periodic timeupdate events (those sent while playing) every
1638// "15 to 250ms", we choose the slowest frequency
1639static const double maxTimeupdateEventFrequency = 0.25;
1640
1641void HTMLMediaElement::startPlaybackProgressTimer()
1642{
1643    if (m_playbackProgressTimer.isActive())
1644        return;
1645
1646    m_previousProgressTime = WTF::currentTime();
1647    m_previousProgress = 0;
1648    m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
1649}
1650
1651void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
1652{
1653    ASSERT(m_player);
1654    if (!m_playbackRate)
1655        return;
1656
1657    scheduleTimeupdateEvent(true);
1658    if (hasMediaControls()) {
1659#if PLATFORM(ANDROID)
1660        m_mouseOver = WTF::currentTime() - m_lastTouch <= TOUCH_DELAY;
1661#endif
1662        if (!m_mouseOver && controls() && hasVideo())
1663            mediaControls()->makeTransparent();
1664
1665        mediaControls()->playbackProgressed();
1666    }
1667    // FIXME: deal with cue ranges here
1668}
1669
1670void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
1671{
1672    double now = WTF::currentTime();
1673    double timedelta = now - m_lastTimeUpdateEventWallTime;
1674
1675    // throttle the periodic events
1676    if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
1677        return;
1678
1679    // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
1680    // event at a given time so filter here
1681    float movieTime = currentTime();
1682    if (movieTime != m_lastTimeUpdateEventMovieTime) {
1683        scheduleEvent(eventNames().timeupdateEvent);
1684        m_lastTimeUpdateEventWallTime = now;
1685        m_lastTimeUpdateEventMovieTime = movieTime;
1686    }
1687}
1688
1689bool HTMLMediaElement::canPlay() const
1690{
1691    return paused() || ended() || m_readyState < HAVE_METADATA;
1692}
1693
1694float HTMLMediaElement::percentLoaded() const
1695{
1696    if (!m_player)
1697        return 0;
1698    float duration = m_player->duration();
1699
1700    if (!duration || isinf(duration))
1701        return 0;
1702
1703    float buffered = 0;
1704    RefPtr<TimeRanges> timeRanges = m_player->buffered();
1705    for (unsigned i = 0; i < timeRanges->length(); ++i) {
1706        ExceptionCode ignoredException;
1707        float start = timeRanges->start(i, ignoredException);
1708        float end = timeRanges->end(i, ignoredException);
1709        buffered += end - start;
1710    }
1711    return buffered / duration;
1712}
1713
1714bool HTMLMediaElement::havePotentialSourceChild()
1715{
1716    // Stash the current <source> node and next nodes so we can restore them after checking
1717    // to see there is another potential.
1718    HTMLSourceElement* currentSourceNode = m_currentSourceNode;
1719    Node* nextNode = m_nextChildNodeToConsider;
1720
1721    KURL nextURL = selectNextSourceChild(0, DoNothing);
1722
1723    m_currentSourceNode = currentSourceNode;
1724    m_nextChildNodeToConsider = nextNode;
1725
1726    return nextURL.isValid();
1727}
1728
1729KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSourceAction actionIfInvalid)
1730{
1731#if !LOG_DISABLED
1732    // Don't log if this was just called to find out if there are any valid <source> elements.
1733    bool shouldLog = actionIfInvalid != DoNothing;
1734    if (shouldLog)
1735        LOG(Media, "HTMLMediaElement::selectNextSourceChild(contentType : \"%s\")", contentType ? contentType->raw().utf8().data() : "");
1736#endif
1737
1738    if (m_nextChildNodeToConsider == sourceChildEndOfListValue()) {
1739#if !LOG_DISABLED
1740        if (shouldLog)
1741            LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\"");
1742#endif
1743        return KURL();
1744    }
1745
1746    KURL mediaURL;
1747    Node* node;
1748    HTMLSourceElement* source = 0;
1749    bool lookingForStartNode = m_nextChildNodeToConsider;
1750    bool canUse = false;
1751
1752    for (node = firstChild(); !canUse && node; node = node->nextSibling()) {
1753        if (lookingForStartNode && m_nextChildNodeToConsider != node)
1754            continue;
1755        lookingForStartNode = false;
1756
1757        if (!node->hasTagName(sourceTag))
1758            continue;
1759
1760        source = static_cast<HTMLSourceElement*>(node);
1761
1762        // 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
1763        mediaURL = source->getNonEmptyURLAttribute(srcAttr);
1764#if !LOG_DISABLED
1765        if (shouldLog)
1766            LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLogging(mediaURL).utf8().data());
1767#endif
1768        if (mediaURL.isEmpty())
1769            goto check_again;
1770
1771        if (source->hasAttribute(mediaAttr)) {
1772            MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
1773            RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media());
1774#if !LOG_DISABLED
1775            if (shouldLog)
1776                LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data());
1777#endif
1778            if (!screenEval.eval(media.get()))
1779                goto check_again;
1780        }
1781
1782        if (source->hasAttribute(typeAttr)) {
1783#if !LOG_DISABLED
1784            if (shouldLog)
1785                LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is %s", source->type().utf8().data());
1786#endif
1787            if (!MediaPlayer::supportsType(ContentType(source->type())))
1788                goto check_again;
1789        }
1790
1791        // Is it safe to load this url?
1792        if (!isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string()))
1793            goto check_again;
1794
1795        // Making it this far means the <source> looks reasonable.
1796        canUse = true;
1797
1798check_again:
1799        if (!canUse && actionIfInvalid == Complain)
1800            source->scheduleErrorEvent();
1801    }
1802
1803    if (canUse) {
1804        if (contentType)
1805            *contentType = ContentType(source->type());
1806        m_currentSourceNode = source;
1807        m_nextChildNodeToConsider = source->nextSibling();
1808        if (!m_nextChildNodeToConsider)
1809            m_nextChildNodeToConsider = sourceChildEndOfListValue();
1810    } else {
1811        m_currentSourceNode = 0;
1812        m_nextChildNodeToConsider = sourceChildEndOfListValue();
1813    }
1814
1815#if !LOG_DISABLED
1816    if (shouldLog)
1817        LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode, canUse ? urlForLogging(mediaURL.string()).utf8().data() : "");
1818#endif
1819    return canUse ? mediaURL : KURL();
1820}
1821
1822void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
1823{
1824    LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source);
1825
1826#if !LOG_DISABLED
1827    if (source->hasTagName(sourceTag)) {
1828        KURL url = source->getNonEmptyURLAttribute(srcAttr);
1829        LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLogging(url).utf8().data());
1830    }
1831#endif
1832
1833    // We should only consider a <source> element when there is not src attribute at all.
1834    if (hasAttribute(srcAttr))
1835        return;
1836
1837    // 4.8.8 - If a source element is inserted as a child of a media element that has no src
1838    // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke
1839    // the media element's resource selection algorithm.
1840    if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
1841        scheduleLoad();
1842        return;
1843    }
1844
1845    if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) {
1846        LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source");
1847        m_nextChildNodeToConsider = source;
1848        return;
1849    }
1850
1851    if (m_nextChildNodeToConsider != sourceChildEndOfListValue())
1852        return;
1853
1854    // 4.8.9.5, resource selection algorithm, source elements section:
1855    // 20 - Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
1856    // 21 - Asynchronously await a stable state...
1857    // 22 - Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case
1858    // it hasn't been fired yet).
1859    setShouldDelayLoadEvent(true);
1860
1861    // 23 - Set the networkState back to NETWORK_LOADING.
1862    m_networkState = NETWORK_LOADING;
1863
1864    // 24 - Jump back to the find next candidate step above.
1865    m_nextChildNodeToConsider = source;
1866    scheduleNextSourceChild();
1867}
1868
1869void HTMLMediaElement::sourceWillBeRemoved(HTMLSourceElement* source)
1870{
1871    LOG(Media, "HTMLMediaElement::sourceWillBeRemoved(%p)", source);
1872
1873#if !LOG_DISABLED
1874    if (source->hasTagName(sourceTag)) {
1875        KURL url = source->getNonEmptyURLAttribute(srcAttr);
1876        LOG(Media, "HTMLMediaElement::sourceWillBeRemoved - 'src' is %s", urlForLogging(url).utf8().data());
1877    }
1878#endif
1879
1880    if (source != m_currentSourceNode && source != m_nextChildNodeToConsider)
1881        return;
1882
1883    if (source == m_nextChildNodeToConsider) {
1884        m_nextChildNodeToConsider = m_nextChildNodeToConsider->nextSibling();
1885        if (!m_nextChildNodeToConsider)
1886            m_nextChildNodeToConsider = sourceChildEndOfListValue();
1887        LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider);
1888    } else if (source == m_currentSourceNode) {
1889        // Clear the current source node pointer, but don't change the movie as the spec says:
1890        // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already
1891        // inserted in a video or audio element will have no effect.
1892        m_currentSourceNode = 0;
1893        LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0");
1894    }
1895}
1896
1897void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
1898{
1899    LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
1900
1901    beginProcessingMediaPlayerCallback();
1902
1903    invalidateCachedTime();
1904
1905    // 4.8.10.9 step 14 & 15.  Needed if no ReadyState change is associated with the seek.
1906    if (m_seeking && m_readyState >= HAVE_CURRENT_DATA)
1907        finishSeek();
1908
1909    // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity,
1910    // it will only queue a 'timeupdate' event if we haven't already posted one at the current
1911    // movie time.
1912    scheduleTimeupdateEvent(false);
1913
1914    float now = currentTime();
1915    float dur = duration();
1916    if (!isnan(dur) && dur && now >= dur) {
1917        if (loop()) {
1918            ExceptionCode ignoredException;
1919            m_sentEndEvent = false;
1920            seek(0, ignoredException);
1921        } else {
1922            if (!m_sentEndEvent) {
1923                m_sentEndEvent = true;
1924                scheduleEvent(eventNames().endedEvent);
1925            }
1926        }
1927    }
1928    else
1929        m_sentEndEvent = false;
1930
1931    updatePlayState();
1932    endProcessingMediaPlayerCallback();
1933}
1934
1935void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
1936{
1937    LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged");
1938
1939    beginProcessingMediaPlayerCallback();
1940    if (m_player) {
1941        float vol = m_player->volume();
1942        if (vol != m_volume) {
1943            m_volume = vol;
1944            updateVolume();
1945            scheduleEvent(eventNames().volumechangeEvent);
1946        }
1947    }
1948    endProcessingMediaPlayerCallback();
1949}
1950
1951void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*)
1952{
1953    LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged");
1954
1955    beginProcessingMediaPlayerCallback();
1956    if (m_player)
1957        setMuted(m_player->muted());
1958    endProcessingMediaPlayerCallback();
1959}
1960
1961void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*)
1962{
1963    LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged");
1964
1965    beginProcessingMediaPlayerCallback();
1966    scheduleEvent(eventNames().durationchangeEvent);
1967    if (renderer())
1968        renderer()->updateFromElement();
1969    endProcessingMediaPlayerCallback();
1970
1971#if PLATFORM(ANDROID)
1972    if (hasMediaControls())
1973        mediaControls()->reset();
1974#endif
1975}
1976
1977void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
1978{
1979    LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged");
1980
1981    beginProcessingMediaPlayerCallback();
1982
1983    invalidateCachedTime();
1984
1985    // Stash the rate in case the one we tried to set isn't what the engine is
1986    // using (eg. it can't handle the rate we set)
1987    m_playbackRate = m_player->rate();
1988    invalidateCachedTime();
1989    endProcessingMediaPlayerCallback();
1990}
1991
1992void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*)
1993{
1994    LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged");
1995
1996    if (!m_player || m_pausedInternal)
1997        return;
1998
1999    beginProcessingMediaPlayerCallback();
2000    if (m_player->paused())
2001        pauseInternal();
2002    else
2003        playInternal();
2004    endProcessingMediaPlayerCallback();
2005}
2006
2007void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*)
2008{
2009    LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks");
2010
2011    // The MediaPlayer came across content it cannot completely handle.
2012    // This is normally acceptable except when we are in a standalone
2013    // MediaDocument. If so, tell the document what has happened.
2014    if (ownerDocument()->isMediaDocument()) {
2015        MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument());
2016        mediaDocument->mediaElementSawUnsupportedTracks();
2017    }
2018}
2019
2020// MediaPlayerPresentation methods
2021void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
2022{
2023    beginProcessingMediaPlayerCallback();
2024    updateDisplayState();
2025    if (renderer())
2026        renderer()->repaint();
2027    endProcessingMediaPlayerCallback();
2028}
2029
2030void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*)
2031{
2032    LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged");
2033
2034    beginProcessingMediaPlayerCallback();
2035    if (renderer())
2036        renderer()->updateFromElement();
2037    endProcessingMediaPlayerCallback();
2038}
2039
2040#if USE(ACCELERATED_COMPOSITING)
2041bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*)
2042{
2043    if (renderer() && renderer()->isVideo()) {
2044        ASSERT(renderer()->view());
2045        return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer()));
2046    }
2047    return false;
2048}
2049
2050void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*)
2051{
2052    LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged");
2053
2054    // Kick off a fake recalcStyle that will update the compositing tree.
2055    setNeedsStyleRecalc(SyntheticStyleChange);
2056}
2057#endif
2058
2059void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*)
2060{
2061    LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated");
2062    beginProcessingMediaPlayerCallback();
2063    if (renderer())
2064        renderer()->updateFromElement();
2065    endProcessingMediaPlayerCallback();
2066}
2067
2068void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*)
2069{
2070    LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable");
2071    beginProcessingMediaPlayerCallback();
2072    if (displayMode() == PosterWaitingForVideo) {
2073        setDisplayMode(Video);
2074#if USE(ACCELERATED_COMPOSITING)
2075        mediaPlayerRenderingModeChanged(m_player.get());
2076#endif
2077    }
2078    endProcessingMediaPlayerCallback();
2079}
2080
2081PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
2082{
2083    if (!m_player)
2084        return TimeRanges::create();
2085    return m_player->buffered();
2086}
2087
2088PassRefPtr<TimeRanges> HTMLMediaElement::played()
2089{
2090    if (m_playing) {
2091        float time = currentTime();
2092        if (time > m_lastSeekTime)
2093            addPlayedRange(m_lastSeekTime, time);
2094    }
2095
2096    if (!m_playedTimeRanges)
2097        m_playedTimeRanges = TimeRanges::create();
2098
2099    return m_playedTimeRanges->copy();
2100}
2101
2102PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
2103{
2104    // FIXME real ranges support
2105    if (!maxTimeSeekable())
2106        return TimeRanges::create();
2107    return TimeRanges::create(minTimeSeekable(), maxTimeSeekable());
2108}
2109
2110bool HTMLMediaElement::potentiallyPlaying() const
2111{
2112    // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing
2113    // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the
2114    // checks in couldPlayIfEnoughData().
2115    bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA;
2116    return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData();
2117}
2118
2119bool HTMLMediaElement::couldPlayIfEnoughData() const
2120{
2121    return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
2122}
2123
2124bool HTMLMediaElement::endedPlayback() const
2125{
2126    float dur = duration();
2127    if (!m_player || isnan(dur))
2128        return false;
2129
2130    // 4.8.10.8 Playing the media resource
2131
2132    // A media element is said to have ended playback when the element's
2133    // readyState attribute is HAVE_METADATA or greater,
2134    if (m_readyState < HAVE_METADATA)
2135        return false;
2136
2137    // and the current playback position is the end of the media resource and the direction
2138    // of playback is forwards and the media element does not have a loop attribute specified,
2139    float now = currentTime();
2140    if (m_playbackRate > 0)
2141        return dur > 0 && now >= dur && !loop();
2142
2143    // or the current playback position is the earliest possible position and the direction
2144    // of playback is backwards
2145    if (m_playbackRate < 0)
2146        return now <= 0;
2147
2148    return false;
2149}
2150
2151bool HTMLMediaElement::stoppedDueToErrors() const
2152{
2153    if (m_readyState >= HAVE_METADATA && m_error) {
2154        RefPtr<TimeRanges> seekableRanges = seekable();
2155        if (!seekableRanges->contain(currentTime()))
2156            return true;
2157    }
2158
2159    return false;
2160}
2161
2162bool HTMLMediaElement::pausedForUserInteraction() const
2163{
2164//    return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user]
2165    return false;
2166}
2167
2168float HTMLMediaElement::minTimeSeekable() const
2169{
2170    return 0;
2171}
2172
2173float HTMLMediaElement::maxTimeSeekable() const
2174{
2175    return m_player ? m_player->maxTimeSeekable() : 0;
2176}
2177
2178void HTMLMediaElement::updateVolume()
2179{
2180    if (!m_player)
2181        return;
2182
2183    // Avoid recursion when the player reports volume changes.
2184    if (!processingMediaPlayerCallback()) {
2185        Page* page = document()->page();
2186        float volumeMultiplier = page ? page->mediaVolume() : 1;
2187
2188        m_player->setMuted(m_muted);
2189        m_player->setVolume(m_volume * volumeMultiplier);
2190    }
2191
2192    if (hasMediaControls())
2193        mediaControls()->changedVolume();
2194}
2195
2196void HTMLMediaElement::updatePlayState()
2197{
2198    if (!m_player)
2199        return;
2200
2201    if (m_pausedInternal) {
2202        if (!m_player->paused())
2203            m_player->pause();
2204        refreshCachedTime();
2205        m_playbackProgressTimer.stop();
2206        if (hasMediaControls())
2207            mediaControls()->playbackStopped();
2208        return;
2209    }
2210
2211    bool shouldBePlaying = potentiallyPlaying();
2212    bool playerPaused = m_player->paused();
2213
2214    LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s",
2215        boolString(shouldBePlaying), boolString(playerPaused));
2216
2217    if (shouldBePlaying) {
2218        setDisplayMode(Video);
2219        invalidateCachedTime();
2220
2221        if (playerPaused) {
2222            if (!m_isFullscreen && isVideo() && document() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback())
2223                enterFullscreen();
2224
2225            // Set rate, muted before calling play in case they were set before the media engine was setup.
2226            // The media engine should just stash the rate and muted values since it isn't already playing.
2227            m_player->setRate(m_playbackRate);
2228            m_player->setMuted(m_muted);
2229
2230            m_player->play();
2231        }
2232
2233        if (hasMediaControls())
2234            mediaControls()->playbackStarted();
2235        startPlaybackProgressTimer();
2236        m_playing = true;
2237
2238    } else { // Should not be playing right now
2239        if (!playerPaused)
2240            m_player->pause();
2241        refreshCachedTime();
2242
2243        m_playbackProgressTimer.stop();
2244        m_playing = false;
2245        float time = currentTime();
2246        if (time > m_lastSeekTime)
2247            addPlayedRange(m_lastSeekTime, time);
2248
2249        if (couldPlayIfEnoughData())
2250            m_player->prepareToPlay();
2251
2252        if (hasMediaControls())
2253            mediaControls()->playbackStopped();
2254    }
2255
2256    if (renderer())
2257        renderer()->updateFromElement();
2258}
2259
2260void HTMLMediaElement::setPausedInternal(bool b)
2261{
2262    m_pausedInternal = b;
2263    updatePlayState();
2264}
2265
2266void HTMLMediaElement::stopPeriodicTimers()
2267{
2268    m_progressEventTimer.stop();
2269    m_playbackProgressTimer.stop();
2270}
2271
2272void HTMLMediaElement::userCancelledLoad()
2273{
2274    LOG(Media, "HTMLMediaElement::userCancelledLoad");
2275
2276    if (m_networkState == NETWORK_EMPTY || m_completelyLoaded)
2277        return;
2278
2279    // If the media data fetching process is aborted by the user:
2280
2281    // 1 - The user agent should cancel the fetching process.
2282#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
2283    m_player.clear();
2284#endif
2285    stopPeriodicTimers();
2286    m_loadTimer.stop();
2287    m_loadState = WaitingForSource;
2288
2289    // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED.
2290    m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
2291
2292    // 3 - Queue a task to fire a simple event named error at the media element.
2293    scheduleEvent(eventNames().abortEvent);
2294
2295    // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
2296    // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
2297    // simple event named emptied at the element. Otherwise, set the element's networkState
2298    // attribute to the NETWORK_IDLE value.
2299    if (m_readyState == HAVE_NOTHING) {
2300        m_networkState = NETWORK_EMPTY;
2301        scheduleEvent(eventNames().emptiedEvent);
2302    }
2303    else
2304        m_networkState = NETWORK_IDLE;
2305
2306    // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
2307    setShouldDelayLoadEvent(false);
2308
2309    // 6 - Abort the overall resource selection algorithm.
2310    m_currentSourceNode = 0;
2311
2312    // Reset m_readyState since m_player is gone.
2313    m_readyState = HAVE_NOTHING;
2314}
2315
2316bool HTMLMediaElement::canSuspend() const
2317{
2318    return true;
2319}
2320
2321void HTMLMediaElement::stop()
2322{
2323    LOG(Media, "HTMLMediaElement::stop");
2324    if (m_isFullscreen)
2325        exitFullscreen();
2326
2327    m_inActiveDocument = false;
2328    userCancelledLoad();
2329
2330    // Stop the playback without generating events
2331    setPausedInternal(true);
2332
2333    if (renderer())
2334        renderer()->updateFromElement();
2335
2336    stopPeriodicTimers();
2337    cancelPendingEventsAndCallbacks();
2338}
2339
2340void HTMLMediaElement::suspend(ReasonForSuspension why)
2341{
2342    LOG(Media, "HTMLMediaElement::suspend");
2343
2344    switch (why)
2345    {
2346        case DocumentWillBecomeInactive:
2347            stop();
2348            break;
2349        case JavaScriptDebuggerPaused:
2350        case WillShowDialog:
2351            // Do nothing, we don't pause media playback in these cases.
2352            break;
2353    }
2354}
2355
2356void HTMLMediaElement::resume()
2357{
2358    LOG(Media, "HTMLMediaElement::resume");
2359
2360    m_inActiveDocument = true;
2361    setPausedInternal(false);
2362
2363    if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) {
2364        // Restart the load if it was aborted in the middle by moving the document to the page cache.
2365        // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to
2366        //  MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards).
2367        // This behavior is not specified but it seems like a sensible thing to do.
2368        ExceptionCode ec;
2369        load(processingUserGesture(), ec);
2370    }
2371
2372    if (renderer())
2373        renderer()->updateFromElement();
2374}
2375
2376bool HTMLMediaElement::hasPendingActivity() const
2377{
2378    // Return true when we have pending events so we can't fire events after the JS
2379    // object gets collected.
2380    bool pending = m_pendingEvents.size();
2381    LOG(Media, "HTMLMediaElement::hasPendingActivity -> %s", boolString(pending));
2382    return pending;
2383}
2384
2385void HTMLMediaElement::mediaVolumeDidChange()
2386{
2387    LOG(Media, "HTMLMediaElement::mediaVolumeDidChange");
2388    updateVolume();
2389}
2390
2391void HTMLMediaElement::defaultEventHandler(Event* event)
2392{
2393#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
2394    RenderObject* r = renderer();
2395    if (!r || !r->isWidget())
2396        return;
2397
2398    Widget* widget = toRenderWidget(r)->widget();
2399    if (widget)
2400        widget->handleEvent(event);
2401#else
2402    if (event->isMouseEvent()) {
2403#if PLATFORM(ANDROID)
2404        m_lastTouch = WTF::currentTime();
2405#endif
2406        MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
2407        if (mouseEvent->relatedTarget() != this) {
2408            if (event->type() == eventNames().mouseoverEvent) {
2409                m_mouseOver = true;
2410                if (hasMediaControls() && controls() && !canPlay())
2411                    mediaControls()->makeOpaque();
2412            } else if (event->type() == eventNames().mouseoutEvent)
2413                m_mouseOver = false;
2414        }
2415    }
2416
2417#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS)
2418    if (event->isTouchEvent()) {
2419        m_mouseOver = !(event->type() == eventNames().touchendEvent || event->type() == eventNames().touchcancelEvent);
2420        if (m_mouseOver && hasMediaControls() && controls() && !canPlay()) {
2421            m_lastTouch = WTF::currentTime();
2422            mediaControls()->makeOpaque();
2423        }
2424    }
2425#endif
2426
2427#if PLATFORM(ANDROID)
2428    // It is really hard to hit the play/pause button on mobile devices.
2429    // This allows user to click the video area to toggle play/pause state.
2430    if (event->type() == eventNames().clickEvent
2431        && !hasEventListeners(eventNames().clickEvent)) {
2432        m_userGestureInitiated = processingUserGesture();
2433        togglePlayState();
2434    }
2435#endif
2436    HTMLElement::defaultEventHandler(event);
2437#endif
2438}
2439
2440bool HTMLMediaElement::processingUserGesture() const
2441{
2442    Frame* frame = document()->frame();
2443    FrameLoader* loader = frame ? frame->loader() : 0;
2444
2445    // return 'true' for safety if we don't know the answer
2446    return loader ? loader->isProcessingUserGesture() : true;
2447}
2448
2449#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
2450
2451void HTMLMediaElement::ensureMediaPlayer()
2452{
2453    if (!m_player)
2454        m_player = MediaPlayer::create(this);
2455}
2456
2457void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification)
2458{
2459    if (notification == MediaPlayerNotificationPlayPauseButtonPressed) {
2460        togglePlayState();
2461        return;
2462    }
2463
2464    if (m_player)
2465        m_player->deliverNotification(notification);
2466}
2467
2468void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy)
2469{
2470    ensureMediaPlayer();
2471    m_player->setMediaPlayerProxy(proxy);
2472}
2473
2474void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values)
2475{
2476    Frame* frame = document()->frame();
2477    FrameLoader* loader = frame ? frame->loader() : 0;
2478
2479    if (isVideo()) {
2480        KURL posterURL = getNonEmptyURLAttribute(posterAttr);
2481        if (!posterURL.isEmpty() && loader && loader->willLoadMediaElementURL(posterURL)) {
2482            names.append("_media_element_poster_");
2483            values.append(posterURL.string());
2484        }
2485    }
2486
2487    if (controls()) {
2488        names.append("_media_element_controls_");
2489        values.append("true");
2490    }
2491
2492    url = src();
2493    if (!isSafeToLoadURL(url, Complain))
2494        url = selectNextSourceChild(0, DoNothing);
2495
2496    m_currentSrc = url.string();
2497    if (url.isValid() && loader && loader->willLoadMediaElementURL(url)) {
2498        names.append("_media_element_src_");
2499        values.append(m_currentSrc);
2500    }
2501}
2502
2503void HTMLMediaElement::finishParsingChildren()
2504{
2505    HTMLElement::finishParsingChildren();
2506    document()->updateStyleIfNeeded();
2507    createMediaPlayerProxy();
2508}
2509
2510void HTMLMediaElement::createMediaPlayerProxy()
2511{
2512    ensureMediaPlayer();
2513
2514    if (m_proxyWidget || (inDocument() && !m_needWidgetUpdate))
2515        return;
2516
2517    Frame* frame = document()->frame();
2518    FrameLoader* loader = frame ? frame->loader() : 0;
2519    if (!loader)
2520        return;
2521
2522    LOG(Media, "HTMLMediaElement::createMediaPlayerProxy");
2523
2524    KURL url;
2525    Vector<String> paramNames;
2526    Vector<String> paramValues;
2527
2528    getPluginProxyParams(url, paramNames, paramValues);
2529
2530    // Hang onto the proxy widget so it won't be destroyed if the plug-in is set to
2531    // display:none
2532    m_proxyWidget = loader->subframeLoader()->loadMediaPlayerProxyPlugin(this, url, paramNames, paramValues);
2533    if (m_proxyWidget)
2534        m_needWidgetUpdate = false;
2535}
2536
2537void HTMLMediaElement::updateWidget(PluginCreationOption)
2538{
2539    mediaElement->setNeedWidgetUpdate(false);
2540
2541    Vector<String> paramNames;
2542    Vector<String> paramValues;
2543    KURL kurl;
2544
2545    mediaElement->getPluginProxyParams(kurl, paramNames, paramValues);
2546    SubframeLoader* loader = document()->frame()->loader()->subframeLoader();
2547    loader->loadMediaPlayerProxyPlugin(mediaElement, kurl, paramNames, paramValues);
2548}
2549
2550#endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
2551
2552bool HTMLMediaElement::isFullscreen() const
2553{
2554    if (m_isFullscreen)
2555        return true;
2556
2557#if ENABLE(FULLSCREEN_API)
2558    if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this)
2559        return true;
2560#endif
2561
2562    return false;
2563}
2564
2565void HTMLMediaElement::enterFullscreen()
2566{
2567    LOG(Media, "HTMLMediaElement::enterFullscreen");
2568#if ENABLE(FULLSCREEN_API)
2569    if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) {
2570        webkitRequestFullScreen(0);
2571        return;
2572    }
2573#endif
2574    ASSERT(!m_isFullscreen);
2575    m_isFullscreen = true;
2576    if (hasMediaControls())
2577        mediaControls()->enteredFullscreen();
2578    if (document() && document()->page()) {
2579        document()->page()->chrome()->client()->enterFullscreenForNode(this);
2580        scheduleEvent(eventNames().webkitbeginfullscreenEvent);
2581    }
2582}
2583
2584void HTMLMediaElement::exitFullscreen()
2585{
2586    LOG(Media, "HTMLMediaElement::exitFullscreen");
2587#if ENABLE(FULLSCREEN_API)
2588    if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) {
2589        if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this)
2590            document()->webkitCancelFullScreen();
2591        return;
2592    }
2593#endif
2594    ASSERT(m_isFullscreen);
2595    m_isFullscreen = false;
2596    if (hasMediaControls())
2597        mediaControls()->exitedFullscreen();
2598    if (document() && document()->page()) {
2599        if (document()->page()->chrome()->requiresFullscreenForVideoPlayback())
2600            pauseInternal();
2601        document()->page()->chrome()->client()->exitFullscreenForNode(this);
2602        scheduleEvent(eventNames().webkitendfullscreenEvent);
2603    }
2604}
2605
2606PlatformMedia HTMLMediaElement::platformMedia() const
2607{
2608    return m_player ? m_player->platformMedia() : NoPlatformMedia;
2609}
2610
2611#if USE(ACCELERATED_COMPOSITING)
2612PlatformLayer* HTMLMediaElement::platformLayer() const
2613{
2614    return m_player ? m_player->platformLayer() : 0;
2615}
2616#endif
2617
2618bool HTMLMediaElement::hasClosedCaptions() const
2619{
2620    return m_player && m_player->hasClosedCaptions();
2621}
2622
2623bool HTMLMediaElement::closedCaptionsVisible() const
2624{
2625    return m_closedCaptionsVisible;
2626}
2627
2628void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible)
2629{
2630    LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible));
2631
2632    if (!m_player ||!hasClosedCaptions())
2633        return;
2634
2635    m_closedCaptionsVisible = closedCaptionVisible;
2636    m_player->setClosedCaptionsVisible(closedCaptionVisible);
2637    if (hasMediaControls())
2638        mediaControls()->changedClosedCaptionsVisibility();
2639}
2640
2641void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible)
2642{
2643    setClosedCaptionsVisible(visible);
2644}
2645
2646bool HTMLMediaElement::webkitClosedCaptionsVisible() const
2647{
2648    return closedCaptionsVisible();
2649}
2650
2651
2652bool HTMLMediaElement::webkitHasClosedCaptions() const
2653{
2654    return hasClosedCaptions();
2655}
2656
2657#if ENABLE(MEDIA_STATISTICS)
2658unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const
2659{
2660    if (!m_player)
2661        return 0;
2662    return m_player->audioDecodedByteCount();
2663}
2664
2665unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const
2666{
2667    if (!m_player)
2668        return 0;
2669    return m_player->videoDecodedByteCount();
2670}
2671#endif
2672
2673void HTMLMediaElement::mediaCanStart()
2674{
2675    LOG(Media, "HTMLMediaElement::mediaCanStart");
2676
2677    ASSERT(m_isWaitingUntilMediaCanStart);
2678    m_isWaitingUntilMediaCanStart = false;
2679    loadInternal();
2680}
2681
2682bool HTMLMediaElement::isURLAttribute(Attribute* attribute) const
2683{
2684    return attribute->name() == srcAttr;
2685}
2686
2687void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay)
2688{
2689    if (m_shouldDelayLoadEvent == shouldDelay)
2690        return;
2691
2692    LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay));
2693
2694    m_shouldDelayLoadEvent = shouldDelay;
2695    if (shouldDelay)
2696        document()->incrementLoadEventDelayCount();
2697    else
2698        document()->decrementLoadEventDelayCount();
2699}
2700
2701
2702void HTMLMediaElement::getSitesInMediaCache(Vector<String>& sites)
2703{
2704    MediaPlayer::getSitesInMediaCache(sites);
2705}
2706
2707void HTMLMediaElement::clearMediaCache()
2708{
2709    MediaPlayer::clearMediaCache();
2710}
2711
2712void HTMLMediaElement::clearMediaCacheForSite(const String& site)
2713{
2714    MediaPlayer::clearMediaCacheForSite(site);
2715}
2716
2717void HTMLMediaElement::privateBrowsingStateDidChange()
2718{
2719    if (!m_player)
2720        return;
2721
2722    Settings* settings = document()->settings();
2723    bool privateMode = !settings || settings->privateBrowsingEnabled();
2724    LOG(Media, "HTMLMediaElement::privateBrowsingStateDidChange(%s)", boolString(privateMode));
2725    m_player->setPrivateBrowsingMode(privateMode);
2726}
2727
2728MediaControls* HTMLMediaElement::mediaControls()
2729{
2730    return toMediaControls(shadowRoot()->firstChild());
2731}
2732
2733bool HTMLMediaElement::hasMediaControls()
2734{
2735    if (!shadowRoot())
2736        return false;
2737
2738    Node* node = shadowRoot()->firstChild();
2739    return node && node->isMediaControls();
2740}
2741
2742bool HTMLMediaElement::createMediaControls()
2743{
2744    if (hasMediaControls())
2745        return true;
2746
2747    ExceptionCode ec;
2748    RefPtr<MediaControls> controls = MediaControls::create(this);
2749    if (!controls)
2750        return false;
2751
2752    ensureShadowRoot()->appendChild(controls, ec);
2753    return true;
2754}
2755
2756void* HTMLMediaElement::preDispatchEventHandler(Event* event)
2757{
2758    if (event && event->type() == eventNames().webkitfullscreenchangeEvent) {
2759        if (controls()) {
2760            if (!hasMediaControls()) {
2761                if (!createMediaControls())
2762                    return 0;
2763
2764                mediaControls()->reset();
2765            }
2766            mediaControls()->show();
2767        } else if (hasMediaControls())
2768            mediaControls()->hide();
2769    }
2770    return 0;
2771}
2772
2773
2774}
2775
2776#endif
2777