Skip to main content

script/dom/webvtt/
texttracklist.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use script_bindings::cell::DomRefCell;
7use script_bindings::reflector::reflect_dom_object;
8
9use crate::dom::bindings::codegen::Bindings::TextTrackListBinding::TextTrackListMethods;
10use crate::dom::bindings::codegen::UnionTypes::VideoTrackOrAudioTrackOrTextTrack;
11use crate::dom::bindings::inheritance::Castable;
12use crate::dom::bindings::refcounted::Trusted;
13use crate::dom::bindings::reflector::DomGlobal;
14use crate::dom::bindings::root::{Dom, DomRoot};
15use crate::dom::bindings::str::DOMString;
16use crate::dom::event::Event;
17use crate::dom::eventtarget::EventTarget;
18use crate::dom::texttrack::TextTrack;
19use crate::dom::trackevent::TrackEvent;
20use crate::dom::window::Window;
21use crate::script_runtime::CanGc;
22
23#[dom_struct]
24pub(crate) struct TextTrackList {
25    eventtarget: EventTarget,
26    dom_tracks: DomRefCell<Vec<Dom<TextTrack>>>,
27}
28
29impl TextTrackList {
30    pub(crate) fn new_inherited(tracks: &[&TextTrack]) -> TextTrackList {
31        TextTrackList {
32            eventtarget: EventTarget::new_inherited(),
33            dom_tracks: DomRefCell::new(tracks.iter().map(|g| Dom::from_ref(&**g)).collect()),
34        }
35    }
36
37    pub(crate) fn new(
38        window: &Window,
39        tracks: &[&TextTrack],
40        can_gc: CanGc,
41    ) -> DomRoot<TextTrackList> {
42        reflect_dom_object(
43            Box::new(TextTrackList::new_inherited(tracks)),
44            window,
45            can_gc,
46        )
47    }
48
49    pub(crate) fn item(&self, idx: usize) -> Option<DomRoot<TextTrack>> {
50        self.dom_tracks
51            .borrow()
52            .get(idx)
53            .map(|t| DomRoot::from_ref(&**t))
54    }
55
56    pub(crate) fn find(&self, track: &TextTrack) -> Option<usize> {
57        self.dom_tracks
58            .borrow()
59            .iter()
60            .enumerate()
61            .find(|(_, t)| **t == track)
62            .map(|(i, _)| i)
63    }
64
65    pub(crate) fn add(&self, track: &TextTrack) {
66        // Only add a track if it does not exist in the list
67        if self.find(track).is_none() {
68            self.dom_tracks.borrow_mut().push(Dom::from_ref(track));
69
70            let Some(idx) = self.find(track) else {
71                return;
72            };
73
74            let this = Trusted::new(self);
75            self.global()
76                .task_manager()
77                .media_element_task_source()
78                .queue(task!(track_event_queue: move || {
79                    let this = this.root();
80
81                    if let Some(track) = this.item(idx) {
82                        let event = TrackEvent::new(
83                            this.global().as_window(),
84                            atom!("addtrack"),
85                            false,
86                            false,
87                            &Some(VideoTrackOrAudioTrackOrTextTrack::TextTrack(
88                                DomRoot::from_ref(&track)
89                            )),
90                            CanGc::deprecated_note()
91                        );
92
93                        event.upcast::<Event>().fire(this.upcast::<EventTarget>(), CanGc::deprecated_note());
94                    }
95                }));
96            track.add_track_list(self);
97        }
98    }
99
100    // FIXME(#22314, dlrobertson) allow TextTracks to be
101    // removed from the TextTrackList.
102    #[expect(dead_code)]
103    pub(crate) fn remove(&self, cx: &mut js::context::JSContext, idx: usize) {
104        if let Some(track) = self.dom_tracks.borrow().get(idx) {
105            track.remove_track_list();
106        }
107        self.dom_tracks.borrow_mut().remove(idx);
108        self.upcast::<EventTarget>()
109            .fire_event(cx, atom!("removetrack"));
110    }
111}
112
113impl TextTrackListMethods<crate::DomTypeHolder> for TextTrackList {
114    /// <https://html.spec.whatwg.org/multipage/#dom-texttracklist-length>
115    fn Length(&self) -> u32 {
116        self.dom_tracks.borrow().len() as u32
117    }
118
119    /// <https://html.spec.whatwg.org/multipage/#dom-texttracklist-item>
120    fn IndexedGetter(&self, idx: u32) -> Option<DomRoot<TextTrack>> {
121        self.item(idx as usize)
122    }
123
124    /// <https://html.spec.whatwg.org/multipage/#dom-texttracklist-gettrackbyid>
125    fn GetTrackById(&self, id: DOMString) -> Option<DomRoot<TextTrack>> {
126        let id_str = String::from(id);
127        self.dom_tracks
128            .borrow()
129            .iter()
130            .find(|track| track.id() == id_str)
131            .map(|t| DomRoot::from_ref(&**t))
132    }
133
134    // https://html.spec.whatwg.org/multipage/#handler-texttracklist-onchange
135    event_handler!(change, GetOnchange, SetOnchange);
136
137    // https://html.spec.whatwg.org/multipage/#handler-texttracklist-onaddtrack
138    event_handler!(addtrack, GetOnaddtrack, SetOnaddtrack);
139
140    // https://html.spec.whatwg.org/multipage/#handler-texttracklist-onremovetrack
141    event_handler!(removetrack, GetOnremovetrack, SetOnremovetrack);
142}