Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/smil/nsSMILParserUtils.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsSMILParserUtils.h"
8
#include "mozilla/TextUtils.h"
9
#include "nsSMILKeySpline.h"
10
#include "nsISMILAttr.h"
11
#include "nsSMILValue.h"
12
#include "nsSMILTimeValue.h"
13
#include "nsSMILTimeValueSpecParams.h"
14
#include "nsSMILTypes.h"
15
#include "nsSMILRepeatCount.h"
16
#include "nsContentUtils.h"
17
#include "nsCharSeparatedTokenizer.h"
18
#include "SVGContentUtils.h"
19
20
using namespace mozilla;
21
using namespace mozilla::dom;
22
//------------------------------------------------------------------------------
23
// Helper functions and Constants
24
25
namespace {
26
27
const uint32_t MSEC_PER_SEC  = 1000;
28
const uint32_t MSEC_PER_MIN  = 1000 * 60;
29
const uint32_t MSEC_PER_HOUR = 1000 * 60 * 60;
30
31
0
#define ACCESSKEY_PREFIX_LC NS_LITERAL_STRING("accesskey(") // SMIL2+
32
0
#define ACCESSKEY_PREFIX_CC NS_LITERAL_STRING("accessKey(") // SVG/SMIL ANIM
33
0
#define REPEAT_PREFIX    NS_LITERAL_STRING("repeat(")
34
0
#define WALLCLOCK_PREFIX NS_LITERAL_STRING("wallclock(")
35
36
inline bool
37
SkipWhitespace(RangedPtr<const char16_t>& aIter,
38
               const RangedPtr<const char16_t>& aEnd)
39
0
{
40
0
  while (aIter != aEnd) {
41
0
    if (!nsContentUtils::IsHTMLWhitespace(*aIter)) {
42
0
      return true;
43
0
    }
44
0
    ++aIter;
45
0
  }
46
0
  return false;
47
0
}
48
49
inline bool
50
ParseColon(RangedPtr<const char16_t>& aIter,
51
           const RangedPtr<const char16_t>& aEnd)
52
0
{
53
0
  if (aIter == aEnd || *aIter != ':') {
54
0
    return false;
55
0
  }
56
0
  ++aIter;
57
0
  return true;
58
0
}
59
60
/*
61
 * Exactly two digits in the range 00 - 59 are expected.
62
 */
63
bool
64
ParseSecondsOrMinutes(RangedPtr<const char16_t>& aIter,
65
                      const RangedPtr<const char16_t>& aEnd,
66
                      uint32_t& aValue)
67
0
{
68
0
  if (aIter == aEnd || !mozilla::IsAsciiDigit(*aIter)) {
69
0
    return false;
70
0
  }
71
0
72
0
  RangedPtr<const char16_t> iter(aIter);
73
0
74
0
  if (++iter == aEnd || !mozilla::IsAsciiDigit(*iter)) {
75
0
     return false;
76
0
  }
77
0
78
0
  uint32_t value = 10 * mozilla::AsciiAlphanumericToNumber(*aIter) +
79
0
                   mozilla::AsciiAlphanumericToNumber(*iter);
80
0
  if (value > 59) {
81
0
    return false;
82
0
  }
83
0
  if (++iter != aEnd && mozilla::IsAsciiDigit(*iter)) {
84
0
    return false;
85
0
  }
86
0
87
0
  aValue = value;
88
0
  aIter = iter;
89
0
  return true;
90
0
}
91
92
inline bool
93
ParseClockMetric(RangedPtr<const char16_t>& aIter,
94
                 const RangedPtr<const char16_t>& aEnd,
95
                 uint32_t& aMultiplier)
96
0
{
97
0
  if (aIter == aEnd) {
98
0
    aMultiplier = MSEC_PER_SEC;
99
0
    return true;
100
0
  }
101
0
102
0
  switch (*aIter) {
103
0
  case 'h':
104
0
    if (++aIter == aEnd) {
105
0
      aMultiplier = MSEC_PER_HOUR;
106
0
      return true;
107
0
    }
108
0
    return false;
109
0
  case 'm':
110
0
    {
111
0
      const nsAString& metric = Substring(aIter.get(), aEnd.get());
112
0
      if (metric.EqualsLiteral("min")) {
113
0
        aMultiplier = MSEC_PER_MIN;
114
0
        aIter = aEnd;
115
0
        return true;
116
0
      }
117
0
      if (metric.EqualsLiteral("ms")) {
118
0
        aMultiplier = 1;
119
0
        aIter = aEnd;
120
0
        return true;
121
0
      }
122
0
    }
123
0
    return false;
124
0
  case 's':
125
0
    if (++aIter == aEnd) {
126
0
      aMultiplier = MSEC_PER_SEC;
127
0
      return true;
128
0
    }
129
0
  }
130
0
  return false;
131
0
}
132
133
/**
134
 * See http://www.w3.org/TR/SVG/animate.html#ClockValueSyntax
135
 */
136
bool
137
ParseClockValue(RangedPtr<const char16_t>& aIter,
138
                const RangedPtr<const char16_t>& aEnd,
139
                nsSMILTimeValue* aResult)
140
0
{
141
0
  if (aIter == aEnd) {
142
0
    return false;
143
0
  }
144
0
145
0
  // TIMECOUNT_VALUE     ::= Timecount ("." Fraction)? (Metric)?
146
0
  // PARTIAL_CLOCK_VALUE ::= Minutes ":" Seconds ("." Fraction)?
147
0
  // FULL_CLOCK_VALUE    ::= Hours ":" Minutes ":" Seconds ("." Fraction)?
148
0
  enum ClockType {
149
0
    TIMECOUNT_VALUE,
150
0
    PARTIAL_CLOCK_VALUE,
151
0
    FULL_CLOCK_VALUE
152
0
  };
153
0
154
0
  int32_t clockType = TIMECOUNT_VALUE;
155
0
156
0
  RangedPtr<const char16_t> iter(aIter);
157
0
158
0
  // Determine which type of clock value we have by counting the number
159
0
  // of colons in the string.
160
0
  do {
161
0
    switch (*iter) {
162
0
    case ':':
163
0
       if (clockType == FULL_CLOCK_VALUE) {
164
0
         return false;
165
0
       }
166
0
       ++clockType;
167
0
       break;
168
0
    case 'e':
169
0
    case 'E':
170
0
    case '-':
171
0
    case '+':
172
0
      // Exclude anything invalid (for clock values)
173
0
      // that number parsing might otherwise allow.
174
0
      return false;
175
0
    }
176
0
    ++iter;
177
0
  } while (iter != aEnd);
178
0
179
0
  iter = aIter;
180
0
181
0
  int32_t hours = 0, timecount;
182
0
  double fraction = 0.0;
183
0
  uint32_t minutes, seconds, multiplier;
184
0
185
0
  switch (clockType) {
186
0
    case FULL_CLOCK_VALUE:
187
0
      if (!SVGContentUtils::ParseInteger(iter, aEnd, hours) ||
188
0
          !ParseColon(iter, aEnd)) {
189
0
        return false;
190
0
      }
191
0
      MOZ_FALLTHROUGH;
192
0
    case PARTIAL_CLOCK_VALUE:
193
0
      if (!ParseSecondsOrMinutes(iter, aEnd, minutes) ||
194
0
          !ParseColon(iter, aEnd) ||
195
0
          !ParseSecondsOrMinutes(iter, aEnd, seconds)) {
196
0
        return false;
197
0
      }
198
0
      if (iter != aEnd &&
199
0
          (*iter != '.' ||
200
0
           !SVGContentUtils::ParseNumber(iter, aEnd, fraction))) {
201
0
        return false;
202
0
      }
203
0
      aResult->SetMillis(nsSMILTime(hours) * MSEC_PER_HOUR +
204
0
                         minutes * MSEC_PER_MIN +
205
0
                         seconds * MSEC_PER_SEC +
206
0
                         NS_round(fraction * MSEC_PER_SEC));
207
0
      aIter = iter;
208
0
      return true;
209
0
    case TIMECOUNT_VALUE:
210
0
      if (!SVGContentUtils::ParseInteger(iter, aEnd, timecount)) {
211
0
        return false;
212
0
      }
213
0
      if (iter != aEnd && *iter == '.' &&
214
0
          !SVGContentUtils::ParseNumber(iter, aEnd, fraction)) {
215
0
        return false;
216
0
      }
217
0
      if (!ParseClockMetric(iter, aEnd, multiplier)) {
218
0
        return false;
219
0
      }
220
0
      aResult->SetMillis(nsSMILTime(timecount) * multiplier +
221
0
                         NS_round(fraction * multiplier));
222
0
      aIter = iter;
223
0
      return true;
224
0
  }
225
0
226
0
  return false;
227
0
}
228
229
bool
230
ParseOffsetValue(RangedPtr<const char16_t>& aIter,
231
                 const RangedPtr<const char16_t>& aEnd,
232
                 nsSMILTimeValue* aResult)
233
0
{
234
0
  RangedPtr<const char16_t> iter(aIter);
235
0
236
0
  int32_t sign;
237
0
  if (!SVGContentUtils::ParseOptionalSign(iter, aEnd, sign) ||
238
0
      !SkipWhitespace(iter, aEnd) ||
239
0
      !ParseClockValue(iter, aEnd, aResult)) {
240
0
    return false;
241
0
  }
242
0
  if (sign == -1) {
243
0
    aResult->SetMillis(-aResult->GetMillis());
244
0
  }
245
0
  aIter = iter;
246
0
  return true;
247
0
}
248
249
bool
250
ParseOffsetValue(const nsAString& aSpec,
251
                 nsSMILTimeValue* aResult)
252
0
{
253
0
  RangedPtr<const char16_t> iter(SVGContentUtils::GetStartRangedPtr(aSpec));
254
0
  const RangedPtr<const char16_t> end(SVGContentUtils::GetEndRangedPtr(aSpec));
255
0
256
0
  return ParseOffsetValue(iter, end, aResult) && iter == end;
257
0
}
258
259
bool
260
ParseOptionalOffset(RangedPtr<const char16_t>& aIter,
261
                    const RangedPtr<const char16_t>& aEnd,
262
                    nsSMILTimeValue* aResult)
263
0
{
264
0
  if (aIter == aEnd) {
265
0
    aResult->SetMillis(0L);
266
0
    return true;
267
0
  }
268
0
269
0
  return SkipWhitespace(aIter, aEnd) &&
270
0
         ParseOffsetValue(aIter, aEnd, aResult);
271
0
}
272
273
void
274
MoveToNextToken(RangedPtr<const char16_t>& aIter,
275
                const RangedPtr<const char16_t>& aEnd,
276
                bool aBreakOnDot,
277
                bool& aIsAnyCharEscaped)
278
0
{
279
0
  aIsAnyCharEscaped = false;
280
0
281
0
  bool isCurrentCharEscaped = false;
282
0
283
0
  while (aIter != aEnd && !nsContentUtils::IsHTMLWhitespace(*aIter)) {
284
0
    if (isCurrentCharEscaped) {
285
0
      isCurrentCharEscaped = false;
286
0
    } else {
287
0
      if (*aIter == '+' || *aIter == '-' ||
288
0
          (aBreakOnDot && *aIter == '.')) {
289
0
        break;
290
0
      }
291
0
      if (*aIter == '\\') {
292
0
        isCurrentCharEscaped = true;
293
0
        aIsAnyCharEscaped = true;
294
0
      }
295
0
    }
296
0
    ++aIter;
297
0
  }
298
0
}
299
300
already_AddRefed<nsAtom>
301
ConvertUnescapedTokenToAtom(const nsAString& aToken)
302
0
{
303
0
  // Whether the token is an id-ref or event-symbol it should be a valid NCName
304
0
  if (aToken.IsEmpty() || NS_FAILED(nsContentUtils::CheckQName(aToken, false)))
305
0
    return nullptr;
306
0
  return NS_Atomize(aToken);
307
0
}
308
309
already_AddRefed<nsAtom>
310
ConvertTokenToAtom(const nsAString& aToken,
311
                   bool aUnescapeToken)
312
0
{
313
0
  // Unescaping involves making a copy of the string which we'd like to avoid if possible
314
0
  if (!aUnescapeToken) {
315
0
    return ConvertUnescapedTokenToAtom(aToken);
316
0
  }
317
0
318
0
  nsAutoString token(aToken);
319
0
320
0
  const char16_t* read = token.BeginReading();
321
0
  const char16_t* const end = token.EndReading();
322
0
  char16_t* write = token.BeginWriting();
323
0
  bool escape = false;
324
0
325
0
  while (read != end) {
326
0
    MOZ_ASSERT(write <= read, "Writing past where we've read");
327
0
    if (!escape && *read == '\\') {
328
0
      escape = true;
329
0
      ++read;
330
0
    } else {
331
0
      *write++ = *read++;
332
0
      escape = false;
333
0
    }
334
0
  }
335
0
  token.Truncate(write - token.BeginReading());
336
0
337
0
  return ConvertUnescapedTokenToAtom(token);
338
0
}
339
340
bool
341
ParseElementBaseTimeValueSpec(const nsAString& aSpec,
342
                              nsSMILTimeValueSpecParams& aResult)
343
0
{
344
0
  nsSMILTimeValueSpecParams result;
345
0
346
0
  //
347
0
  // The spec will probably look something like one of these
348
0
  //
349
0
  // element-name.begin
350
0
  // element-name.event-name
351
0
  // event-name
352
0
  // element-name.repeat(3)
353
0
  // event\.name
354
0
  //
355
0
  // Technically `repeat(3)' is permitted but the behaviour in this case is not
356
0
  // defined (for SMIL Animation) so we don't support it here.
357
0
  //
358
0
359
0
  RangedPtr<const char16_t> start(SVGContentUtils::GetStartRangedPtr(aSpec));
360
0
  RangedPtr<const char16_t> end(SVGContentUtils::GetEndRangedPtr(aSpec));
361
0
362
0
  if (start == end) {
363
0
    return false;
364
0
  }
365
0
366
0
  RangedPtr<const char16_t> tokenEnd(start);
367
0
368
0
  bool requiresUnescaping;
369
0
  MoveToNextToken(tokenEnd, end, true, requiresUnescaping);
370
0
371
0
  RefPtr<nsAtom> atom =
372
0
    ConvertTokenToAtom(Substring(start.get(), tokenEnd.get()),
373
0
                       requiresUnescaping);
374
0
  if (atom == nullptr) {
375
0
    return false;
376
0
  }
377
0
378
0
  // Parse the second token if there is one
379
0
  if (tokenEnd != end && *tokenEnd == '.') {
380
0
    result.mDependentElemID = atom;
381
0
382
0
    ++tokenEnd;
383
0
    start = tokenEnd;
384
0
    MoveToNextToken(tokenEnd, end, false, requiresUnescaping);
385
0
386
0
    const nsAString& token2 = Substring(start.get(), tokenEnd.get());
387
0
388
0
    // element-name.begin
389
0
    if (token2.EqualsLiteral("begin")) {
390
0
      result.mType = nsSMILTimeValueSpecParams::SYNCBASE;
391
0
      result.mSyncBegin = true;
392
0
    // element-name.end
393
0
    } else if (token2.EqualsLiteral("end")) {
394
0
      result.mType = nsSMILTimeValueSpecParams::SYNCBASE;
395
0
      result.mSyncBegin = false;
396
0
    // element-name.repeat(digit+)
397
0
    } else if (StringBeginsWith(token2, REPEAT_PREFIX)) {
398
0
      start += REPEAT_PREFIX.Length();
399
0
      int32_t repeatValue;
400
0
      if (start == tokenEnd || *start == '+' || *start == '-' ||
401
0
          !SVGContentUtils::ParseInteger(start, tokenEnd, repeatValue)) {
402
0
        return false;
403
0
      }
404
0
      if (start == tokenEnd || *start != ')') {
405
0
        return false;
406
0
      }
407
0
      result.mType = nsSMILTimeValueSpecParams::REPEAT;
408
0
      result.mRepeatIteration = repeatValue;
409
0
    // element-name.event-symbol
410
0
    } else {
411
0
      atom = ConvertTokenToAtom(token2, requiresUnescaping);
412
0
      if (atom == nullptr) {
413
0
        return false;
414
0
      }
415
0
      result.mType = nsSMILTimeValueSpecParams::EVENT;
416
0
      result.mEventSymbol = atom;
417
0
    }
418
0
  } else {
419
0
    // event-symbol
420
0
    result.mType = nsSMILTimeValueSpecParams::EVENT;
421
0
    result.mEventSymbol = atom;
422
0
  }
423
0
424
0
  // We've reached the end of the token, so we should now be either looking at
425
0
  // a '+', '-' (possibly with whitespace before it), or the end.
426
0
  if (!ParseOptionalOffset(tokenEnd, end, &result.mOffset) || tokenEnd != end) {
427
0
    return false;
428
0
  }
429
0
  aResult = result;
430
0
  return true;
431
0
}
432
433
} // namespace
434
435
//------------------------------------------------------------------------------
436
// Implementation
437
438
const nsDependentSubstring
439
nsSMILParserUtils::TrimWhitespace(const nsAString& aString)
440
0
{
441
0
  nsAString::const_iterator start, end;
442
0
443
0
  aString.BeginReading(start);
444
0
  aString.EndReading(end);
445
0
446
0
  // Skip whitespace characters at the beginning
447
0
  while (start != end && nsContentUtils::IsHTMLWhitespace(*start)) {
448
0
    ++start;
449
0
  }
450
0
451
0
  // Skip whitespace characters at the end.
452
0
  while (end != start) {
453
0
    --end;
454
0
455
0
    if (!nsContentUtils::IsHTMLWhitespace(*end)) {
456
0
      // Step back to the last non-whitespace character.
457
0
      ++end;
458
0
459
0
      break;
460
0
    }
461
0
  }
462
0
463
0
  return Substring(start, end);
464
0
}
465
466
bool
467
nsSMILParserUtils::ParseKeySplines(const nsAString& aSpec,
468
                                   FallibleTArray<nsSMILKeySpline>& aKeySplines)
