Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/base/nsMediaFragmentURIParser.cpp
Line
Count
Source (jump to first uncovered line)
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
0
{
23
0
  nsAutoCString ref;
24
0
  aURI->GetRef(ref);
25
0
  Parse(ref);
26
0
}
27
28
nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsCString& aRef)
29
  : mClipUnit(eClipUnit_Pixel)
30
0
{
31
0
  Parse(aRef);
32
0
}
33
34
bool nsMediaFragmentURIParser::ParseNPT(nsDependentSubstring aString)
35
0
{
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
0
  }
42
0
43
0
  if (aString.Length() == 0) {
44
0
    return false;
45
0
  }
46
0
47
0
  double start = -1.0;
48
0
  double end = -1.0;
49
0
50
0
  ParseNPTTime(aString, start);
51
0
52
0
  if (aString.Length() == 0) {
53
0
    mStart.emplace(start);
54
0
    return true;
55
0
  }
56
0
57
0
  if (aString[0] != ',') {
58
0
    aString.Rebind(original, 0);
59
0
    return false;
60
0
  }
61
0
62
0
  aString.Rebind(aString, 1);
63
0
64
0
  if (aString.Length() == 0) {
65
0
    aString.Rebind(original, 0);
66
0
    return false;
67
0
  }
68
0
69
0
  ParseNPTTime(aString, end);
70
0
71
0
  if (end <= start || aString.Length() != 0) {
72
0
    aString.Rebind(original, 0);
73
0
    return false;
74
0
  }
75
0
76
0
  mStart.emplace(start);
77
0
  mEnd.emplace(end);
78
0
  return true;
79
0
}
80
81
bool nsMediaFragmentURIParser::ParseNPTTime(nsDependentSubstring& aString, double& aTime)
82
0
{
83
0
  if (aString.Length() == 0) {
84
0
    return false;
85
0
  }
86
0
87
0
  return
88
0
    ParseNPTHHMMSS(aString, aTime) ||
89
0
    ParseNPTMMSS(aString, aTime) ||
90
0
    ParseNPTSec(aString, aTime);
91
0
}
92
93
// Return true if the given character is a numeric character
94
static bool IsDigit(nsDependentSubstring::char_type aChar)
95
0
{
96
0
  return (aChar >= '0' && aChar <= '9');
97
0
}
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
0
{
103
0
   while (aStart < aString.Length() && IsDigit(aString[aStart])) {
104
0
    ++aStart;
105
0
  }
106
0
  return aStart;
107
0
}
108
109
bool nsMediaFragmentURIParser::ParseNPTSec(nsDependentSubstring& aString, double& aSec)
110
0
{
111
0
  nsDependentSubstring original(aString);
112
0
  if (aString.Length() == 0) {
113
0
    return false;
114
0
  }
115
0
116
0
  uint32_t index = FirstNonDigit(aString, 0);
117
0
  if (index == 0) {
118
0
    return false;
119
0
  }
120
0
121
0
  nsDependentSubstring n(aString, 0, index);
122
0
  nsresult ec;
123
0
  int32_t s = PromiseFlatString(n).ToInteger(&ec);
124
0
  if (NS_FAILED(ec)) {
125
0
    return false;
126
0
  }
127
0
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
0
  }
134
0
135
0
  aSec = s + fraction;
136
0
  return true;
137
0
}
138
139
bool nsMediaFragmentURIParser::ParseNPTMMSS(nsDependentSubstring& aString, double& aTime)
140
0
{
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
0
  }
149
0
150
0
  if (aString.Length() < 2 || aString[0] != ':') {
151
0
    aString.Rebind(original, 0);
152
0
    return false;
153
0
  }
154
0
155
0
  aString.Rebind(aString, 1);
156
0
  if (!ParseNPTSS(aString, ss)) {
157
0
    aString.Rebind(original, 0);
158
0
    return false;
159
0
  }
160
0
161
0
  if (!ParseNPTFraction(aString, fraction)) {
162
0
    aString.Rebind(original, 0);
163
0
    return false;
164
0
  }
165
0
  aTime = mm * 60 + ss + fraction;
166
0
  return true;
167
0
}
168
169
bool nsMediaFragmentURIParser::ParseNPTFraction(nsDependentSubstring& aString, double& aFraction)
170
0
{
171
0
  double fraction = 0.0;
172
0
173
0
  if (aString.Length() > 0 && aString[0] == '.') {
174
0
    uint32_t index = FirstNonDigit(aString, 1);
175
0
176
0
    if (index > 1) {
177
0
      nsDependentSubstring number(aString, 0, index);
178
0
      nsresult ec;
179
0
      fraction = PromiseFlatString(number).ToDouble(&ec);
180
0
      if (NS_FAILED(ec)) {
181
0
        return false;
182
0
      }
183
0
    }
184
0
    aString.Rebind(aString, index);
185
0
  }
186
0
187
0
  aFraction = fraction;
188
0
  return true;
189
0
}
190
191
bool nsMediaFragmentURIParser::ParseNPTHHMMSS(nsDependentSubstring& aString, double& aTime)
192
0
{
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
0
  }
