dom/smil/nsSMILParserUtils.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial