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