1use std::fmt;
6
7use dom_struct::dom_struct;
8
9use crate::dom::bindings::codegen::Bindings::TimeRangesBinding::TimeRangesMethods;
10use crate::dom::bindings::error::{Error, Fallible};
11use crate::dom::bindings::num::Finite;
12use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
13use crate::dom::bindings::root::DomRoot;
14use crate::dom::window::Window;
15use crate::script_runtime::CanGc;
16
17#[derive(Clone, JSTraceable, MallocSizeOf)]
18struct TimeRange {
19 start: f64,
20 end: f64,
21}
22
23impl TimeRange {
24 pub(crate) fn union(&mut self, other: &TimeRange) {
25 self.start = f64::min(self.start, other.start);
26 self.end = f64::max(self.end, other.end);
27 }
28
29 fn contains(&self, time: f64) -> bool {
30 self.start <= time && time < self.end
31 }
32
33 fn is_overlapping(&self, other: &TimeRange) -> bool {
34 self.contains(other.start) || self.contains(other.end) || other.contains(self.start)
37 }
38
39 fn is_contiguous(&self, other: &TimeRange) -> bool {
40 other.start == self.end || other.end == self.start
41 }
42
43 pub(crate) fn is_before(&self, other: &TimeRange) -> bool {
44 other.start >= self.end
45 }
46}
47
48impl fmt::Debug for TimeRange {
49 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
50 write!(fmt, "[{},{})", self.start, self.end)
51 }
52}
53
54#[derive(Debug)]
55pub enum TimeRangesError {
56 EndOlderThanStart,
57 OutOfRange,
58}
59
60#[derive(Clone, Debug, Default, JSTraceable, MallocSizeOf)]
61pub struct TimeRangesContainer {
62 ranges: Vec<TimeRange>,
63}
64
65impl TimeRangesContainer {
66 #[allow(clippy::len_without_is_empty)]
67 pub fn len(&self) -> u32 {
68 self.ranges.len() as u32
69 }
70
71 pub(crate) fn is_empty(&self) -> bool {
72 self.ranges.is_empty()
73 }
74
75 pub fn start(&self, index: u32) -> Result<f64, TimeRangesError> {
76 self.ranges
77 .get(index as usize)
78 .map(|r| r.start)
79 .ok_or(TimeRangesError::OutOfRange)
80 }
81
82 pub fn end(&self, index: u32) -> Result<f64, TimeRangesError> {
83 self.ranges
84 .get(index as usize)
85 .map(|r| r.end)
86 .ok_or(TimeRangesError::OutOfRange)
87 }
88
89 pub fn add(&mut self, start: f64, end: f64) -> Result<(), TimeRangesError> {
90 if start > end {
91 return Err(TimeRangesError::EndOlderThanStart);
92 }
93
94 let mut new_range = TimeRange { start, end };
95
96 let mut idx = 0;
101 while idx < self.ranges.len() {
102 if new_range.is_overlapping(&self.ranges[idx]) ||
103 new_range.is_contiguous(&self.ranges[idx])
104 {
105 new_range.union(&self.ranges[idx]);
108 self.ranges.remove(idx);
109 } else if new_range.is_before(&self.ranges[idx]) &&
110 (idx == 0 || self.ranges[idx - 1].is_before(&new_range))
111 {
112 self.ranges.insert(idx, new_range);
116 return Ok(());
117 } else {
118 idx += 1;
119 }
120 }
121
122 self.ranges.insert(idx, new_range);
124
125 Ok(())
126 }
127}
128
129#[dom_struct]
130pub(crate) struct TimeRanges {
131 reflector_: Reflector,
132 ranges: TimeRangesContainer,
133}
134
135impl TimeRanges {
136 fn new_inherited(ranges: TimeRangesContainer) -> TimeRanges {
137 Self {
138 reflector_: Reflector::new(),
139 ranges,
140 }
141 }
142
143 pub(crate) fn new(
144 window: &Window,
145 ranges: TimeRangesContainer,
146 can_gc: CanGc,
147 ) -> DomRoot<TimeRanges> {
148 reflect_dom_object(Box::new(TimeRanges::new_inherited(ranges)), window, can_gc)
149 }
150}
151
152impl TimeRangesMethods<crate::DomTypeHolder> for TimeRanges {
153 fn Length(&self) -> u32 {
155 self.ranges.len()
156 }
157
158 fn Start(&self, index: u32) -> Fallible<Finite<f64>> {
160 self.ranges
161 .start(index)
162 .map(Finite::wrap)
163 .map_err(|_| Error::IndexSize)
164 }
165
166 fn End(&self, index: u32) -> Fallible<Finite<f64>> {
168 self.ranges
169 .end(index)
170 .map(Finite::wrap)
171 .map_err(|_| Error::IndexSize)
172 }
173}