469
0
{
470
0
  nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace>
471
0
    controlPointTokenizer(aSpec, ';');
472
0
  while (controlPointTokenizer.hasMoreTokens()) {
473
0
474
0
    nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace>
475
0
      tokenizer(controlPointTokenizer.nextToken(), ',',
476
0
                nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
477
0
478
0
    double values[4];
479
0
    for (int i = 0 ; i < 4; i++) {
480
0
      if (!tokenizer.hasMoreTokens() ||
481
0
          !SVGContentUtils::ParseNumber(tokenizer.nextToken(), values[i]) ||
482
0
          values[i] > 1.0 || values[i] < 0.0) {
483
0
        return false;
484
0
      }
485
0
    }
486
0
    if (tokenizer.hasMoreTokens() ||
487
0
        tokenizer.separatorAfterCurrentToken() ||
488
0
        !aKeySplines.AppendElement(nsSMILKeySpline(values[0],
489
0
                                                   values[1],
490
0
                                                   values[2],
491
0
                                                   values[3]),
492
0
                                   fallible)) {
493
0
      return false;
494
0
    }
495
0
  }
496
0
497
0
  return !aKeySplines.IsEmpty();
498
0
}
499
500
bool
501
nsSMILParserUtils::ParseSemicolonDelimitedProgressList(const nsAString& aSpec,
502
                                                       bool aNonDecreasing,
503
                                                       FallibleTArray<double>& aArray)
504
0
{
505
0
  nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace>
506
0
    tokenizer(aSpec, ';');
507
0
508
0
  double previousValue = -1.0;
509
0
510
0
  while (tokenizer.hasMoreTokens()) {
511
0
    double value;
512
0
    if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), value)) {
513
0
      return false;
514
0
    }
515
0
516
0
    if (value > 1.0 || value < 0.0 ||
517
0
        (aNonDecreasing && value < previousValue)) {
518
0
      return false;
519
0
    }
520
0
521
0
    if (!aArray.AppendElement(value, fallible)) {
522
0
      return false;
523
0
    }
524
0
    previousValue = value;
525
0
  }
526
0
527
0
  return !aArray.IsEmpty();
528
0
}
529
530
// Helper class for ParseValues
531
class MOZ_STACK_CLASS SMILValueParser :
532
  public nsSMILParserUtils::GenericValueParser
533
{
534
public:
535
  SMILValueParser(const SVGAnimationElement* aSrcElement,
536
                  const nsISMILAttr* aSMILAttr,
537
                  FallibleTArray<nsSMILValue>* aValuesArray,
538
                  bool* aPreventCachingOfSandwich) :
539
    mSrcElement(aSrcElement),
540
    mSMILAttr(aSMILAttr),
541
    mValuesArray(aValuesArray),
542
    mPreventCachingOfSandwich(aPreventCachingOfSandwich)
543
0
  {}
544
545
0
  virtual bool Parse(const nsAString& aValueStr) override {
546
0
    nsSMILValue newValue;
547
0
    bool tmpPreventCachingOfSandwich = false;
548
0
    if (NS_FAILED(mSMILAttr->ValueFromString(aValueStr, mSrcElement, newValue,
549
0
                                             tmpPreventCachingOfSandwich)))
550
0
      return false;
551
0
552
0
    if (!mValuesArray->AppendElement(newValue, fallible)) {
553
0
      return false;
554
0
    }
555
0
    if (tmpPreventCachingOfSandwich) {
556
0
      *mPreventCachingOfSandwich = true;
557
0
    }
558
0
    return true;
559
0
  }
560
protected:
561
  const SVGAnimationElement* mSrcElement;
562
  const nsISMILAttr* mSMILAttr;
563
  FallibleTArray<nsSMILValue>* mValuesArray;
564
  bool* mPreventCachingOfSandwich;
565
};
566
567
bool
568
nsSMILParserUtils::ParseValues(const nsAString& aSpec,
569
                               const SVGAnimationElement* aSrcElement,
570
                               const nsISMILAttr& aAttribute,
571
                               FallibleTArray<nsSMILValue>& aValuesArray,
572
                               bool& aPreventCachingOfSandwich)
