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 "third_party/blink/renderer/core/html/track/text_track_list.h"
27
28 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
29 #include "third_party/blink/renderer/core/html/track/inband_text_track.h"
30 #include "third_party/blink/renderer/core/html/track/loadable_text_track.h"
31 #include "third_party/blink/renderer/core/html/track/text_track.h"
32 #include "third_party/blink/renderer/core/html/track/track_event.h"
33 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
34
35 namespace blink {
36
TextTrackList(HTMLMediaElement * owner)37 TextTrackList::TextTrackList(HTMLMediaElement* owner) : owner_(owner) {}
38
39 TextTrackList::~TextTrackList() = default;
40
length() const41 unsigned TextTrackList::length() const {
42 return add_track_tracks_.size() + element_tracks_.size() +
43 inband_tracks_.size();
44 }
45
GetTrackIndex(TextTrack * text_track)46 int TextTrackList::GetTrackIndex(TextTrack* text_track) {
47 if (auto* loadable_text_track = DynamicTo<LoadableTextTrack>(text_track))
48 return loadable_text_track->TrackElementIndex();
49
50 if (text_track->TrackType() == TextTrack::kAddTrack)
51 return element_tracks_.size() + add_track_tracks_.Find(text_track);
52
53 if (text_track->TrackType() == TextTrack::kInBand)
54 return element_tracks_.size() + add_track_tracks_.size() +
55 inband_tracks_.Find(text_track);
56
57 NOTREACHED();
58
59 return -1;
60 }
61
GetTrackIndexRelativeToRenderedTracks(TextTrack * text_track)62 int TextTrackList::GetTrackIndexRelativeToRenderedTracks(
63 TextTrack* text_track) {
64 // Calculate the "Let n be the number of text tracks whose text track mode is
65 // showing and that are in the media element's list of text tracks before
66 // track."
67 int track_index = 0;
68
69 for (const auto& track : element_tracks_) {
70 if (!track->IsRendered())
71 continue;
72
73 if (track == text_track)
74 return track_index;
75 ++track_index;
76 }
77
78 for (const auto& track : add_track_tracks_) {
79 if (!track->IsRendered())
80 continue;
81
82 if (track == text_track)
83 return track_index;
84 ++track_index;
85 }
86
87 for (const auto& track : inband_tracks_) {
88 if (!track->IsRendered())
89 continue;
90
91 if (track == text_track)
92 return track_index;
93 ++track_index;
94 }
95
96 NOTREACHED();
97
98 return -1;
99 }
100
AnonymousIndexedGetter(unsigned index)101 TextTrack* TextTrackList::AnonymousIndexedGetter(unsigned index) {
102 // 4.8.10.12.1 Text track model
103 // The text tracks are sorted as follows:
104 // 1. The text tracks corresponding to track element children of the media
105 // element, in tree order.
106 // 2. Any text tracks added using the addTextTrack() method, in the order they
107 // were added, oldest first.
108 // 3. Any media-resource-specific text tracks (text tracks corresponding to
109 // data in the media resource), in the order defined by the media resource's
110 // format specification.
111
112 if (index < element_tracks_.size())
113 return element_tracks_[index];
114
115 index -= element_tracks_.size();
116 if (index < add_track_tracks_.size())
117 return add_track_tracks_[index];
118
119 index -= add_track_tracks_.size();
120 if (index < inband_tracks_.size())
121 return inband_tracks_[index];
122
123 return nullptr;
124 }
125
getTrackById(const AtomicString & id)126 TextTrack* TextTrackList::getTrackById(const AtomicString& id) {
127 // 4.8.10.12.5 Text track API
128 // The getTrackById(id) method must return the first TextTrack in the
129 // TextTrackList object whose id IDL attribute would return a value equal
130 // to the value of the id argument.
131 for (unsigned i = 0; i < length(); ++i) {
132 TextTrack* track = AnonymousIndexedGetter(i);
133 if (String(track->id()) == id)
134 return track;
135 }
136
137 // When no tracks match the given argument, the method must return null.
138 return nullptr;
139 }
140
InvalidateTrackIndexesAfterTrack(TextTrack * track)141 void TextTrackList::InvalidateTrackIndexesAfterTrack(TextTrack* track) {
142 HeapVector<Member<TextTrack>>* tracks = nullptr;
143
144 if (IsA<LoadableTextTrack>(track)) {
145 tracks = &element_tracks_;
146 for (const auto& add_track : add_track_tracks_)
147 add_track->InvalidateTrackIndex();
148 for (const auto& inband_track : inband_tracks_)
149 inband_track->InvalidateTrackIndex();
150 } else if (track->TrackType() == TextTrack::kAddTrack) {
151 tracks = &add_track_tracks_;
152 for (const auto& inband_track : inband_tracks_)
153 inband_track->InvalidateTrackIndex();
154 } else if (track->TrackType() == TextTrack::kInBand) {
155 tracks = &inband_tracks_;
156 } else {
157 NOTREACHED();
158 }
159
160 wtf_size_t index = tracks->Find(track);
161 if (index == kNotFound)
162 return;
163
164 for (wtf_size_t i = index; i < tracks->size(); ++i)
165 tracks->at(i)->InvalidateTrackIndex();
166 }
167
Append(TextTrack * track)168 void TextTrackList::Append(TextTrack* track) {
169 if (track->TrackType() == TextTrack::kAddTrack) {
170 add_track_tracks_.push_back(track);
171 } else if (auto* loadable_text_track = DynamicTo<LoadableTextTrack>(track)) {
172 // Insert tracks added for <track> element in tree order.
173 wtf_size_t index = loadable_text_track->TrackElementIndex();
174 element_tracks_.insert(index, track);
175 } else if (track->TrackType() == TextTrack::kInBand) {
176 inband_tracks_.push_back(track);
177 } else {
178 NOTREACHED();
179 }
180
181 InvalidateTrackIndexesAfterTrack(track);
182
183 DCHECK(!track->TrackList());
184 track->SetTrackList(this);
185
186 ScheduleAddTrackEvent(track);
187 }
188
Remove(TextTrack * track)189 void TextTrackList::Remove(TextTrack* track) {
190 HeapVector<Member<TextTrack>>* tracks = nullptr;
191
192 if (IsA<LoadableTextTrack>(track)) {
193 tracks = &element_tracks_;
194 } else if (track->TrackType() == TextTrack::kAddTrack) {
195 tracks = &add_track_tracks_;
196 } else if (track->TrackType() == TextTrack::kInBand) {
197 tracks = &inband_tracks_;
198 } else {
199 NOTREACHED();
200 }
201
202 wtf_size_t index = tracks->Find(track);
203 if (index == kNotFound)
204 return;
205
206 InvalidateTrackIndexesAfterTrack(track);
207
208 DCHECK_EQ(track->TrackList(), this);
209 track->SetTrackList(nullptr);
210
211 tracks->EraseAt(index);
212
213 ScheduleRemoveTrackEvent(track);
214 }
215
RemoveAllInbandTracks()216 void TextTrackList::RemoveAllInbandTracks() {
217 for (const auto& track : inband_tracks_) {
218 track->SetTrackList(nullptr);
219 }
220 inband_tracks_.clear();
221 }
222
Contains(TextTrack * track) const223 bool TextTrackList::Contains(TextTrack* track) const {
224 const HeapVector<Member<TextTrack>>* tracks = nullptr;
225
226 if (IsA<LoadableTextTrack>(track))
227 tracks = &element_tracks_;
228 else if (track->TrackType() == TextTrack::kAddTrack)
229 tracks = &add_track_tracks_;
230 else if (track->TrackType() == TextTrack::kInBand)
231 tracks = &inband_tracks_;
232 else
233 NOTREACHED();
234
235 return tracks->Find(track) != kNotFound;
236 }
237
InterfaceName() const238 const AtomicString& TextTrackList::InterfaceName() const {
239 return event_target_names::kTextTrackList;
240 }
241
GetExecutionContext() const242 ExecutionContext* TextTrackList::GetExecutionContext() const {
243 return owner_ ? owner_->GetExecutionContext() : nullptr;
244 }
245
ScheduleTrackEvent(const AtomicString & event_name,TextTrack * track)246 void TextTrackList::ScheduleTrackEvent(const AtomicString& event_name,
247 TextTrack* track) {
248 EnqueueEvent(*TrackEvent::Create(event_name, track),
249 TaskType::kMediaElementEvent);
250 }
251
ScheduleAddTrackEvent(TextTrack * track)252 void TextTrackList::ScheduleAddTrackEvent(TextTrack* track) {
253 // 4.8.10.12.3 Sourcing out-of-band text tracks
254 // 4.8.10.12.4 Text track API
255 // ... then queue a task to fire an event with the name addtrack, that does
256 // not bubble and is not cancelable, and that uses the TrackEvent interface,
257 // with the track attribute initialized to the text track's TextTrack object,
258 // at the media element's textTracks attribute's TextTrackList object.
259 ScheduleTrackEvent(event_type_names::kAddtrack, track);
260 }
261
ScheduleChangeEvent()262 void TextTrackList::ScheduleChangeEvent() {
263 // 4.8.10.12.1 Text track model
264 // Whenever a text track that is in a media element's list of text tracks
265 // has its text track mode change value, the user agent must run the
266 // following steps for the media element:
267 // ...
268 // Fire a simple event named change at the media element's textTracks
269 // attribute's TextTrackList object.
270 EnqueueEvent(*Event::Create(event_type_names::kChange),
271 TaskType::kMediaElementEvent);
272 }
273
ScheduleRemoveTrackEvent(TextTrack * track)274 void TextTrackList::ScheduleRemoveTrackEvent(TextTrack* track) {
275 // 4.8.10.12.3 Sourcing out-of-band text tracks
276 // When a track element's parent element changes and the old parent was a
277 // media element, then the user agent must remove the track element's
278 // corresponding text track from the media element's list of text tracks,
279 // and then queue a task to fire a trusted event with the name removetrack,
280 // that does not bubble and is not cancelable, and that uses the TrackEvent
281 // interface, with the track attribute initialized to the text track's
282 // TextTrack object, at the media element's textTracks attribute's
283 // TextTrackList object.
284 ScheduleTrackEvent(event_type_names::kRemovetrack, track);
285 }
286
HasShowingTracks()287 bool TextTrackList::HasShowingTracks() {
288 for (unsigned i = 0; i < length(); ++i) {
289 if (AnonymousIndexedGetter(i)->mode() == TextTrack::ShowingKeyword())
290 return true;
291 }
292 return false;
293 }
294
Owner() const295 HTMLMediaElement* TextTrackList::Owner() const {
296 return owner_;
297 }
298
Trace(Visitor * visitor) const299 void TextTrackList::Trace(Visitor* visitor) const {
300 visitor->Trace(owner_);
301 visitor->Trace(add_track_tracks_);
302 visitor->Trace(element_tracks_);
303 visitor->Trace(inband_tracks_);
304 EventTargetWithInlineData::Trace(visitor);
305 }
306
307 } // namespace blink
308