diff options
Diffstat (limited to 'dom/animation/KeyframeEffectParams.cpp')
-rw-r--r-- | dom/animation/KeyframeEffectParams.cpp | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/dom/animation/KeyframeEffectParams.cpp b/dom/animation/KeyframeEffectParams.cpp new file mode 100644 index 000000000..257640691 --- /dev/null +++ b/dom/animation/KeyframeEffectParams.cpp @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/KeyframeEffectParams.h" + +#include "mozilla/AnimationUtils.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/KeyframeUtils.h" +#include "mozilla/RangedPtr.h" +#include "nsReadableUtils.h" + +namespace mozilla { + +static inline bool +IsLetter(char16_t aCh) +{ + return (0x41 <= aCh && aCh <= 0x5A) || (0x61 <= aCh && aCh <= 0x7A); +} + +static inline bool +IsDigit(char16_t aCh) +{ + return 0x30 <= aCh && aCh <= 0x39; +} + +static inline bool +IsNameStartCode(char16_t aCh) +{ + return IsLetter(aCh) || aCh >= 0x80 || aCh == '_'; +} + +static inline bool +IsNameCode(char16_t aCh) +{ + return IsNameStartCode(aCh) || IsDigit(aCh) || aCh == '-'; +} + +static inline bool +IsNewLine(char16_t aCh) +{ + // 0x0A (LF), 0x0C (FF), 0x0D (CR), or pairs of CR followed by LF are + // replaced by LF. + return aCh == 0x0A || aCh == 0x0C || aCh == 0x0D; +} + +static inline bool +IsValidEscape(char16_t aFirst, char16_t aSecond) +{ + return aFirst == '\\' && !IsNewLine(aSecond); +} + +static bool +IsIdentStart(RangedPtr<const char16_t> aIter, + const char16_t* const aEnd) +{ + if (aIter == aEnd) { + return false; + } + + if (*aIter == '-') { + if (aIter + 1 == aEnd) { + return false; + } + char16_t second = *(aIter + 1); + return IsNameStartCode(second) || + second == '-' || + (aIter + 2 != aEnd && IsValidEscape(second, *(aIter + 2))); + } + return IsNameStartCode(*aIter) || + (aIter + 1 != aEnd && IsValidEscape(*aIter, *(aIter + 1))); +} + +static void +ConsumeIdentToken(RangedPtr<const char16_t>& aIter, + const char16_t* const aEnd, + nsAString& aResult) +{ + aResult.Truncate(); + + // Check if it starts with an identifier. + if (!IsIdentStart(aIter, aEnd)) { + return; + } + + // Start to consume. + while (aIter != aEnd) { + if (IsNameCode(*aIter)) { + aResult.Append(*aIter); + } else if (*aIter == '\\') { + const RangedPtr<const char16_t> secondChar = aIter + 1; + if (secondChar == aEnd || !IsValidEscape(*aIter, *secondChar)) { + break; + } + // Consume '\\' and append the character following this '\\'. + ++aIter; + aResult.Append(*aIter); + } else { + break; + } + ++aIter; + } +} + +/* static */ void +KeyframeEffectParams::ParseSpacing(const nsAString& aSpacing, + SpacingMode& aSpacingMode, + nsCSSPropertyID& aPacedProperty, + nsAString& aInvalidPacedProperty, + ErrorResult& aRv) +{ + aInvalidPacedProperty.Truncate(); + + // Ignore spacing if the core API is not enabled since it is not yet ready to + // ship. + if (!AnimationUtils::IsCoreAPIEnabledForCaller()) { + aSpacingMode = SpacingMode::distribute; + return; + } + + // Parse spacing. + // distribute | paced({ident}) + // https://w3c.github.io/web-animations/#dom-keyframeeffectreadonly-spacing + // 1. distribute spacing. + if (aSpacing.EqualsLiteral("distribute")) { + aSpacingMode = SpacingMode::distribute; + return; + } + + // 2. paced spacing. + static const nsLiteralString kPacedPrefix = NS_LITERAL_STRING("paced("); + if (!StringBeginsWith(aSpacing, kPacedPrefix)) { + aRv.ThrowTypeError<dom::MSG_INVALID_SPACING_MODE_ERROR>(aSpacing); + return; + } + + RangedPtr<const char16_t> iter(aSpacing.Data() + kPacedPrefix.Length(), + aSpacing.Data(), aSpacing.Length()); + const char16_t* const end = aSpacing.EndReading(); + + nsAutoString identToken; + ConsumeIdentToken(iter, end, identToken); + if (identToken.IsEmpty()) { + aRv.ThrowTypeError<dom::MSG_INVALID_SPACING_MODE_ERROR>(aSpacing); + return; + } + + aPacedProperty = + nsCSSProps::LookupProperty(identToken, CSSEnabledState::eForAllContent); + if (aPacedProperty == eCSSProperty_UNKNOWN || + aPacedProperty == eCSSPropertyExtra_variable || + !KeyframeUtils::IsAnimatableProperty(aPacedProperty)) { + aPacedProperty = eCSSProperty_UNKNOWN; + aInvalidPacedProperty = identToken; + } + + if (end - iter.get() != 1 || *iter != ')') { + aRv.ThrowTypeError<dom::MSG_INVALID_SPACING_MODE_ERROR>(aSpacing); + return; + } + + aSpacingMode = aPacedProperty == eCSSProperty_UNKNOWN + ? SpacingMode::distribute + : SpacingMode::paced; +} + +} // namespace mozilla |