199
0
200
0
  if (aString.Length() < 2 || aString[0] != ':') {
201
0
    aString.Rebind(original, 0);
202
0
    return false;
203
0
  }
204
0
205
0
  aString.Rebind(aString, 1);
206
0
  if (!ParseNPTMMSS(aString, seconds)) {
207
0
    aString.Rebind(original, 0);
208
0
    return false;
209
0
  }
210
0
211
0
  aTime = hh * 3600 + seconds;
212
0
  return true;
213
0
}
214
215
bool nsMediaFragmentURIParser::ParseNPTHH(nsDependentSubstring& aString, uint32_t& aHour)
216
0
{
217
0
  if (aString.Length() == 0) {
218
0
    return false;
219
0
  }
220
0
221
0
  uint32_t index = FirstNonDigit(aString, 0);
222
0
  if (index == 0) {
223
0
    return false;
224
0
  }
225
0
226
0
  nsDependentSubstring n(aString, 0, index);
227
0
  nsresult ec;
228
0
  int32_t u = PromiseFlatString(n).ToInteger(&ec);
229
0
  if (NS_FAILED(ec)) {
230
0
    return false;
231
0
  }
232
0
233
0
  aString.Rebind(aString, index);
234
0
  aHour = u;
235
0
  return true;
236
0
}
237
238
bool nsMediaFragmentURIParser::ParseNPTMM(nsDependentSubstring& aString, uint32_t& aMinute)
239
0
{
240
0
  return ParseNPTSS(aString, aMinute);
241
0
}
242
243
bool nsMediaFragmentURIParser::ParseNPTSS(nsDependentSubstring& aString, uint32_t& aSecond)
244
0
{
245
0
  if (aString.Length() < 2) {
246
0
    return false;
247
0
  }
248
0
249
0
  if (IsDigit(aString[0]) && IsDigit(aString[1])) {
250
0
    nsDependentSubstring n(aString, 0, 2);
251
0
    nsresult ec;
252
0
    int32_t u = PromiseFlatString(n).ToInteger(&ec);
253
0
    if (NS_FAILED(ec)) {
254
0
      return false;
255
0
    }
256
0
257
0
    aString.Rebind(aString, 2);
258
0
    if (u >= 60)
259
0
      return false;
260
0
261
0
    aSecond = u;
262
0
    return true;
263
0
  }
264
0
265
0
  return false;
266
0
}
267
268
static bool ParseInteger(nsDependentSubstring& aString,
269
                         int32_t& aResult)
270
0
{
271
0
  uint32_t index = FirstNonDigit(aString, 0);
272
0
  if (index == 0) {
273
0
    return false;
274
0
  }
275
0
276
0
  nsDependentSubstring n(aString, 0, index);
277
0
  nsresult ec;
278
0
  int32_t s = PromiseFlatString(n).ToInteger(&ec);
279
0
  if (NS_FAILED(ec)) {
280
0
    return false;
281
0
  }
282
0
283
0
  aString.Rebind(aString, index);
284
0
  aResult = s;
285
0
  return true;
286
0
}
287
288
static bool ParseCommaSeparator(nsDependentSubstring& aString)
289
0
{
290
0
  if (aString.Length() > 1 && aString[0] == ',') {
291
0
    aString.Rebind(aString, 1);
292
0
    return true;
293
0
  }
294
0
295
0
  return false;
296
0
}
297
298
bool nsMediaFragmentURIParser::ParseXYWH(nsDependentSubstring aString)
299
0
{
300
0
  int32_t x, y, w, h;
301
0
  ClipUnit clipUnit;
302
0
303
0
  // 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
0
  } else {
311
0
    clipUnit = eClipUnit_Pixel;
312
0
  }
313
0
314
0
  // 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
0
324
0
    // Reject invalid percentage coordinates.
325
0
    if (clipUnit == eClipUnit_Percent &&
326
0
        (x + w > 100 || y + h > 100)) {
327
0
      return false;
328
0
    }
329
0
330
0
    mClip.emplace(x, y, w, h);
331
0
    mClipUnit = clipUnit;
332
0
    return true;
333
0
  }
334
0
335
0
  return false;
336
0
}
337
338
void nsMediaFragmentURIParser::Parse(nsACString& aRef)
339
0
{
340
0
  // Create an array of possibly-invalid media fragments.
341
0
  nsTArray< std::pair<nsCString, nsCString> > fragments;
342
0
  nsCCharSeparatedTokenizer tokenizer(aRef, '&');
343
0
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
0
    }
355
0
  }
356
0
357
0
  // 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
0
      // We've got one of each possible type. No need to look at the rest.
362
0
      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
0
    }
370
0
  }
371
0
}
372
373
} // namespace net
374
} // namespace mozilla