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 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 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
28#if ENABLE(VIDEO_TRACK)
29
30#include "TextTrackList.h"
31
32#include "HTMLMediaElement.h"
33#include "InbandTextTrack.h"
34#include "InbandTextTrackPrivate.h"
35#include "LoadableTextTrack.h"
36#include <wtf/IsoMallocInlines.h>
37
38namespace WebCore {
39
40WTF_MAKE_ISO_ALLOCATED_IMPL(TextTrackList);
41
42TextTrackList::TextTrackList(HTMLMediaElement* element, ScriptExecutionContext* context)
43 : TrackListBase(element, context)
44{
45}
46
47TextTrackList::~TextTrackList()
48{
49 clearElement();
50}
51
52void TextTrackList::clearElement()
53{
54 TrackListBase::clearElement();
55 for (auto& track : m_elementTracks) {
56 track->setMediaElement(nullptr);
57 track->clearClient();
58 }
59 for (auto& track : m_addTrackTracks) {
60 track->setMediaElement(nullptr);
61 track->clearClient();
62 }
63}
64
65unsigned TextTrackList::length() const
66{
67 return m_addTrackTracks.size() + m_elementTracks.size() + m_inbandTracks.size();
68}
69
70int TextTrackList::getTrackIndex(TextTrack& textTrack)
71{
72 if (is<LoadableTextTrack>(textTrack))
73 return downcast<LoadableTextTrack>(textTrack).trackElementIndex();
74
75 if (textTrack.trackType() == TextTrack::AddTrack)
76 return m_elementTracks.size() + m_addTrackTracks.find(&textTrack);
77
78 if (textTrack.trackType() == TextTrack::InBand)
79 return m_elementTracks.size() + m_addTrackTracks.size() + m_inbandTracks.find(&textTrack);
80
81 ASSERT_NOT_REACHED();
82
83 return -1;
84}
85
86int TextTrackList::getTrackIndexRelativeToRenderedTracks(TextTrack& textTrack)
87{
88 // 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."
89 int trackIndex = 0;
90
91 for (auto& elementTrack : m_elementTracks) {
92 if (!downcast<TextTrack>(*elementTrack).isRendered())
93 continue;
94 if (elementTrack == &textTrack)
95 return trackIndex;
96 ++trackIndex;
97 }
98
99 for (auto& addTrack : m_addTrackTracks) {
100 if (!downcast<TextTrack>(*addTrack).isRendered())
101 continue;
102 if (addTrack == &textTrack)
103 return trackIndex;
104 ++trackIndex;
105 }
106
107 for (auto& inbandTrack : m_inbandTracks) {
108 if (!downcast<TextTrack>(*inbandTrack).isRendered())
109 continue;
110 if (inbandTrack == &textTrack)
111 return trackIndex;
112 ++trackIndex;
113 }
114 ASSERT_NOT_REACHED();
115 return -1;
116}
117
118TextTrack* TextTrackList::item(unsigned index) const
119{
120 // 4.8.10.12.1 Text track model
121 // The text tracks are sorted as follows:
122 // 1. The text tracks corresponding to track element children of the media element, in tree order.
123 // 2. Any text tracks added using the addTextTrack() method, in the order they were added, oldest first.
124 // 3. Any media-resource-specific text tracks (text tracks corresponding to data in the media
125 // resource), in the order defined by the media resource's format specification.
126
127 if (index < m_elementTracks.size())
128 return downcast<TextTrack>(m_elementTracks[index].get());
129
130 index -= m_elementTracks.size();
131 if (index < m_addTrackTracks.size())
132 return downcast<TextTrack>(m_addTrackTracks[index].get());
133
134 index -= m_addTrackTracks.size();
135 if (index < m_inbandTracks.size())
136 return downcast<TextTrack>(m_inbandTracks[index].get());
137
138 return nullptr;
139}
140
141TextTrack* TextTrackList::getTrackById(const AtomicString& id)
142{
143 // 4.8.10.12.5 Text track API
144 // The getTrackById(id) method must return the first TextTrack in the
145 // TextTrackList object whose id IDL attribute would return a value equal
146 // to the value of the id argument.
147 for (unsigned i = 0; i < length(); ++i) {
148 auto& track = *item(i);
149 if (track.id() == id)
150 return &track;
151 }
152
153 // When no tracks match the given argument, the method must return null.
154 return nullptr;
155}
156
157void TextTrackList::invalidateTrackIndexesAfterTrack(TextTrack& track)
158{
159 Vector<RefPtr<TrackBase>>* tracks = nullptr;
160
161 switch (track.trackType()) {
162 case TextTrack::TrackElement:
163 tracks = &m_elementTracks;
164 for (auto& addTrack : m_addTrackTracks)
165 downcast<TextTrack>(addTrack.get())->invalidateTrackIndex();
166 for (auto& inbandTrack : m_inbandTracks)
167 downcast<TextTrack>(inbandTrack.get())->invalidateTrackIndex();
168 break;
169 case TextTrack::AddTrack:
170 tracks = &m_addTrackTracks;
171 for (auto& inbandTrack : m_inbandTracks)
172 downcast<TextTrack>(inbandTrack.get())->invalidateTrackIndex();
173 break;
174 case TextTrack::InBand:
175 tracks = &m_inbandTracks;
176 break;
177 default:
178 ASSERT_NOT_REACHED();
179 }
180
181 size_t index = tracks->find(&track);
182 if (index == notFound)
183 return;
184
185 for (size_t i = index; i < tracks->size(); ++i)
186 downcast<TextTrack>(*tracks->at(index)).invalidateTrackIndex();
187}
188
189void TextTrackList::append(Ref<TextTrack>&& track)
190{
191 if (track->trackType() == TextTrack::AddTrack)
192 m_addTrackTracks.append(track.ptr());
193 else if (is<LoadableTextTrack>(track)) {
194 // Insert tracks added for <track> element in tree order.
195 size_t index = downcast<LoadableTextTrack>(track.get()).trackElementIndex();
196 m_elementTracks.insert(index, track.ptr());
197 } else if (track->trackType() == TextTrack::InBand) {
198 // Insert tracks added for in-band in the media file order.
199 size_t index = downcast<InbandTextTrack>(track.get()).inbandTrackIndex();
200 m_inbandTracks.insert(index, track.ptr());
201 } else
202 ASSERT_NOT_REACHED();
203
204 invalidateTrackIndexesAfterTrack(track);
205
206 ASSERT(!track->mediaElement() || track->mediaElement() == mediaElement());
207 track->setMediaElement(mediaElement());
208
209 scheduleAddTrackEvent(WTFMove(track));
210}
211
212void TextTrackList::remove(TrackBase& track, bool scheduleEvent)
213{
214 auto& textTrack = downcast<TextTrack>(track);
215 Vector<RefPtr<TrackBase>>* tracks = nullptr;
216 switch (textTrack.trackType()) {
217 case TextTrack::TrackElement:
218 tracks = &m_elementTracks;
219 break;
220 case TextTrack::AddTrack:
221 tracks = &m_addTrackTracks;
222 break;
223 case TextTrack::InBand:
224 tracks = &m_inbandTracks;
225 break;
226 default:
227 ASSERT_NOT_REACHED();
228 }
229
230 size_t index = tracks->find(&track);
231 if (index == notFound)
232 return;
233
234 invalidateTrackIndexesAfterTrack(textTrack);
235
236 ASSERT(!track.mediaElement() || !element() || track.mediaElement() == element());
237 track.setMediaElement(nullptr);
238
239 Ref<TrackBase> trackRef = *(*tracks)[index];
240 tracks->remove(index);
241
242 if (scheduleEvent)
243 scheduleRemoveTrackEvent(WTFMove(trackRef));
244}
245
246bool TextTrackList::contains(TrackBase& track) const
247{
248 const Vector<RefPtr<TrackBase>>* tracks = nullptr;
249 switch (downcast<TextTrack>(track).trackType()) {
250 case TextTrack::TrackElement:
251 tracks = &m_elementTracks;
252 break;
253 case TextTrack::AddTrack:
254 tracks = &m_addTrackTracks;
255 break;
256 case TextTrack::InBand:
257 tracks = &m_inbandTracks;
258 break;
259 default:
260 ASSERT_NOT_REACHED();
261 }
262
263 return tracks->find(&track) != notFound;
264}
265
266EventTargetInterface TextTrackList::eventTargetInterface() const
267{
268 return TextTrackListEventTargetInterfaceType;
269}
270
271const char* TextTrackList::activeDOMObjectName() const
272{
273 return "TextTrackList";
274}
275
276} // namespace WebCore
277#endif
278