1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/smil/nsSMILParserUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,729 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsSMILParserUtils.h" 1.10 +#include "nsSMILKeySpline.h" 1.11 +#include "nsISMILAttr.h" 1.12 +#include "nsSMILValue.h" 1.13 +#include "nsSMILTimeValue.h" 1.14 +#include "nsSMILTimeValueSpecParams.h" 1.15 +#include "nsSMILTypes.h" 1.16 +#include "nsSMILRepeatCount.h" 1.17 +#include "nsContentUtils.h" 1.18 +#include "nsCharSeparatedTokenizer.h" 1.19 +#include "SVGContentUtils.h" 1.20 + 1.21 +using namespace mozilla; 1.22 +using namespace mozilla::dom; 1.23 +//------------------------------------------------------------------------------ 1.24 +// Helper functions and Constants 1.25 + 1.26 +namespace { 1.27 + 1.28 +const uint32_t MSEC_PER_SEC = 1000; 1.29 +const uint32_t MSEC_PER_MIN = 1000 * 60; 1.30 +const uint32_t MSEC_PER_HOUR = 1000 * 60 * 60; 1.31 + 1.32 +#define ACCESSKEY_PREFIX_LC NS_LITERAL_STRING("accesskey(") // SMIL2+ 1.33 +#define ACCESSKEY_PREFIX_CC NS_LITERAL_STRING("accessKey(") // SVG/SMIL ANIM 1.34 +#define REPEAT_PREFIX NS_LITERAL_STRING("repeat(") 1.35 +#define WALLCLOCK_PREFIX NS_LITERAL_STRING("wallclock(") 1.36 + 1.37 +inline bool 1.38 +SkipWhitespace(RangedPtr<const char16_t>& aIter, 1.39 + const RangedPtr<const char16_t>& aEnd) 1.40 +{ 1.41 + while (aIter != aEnd) { 1.42 + if (!IsSVGWhitespace(*aIter)) { 1.43 + return true; 1.44 + } 1.45 + ++aIter; 1.46 + } 1.47 + return false; 1.48 +} 1.49 + 1.50 +inline bool 1.51 +ParseColon(RangedPtr<const char16_t>& aIter, 1.52 + const RangedPtr<const char16_t>& aEnd) 1.53 +{ 1.54 + if (aIter == aEnd || *aIter != ':') { 1.55 + return false; 1.56 + } 1.57 + ++aIter; 1.58 + return true; 1.59 +} 1.60 + 1.61 +/* 1.62 + * Exactly two digits in the range 00 - 59 are expected. 1.63 + */ 1.64 +bool 1.65 +ParseSecondsOrMinutes(RangedPtr<const char16_t>& aIter, 1.66 + const RangedPtr<const char16_t>& aEnd, 1.67 + uint32_t& aValue) 1.68 +{ 1.69 + if (aIter == aEnd || !SVGContentUtils::IsDigit(*aIter)) { 1.70 + return false; 1.71 + } 1.72 + 1.73 + RangedPtr<const char16_t> iter(aIter); 1.74 + 1.75 + if (++iter == aEnd || !SVGContentUtils::IsDigit(*iter)) { 1.76 + return false; 1.77 + } 1.78 + 1.79 + uint32_t value = 10 * SVGContentUtils::DecimalDigitValue(*aIter) + 1.80 + SVGContentUtils::DecimalDigitValue(*iter); 1.81 + if (value > 59) { 1.82 + return false; 1.83 + } 1.84 + if (++iter != aEnd && SVGContentUtils::IsDigit(*iter)) { 1.85 + return false; 1.86 + } 1.87 + 1.88 + aValue = value; 1.89 + aIter = iter; 1.90 + return true; 1.91 +} 1.92 + 1.93 +inline bool 1.94 +ParseClockMetric(RangedPtr<const char16_t>& aIter, 1.95 + const RangedPtr<const char16_t>& aEnd, 1.96 + uint32_t& aMultiplier) 1.97 +{ 1.98 + if (aIter == aEnd) { 1.99 + aMultiplier = MSEC_PER_SEC; 1.100 + return true; 1.101 + } 1.102 + 1.103 + switch (*aIter) { 1.104 + case 'h': 1.105 + if (++aIter == aEnd) { 1.106 + aMultiplier = MSEC_PER_HOUR; 1.107 + return true; 1.108 + } 1.109 + return false; 1.110 + case 'm': 1.111 + { 1.112 + const nsAString& metric = Substring(aIter.get(), aEnd.get()); 1.113 + if (metric.EqualsLiteral("min")) { 1.114 + aMultiplier = MSEC_PER_MIN; 1.115 + aIter = aEnd; 1.116 + return true; 1.117 + } 1.118 + if (metric.EqualsLiteral("ms")) { 1.119 + aMultiplier = 1; 1.120 + aIter = aEnd; 1.121 + return true; 1.122 + } 1.123 + } 1.124 + return false; 1.125 + case 's': 1.126 + if (++aIter == aEnd) { 1.127 + aMultiplier = MSEC_PER_SEC; 1.128 + return true; 1.129 + } 1.130 + } 1.131 + return false; 1.132 +} 1.133 + 1.134 +/** 1.135 + * See http://www.w3.org/TR/SVG/animate.html#ClockValueSyntax 1.136 + */ 1.137 +bool 1.138 +ParseClockValue(RangedPtr<const char16_t>& aIter, 1.139 + const RangedPtr<const char16_t>& aEnd, 1.140 + nsSMILTimeValue* aResult) 1.141 +{ 1.142 + if (aIter == aEnd) { 1.143 + return false; 1.144 + } 1.145 + 1.146 + // TIMECOUNT_VALUE ::= Timecount ("." Fraction)? (Metric)? 1.147 + // PARTIAL_CLOCK_VALUE ::= Minutes ":" Seconds ("." Fraction)? 1.148 + // FULL_CLOCK_VALUE ::= Hours ":" Minutes ":" Seconds ("." Fraction)? 1.149 + enum ClockType { 1.150 + TIMECOUNT_VALUE, 1.151 + PARTIAL_CLOCK_VALUE, 1.152 + FULL_CLOCK_VALUE 1.153 + }; 1.154 + 1.155 + int32_t clockType = TIMECOUNT_VALUE; 1.156 + 1.157 + RangedPtr<const char16_t> iter(aIter); 1.158 + 1.159 + // Determine which type of clock value we have by counting the number 1.160 + // of colons in the string. 1.161 + do { 1.162 + switch (*iter) { 1.163 + case ':': 1.164 + if (clockType == FULL_CLOCK_VALUE) { 1.165 + return false; 1.166 + } 1.167 + ++clockType; 1.168 + break; 1.169 + case 'e': 1.170 + case 'E': 1.171 + case '-': 1.172 + case '+': 1.173 + // Exclude anything invalid (for clock values) 1.174 + // that number parsing might otherwise allow. 1.175 + return false; 1.176 + } 1.177 + ++iter; 1.178 + } while (iter != aEnd); 1.179 + 1.180 + iter = aIter; 1.181 + 1.182 + int32_t hours = 0, timecount; 1.183 + double fraction = 0.0; 1.184 + uint32_t minutes, seconds, multiplier; 1.185 + 1.186 + switch (clockType) { 1.187 + case FULL_CLOCK_VALUE: 1.188 + if (!SVGContentUtils::ParseInteger(iter, aEnd, hours) || 1.189 + !ParseColon(iter, aEnd)) { 1.190 + return false; 1.191 + } 1.192 + // intentional fall through 1.193 + case PARTIAL_CLOCK_VALUE: 1.194 + if (!ParseSecondsOrMinutes(iter, aEnd, minutes) || 1.195 + !ParseColon(iter, aEnd) || 1.196 + !ParseSecondsOrMinutes(iter, aEnd, seconds)) { 1.197 + return false; 1.198 + } 1.199 + if (iter != aEnd && 1.200 + (*iter != '.' || 1.201 + !SVGContentUtils::ParseNumber(iter, aEnd, fraction))) { 1.202 + return false; 1.203 + } 1.204 + aResult->SetMillis(nsSMILTime(hours) * MSEC_PER_HOUR + 1.205 + minutes * MSEC_PER_MIN + 1.206 + seconds * MSEC_PER_SEC + 1.207 + NS_round(fraction * MSEC_PER_SEC)); 1.208 + aIter = iter; 1.209 + return true; 1.210 + case TIMECOUNT_VALUE: 1.211 + if (!SVGContentUtils::ParseInteger(iter, aEnd, timecount)) { 1.212 + return false; 1.213 + } 1.214 + if (iter != aEnd && *iter == '.' && 1.215 + !SVGContentUtils::ParseNumber(iter, aEnd, fraction)) { 1.216 + return false; 1.217 + } 1.218 + if (!ParseClockMetric(iter, aEnd, multiplier)) { 1.219 + return false; 1.220 + } 1.221 + aResult->SetMillis(nsSMILTime(timecount) * multiplier + 1.222 + NS_round(fraction * multiplier)); 1.223 + aIter = iter; 1.224 + return true; 1.225 + } 1.226 + 1.227 + return false; 1.228 +} 1.229 + 1.230 +bool 1.231 +ParseOffsetValue(RangedPtr<const char16_t>& aIter, 1.232 + const RangedPtr<const char16_t>& aEnd, 1.233 + nsSMILTimeValue* aResult) 1.234 +{ 1.235 + RangedPtr<const char16_t> iter(aIter); 1.236 + 1.237 + int32_t sign; 1.238 + if (!SVGContentUtils::ParseOptionalSign(iter, aEnd, sign) || 1.239 + !SkipWhitespace(iter, aEnd) || 1.240 + !ParseClockValue(iter, aEnd, aResult)) { 1.241 + return false; 1.242 + } 1.243 + if (sign == -1) { 1.244 + aResult->SetMillis(-aResult->GetMillis()); 1.245 + } 1.246 + aIter = iter; 1.247 + return true; 1.248 +} 1.249 + 1.250 +bool 1.251 +ParseOffsetValue(const nsAString& aSpec, 1.252 + nsSMILTimeValue* aResult) 1.253 +{ 1.254 + RangedPtr<const char16_t> iter(SVGContentUtils::GetStartRangedPtr(aSpec)); 1.255 + const RangedPtr<const char16_t> end(SVGContentUtils::GetEndRangedPtr(aSpec)); 1.256 + 1.257 + return ParseOffsetValue(iter, end, aResult) && iter == end; 1.258 +} 1.259 + 1.260 +bool 1.261 +ParseOptionalOffset(RangedPtr<const char16_t>& aIter, 1.262 + const RangedPtr<const char16_t>& aEnd, 1.263 + nsSMILTimeValue* aResult) 1.264 +{ 1.265 + if (aIter == aEnd) { 1.266 + aResult->SetMillis(0L); 1.267 + return true; 1.268 + } 1.269 + 1.270 + return SkipWhitespace(aIter, aEnd) && 1.271 + ParseOffsetValue(aIter, aEnd, aResult); 1.272 +} 1.273 + 1.274 +bool 1.275 +ParseAccessKey(const nsAString& aSpec, nsSMILTimeValueSpecParams& aResult) 1.276 +{ 1.277 + NS_ABORT_IF_FALSE(StringBeginsWith(aSpec, ACCESSKEY_PREFIX_CC) || 1.278 + StringBeginsWith(aSpec, ACCESSKEY_PREFIX_LC), 1.279 + "Calling ParseAccessKey on non-accesskey-type spec"); 1.280 + 1.281 + nsSMILTimeValueSpecParams result; 1.282 + result.mType = nsSMILTimeValueSpecParams::ACCESSKEY; 1.283 + 1.284 + NS_ABORT_IF_FALSE( 1.285 + ACCESSKEY_PREFIX_LC.Length() == ACCESSKEY_PREFIX_CC.Length(), 1.286 + "Case variations for accesskey prefix differ in length"); 1.287 + 1.288 + RangedPtr<const char16_t> iter(SVGContentUtils::GetStartRangedPtr(aSpec)); 1.289 + RangedPtr<const char16_t> end(SVGContentUtils::GetEndRangedPtr(aSpec)); 1.290 + 1.291 + iter += ACCESSKEY_PREFIX_LC.Length(); 1.292 + 1.293 + // Expecting at least <accesskey> + ')' 1.294 + if (end - iter < 2) 1.295 + return false; 1.296 + 1.297 + uint32_t c = *iter++; 1.298 + 1.299 + // Process 32-bit codepoints 1.300 + if (NS_IS_HIGH_SURROGATE(c)) { 1.301 + if (end - iter < 2) // Expecting at least low-surrogate + ')' 1.302 + return false; 1.303 + uint32_t lo = *iter++; 1.304 + if (!NS_IS_LOW_SURROGATE(lo)) 1.305 + return false; 1.306 + c = SURROGATE_TO_UCS4(c, lo); 1.307 + // XML 1.1 says that 0xFFFE and 0xFFFF are not valid characters 1.308 + } else if (NS_IS_LOW_SURROGATE(c) || c == 0xFFFE || c == 0xFFFF) { 1.309 + return false; 1.310 + } 1.311 + 1.312 + result.mRepeatIterationOrAccessKey = c; 1.313 + 1.314 + if (*iter++ != ')') 1.315 + return false; 1.316 + 1.317 + if (!ParseOptionalOffset(iter, end, &result.mOffset) || iter != end) { 1.318 + return false; 1.319 + } 1.320 + aResult = result; 1.321 + return true; 1.322 +} 1.323 + 1.324 +void 1.325 +MoveToNextToken(RangedPtr<const char16_t>& aIter, 1.326 + const RangedPtr<const char16_t>& aEnd, 1.327 + bool aBreakOnDot, 1.328 + bool& aIsAnyCharEscaped) 1.329 +{ 1.330 + aIsAnyCharEscaped = false; 1.331 + 1.332 + bool isCurrentCharEscaped = false; 1.333 + 1.334 + while (aIter != aEnd && !IsSVGWhitespace(*aIter)) { 1.335 + if (isCurrentCharEscaped) { 1.336 + isCurrentCharEscaped = false; 1.337 + } else { 1.338 + if (*aIter == '+' || *aIter == '-' || 1.339 + (aBreakOnDot && *aIter == '.')) { 1.340 + break; 1.341 + } 1.342 + if (*aIter == '\\') { 1.343 + isCurrentCharEscaped = true; 1.344 + aIsAnyCharEscaped = true; 1.345 + } 1.346 + } 1.347 + ++aIter; 1.348 + } 1.349 +} 1.350 + 1.351 +already_AddRefed<nsIAtom> 1.352 +ConvertUnescapedTokenToAtom(const nsAString& aToken) 1.353 +{ 1.354 + // Whether the token is an id-ref or event-symbol it should be a valid NCName 1.355 + if (aToken.IsEmpty() || NS_FAILED(nsContentUtils::CheckQName(aToken, false))) 1.356 + return nullptr; 1.357 + return do_GetAtom(aToken); 1.358 +} 1.359 + 1.360 +already_AddRefed<nsIAtom> 1.361 +ConvertTokenToAtom(const nsAString& aToken, 1.362 + bool aUnescapeToken) 1.363 +{ 1.364 + // Unescaping involves making a copy of the string which we'd like to avoid if possible 1.365 + if (!aUnescapeToken) { 1.366 + return ConvertUnescapedTokenToAtom(aToken); 1.367 + } 1.368 + 1.369 + nsAutoString token(aToken); 1.370 + 1.371 + const char16_t* read = token.BeginReading(); 1.372 + const char16_t* const end = token.EndReading(); 1.373 + char16_t* write = token.BeginWriting(); 1.374 + bool escape = false; 1.375 + 1.376 + while (read != end) { 1.377 + NS_ABORT_IF_FALSE(write <= read, "Writing past where we've read"); 1.378 + if (!escape && *read == '\\') { 1.379 + escape = true; 1.380 + ++read; 1.381 + } else { 1.382 + *write++ = *read++; 1.383 + escape = false; 1.384 + } 1.385 + } 1.386 + token.Truncate(write - token.BeginReading()); 1.387 + 1.388 + return ConvertUnescapedTokenToAtom(token); 1.389 +} 1.390 + 1.391 +bool 1.392 +ParseElementBaseTimeValueSpec(const nsAString& aSpec, 1.393 + nsSMILTimeValueSpecParams& aResult) 1.394 +{ 1.395 + nsSMILTimeValueSpecParams result; 1.396 + 1.397 + // 1.398 + // The spec will probably look something like one of these 1.399 + // 1.400 + // element-name.begin 1.401 + // element-name.event-name 1.402 + // event-name 1.403 + // element-name.repeat(3) 1.404 + // event\.name 1.405 + // 1.406 + // Technically `repeat(3)' is permitted but the behaviour in this case is not 1.407 + // defined (for SMIL Animation) so we don't support it here. 1.408 + // 1.409 + 1.410 + RangedPtr<const char16_t> start(SVGContentUtils::GetStartRangedPtr(aSpec)); 1.411 + RangedPtr<const char16_t> end(SVGContentUtils::GetEndRangedPtr(aSpec)); 1.412 + 1.413 + if (start == end) { 1.414 + return false; 1.415 + } 1.416 + 1.417 + RangedPtr<const char16_t> tokenEnd(start); 1.418 + 1.419 + bool requiresUnescaping; 1.420 + MoveToNextToken(tokenEnd, end, true, requiresUnescaping); 1.421 + 1.422 + nsRefPtr<nsIAtom> atom = 1.423 + ConvertTokenToAtom(Substring(start.get(), tokenEnd.get()), 1.424 + requiresUnescaping); 1.425 + if (atom == nullptr) { 1.426 + return false; 1.427 + } 1.428 + 1.429 + // Parse the second token if there is one 1.430 + if (tokenEnd != end && *tokenEnd == '.') { 1.431 + result.mDependentElemID = atom; 1.432 + 1.433 + ++tokenEnd; 1.434 + start = tokenEnd; 1.435 + MoveToNextToken(tokenEnd, end, false, requiresUnescaping); 1.436 + 1.437 + const nsAString& token2 = Substring(start.get(), tokenEnd.get()); 1.438 + 1.439 + // element-name.begin 1.440 + if (token2.EqualsLiteral("begin")) { 1.441 + result.mType = nsSMILTimeValueSpecParams::SYNCBASE; 1.442 + result.mSyncBegin = true; 1.443 + // element-name.end 1.444 + } else if (token2.EqualsLiteral("end")) { 1.445 + result.mType = nsSMILTimeValueSpecParams::SYNCBASE; 1.446 + result.mSyncBegin = false; 1.447 + // element-name.repeat(digit+) 1.448 + } else if (StringBeginsWith(token2, REPEAT_PREFIX)) { 1.449 + start += REPEAT_PREFIX.Length(); 1.450 + int32_t repeatValue; 1.451 + if (start == tokenEnd || *start == '+' || *start == '-' || 1.452 + !SVGContentUtils::ParseInteger(start, tokenEnd, repeatValue)) { 1.453 + return false; 1.454 + } 1.455 + if (start == tokenEnd || *start != ')') { 1.456 + return false; 1.457 + } 1.458 + result.mType = nsSMILTimeValueSpecParams::REPEAT; 1.459 + result.mRepeatIterationOrAccessKey = repeatValue; 1.460 + // element-name.event-symbol 1.461 + } else { 1.462 + atom = ConvertTokenToAtom(token2, requiresUnescaping); 1.463 + if (atom == nullptr) { 1.464 + return false; 1.465 + } 1.466 + result.mType = nsSMILTimeValueSpecParams::EVENT; 1.467 + result.mEventSymbol = atom; 1.468 + } 1.469 + } else { 1.470 + // event-symbol 1.471 + result.mType = nsSMILTimeValueSpecParams::EVENT; 1.472 + result.mEventSymbol = atom; 1.473 + } 1.474 + 1.475 + // We've reached the end of the token, so we should now be either looking at 1.476 + // a '+', '-' (possibly with whitespace before it), or the end. 1.477 + if (!ParseOptionalOffset(tokenEnd, end, &result.mOffset) || tokenEnd != end) { 1.478 + return false; 1.479 + } 1.480 + aResult = result; 1.481 + return true; 1.482 +} 1.483 + 1.484 +} // end anonymous namespace block 1.485 + 1.486 +//------------------------------------------------------------------------------ 1.487 +// Implementation 1.488 + 1.489 +const nsDependentSubstring 1.490 +nsSMILParserUtils::TrimWhitespace(const nsAString& aString) 1.491 +{ 1.492 + nsAString::const_iterator start, end; 1.493 + 1.494 + aString.BeginReading(start); 1.495 + aString.EndReading(end); 1.496 + 1.497 + // Skip whitespace characters at the beginning 1.498 + while (start != end && IsSVGWhitespace(*start)) { 1.499 + ++start; 1.500 + } 1.501 + 1.502 + // Skip whitespace characters at the end. 1.503 + while (end != start) { 1.504 + --end; 1.505 + 1.506 + if (!IsSVGWhitespace(*end)) { 1.507 + // Step back to the last non-whitespace character. 1.508 + ++end; 1.509 + 1.510 + break; 1.511 + } 1.512 + } 1.513 + 1.514 + return Substring(start, end); 1.515 +} 1.516 + 1.517 +bool 1.518 +nsSMILParserUtils::ParseKeySplines(const nsAString& aSpec, 1.519 + FallibleTArray<nsSMILKeySpline>& aKeySplines) 1.520 +{ 1.521 + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> controlPointTokenizer(aSpec, ';'); 1.522 + while (controlPointTokenizer.hasMoreTokens()) { 1.523 + 1.524 + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> 1.525 + tokenizer(controlPointTokenizer.nextToken(), ',', 1.526 + nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); 1.527 + 1.528 + double values[4]; 1.529 + for (int i = 0 ; i < 4; i++) { 1.530 + if (!tokenizer.hasMoreTokens() || 1.531 + !SVGContentUtils::ParseNumber(tokenizer.nextToken(), values[i]) || 1.532 + values[i] > 1.0 || values[i] < 0.0) { 1.533 + return false; 1.534 + } 1.535 + } 1.536 + if (tokenizer.hasMoreTokens() || 1.537 + tokenizer.separatorAfterCurrentToken() || 1.538 + !aKeySplines.AppendElement(nsSMILKeySpline(values[0], 1.539 + values[1], 1.540 + values[2], 1.541 + values[3]))) { 1.542 + return false; 1.543 + } 1.544 + } 1.545 + 1.546 + return !aKeySplines.IsEmpty(); 1.547 +} 1.548 + 1.549 +bool 1.550 +nsSMILParserUtils::ParseSemicolonDelimitedProgressList(const nsAString& aSpec, 1.551 + bool aNonDecreasing, 1.552 + FallibleTArray<double>& aArray) 1.553 +{ 1.554 + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> tokenizer(aSpec, ';'); 1.555 + 1.556 + double previousValue = -1.0; 1.557 + 1.558 + while (tokenizer.hasMoreTokens()) { 1.559 + double value; 1.560 + if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), value)) { 1.561 + return false; 1.562 + } 1.563 + 1.564 + if (value > 1.0 || value < 0.0 || 1.565 + (aNonDecreasing && value < previousValue)) { 1.566 + return false; 1.567 + } 1.568 + 1.569 + if (!aArray.AppendElement(value)) { 1.570 + return false; 1.571 + } 1.572 + previousValue = value; 1.573 + } 1.574 + 1.575 + return !aArray.IsEmpty(); 1.576 +} 1.577 + 1.578 +// Helper class for ParseValues 1.579 +class MOZ_STACK_CLASS SMILValueParser : 1.580 + public nsSMILParserUtils::GenericValueParser 1.581 +{ 1.582 +public: 1.583 + SMILValueParser(const SVGAnimationElement* aSrcElement, 1.584 + const nsISMILAttr* aSMILAttr, 1.585 + FallibleTArray<nsSMILValue>* aValuesArray, 1.586 + bool* aPreventCachingOfSandwich) : 1.587 + mSrcElement(aSrcElement), 1.588 + mSMILAttr(aSMILAttr), 1.589 + mValuesArray(aValuesArray), 1.590 + mPreventCachingOfSandwich(aPreventCachingOfSandwich) 1.591 + {} 1.592 + 1.593 + virtual bool Parse(const nsAString& aValueStr) MOZ_OVERRIDE { 1.594 + nsSMILValue newValue; 1.595 + bool tmpPreventCachingOfSandwich = false; 1.596 + if (NS_FAILED(mSMILAttr->ValueFromString(aValueStr, mSrcElement, newValue, 1.597 + tmpPreventCachingOfSandwich))) 1.598 + return false; 1.599 + 1.600 + if (!mValuesArray->AppendElement(newValue)) { 1.601 + return false; 1.602 + } 1.603 + if (tmpPreventCachingOfSandwich) { 1.604 + *mPreventCachingOfSandwich = true; 1.605 + } 1.606 + return true; 1.607 + } 1.608 +protected: 1.609 + const SVGAnimationElement* mSrcElement; 1.610 + const nsISMILAttr* mSMILAttr; 1.611 + FallibleTArray<nsSMILValue>* mValuesArray; 1.612 + bool* mPreventCachingOfSandwich; 1.613 +}; 1.614 + 1.615 +bool 1.616 +nsSMILParserUtils::ParseValues(const nsAString& aSpec, 1.617 + const SVGAnimationElement* aSrcElement, 1.618 + const nsISMILAttr& aAttribute, 1.619 + FallibleTArray<nsSMILValue>& aValuesArray, 1.620 + bool& aPreventCachingOfSandwich) 1.621 +{ 1.622 + // Assume all results can be cached, until we find one that can't. 1.623 + aPreventCachingOfSandwich = false; 1.624 + SMILValueParser valueParser(aSrcElement, &aAttribute, 1.625 + &aValuesArray, &aPreventCachingOfSandwich); 1.626 + return ParseValuesGeneric(aSpec, valueParser); 1.627 +} 1.628 + 1.629 +bool 1.630 +nsSMILParserUtils::ParseValuesGeneric(const nsAString& aSpec, 1.631 + GenericValueParser& aParser) 1.632 +{ 1.633 + nsCharSeparatedTokenizerTemplate<IsSVGWhitespace> tokenizer(aSpec, ';'); 1.634 + if (!tokenizer.hasMoreTokens()) { // Empty list 1.635 + return false; 1.636 + } 1.637 + 1.638 + while (tokenizer.hasMoreTokens()) { 1.639 + if (!aParser.Parse(tokenizer.nextToken())) { 1.640 + return false; 1.641 + } 1.642 + } 1.643 + 1.644 + return true; 1.645 +} 1.646 + 1.647 +bool 1.648 +nsSMILParserUtils::ParseRepeatCount(const nsAString& aSpec, 1.649 + nsSMILRepeatCount& aResult) 1.650 +{ 1.651 + const nsAString& spec = 1.652 + nsSMILParserUtils::TrimWhitespace(aSpec); 1.653 + 1.654 + if (spec.EqualsLiteral("indefinite")) { 1.655 + aResult.SetIndefinite(); 1.656 + return true; 1.657 + } 1.658 + 1.659 + double value; 1.660 + if (!SVGContentUtils::ParseNumber(spec, value) || value <= 0.0) { 1.661 + return false; 1.662 + } 1.663 + aResult = value; 1.664 + return true; 1.665 +} 1.666 + 1.667 +bool 1.668 +nsSMILParserUtils::ParseTimeValueSpecParams(const nsAString& aSpec, 1.669 + nsSMILTimeValueSpecParams& aResult) 1.670 +{ 1.671 + const nsAString& spec = TrimWhitespace(aSpec); 1.672 + 1.673 + if (spec.EqualsLiteral("indefinite")) { 1.674 + aResult.mType = nsSMILTimeValueSpecParams::INDEFINITE; 1.675 + return true; 1.676 + } 1.677 + 1.678 + // offset type 1.679 + if (ParseOffsetValue(spec, &aResult.mOffset)) { 1.680 + aResult.mType = nsSMILTimeValueSpecParams::OFFSET; 1.681 + return true; 1.682 + } 1.683 + 1.684 + // wallclock type 1.685 + if (StringBeginsWith(spec, WALLCLOCK_PREFIX)) { 1.686 + return false; // Wallclock times not implemented 1.687 + } 1.688 + 1.689 + // accesskey type 1.690 + if (StringBeginsWith(spec, ACCESSKEY_PREFIX_LC) || 1.691 + StringBeginsWith(spec, ACCESSKEY_PREFIX_CC)) { 1.692 + return ParseAccessKey(spec, aResult); 1.693 + } 1.694 + 1.695 + // event, syncbase, or repeat 1.696 + return ParseElementBaseTimeValueSpec(spec, aResult); 1.697 +} 1.698 + 1.699 +bool 1.700 +nsSMILParserUtils::ParseClockValue(const nsAString& aSpec, 1.701 + nsSMILTimeValue* aResult) 1.702 +{ 1.703 + RangedPtr<const char16_t> iter(SVGContentUtils::GetStartRangedPtr(aSpec)); 1.704 + RangedPtr<const char16_t> end(SVGContentUtils::GetEndRangedPtr(aSpec)); 1.705 + 1.706 + return ::ParseClockValue(iter, end, aResult) && iter == end; 1.707 +} 1.708 + 1.709 +int32_t 1.710 +nsSMILParserUtils::CheckForNegativeNumber(const nsAString& aStr) 1.711 +{ 1.712 + int32_t absValLocation = -1; 1.713 + 1.714 + nsAString::const_iterator start, end; 1.715 + aStr.BeginReading(start); 1.716 + aStr.EndReading(end); 1.717 + 1.718 + // Skip initial whitespace 1.719 + while (start != end && IsSVGWhitespace(*start)) { 1.720 + ++start; 1.721 + } 1.722 + 1.723 + // Check for dash 1.724 + if (start != end && *start == '-') { 1.725 + ++start; 1.726 + // Check for numeric character 1.727 + if (start != end && SVGContentUtils::IsDigit(*start)) { 1.728 + absValLocation = start.get() - start.start(); 1.729 + } 1.730 + } 1.731 + return absValLocation; 1.732 +}