573
0
{
574
0
  // Assume all results can be cached, until we find one that can't.
575
0
  aPreventCachingOfSandwich = false;
576
0
  SMILValueParser valueParser(aSrcElement, &aAttribute,
577
0
                              &aValuesArray, &aPreventCachingOfSandwich);
578
0
  return ParseValuesGeneric(aSpec, valueParser);
579
0
}
580
581
bool
582
nsSMILParserUtils::ParseValuesGeneric(const nsAString& aSpec,
583
                                      GenericValueParser& aParser)
584
0
{
585
0
  nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace>
586
0
    tokenizer(aSpec, ';');
587
0
  if (!tokenizer.hasMoreTokens()) { // Empty list
588
0
    return false;
589
0
  }
590
0
591
0
  while (tokenizer.hasMoreTokens()) {
592
0
    if (!aParser.Parse(tokenizer.nextToken())) {
593
0
      return false;
594
0
    }
595
0
  }
596
0
597
0
  return true;
598
0
}
599
600
bool
601
nsSMILParserUtils::ParseRepeatCount(const nsAString& aSpec,
602
                                    nsSMILRepeatCount& aResult)
603
0
{
604
0
  const nsAString& spec =
605
0
    nsSMILParserUtils::TrimWhitespace(aSpec);
606
0
607
0
  if (spec.EqualsLiteral("indefinite")) {
608
0
    aResult.SetIndefinite();
609
0
    return true;
610
0
  }
611
0
612
0
  double value;
613
0
  if (!SVGContentUtils::ParseNumber(spec, value) || value <= 0.0) {
614
0
    return false;
615
0
  }
616
0
  aResult = value;
617
0
  return true;
618
0
}
619
620
bool
621
nsSMILParserUtils::ParseTimeValueSpecParams(const nsAString& aSpec,
622
                                            nsSMILTimeValueSpecParams& aResult)
