Bug 1244590 - Part 3: Parse spacing. draft
authorBoris Chiou <boris.chiou@gmail.com>
Fri, 13 May 2016 16:38:25 +0800
changeset 369278 6d20f8be0711f430290457097b196a652ed5ba52
parent 369277 c158a2418b964c0695f5e837d9bb1ba55e88ecd3
child 369279 887010c3b05118e3effb8cd86193806f869c48e1
push id18826
push userbmo:boris.chiou@gmail.com
push dateSat, 21 May 2016 11:25:24 +0000
bugs1244590
milestone49.0a1
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
dom/animation/KeyframeEffect.cpp
dom/animation/KeyframeEffectParams.cpp
dom/animation/KeyframeEffectParams.h
dom/animation/moz.build
dom/bindings/Errors.msg
--- 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.")