Skip to content

Commit c39ac49

Browse files
committed
Improved CSS API
1 parent 183022f commit c39ac49

29 files changed

+430
-96
lines changed

doc/API.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,17 @@ The `ICssValue` interface is split in various interfaces with respect to their u
2828
![The CSSOM Value Tree](cssom-value-tree.png)
2929

3030
A converter may also implement the `IValueAggregator` interface, which indicates that the declaration behind it is actually a shorthand that can additionally merge values into a shorthand representation or split the shorthand representation into the atoms described by the shorthand.
31+
32+
For convenience some (extension) methods have been introduced to make working with the CSSOM (specifically values) simpler. The shown extension methods are placed in the `AngleSharp.Css.Dom` namespace.
33+
34+
```cs
35+
// sheet is a stylesheet, e.g., obtained from a document, with content:
36+
// p > a { border: 1px solid red }
37+
var rule = sheet.GetStyleRuleWith("p>a");
38+
var color = rule.GetValueOf("border-right-color").AsRgba();
39+
// the color is an Int32, e.g., 0xFF_FF - same as rgba(255, 0, 0, 1)
40+
```
41+
42+
The idea behind `GetStyleRuleWith` is to get the first top-level style rule that matches the given selector exactly. The text of the selector does not have to be equal to the text of the selector in the stylesheet, but it needs to be equal *semantically*, i.e., in the provided example the spaces do not matter as the semantics are not influenced by them.
43+
44+
The `GetValueOf` obtains the `ICssValue` instance behind the property with the given name. The `AsRgba` works (like all the other `As*` extension methods) against the `ICssValue` to get an elementary value out of it. This works in simple cases, but will fail, e.g., when there are multiple values available or when the primitive value is hidden in a composite one.

src/AngleSharp.Css.Tests/Styling/CssSheet.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace AngleSharp.Css.Tests.Styling
33
using AngleSharp.Css.Converters;
44
using AngleSharp.Css.Dom;
55
using AngleSharp.Css.Parser;
6+
using AngleSharp.Css.Values;
67
using NUnit.Framework;
78
using System;
89
using System.IO;
@@ -987,6 +988,24 @@ public void CssStyleSheetInsertShouldSetParentStyleSheetCorrectly()
987988
Assert.AreEqual(s, s.Rules[0].Owner);
988989
}
989990

