Example #1
0
        // Assigns to aValue iff it returns true.
        internal bool ParseVariant(ref nsCSSValue aValue,
                                    int32_t aVariantMask,
                                    int32_t[] aKeywordTable)
        {
            Debug.Assert(!(mHashlessColorQuirk && ((aVariantMask & VARIANT_COLOR) != 0)) ||
                       !((aVariantMask & VARIANT_NUMBER) != 0),
                       "can't distinguish colors from numbers");
              Debug.Assert(!(mHashlessColorQuirk && ((aVariantMask & VARIANT_COLOR) != 0)) ||
                       !(mUnitlessLengthQuirk && ((aVariantMask & VARIANT_LENGTH) != 0)),
                       "can't distinguish colors from lengths");
              Debug.Assert(!(mUnitlessLengthQuirk && ((aVariantMask & VARIANT_LENGTH) != 0)) ||
                       !((aVariantMask & VARIANT_NUMBER) != 0),
                       "can't distinguish lengths from numbers");
              Debug.Assert(!((aVariantMask & VARIANT_IDENTIFIER) != 0) ||
                            !((aVariantMask & VARIANT_IDENTIFIER_NO_INHERIT) != 0),
                            "must not set both VARIANT_IDENTIFIER and VARIANT_IDENTIFIER_NO_INHERIT");

              if (!GetToken(true)) {
            return false;
              }
              nsCSSToken tk = mToken;
              if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) &&
              (nsCSSTokenType.Ident == tk.mType)) {
            nsCSSKeyword keyword = nsCSSKeywords.LookupKeyword(tk.mIdentStr);
            if (nsCSSKeyword.UNKNOWN < keyword) { // known keyword
              if ((aVariantMask & VARIANT_AUTO) != 0) {
                if (nsCSSKeyword.auto == keyword) {
                  aValue.SetAutoValue();
                  return true;
                }
              }
              if ((aVariantMask & VARIANT_INHERIT) != 0) {
                // XXX Should we check IsParsingCompoundProperty, or do all
                // callers handle it?  (Not all callers set it, though, since
                // they want the quirks that are disabled by setting it.)
                if (nsCSSKeyword.inherit == keyword) {
                  aValue.SetInheritValue();
                  return true;
                }
                else if (nsCSSKeyword._moz_initial == keyword ||
                         nsCSSKeyword.initial == keyword) { // anything that can inherit can also take an initial val.
                  aValue.SetInitialValue();
                  return true;
                }
              }
              if ((aVariantMask & VARIANT_NONE) != 0) {
                if (nsCSSKeyword.none == keyword) {
                  aValue.SetNoneValue();
                  return true;
                }
              }
              if ((aVariantMask & VARIANT_ALL) != 0) {
                if (nsCSSKeyword.all == keyword) {
                  aValue.SetAllValue();
                  return true;
                }
              }
              if ((aVariantMask & VARIANT_NORMAL) != 0) {
                if (nsCSSKeyword.normal == keyword) {
                  aValue.SetNormalValue();
                  return true;
                }
              }
              if ((aVariantMask & VARIANT_SYSFONT) != 0) {
                if (nsCSSKeyword._moz_use_system_font == keyword &&
                    !IsParsingCompoundProperty()) {
                  aValue.SetSystemFontValue();
                  return true;
                }
              }
              if ((aVariantMask & VARIANT_KEYWORD) != 0) {
                int32_t value = 0;
                if (nsCSSProps.FindKeyword(keyword, aKeywordTable, ref value)) {
                  aValue.SetIntValue(value, nsCSSUnit.Enumerated);
                  return true;
                }
              }
            }
              }
              // Check VARIANT_NUMBER and VARIANT_INTEGER before VARIANT_LENGTH or
              // VARIANT_ZERO_ANGLE.
              if (((aVariantMask & VARIANT_NUMBER) != 0) &&
              (nsCSSTokenType.Number == tk.mType)) {
            aValue.SetFloatValue(tk.mNumber, nsCSSUnit.Number);
            return true;
              }
              if (((aVariantMask & VARIANT_INTEGER) != 0) &&
              (nsCSSTokenType.Number == tk.mType) && tk.mIntegerValid) {
            aValue.SetIntValue(tk.mInteger, nsCSSUnit.Integer);
            return true;
              }
              if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE |
                                VARIANT_FREQUENCY | VARIANT_TIME)) != 0 &&
               nsCSSTokenType.Dimension == tk.mType) ||
              ((aVariantMask & (VARIANT_LENGTH | VARIANT_ZERO_ANGLE)) != 0 &&
               nsCSSTokenType.Number == tk.mType &&
               tk.mNumber == 0.0f)) {
            if (((aVariantMask & VARIANT_POSITIVE_DIMENSION) != 0 &&
                 tk.mNumber <= 0.0) ||
                ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 &&
                 tk.mNumber < 0.0)) {
                UngetToken();
                return false;
            }
            if (TranslateDimension(ref aValue, aVariantMask, tk.mNumber, tk.mIdentStr)) {
              return true;
            }
            // Put the token back; we didn't parse it, so we shouldn't consume it
            UngetToken();
            return false;
              }
              if (((aVariantMask & VARIANT_PERCENT) != 0) &&
              (nsCSSTokenType.Percentage == tk.mType)) {
            aValue.SetPercentValue(tk.mNumber);
            return true;
              }
              if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px
            if (((aVariantMask & VARIANT_LENGTH) != 0) &&
                (nsCSSTokenType.Number == tk.mType)) {
              aValue.SetFloatValue(tk.mNumber, nsCSSUnit.Pixel);
              return true;
            }
              }

              if (IsSVGMode() && !IsParsingCompoundProperty()) {
            // STANDARD: SVG Spec states that lengths and coordinates can be unitless
            // in which case they default to user-units (1 px = 1 user unit)
            if (((aVariantMask & VARIANT_LENGTH) != 0) &&
                (nsCSSTokenType.Number == tk.mType)) {
              aValue.SetFloatValue(tk.mNumber, nsCSSUnit.Pixel);
              return true;
            }
              }

              if (((aVariantMask & VARIANT_URL) != 0) &&
              nsCSSTokenType.URL == tk.mType) {
            SetValueToURL(ref aValue, tk.mIdentStr);
            return true;
              }
              if ((aVariantMask & VARIANT_GRADIENT) != 0 &&
              nsCSSTokenType.Function == tk.mType) {
            // a generated gradient
            string tmp = tk.mIdentStr;
            bool isLegacy = false;
            if (StringBeginsWith(tmp, "-moz-")) {
              tmp = tmp.Substring(5);
              isLegacy = true;
            }
            bool isRepeating = false;
            if (StringBeginsWith(tmp, "repeating-")) {
              tmp = tmp.Substring(10);
              isRepeating = true;
            }

            if (tmp.LowerCaseEqualsLiteral("linear-gradient")) {
              return ParseLinearGradient(ref aValue, isRepeating, isLegacy);
            }
            if (tmp.LowerCaseEqualsLiteral("radial-gradient")) {
              return ParseRadialGradient(ref aValue, isRepeating, isLegacy);
            }
              }
              if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 &&
              nsCSSTokenType.Function == tk.mType &&
              tk.mIdentStr.LowerCaseEqualsLiteral("-moz-image-rect")) {
            return ParseImageRect(ref aValue);
              }
              if ((aVariantMask & VARIANT_ELEMENT) != 0 &&
              nsCSSTokenType.Function == tk.mType &&
              tk.mIdentStr.LowerCaseEqualsLiteral("-moz-element")) {
            return ParseElement(ref aValue);
              }
              if ((aVariantMask & VARIANT_COLOR) != 0) {
            if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix
                (nsCSSTokenType.ID == tk.mType) ||
                (nsCSSTokenType.Hash == tk.mType) ||
                (nsCSSTokenType.Ident == tk.mType) ||
                ((nsCSSTokenType.Function == tk.mType) &&
                 (tk.mIdentStr.LowerCaseEqualsLiteral("rgb") ||
                  tk.mIdentStr.LowerCaseEqualsLiteral("hsl") ||
                  tk.mIdentStr.LowerCaseEqualsLiteral("-moz-rgba") ||
                  tk.mIdentStr.LowerCaseEqualsLiteral("-moz-hsla") ||
                  tk.mIdentStr.LowerCaseEqualsLiteral("rgba") ||
                  tk.mIdentStr.LowerCaseEqualsLiteral("hsla"))))
            {
              // Put token back so that parse color can get it
              UngetToken();
              if (ParseColor(ref aValue)) {
                return true;
              }
              return false;
            }
              }
              if (((aVariantMask & VARIANT_STRING) != 0) &&
              (nsCSSTokenType.String == tk.mType)) {
            string  buffer;
            buffer = tk.mIdentStr;
            aValue.SetStringValue(buffer, nsCSSUnit.String);
            return true;
              }
              if (((aVariantMask &
                (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
              (nsCSSTokenType.Ident == tk.mType) &&
              ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
               !(tk.mIdentStr.LowerCaseEqualsLiteral("inherit") ||
                 tk.mIdentStr.LowerCaseEqualsLiteral("initial")))) {
            aValue.SetStringValue(tk.mIdentStr, nsCSSUnit.Ident);
            return true;
              }
              if (((aVariantMask & VARIANT_COUNTER) != 0) &&
              (nsCSSTokenType.Function == tk.mType) &&
              (tk.mIdentStr.LowerCaseEqualsLiteral("counter") ||
               tk.mIdentStr.LowerCaseEqualsLiteral("counters"))) {
            return ParseCounter(ref aValue);
              }
              if (((aVariantMask & VARIANT_ATTR) != 0) &&
              (nsCSSTokenType.Function == tk.mType) &&
              tk.mIdentStr.LowerCaseEqualsLiteral("attr")) {
            if (!ParseAttr(ref aValue)) {
              SkipUntil(')');
              return false;
            }
            return true;
              }
              if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) &&
              (nsCSSTokenType.Function == tk.mType)) {
            if (tk.mIdentStr.LowerCaseEqualsLiteral("cubic-bezier")) {
              if (!ParseTransitionTimingFunctionValues(ref aValue)) {
                SkipUntil(')');
                return false;
              }
              return true;
            }
            if (tk.mIdentStr.LowerCaseEqualsLiteral("steps")) {
              if (!ParseTransitionStepTimingFunctionValues(ref aValue)) {
                SkipUntil(')');
                return false;
              }
              return true;
            }
              }
              if (((aVariantMask & VARIANT_CALC) != 0) &&
              (nsCSSTokenType.Function == tk.mType) &&
              (tk.mIdentStr.LowerCaseEqualsLiteral("calc") ||
               tk.mIdentStr.LowerCaseEqualsLiteral("-moz-calc"))) {
            // calc() currently allows only lengths and percents inside it.
            return ParseCalc(ref aValue, aVariantMask & VARIANT_LP);
              }

              UngetToken();
              return false;
        }
