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