991+
[Test]
992+
public void GetImageRefOfACertainDeclarationFromSheet()
993+
{
994+
var s = ParseStyleSheet("body { background: url(http://example.com/foo.png) no-repeat }");
995+
var rule = s.GetStyleRuleWith("body");
996+
var url = rule.GetValueOf("background-image").AsUrl();
997+
Assert.AreEqual("http://example.com/foo.png", url);
998+
}
999+
1000+
[Test]
1001+
public void GetBorderRightColorOfACertainDeclarationFromSheet()
1002+
{
1003+
var s = ParseStyleSheet("p > a { border: 1px solid red }");
1004+
var rule = s.GetStyleRuleWith("p > a");
1005+
var color = rule.GetValueOf("border-right-color").AsRgba();
1006+
Assert.AreEqual(0x00_00_ff_ff, color);
1007+
}
1008+
9901009
[Test]
9911010
public void CssColorFunctionsMixAllShouldWork()
9921011
{

src/AngleSharp.Css/Dom/ICssProperties .cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace AngleSharp.Css.Dom
1+
namespace AngleSharp.Css.Dom
22
{
33
using AngleSharp.Attributes;
44
using System;
@@ -33,6 +33,13 @@ public interface ICssProperties : IEnumerable<ICssProperty>
3333
[DomName("getPropertyValue")]
3434
String GetPropertyValue(String propertyName);
3535

36+
/// <summary>
37+
/// Gets the full property model for the given property name.
38+
/// </summary>
39+
/// <param name="propertyName">The name of the property to get.</param>
40+
/// <returns>The computed or stored property model.</returns>
41+
ICssProperty GetProperty(String propertyName);
42+
3643
/// <summary>
3744
/// Returns the optional priority, "important" or null, if no priority
3845
/// has been set.

src/AngleSharp.Css/Dom/Internal/CssStyleDeclaration.cs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,21 @@ public String CssText
7474

7575
#region Methods
7676

77+
public ICssProperty GetProperty(String name)
78+
{
79+
for (var i = 0; i < _declarations.Count; i++)
80+
{
81+
var declaration = _declarations[i];
82+
83+
if (declaration.Name.Isi(name))
84+
{
85+
return declaration;
86+
}
87+
}
88+
89+
return GetPropertyShorthand(name);
90+
}
91+
7792
public void SetParent(ICssRule parent) => _parent = parent;
7893

7994
public void Update(String value)
@@ -290,24 +305,6 @@ public void SetProperty(String propertyName, String propertyValue, String priori
290305

291306
#region Internal Methods
292307

293-
internal ICssProperty GetProperty(String name)
294-
{
295-
for (var i = 0; i < _declarations.Count; i++)
296-
{
297-
var declaration = _declarations[i];
298-
299-
if (declaration.Name.Isi(name))
300-
{
301-
return declaration;
302-
}
303-
}
304-
305-
return GetPropertyShorthand(name);
306-
}
307-
308-
private ICssProperty GetPropertyShorthand(String name) =>
309-
TryCreateShorthand(name, Enumerable.Empty<String>(), new List<String>(), true);
310-
311308
internal void SetDeclarations(IEnumerable<ICssProperty> decls) =>
312309
ChangeDeclarations(decls, m => false, (o, n) => !o.IsImportant || n.IsImportant);
313310

@@ -318,6 +315,9 @@ internal void UpdateDeclarations(IEnumerable<ICssProperty> decls) =>
318315

319316
#region Helpers
320317

318+
private ICssProperty GetPropertyShorthand(String name) =>
319+
TryCreateShorthand(name, Enumerable.Empty<String>(), new List<String>(), true);
320+
321321
private ICssProperty CreateProperty(String propertyName) =>
322322
GetProperty(propertyName) ?? _context.CreateProperty(propertyName);
323323

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

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,15 @@ internal CssDeclarationRule(ICssStyleSheet owner, CssRuleType type, String name,
4242

4343
#region Methods
4444

45-
public String GetPropertyValue(String propertyName)
46-
{
47-
return GetValue(propertyName);
48-
}
45+
public ICssProperty GetProperty(String propertyName) =>
46+
_declarations.Find(m => m.Name.Is(propertyName));
4947

50-
public String GetPropertyPriority(String propertyName)
51-
{
52-
return null;
53-
}
48+
public String GetPropertyValue(String propertyName) => GetValue(propertyName);
5449

55-
public void SetProperty(String propertyName, String propertyValue, String priority = null)
56-
{
50+
public String GetPropertyPriority(String propertyName) => null;
51+
52+
public void SetProperty(String propertyName, String propertyValue, String priority = null) =>
5753
SetValue(propertyName, propertyValue);
58-
}
5954

6055
public String RemoveProperty(String propertyName)
6156
{
@@ -73,15 +68,9 @@ public String RemoveProperty(String propertyName)
7368
return null;
7469
}
7570

76-
public IEnumerator<ICssProperty> GetEnumerator()
77-
{
78-
return _declarations.GetEnumerator();
79-
}
71+
public IEnumerator<ICssProperty> GetEnumerator() => _declarations.GetEnumerator();
8072

81-
IEnumerator IEnumerable.GetEnumerator()
82-
{
83-
return GetEnumerator();
84-
}
73+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
8574

8675
public override void ToCss(TextWriter writer, IStyleFormatter formatter)
8776
{
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
namespace AngleSharp.Css.Dom
2+
{
3+
using AngleSharp.Css.Parser;
4+
using AngleSharp.Text;
5+
using System;
6+
using System.Linq;
7+
8+
/// <summary>
9+
/// CSSOM API extension methods.
10+
/// </summary>
11+
public static class CssOmExtensions
12+
{
13+
/// <summary>
14+
/// Gets the style rule with the provided selector text.
15+
/// </summary>
16+
/// <param name="rule">The container rule.</param>
17+
/// <param name="selectorText">The selector text to look for.</param>
18+
/// <returns>The style rule, if any.</returns>
19+
public static ICssStyleRule GetStyleRuleWith(this ICssGroupingRule rule, String selectorText) =>
20+
rule.Rules.GetStyleRuleWith(selectorText, rule.Owner?.Context);
21+
22+
/// <summary>
23+
/// Gets the style rule with the provided selector text.
24+
/// </summary>
25+
/// <param name="sheet">The sheet.</param>
26+
/// <param name="selectorText">The selector text to look for.</param>
27+
/// <returns>The style rule, if any.</returns>
28+
public static ICssStyleRule GetStyleRuleWith(this ICssStyleSheet sheet, String selectorText) =>
29+
sheet.Rules.GetStyleRuleWith(selectorText, sheet.Context);
30+
31+
/// <summary>
32+
/// Gets the style rule with the provided selector text.
33+
/// </summary>
34+
/// <param name="rules">The rules to look in.</param>
35+
/// <param name="selectorText">The selector text to look for.</param>
36+
/// <param name="context">The context for normalizing the CSS selector.</param>
37+
/// <returns>The style rule, if any.</returns>
38+
public static ICssStyleRule GetStyleRuleWith(this ICssRuleList rules, String selectorText, IBrowsingContext context = null)
39+
{
40+
var styleRules = rules.OfType<ICssStyleRule>();
41+
var parser = context?.GetService<ICssSelectorParser>();
42+
var normalizedSelectorText = parser?.ParseSelector(selectorText)?.Text ?? selectorText;
43+
44+
foreach (var rule in styleRules)
45+
{
46+
if (rule.SelectorText.Is(normalizedSelectorText))
47+
{
48+
return rule;
49+
}
50+
}
51+
52+
return null;
53+
}
54+
55+
/// <summary>
56+
/// Gets the ICssValue of a property with the given name.
57+
/// </summary>
58+
/// <param name="rule">The rule to extend.</param>
59+
/// <param name="propertyName">The property to obtain.</param>
60+
/// <returns>The value of the provided property, if any.</returns>
61+
public static ICssValue GetValueOf(this ICssStyleRule rule, String propertyName)
62+
{
63+
rule = rule ?? throw new ArgumentNullException(nameof(rule));
64+
var property = rule.Style.GetProperty(propertyName);
65+
return property?.RawValue;
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)