Skip to main content

script/dom/webvtt/
vttcue.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 std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::rust::HandleObject;
10use script_bindings::cell::DomRefCell;
11use script_bindings::reflector::reflect_dom_object_with_proto_and_cx;
12use servo_webvtt::{
13    WebVttCue, WebVttCueSize, WebVttLineAlignment, WebVttLineAndPositionSetting,
14    WebVttPositionAlignment, WebVttSnapToLines, WebVttTextAlignment, WebVttWritingDirection,
15};
16
17use crate::conversions::Convert;
18use crate::dom::bindings::codegen::Bindings::VTTCueBinding::{
19    self, AlignSetting, AutoKeyword, DirectionSetting, LineAlignSetting, PositionAlignSetting,
20    VTTCueMethods,
21};
22use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
23use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
24use crate::dom::bindings::num::Finite;
25use crate::dom::bindings::reflector::DomGlobal;
26use crate::dom::bindings::root::{Dom, DomRoot};
27use crate::dom::bindings::str::DOMString;
28use crate::dom::documentfragment::DocumentFragment;
29use crate::dom::texttrack::TextTrack;
30use crate::dom::texttrackcue::TextTrackCue;
31use crate::dom::vttregion::VTTRegion;
32use crate::dom::window::Window;
33
34#[dom_struct]
35pub(crate) struct VTTCue {
36    texttrackcue: TextTrackCue,
37    region: DomRefCell<Option<Dom<VTTRegion>>>,
38    vertical: Cell<DirectionSetting>,
39    snap_to_lines: Cell<bool>,
40    line: DomRefCell<LineAndPositionSetting>,
41    line_align: Cell<LineAlignSetting>,
42    position: DomRefCell<LineAndPositionSetting>,
43    position_align: Cell<PositionAlignSetting>,
44    size: Cell<f64>,
45    align: Cell<AlignSetting>,
46    text: DomRefCell<DOMString>,
47}
48
49impl VTTCue {
50    #[expect(clippy::too_many_arguments)]
51    fn new_inherited(
52        start_time: f64,
53        end_time: f64,
54        text: DOMString,
55        id: DOMString,
56        vertical: DirectionSetting,
57        snap_to_lines: bool,
58        line: LineAndPositionSetting,
59        line_align: LineAlignSetting,
60        position: LineAndPositionSetting,
61        position_align: PositionAlignSetting,
62        size: f64,
63        align: AlignSetting,
64        track: Option<&TextTrack>,
65    ) -> Self {
66        VTTCue {
67            texttrackcue: TextTrackCue::new_inherited(id, start_time, end_time, track),
68            text: DomRefCell::new(text),
69            region: DomRefCell::new(None),
70            vertical: Cell::new(vertical),
71            snap_to_lines: Cell::new(snap_to_lines),
72            line: DomRefCell::new(line),
73            line_align: Cell::new(line_align),
74            position: DomRefCell::new(position),
75            position_align: Cell::new(position_align),
76            size: Cell::new(size),
77            align: Cell::new(align),
78        }
79    }
80
81    #[expect(clippy::too_many_arguments)]
82    fn new(
83        cx: &mut JSContext,
84        window: &Window,
85        proto: Option<HandleObject>,
86        start_time: f64,
87        end_time: f64,
88        text: DOMString,
89        id: DOMString,
90        vertical: DirectionSetting,
91        snap_to_lines: bool,
92        line: LineAndPositionSetting,
93        line_align: LineAlignSetting,
94        position: LineAndPositionSetting,
95        position_align: PositionAlignSetting,
96        size: f64,
97        align: AlignSetting,
98        track: Option<&TextTrack>,
99    ) -> DomRoot<Self> {
100        reflect_dom_object_with_proto_and_cx(
101            Box::new(Self::new_inherited(
102                start_time,
103                end_time,
104                text,
105                id,
106                vertical,
107                snap_to_lines,
108                line,
109                line_align,
110                position,
111                position_align,
112                size,
113                align,
114                track,
115            )),
116            window,
117            proto,
118            cx,
119        )
120    }
121
122    pub(crate) fn create_from_vtt(
123        cx: &mut JSContext,
124        vtt_cue: WebVttCue,
125        window: &Window,
126        track: Option<&TextTrack>,
127    ) -> DomRoot<VTTCue> {
128        Self::new(
129            cx,
130            window,
131            None,
132            vtt_cue.start_time,
133            vtt_cue.end_time,
134            vtt_cue.text.into(),
135            vtt_cue.identifier.into(),
136            vtt_cue.writing_direction.convert(),
137            vtt_cue.snap_to_lines.convert(),
138            vtt_cue.line.convert(),
139            vtt_cue.line_alignment.convert(),
140            vtt_cue.position.convert(),
141            vtt_cue.position_alignment.convert(),
142            vtt_cue.size.convert(),
143            vtt_cue.text_alignment.convert(),
144            track,
145        )
146    }
147}
148
149impl VTTCueMethods<crate::DomTypeHolder> for VTTCue {
150    /// <https://w3c.github.io/webvtt/#dom-vttcue-vttcue>
151    fn Constructor(
152        cx: &mut JSContext,
153        window: &Window,
154        proto: Option<HandleObject>,
155        start_time: Finite<f64>,
156        end_time: Finite<f64>,
157        text: DOMString,
158    ) -> Fallible<DomRoot<Self>> {
159        // Step 3. If the value of the endTime argument is negative Infinity or a Not-a-Number (NaN) value,
160        // then throw a TypeError exception.
161        // Otherwise, let cue’s text track cue end time be the value of the endTime argument.
162        if (end_time.is_infinite() && end_time.is_sign_negative()) || end_time.is_nan() {
163            return Err(Error::Type(
164                c"End time is negative Infinity or Not-a-Number".to_owned(),
165            ));
166        }
167        // Step 1. Create a new WebVTT cue. Let cue be that WebVTT cue.
168        // Step 16. Return the VTTCue object representing cue.
169        Ok(VTTCue::new(
170            cx,
171            window,
172            proto,
173            // Step 2. Let cue’s text track cue start time be the value of the startTime argument.
174            *start_time,
175            *end_time,
176            // Step 4. Let cue’s cue text be the value of the text argument,
177            // and let the rules for extracting the chapter title be the
178            // WebVTT rules for extracting the chapter title.
179            // TODO: Extract title
180            text,
181            // Step 5. Let cue’s text track cue identifier be the empty string.
182            Default::default(),
183            // Step 8. Let cue’s WebVTT cue writing direction be horizontal.
184            DirectionSetting::_empty,
185            // Step 9. Let cue’s WebVTT cue snap-to-lines flag be true.
186            true,
187            // Step 10. Let cue’s WebVTT cue line be auto.
188            LineAndPositionSetting::Auto,
189            // Step 11. Let cue’s WebVTT cue line alignment be start alignment.
190            LineAlignSetting::Start,
191            // Step 12. Let cue’s WebVTT cue position be auto.
192            LineAndPositionSetting::Auto,
193            // Step 13. Let cue’s WebVTT cue position alignment be auto.
194            PositionAlignSetting::Auto,
195            // Step 14. Let cue’s WebVTT cue size be 100.
196            100.,
197            // Step 15. Let cue’s WebVTT cue text alignment be center alignment.
198            AlignSetting::Center,
199            None,
200        ))
201    }
202
203    /// <https://w3c.github.io/webvtt/#dom-vttcue-region>
204    fn GetRegion(&self) -> Option<DomRoot<VTTRegion>> {
205        self.region
206            .borrow()
207            .as_ref()
208            .map(|r| DomRoot::from_ref(&**r))
209    }
210
211    /// <https://w3c.github.io/webvtt/#dom-vttcue-region>
212    fn SetRegion(&self, value: Option<&VTTRegion>) {
213        *self.region.borrow_mut() = value.map(Dom::from_ref)
214    }
215
216    /// <https://w3c.github.io/webvtt/#dom-vttcue-vertical>
217    fn Vertical(&self) -> DirectionSetting {
218        self.vertical.get()
219    }
220
221    /// <https://w3c.github.io/webvtt/#dom-vttcue-vertical>
222    fn SetVertical(&self, value: DirectionSetting) {
223        self.vertical.set(value);
224    }
225
226    /// <https://w3c.github.io/webvtt/#dom-vttcue-snaptolines>
227    fn SnapToLines(&self) -> bool {
228        self.snap_to_lines.get()
229    }
230
231    /// <https://w3c.github.io/webvtt/#dom-vttcue-snaptolines>
232    fn SetSnapToLines(&self, value: bool) {
233        self.snap_to_lines.set(value)
234    }
235
236    /// <https://w3c.github.io/webvtt/#dom-vttcue-line>
237    fn Line(&self) -> VTTCueBinding::LineAndPositionSetting {
238        VTTCueBinding::LineAndPositionSetting::from(self.line.borrow().clone())
239    }
240
241    /// <https://w3c.github.io/webvtt/#dom-vttcue-line>
242    fn SetLine(&self, value: VTTCueBinding::LineAndPositionSetting) {
243        *self.line.borrow_mut() = value.into();
244    }
245
246    /// <https://w3c.github.io/webvtt/#dom-vttcue-linealign>
247    fn LineAlign(&self) -> LineAlignSetting {
248        self.line_align.get()
249    }
250
251    /// <https://w3c.github.io/webvtt/#dom-vttcue-linealign>
252    fn SetLineAlign(&self, value: LineAlignSetting) {
253        self.line_align.set(value);
254    }
255
256    /// <https://w3c.github.io/webvtt/#dom-vttcue-position>
257    fn Position(&self) -> VTTCueBinding::LineAndPositionSetting {
258        VTTCueBinding::LineAndPositionSetting::from(self.position.borrow().clone())
259    }
260
261    /// <https://w3c.github.io/webvtt/#dom-vttcue-position>
262    fn SetPosition(&self, value: VTTCueBinding::LineAndPositionSetting) -> ErrorResult {
263        if let VTTCueBinding::LineAndPositionSetting::Double(x) = value &&
264            (*x < 0_f64 || *x > 100_f64)
265        {
266            return Err(Error::IndexSize(None));
267        }
268
269        *self.position.borrow_mut() = value.into();
270        Ok(())
271    }
272
273    /// <https://w3c.github.io/webvtt/#dom-vttcue-positionalign>
274    fn PositionAlign(&self) -> PositionAlignSetting {
275        self.position_align.get()
276    }
277
278    /// <https://w3c.github.io/webvtt/#dom-vttcue-positionalign>
279    fn SetPositionAlign(&self, value: PositionAlignSetting) {
280        self.position_align.set(value);
281    }
282
283    /// <https://w3c.github.io/webvtt/#dom-vttcue-size>
284    fn Size(&self) -> Finite<f64> {
285        Finite::wrap(self.size.get())
286    }
287
288    /// <https://w3c.github.io/webvtt/#dom-vttcue-size>
289    fn SetSize(&self, value: Finite<f64>) -> ErrorResult {
290        if *value < 0_f64 || *value > 100_f64 {
291            return Err(Error::IndexSize(None));
292        }
293
294        self.size.set(*value);
295        Ok(())
296    }
297
298    /// <https://w3c.github.io/webvtt/#dom-vttcue-align>
299    fn Align(&self) -> AlignSetting {
300        self.align.get()
301    }
302
303    /// <https://w3c.github.io/webvtt/#dom-vttcue-align>
304    fn SetAlign(&self, value: AlignSetting) {
305        self.align.set(value);
306    }
307
308    /// <https://w3c.github.io/webvtt/#dom-vttcue-text>
309    fn Text(&self) -> DOMString {
310        self.text.borrow().clone()
311    }
312
313    /// <https://w3c.github.io/webvtt/#dom-vttcue-text>
314    fn SetText(&self, value: DOMString) {
315        *self.text.borrow_mut() = value;
316    }
317
318    /// <https://w3c.github.io/webvtt/#dom-vttcue-getcueashtml>
319    fn GetCueAsHTML(&self, cx: &mut JSContext) -> DomRoot<DocumentFragment> {
320        // TODO: Implement this
321        let global = self.global();
322        let window = global.as_window();
323        let document = window.Document();
324        DocumentFragment::new(cx, &document)
325    }
326}
327
328#[derive(Clone, JSTraceable, MallocSizeOf)]
329enum LineAndPositionSetting {
330    Double(f64),
331    Auto,
332}
333
334impl From<VTTCueBinding::LineAndPositionSetting> for LineAndPositionSetting {
335    fn from(value: VTTCueBinding::LineAndPositionSetting) -> Self {
336        match value {
337            VTTCueBinding::LineAndPositionSetting::Double(x) => LineAndPositionSetting::Double(*x),
338            VTTCueBinding::LineAndPositionSetting::AutoKeyword(_) => LineAndPositionSetting::Auto,
339        }
340    }
341}
342
343impl From<LineAndPositionSetting> for VTTCueBinding::LineAndPositionSetting {
344    fn from(value: LineAndPositionSetting) -> Self {
345        match value {
346            LineAndPositionSetting::Double(x) => {
347                VTTCueBinding::LineAndPositionSetting::Double(Finite::wrap(x))
348            },
349            LineAndPositionSetting::Auto => {
350                VTTCueBinding::LineAndPositionSetting::AutoKeyword(AutoKeyword::Auto)
351            },
352        }
353    }
354}
355
356impl Convert<LineAndPositionSetting> for WebVttLineAndPositionSetting {
357    fn convert(self) -> LineAndPositionSetting {
358        match self {
359            WebVttLineAndPositionSetting::Double(x) => LineAndPositionSetting::Double(x),
360            WebVttLineAndPositionSetting::Auto => LineAndPositionSetting::Auto,
361        }
362    }
363}
364
365impl Convert<DirectionSetting> for WebVttWritingDirection {
366    fn convert(self) -> DirectionSetting {
367        match self {
368            WebVttWritingDirection::Horizontal => DirectionSetting::_empty,
369            WebVttWritingDirection::VerticalGrowingLeft => DirectionSetting::Rl,
370            WebVttWritingDirection::VerticalGrowingRight => DirectionSetting::Lr,
371        }
372    }
373}
374
375impl Convert<AlignSetting> for WebVttTextAlignment {
376    fn convert(self) -> AlignSetting {
377        match self {
378            WebVttTextAlignment::Start => AlignSetting::Start,
379            WebVttTextAlignment::Center => AlignSetting::Center,
380            WebVttTextAlignment::End => AlignSetting::End,
381            WebVttTextAlignment::Left => AlignSetting::Left,
382            WebVttTextAlignment::Right => AlignSetting::Right,
383        }
384    }
385}
386
387impl Convert<LineAlignSetting> for WebVttLineAlignment {
388    fn convert(self) -> LineAlignSetting {
389        match self {
390            WebVttLineAlignment::Start => LineAlignSetting::Start,
391            WebVttLineAlignment::Center => LineAlignSetting::Center,
392            WebVttLineAlignment::End => LineAlignSetting::End,
393        }
394    }
395}
396
397impl Convert<PositionAlignSetting> for WebVttPositionAlignment {
398    fn convert(self) -> PositionAlignSetting {
399        match self {
400            WebVttPositionAlignment::LineLeft => PositionAlignSetting::Line_left,
401            WebVttPositionAlignment::Center => PositionAlignSetting::Center,
402            WebVttPositionAlignment::LineRight => PositionAlignSetting::Line_right,
403            WebVttPositionAlignment::Auto => PositionAlignSetting::Auto,
404        }
405    }
406}
407
408impl Convert<bool> for WebVttSnapToLines {
409    fn convert(self) -> bool {
410        match self {
411            WebVttSnapToLines::Yes => true,
412            WebVttSnapToLines::No => false,
413        }
414    }
415}
416
417impl Convert<f64> for WebVttCueSize {
418    fn convert(self) -> f64 {
419        self.0
420    }
421}