// 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; }
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; }
// 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; }
// 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; }
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; }
// <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; }
// 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; }