623
0
{
624
0
  const nsAString& spec = TrimWhitespace(aSpec);
625
0
626
0
  if (spec.EqualsLiteral("indefinite")) {
627
0
     aResult.mType = nsSMILTimeValueSpecParams::INDEFINITE;
628
0
     return true;
629
0
  }
630
0
631
0
  // offset type
632
0
  if (ParseOffsetValue(spec, &aResult.mOffset)) {
633
0
    aResult.mType = nsSMILTimeValueSpecParams::OFFSET;
634
0
    return true;
635
0
  }
636
0
637
0
  // wallclock type
638
0
  if (StringBeginsWith(spec, WALLCLOCK_PREFIX)) {
639
0
    return false; // Wallclock times not implemented
640
0
  }
641
0
642
0
  // accesskey type
643
0
  if (StringBeginsWith(spec, ACCESSKEY_PREFIX_LC) ||
644
0
      StringBeginsWith(spec, ACCESSKEY_PREFIX_CC)) {
645
0
    return false; // accesskey is not supported
646
0
  }
647
0
648
0
  // event, syncbase, or repeat
649
0
  return ParseElementBaseTimeValueSpec(spec, aResult);
650
0
}
651
652
bool
653
nsSMILParserUtils::ParseClockValue(const nsAString& aSpec,
654
                                   nsSMILTimeValue* aResult)
