Bug 1273706 - Part 18: Add the |ParseTypedValue| method for parsing typed CSS values. r?dbaron draft
authorJonathan Chan <jyc@eqv.io>
Thu, 18 Aug 2016 15:30:37 -0700
changeset 402907 de545674b6682178fab8665247e343f0583bed99
parent 402906 100dd59be2f5f207dfdb81a0f0ebe1e87b0b1836
child 402908 a1674b9b7b4c3444b01cc8d28de1caae1c16d712
push id26775
push userjchan@mozilla.com
push dateThu, 18 Aug 2016 22:38:41 +0000
reviewersdbaron
bugs1273706
milestone51.0a1
Bug 1273706 - Part 18: Add the |ParseTypedValue| method for parsing typed CSS values. r?dbaron This is what we use for checking the types of custom property values in future patches in this series. MozReview-Commit-ID: K5TfovTP1M2
layout/style/nsCSSParser.cpp
layout/style/nsCSSParser.h
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -53,16 +53,17 @@
 #include "nsLayoutUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsRuleData.h"
 #include "mozilla/CSSVariableRegistrations.h"
 #include "mozilla/CSSVariableValues.h"
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h"
 #include "mozilla/dom/URL.h"
 #include "gfxFontFamilyList.h"
+#include "mozilla/CSSVariableSyntax.h"
 
 using namespace mozilla;
 using namespace mozilla::css;
 
 typedef nsCSSProps::KTableEntry KTableEntry;
 
 // pref-backed bool values (hooked up in nsCSSParser::Startup)
 static bool sOpentypeSVGEnabled;
@@ -270,16 +271,24 @@ public:
                                nsIURI* aSheetURL,
                                nsIURI* aBaseURL,
                                nsIPrincipal* aSheetPrincipal,
                                nsCSSValue& aValue);
 
   bool IsValueValidForProperty(const nsCSSPropertyID aPropID,
                                const nsAString& aPropValue);
 
+  bool ParseTypedValue(const CSSVariableSyntax& aSyntax,
+                       const nsAString& aPropValue,
+                       nsIURI* aSheetURL,
+                       nsIURI* aBaseURL,
+                       nsIPrincipal* aSheetPrincipal,
+                       nsCSSValue& aValue,
+                       mozilla::CSSValueType& aType);
+
   typedef nsCSSParser::VariableEnumFunc VariableEnumFunc;
 
   /**
    * Parses a CSS token stream value and invokes a callback function for each
    * variable reference that is encountered.
    *
    * @param aPropertyValue The CSS token stream value.
    * @param aFunc The callback function to invoke; its parameters are the
@@ -293,17 +302,17 @@ public:
                                    void* aData);
 
   /**
    * Parses aPropertyValue as a CSS token stream value and resolves any
    * variable references using the variables in aVariables.
    *
    * @param aPropertyValue The CSS token stream value.
    * @param aVariables The set of variable values to use when resolving variable
-   *   references.
+   *   references. Can be null, in which case no variables will be resolved.
    * @param aResult Out parameter that gets the resolved value.
    * @param aFirstToken Out parameter that gets the type of the first token in
    *   aResult.
    * @param aLastToken Out parameter that gets the type of the last token in
    *   aResult.
    * @return Whether aResult could be parsed successfully and variable reference
    *   substitution succeeded.
    */
@@ -1358,16 +1367,23 @@ protected:
   /* Functions for transform-origin/perspective-origin Parsing */
   bool ParseTransformOrigin(bool aPerspective);
 
   /* Functions for filter parsing */
   bool ParseFilter();
   bool ParseSingleFilter(nsCSSValue* aValue);
   bool ParseDropShadow(nsCSSValue* aValue);
 
+  bool ParseSingleTypedValue(const CSSVariableSyntax::Term& aTerm,
+                             nsCSSValue& aValue);
+  bool ParseTypedValue(const CSSVariableSyntax& aSyntax,
+                       bool aAllowVariableReferences,
+                       nsCSSValue& aValue,
+                       mozilla::CSSValueType& aType);
+
   /* Find and return the namespace ID associated with aPrefix.
      If aPrefix has not been declared in an @namespace rule, returns
      kNameSpaceID_Unknown. */
   int32_t GetNamespaceIdForPrefix(const nsString& aPrefix);
 
   /* Find the correct default namespace, and set it on aSelector. */
   void SetDefaultNamespaceOnSelector(nsCSSSelector& aSelector);
 
@@ -3119,16 +3135,187 @@ CSSParserImpl::ParseFontFaceDescriptor(n
                  !GetToken(true);
 
   OUTPUT_ERROR();
   ReleaseScanner();
 
   return success;
 }
 
