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/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) 40 : m_owner(owner) 41 , m_asyncEventQueue(GenericEventQueue::create(this)) 42 { 43 ScriptWrappable::init(this); 44 } 45 46 TextTrackList::~TextTrackList() 47 { 48 #if !ENABLE(OILPAN) 49 ASSERT(!m_owner); 50 51 // TextTrackList and m_asyncEventQueue always become unreachable 52 // together. So TextTrackList and m_asyncEventQueue are destructed in the 53 // same GC. We don't need to close it explicitly in Oilpan. 54 m_asyncEventQueue->close(); 55 56 for (unsigned i = 0; i < length(); ++i) { 57 item(i)->setTrackList(0); 58 } 59 #endif 60 } 61 62 unsigned TextTrackList::length() const 63 { 64 return m_addTrackTracks.size() + m_elementTracks.size() + m_inbandTracks.size(); 65 } 66 67 int TextTrackList::getTrackIndex(TextTrack *textTrack) 68 { 69 if (textTrack->trackType() == TextTrack::TrackElement) 70 return static_cast<LoadableTextTrack*>(textTrack)->trackElementIndex(); 71 72 if (textTrack->trackType() == TextTrack::AddTrack) 73 return m_elementTracks.size() + m_addTrackTracks.find(textTrack); 74 75 if (textTrack->trackType() == TextTrack::InBand) 76 return m_elementTracks.size() + m_addTrackTracks.size() + m_inbandTracks.find(textTrack); 77 78 ASSERT_NOT_REACHED(); 79 80 return -1; 81 } 82 83 int TextTrackList::getTrackIndexRelativeToRenderedTracks(TextTrack *textTrack) 84 { 85 // 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." 86 int trackIndex = 0; 87 88 for (size_t i = 0; i < m_elementTracks.size(); ++i) { 89 if (!m_elementTracks[i]->isRendered()) 90 continue; 91 92 if (m_elementTracks[i] == textTrack) 93 return trackIndex; 94 ++trackIndex; 95 } 96 97 for (size_t i = 0; i < m_addTrackTracks.size(); ++i) { 98 if (!m_addTrackTracks[i]->isRendered()) 99 continue; 100 101 if (m_addTrackTracks[i] == textTrack) 102 return trackIndex; 103 ++trackIndex; 104 } 105 106 for (size_t i = 0; i < m_inbandTracks.size(); ++i) { 107 if (!m_inbandTracks[i]->isRendered()) 108 continue; 109 110 if (m_inbandTracks[i] == textTrack) 111 return trackIndex; 112 ++trackIndex; 113 } 114 115 ASSERT_NOT_REACHED(); 116 117 return -1; 118 } 119 120 TextTrack* TextTrackList::item(unsigned index) 121 { 122 // 4.8.10.12.1 Text track model 123 // The text tracks are sorted as follows: 124 // 1. The text tracks corresponding to track element children of the media element, in tree order. 125 // 2. Any text tracks added using the addTextTrack() method, in the order they were added, oldest first. 126 // 3. Any media-resource-specific text tracks (text tracks corresponding to data in the media 127 // resource), in the order defined by the media resource's format specification. 128 129 if (index < m_elementTracks.size()) 130 return m_elementTracks[index].get(); 131 132 index -= m_elementTracks.size(); 133 if (index < m_addTrackTracks.size()) 134 return m_addTrackTracks[index].get(); 135 136 index -= m_addTrackTracks.size(); 137 if (index < m_inbandTracks.size()) 138 return m_inbandTracks[index].get(); 139 140 return 0; 141 } 142 143 TextTrack* TextTrackList::getTrackById(const AtomicString& id) 144 { 145 // 4.8.10.12.5 Text track API 146 // The getTrackById(id) method must return the first TextTrack in the 147 // TextTrackList object whose id IDL attribute would return a value equal 148 // to the value of the id argument. 149 for (unsigned i = 0; i < length(); ++i) { 150 TextTrack* track = item(i); 151 if (track->id() == id) 152 return track; 153 } 154 155 // When no tracks match the given argument, the method must return null. 156 return 0; 157 } 158 159 void TextTrackList::invalidateTrackIndexesAfterTrack(TextTrack* track) 160 { 161 WillBeHeapVector<RefPtrWillBeMember<TextTrack> >* tracks = 0; 162 163 if (track->trackType() == TextTrack::TrackElement) { 164 tracks = &m_elementTracks; 165 for (size_t i = 0; i < m_addTrackTracks.size(); ++i) 166 m_addTrackTracks[i]->invalidateTrackIndex(); 167 for (size_t i = 0; i < m_inbandTracks.size(); ++i) 168 m_inbandTracks[i]->invalidateTrackIndex(); 169 } else if (track->trackType() == TextTrack::AddTrack) { 170 tracks = &m_addTrackTracks; 171 for (size_t i = 0; i < m_inbandTracks.size(); ++i) 172 m_inbandTracks[i]->invalidateTrackIndex(); 173 } else if (track->trackType() == TextTrack::InBand) 174 tracks = &m_inbandTracks; 175 else 176 ASSERT_NOT_REACHED(); 177 178 size_t index = tracks->find(track); 179 if (index == kNotFound) 180 return; 181 182 for (size_t i = index; i < tracks->size(); ++i) 183 tracks->at(index)->invalidateTrackIndex(); 184 } 185 186 void TextTrackList::append(PassRefPtrWillBeRawPtr<TextTrack> prpTrack) 187 { 188 RefPtrWillBeRawPtr<TextTrack> track = prpTrack; 189 190 if (track->trackType() == TextTrack::AddTrack) 191 m_addTrackTracks.append(track); 192 else if (track->trackType() == TextTrack::TrackElement) { 193 // Insert tracks added for <track> element in tree order. 194 size_t index = static_cast<LoadableTextTrack*>(track.get())->trackElementIndex(); 195 m_elementTracks.insert(index, track); 196 } else if (track->trackType() == TextTrack::InBand) { 197 // Insert tracks added for in-band in the media file order. 198 size_t index = static_cast<InbandTextTrack*>(track.get())->inbandTrackIndex(); 199 m_inbandTracks.insert(index, track); 200 } else 201 ASSERT_NOT_REACHED(); 202 203 invalidateTrackIndexesAfterTrack(track.get()); 204 205 ASSERT(!track->trackList()); 206 track->setTrackList(this); 207 208 scheduleAddTrackEvent(track.release()); 209 } 210 211 void TextTrackList::remove(TextTrack* track) 212 { 213 WillBeHeapVector<RefPtrWillBeMember<TextTrack> >* tracks = 0; 214 215 if (track->trackType() == TextTrack::TrackElement) { 216 tracks = &m_elementTracks; 217 } else if (track->trackType() == TextTrack::AddTrack) { 218 tracks = &m_addTrackTracks; 219 } else if (track->trackType() == TextTrack::InBand) { 220 tracks = &m_inbandTracks; 221 } else { 222 ASSERT_NOT_REACHED(); 223 } 224 225 size_t index = tracks->find(track); 226 if (index == kNotFound) 227 return; 228 229 invalidateTrackIndexesAfterTrack(track); 230 231 ASSERT(track->trackList() == this); 232 track->setTrackList(0); 233 234 tracks->remove(index); 235 236 scheduleRemoveTrackEvent(track); 237 } 238 239 void TextTrackList::removeAllInbandTracks() 240 { 241 for (unsigned i = 0; i < m_inbandTracks.size(); ++i) { 242 m_inbandTracks[i]->setTrackList(0); 243 } 244 m_inbandTracks.clear(); 245 } 246 247 bool TextTrackList::contains(TextTrack* track) const 248 { 249 const WillBeHeapVector<RefPtrWillBeMember<TextTrack> >* tracks = 0; 250 251 if (track->trackType() == TextTrack::TrackElement) 252 tracks = &m_elementTracks; 253 else if (track->trackType() == TextTrack::AddTrack) 254 tracks = &m_addTrackTracks; 255 else if (track->trackType() == TextTrack::InBand) 256 tracks = &m_inbandTracks; 257 else 258 ASSERT_NOT_REACHED(); 259 260 return tracks->find(track) != kNotFound; 261 } 262 263 const AtomicString& TextTrackList::interfaceName() const 264 { 265 return EventTargetNames::TextTrackList; 266 } 267 268 ExecutionContext* TextTrackList::executionContext() const 269 { 270 return m_owner ? m_owner->executionContext() : 0; 271 } 272 273 #if !ENABLE(OILPAN) 274 void TextTrackList::clearOwner() 275 { 276 m_owner = nullptr; 277 } 278 #endif 279 280 void TextTrackList::scheduleTrackEvent(const AtomicString& eventName, PassRefPtrWillBeRawPtr<TextTrack> track) 281 { 282 TrackEventInit initializer; 283 initializer.track = track; 284 initializer.bubbles = false; 285 initializer.cancelable = false; 286 287 m_asyncEventQueue->enqueueEvent(TrackEvent::create(eventName, initializer)); 288 } 289 290 void TextTrackList::scheduleAddTrackEvent(PassRefPtrWillBeRawPtr<TextTrack> track) 291 { 292 // 4.8.10.12.3 Sourcing out-of-band text tracks 293 // 4.8.10.12.4 Text track API 294 // ... then queue a task to fire an event with the name addtrack, that does not 295 // bubble and is not cancelable, and that uses the TrackEvent interface, with 296 // the track attribute initialized to the text track's TextTrack object, at 297 // the media element's textTracks attribute's TextTrackList object. 298 scheduleTrackEvent(EventTypeNames::addtrack, track); 299 } 300 301 void TextTrackList::scheduleChangeEvent() 302 { 303 // 4.8.10.12.1 Text track model 304 // Whenever a text track that is in a media element's list of text tracks 305 // has its text track mode change value, the user agent must run the 306 // following steps for the media element: 307 // ... 308 // Fire a simple event named change at the media element's textTracks 309 // attribute's TextTrackList object. 310 311 EventInit initializer; 312 initializer.bubbles = false; 313 initializer.cancelable = false; 314 315 m_asyncEventQueue->enqueueEvent(Event::create(EventTypeNames::change, initializer)); 316 } 317 318 void TextTrackList::scheduleRemoveTrackEvent(PassRefPtrWillBeRawPtr<TextTrack> track) 319 { 320 // 4.8.10.12.3 Sourcing out-of-band text tracks 321 // When a track element's parent element changes and the old parent was a 322 // media element, then the user agent must remove the track element's 323 // corresponding text track from the media element's list of text tracks, 324 // and then queue a task to fire a trusted event with the name removetrack, 325 // that does not bubble and is not cancelable, and that uses the TrackEvent 326 // interface, with the track attribute initialized to the text track's 327 // TextTrack object, at the media element's textTracks attribute's 328 // TextTrackList object. 329 scheduleTrackEvent(EventTypeNames::removetrack, track); 330 } 331 332 HTMLMediaElement* TextTrackList::owner() const 333 { 334 return m_owner; 335 } 336 337 void TextTrackList::trace(Visitor* visitor) 338 { 339 visitor->trace(m_owner); 340 visitor->trace(m_asyncEventQueue); 341 visitor->trace(m_addTrackTracks); 342 visitor->trace(m_elementTracks); 343 visitor->trace(m_inbandTracks); 344 EventTargetWithInlineData::trace(visitor); 345 } 346