Example #2
0
        internal bool ParseAttr(ref nsCSSValue aValue)
        {
            if (!GetToken(true)) {
            return false;
              }

              var attr = new StringBuilder();
              if (nsCSSTokenType.Ident == mToken.mType) {  // attr name or namespace
            string holdIdent = mToken.mIdentStr;
            if (ExpectSymbol('|', false)) {  // namespace
              int32_t nameSpaceID = GetNamespaceIdForPrefix(holdIdent);
              if (nameSpaceID == nsNameSpace.Unknown) {
                return false;
              }
              attr.AppendInt(nameSpaceID, 10);
              attr.Append('|');
              if (! GetToken(false)) {
                { if (!mSuppressErrors) mReporter.ReportUnexpected("PEAttributeNameEOF"); };
                return false;
              }
              if (nsCSSTokenType.Ident == mToken.mType) {
                attr.Append(mToken.mIdentStr);
              }
              else {
                { if (!mSuppressErrors) mReporter.ReportUnexpected("PEAttributeNameExpected", mToken); };
                UngetToken();
                return false;
              }
            }
            else {  // no namespace
              attr.AssignLiteral(holdIdent);
            }
              }
              else if (mToken.IsSymbol('*')) {  // namespace wildcard
            // Wildcard namespace makes no sense here and is not allowed
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEAttributeNameExpected", mToken); };
            UngetToken();
            return false;
              }
              else if (mToken.IsSymbol('|')) {  // explicit NO namespace
            if (! GetToken(false)) {
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEAttributeNameEOF"); };
              return false;
            }
            if (nsCSSTokenType.Ident == mToken.mType) {
              attr.Append(mToken.mIdentStr);
            }
            else {
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEAttributeNameExpected", mToken); };
              UngetToken();
              return false;
            }
              }
              else {
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEAttributeNameOrNamespaceExpected", mToken); };
            UngetToken();
            return false;
              }
              if (!ExpectSymbol(')', true)) {
            return false;
              }
              aValue.SetStringValue(attr, nsCSSUnit.Attr);
              return true;
        }
