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