Working on new media list parser

This commit is contained in:
Florian Rappl 2023-06-10 17:52:35 +02:00
Родитель 6ab71660fa
Коммит c8321b81d4
8 изменённых файлов: 297 добавлений и 44 удалений

Просмотреть файл

@ -16,7 +16,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AngleSharp.Xml" Version="0.17.0" />
<PackageReference Include="AngleSharp.Xml" Version="1.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.0" />

Просмотреть файл

@ -319,9 +319,9 @@ li{background:orange;}
/*#\{\}{background:lime;}*/");
Assert.AreEqual(42, sheet.Rules.Length);
Assert.AreEqual(@".:`(", ((ICssStyleRule)sheet.Rules[0]).SelectorText);
Assert.AreEqual(@".1a2b3c", ((ICssStyleRule)sheet.Rules[1]).SelectorText);
Assert.AreEqual(@"##fake-id", ((ICssStyleRule)sheet.Rules[2]).SelectorText);
Assert.AreEqual(@".\:\`\(", ((ICssStyleRule)sheet.Rules[0]).SelectorText);
Assert.AreEqual(@".\31 a2b3c", ((ICssStyleRule)sheet.Rules[1]).SelectorText);
Assert.AreEqual(@"#\#fake-id", ((ICssStyleRule)sheet.Rules[2]).SelectorText);
Assert.AreEqual(@"#---", ((ICssStyleRule)sheet.Rules[3]).SelectorText);
Assert.AreEqual(@"#-a-b-c-", ((ICssStyleRule)sheet.Rules[4]).SelectorText);
Assert.AreEqual(@"#©", ((ICssStyleRule)sheet.Rules[5]).SelectorText);
@ -346,57 +346,57 @@ li{background:orange;}
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[14]).Style["background"]);
Assert.AreEqual(@"#𝄞♪♩♫♬", ((ICssStyleRule)sheet.Rules[15]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[15]).Style["background"]);
Assert.AreEqual(@"#?", ((ICssStyleRule)sheet.Rules[16]).SelectorText);
Assert.AreEqual(@"#\?", ((ICssStyleRule)sheet.Rules[16]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[16]).Style["background"]);
Assert.AreEqual(@"#@", ((ICssStyleRule)sheet.Rules[17]).SelectorText);
Assert.AreEqual(@"#\@", ((ICssStyleRule)sheet.Rules[17]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[17]).Style["background"]);
Assert.AreEqual(@"#.", ((ICssStyleRule)sheet.Rules[18]).SelectorText);
Assert.AreEqual(@"#\.", ((ICssStyleRule)sheet.Rules[18]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[18]).Style["background"]);
Assert.AreEqual(@"#:)", ((ICssStyleRule)sheet.Rules[19]).SelectorText);
Assert.AreEqual(@"#\:\)", ((ICssStyleRule)sheet.Rules[19]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[19]).Style["background"]);
Assert.AreEqual(@"#:`(", ((ICssStyleRule)sheet.Rules[20]).SelectorText);
Assert.AreEqual(@"#\:\`\(", ((ICssStyleRule)sheet.Rules[20]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[20]).Style["background"]);
Assert.AreEqual(@"#123", ((ICssStyleRule)sheet.Rules[21]).SelectorText);
Assert.AreEqual(@"#\31 23", ((ICssStyleRule)sheet.Rules[21]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[21]).Style["background"]);
Assert.AreEqual(@"#1a2b3c", ((ICssStyleRule)sheet.Rules[22]).SelectorText);
Assert.AreEqual(@"#\31 a2b3c", ((ICssStyleRule)sheet.Rules[22]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[22]).Style["background"]);
Assert.AreEqual(@"#<p>", ((ICssStyleRule)sheet.Rules[23]).SelectorText);
Assert.AreEqual(@"#\<p\>", ((ICssStyleRule)sheet.Rules[23]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[23]).Style["background"]);
Assert.AreEqual(@"#<><<<>><>", ((ICssStyleRule)sheet.Rules[24]).SelectorText);
Assert.AreEqual(@"#\<\>\<\<\<\>\>\<\>", ((ICssStyleRule)sheet.Rules[24]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[24]).Style["background"]);
Assert.AreEqual(@"#++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.", ((ICssStyleRule)sheet.Rules[25]).SelectorText);
Assert.AreEqual("#\\+\\+\\+\\+\\+\\+\\+\\+\\+\\+\\[\\>\\+\\+\\+\\+\\+\\+\\+\\>\\+\\+\\+\\+\\+\\+\\+\\+\\+\\+\\>\\+\\+\\+\\>\\+\\<\\<\\<\\<-\\]\\>\\+\\+\\.\\>\\+\\.\\+\\+\\+\\+\\+\\+\\+\\.\\.\\+\\+\\+\\.\\>\\+\\+\\.\\<\\<\\+\\+\\+\\+\\+\\+\\+\\+\\+\\+\\+\\+\\+\\+\\+\\.\\>\\.\\+\\+\\+\\.------\\.--------\\.\\>\\+\\.\\>\\.", ((ICssStyleRule)sheet.Rules[25]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[25]).Style["background"]);
Assert.AreEqual(@"##", ((ICssStyleRule)sheet.Rules[26]).SelectorText);
Assert.AreEqual(@"#\#", ((ICssStyleRule)sheet.Rules[26]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[26]).Style["background"]);
Assert.AreEqual(@"###", ((ICssStyleRule)sheet.Rules[27]).SelectorText);
Assert.AreEqual(@"#\#\#", ((ICssStyleRule)sheet.Rules[27]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[27]).Style["background"]);
Assert.AreEqual(@"##.#.#", ((ICssStyleRule)sheet.Rules[28]).SelectorText);
Assert.AreEqual(@"#\#\.\#\.\#", ((ICssStyleRule)sheet.Rules[28]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[28]).Style["background"]);
Assert.AreEqual(@"#_", ((ICssStyleRule)sheet.Rules[29]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[29]).Style["background"]);
Assert.AreEqual(@"#.fake-class", ((ICssStyleRule)sheet.Rules[30]).SelectorText);
Assert.AreEqual(@"#\.fake-class", ((ICssStyleRule)sheet.Rules[30]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[30]).Style["background"]);
Assert.AreEqual(@"#foo.bar", ((ICssStyleRule)sheet.Rules[31]).SelectorText);
Assert.AreEqual(@"#foo\.bar", ((ICssStyleRule)sheet.Rules[31]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[31]).Style["background"]);
Assert.AreEqual(@"#:hover", ((ICssStyleRule)sheet.Rules[32]).SelectorText);
Assert.AreEqual(@"#\:hover", ((ICssStyleRule)sheet.Rules[32]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[32]).Style["background"]);
Assert.AreEqual(@"#:hover:focus:active", ((ICssStyleRule)sheet.Rules[33]).SelectorText);
Assert.AreEqual(@"#\:hover\:focus\:active", ((ICssStyleRule)sheet.Rules[33]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[33]).Style["background"]);
Assert.AreEqual(@"#[attr=value]", ((ICssStyleRule)sheet.Rules[34]).SelectorText);
Assert.AreEqual(@"#\[attr\=value\]", ((ICssStyleRule)sheet.Rules[34]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[34]).Style["background"]);
Assert.AreEqual(@"#f/o/o", ((ICssStyleRule)sheet.Rules[35]).SelectorText);
Assert.AreEqual(@"#f\/o\/o", ((ICssStyleRule)sheet.Rules[35]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[35]).Style["background"]);
Assert.AreEqual(@"#f\o\o", ((ICssStyleRule)sheet.Rules[36]).SelectorText);
Assert.AreEqual(@"#f\\o\\o", ((ICssStyleRule)sheet.Rules[36]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[36]).Style["background"]);
Assert.AreEqual(@"#f*o*o", ((ICssStyleRule)sheet.Rules[37]).SelectorText);
Assert.AreEqual(@"#f\*o\*o", ((ICssStyleRule)sheet.Rules[37]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[37]).Style["background"]);
Assert.AreEqual(@"#f!o!o", ((ICssStyleRule)sheet.Rules[38]).SelectorText);
Assert.AreEqual(@"#f\!o\!o", ((ICssStyleRule)sheet.Rules[38]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[38]).Style["background"]);
Assert.AreEqual(@"#f'o'o", ((ICssStyleRule)sheet.Rules[39]).SelectorText);
Assert.AreEqual(@"#f\'o\'o", ((ICssStyleRule)sheet.Rules[39]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[39]).Style["background"]);
Assert.AreEqual(@"#f~o~o", ((ICssStyleRule)sheet.Rules[40]).SelectorText);
Assert.AreEqual(@"#f\~o\~o", ((ICssStyleRule)sheet.Rules[40]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[40]).Style["background"]);
Assert.AreEqual(@"#f+o+o", ((ICssStyleRule)sheet.Rules[41]).SelectorText);
Assert.AreEqual(@"#f\+o\+o", ((ICssStyleRule)sheet.Rules[41]).SelectorText);
Assert.AreEqual(@"rgba(0, 255, 0, 1)", ((ICssStyleRule)sheet.Rules[41]).Style["background"]);
}