Example #3
0
        // src: ( uri-src | local-src ) (',' ( uri-src | local-src ) )*
        // uri-src: uri [ 'format(' string ( ',' string )* ')' ]
        // local-src: 'local(' ( string | ident ) ')'
        internal bool ParseFontSrc(ref nsCSSValue aValue)
        {
            // could we maybe turn nsCSSValue[] into List<nsCSSValue>?
              var values = new List<nsCSSValue>();
              var cur = new nsCSSValue();
              for (;;) {
            if (!GetToken(true))
              break;

            if (mToken.mType == nsCSSTokenType.URL) {
              SetValueToURL(ref cur, mToken.mIdentStr);
              values.AppendElement(cur);
              if (!ParseFontSrcFormat(values))
                return false;

            } else if (mToken.mType == nsCSSTokenType.Function &&
                       mToken.mIdentStr.LowerCaseEqualsLiteral("local")) {
              // css3-fonts does not specify a formal grammar for local().
              // The text permits both unquoted identifiers and quoted
              // strings.  We resolve this ambiguity in the spec by
              // assuming that the appropriate production is a single
              // <family-name>, possibly surrounded by whitespace.

              var family = new StringBuilder();
              bool single = false;
              if (!ParseOneFamily(family, ref single)) {
                SkipUntil(')');
                return false;
              }
              if (!ExpectSymbol(')', true)) {
                SkipUntil(')');
                return false;
              }

              // the style parameters to the nsFont constructor are ignored,
              // because it's only being used to call EnumerateFamilies
              var font = new nsFont(family, 0, 0, 0, 0, 0, 0);
              var dat = new ExtractFirstFamilyData();

              font.EnumerateFamilies(ExtractFirstFamily, (object) dat);
              if (!dat.mGood)
                return false;

              cur.SetStringValue(dat.mFamilyName, nsCSSUnit.LocalFont);
              values.AppendElement(cur);
            } else {
              // We don't know what to do with this token; unget it and error out
              UngetToken();
              return false;
            }

            if (!ExpectSymbol(',', true))
              break;
              }

              if (values.Length() == 0)
            return false;

              nsCSSValue[] srcVals
            = new nsCSSValue[values.Length()];

              uint32_t i = 0;
              for (i = 0; i < values.Length(); i++)
            srcVals[i] = values[i];
              aValue.SetArrayValue(srcVals, nsCSSUnit.Array);
              return true;
        }