655
0
{
656
0
  RangedPtr<const char16_t> iter(SVGContentUtils::GetStartRangedPtr(aSpec));
657
0
  RangedPtr<const char16_t> end(SVGContentUtils::GetEndRangedPtr(aSpec));
658
0
659
0
  return ::ParseClockValue(iter, end, aResult) && iter == end;
660
0
}
661
662
int32_t
663
nsSMILParserUtils::CheckForNegativeNumber(const nsAString& aStr)
664
0
{
665
0
  int32_t absValLocation = -1;
666
0
667
0
  RangedPtr<const char16_t> start(SVGContentUtils::GetStartRangedPtr(aStr));
668
0
  RangedPtr<const char16_t> iter = start;
669
0
  RangedPtr<const char16_t> end(SVGContentUtils::GetEndRangedPtr(aStr));
670
0
671
0
  // Skip initial whitespace
672
0
  while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
673
0
    ++iter;
674
0
  }
675
0
676
0
  // Check for dash
677
0
  if (iter != end && *iter == '-') {
678
0
    ++iter;
679
0
    // Check for numeric character
680
0
    if (iter != end && mozilla::IsAsciiDigit(*iter)) {
681
0
      absValLocation = iter - start;
682
0
    }
683
0
  }
684
0
  return absValLocation;
685
0
}