Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# 1.1.2

- Fixed `StyleAttributeComparer` and `OrderingStyleAttributeComparer` throwing a `NullReferenceException` when comparing a `style` attribute on an element that has no inline CSS style declaration — e.g. a non-HTML (SVG/MathML) element, or any element when the browsing context has no CSS parser registered. Such `style` attributes are now compared by their raw value.

# 1.1.1

- Fixed marking comaprison results in the pipeline so that strategies down the line see if there is a diff. By [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet).
- Fixed marking comparison results in the pipeline so that strategies down the line see if there is a diff. By [@egil](https://github.com/egil) and [@linkdotnet](https://github.com/linkdotnet).

# 1.1.0

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using AngleSharp.Html.Parser;

namespace AngleSharp.Diffing.Strategies.AttributeStrategies;

/// <summary>
/// Builds style-attribute comparisons whose elements are parsed in a browsing context without CSS support.
/// With no <see cref="AngleSharp.Css.Parser.ICssParser"/> registered, <c>IElement.GetStyle()</c> returns
/// <c>null</c> — the situation CSS-less consumers such as bUnit hit when diffing markup that contains inline
/// SVG. The shared <see cref="DiffingTestFixture"/> enables CSS, so it cannot reproduce this on its own.
/// </summary>
internal static class CssLessComparisonFactory
{
public static AttributeComparison ToStyleAttributeComparison(string controlHtml, string testHtml)
{
// Same parser setup as DiffingTestFixture, but deliberately without .WithCss().
var config = Configuration.Default
.With<IHtmlParser>(_ =>
new HtmlParser(
new()
{
IsKeepingSourceReferences = true
},
_));
var context = BrowsingContext.New(config);
var parser = context.GetService<IHtmlParser>()!;
var document = context.OpenNewAsync().Result;

return new AttributeComparison(
ToStyleSource(ComparisonSourceType.Control, controlHtml),
ToStyleSource(ComparisonSourceType.Test, testHtml));

AttributeComparisonSource ToStyleSource(ComparisonSourceType sourceType, string html)
{
var element = parser.ParseFragment(html, document.Body!)[0];
return new("style", element.ToComparisonSource(0, sourceType));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,19 @@ public void Test006(string control, string test)
var comparison = ToAttributeComparison(control, "style", test, "style");
OrderingStyleAttributeComparer.Compare(comparison, CompareResult.Unknown).ShouldBe(CompareResult.Same);
}

[Theory(DisplayName = "Style comparison falls back to the raw value when an element has no CSS style declaration (e.g. inline SVG)")]
[InlineData(@"<svg style=""color:red"">", @"<svg style=""color:red"">", true)]
[InlineData(@"<svg style=""color:red"">", @"<svg style=""color:blue"">", false)]
public void Test007(string control, string test, bool expectedSame)
{
// GetStyle() returns null when the browsing context has no CSS parser; the comparer must not throw.
var comparison = CssLessComparisonFactory.ToStyleAttributeComparison(control, test);

var expected = expectedSame
? CompareResult.Same
: CompareResult.FromDiff(new AttrDiff(comparison, AttrDiffKind.Value));

OrderingStyleAttributeComparer.Compare(comparison, CompareResult.Unknown).ShouldBe(expected);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,19 @@ public void Test005(string control, string test)
var comparison = ToAttributeComparison(control, "style", test, "style");
StyleAttributeComparer.Compare(comparison, CompareResult.Unknown).ShouldBe(CompareResult.Same);
}

[Theory(DisplayName = "Style comparison falls back to the raw value when an element has no CSS style declaration (e.g. inline SVG)")]
[InlineData(@"<svg style=""color:red"">", @"<svg style=""color:red"">", true)]
[InlineData(@"<svg style=""color:red"">", @"<svg style=""color:blue"">", false)]
public void Test006(string control, string test, bool expectedSame)
{
// GetStyle() returns null when the browsing context has no CSS parser; the comparer must not throw.
var comparison = CssLessComparisonFactory.ToStyleAttributeComparison(control, test);

var expected = expectedSame
? CompareResult.Same
: CompareResult.FromDiff(new AttrDiff(comparison, AttrDiffKind.Value));

StyleAttributeComparer.Compare(comparison, CompareResult.Unknown).ShouldBe(expected);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,16 @@ private static CompareResult CompareElementStyle(in AttributeComparison comparis
var (ctrlElm, testElm) = comparison.AttributeElements;
var ctrlStyle = ctrlElm.GetStyle();
var testStyle = testElm.GetStyle();
return CompareCssStyleDeclarations(ctrlStyle, testStyle)

// GetStyle() returns null when an element exposes no inline CSS style declaration — e.g. a non-HTML
// (SVG/MathML) element, or any element when the browsing context has no CSS parser registered. Fall
// back to comparing the raw style attribute values in that case, so such elements are compared by
// value instead of throwing a NullReferenceException.
var areEqual = ctrlStyle is not null && testStyle is not null
? CompareCssStyleDeclarations(ctrlStyle, testStyle)
: string.Equals(comparison.Control.Attribute.Value, comparison.Test.Attribute.Value, StringComparison.Ordinal);

return areEqual
? CompareResult.Same
: CompareResult.FromDiff(new AttrDiff(comparison, AttrDiffKind.Value));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,16 @@ private static CompareResult CompareElementStyle(in AttributeComparison comparis
var (ctrlElm, testElm) = comparison.AttributeElements;
var ctrlStyle = ctrlElm.GetStyle();
var testStyle = testElm.GetStyle();
return CompareCssStyleDeclarations(ctrlStyle, testStyle)

// GetStyle() returns null when an element exposes no inline CSS style declaration — e.g. a non-HTML
// (SVG/MathML) element, or any element when the browsing context has no CSS parser registered. Fall
// back to comparing the raw style attribute values in that case, so such elements are compared by
// value instead of throwing a NullReferenceException.
var areEqual = ctrlStyle is not null && testStyle is not null
? CompareCssStyleDeclarations(ctrlStyle, testStyle)
: string.Equals(comparison.Control.Attribute.Value, comparison.Test.Attribute.Value, StringComparison.Ordinal);

return areEqual
? CompareResult.Same
: CompareResult.FromDiff(new AttrDiff(comparison, AttrDiffKind.Value));
}
Expand Down