Larger and green";
+ var document = ParseDocument(source);
+ var window = document.DefaultView;
var element = document.QuerySelector(".bar");
- Assert.IsNotNull(element);
+ var style = window.GetComputedStyle(element);
+
+ Assert.AreEqual("1.4rem", style.GetFontSize());
+ }
+
+ [Test]
+ public void SimpleSelectorNestingOverwritten()
+ {
+ var source = @"
Larger and green";
+ var document = ParseDocument(source);
+ var window = document.DefaultView;
+ var element = document.QuerySelector(".bar");
var style = window.GetComputedStyle(element);
- Assert.IsNotNull(style);
Assert.AreEqual("1.4rem", style.GetFontSize());
}
+
+ [Test]
+ public void CombinedSelectorNesting()
+ {
+ var source = @"
green";
+ var document = ParseDocument(source);
+ var window = document.DefaultView;
+ var element = document.QuerySelector(".bar");
+ var style = window.GetComputedStyle(element);
+
+ Assert.AreEqual("rgba(0, 128, 0, 1)", style.GetColor());
+ }
+
+ [Test]
+ public void ReversedSelectorNesting()
+ {
+ var source = @"
- green";
+ var document = ParseDocument(source);
+ var window = document.DefaultView;
+ var element = document.QuerySelector("li");
+ var style = window.GetComputedStyle(element);
+
+ Assert.AreEqual("rgba(0, 128, 0, 1)", style.GetColor());
+ }
}
}
diff --git a/src/AngleSharp.Css/AngleSharp.Css.csproj b/src/AngleSharp.Css/AngleSharp.Css.csproj
index 7170241b..a821523d 100644
--- a/src/AngleSharp.Css/AngleSharp.Css.csproj
+++ b/src/AngleSharp.Css/AngleSharp.Css.csproj
@@ -21,7 +21,7 @@
-
+
diff --git a/src/AngleSharp.Css/Dom/Internal/ReferencedNestedSelector.cs b/src/AngleSharp.Css/Dom/Internal/ReferencedNestedSelector.cs
new file mode 100644
index 00000000..771cf08c
--- /dev/null
+++ b/src/AngleSharp.Css/Dom/Internal/ReferencedNestedSelector.cs
@@ -0,0 +1,29 @@
+namespace AngleSharp.Css.Dom
+{
+ using AngleSharp.Dom;
+ using System;
+
+ internal class ReferencedNestedSelector : ISelector
+ {
+ private readonly ISelector _referenced;
+
+ public ReferencedNestedSelector(ISelector referenced)
+ {
+ _referenced = referenced;
+ }
+
+ public String Text => "&";
+
+ public Priority Specificity => _referenced.Specificity;
+
+ public void Accept(ISelectorVisitor visitor)
+ {
+ // Right now we have nothing here.
+ }
+
+ public Boolean Match(IElement element, IElement scope)
+ {
+ return _referenced.Match(element, scope);
+ }
+ }
+}
diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs
index 60901066..b9b9a135 100644
--- a/src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs
+++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs
@@ -20,6 +20,7 @@ sealed class CssStyleRule : CssRule, ICssStyleRule, ISelectorVisitor
private readonly CssRuleList _rules;
private ISelector _selector;
private IEnumerable _selectorList;
+ private Boolean _nested;
#endregion
@@ -37,6 +38,12 @@ internal CssStyleRule(ICssStyleSheet owner)
#region Properties
+ public Boolean IsNested
+ {
+ get => _nested;
+ set => _nested = value;
+ }
+
public ISelector Selector
{
get => _selector;
@@ -85,13 +92,37 @@ protected override void ReplaceWith(ICssRule rule)
public Boolean TryMatch(IElement element, IElement? scope, out Priority specificity)
{
+ specificity = Priority.Zero;
+ scope ??= element?.Owner!.DocumentElement;
+
+ if (!_nested && Parent is CssStyleRule parent)
+ {
+ var pe = element;
+
+ do
+ {
+ if (pe == scope)
+ {
+ return false;
+ }
+
+ pe = pe.ParentElement;
+
+ if (pe is null)
+ {
+ return false;
+ }
+ }
+ while (!parent.TryMatch(pe, scope, out specificity));
+ }
+
if (_selectorList is not null)
{
foreach (var selector in _selectorList.OrderByDescending(m => m.Specificity))
{
if (selector.Match(element, scope))
{
- specificity = selector.Specificity;
+ specificity += selector.Specificity;
return true;
}
}
@@ -99,11 +130,10 @@ public Boolean TryMatch(IElement element, IElement? scope, out Priority specific
if (_selector is not null && _selector.Match(element, scope))
{
- specificity = _selector.Specificity;
+ specificity += _selector.Specificity;
return true;
}
- specificity = default;
return false;
}
@@ -117,7 +147,7 @@ public override void ToCss(TextWriter writer, IStyleFormatter formatter)
#region Selector
- private void ChangeSelector(ISelector value)
+ internal void ChangeSelector(ISelector value)
{
_selectorList = null;
_selector = value;
diff --git a/src/AngleSharp.Css/Parser/CssBuilder.cs b/src/AngleSharp.Css/Parser/CssBuilder.cs
index 78a342e4..66ccf126 100644
--- a/src/AngleSharp.Css/Parser/CssBuilder.cs
+++ b/src/AngleSharp.Css/Parser/CssBuilder.cs
@@ -526,8 +526,21 @@ public void CreateDeclarationWith(ICssProperties properties, ref CssToken token)
if (!_options.IsExcludingNesting && token.IsPotentiallyNested() && properties is ICssStyleDeclaration decl && decl.Parent is CssStyleRule style)
{
+ var factory = _context.GetService();
var rule = new CssStyleRule(style.Owner);
+ var previous = factory.Unregister("&");
+ factory.Register("&", (_, _, _, _) =>
+ {
+ rule.IsNested = true;
+ return new ReferencedNestedSelector(style.Selector);
+ });
var result = CreateStyle(rule, token);
+ factory.Unregister("&");
+
+ if (previous is not null)
+ {
+ factory.Register("&", previous);
+ }
if (result is not null)
{
diff --git a/src/AngleSharp.Performance.Css/AngleSharp.Performance.Css.csproj b/src/AngleSharp.Performance.Css/AngleSharp.Performance.Css.csproj
index dd1ffa2f..6b9a1aba 100644
--- a/src/AngleSharp.Performance.Css/AngleSharp.Performance.Css.csproj
+++ b/src/AngleSharp.Performance.Css/AngleSharp.Performance.Css.csproj
@@ -21,7 +21,7 @@
-
+
\ No newline at end of file