|
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/. */ |
|
6 |
|
7 #include "nsTArray.h" |
|
8 #include "nsCharSeparatedTokenizer.h" |
|
9 #include "nsEscape.h" |
|
10 #include "nsIURI.h" |
|
11 #include <utility> |
|
12 |
|
13 #include "nsMediaFragmentURIParser.h" |
|
14 |
|
15 using std::pair; |
|
16 using std::make_pair; |
|
17 |
|
18 namespace mozilla { namespace net { |
|
19 |
|
20 nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsIURI* aURI) |
|
21 : mClipUnit(eClipUnit_Pixel) |
|
22 { |
|
23 nsAutoCString ref; |
|
24 aURI->GetRef(ref); |
|
25 Parse(ref); |
|
26 } |
|
27 |
|
28 nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsCString& aRef) |
|
29 : mClipUnit(eClipUnit_Pixel) |
|
30 { |
|
31 Parse(aRef); |
|
32 } |
|
33 |
|
34 bool nsMediaFragmentURIParser::ParseNPT(nsDependentSubstring aString) |
|
35 { |
|
36 nsDependentSubstring original(aString); |
|
37 if (aString.Length() > 4 && |
|
38 aString[0] == 'n' && aString[1] == 'p' && |
|
39 aString[2] == 't' && aString[3] == ':') { |
|
40 aString.Rebind(aString, 4); |
|
41 } |
|
42 |
|
43 if (aString.Length() == 0) { |
|
44 return false; |
|
45 } |
|
46 |
|
47 double start = -1.0; |
|
48 double end = -1.0; |
|
49 |
|
50 ParseNPTTime(aString, start); |
|
51 |
|
52 if (aString.Length() == 0) { |
|
53 mStart.construct(start); |
|
54 return true; |
|
55 } |
|
56 |
|
57 if (aString[0] != ',') { |
|
58 aString.Rebind(original, 0); |
|
59 return false; |
|
60 } |
|
61 |
|
62 aString.Rebind(aString, 1); |
|
63 |
|
64 if (aString.Length() == 0) { |
|
65 aString.Rebind(original, 0); |
|
66 return false; |
|
67 } |
|
68 |
|
69 ParseNPTTime(aString, end); |
|
70 |
|
71 if (end <= start || aString.Length() != 0) { |
|
72 aString.Rebind(original, 0); |
|
73 return false; |
|
74 } |
|
75 |
|
76 mStart.construct(start); |
|
77 mEnd.construct(end); |
|
78 return true; |
|
79 } |
|
80 |
|
81 bool nsMediaFragmentURIParser::ParseNPTTime(nsDependentSubstring& aString, double& aTime) |
|
82 { |
|
83 if (aString.Length() == 0) { |
|
84 return false; |
|
85 } |
|
86 |
|
87 return |
|
88 ParseNPTHHMMSS(aString, aTime) || |
|
89 ParseNPTMMSS(aString, aTime) || |
|
90 ParseNPTSec(aString, aTime); |
|
91 } |
|
92 |
|
93 // Return true if the given character is a numeric character |
|
94 static bool IsDigit(nsDependentSubstring::char_type aChar) |
|
95 { |
|
96 return (aChar >= '0' && aChar <= '9'); |
|
97 } |
|
98 |
|
99 // Return the index of the first character in the string that is not |
|
100 // a numerical digit, starting from 'aStart'. |
|
101 static uint32_t FirstNonDigit(nsDependentSubstring& aString, uint32_t aStart) |
|
102 { |
|
103 while (aStart < aString.Length() && IsDigit(aString[aStart])) { |
|
104 ++aStart; |
|
105 } |
|
106 return aStart; |
|
107 } |
|
108 |
|
109 bool nsMediaFragmentURIParser::ParseNPTSec(nsDependentSubstring& aString, double& aSec) |
|
110 { |
|
111 nsDependentSubstring original(aString); |
|
112 if (aString.Length() == 0) { |
|
113 return false; |
|
114 } |
|
115 |
|
116 uint32_t index = FirstNonDigit(aString, 0); |
|
117 if (index == 0) { |
|
118 return false; |
|
119 } |
|
120 |
|
121 nsDependentSubstring n(aString, 0, index); |
|
122 nsresult ec; |
|
123 int32_t s = PromiseFlatString(n).ToInteger(&ec); |
|
124 if (NS_FAILED(ec)) { |
|
125 return false; |
|
126 } |
|
127 |
|
128 aString.Rebind(aString, index); |
|
129 double fraction = 0.0; |
|
130 if (!ParseNPTFraction(aString, fraction)) { |
|
131 aString.Rebind(original, 0); |
|
132 return false; |
|
133 } |
|
134 |
|
135 aSec = s + fraction; |
|
136 return true; |
|
137 } |
|
138 |
|
139 bool nsMediaFragmentURIParser::ParseNPTMMSS(nsDependentSubstring& aString, double& aTime) |
|
140 { |
|
141 nsDependentSubstring original(aString); |
|
142 uint32_t mm = 0; |
|
143 uint32_t ss = 0; |
|
144 double fraction = 0.0; |
|
145 if (!ParseNPTMM(aString, mm)) { |
|
146 aString.Rebind(original, 0); |
|
147 return false; |
|
148 } |
|
149 |
|
150 if (aString.Length() < 2 || aString[0] != ':') { |
|
151 aString.Rebind(original, 0); |
|
152 return false; |
|
153 } |
|
154 |
|
155 aString.Rebind(aString, 1); |
|
156 if (!ParseNPTSS(aString, ss)) { |
|
157 aString.Rebind(original, 0); |
|
158 return false; |
|
159 } |
|
160 |
|
161 if (!ParseNPTFraction(aString, fraction)) { |
|
162 aString.Rebind(original, 0); |
|
163 return false; |
|
164 } |
|
165 aTime = mm * 60 + ss + fraction; |
|
166 return true; |
|
167 } |
|
168 |
|
169 bool nsMediaFragmentURIParser::ParseNPTFraction(nsDependentSubstring& aString, double& aFraction) |
|
170 { |
|
171 double fraction = 0.0; |
|
172 |
|
173 if (aString.Length() > 0 && aString[0] == '.') { |
|
174 uint32_t index = FirstNonDigit(aString, 1); |
|
175 |
|
176 if (index > 1) { |
|
177 nsDependentSubstring number(aString, 0, index); |
|
178 nsresult ec; |
|
179 fraction = PromiseFlatString(number).ToDouble(&ec); |
|
180 if (NS_FAILED(ec)) { |
|
181 return false; |
|
182 } |
|
183 } |
|
184 aString.Rebind(aString, index); |
|
185 } |
|
186 |
|
187 aFraction = fraction; |
|
188 return true; |
|
189 } |
|
190 |
|
191 bool nsMediaFragmentURIParser::ParseNPTHHMMSS(nsDependentSubstring& aString, double& aTime) |
|
192 { |
|
193 nsDependentSubstring original(aString); |
|
194 uint32_t hh = 0; |
|
195 double seconds = 0.0; |
|
196 if (!ParseNPTHH(aString, hh)) { |
|
197 return false; |
|
198 } |
|
199 |
|
200 if (aString.Length() < 2 || aString[0] != ':') { |
|
201 aString.Rebind(original, 0); |
|
202 return false; |
|
203 } |
|
204 |
|
205 aString.Rebind(aString, 1); |
|
206 if (!ParseNPTMMSS(aString, seconds)) { |
|
207 aString.Rebind(original, 0); |
|
208 return false; |
|
209 } |
|
210 |
|
211 aTime = hh * 3600 + seconds; |
|
212 return true; |
|
213 } |
|
214 |
|
215 bool nsMediaFragmentURIParser::ParseNPTHH(nsDependentSubstring& aString, uint32_t& aHour) |
|
216 { |
|
217 if (aString.Length() == 0) { |
|
218 return false; |
|
219 } |
|
220 |
|
221 uint32_t index = FirstNonDigit(aString, 0); |
|
222 if (index == 0) { |
|
223 return false; |
|
224 } |
|
225 |
|
226 nsDependentSubstring n(aString, 0, index); |
|
227 nsresult ec; |
|
228 int32_t u = PromiseFlatString(n).ToInteger(&ec); |
|
229 if (NS_FAILED(ec)) { |
|
230 return false; |
|
231 } |
|
232 |
|
233 aString.Rebind(aString, index); |
|
234 aHour = u; |
|
235 return true; |
|
236 } |
|
237 |
|
238 bool nsMediaFragmentURIParser::ParseNPTMM(nsDependentSubstring& aString, uint32_t& aMinute) |
|
239 { |
|
240 return ParseNPTSS(aString, aMinute); |
|
241 } |
|
242 |
|
243 bool nsMediaFragmentURIParser::ParseNPTSS(nsDependentSubstring& aString, uint32_t& aSecond) |
|
244 { |
|
245 if (aString.Length() < 2) { |
|
246 return false; |
|
247 } |
|
248 |
|
249 if (IsDigit(aString[0]) && IsDigit(aString[1])) { |
|
250 nsDependentSubstring n(aString, 0, 2); |
|
251 nsresult ec; |
|
252 int32_t u = PromiseFlatString(n).ToInteger(&ec); |
|
253 if (NS_FAILED(ec)) { |
|
254 return false; |
|
255 } |
|
256 |
|
257 aString.Rebind(aString, 2); |
|
258 if (u >= 60) |
|
259 return false; |
|
260 |
|
261 aSecond = u; |
|
262 return true; |
|
263 } |
|
264 |
|
265 return false; |
|
266 } |
|
267 |
|
268 static bool ParseInteger(nsDependentSubstring& aString, |
|
269 int32_t& aResult) |
|
270 { |
|
271 uint32_t index = FirstNonDigit(aString, 0); |
|
272 if (index == 0) { |
|
273 return false; |
|
274 } |
|
275 |
|
276 nsDependentSubstring n(aString, 0, index); |
|
277 nsresult ec; |
|
278 int32_t s = PromiseFlatString(n).ToInteger(&ec); |
|
279 if (NS_FAILED(ec)) { |
|
280 return false; |
|
281 } |
|
282 |
|
283 aString.Rebind(aString, index); |
|
284 aResult = s; |
|
285 return true; |
|
286 } |
|
287 |
|
288 static bool ParseCommaSeparator(nsDependentSubstring& aString) |
|
289 { |
|
290 if (aString.Length() > 1 && aString[0] == ',') { |
|
291 aString.Rebind(aString, 1); |
|
292 return true; |
|
293 } |
|
294 |
|
295 return false; |
|
296 } |
|
297 |
|
298 bool nsMediaFragmentURIParser::ParseXYWH(nsDependentSubstring aString) |
|
299 { |
|
300 int32_t x, y, w, h; |
|
301 ClipUnit clipUnit; |
|
302 |
|
303 // Determine units. |
|
304 if (StringBeginsWith(aString, NS_LITERAL_STRING("pixel:"))) { |
|
305 clipUnit = eClipUnit_Pixel; |
|
306 aString.Rebind(aString, 6); |
|
307 } else if (StringBeginsWith(aString, NS_LITERAL_STRING("percent:"))) { |
|
308 clipUnit = eClipUnit_Percent; |
|
309 aString.Rebind(aString, 8); |
|
310 } else { |
|
311 clipUnit = eClipUnit_Pixel; |
|
312 } |
|
313 |
|
314 // Read and validate coordinates. |
|
315 if (ParseInteger(aString, x) && x >= 0 && |
|
316 ParseCommaSeparator(aString) && |
|
317 ParseInteger(aString, y) && y >= 0 && |
|
318 ParseCommaSeparator(aString) && |
|
319 ParseInteger(aString, w) && w > 0 && |
|
320 ParseCommaSeparator(aString) && |
|
321 ParseInteger(aString, h) && h > 0 && |
|
322 aString.Length() == 0) { |
|
323 |
|
324 // Reject invalid percentage coordinates. |
|
325 if (clipUnit == eClipUnit_Percent && |
|
326 (x + w > 100 || y + h > 100)) { |
|
327 return false; |
|
328 } |
|
329 |
|
330 mClip.construct(x, y, w, h); |
|
331 mClipUnit = clipUnit; |
|
332 return true; |
|
333 } |
|
334 |
|
335 return false; |
|
336 } |
|
337 |
|
338 bool nsMediaFragmentURIParser::ParseMozResolution(nsDependentSubstring aString) |
|
339 { |
|
340 int32_t w, h; |
|
341 |
|
342 // Read and validate coordinates. |
|
343 if (ParseInteger(aString, w) && w >= 0 && |
|
344 ParseCommaSeparator(aString) && |
|
345 ParseInteger(aString, h) && h >= 0 && |
|
346 aString.Length() == 0) { |
|
347 mResolution.construct(w,h); |
|
348 return true; |
|
349 } |
|
350 |
|
351 return false; |
|
352 } |
|
353 |
|
354 bool nsMediaFragmentURIParser::ParseMozSampleSize(nsDependentSubstring aString) |
|
355 { |
|
356 int32_t sampleSize; |
|
357 |
|
358 // Read and validate coordinates. |
|
359 if (ParseInteger(aString, sampleSize) && sampleSize > 0) { |
|
360 mSampleSize.construct(sampleSize); |
|
361 return true; |
|
362 } |
|
363 |
|
364 return false; |
|
365 } |
|
366 |
|
367 void nsMediaFragmentURIParser::Parse(nsACString& aRef) |
|
368 { |
|
369 // Create an array of possibly-invalid media fragments. |
|
370 nsTArray< std::pair<nsCString, nsCString> > fragments; |
|
371 nsCCharSeparatedTokenizer tokenizer(aRef, '&'); |
|
372 |
|
373 while (tokenizer.hasMoreTokens()) { |
|
374 const nsCSubstring& nv = tokenizer.nextToken(); |
|
375 int32_t index = nv.FindChar('='); |
|
376 if (index >= 0) { |
|
377 nsAutoCString name; |
|
378 nsAutoCString value; |
|
379 NS_UnescapeURL(StringHead(nv, index), esc_Ref | esc_AlwaysCopy, name); |
|
380 NS_UnescapeURL(Substring(nv, index + 1, nv.Length()), |
|
381 esc_Ref | esc_AlwaysCopy, value); |
|
382 fragments.AppendElement(make_pair(name, value)); |
|
383 } |
|
384 } |
|
385 |
|
386 // Parse the media fragment values. |
|
387 bool gotTemporal = false, gotSpatial = false, |
|
388 gotResolution = false, gotSampleSize = false; |
|
389 for (int i = fragments.Length() - 1 ; i >= 0 ; --i) { |
|
390 if (gotTemporal && gotSpatial && gotResolution && gotSampleSize) { |
|
391 // We've got one of each possible type. No need to look at the rest. |
|
392 break; |
|
393 } else if (!gotTemporal && fragments[i].first.EqualsLiteral("t")) { |
|
394 nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second); |
|
395 gotTemporal = ParseNPT(nsDependentSubstring(value, 0)); |
|
396 } else if (!gotSpatial && fragments[i].first.EqualsLiteral("xywh")) { |
|
397 nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second); |
|
398 gotSpatial = ParseXYWH(nsDependentSubstring(value, 0)); |
|
399 } else if (!gotResolution && fragments[i].first.EqualsLiteral("-moz-resolution")) { |
|
400 nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second); |
|
401 gotResolution = ParseMozResolution(nsDependentSubstring(value, 0)); |
|
402 } else if (!gotSampleSize && fragments[i].first.EqualsLiteral("-moz-samplesize")) { |
|
403 nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second); |
|
404 gotSampleSize = ParseMozSampleSize(nsDependentSubstring(value, 0)); |
|
405 } |
|
406 } |
|
407 } |
|
408 |
|
409 }} // namespace mozilla::net |