1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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/. */
8 #include "nsCharSeparatedTokenizer.h"
13 #include "nsMediaFragmentURIParser.h"
21 nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsIURI
* aURI
)
22 : mClipUnit(eClipUnit_Pixel
) {
28 nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsCString
& aRef
)
29 : mClipUnit(eClipUnit_Pixel
) {
33 bool nsMediaFragmentURIParser::ParseNPT(nsDependentSubstring aString
) {
34 nsDependentSubstring
original(aString
);
35 if (aString
.Length() > 4 && aString
[0] == 'n' && aString
[1] == 'p' &&
36 aString
[2] == 't' && aString
[3] == ':') {
37 aString
.Rebind(aString
, 4);
40 if (aString
.Length() == 0) {
47 ParseNPTTime(aString
, start
);
49 if (aString
.Length() == 0) {
50 mStart
.emplace(start
);
54 if (aString
[0] != ',') {
55 aString
.Rebind(original
, 0);
59 aString
.Rebind(aString
, 1);
61 if (aString
.Length() == 0) {
62 aString
.Rebind(original
, 0);
66 ParseNPTTime(aString
, end
);
68 if (end
<= start
|| aString
.Length() != 0) {
69 aString
.Rebind(original
, 0);
73 mStart
.emplace(start
);
78 bool nsMediaFragmentURIParser::ParseNPTTime(nsDependentSubstring
& aString
,
80 if (aString
.Length() == 0) {
84 return ParseNPTHHMMSS(aString
, aTime
) || ParseNPTMMSS(aString
, aTime
) ||
85 ParseNPTSec(aString
, aTime
);
88 // Return true if the given character is a numeric character
89 static bool IsDigit(nsDependentSubstring::char_type aChar
) {
90 return (aChar
>= '0' && aChar
<= '9');
93 // Return the index of the first character in the string that is not
94 // a numerical digit, starting from 'aStart'.
95 static uint32_t FirstNonDigit(nsDependentSubstring
& aString
, uint32_t aStart
) {
96 while (aStart
< aString
.Length() && IsDigit(aString
[aStart
])) {
102 bool nsMediaFragmentURIParser::ParseNPTSec(nsDependentSubstring
& aString
,
104 nsDependentSubstring
original(aString
);
105 if (aString
.Length() == 0) {
109 uint32_t index
= FirstNonDigit(aString
, 0);
114 nsDependentSubstring
n(aString
, 0, index
);
116 int32_t s
= n
.ToInteger(&ec
);
121 aString
.Rebind(aString
, index
);
122 double fraction
= 0.0;
123 if (!ParseNPTFraction(aString
, fraction
)) {
124 aString
.Rebind(original
, 0);
132 bool nsMediaFragmentURIParser::ParseNPTMMSS(nsDependentSubstring
& aString
,
134 nsDependentSubstring
original(aString
);
137 double fraction
= 0.0;
138 if (!ParseNPTMM(aString
, mm
)) {
139 aString
.Rebind(original
, 0);
143 if (aString
.Length() < 2 || aString
[0] != ':') {
144 aString
.Rebind(original
, 0);
148 aString
.Rebind(aString
, 1);
149 if (!ParseNPTSS(aString
, ss
)) {
150 aString
.Rebind(original
, 0);
154 if (!ParseNPTFraction(aString
, fraction
)) {
155 aString
.Rebind(original
, 0);
158 aTime
= mm
* 60 + ss
+ fraction
;
162 bool nsMediaFragmentURIParser::ParseNPTFraction(nsDependentSubstring
& aString
,
164 double fraction
= 0.0;
166 if (aString
.Length() > 0 && aString
[0] == '.') {
167 uint32_t index
= FirstNonDigit(aString
, 1);
170 nsDependentSubstring
number(aString
, 0, index
);
172 fraction
= PromiseFlatString(number
).ToDouble(&ec
);
177 aString
.Rebind(aString
, index
);
180 aFraction
= fraction
;
184 bool nsMediaFragmentURIParser::ParseNPTHHMMSS(nsDependentSubstring
& aString
,
186 nsDependentSubstring
original(aString
);
188 double seconds
= 0.0;
189 if (!ParseNPTHH(aString
, hh
)) {
193 if (aString
.Length() < 2 || aString
[0] != ':') {
194 aString
.Rebind(original
, 0);
198 aString
.Rebind(aString
, 1);
199 if (!ParseNPTMMSS(aString
, seconds
)) {
200 aString
.Rebind(original
, 0);
204 aTime
= hh
* 3600 + seconds
;
208 bool nsMediaFragmentURIParser::ParseNPTHH(nsDependentSubstring
& aString
,
210 if (aString
.Length() == 0) {
214 uint32_t index
= FirstNonDigit(aString
, 0);
219 nsDependentSubstring
n(aString
, 0, index
);
221 int32_t u
= n
.ToInteger(&ec
);
226 aString
.Rebind(aString
, index
);
231 bool nsMediaFragmentURIParser::ParseNPTMM(nsDependentSubstring
& aString
,
233 return ParseNPTSS(aString
, aMinute
);
236 bool nsMediaFragmentURIParser::ParseNPTSS(nsDependentSubstring
& aString
,
238 if (aString
.Length() < 2) {
242 if (IsDigit(aString
[0]) && IsDigit(aString
[1])) {
243 nsDependentSubstring
n(aString
, 0, 2);
245 int32_t u
= n
.ToInteger(&ec
);
250 aString
.Rebind(aString
, 2);
251 if (u
>= 60) return false;
260 static bool ParseInteger(nsDependentSubstring
& aString
, int32_t& aResult
) {
261 uint32_t index
= FirstNonDigit(aString
, 0);
266 nsDependentSubstring
n(aString
, 0, index
);
268 int32_t s
= n
.ToInteger(&ec
);
273 aString
.Rebind(aString
, index
);
278 static bool ParseCommaSeparator(nsDependentSubstring
& aString
) {
279 if (aString
.Length() > 1 && aString
[0] == ',') {
280 aString
.Rebind(aString
, 1);
287 bool nsMediaFragmentURIParser::ParseXYWH(nsDependentSubstring aString
) {
292 if (StringBeginsWith(aString
, u
"pixel:"_ns
)) {
293 clipUnit
= eClipUnit_Pixel
;
294 aString
.Rebind(aString
, 6);
295 } else if (StringBeginsWith(aString
, u
"percent:"_ns
)) {
296 clipUnit
= eClipUnit_Percent
;
297 aString
.Rebind(aString
, 8);
299 clipUnit
= eClipUnit_Pixel
;
302 // Read and validate coordinates.
303 if (ParseInteger(aString
, x
) && x
>= 0 && ParseCommaSeparator(aString
) &&
304 ParseInteger(aString
, y
) && y
>= 0 && ParseCommaSeparator(aString
) &&
305 ParseInteger(aString
, w
) && w
> 0 && ParseCommaSeparator(aString
) &&
306 ParseInteger(aString
, h
) && h
> 0 && aString
.Length() == 0) {
307 // Reject invalid percentage coordinates.
308 if (clipUnit
== eClipUnit_Percent
&& (x
+ w
> 100 || y
+ h
> 100)) {
312 mClip
.emplace(x
, y
, w
, h
);
313 mClipUnit
= clipUnit
;
320 void nsMediaFragmentURIParser::Parse(nsACString
& aRef
) {
321 // Create an array of possibly-invalid media fragments.
322 nsTArray
<std::pair
<nsCString
, nsCString
> > fragments
;
324 for (const nsACString
& nv
: nsCCharSeparatedTokenizer(aRef
, '&').ToRange()) {
325 int32_t index
= nv
.FindChar('=');
329 NS_UnescapeURL(StringHead(nv
, index
), esc_Ref
| esc_AlwaysCopy
, name
);
330 NS_UnescapeURL(Substring(nv
, index
+ 1, nv
.Length()),
331 esc_Ref
| esc_AlwaysCopy
, value
);
332 fragments
.AppendElement(make_pair(name
, value
));
336 // Parse the media fragment values.
337 bool gotTemporal
= false, gotSpatial
= false;
338 for (int i
= fragments
.Length() - 1; i
>= 0; --i
) {
339 if (gotTemporal
&& gotSpatial
) {
340 // We've got one of each possible type. No need to look at the rest.
343 if (!gotTemporal
&& fragments
[i
].first
.EqualsLiteral("t")) {
344 nsAutoString value
= NS_ConvertUTF8toUTF16(fragments
[i
].second
);
345 gotTemporal
= ParseNPT(nsDependentSubstring(value
, 0));
346 } else if (!gotSpatial
&& fragments
[i
].first
.EqualsLiteral("xywh")) {
347 nsAutoString value
= NS_ConvertUTF8toUTF16(fragments
[i
].second
);
348 gotSpatial
= ParseXYWH(nsDependentSubstring(value
, 0));
354 } // namespace mozilla