Skip to content

Commit 174630e

Browse files
authored
Merge pull request #1233 from Rtwo-Dtwo/feature/#1232-add-svg-style-element
Add `ISvgStyleElement` and the associated implementation
2 parents 98a48e5 + d5de4db commit 174630e

File tree

4 files changed

+213
-10
lines changed

4 files changed

+213
-10
lines changed

src/AngleSharp.Core.Tests/Css/StyleExtensions.cs

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
namespace AngleSharp.Core.Tests.Css
22
{
3+
using AngleSharp.Css;
4+
using AngleSharp.Css.Dom;
5+
using AngleSharp.Dom;
6+
using AngleSharp.Html.Parser;
7+
using AngleSharp.Io;
8+
using AngleSharp.Text;
9+
using NUnit.Framework;
310
using System;
411
using System.Collections;
512
using System.Collections.Generic;
613
using System.IO;
714
using System.Linq;
815
using System.Threading;
916
using System.Threading.Tasks;
10-
using AngleSharp.Css;
11-
using AngleSharp.Css.Dom;
12-
using AngleSharp.Html.Parser;
13-
using AngleSharp.Io;
14-
using Dom;
15-
using Mocks;
16-
using NUnit.Framework;
17-
using Text;
1817

1918
public class StylesheetExtensions
2019
{
@@ -24,7 +23,7 @@ public class StylesheetExtensions
2423
[TestCase(10000)]
2524
public void TestStyleSheetsDoesNotThrowStackOverflowException(Int32 count)
2625
{
27-
var thread = new Thread( () =>
26+
var thread = new Thread(() =>
2827
{
2928
var beginTags = String.Join("", Enumerable.Repeat("<div>", count));
3029
var endTags = String.Join("", Enumerable.Repeat("</div>", count));
@@ -53,7 +52,7 @@ public async Task AppendingStylesheetLinkShouldLoadResource()
5352
var stylingService = new MockStylingService();
5453
var cfg = Configuration.Default
5554
.WithOnly<IStylingService>(stylingService)
56-
.WithMockRequester(req => request = req );
55+
.WithMockRequester(req => request = req);
5756
var html = @"<!doctype html><head><link rel=stylesheet href=/mock-stylesheet-1.css /></head><body></body>";
5857
var document = await BrowsingContext.New(cfg).OpenAsync(m => m.Content(html));
5958

@@ -73,6 +72,49 @@ public async Task AppendingStylesheetLinkShouldLoadResource()
7372
Assert.IsNotNull(request);
7473
Assert.AreEqual("/mock-stylesheet-2.css", request.Address.PathName);
7574
}
75+
76+
[Test]
77+
public async Task HtmlInlineStyleSheetShouldLoad()
78+
{
79+
var stylingService = new MockStylingService();
80+
var cfg = Configuration.Default.WithOnly<IStylingService>(stylingService);
81+
var html = @"<!doctype html><head><style>p { color: blue; }</style></head><body></body>";
82+
var document = await BrowsingContext.New(cfg).OpenAsync(m => m.Content(html));
83+
84+
Assert.AreEqual(1, document.Head.ChildNodes.Length);
85+
86+
var htmlHeadStyle = document.Head.ChildNodes[0];
87+
Assert.AreEqual("style", htmlHeadStyle.GetTagName());
88+
Assert.AreEqual(NodeType.Element, htmlHeadStyle.NodeType);
89+
90+
var style = htmlHeadStyle as ILinkStyle;
91+
Assert.IsNotNull(style);
92+
Assert.AreEqual(style.Sheet.OwnerNode.TextContent, "p { color: blue; }");
93+
}
94+
95+
[Test]
96+
public async Task SvgInlineStyleSheetShouldLoad()
97+
{
98+
var stylingService = new MockStylingService();
99+
var cfg = Configuration.Default.WithOnly<IStylingService>(stylingService);
100+
var svg = @"<svg><style>circle { fill: gold; }</style></svg>";
101+
var document = await BrowsingContext.New(cfg).OpenAsync(m => m.Content(svg));
102+
103+
Assert.AreEqual(1, document.Body.ChildNodes.Length);
104+
105+
var svgBodySvg = document.Body.ChildNodes[0];
106+
Assert.AreEqual(1, svgBodySvg.ChildNodes.Length);
107+
Assert.AreEqual("svg", svgBodySvg.GetTagName());
108+
Assert.AreEqual(NodeType.Element, svgBodySvg.NodeType);
109+
110+
var svgBodySvgStyle = svgBodySvg.ChildNodes[0];
111+
Assert.AreEqual("style", svgBodySvgStyle.GetTagName());
112+
Assert.AreEqual(NodeType.Element, svgBodySvgStyle.NodeType);
113+
114+
var style = svgBodySvgStyle as ILinkStyle;
115+
Assert.IsNotNull(style);
116+
Assert.AreEqual(style.Sheet.OwnerNode.TextContent, "circle { fill: gold; }");
117+
}
76118
}
77119

78120
internal class MockStylingService : IStylingService
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace AngleSharp.Svg.Dom
2+
{
3+
using AngleSharp.Attributes;
4+
using AngleSharp.Dom;
5+
using System;
6+
7+
/// <summary>
8+
/// Represents a style SVG element.
9+
/// </summary>
10+
[DomName("SVGStyleElement")]
11+
public interface ISvgStyleElement : ISvgElement, ILinkStyle
12+
{
13+
/// <summary>
14+
/// Gets or sets if the style is enabled or disabled.
15+
/// </summary>
16+
[DomName("disabled")]
17+
Boolean IsDisabled { get; set; }
18+
19+
/// <summary>
20+
/// Gets or sets the use with one or more target media.
21+
/// </summary>
22+
[DomName("media")]
23+
String? Media { get; set; }
24+
25+
/// <summary>
26+
/// Gets or sets the content type of the style sheet language.
27+
/// </summary>
28+
[DomName("type")]
29+
String? Type { get; set; }
30+
}
31+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
namespace AngleSharp.Svg.Dom
2+
{
3+
using AngleSharp.Css;
4+
using AngleSharp.Dom;
5+
using AngleSharp.Io;
6+
using System;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
/// <summary>
11+
/// Represents the SVG style element.
12+
/// </summary>
13+
sealed class SvgStyleElement : SvgElement, ISvgStyleElement
14+
{
15+
#region Fields
16+
17+
private IStyleSheet? _sheet;
18+
19+
#endregion
20+
21+
#region ctor
22+
23+
public SvgStyleElement(Document owner, String? prefix = null)
24+
: base(owner, TagNames.Style, prefix, NodeFlags.Special | NodeFlags.LiteralText)
25+
{
26+
}
27+
28+
#endregion
29+
30+
#region Properties
31+
32+
public IStyleSheet? Sheet => _sheet;
33+
34+
public Boolean IsDisabled
35+
{
36+
get => this.GetBoolAttribute(AttributeNames.Disabled);
37+
set
38+
{
39+
this.SetBoolAttribute(AttributeNames.Disabled, value);
40+
41+
if (_sheet != null)
42+
{
43+
_sheet.IsDisabled = value;
44+
}
45+
}
46+
}
47+
48+
public String? Media
49+
{
50+
get => this.GetOwnAttribute(AttributeNames.Media);
51+
set => this.SetOwnAttribute(AttributeNames.Media, value);
52+
}
53+
54+
public String? Type
55+
{
56+
get => this.GetOwnAttribute(AttributeNames.Type);
57+
set => this.SetOwnAttribute(AttributeNames.Type, value);
58+
}
59+
60+
#endregion
61+
62+
#region Internal Methods
63+
64+
internal override void SetupElement()
65+
{
66+
base.SetupElement();
67+
UpdateSheet();
68+
}
69+
70+
internal void UpdateMedia(String value)
71+
{
72+
if (_sheet != null)
73+
{
74+
_sheet.Media.MediaText = value;
75+
}
76+
}
77+
78+
#endregion
79+
80+
#region Helpers
81+
82+
protected override void NodeIsInserted(Node newNode)
83+
{
84+
base.NodeIsInserted(newNode);
85+
UpdateSheet();
86+
}
87+
88+
protected override void NodeIsRemoved(Node removedNode, Node? oldPreviousSibling)
89+
{
90+
base.NodeIsRemoved(removedNode, oldPreviousSibling);
91+
UpdateSheet();
92+
}
93+
94+
private void UpdateSheet()
95+
{
96+
var document = Owner;
97+
98+
if (document != null)
99+
{
100+
var context = Context;
101+
var type = Type ?? MimeTypeNames.Css;
102+
var engine = context.GetStyling(type);
103+
104+
if (engine != null)
105+
{
106+
var task = CreateSheetAsync(engine, document);
107+
document.DelayLoad(task);
108+
}
109+
}
110+
}
111+
112+
private async Task CreateSheetAsync(IStylingService engine, IDocument document)
113+
{
114+
var cancel = CancellationToken.None;
115+
var response = VirtualResponse.Create(res => res.Content(TextContent).Address(default(Url)));
116+
var options = new StyleOptions(document)
117+
{
118+
Element = this,
119+
IsDisabled = IsDisabled,
120+
IsAlternate = false,
121+
};
122+
var task = engine.ParseStylesheetAsync(response, options, cancel);
123+
_sheet = await task.ConfigureAwait(false);
124+
UpdateMedia(Media!);
125+
}
126+
127+
#endregion
128+
}
129+
}

src/AngleSharp/Svg/SvgElementFactory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ sealed class SvgElementFactory : IElementFactory<Document, SvgElement>
1919
{ TagNames.Desc, (document, prefix) => new SvgDescElement(document, prefix) },
2020
{ TagNames.ForeignObject, (document, prefix) => new SvgForeignObjectElement(document, prefix) },
2121
{ TagNames.Title, (document, prefix) => new SvgTitleElement(document, prefix) },
22+
{ TagNames.Style, (document, prefix) => new SvgStyleElement(document, prefix) },
2223
};
2324

2425
internal static readonly SvgElementFactory Instance = new();

0 commit comments

Comments
 (0)