mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 06:05:44 +00:00
cd6c2e6bff
MozReview-Commit-ID: 30BOUA8QPh8 --HG-- extra : rebase_source : 21af58ad6f27f47404affe2f87fb9a426f01b496
170 lines
4.5 KiB
C++
170 lines
4.5 KiB
C++
/* -*- 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
|