| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ |
| |
| use std::fmt; |
| |
| use dom_struct::dom_struct; |
| |
| use crate::dom::bindings::codegen::Bindings::TimeRangesBinding::TimeRangesMethods; |
| use crate::dom::bindings::error::{Error, Fallible}; |
| use crate::dom::bindings::num::Finite; |
| use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; |
| use crate::dom::bindings::root::DomRoot; |
| use crate::dom::window::Window; |
| use crate::script_runtime::CanGc; |
| |
| #[derive(Clone, JSTraceable, MallocSizeOf)] |
| struct TimeRange { |
| start: f64, |
| end: f64, |
| } |
| |
| impl TimeRange { |
| pub(crate) fn union(&mut self, other: &TimeRange) { |
| self.start = f64::min(self.start, other.start); |
| self.end = f64::max(self.end, other.end); |
| } |
| |
| fn contains(&self, time: f64) -> bool { |
| self.start <= time && time < self.end |
| } |
| |
| fn is_overlapping(&self, other: &TimeRange) -> bool { |
| // This also covers the case where `self` is entirely contained within `other`, |
| // for example: `self` = [2,3) and `other` = [1,4). |
| self.contains(other.start) || self.contains(other.end) || other.contains(self.start) |
| } |
| |
| fn is_contiguous(&self, other: &TimeRange) -> bool { |
| other.start == self.end || other.end == self.start |
| } |
| |
| pub(crate) fn is_before(&self, other: &TimeRange) -> bool { |
| other.start >= self.end |
| } |
| } |
| |
| impl fmt::Debug for TimeRange { |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| write!(fmt, "[{},{})", self.start, self.end) |
| } |
| } |
| |
| #[derive(Debug)] |
| pub enum TimeRangesError { |
| EndOlderThanStart, |
| OutOfRange, |
| } |
| |
| #[derive(Clone, Debug, Default, JSTraceable, MallocSizeOf)] |
| pub struct TimeRangesContainer { |
| ranges: Vec<TimeRange>, |
| } |
| |
| impl TimeRangesContainer { |
| #[allow(clippy::len_without_is_empty)] |
| pub fn len(&self) -> u32 { |
| self.ranges.len() as u32 |
| } |
| |
| pub(crate) fn is_empty(&self) -> bool { |
| self.ranges.is_empty() |
| } |
| |
| pub fn start(&self, index: u32) -> Result<f64, TimeRangesError> { |
| self.ranges |
| .get(index as usize) |
| .map(|r| r.start) |
| .ok_or(TimeRangesError::OutOfRange) |
| } |
| |
| pub fn end(&self, index: u32) -> Result<f64, TimeRangesError> { |
| self.ranges |
| .get(index as usize) |
| .map(|r| r.end) |
| .ok_or(TimeRangesError::OutOfRange) |
| } |
| |
| pub fn add(&mut self, start: f64, end: f64) -> Result<(), TimeRangesError> { |
| if start > end { |
| return Err(TimeRangesError::EndOlderThanStart); |
| } |
| |
| let mut new_range = TimeRange { start, end }; |
| |
| // For each present range check if we need to: |
| // - merge with the added range, in case we are overlapping or contiguous, |
| // - insert in place, we are completely, not overlapping and not contiguous |
| // in between two ranges. |
| let mut idx = 0; |
| while idx < self.ranges.len() { |
| if new_range.is_overlapping(&self.ranges[idx]) || |
| new_range.is_contiguous(&self.ranges[idx]) |
| { |
| // The ranges are either overlapping or contiguous, |
| // we need to merge the new range with the existing one. |
| new_range.union(&self.ranges[idx]); |
| self.ranges.remove(idx); |
| } else if new_range.is_before(&self.ranges[idx]) && |
| (idx == 0 || self.ranges[idx - 1].is_before(&new_range)) |
| { |
| // We are exactly after the current previous range and before the current |
| // range, while not overlapping with none of them. |
| // Or we are simply at the beginning. |
| self.ranges.insert(idx, new_range); |
| return Ok(()); |
| } else { |
| idx += 1; |
| } |
| } |
| |
| // Insert at the end. |
| self.ranges.insert(idx, new_range); |
| |
| Ok(()) |
| } |
| } |
| |
| #[dom_struct] |
| pub(crate) struct TimeRanges { |
| reflector_: Reflector, |
| ranges: TimeRangesContainer, |
| } |
| |
| impl TimeRanges { |
| fn new_inherited(ranges: TimeRangesContainer) -> TimeRanges { |
| Self { |
| reflector_: Reflector::new(), |
| ranges, |
| } |
| } |
| |
| pub(crate) fn new( |
| window: &Window, |
| ranges: TimeRangesContainer, |
| can_gc: CanGc, |
| ) -> DomRoot<TimeRanges> { |
| reflect_dom_object(Box::new(TimeRanges::new_inherited(ranges)), window, can_gc) |
| } |
| } |
| |
| impl TimeRangesMethods<crate::DomTypeHolder> for TimeRanges { |
| // https://html.spec.whatwg.org/multipage/#dom-timeranges-length |
| fn Length(&self) -> u32 { |
| self.ranges.len() |
| } |
| |
| // https://html.spec.whatwg.org/multipage/#dom-timeranges-start |
| fn Start(&self, index: u32) -> Fallible<Finite<f64>> { |
| self.ranges |
| .start(index) |
| .map(Finite::wrap) |
| .map_err(|_| Error::IndexSize) |
| } |
| |
| // https://html.spec.whatwg.org/multipage/#dom-timeranges-end |
| fn End(&self, index: u32) -> Fallible<Finite<f64>> { |
| self.ranges |
| .end(index) |
| .map(Finite::wrap) |
| .map_err(|_| Error::IndexSize) |
| } |
| } |