Example #4
0
        // font-descriptor: descriptor ':' value ';'
        // caller has advanced mToken to point at the descriptor
        internal bool ParseFontDescriptorValue(nsCSSFontDesc aDescID,
                                                ref nsCSSValue aValue)
        {
            switch (aDescID) {
            // These four are similar to the properties of the same name,
            // possibly with more restrictions on the values they can take.

              case nsCSSFontDesc.Family: {
            if (!ParseFamily(ref aValue) ||
                aValue.GetUnit() != nsCSSUnit.Families)
              return false;

            // the style parameters to the nsFont constructor are ignored,
            // because it's only being used to call EnumerateFamilies
            string valueStr = "";
            aValue.GetStringValue(ref valueStr);
            var font = new nsFont(valueStr, 0, 0, 0, 0, 0, 0);
            var dat = new ExtractFirstFamilyData();

            font.EnumerateFamilies(ExtractFirstFamily, (object) dat);
            if (!dat.mGood)
              return false;

            aValue.SetStringValue(dat.mFamilyName, nsCSSUnit.String);
            return true;
              }

            goto case nsCSSFontDesc.Style;
              case nsCSSFontDesc.Style:
            // property is VARIANT_HMK|VARIANT_SYSFONT
            return ParseVariant(ref aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
                                nsCSSProps.kFontStyleKTable);

              case nsCSSFontDesc.Weight:
            return (ParseFontWeight(ref aValue) &&
                    aValue.GetUnit() != nsCSSUnit.Inherit &&
                    aValue.GetUnit() != nsCSSUnit.Initial &&
                    (aValue.GetUnit() != nsCSSUnit.Enumerated ||
                     (aValue.GetIntValue() != nsStyle.FONT_WEIGHT_BOLDER &&
                      aValue.GetIntValue() != nsStyle.FONT_WEIGHT_LIGHTER)));

              case nsCSSFontDesc.Stretch:
            // property is VARIANT_HK|VARIANT_SYSFONT
            return ParseVariant(ref aValue, VARIANT_KEYWORD,
                                nsCSSProps.kFontStretchKTable);

            // These two are unique to @font-face and have their own special grammar.
            goto case nsCSSFontDesc.Src;
              case nsCSSFontDesc.Src:
            return ParseFontSrc(ref aValue);

              case nsCSSFontDesc.UnicodeRange:
            return ParseFontRanges(ref aValue);

              case nsCSSFontDesc.FontFeatureSettings:
            return ParseFontFeatureSettings(ref aValue);

              case nsCSSFontDesc.FontLanguageOverride:
            return ParseVariant(ref aValue, VARIANT_NORMAL | VARIANT_STRING, null);

              case nsCSSFontDesc.UNKNOWN:
              case nsCSSFontDesc.COUNT:
            Debug.Fail("bad nsCSSFontDesc code");
            break;
              }
              // explicitly do NOT have a default case to let the compiler
              // help find missing descriptors
              return false;
        }
