Home | History | Annotate | Download | only in track
      1 /*
      2  * Copyright (C) 2011, 2012 Apple Inc.  All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "core/html/track/TextTrackList.h"
     28 
     29 #include "bindings/v8/ExceptionStatePlaceholder.h"
     30 #include "core/events/GenericEventQueue.h"
     31 #include "core/events/ThreadLocalEventNames.h"
     32 #include "core/html/HTMLMediaElement.h"
     33 #include "core/html/track/InbandTextTrack.h"
     34 #include "core/html/track/LoadableTextTrack.h"
     35 #include "core/html/track/TextTrack.h"
     36 #include "core/html/track/TrackEvent.h"
     37 
     38 using namespace WebCore;
     39 
     40 TextTrackList::TextTrackList(HTMLMediaElement* owner)
     41     : m_owner(owner)
     42     , m_asyncEventQueue(GenericEventQueue::create(this))
     43 {
     44     ScriptWrappable::init(this);
     45 }
     46 
     47 TextTrackList::~TextTrackList()
     48 {
     49     m_asyncEventQueue->close();
     50 }
     51 
     52 unsigned TextTrackList::length() const
     53 {
     54     return m_addTrackTracks.size() + m_elementTracks.size() + m_inbandTracks.size();
     55 }
     56 
     57 int TextTrackList::getTrackIndex(TextTrack *textTrack)
     58 {
     59     if (textTrack->trackType() == TextTrack::TrackElement)
     60         return static_cast<LoadableTextTrack*>(textTrack)->trackElementIndex();
     61 
     62     if (textTrack->trackType() == TextTrack::AddTrack)
     63         return m_elementTracks.size() + m_addTrackTracks.find(textTrack);
     64 
     65     if (textTrack->trackType() == TextTrack::InBand)
     66         return m_elementTracks.size() + m_addTrackTracks.size() + m_inbandTracks.find(textTrack);
     67 
     68     ASSERT_NOT_REACHED();
     69 
     70     return -1;
     71 }
     72 
     73 int TextTrackList::getTrackIndexRelativeToRenderedTracks(TextTrack *textTrack)
     74 {
     75     // Calculate the "Let n be the number of text tracks whose text track mode is showing and that are in the media element's list of text tracks before track."
     76     int trackIndex = 0;
     77 
     78     for (size_t i = 0; i < m_elementTracks.size(); ++i) {
     79         if (!m_elementTracks[i]->isRendered())
     80             continue;
     81 
     82         if (m_elementTracks[i] == textTrack)
     83             return trackIndex;
     84         ++trackIndex;
     85     }
     86 
     87     for (size_t i = 0; i < m_addTrackTracks.size(); ++i) {
     88         if (!m_addTrackTracks[i]->isRendered())
     89             continue;
     90 
     91         if (m_addTrackTracks[i] == textTrack)
     92             return trackIndex;
     93         ++trackIndex;
     94     }
     95 
     96     for (size_t i = 0; i < m_inbandTracks.size(); ++i) {
     97         if (!m_inbandTracks[i]->isRendered())
     98             continue;
     99 
    100         if (m_inbandTracks[i] == textTrack)
    101             return trackIndex;
    102         ++trackIndex;
    103     }
    104 
    105     ASSERT_NOT_REACHED();
    106 
    107     return -1;
    108 }
    109 
    110 TextTrack* TextTrackList::item(unsigned index)
    111 {
    112     // 4.8.10.12.1 Text track model
    113     // The text tracks are sorted as follows:
    114     // 1. The text tracks corresponding to track element children of the media element, in tree order.
    115     // 2. Any text tracks added using the addTextTrack() method, in the order they were added, oldest first.
    116     // 3. Any media-resource-specific text tracks (text tracks corresponding to data in the media
    117     // resource), in the order defined by the media resource's format specification.
    118 
    119     if (index < m_elementTracks.size())
    120         return m_elementTracks[index].get();
    121 
    122     index -= m_elementTracks.size();
    123     if (index < m_addTrackTracks.size())
    124         return m_addTrackTracks[index].get();
    125 
    126     index -= m_addTrackTracks.size();
    127     if (index < m_inbandTracks.size())
    128         return m_inbandTracks[index].get();
    129 
    130     return 0;
    131 }
    132 
    133 TextTrack* TextTrackList::getTrackById(const AtomicString& id)
    134 {
    135     // 4.8.10.12.5 Text track API
    136     // The getTrackById(id) method must return the first TextTrack in the
    137     // TextTrackList object whose id IDL attribute would return a value equal
    138     // to the value of the id argument.
    139     for (unsigned i = 0; i < length(); ++i) {
    140         TextTrack* track = item(i);
    141         if (track->id() == id)
    142             return track;
    143     }
    144 
    145     // When no tracks match the given argument, the method must return null.
    146     return 0;
    147 }
    148 
    149 void TextTrackList::invalidateTrackIndexesAfterTrack(TextTrack* track)
    150 {
    151     Vector<RefPtr<TextTrack> >* tracks = 0;
    152 
    153     if (track->trackType() == TextTrack::TrackElement) {
    154         tracks = &m_elementTracks;
    155         for (size_t i = 0; i < m_addTrackTracks.size(); ++i)
    156             m_addTrackTracks[i]->invalidateTrackIndex();
    157         for (size_t i = 0; i < m_inbandTracks.size(); ++i)
    158             m_inbandTracks[i]->invalidateTrackIndex();
    159     } else if (track->trackType() == TextTrack::AddTrack) {
    160         tracks = &m_addTrackTracks;
    161         for (size_t i = 0; i < m_inbandTracks.size(); ++i)
    162             m_inbandTracks[i]->invalidateTrackIndex();
    163     } else if (track->trackType() == TextTrack::InBand)
    164         tracks = &m_inbandTracks;
    165     else
    166         ASSERT_NOT_REACHED();
    167 
    168     size_t index = tracks->find(track);
    169     if (index == kNotFound)
    170         return;
    171 
    172     for (size_t i = index; i < tracks->size(); ++i)
    173         tracks->at(index)->invalidateTrackIndex();
    174 }
    175 
    176 void TextTrackList::append(PassRefPtr<TextTrack> prpTrack)
    177 {
    178     RefPtr<TextTrack> track = prpTrack;
    179 
    180     if (track->trackType() == TextTrack::AddTrack)
    181         m_addTrackTracks.append(track);
    182     else if (track->trackType() == TextTrack::TrackElement) {
    183         // Insert tracks added for <track> element in tree order.
    184         size_t index = static_cast<LoadableTextTrack*>(track.get())->trackElementIndex();
    185         m_elementTracks.insert(index, track);
    186     } else if (track->trackType() == TextTrack::InBand) {
    187         // Insert tracks added for in-band in the media file order.
    188         size_t index = static_cast<InbandTextTrack*>(track.get())->inbandTrackIndex();
    189         m_inbandTracks.insert(index, track);
    190     } else
    191         ASSERT_NOT_REACHED();
    192 
    193     invalidateTrackIndexesAfterTrack(track.get());
    194 
    195     ASSERT(!track->mediaElement() || track->mediaElement() == m_owner);
    196     track->setMediaElement(m_owner);
    197 
    198     scheduleAddTrackEvent(track.release());
    199 }
    200 
    201 void TextTrackList::remove(TextTrack* track)
    202 {
    203     Vector<RefPtr<TextTrack> >* tracks = 0;
    204     RefPtr<InbandTextTrack> inbandTrack;
    205 
    206     if (track->trackType() == TextTrack::TrackElement) {
    207         tracks = &m_elementTracks;
    208     } else if (track->trackType() == TextTrack::AddTrack) {
    209         tracks = &m_addTrackTracks;
    210     } else if (track->trackType() == TextTrack::InBand) {
    211         tracks = &m_inbandTracks;
    212         inbandTrack = static_cast<InbandTextTrack*>(track);
    213     } else {
    214         ASSERT_NOT_REACHED();
    215     }
    216 
    217     size_t index = tracks->find(track);
    218     if (index == kNotFound)
    219         return;
    220 
    221     invalidateTrackIndexesAfterTrack(track);
    222 
    223     ASSERT(track->mediaElement() == m_owner);
    224     track->setMediaElement(0);
    225 
    226     tracks->remove(index);
    227 
    228     if (inbandTrack)
    229         inbandTrack->trackRemoved();
    230 
    231     scheduleRemoveTrackEvent(track);
    232 }
    233 
    234 bool TextTrackList::contains(TextTrack* track) const
    235 {
    236     const Vector<RefPtr<TextTrack> >* tracks = 0;
    237 
    238     if (track->trackType() == TextTrack::TrackElement)
    239         tracks = &m_elementTracks;
    240     else if (track->trackType() == TextTrack::AddTrack)
    241         tracks = &m_addTrackTracks;
    242     else if (track->trackType() == TextTrack::InBand)
    243         tracks = &m_inbandTracks;
    244     else
    245         ASSERT_NOT_REACHED();
    246 
    247     return tracks->find(track) != kNotFound;
    248 }
    249 
    250 const AtomicString& TextTrackList::interfaceName() const
    251 {
    252     return EventTargetNames::TextTrackList;
    253 }
    254 
    255 ExecutionContext* TextTrackList::executionContext() const
    256 {
    257     ASSERT(m_owner);
    258     return m_owner->executionContext();
    259 }
    260 
    261 void TextTrackList::scheduleTrackEvent(const AtomicString& eventName, PassRefPtr<TextTrack> track)
    262 {
    263     TrackEventInit initializer;
    264     initializer.track = track;
    265     initializer.bubbles = false;
    266     initializer.cancelable = false;
    267 
    268     m_asyncEventQueue->enqueueEvent(TrackEvent::create(eventName, initializer));
    269 }
    270 
    271 void TextTrackList::scheduleAddTrackEvent(PassRefPtr<TextTrack> track)
    272 {
    273     // 4.8.10.12.3 Sourcing out-of-band text tracks
    274     // 4.8.10.12.4 Text track API
    275     // ... then queue a task to fire an event with the name addtrack, that does not
    276     // bubble and is not cancelable, and that uses the TrackEvent interface, with
    277     // the track attribute initialized to the text track's TextTrack object, at
    278     // the media element's textTracks attribute's TextTrackList object.
    279     scheduleTrackEvent(EventTypeNames::addtrack, track);
    280 }
    281 
    282 void TextTrackList::scheduleChangeEvent()
    283 {
    284     // 4.8.10.12.1 Text track model
    285     // Whenever a text track that is in a media element's list of text tracks
    286     // has its text track mode change value, the user agent must run the
    287     // following steps for the media element:
    288     // ...
    289     // Fire a simple event named change at the media element's textTracks
    290     // attribute's TextTrackList object.
    291 
    292     EventInit initializer;
    293     initializer.bubbles = false;
    294     initializer.cancelable = false;
    295 
    296     m_asyncEventQueue->enqueueEvent(Event::create(EventTypeNames::change, initializer));
    297 }
    298 
    299 void TextTrackList::scheduleRemoveTrackEvent(PassRefPtr<TextTrack> track)
    300 {
    301     // 4.8.10.12.3 Sourcing out-of-band text tracks
    302     // When a track element's parent element changes and the old parent was a
    303     // media element, then the user agent must remove the track element's
    304     // corresponding text track from the media element's list of text tracks,
    305     // and then queue a task to fire a trusted event with the name removetrack,
    306     // that does not bubble and is not cancelable, and that uses the TrackEvent
    307     // interface, with the track attribute initialized to the text track's
    308     // TextTrack object, at the media element's textTracks attribute's
    309     // TextTrackList object.
    310     scheduleTrackEvent(EventTypeNames::removetrack, track);
    311 }
    312 
    313 Node* TextTrackList::owner() const
    314 {
    315     return m_owner;
    316 }
    317