Skip to content

Commit 6ab4a63

Browse files
committed
Improved rule prios
1 parent dba9069 commit 6ab4a63

File tree

7 files changed

+127
-12
lines changed

7 files changed

+127
-12
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Released on tbd.
44

5-
- Updated to use AngleSharp 1.0
5+
- Updated to use AngleSharp 1.0 (#150)
66
- Updated media parsing to media L4 spec (#133)
77
- Fixed issue when updating shorthands with invalid values (#129)
88
- Fixed issue with appended EOF character in `CssText` (#123)
@@ -11,6 +11,7 @@ Released on tbd.
1111
- Fixed ordering of rows and columns in `grid` and `grid-gap` (#137)
1212
- Fixed inclusion of CSS from stylesheets (#116, #140)
1313
- Fixed style empty if `text-align` is `start` (#151)
14+
- Fixed computation of priority in CSS rules using multi selector
1415
- Added further compactification of CSS tuples (#89, #93)
1516
- Added support for 8-digit hex color codes (#132)
1617
- Added more CSSOM possibilities and helpers (#6)

src/AngleSharp.Css.Tests/Extensions/AnalysisWindow.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ namespace AngleSharp.Css.Tests.Extensions
22
{
33
using AngleSharp.Css.Dom;
44
using AngleSharp.Dom;
5+
using AngleSharp.Html.Dom;
56
using NUnit.Framework;
7+
using System.Linq;
68
using System.Text;
79
using System.Threading.Tasks;
810
using static CssConstructionFunctions;
@@ -284,10 +286,19 @@ public async Task NullSelectorStillWorks_Issue52()
284286
{
285287
var sheet = ParseStyleSheet("a {}");
286288
var document = await sheet.Context.OpenAsync(res => res.Content("<body></body>"));
287-
sheet.Add(new CssStyleRule(sheet));
288289
var sc = new StyleCollection(new[] { sheet }, new DefaultRenderDevice());
289290
var decl = sc.ComputeCascadedStyle(document.Body);
290291
Assert.IsNotNull(decl);
291292
}
293+
294+
[Test]
295+
public async Task PriorityInMultiSelectorIsEvaluatedPerMatch()
296+
{
297+
var sheet = ParseStyleSheet(@"#target {color: blue} h3, #nottarget { color: purple; } ");
298+
var document = await sheet.Context.OpenAsync(res => res.Content(@"<h3 id='target'>Test</h3>"));
299+
var sc = new StyleCollection(new[] { sheet }, new DefaultRenderDevice());
300+
var style = sc.ComputeCascadedStyle(document.QuerySelector("h3"));
301+
Assert.AreEqual("rgba(0, 0, 255, 1)", style.GetColor());
302+
}
292303
}
293304
}

src/AngleSharp.Css/Dom/ICssStyleRule.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
namespace AngleSharp.Css.Dom
1+
namespace AngleSharp.Css.Dom
22
{
33
using AngleSharp.Attributes;
4+
using AngleSharp.Dom;
45
using System;
56

67
/// <summary>
@@ -26,5 +27,10 @@ public interface ICssStyleRule : ICssRule
2627
/// Gets the selector for matching elements.
2728
/// </summary>
2829
ISelector Selector { get; }
30+
31+
/// <summary>
32+
/// Gets the selector for matching elements.
33+
/// </summary>
34+
Boolean TryMatch(IElement element, IElement? scope, out Priority specificity);
2935
}
3036
}

src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@ namespace AngleSharp.Css.Dom
33
using AngleSharp.Css;
44
using AngleSharp.Dom;
55
using System;
6+
using System.Collections.Generic;
67
using System.Diagnostics;
78
using System.IO;
9+
using System.Linq;
810

911
/// <summary>
1012
/// Represents a CSS style rule.
1113
/// </summary>
1214
[DebuggerDisplay(null, Name = "CssStyleRule ({SelectorText})")]
13-
sealed class CssStyleRule : CssRule, ICssStyleRule
15+
sealed class CssStyleRule : CssRule, ICssStyleRule, ISelectorVisitor
1416
{
1517
#region Fields
1618

1719
private readonly CssStyleDeclaration _style;
1820
private ISelector _selector;
21+
private IEnumerable<ISelector> _selectorList;
1922

2023
#endregion
2124

@@ -25,18 +28,23 @@ internal CssStyleRule(ICssStyleSheet owner)
2528
: base(owner, CssRuleType.Style)
2629
{
2730
_style = new CssStyleDeclaration(this);
31+
_selectorList = null;
2832
}
2933

3034
#endregion
3135

3236
#region Properties
3337

34-
public ISelector Selector => _selector;
38+
public ISelector Selector
39+
{
40+
get => _selector;
41+
private set => ChangeSelector(value);
42+
}
3543

3644
public String SelectorText
3745
{
3846
get => _selector?.Text;
39-
set => _selector = ParseSelector(value);
47+
set => ChangeSelector(ParseSelector(value));
4048
}
4149

4250
ICssStyleDeclaration ICssStyleRule.Style => _style;
@@ -55,10 +63,34 @@ internal void SetInvalidSelector(String selectorText)
5563
protected override void ReplaceWith(ICssRule rule)
5664
{
5765
var newRule = (ICssStyleRule)rule;
58-
_selector = newRule.Selector;
66+
ChangeSelector(newRule.Selector);
5967
_style.SetDeclarations(newRule.Style);
6068
}
6169

70+
public Boolean TryMatch(IElement element, IElement? scope, out Priority specificity)
71+
{
72+
if (_selectorList is not null)
73+
{
74+
foreach (var selector in _selectorList.OrderByDescending(m => m.Specificity))
75+
{
76+
if (selector.Match(element, scope))
77+
{
78+
specificity = selector.Specificity;
79+
return true;
80+
}
81+
}
82+
}
83+
84+
if (_selector is not null && _selector.Match(element, scope))
85+
{
86+
specificity = _selector.Specificity;
87+
return true;
88+
}
89+
90+
specificity = default;
91+
return false;
92+
}
93+
6294
public override void ToCss(TextWriter writer, IStyleFormatter formatter)
6395
{
6496
var block = _style.ToCssBlock(formatter);
@@ -69,7 +101,55 @@ public override void ToCss(TextWriter writer, IStyleFormatter formatter)
69101

70102
#region Selector
71103

72-
class InvalidSelector : ISelector
104+
private void ChangeSelector(ISelector value)
105+
{
106+
_selectorList = null;
107+
_selector = value;
108+
value?.Accept(this);
109+
}
110+
111+
void ISelectorVisitor.Attribute(string name, string op, string value)
112+
{
113+
}
114+
115+
void ISelectorVisitor.Type(string name)
116+
{
117+
}
118+
119+
void ISelectorVisitor.Id(string value)
120+
{
121+
}
122+
123+
void ISelectorVisitor.Child(string name, int step, int offset, ISelector selector)
124+
{
125+
}
126+
127+
void ISelectorVisitor.Class(string name)
128+
{
129+
}
130+
131+
void ISelectorVisitor.PseudoClass(string name)
132+
{
133+
}
134+
135+
void ISelectorVisitor.PseudoElement(string name)
136+
{
137+
}
138+
139+
void ISelectorVisitor.List(IEnumerable<ISelector> selectors)
140+
{
141+
_selectorList = selectors;
142+
}
143+
144+
void ISelectorVisitor.Combinator(IEnumerable<ISelector> selectors, IEnumerable<string> symbols)
145+
{
146+
}
147+
148+
void ISelectorVisitor.Many(IEnumerable<ISelector> selectors)
149+
{
150+
}
151+
152+
sealed class InvalidSelector : ISelector
73153
{
74154
private readonly String _text;
75155

src/AngleSharp.Css/Extensions/StyleCollectionExtensions.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,26 @@ public static ICssStyleDeclaration ComputeCascadedStyle(this IEnumerable<ICssSty
109109

110110
#region Helpers
111111

112-
private static IEnumerable<ICssStyleRule> SortBySpecificity(this IEnumerable<ICssStyleRule> rules, IElement element) =>
113-
rules.Where(m => m.Selector?.Match(element) ?? false).OrderBy(m => m.Selector.Specificity);
112+
private static IEnumerable<ICssStyleRule> SortBySpecificity(this IEnumerable<ICssStyleRule> rules, IElement element)
113+
{
114+
Tuple<ICssStyleRule, Priority> MapPriority(ICssStyleRule rule)
115+
{
116+
if (rule.TryMatch(element, null, out var specificity))
117+
{
118+
return Tuple.Create(rule, specificity);
119+
}
120+
121+
return null;
122+
}
123+
124+
return rules.Select(MapPriority).Where(IsNotNull).OrderBy(GetPriority).Select(GetRule);
125+
}
126+
127+
private static Boolean IsNotNull(Tuple<ICssStyleRule, Priority> item) => item is not null;
128+
129+
private static Priority GetPriority(Tuple<ICssStyleRule, Priority> item) => item.Item2;
130+
131+
private static ICssStyleRule GetRule(Tuple<ICssStyleRule, Priority> item) => item.Item1;
114132

115133
#endregion
116134
}

src/AngleSharp.Css/Parser/CssBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ public TextPosition CreateRules(CssStyleSheet sheet)
491491
token = NextToken();
492492
CollectTrivia(ref token);
493493

494-
if (rule != null)
494+
if (rule is not null)
495495
{
496496
sheet.Add(rule);
497497
}

src/AngleSharp.Css/Parser/CssParser.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ internal ICssStyleSheet ParseStylesheet(TextSource source)
203203
{
204204
var sheet = new CssStyleSheet(_context, source);
205205
var tokenizer = CreateTokenizer(source);
206-
var start = tokenizer.GetCurrentPosition();
207206
var builder = new CssBuilder(_options, tokenizer, _context);
208207
InvokeEventListener(new CssParseEvent(sheet, completed: false));
209208
builder.CreateRules(sheet);

0 commit comments

Comments
 (0)