Example #5
0
        internal bool ParseFamily(ref nsCSSValue aValue)
        {
            var family = new StringBuilder();
              bool single = false;

              // keywords only have meaning in the first position
              if (!ParseOneFamily(family, ref single))
            return false;

              // check for keywords, but only when keywords appear by themselves
              // i.e. not in compounds such as font-family: default blah;
              if (single) {
            nsCSSKeyword keyword = nsCSSKeywords.LookupKeyword(family);
            if (keyword == nsCSSKeyword.inherit) {
              aValue.SetInheritValue();
              return true;
            }
            // 605231 - don't parse unquoted 'default' reserved keyword
            if (keyword == nsCSSKeyword.@default) {
              return false;
            }
            if (keyword == nsCSSKeyword._moz_initial || keyword == nsCSSKeyword.initial) {
              aValue.SetInitialValue();
              return true;
            }
            if (keyword == nsCSSKeyword._moz_use_system_font &&
                !IsParsingCompoundProperty()) {
              aValue.SetSystemFontValue();
              return true;
            }
              }

              for (;;) {
            if (!ExpectSymbol(',', true))
              break;

            family.Append(',');

            var nextFamily = new StringBuilder();
            if (!ParseOneFamily(nextFamily, ref single))
              return false;

            // at this point unquoted keywords are not allowed
            // as font family names but can appear within names
            if (single) {
              nsCSSKeyword keyword = nsCSSKeywords.LookupKeyword(nextFamily);
              switch (keyword) {
                case nsCSSKeyword.inherit:
                case nsCSSKeyword.initial:
                case nsCSSKeyword.@default:
                case nsCSSKeyword._moz_initial:
                case nsCSSKeyword._moz_use_system_font:
                  return false;
                default:
                  break;
              }
            }

            family.Append(nextFamily);
              }

              if (family.IsEmpty()) {
            return false;
              }
              aValue.SetStringValue(family, nsCSSUnit.Families);
              return true;
        }
Example #6
0
        // <element>: -moz-element(# <element_id> )
        internal bool ParseElement(ref nsCSSValue aValue)
        {
            // A non-iterative for loop to break out when an error occurs.
              for (;;) {
            if (!GetToken(true))
              break;

            if (mToken.mType == nsCSSTokenType.ID) {
              aValue.SetStringValue(mToken.mIdentStr, nsCSSUnit.Element);
            } else {
              UngetToken();
              break;
            }

            if (!ExpectSymbol(')', true))
              break;

            return true;
              }

              // If we detect a syntax error, we must match the opening parenthesis of the
              // function with the closing parenthesis and skip all the tokens in between.
              SkipUntil(')');
              return false;
        }
