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 | |
38 | namespace WebCore { |
39 | |
40 | WTF_MAKE_ISO_ALLOCATED_IMPL(TextTrackList); |
41 | |
42 | TextTrackList::TextTrackList(HTMLMediaElement* element, ScriptExecutionContext* context) |
43 | : TrackListBase(element, context) |
44 | { |
45 | } |
46 | |
47 | TextTrackList::~TextTrackList() |
48 | { |
49 | clearElement(); |
50 | } |
51 | |
52 | void 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 | |
65 | unsigned TextTrackList::length() const |
66 | { |
67 | return m_addTrackTracks.size() + m_elementTracks.size() + m_inbandTracks.size(); |
68 | } |
69 | |
70 | int 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 | |
86 | int 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 | |
118 | TextTrack* 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 | |
141 | TextTrack* 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 | |
157 | void 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 | |
189 | void 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 | |
212 | void 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 | |
246 | bool 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 | |
266 | EventTargetInterface TextTrackList::eventTargetInterface() const |
267 | { |
268 | return TextTrackListEventTargetInterfaceType; |
269 | } |
270 | |
271 | const char* TextTrackList::activeDOMObjectName() const |
272 | { |
273 | return "TextTrackList" ; |
274 | } |
275 | |
276 | } // namespace WebCore |
277 | #endif |
278 | |