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.

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

mercurial