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
37 using namespace blink;
38
TextTrackList(HTMLMediaElement * owner)39 TextTrackList::TextTrackList(HTMLMediaElement* owner)
40 : m_owner(owner)
41 , m_asyncEventQueue(GenericEventQueue::create(this))
42 {
43 }
44
~TextTrackList()45 TextTrackList::~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
length() const61 unsigned TextTrackList::length() const
62 {
63 return m_addTrackTracks.size() + m_elementTracks.size() + m_inbandTracks.size();
64 }
65
getTrackIndex(TextTrack * textTrack)66 int 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
getTrackIndexRelativeToRenderedTracks(TextTrack * textTrack)82 int 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
item(unsigned index)119 TextTrack* 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
getTrackById(const AtomicString & id)142 TextTrack* 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
invalidateTrackIndexesAfterTrack(TextTrack * track)158 void 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
append(PassRefPtrWillBeRawPtr<TextTrack> prpTrack)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
204 invalidateTrackIndexesAfterTrack(track.get());
205
206 ASSERT(!track->trackList());
207 track->setTrackList(this);
208
209 scheduleAddTrackEvent(track.release());
210 }
211
remove(TextTrack * track)212 void 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
removeAllInbandTracks()240 void 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
contains(TextTrack * track) const248 bool 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
interfaceName() const264 const AtomicString& TextTrackList::interfaceName() const
265 {
266 return EventTargetNames::TextTrackList;
267 }
268
executionContext() const269 ExecutionContext* TextTrackList::executionContext() const
270 {
271 return m_owner ? m_owner->executionContext() : 0;
272 }
273
274 #if !ENABLE(OILPAN)
clearOwner()275 void TextTrackList::clearOwner()
276 {
277 m_owner = nullptr;
278 }
279 #endif
280
scheduleTrackEvent(const AtomicString & eventName,PassRefPtrWillBeRawPtr<TextTrack> track)281 void 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
scheduleAddTrackEvent(PassRefPtrWillBeRawPtr<TextTrack> track)291 void 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
scheduleChangeEvent()302 void 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
scheduleRemoveTrackEvent(PassRefPtrWillBeRawPtr<TextTrack> track)315 void 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
owner() const329 HTMLMediaElement* TextTrackList::owner() const
330 {
331 return m_owner;
332 }
333
trace(Visitor * visitor)334 void 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