Просмотреть файл

@ -21,7 +21,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.0.0" />
<PackageReference Include="AngleSharp" Version="1.0.3" />
</ItemGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">

Просмотреть файл

@ -18,6 +18,7 @@ namespace AngleSharp.Css.Dom
private readonly String _type;
private readonly Boolean _exclusive;
private readonly Boolean _inverse;
private readonly String _connector;
#endregion
@ -30,7 +31,7 @@ namespace AngleSharp.Css.Dom
/// <param name="inverse">Specifies if it should be inverted.</param>
/// <param name="exclusive">Specifies if the rule is exclusive.</param>
public CssMedium(String type, Boolean inverse, Boolean exclusive)
: this(type, inverse, exclusive, Enumerable.Empty<IMediaFeature>())
: this(type, inverse, exclusive, Enumerable.Empty<IMediaFeature>(), "and")
{
}
@ -41,12 +42,14 @@ namespace AngleSharp.Css.Dom
/// <param name="inverse">Specifies if it should be inverted.</param>
/// <param name="exclusive">Specifies if the rule is exclusive.</param>
/// <param name="features">The features of the medium.</param>
public CssMedium(String type, Boolean inverse, Boolean exclusive, IEnumerable<IMediaFeature> features)
/// <param name="connector">The connector of the features ("and" or "or").</param>
public CssMedium(String type, Boolean inverse, Boolean exclusive, IEnumerable<IMediaFeature> features, String connector)
{
_features = new List<IMediaFeature>(features);
_type = type;
_inverse = inverse;
_exclusive = exclusive;
_connector = connector;
}
#endregion
@ -63,6 +66,11 @@ namespace AngleSharp.Css.Dom
/// </summary>
public String Type => _type;
/// <summary>
/// Gets the connector of the contained features.
/// </summary>
public String Connector => _connector;
/// <summary>
/// Gets if the medium is exclusive to other media.
/// </summary>
@ -76,7 +84,7 @@ namespace AngleSharp.Css.Dom
/// <summary>
/// Gets the constraints - i.e., the stringified features.
/// </summary>
public String Constraints => String.Join(" and ", Features.Select(m => m.ToCss()));
public String Constraints => String.Join($" {_connector} ", Features.Select(m => m.ToCss()));
#endregion
@ -87,10 +95,11 @@ namespace AngleSharp.Css.Dom
{
var other = obj as CssMedium;
if (other != null &&
other.IsExclusive == IsExclusive &&
other.IsInverse == IsInverse &&
other.Type.Is(Type) &&
if (other != null &&
other.IsExclusive == IsExclusive &&
other.IsInverse == IsInverse &&
other.Type.Is(Type) &&
other.Connector.Is(Connector) &&
other.Features.Count() == Features.Count())
{
foreach (var feature in other.Features)
@ -143,7 +152,9 @@ namespace AngleSharp.Css.Dom
for (var i = offset; i < _features.Count; i++)
{
writer.Write(" and ");
writer.Write(' ');
writer.Write(_connector);
writer.Write(' ');
_features[i].ToCss(writer, formatter);
}
}

Просмотреть файл

@ -14,13 +14,17 @@
private readonly Boolean _min;
private readonly Boolean _max;
private readonly String _name;
private readonly String _value;
private readonly ICssValue _value;
#endregion
#region ctor
internal MediaFeature(String name, String value)
internal MediaFeature(String name)
: this(name, null)
{}
internal MediaFeature(String name, ICssValue value)
{
_name = name;
_value = value;
@ -38,7 +42,7 @@
public Boolean IsMaximum => _max;
public String Value => _value;
public String Value => _value?.CssText ?? String.Empty;
public Boolean HasValue => _value != null;
@ -54,7 +58,7 @@
if (_value != null)
{
writer.Write(": ");
writer.Write(_value);
writer.Write(Value);
}
writer.Write(Symbols.RoundBracketClose);

Просмотреть файл

@ -7,7 +7,6 @@ namespace AngleSharp.Css.Dom
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
/// <summary>
/// Represents a list of media elements.

Просмотреть файл

@ -4,6 +4,7 @@ namespace AngleSharp.Css.Parser
using AngleSharp.Text;
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Represents extensions to for medium (media type) values.
@ -96,6 +97,244 @@ namespace AngleSharp.Css.Parser
return new CssMedium(type, inverse, exclusive, features);
}
/// <summary>
/// Parses a medium value.
/// </summary>
public static CssMedium ParseMediumNew(this StringSource source, IFeatureValidatorFactory factory)
{
// <media-query> = <media-condition> | <media-type>
source.SkipSpacesAndComments();
return source.ParseMediaCondition() ?? source.ParseMediaType();
}
private static CssMedium ParseMediaCondition(this StringSource source)
{
// <media-condition> = <media-not> | <media-in-parens> [ <media-and>* | <media-or>* ]
var medium = source.ParseMediaNot();
if (medium is null)
{
medium = source.ParseMediaInParens();
if (medium != null)
{
var other = source.ParseMediaAnd();
//TODO
}
}
return medium;
}
private static CssMedium ParseMediaConditionWithoutOr(this StringSource source)
{
// <media-condition-without-or> = <media-not> | <media-in-parens> <media-and>*
var medium = source.ParseMediaNot();
if (medium is null)
{
medium = source.ParseMediaInParens();
if (medium is not null)
{
do
{
source.SkipSpacesAndComments();
var other = source.ParseMediaAnd();
if (other is null)
{
break;
}
medium = new CssMedium(medium.Type, medium.IsInverse, medium.IsExclusive, medium.Features.Concat(other.Features));
}
while (!source.IsDone);
}
}
return medium;
}
private static CssMedium ParseMediaNot(this StringSource source)
{
// <media-not> = not <media-in-parens>
if (source.IsIdentifier("not"))
{
var pos = source.Index;
source.ParseIdent();
source.SkipCurrentAndSpaces();
var medium = source.ParseMediaInParens();
if (medium is not null)
{
source.SkipSpacesAndComments();
return new CssMedium(medium.Type, !medium.IsInverse, medium.IsExclusive, medium.Features);
}
source.BackTo(pos);
}
return null;
}
private static CssMedium ParseMediaInParens(this StringSource source)
{
// <media-in-parens> = ( <media-condition> ) | <media-feature> | <general-enclosed>
if (source.Current == '(')
{
var pos = source.Index;
source.SkipCurrentAndSpaces();
var medium = source.ParseMediaCondition();
if (medium is not null && source.Current == ')')
{
source.SkipCurrentAndSpaces();
return medium;
}
source.BackTo(pos);
}
return source.ParseMediaFeature() ?? source.ParseGeneralEnclosed();
}
private static CssMedium ParseMediaAnd(this StringSource source)
{
// <media-and> = and <media-in-parens>
if (source.IsIdentifier("and"))
{
var pos = source.Index;
source.ParseIdent();
source.SkipCurrentAndSpaces();
var medium = source.ParseMediaInParens();
if (medium is not null)
{
source.SkipSpacesAndComments();
return new CssMedium(medium.Type, medium.IsInverse, medium.IsExclusive, medium.Features, "and");
}
source.BackTo(pos);
}
return null;
}
private static CssMedium ParseMediaOr(this StringSource source)
{
// <media-or> = or <media-in-parens>
if (source.IsIdentifier("or"))
{
var pos = source.Index;
source.ParseIdent();
source.SkipCurrentAndSpaces();
var medium = source.ParseMediaInParens();
if (medium is not null)
{
source.SkipSpacesAndComments();
return new CssMedium(medium.Type, medium.IsInverse, medium.IsExclusive, medium.Features, "or");
}
source.BackTo(pos);
}
return null;
}
private static CssMedium ParseMediaType(this StringSource source)
{
// <media-type> = [ not | only ]? <ident> [ and <media-condition-without-or> ]?
//TODO
return null;
}
private static CssMedium ParseMediaFeature(this StringSource source)
{
// <media-feature> = ( [ <mf-plain> | <mf-boolean> | <mf-range> ] )
if (source.Current == '(')
{
var feature = source.ParseMediaFeaturePlain() ?? source.ParseMediaFeatureBoolean() ?? source.ParseMediaFeatureRange();
if (feature is not null && source.Current == ')')
{
source.SkipCurrentAndSpaces();
return new CssMedium("", false, false, Enumerable.Repeat(feature, 1), "and");
}
}
return null;
}
private static CssMedium ParseGeneralEnclosed(this StringSource source)
{
// <general-enclosed> = [ <function-token> <any-value> ) ] | ( <ident> <any-value> )
//TODO
return null;
}
private static MediaFeature ParseMediaFeaturePlain(this StringSource source)
{
// <mf-plain> = <ident> : <mf-value>
var pos = source.Index;
var ident = source.ParseIdent();
if (ident is not null && source.Current == ':')
{
var value = source.ParseMediaFeatureValue();
if (value is not null)
{
source.SkipSpacesAndComments();
return new MediaFeature(ident, value);
}
source.BackTo(pos);
}
return null;
}
private static MediaFeature ParseMediaFeatureBoolean(this StringSource source)
{
// <mf-boolean> = <ident>
var ident = source.ParseIdent();
if (ident is not null)
{
source.SkipSpacesAndComments();
return new MediaFeature(ident);
}
return null;
}
private static MediaFeature ParseMediaFeatureRange(this StringSource source)
{
// <mf-range> = <ident> '<' '='? | '>' '='? | '=' <mf-value>
// | <mf-value> '<' '='? | '>' '='? | '=' <ident>
// | <mf-value> '<' '='? <ident> '<' '='? <mf-value>
// | <mf-value> '>' '='? <ident> '>' '='? <mf-value>
//TODO
return null;
}
private static ICssValue ParseMediaFeatureValue(this StringSource source)
{
// <mf-value> = <number> | <dimension> | <ident> | <ratio>
//TODO
var ident = source.ParseNumber() ?? source.ParseIdent() ?? source.ParseUnit() ?? source.ParseRatio();
return null;
}
private static String ParseMediumIdent(this StringSource source)
{
var ident = source.ParseIdent();

Просмотреть файл

@ -21,7 +21,7 @@
<ItemGroup>
<PackageReference Include="Alba.CsCss" version="1.0.1.0" />
<PackageReference Include="AngleSharp" Version="1.0.0" />
<PackageReference Include="AngleSharp" Version="1.0.3" />
<PackageReference Include="ExCSS" version="2.0.6" />
</ItemGroup>
</Project>