diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a1b4e6..9167d24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Released on Saturday, September 5 2026 - Fixed `not ` is always false (#231) - Fixed `calc()` computations in AoT-compiled applications (#236) @sebastienros - Fixed usage of `calc()` with unitless scaling (multiplication / division) +- Fixed case-sensitive matching of `and` / `or` in `@supports` and `from` / `to` in `@keyframes` (#240) - Added optional CSSOM compliant color seralization (#229) @lahma - Added user-preference media features to the render device (#235) @lahma - Added media query list evaluation using `IRenderDevice` (#228) @lahma diff --git a/src/AngleSharp.Css.Tests/Rules/CssKeyframeRule.cs b/src/AngleSharp.Css.Tests/Rules/CssKeyframeRule.cs index efcc9a5..9fc71c7 100644 --- a/src/AngleSharp.Css.Tests/Rules/CssKeyframeRule.cs +++ b/src/AngleSharp.Css.Tests/Rules/CssKeyframeRule.cs @@ -84,5 +84,38 @@ public void KeyframeRuleWithPercentage_Issue128() Assert.AreEqual(3, rule.Key.Stops.Count()); Assert.AreEqual(0, rule.Style.Length); } + + [Test] + public void KeyframeRuleWithUppercaseFrom() + { + var rule = ParseKeyframeRule(@" FROM { + margin-left: 0px; + }"); + Assert.IsNotNull(rule); + Assert.AreEqual("0%", rule.KeyText); + Assert.AreEqual(1, rule.Key.Stops.Count()); + Assert.AreEqual(1, rule.Style.Length); + } + + [Test] + public void KeyframeRuleWithUppercaseTo() + { + var rule = ParseKeyframeRule(@" TO { + margin-left: 200px; + }"); + Assert.IsNotNull(rule); + Assert.AreEqual("100%", rule.KeyText); + Assert.AreEqual(1, rule.Key.Stops.Count()); + Assert.AreEqual(1, rule.Style.Length); + } + + [Test] + public void KeyframeRuleWithMixedCaseFromAndTo() + { + var rule = ParseKeyframeRule(@" From, To { }"); + Assert.IsNotNull(rule); + Assert.AreEqual("0%, 100%", rule.KeyText); + Assert.AreEqual(2, rule.Key.Stops.Count()); + } } } diff --git a/src/AngleSharp.Css.Tests/Rules/CssSupports.cs b/src/AngleSharp.Css.Tests/Rules/CssSupports.cs index 1a51280..c419dd1 100644 --- a/src/AngleSharp.Css.Tests/Rules/CssSupports.cs +++ b/src/AngleSharp.Css.Tests/Rules/CssSupports.cs @@ -209,5 +209,57 @@ public void SupportsNegatedDisplayFlexRuleWithDeclarations() Assert.AreEqual("not (display: flex)", supports.ConditionText); Assert.IsFalse(supports.Condition.Check(device)); } + + [Test] + public void SupportsUppercaseAndKeywordRule() + { + var source = @"@supports ((background-color: red) AND (color: blue)) { }"; + var sheet = ParseStyleSheet(source); + var device = new DefaultRenderDevice(); + Assert.AreEqual(1, sheet.Rules.Length); + Assert.IsInstanceOf(sheet.Rules[0]); + var supports = sheet.Rules[0] as CssSupportsRule; + Assert.AreEqual("((background-color: red) and (color: blue))", supports.ConditionText); + Assert.IsTrue(supports.Condition.Check(device)); + } + + [Test] + public void SupportsUppercaseOrKeywordRule() + { + var source = @"@supports ((background-transparency: half) OR (color: blue)) { }"; + var sheet = ParseStyleSheet(source); + var device = new DefaultRenderDevice(); + Assert.AreEqual(1, sheet.Rules.Length); + Assert.IsInstanceOf(sheet.Rules[0]); + var supports = sheet.Rules[0] as CssSupportsRule; + Assert.AreEqual("((background-transparency: half) or (color: blue))", supports.ConditionText); + Assert.IsTrue(supports.Condition.Check(device)); + } + + [Test] + public void SupportsMixedCaseAndKeywordChainRule() + { + var source = @"@supports ((background-color: red) And (color: blue) aND (width: 10px)) { }"; + var sheet = ParseStyleSheet(source); + var device = new DefaultRenderDevice(); + Assert.AreEqual(1, sheet.Rules.Length); + Assert.IsInstanceOf(sheet.Rules[0]); + var supports = sheet.Rules[0] as CssSupportsRule; + Assert.AreEqual("((background-color: red) and (color: blue) and (width: 10px))", supports.ConditionText); + Assert.IsTrue(supports.Condition.Check(device)); + } + + [Test] + public void SupportsUppercaseAndKeywordKeepsInnerRules() + { + var source = @"@supports (color: red) AND (display: flex) { + body { width: 100%; } +}"; + var sheet = ParseStyleSheet(source); + Assert.AreEqual(1, sheet.Rules.Length); + Assert.IsInstanceOf(sheet.Rules[0]); + var supports = sheet.Rules[0] as CssSupportsRule; + Assert.AreEqual(1, supports.Rules.Length); + } } } diff --git a/src/AngleSharp.Css/Parser/Micro/ConditionParser.cs b/src/AngleSharp.Css/Parser/Micro/ConditionParser.cs index 9fa66f6..55561c8 100644 --- a/src/AngleSharp.Css/Parser/Micro/ConditionParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/ConditionParser.cs @@ -57,8 +57,8 @@ private static IConditionFunction ConjunctionOrDisjunction(this StringSource sou if (ident != null) { - var isAnd = ident.Is(CssKeywords.And); - var isOr = ident.Is(CssKeywords.Or); + var isAnd = ident.Isi(CssKeywords.And); + var isOr = ident.Isi(CssKeywords.Or); if (isAnd || isOr) { @@ -141,7 +141,7 @@ private static IEnumerable Scan(this StringSource source, St source.SkipSpacesAndComments(); ident = source.ParseIdent(); } - while (ident != null && ident.Is(keyword)); + while (ident != null && ident.Isi(keyword)); return conditions; } diff --git a/src/AngleSharp.Css/Parser/Micro/KeyframeParser.cs b/src/AngleSharp.Css/Parser/Micro/KeyframeParser.cs index ca6bc7f..8ea41c0 100644 --- a/src/AngleSharp.Css/Parser/Micro/KeyframeParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/KeyframeParser.cs @@ -43,11 +43,11 @@ public static IKeyframeSelector ParseKeyframeSelector(this StringSource source) stops.Add(test.Value); } - else if (id.Is(CssKeywords.From)) + else if (id.Isi(CssKeywords.From)) { stops.Add(0f); } - else if (id.Is(CssKeywords.To)) + else if (id.Isi(CssKeywords.To)) { stops.Add(1f); }