Example #7
0
        // The types to pass to ParseColorComponent.  These correspond to the
        // various datatypes that can go within rgb().
        internal bool ParseColor(ref nsCSSValue aValue)
        {
            if (!GetToken(true)) {
            { if (!mSuppressErrors) mReporter.ReportUnexpected("PEColorEOF"); };
            return false;
              }

              nsCSSToken tk = mToken;
              var rgba = new nscolor();
              switch (tk.mType) {
            case nsCSSTokenType.ID:
            case nsCSSTokenType.Hash:
              // #xxyyzz
              if (nscolor.HexToRGB(tk.mIdentStr, ref rgba)) {
                aValue.SetColorValue(rgba);
                return true;
              }
              break;

            case nsCSSTokenType.Ident:
              if (nscolor.ColorNameToRGB(tk.mIdentStr, ref rgba)) {
                aValue.SetStringValue(tk.mIdentStr, nsCSSUnit.Ident);
                return true;
              }
              else {
                nsCSSKeyword keyword = nsCSSKeywords.LookupKeyword(tk.mIdentStr);
                if (nsCSSKeyword.UNKNOWN < keyword) { // known keyword
                  int32_t value = 0;
                  if (nsCSSProps.FindKeyword(keyword, nsCSSProps.kColorKTable, ref value)) {
                    aValue.SetIntValue(value, nsCSSUnit.EnumColor);
                    return true;
                  }
                }
              }
              break;
            case nsCSSTokenType.Function:
              if (mToken.mIdentStr.LowerCaseEqualsLiteral("rgb")) {
                // rgb ( component , component , component )
                uint8_t r = 0, g = 0, b = 0;
                int32_t type = COLOR_TYPE_UNKNOWN;
                if (ParseColorComponent(ref r, ref type, ',') &&
                    ParseColorComponent(ref g, ref type, ',') &&
                    ParseColorComponent(ref b, ref type, ')')) {
                  aValue.SetColorValue(nscolor.RGB(r,g,b));
                  return true;
                }
                SkipUntil(')');
                return false;
              }
              else if (mToken.mIdentStr.LowerCaseEqualsLiteral("-moz-rgba") ||
                       mToken.mIdentStr.LowerCaseEqualsLiteral("rgba")) {
                // rgba ( component , component , component , opacity )
                uint8_t r = 0, g = 0, b = 0, a = 0;
                int32_t type = COLOR_TYPE_UNKNOWN;
                if (ParseColorComponent(ref r, ref type, ',') &&
                    ParseColorComponent(ref g, ref type, ',') &&
                    ParseColorComponent(ref b, ref type, ',') &&
                    ParseColorOpacity(ref a)) {
                  aValue.SetColorValue(nscolor.RGBA(r, g, b, a));
                  return true;
                }
                SkipUntil(')');
                return false;
              }
              else if (mToken.mIdentStr.LowerCaseEqualsLiteral("hsl")) {
                // hsl ( hue , saturation , lightness )
                // "hue" is a number, "saturation" and "lightness" are percentages.
                if (ParseHSLColor(ref rgba, ')')) {
                  aValue.SetColorValue(rgba);
                  return true;
                }
                SkipUntil(')');
                return false;
              }
              else if (mToken.mIdentStr.LowerCaseEqualsLiteral("-moz-hsla") ||
                       mToken.mIdentStr.LowerCaseEqualsLiteral("hsla")) {
                // hsla ( hue , saturation , lightness , opacity )
                // "hue" is a number, "saturation" and "lightness" are percentages,
                // "opacity" is a number.
                uint8_t a = 0;
                if (ParseHSLColor(ref rgba, ',') &&
                    ParseColorOpacity(ref a)) {
                  aValue.SetColorValue(nscolor.RGBA(rgba.R, rgba.G,
                                               rgba.B, a));
                  return true;
                }
                SkipUntil(')');
                return false;
              }
              break;
            default:
              break;
              }

              // try 'xxyyzz' without '#' prefix for compatibility with IE and Nav4x (bug 23236 and 45804)
              // TODO support hashless colors

              // It's not a color
              { if (!mSuppressErrors) mReporter.ReportUnexpected("PEColorNotColor", mToken); };
              UngetToken();
              return false;
        }