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/dom/EventNames.h"
     31 #include "core/html/HTMLMediaElement.h"
     32 #include "core/html/track/InbandTextTrack.h"
     33 #include "core/html/track/LoadableTextTrack.h"
     34 #include "core/html/track/TextTrack.h"
     35 #include "core/html/track/TrackEvent.h"
     36 
     37 using namespace WebCore;
     38 
     39 TextTrackList::TextTrackList(HTMLMediaElement* owner, ScriptExecutionContext* context)
     40     : m_context(context)
     41     , m_owner(owner)
     42     , m_pendingEventTimer(this, &TextTrackList::asyncEventTimerFired)
     43     , m_dispatchingEvents(0)
     44 {
     45     ASSERT(context->isDocument());
     46     ScriptWrappable::init(this);
     47 }
     48 
     49 TextTrackList::~TextTrackList()
     50 {
     51 }
     52 
     53 unsigned TextTrackList::length() const
     54 {
     55     return m_addTrackTracks.size() + m_elementTracks.size() + m_inbandTracks.size();
     56 }
     57 
     58 int TextTrackList::getTrackIndex(TextTrack *textTrack)
     59 {
     60     if (textTrack->trackType() == TextTrack::TrackElement)
     61         return static_cast<LoadableTextTrack*>(textTrack)->trackElementIndex();
     62 
     63     if (textTrack->trackType() == TextTrack::AddTrack)
     64         return m_elementTracks.size() + m_addTrackTracks.find(textTrack);
     65 
     66     if (textTrack->trackType() == TextTrack::InBand)
     67         return m_elementTracks.size() + m_addTrackTracks.size() + m_inbandTracks.find(textTrack);
     68 
     69     ASSERT_NOT_REACHED();
     70 
     71     return -1;
     72 }
     73 
     74 int TextTrackList::getTrackIndexRelativeToRenderedTracks(TextTrack *textTrack)
     75 {
     76     // 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."
     77     int trackIndex = 0;
     78 
     79     for (size_t i = 0; i < m_elementTracks.size(); ++i) {
     80         if (!m_elementTracks[i]->isRendered())
     81             continue;
     82 
     83         if (m_elementTracks[i] == textTrack)
     84             return trackIndex;
     85         ++trackIndex;
     86     }
     87 
     88     for (size_t i = 0; i < m_addTrackTracks.size(); ++i) {
     89         if (!m_addTrackTracks[i]->isRendered())
     90             continue;
     91 
     92         if (m_addTrackTracks[i] == textTrack)
     93             return trackIndex;
     94         ++trackIndex;
     95     }
     96 
     97     for (size_t i = 0; i < m_inbandTracks.size(); ++i) {
     98         if (!m_inbandTracks[i]->isRendered())
     99             continue;
    100 
    101         if (m_inbandTracks[i] == textTrack)
    102             return trackIndex;
    103         ++trackIndex;
    104     }
    105 
    106     ASSERT_NOT_REACHED();
    107 
    108     return -1;
    109 }
    110 
    111 TextTrack* TextTrackList::item(unsigned index)
    112 {
    113     // 4.8.10.12.1 Text track model
    114     // The text tracks are sorted as follows:
    115     // 1. The text tracks corresponding to track element children of the media element, in tree order.
    116     // 2. Any text tracks added using the addTextTrack() method, in the order they were added, oldest first.
    117     // 3. Any media-resource-specific text tracks (text tracks corresponding to data in the media
    118     // resource), in the order defined by the media resource's format specification.
    119 
    120     if (index < m_elementTracks.size())
    121         return m_elementTracks[index].get();
    122 
    123     index -= m_elementTracks.size();
    124     if (index < m_addTrackTracks.size())
    125         return m_addTrackTracks[index].get();
    126 
    127     index -= m_addTrackTracks.size();
    128     if (index < m_inbandTracks.size())
    129         return m_inbandTracks[index].get();
    130 
    131     return 0;
    132 }
    133 
    134 void TextTrackList::invalidateTrackIndexesAfterTrack(TextTrack* track)
    135 {
    136     Vector<RefPtr<TextTrack> >* tracks = 0;
    137 
    138     if (track->trackType() == TextTrack::TrackElement) {
    139         tracks = &m_elementTracks;
    140         for (size_t i = 0; i < m_addTrackTracks.size(); ++i)
    141             m_addTrackTracks[i]->invalidateTrackIndex();
    142         for (size_t i = 0; i < m_inbandTracks.size(); ++i)
    143             m_inbandTracks[i]->invalidateTrackIndex();
    144     } else if (track->trackType() == TextTrack::AddTrack) {
    145         tracks = &m_addTrackTracks;
    146         for (size_t i = 0; i < m_inbandTracks.size(); ++i)
    147             m_inbandTracks[i]->invalidateTrackIndex();
    148     } else if (track->trackType() == TextTrack::InBand)
    149         tracks = &m_inbandTracks;
    150     else
    151         ASSERT_NOT_REACHED();
    152 
    153     size_t index = tracks->find(track);
    154     if (index == notFound)
    155         return;
    156 
    157     for (size_t i = index; i < tracks->size(); ++i)
    158         tracks->at(index)->invalidateTrackIndex();
    159 }
    160 
    161 void TextTrackList::append(PassRefPtr<TextTrack> prpTrack)
    162 {
    163     RefPtr<TextTrack> track = prpTrack;
    164 
    165     if (track->trackType() == TextTrack::AddTrack)
    166         m_addTrackTracks.append(track);
    167     else if (track->trackType() == TextTrack::TrackElement) {
    168         // Insert tracks added for <track> element in tree order.
    169         size_t index = static_cast<LoadableTextTrack*>(track.get())->trackElementIndex();
    170         m_elementTracks.insert(index, track);
    171     } else if (track->trackType() == TextTrack::InBand) {
    172         // Insert tracks added for in-band in the media file order.
    173         size_t index = static_cast<InbandTextTrack*>(track.get())->inbandTrackIndex();
    174         m_inbandTracks.insert(index, track);
    175     } else
    176         ASSERT_NOT_REACHED();
    177 
    178     invalidateTrackIndexesAfterTrack(track.get());
    179 
    180     ASSERT(!track->mediaElement() || track->mediaElement() == m_owner);
    181     track->setMediaElement(m_owner);
    182 
    183     scheduleAddTrackEvent(track.release());
    184 }
    185 
    186 void TextTrackList::remove(TextTrack* track)
    187 {
    188     Vector<RefPtr<TextTrack> >* tracks = 0;
    189     RefPtr<InbandTextTrack> inbandTrack;
    190 
    191     if (track->trackType() == TextTrack::TrackElement) {
    192         tracks = &m_elementTracks;
    193     } else if (track->trackType() == TextTrack::AddTrack) {
    194         tracks = &m_addTrackTracks;
    195     } else if (track->trackType() == TextTrack::InBand) {
    196         tracks = &m_inbandTracks;
    197         inbandTrack = static_cast<InbandTextTrack*>(track);
    198     } else {
    199         ASSERT_NOT_REACHED();
    200     }
    201 
    202     size_t index = tracks->find(track);
    203     if (index == notFound)
    204         return;
    205 
    206     invalidateTrackIndexesAfterTrack(track);
    207 
    208     ASSERT(track->mediaElement() == m_owner);
    209     track->setMediaElement(0);
    210 
    211     tracks->remove(index);
    212 
    213     if (inbandTrack)
    214         inbandTrack->trackRemoved();
    215 }
    216 
    217 bool TextTrackList::contains(TextTrack* track) const
    218 {
    219     const Vector<RefPtr<TextTrack> >* tracks = 0;
    220 
    221     if (track->trackType() == TextTrack::TrackElement)
    222         tracks = &m_elementTracks;
    223     else if (track->trackType() == TextTrack::AddTrack)
    224         tracks = &m_addTrackTracks;
    225     else if (track->trackType() == TextTrack::InBand)
    226         tracks = &m_inbandTracks;
    227     else
    228         ASSERT_NOT_REACHED();
    229 
    230     return tracks->find(track) != notFound;
    231 }
    232 
    233 const AtomicString& TextTrackList::interfaceName() const
    234 {
    235     return eventNames().interfaceForTextTrackList;
    236 }
    237 
    238 void TextTrackList::scheduleAddTrackEvent(PassRefPtr<TextTrack> track)
    239 {
    240     // 4.8.10.12.3 Sourcing out-of-band text tracks
    241     // 4.8.10.12.4 Text track API
    242     // ... then queue a task to fire an event with the name addtrack, that does not
    243     // bubble and is not cancelable, and that uses the TrackEvent interface, with
    244     // the track attribute initialized to the text track's TextTrack object, at
    245     // the media element's textTracks attribute's TextTrackList object.
    246 
    247     RefPtr<TextTrack> trackRef = track;
    248     TrackEventInit initializer;
    249     initializer.track = trackRef;
    250     initializer.bubbles = false;
    251     initializer.cancelable = false;
    252 
    253     m_pendingEvents.append(TrackEvent::create(eventNames().addtrackEvent, initializer));
    254     if (!m_pendingEventTimer.isActive())
    255         m_pendingEventTimer.startOneShot(0);
    256 }
    257 
    258 void TextTrackList::asyncEventTimerFired(Timer<TextTrackList>*)
    259 {
    260     Vector<RefPtr<Event> > pendingEvents;
    261 
    262     ++m_dispatchingEvents;
    263     m_pendingEvents.swap(pendingEvents);
    264     size_t count = pendingEvents.size();
    265     for (size_t index = 0; index < count; ++index)
    266         dispatchEvent(pendingEvents[index].release(), IGNORE_EXCEPTION);
    267     --m_dispatchingEvents;
    268 }
    269 
    270 Node* TextTrackList::owner() const
    271 {
    272     return m_owner;
    273 }
    274