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