+static bool
+TryParseInherit(nsCSSToken aToken, nsCSSValue& aValue)
+{
+  // TODO(jyc) Add support for the 'revert' keyword here and in the resolver.
+  if (aToken.mType == eCSSToken_Ident) {
+    if (aToken.mIdent.Equals(NS_LITERAL_STRING("initial"))) {
+      aValue.SetInitialValue();
+      return true;
+    }
+    if (aToken.mIdent.Equals(NS_LITERAL_STRING("inherit"))) {
+      aValue.SetInheritValue();
+      return true;
+    }
+    if (aToken.mIdent.Equals(NS_LITERAL_STRING("unset"))) {
+      aValue.SetUnsetValue();
+      return true;
+    }
+  }
+  return false;
+}
+
+
+bool
+CSSParserImpl::ParseSingleTypedValue(const CSSVariableSyntax::Term& aTerm,
+                                     nsCSSValue& aValue)
+{
+  if (aTerm.GetType() == CSSValueType::SpecificIdent) {
+    if (!GetToken(true)) {
+      return false;
+    }
+    if (mToken.mType == eCSSToken_Ident &&
+        mToken.mIdent.Equals(aTerm.GetIdentifier())) {
+      aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
+      return true;
+    }
+    return TryParseInherit(mToken, aValue);
+  } else if (aTerm.GetType() == CSSValueType::CustomIdent) {
+    if (!GetToken(true)) {
+      return false;
+    }
+    if (mToken.mType == eCSSToken_Ident) {
+      aValue.SetStringValue(mToken.mIdent, eCSSUnit_Ident);
+      return true;
+    }
+    return TryParseInherit(mToken, aValue);
+  } else if (aTerm.GetType() == CSSValueType::Resolution) {
+    return ParseResolution(aValue) || TryParseInherit(mToken, aValue);
+  } else if (aTerm.GetType() == CSSValueType::TransformFunction) {
+    // Try to parse with aIsPrefixed = false then true.
+    return ParseSingleTransform(false, false, aValue) ||
+           ParseSingleTransform(true, false, aValue) ||
+           TryParseInherit(mToken, aValue);
+  }
+
+  uint32_t variant = aTerm.GetVariant();
+  MOZ_ASSERT(variant);
+
+  // VARIANT_INHERIT to allow for inherit, initial, and unset.
+  // In the future, need to allow revert too.
+  return ParseVariant(aValue, variant | VARIANT_INHERIT, nullptr) == CSSParseResult::Ok;
+}
+
+bool
+CSSParserImpl::ParseTypedValue(const CSSVariableSyntax& aSyntax,
+                               bool aAllowVariableReferences,
+                               nsCSSValue& aValue,
+                               mozilla::CSSValueType& aType)
+{
+  CSSVariableSyntax::Type syntaxType = aSyntax.GetType();
+
+  if (syntaxType == CSSVariableSyntax::Type::Anything) {
+    CSSVariableDeclarations::Type type;
+    nsString result;
+    if (!ParseVariableDeclaration(&type, aAllowVariableReferences, result)) {
+      return false;
+    }
+    if (type != CSSVariableDeclarations::Type::eTokenStream) {
+      return false;
+    }
+    nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
+    // Can't use eCSSPropertyExtra_UNKNOWN because that would cause
+    // KeyframeUtils::IsInvalidValuePair to think these values were invalid.
+    tokenStream->mPropertyID = eCSSPropertyExtra_variable;
+    tokenStream->mTokenStream = result;
+    tokenStream->mBaseURI = mBaseURI;
+    tokenStream->mSheetURI = mSheetURI;
+    tokenStream->mSheetPrincipal = mSheetPrincipal;
+    // XXX Should store sheet here (see bug 952338).
+    // tokenStream->mSheet = mSheet;
+    tokenStream->mLineNumber = 0;
+    tokenStream->mLineOffset = 0;
+    aValue.SetTokenStreamValue(tokenStream);
+    aType = CSSValueType::TokenStream;
+    return true;
+  }
+
+  MOZ_ASSERT(syntaxType == CSSVariableSyntax::Type::Disjunction);
+
+  CSSParserInputState state;
+  SaveInputState(state);
+
+  nsCSSValue result;
+
+  for (const CSSVariableSyntax::Term& term : aSyntax.GetTerms()) {
+    bool ok = false;
+    if (term.IsList()) {
+      nsCSSValueList* item = nullptr;
+      for (;;) {
+        nsCSSValue value;
+        if (!GetToken(/* aSkipWS = */ true)) {
+          // If at EOF, stop.
+          if (item) {
+            item->mNext = nullptr;
+          }
+          break;
+        }
+        UngetToken();
+        if (!ParseSingleTypedValue(term, value)) {
+          ok = false;
+          break;
+        }
+        if (!item) {
+          ok = true;
+          item = result.SetListValue();
+        } else {
+          item->mNext = new nsCSSValueList;
+          item = item->mNext;
+        }
+        item->mValue = value;
+      }
+    } else {
+      if (ParseSingleTypedValue(term, result)) {
+        ok = true;
+      }
+      // If we are in a disjunction (<A> | <B> | ...) and this one failed, we
+      // need to keep trying.
+    }
+    if (ok) {
+      aValue = result;
+      aType = term.GetType();
+      return true;
+    }
+    RestoreSavedInputState(state);
+    result.Reset();
+  }
+
+  return false;
+}
+
+bool
+CSSParserImpl::ParseTypedValue(const CSSVariableSyntax& aSyntax,
+                               const nsAString& aBuffer,
+                               nsIURI* aSheetURL,
+                               nsIURI* aBaseURL,
+                               nsIPrincipal* aSheetPrincipal,
+                               nsCSSValue& aValue,
+                               mozilla::CSSValueType& aType)
+{
+  nsCSSScanner scanner(aBuffer, 0);
+  css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aSheetURL);
+  InitScanner(scanner, reporter, aSheetURL, aBaseURL, aSheetPrincipal);
+
+  bool success = ParseTypedValue(aSyntax, false, aValue, aType) &&
+                 !GetToken(true);
+
+  OUTPUT_ERROR();
+  ReleaseScanner();
+
+  return success;
+}
+
 //----------------------------------------------------------------------
 
 bool
 CSSParserImpl::GetToken(bool aSkipWS)
 {
   if (mHavePushBack) {
     mHavePushBack = false;
     if (!aSkipWS || mToken.mType != eCSSToken_Whitespace) {
@@ -18066,8 +18253,22 @@ nsCSSParser::SetVariableRegistrations(
   CSSParserImpl *impl = static_cast<CSSParserImpl*>(mImpl);
 
   MOZ_ASSERT(impl->GetDocument() == nullptr,
              "If impl->mSheet is not null, then there should be no need for "
              "SetVariableRegistrations.");
 
   impl->mVariableRegistrations = aRegistrations;
 }
+
+bool
+nsCSSParser::ParseTypedValue(const CSSVariableSyntax& aSyntax,
+                             const nsAString& aPropValue,
+                             nsIURI* aSheetURL,
+                             nsIURI* aBaseURL,
+                             nsIPrincipal* aSheetPrincipal,
+                             nsCSSValue& aValue,
+                             mozilla::CSSValueType& aType)
+{
+  return static_cast<CSSParserImpl*>(mImpl)->
+    ParseTypedValue(aSyntax, aPropValue, aSheetURL, aBaseURL, aSheetPrincipal,
+                    aValue, aType);
+}
--- a/layout/style/nsCSSParser.h
+++ b/layout/style/nsCSSParser.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* parsing of CSS stylesheets, based on a token stream from the CSS scanner */
 
 #ifndef nsCSSParser_h___
 #define nsCSSParser_h___
 
 #include "mozilla/Attributes.h"
+#include "mozilla/CSSValueType.h"
 #include "mozilla/css/Loader.h"
 
 #include "nsCSSPropertyID.h"
 #include "nsCSSScanner.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsStringFwd.h"
 #include "nsTArrayForwardDeclare.h"
@@ -25,16 +26,17 @@ class nsMediaList;
 class nsMediaQuery;
 class nsCSSKeyframeRule;
 class nsCSSValue;
 struct nsRuleData;
 
 namespace mozilla {
 class CSSStyleSheet;
 struct CSSVariableRegistrations;
+class CSSVariableSyntax;
 class CSSVariableValues;
 namespace css {
 class Rule;
 class Declaration;
 class StyleRule;
 } // namespace css
 } // namespace mozilla
 
@@ -315,16 +317,24 @@ public:
                                nsIURI* aBaseURL,
                                nsIPrincipal* aSheetPrincipal,
                                nsCSSValue& aValue);
 
   // Check whether a given value can be applied to a property.
   bool IsValueValidForProperty(const nsCSSPropertyID aPropID,
                                const nsAString&    aPropValue);
 
+  bool ParseTypedValue(const mozilla::CSSVariableSyntax& aSyntax,
+                       const nsAString& aBuffer,
+                       nsIURI* aSheetURL,
+                       nsIURI* aBaseURL,
+                       nsIPrincipal* aSheetPrincipal,
+                       nsCSSValue& aValue,
+                       mozilla::CSSValueType& aType);
+
   // Return the default value to be used for -moz-control-character-visibility,
   // from preferences (cached by our Startup(), so that both nsStyleText and
   // nsRuleNode can have fast access to it).
   static uint8_t ControlCharVisibilityDefault();
 
   void SetVariableRegistrations(
       const mozilla::CSSVariableRegistrations* aRegistrations);