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