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