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