script/dom/webvtt/
texttrack.rs1use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use script_bindings::cell::DomRefCell;
10use script_bindings::reflector::reflect_dom_object_with_cx;
11
12use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{
13 TextTrackKind, TextTrackMethods, TextTrackMode,
14};
15use crate::dom::bindings::error::{Error, ErrorResult};
16use crate::dom::bindings::reflector::DomGlobal;
17use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
18use crate::dom::bindings::str::DOMString;
19use crate::dom::eventtarget::EventTarget;
20use crate::dom::html::htmltrackelement::HTMLTrackElement;
21use crate::dom::texttrackcue::TextTrackCue;
22use crate::dom::texttrackcuelist::TextTrackCueList;
23use crate::dom::texttracklist::TextTrackList;
24use crate::dom::window::Window;
25
26#[dom_struct]
27pub(crate) struct TextTrack {
28 eventtarget: EventTarget,
29 kind: TextTrackKind,
31 label: String,
33 language: String,
35 id: String,
36 mode: Cell<TextTrackMode>,
38 cue_list: MutNullableDom<TextTrackCueList>,
40 track_list: DomRefCell<Option<Dom<TextTrackList>>>,
41 associated_track: DomRefCell<Option<Dom<HTMLTrackElement>>>,
42}
43
44impl TextTrack {
45 pub(crate) fn new_inherited(
46 id: DOMString,
47 kind: TextTrackKind,
48 label: DOMString,
49 language: DOMString,
50 mode: TextTrackMode,
51 track_list: Option<&TextTrackList>,
52 ) -> TextTrack {
53 TextTrack {
54 eventtarget: EventTarget::new_inherited(),
55 kind,
56 label: label.into(),
57 language: language.into(),
58 id: id.into(),
59 mode: Cell::new(mode),
60 cue_list: Default::default(),
61 track_list: DomRefCell::new(track_list.map(Dom::from_ref)),
62 associated_track: Default::default(),
63 }
64 }
65
66 #[expect(clippy::too_many_arguments)]
67 pub(crate) fn new(
68 cx: &mut JSContext,
69 window: &Window,
70 id: DOMString,
71 kind: TextTrackKind,
72 label: DOMString,
73 language: DOMString,
74 mode: TextTrackMode,
75 track_list: Option<&TextTrackList>,
76 ) -> DomRoot<TextTrack> {
77 reflect_dom_object_with_cx(
78 Box::new(TextTrack::new_inherited(
79 id, kind, label, language, mode, track_list,
80 )),
81 window,
82 cx,
83 )
84 }
85
86 pub(crate) fn get_cues(&self, cx: &mut JSContext) -> DomRoot<TextTrackCueList> {
87 self.cue_list
88 .or_init(|| TextTrackCueList::new(cx, self.global().as_window(), &[]))
89 }
90
91 pub(crate) fn id(&self) -> &str {
92 &self.id
93 }
94
95 pub(crate) fn add_track_list(&self, track_list: &TextTrackList) {
96 *self.track_list.borrow_mut() = Some(Dom::from_ref(track_list));
97 }
98
99 pub(crate) fn remove_track_list(&self) {
100 *self.track_list.borrow_mut() = None;
101 }
102
103 pub(crate) fn associated_track(&self) -> Option<DomRoot<HTMLTrackElement>> {
104 self.associated_track
105 .borrow()
106 .as_ref()
107 .map(|track| DomRoot::from_ref(&**track))
108 }
109
110 pub(crate) fn set_associated_track(&self, track_element: &HTMLTrackElement) {
111 *self.associated_track.borrow_mut() = Some(Dom::from_ref(track_element));
112 }
113
114 pub(crate) fn empty_cue_list(&self) {
115 if let Some(cue_list) = self.cue_list.get() {
116 cue_list.empty();
117 }
118 }
119
120 pub(crate) fn set_text_track_mode(&self, cx: &mut JSContext, value: TextTrackMode) {
121 if self.mode.get() == value {
122 return;
123 }
124 self.mode.set(value);
125 if let Some(track_element) = self.associated_track.borrow().as_ref() {
128 track_element.start_the_track_processing_model(cx);
129 }
130 }
131}
132
133impl TextTrackMethods<crate::DomTypeHolder> for TextTrack {
134 fn Kind(&self) -> TextTrackKind {
136 self.kind
137 }
138
139 fn Label(&self) -> DOMString {
141 DOMString::from(self.label.clone())
142 }
143
144 fn Language(&self) -> DOMString {
146 DOMString::from(self.language.clone())
147 }
148
149 fn Id(&self) -> DOMString {
151 DOMString::from(self.id.clone())
152 }
153
154 fn Mode(&self) -> TextTrackMode {
156 self.mode.get()
157 }
158
159 fn SetMode(&self, cx: &mut JSContext, value: TextTrackMode) {
161 self.set_text_track_mode(cx, value)
162 }
163
164 fn GetCues(&self, cx: &mut JSContext) -> Option<DomRoot<TextTrackCueList>> {
166 match self.Mode() {
167 TextTrackMode::Disabled => None,
168 _ => Some(self.get_cues(cx)),
169 }
170 }
171
172 fn GetActiveCues(&self, cx: &mut JSContext) -> Option<DomRoot<TextTrackCueList>> {
174 Some(TextTrackCueList::new(cx, self.global().as_window(), &[]))
177 }
178
179 fn AddCue(&self, cx: &mut JSContext, cue: &TextTrackCue) -> ErrorResult {
181 if let Some(old_track) = cue.get_track() {
184 if old_track.RemoveCue(cx, cue).is_err() {
188 warn!("Failed to remove cues for the added cue's text track");
189 }
190 }
191 self.get_cues(cx).add(cue);
193 Ok(())
194 }
195
196 fn RemoveCue(&self, cx: &mut JSContext, cue: &TextTrackCue) -> ErrorResult {
198 let cues = self.get_cues(cx);
200 let index = match cues.find(cue) {
201 Some(i) => Ok(i),
202 None => Err(Error::NotFound(None)),
203 }?;
204 cues.remove(index);
206 Ok(())
207 }
208
209 event_handler!(cuechange, GetOncuechange, SetOncuechange);
211}