1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/dom/TimeRanges.h"
8 #include "mozilla/dom/TimeRangesBinding.h"
9 #include "mozilla/dom/HTMLMediaElement.h"
10 #include "TimeUnits.h"
11 #include "nsError.h"
12 
13 namespace mozilla::dom {
14 
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TimeRanges,mParent)15 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TimeRanges, mParent)
16 NS_IMPL_CYCLE_COLLECTING_ADDREF(TimeRanges)
17 NS_IMPL_CYCLE_COLLECTING_RELEASE(TimeRanges)
18 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TimeRanges)
19   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
20   NS_INTERFACE_MAP_ENTRY(nsISupports)
21 NS_INTERFACE_MAP_END
22 
23 TimeRanges::TimeRanges() : mParent(nullptr) {}
24 
TimeRanges(nsISupports * aParent)25 TimeRanges::TimeRanges(nsISupports* aParent) : mParent(aParent) {}
26 
TimeRanges(nsISupports * aParent,const media::TimeIntervals & aTimeIntervals)27 TimeRanges::TimeRanges(nsISupports* aParent,
28                        const media::TimeIntervals& aTimeIntervals)
29     : TimeRanges(aParent) {
30   if (aTimeIntervals.IsInvalid()) {
31     return;
32   }
33   for (const media::TimeInterval& interval : aTimeIntervals) {
34     Add(interval.mStart.ToSeconds(), interval.mEnd.ToSeconds());
35   }
36 }
37 
TimeRanges(const media::TimeIntervals & aTimeIntervals)38 TimeRanges::TimeRanges(const media::TimeIntervals& aTimeIntervals)
39     : TimeRanges(nullptr, aTimeIntervals) {}
40 
ToTimeIntervals() const41 media::TimeIntervals TimeRanges::ToTimeIntervals() const {
42   media::TimeIntervals t;
43   for (uint32_t i = 0; i < Length(); i++) {
44     t += media::TimeInterval(media::TimeUnit::FromSeconds(Start(i)),
45                              media::TimeUnit::FromSeconds(End(i)));
46   }
47   return t;
48 }
49 
50 TimeRanges::~TimeRanges() = default;
51 
Start(uint32_t aIndex,ErrorResult & aRv) const52 double TimeRanges::Start(uint32_t aIndex, ErrorResult& aRv) const {
53   if (aIndex >= mRanges.Length()) {
54     aRv = NS_ERROR_DOM_INDEX_SIZE_ERR;
55     return 0;
56   }
57 
58   return Start(aIndex);
59 }
60 
End(uint32_t aIndex,ErrorResult & aRv) const61 double TimeRanges::End(uint32_t aIndex, ErrorResult& aRv) const {
62   if (aIndex >= mRanges.Length()) {
63     aRv = NS_ERROR_DOM_INDEX_SIZE_ERR;
64     return 0;
65   }
66 
67   return End(aIndex);
68 }
69 
Add(double aStart,double aEnd)70 void TimeRanges::Add(double aStart, double aEnd) {
71   if (aStart > aEnd) {
72     NS_WARNING("Can't add a range if the end is older that the start.");
73     return;
74   }
75   mRanges.AppendElement(TimeRange(aStart, aEnd));
76 }
77 
GetStartTime()78 double TimeRanges::GetStartTime() {
79   if (mRanges.IsEmpty()) {
80     return -1.0;
81   }
82   return mRanges[0].mStart;
83 }
84 
GetEndTime()85 double TimeRanges::GetEndTime() {
86   if (mRanges.IsEmpty()) {
87     return -1.0;
88   }
89   return mRanges[mRanges.Length() - 1].mEnd;
90 }
91 
Normalize(double aTolerance)92 void TimeRanges::Normalize(double aTolerance) {
93   if (mRanges.Length() >= 2) {
94     AutoTArray<TimeRange, 4> normalized;
95 
96     mRanges.Sort(CompareTimeRanges());
97 
98     // This merges the intervals.
99     TimeRange current(mRanges[0]);
100     for (uint32_t i = 1; i < mRanges.Length(); i++) {
101       if (current.mStart <= mRanges[i].mStart &&
102           current.mEnd >= mRanges[i].mEnd) {
103         continue;
104       }
105       if (current.mEnd + aTolerance >= mRanges[i].mStart) {
106         current.mEnd = mRanges[i].mEnd;
107       } else {
108         normalized.AppendElement(current);
109         current = mRanges[i];
110       }
111     }
112 
113     normalized.AppendElement(current);
114 
115     mRanges = std::move(normalized);
116   }
117 }
118 
Union(const TimeRanges * aOtherRanges,double aTolerance)119 void TimeRanges::Union(const TimeRanges* aOtherRanges, double aTolerance) {
120   mRanges.AppendElements(aOtherRanges->mRanges);
121   Normalize(aTolerance);
122 }
123 
Intersection(const TimeRanges * aOtherRanges)124 void TimeRanges::Intersection(const TimeRanges* aOtherRanges) {
125   AutoTArray<TimeRange, 4> intersection;
126 
127   const nsTArray<TimeRange>& otherRanges = aOtherRanges->mRanges;
128   for (index_type i = 0, j = 0;
129        i < mRanges.Length() && j < otherRanges.Length();) {
130     double start = std::max(mRanges[i].mStart, otherRanges[j].mStart);
131     double end = std::min(mRanges[i].mEnd, otherRanges[j].mEnd);
132     if (start < end) {
133       intersection.AppendElement(TimeRange(start, end));
134     }
135     if (mRanges[i].mEnd < otherRanges[j].mEnd) {
136       i += 1;
137     } else {
138       j += 1;
139     }
140   }
141 
142   mRanges = std::move(intersection);
143 }
144 
Find(double aTime,double aTolerance)145 TimeRanges::index_type TimeRanges::Find(double aTime,
146                                         double aTolerance /* = 0 */) {
147   for (index_type i = 0; i < mRanges.Length(); ++i) {
148     if (aTime < mRanges[i].mEnd && (aTime + aTolerance) >= mRanges[i].mStart) {
149       return i;
150     }
151   }
152   return NoIndex;
153 }
154 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)155 JSObject* TimeRanges::WrapObject(JSContext* aCx,
156                                  JS::Handle<JSObject*> aGivenProto) {
157   return TimeRanges_Binding::Wrap(aCx, this, aGivenProto);
158 }
159 
GetParentObject() const160 nsISupports* TimeRanges::GetParentObject() const { return mParent; }
161 
Shift(double aOffset)162 void TimeRanges::Shift(double aOffset) {
163   for (index_type i = 0; i < mRanges.Length(); ++i) {
164     mRanges[i].mStart += aOffset;
165     mRanges[i].mEnd += aOffset;
166   }
167 }
168 
169 }  // namespace mozilla::dom
170