Bug 1244590 - Part 3: Parse spacing.
We only support spacing mode from Web Animations API, so add a simple parser and
use it only from the API. In this patch, parse spacing mode from the constructor
and throw TypeError if we don't support this CSS Property. (e.g. We can not find
a valid property or any parsing error.)
MozReview-Commit-ID: 9H7g80IcZfZ
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -726,19 +726,20 @@ template <class OptionsType>
static KeyframeEffectParams
KeyframeEffectParamsFromUnion(const OptionsType& aOptions,
ErrorResult& aRv)
{
KeyframeEffectParams result;
if (!aOptions.IsUnrestrictedDouble()) {
const KeyframeEffectOptions& options =
KeyframeEffectOptionsFromUnion(aOptions);
- // TODO: If the grammar of spacing is not correct, we should throw a
- // TypeError, aRv.Throw(NS_ERROR_TYPE_ERR). Parse spacing string and
- // handle TypeError in the next patch.
+ KeyframeEffectParams::ParseSpacing(options.mSpacing,
+ result.mSpacingMode,
+ result.mPacedProperty,
+ aRv);
}
return result;
}
static Maybe<OwningAnimationTarget>
ConvertTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget)
{
// Return value optimization.
@@ -779,16 +780,20 @@ KeyframeEffectReadOnly::ConstructKeyfram
TimingParams timingParams =
TimingParams::FromOptionsUnion(aOptions, doc, aRv);
if (aRv.Failed()) {
return nullptr;
}
KeyframeEffectParams effectOptions =
KeyframeEffectParamsFromUnion(aOptions, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
Maybe<OwningAnimationTarget> target = ConvertTarget(aTarget);
RefPtr<KeyframeEffectType> effect =
new KeyframeEffectType(doc, target, timingParams, effectOptions);
effect->SetKeyframes(aGlobal.Context(), aKeyframes, aRv);
if (aRv.Failed()) {
return nullptr;
}
new file mode 100644
--- /dev/null
+++ b/dom/animation/KeyframeEffectParams.cpp
@@ -0,0 +1,110 @@
+/* -*- 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/RangedPtr.h"
+
+namespace mozilla {
+
+#define PACED_PREFIX NS_LITERAL_STRING("paced")
+
+static const nsDependentSubstring
+TrimWhitespace(const nsAString& aString)
+{
+ RangedPtr<const char16_t> start(aString.Data(), aString.Length());
+ RangedPtr<const char16_t> end(aString.Data() + aString.Length(),
+ aString.Data(), aString.Length());
+
+ // Skip whitespace characters at the beginning
+ while (start != end && *start == ' ') {
+ ++start;
+ }
+
+ // Skip whitespace characters at the end.
+ while (end != start) {
+ --end;
+ if (*end != ' ') {
+ // Step back to the last non-whitespace character.
+ ++end;
+ break;
+ }
+ }
+
+ return Substring(start.get(), end.get());
+}
+
+/* static */ void
+KeyframeEffectParams::ParseSpacing(const nsAString& aSpacing,
+ SpacingMode& aSpacingMode,
+ nsCSSProperty& aPacedProperty,
+ ErrorResult& aRv)
+{
+ const nsAString& spacing = TrimWhitespace(aSpacing);
+ if (spacing.IsEmpty()) {
+ // Throw the original string to let the user know white spaces or
+ // empty string is not acceptable.
+ aRv.ThrowTypeError<dom::MSG_INVALID_SPACING_MODE_ERROR>(aSpacing);
+ }
+
+ // 1. distribute spacing.
+ if (spacing.EqualsLiteral("distribute")) {
+ aSpacingMode = SpacingMode::distribute;
+ return;
+ }
+
+ // 2. paced spacing, paced(css-property).
+ // e.g. "paced(margin-left)" is accepted,
+ // "paced(marginLeft)" is not accepted.
+ // "paced()" is not accepted.
+ // "paced( margin-left)" is not accepted.
+ if (!StringBeginsWith(spacing, PACED_PREFIX)) {
+ aRv.ThrowTypeError<dom::MSG_INVALID_SPACING_MODE_ERROR>(spacing);
+ return;
+ }
+
+ RangedPtr<const char16_t> iter(spacing.Data() + PACED_PREFIX.Length(),
+ spacing.Data(), spacing.Length());
+ RangedPtr<const char16_t> end(spacing.Data() + spacing.Length(),
+ spacing.Data(), spacing.Length());
+
+ if (*iter++ != '(') {
+ aRv.ThrowTypeError<dom::MSG_INVALID_SPACING_MODE_ERROR>(spacing);
+ return;
+ }
+
+ RangedPtr<const char16_t> tokenEnd(iter);
+ while (tokenEnd != end && *tokenEnd != ')') {
+ ++tokenEnd;
+ }
+
+ if (tokenEnd == end) {
+ aRv.ThrowTypeError<dom::MSG_INVALID_SPACING_MODE_ERROR>(spacing);
+ return;
+ }
+
+ const nsAString& token = Substring(iter.get(), tokenEnd.get());
+ aPacedProperty =
+ nsCSSProps::LookupProperty(token, CSSEnabledState::eForAllContent);
+ if (aPacedProperty == eCSSProperty_UNKNOWN) {
+ aRv.ThrowTypeError<dom::MSG_INVALID_SPACING_MODE_ERROR>(spacing);
+ return;
+ }
+
+ iter += token.Length();
+
+ // Skip check ')' because we did already.
+ iter += 1;
+
+ if (iter != end) {
+ aRv.ThrowTypeError<dom::MSG_INVALID_SPACING_MODE_ERROR>(spacing);
+ return;
+ }
+
+ aSpacingMode = SpacingMode::paced;
+}
+
+} // namespace mozilla
--- a/dom/animation/KeyframeEffectParams.h
+++ b/dom/animation/KeyframeEffectParams.h
@@ -26,16 +26,21 @@ struct KeyframeEffectParams
aSpacing.AssignLiteral("distribute");
} else {
aSpacing.AssignLiteral("paced(");
aSpacing.AppendASCII(nsCSSProps::GetStringValue(mPacedProperty).get());
aSpacing.AppendLiteral(")");
}
}
+ static void ParseSpacing(const nsAString& aSpacing,
+ SpacingMode& aSpacingMode,
+ nsCSSProperty& aPacedProperty,
+ ErrorResult& aRv);
+
// FIXME: Bug 1216843: Add IterationCompositeOperations and
// Bug 1216844: Add CompositeOperation
SpacingMode mSpacingMode = SpacingMode::distribute;
nsCSSProperty mPacedProperty = eCSSProperty_UNKNOWN;
};
} // namespace mozilla
--- a/dom/animation/moz.build
+++ b/dom/animation/moz.build
@@ -45,16 +45,17 @@ UNIFIED_SOURCES += [
'AnimationUtils.cpp',
'AnimValuesStyleRule.cpp',
'ComputedTimingFunction.cpp',
'CSSPseudoElement.cpp',
'DocumentTimeline.cpp',
'EffectCompositor.cpp',
'EffectSet.cpp',
'KeyframeEffect.cpp',
+ 'KeyframeEffectParams.cpp',
'KeyframeUtils.cpp',
'PendingAnimationTracker.cpp',
'TimingParams.cpp',
]
LOCAL_INCLUDES += [
'/dom/base',
'/layout/base',
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -90,11 +90,12 @@ MSG_DEF(MSG_PROMISE_ARG_NOT_ITERABLE, 1,
MSG_DEF(MSG_IS_NOT_PROMISE, 1, JSEXN_TYPEERR, "{0} is not a Promise")
MSG_DEF(MSG_SW_INSTALL_ERROR, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} encountered an error during installation.")
MSG_DEF(MSG_SW_SCRIPT_THREW, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} threw an exception during script evaluation.")
MSG_DEF(MSG_TYPEDARRAY_IS_SHARED, 1, JSEXN_TYPEERR, "{0} can't be a typed array on SharedArrayBuffer")
MSG_DEF(MSG_CACHE_ADD_FAILED_RESPONSE, 3, JSEXN_TYPEERR, "Cache got {0} response with bad status {1} while trying to add request {2}")
MSG_DEF(MSG_SW_UPDATE_BAD_REGISTRATION, 2, JSEXN_TYPEERR, "Failed to update the ServiceWorker for scope {0] because the registration has been {1} since the update was scheduled.")
MSG_DEF(MSG_INVALID_DURATION_ERROR, 1, JSEXN_TYPEERR, "Invalid duration '{0}'.")
MSG_DEF(MSG_INVALID_EASING_ERROR, 1, JSEXN_TYPEERR, "Invalid easing '{0}'.")
+MSG_DEF(MSG_INVALID_SPACING_MODE_ERROR, 1, JSEXN_TYPEERR, "Invalid spacing '{0}'.")
MSG_DEF(MSG_USELESS_SETTIMEOUT, 1, JSEXN_TYPEERR, "Useless {0} call (missing quotes around argument?)")
MSG_DEF(MSG_TOKENLIST_NO_SUPPORTED_TOKENS, 2, JSEXN_TYPEERR, "{0} attribute of <{1}> does not define any supported tokens")
MSG_DEF(MSG_CACHE_STREAM_CLOSED, 0, JSEXN_TYPEERR, "Response body is a cache file stream that has already been closed.")