Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 2011 Google 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 #include "core/html/HTMLTrackElement.h"
     28 
     29 #include "bindings/v8/ExceptionStatePlaceholder.h"
     30 #include "core/HTMLNames.h"
     31 #include "core/events/Event.h"
     32 #include "core/frame/csp/ContentSecurityPolicy.h"
     33 #include "core/html/HTMLMediaElement.h"
     34 #include "platform/Logging.h"
     35 
     36 namespace WebCore {
     37 
     38 using namespace HTMLNames;
     39 
     40 #if !LOG_DISABLED
     41 static String urlForLoggingTrack(const KURL& url)
     42 {
     43     static const unsigned maximumURLLengthForLogging = 128;
     44 
     45     if (url.string().length() < maximumURLLengthForLogging)
     46         return url.string();
     47     return url.string().substring(0, maximumURLLengthForLogging) + "...";
     48 }
     49 #endif
     50 
     51 inline HTMLTrackElement::HTMLTrackElement(Document& document)
     52     : HTMLElement(trackTag, document)
     53     , m_loadTimer(this, &HTMLTrackElement::loadTimerFired)
     54 {
     55     WTF_LOG(Media, "HTMLTrackElement::HTMLTrackElement - %p", this);
     56     ScriptWrappable::init(this);
     57 }
     58 
     59 DEFINE_NODE_FACTORY(HTMLTrackElement)
     60 
     61 HTMLTrackElement::~HTMLTrackElement()
     62 {
     63 #if !ENABLE(OILPAN)
     64     if (m_track)
     65         m_track->clearTrackElement();
     66 #endif
     67 }
     68 
     69 Node::InsertionNotificationRequest HTMLTrackElement::insertedInto(ContainerNode* insertionPoint)
     70 {
     71     WTF_LOG(Media, "HTMLTrackElement::insertedInto");
     72 
     73     // Since we've moved to a new parent, we may now be able to load.
     74     scheduleLoad();
     75 
     76     HTMLElement::insertedInto(insertionPoint);
     77     HTMLMediaElement* parent = mediaElement();
     78     if (insertionPoint == parent)
     79         parent->didAddTrackElement(this);
     80     return InsertionDone;
     81 }
     82 
     83 void HTMLTrackElement::removedFrom(ContainerNode* insertionPoint)
     84 {
     85     if (!parentNode() && isHTMLMediaElement(*insertionPoint))
     86         toHTMLMediaElement(insertionPoint)->didRemoveTrackElement(this);
     87     HTMLElement::removedFrom(insertionPoint);
     88 }
     89 
     90 void HTMLTrackElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
     91 {
     92     if (name == srcAttr) {
     93         if (!value.isEmpty())
     94             scheduleLoad();
     95         else if (m_track)
     96             m_track->removeAllCues();
     97 
     98     // 4.8.10.12.3 Sourcing out-of-band text tracks
     99     // As the kind, label, and srclang attributes are set, changed, or removed, the text track must update accordingly...
    100     } else if (name == kindAttr) {
    101         track()->setKind(value.lower());
    102     } else if (name == labelAttr) {
    103         track()->setLabel(value);
    104     } else if (name == srclangAttr) {
    105         track()->setLanguage(value);
    106     } else if (name == idAttr) {
    107         track()->setId(value);
    108     } else if (name == defaultAttr) {
    109         track()->setIsDefault(!value.isNull());
    110     }
    111 
    112     HTMLElement::parseAttribute(name, value);
    113 }
    114 
    115 const AtomicString& HTMLTrackElement::kind()
    116 {
    117     return track()->kind();
    118 }
    119 
    120 void HTMLTrackElement::setKind(const AtomicString& kind)
    121 {
    122     setAttribute(kindAttr, kind);
    123 }
    124 
    125 LoadableTextTrack* HTMLTrackElement::ensureTrack()
    126 {
    127     if (!m_track) {
    128         // kind, label and language are updated by parseAttribute
    129         m_track = LoadableTextTrack::create(this);
    130     }
    131     return m_track.get();
    132 }
    133 
    134 TextTrack* HTMLTrackElement::track()
    135 {
    136     return ensureTrack();
    137 }
    138 
    139 bool HTMLTrackElement::isURLAttribute(const Attribute& attribute) const
    140 {
    141     return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute);
    142 }
    143 
    144 void HTMLTrackElement::scheduleLoad()
    145 {
    146     WTF_LOG(Media, "HTMLTrackElement::scheduleLoad");
    147 
    148     // 1. If another occurrence of this algorithm is already running for this text track and its track element,
    149     // abort these steps, letting that other algorithm take care of this element.
    150     if (m_loadTimer.isActive())
    151         return;
    152 
    153     // 2. If the text track's text track mode is not set to one of hidden or showing, abort these steps.
    154     if (ensureTrack()->mode() != TextTrack::hiddenKeyword() && ensureTrack()->mode() != TextTrack::showingKeyword())
    155         return;
    156 
    157     // 3. If the text track's track element does not have a media element as a parent, abort these steps.
    158     if (!mediaElement())
    159         return;
    160 
    161     // 4. Run the remainder of these steps asynchronously, allowing whatever caused these steps to run to continue.
    162     m_loadTimer.startOneShot(0, FROM_HERE);
    163 }
    164 
    165 void HTMLTrackElement::loadTimerFired(Timer<HTMLTrackElement>*)
    166 {
    167     if (!fastHasAttribute(srcAttr))
    168         return;
    169 
    170     WTF_LOG(Media, "HTMLTrackElement::loadTimerFired");
    171 
    172     // 6. Set the text track readiness state to loading.
    173     setReadyState(HTMLTrackElement::LOADING);
    174 
    175     // 7. Let URL be the track URL of the track element.
    176     KURL url = getNonEmptyURLAttribute(srcAttr);
    177 
    178     // 8. If the track element's parent is a media element then let CORS mode be the state of the parent media
    179     // element's crossorigin content attribute. Otherwise, let CORS mode be No CORS.
    180     if (!canLoadUrl(url)) {
    181         didCompleteLoad(HTMLTrackElement::Failure);
    182         return;
    183     }
    184 
    185     ensureTrack()->scheduleLoad(url);
    186 }
    187 
    188 bool HTMLTrackElement::canLoadUrl(const KURL& url)
    189 {
    190     HTMLMediaElement* parent = mediaElement();
    191     if (!parent)
    192         return false;
    193 
    194     // 4.8.10.12.3 Sourcing out-of-band text tracks
    195 
    196     // 4. Download: If URL is not the empty string, perform a potentially CORS-enabled fetch of URL, with the
    197     // mode being the state of the media element's crossorigin content attribute, the origin being the
    198     // origin of the media element's Document, and the default origin behaviour set to fail.
    199     if (url.isEmpty())
    200         return false;
    201 
    202     if (!document().contentSecurityPolicy()->allowMediaFromSource(url)) {
    203         WTF_LOG(Media, "HTMLTrackElement::canLoadUrl(%s) -> rejected by Content Security Policy", urlForLoggingTrack(url).utf8().data());
    204         return false;
    205     }
    206 
    207     return true;
    208 }
    209 
    210 void HTMLTrackElement::didCompleteLoad(LoadStatus status)
    211 {
    212     // 4.8.10.12.3 Sourcing out-of-band text tracks (continued)
    213 
    214     // 4. Download: ...
    215     // If the fetching algorithm fails for any reason (network error, the server returns an error
    216     // code, a cross-origin check fails, etc), or if URL is the empty string or has the wrong origin
    217     // as determined by the condition at the start of this step, or if the fetched resource is not in
    218     // a supported format, then queue a task to first change the text track readiness state to failed
    219     // to load and then fire a simple event named error at the track element; and then, once that task
    220     // is queued, move on to the step below labeled monitoring.
    221 
    222     if (status == Failure) {
    223         setReadyState(HTMLTrackElement::TRACK_ERROR);
    224         dispatchEvent(Event::create(EventTypeNames::error), IGNORE_EXCEPTION);
    225         return;
    226     }
    227 
    228     // If the fetching algorithm does not fail, then the final task that is queued by the networking
    229     // task source must run the following steps:
    230     //     1. Change the text track readiness state to loaded.
    231     setReadyState(HTMLTrackElement::LOADED);
    232 
    233     //     2. If the file was successfully processed, fire a simple event named load at the
    234     //        track element.
    235     dispatchEvent(Event::create(EventTypeNames::load), IGNORE_EXCEPTION);
    236 }
    237 
    238 // NOTE: The values in the TextTrack::ReadinessState enum must stay in sync with those in HTMLTrackElement::ReadyState.
    239 COMPILE_ASSERT(HTMLTrackElement::NONE == static_cast<HTMLTrackElement::ReadyState>(TextTrack::NotLoaded), TextTrackEnumNotLoaded_Is_Wrong_Should_Be_HTMLTrackElementEnumNONE);
    240 COMPILE_ASSERT(HTMLTrackElement::LOADING == static_cast<HTMLTrackElement::ReadyState>(TextTrack::Loading), TextTrackEnumLoadingIsWrong_ShouldBe_HTMLTrackElementEnumLOADING);
    241 COMPILE_ASSERT(HTMLTrackElement::LOADED == static_cast<HTMLTrackElement::ReadyState>(TextTrack::Loaded), TextTrackEnumLoaded_Is_Wrong_Should_Be_HTMLTrackElementEnumLOADED);
    242 COMPILE_ASSERT(HTMLTrackElement::TRACK_ERROR == static_cast<HTMLTrackElement::ReadyState>(TextTrack::FailedToLoad), TextTrackEnumFailedToLoad_Is_Wrong_Should_Be_HTMLTrackElementEnumTRACK_ERROR);
    243 
    244 void HTMLTrackElement::setReadyState(ReadyState state)
    245 {
    246     ensureTrack()->setReadinessState(static_cast<TextTrack::ReadinessState>(state));
    247     if (HTMLMediaElement* parent = mediaElement())
    248         return parent->textTrackReadyStateChanged(m_track.get());
    249 }
    250 
    251 HTMLTrackElement::ReadyState HTMLTrackElement::readyState()
    252 {
    253     return static_cast<ReadyState>(ensureTrack()->readinessState());
    254 }
    255 
    256 const AtomicString& HTMLTrackElement::mediaElementCrossOriginAttribute() const
    257 {
    258     if (HTMLMediaElement* parent = mediaElement())
    259         return parent->fastGetAttribute(HTMLNames::crossoriginAttr);
    260 
    261     return nullAtom;
    262 }
    263 
    264 HTMLMediaElement* HTMLTrackElement::mediaElement() const
    265 {
    266     Element* parent = parentElement();
    267     if (isHTMLMediaElement(parent))
    268         return toHTMLMediaElement(parent);
    269     return 0;
    270 }
    271 
    272 void HTMLTrackElement::trace(Visitor* visitor)
    273 {
    274     visitor->trace(m_track);
    275     HTMLElement::trace(visitor);
    276 }
    277 
    278 }
    279