LCOV - code coverage report
Current view: top level - dom/smil - nsSMILParserUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 326 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 24 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13