This commit is contained in:
Florian Rappl 2019-05-12 14:43:49 +02:00
Родитель e93e2bb340
Коммит 894bd529af
17 изменённых файлов: 176 добавлений и 178 удалений

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

@ -1,83 +0,0 @@
namespace AngleSharp.Css.Tests.Mocks
{
sealed class MockRenderDevice : IRenderDevice
{
public DeviceCategory Category
{
get;
set;
}
public int ColorBits
{
get;
set;
}
public int DeviceHeight
{
get;
set;
}
public int DeviceWidth
{
get;
set;
}
public int Frequency
{
get;
set;
}
public bool IsGrid
{
get;
set;
}
public bool IsInterlaced
{
get;
set;
}
public bool IsScripting
{
get;
set;
}
public int MonochromeBits
{
get;
set;
}
public int Resolution
{
get;
set;
}
public int ViewPortHeight
{
get;
set;
}
public int ViewPortWidth
{
get;
set;
}
public IBrowsingContext Context
{
get;
set;
}
}
}

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

@ -32,8 +32,8 @@ namespace AngleSharp.Css.Tests.Rules
public void CssMediaWidthValidation()
{
var validate = CreateValidator(FeatureNames.Width, "100px");
var valid = validate(new MockRenderDevice { ViewPortWidth = 100, ViewPortHeight = 0 });
var invalid = validate(new MockRenderDevice { ViewPortWidth = 0, ViewPortHeight = 0 });
var valid = validate(new DefaultRenderDevice { ViewPortWidth = 100, ViewPortHeight = 0 });
var invalid = validate(new DefaultRenderDevice { ViewPortWidth = 0, ViewPortHeight = 0 });
Assert.IsTrue(valid);
Assert.IsFalse(invalid);
}
@ -42,8 +42,8 @@ namespace AngleSharp.Css.Tests.Rules
public void CssMediaMaxHeightValidation()
{
var validate = CreateValidator(FeatureNames.MaxHeight, "100px");
var valid = validate(new MockRenderDevice { ViewPortWidth = 0, ViewPortHeight = 99 });
var invalid = validate(new MockRenderDevice { ViewPortWidth = 0, ViewPortHeight = 101 });
var valid = validate(new DefaultRenderDevice { ViewPortWidth = 0, ViewPortHeight = 99 });
var invalid = validate(new DefaultRenderDevice { ViewPortWidth = 0, ViewPortHeight = 101 });
Assert.IsTrue(valid);
Assert.IsFalse(invalid);
}
@ -52,8 +52,8 @@ namespace AngleSharp.Css.Tests.Rules
public void CssMediaMinDeviceWidthValidation()
{
var validate = CreateValidator(FeatureNames.MinDeviceWidth, "100px");
var valid = validate(new MockRenderDevice { DeviceWidth = 100, DeviceHeight = 0 });
var invalid = validate(new MockRenderDevice { DeviceWidth = 99, DeviceHeight = 0 });
var valid = validate(new DefaultRenderDevice { DeviceWidth = 100, DeviceHeight = 0 });
var invalid = validate(new DefaultRenderDevice { DeviceWidth = 99, DeviceHeight = 0 });
Assert.IsTrue(valid);
Assert.IsFalse(invalid);
}
@ -62,8 +62,8 @@ namespace AngleSharp.Css.Tests.Rules
public void CssMediaAspectRatio()
{
var validate = CreateValidator(FeatureNames.AspectRatio, "1/1");
var valid = validate(new MockRenderDevice { ViewPortWidth = 100, ViewPortHeight = 100 });
var invalid = validate(new MockRenderDevice { ViewPortWidth = 16, ViewPortHeight = 9 });
var valid = validate(new DefaultRenderDevice { ViewPortWidth = 100, ViewPortHeight = 100 });
var invalid = validate(new DefaultRenderDevice { ViewPortWidth = 16, ViewPortHeight = 9 });
Assert.IsTrue(valid);
Assert.IsFalse(invalid);
}

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

@ -1,4 +1,4 @@
namespace AngleSharp.Css.Tests.Rules
namespace AngleSharp.Css.Tests.Rules
{
using AngleSharp.Css.Dom;
using AngleSharp.Css.Tests.Mocks;
@ -13,7 +13,7 @@
{
var source = @"@supports () { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -26,7 +26,7 @@
{
var source = @"@supports (background-color: red) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -39,7 +39,7 @@
{
var source = @"@supports ((background-color: red) and (color: blue)) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -52,7 +52,7 @@
{
var source = @"@supports (not (background-transparency: half)) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -65,7 +65,7 @@
{
var source = @"@supports ((background-transparency: zero)) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -78,7 +78,7 @@
{
var source = @"@supports (background: red !important) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -91,7 +91,7 @@
{
var source = @"@supports ((padding-TOP : 0) or (padding-left : 0)) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -104,7 +104,7 @@
{
var source = @"@supports (((padding-top: 0) or (padding-left: 0)) and ((padding-bottom: 0) or (padding-right: 0))) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -117,7 +117,7 @@
{
var source = @"@supports (display: flex !important) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -138,7 +138,7 @@
{
var source = @"@supports ((display: flex)) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -153,7 +153,7 @@
(animation-name: foo)) and
(transform: rotate(10deg)) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -168,7 +168,7 @@
((animation-name: foo) and
(transform: rotate(10deg))) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -184,7 +184,7 @@
( -webkit-box-shadow: 0 0 2px black ) or
( -o-box-shadow: 0 0 2px black ) { }";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;
@ -201,7 +201,7 @@
#article { width: 75%; }
}";
var sheet = ParseStyleSheet(source);
var device = new MockRenderDevice { Context = sheet.Context };
var device = new DefaultRenderDevice();
Assert.AreEqual(1, sheet.Rules.Length);
Assert.IsInstanceOf<CssSupportsRule>(sheet.Rules[0]);
var supports = sheet.Rules[0] as CssSupportsRule;

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

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="EmitMSBuildWarning" BeforeTargets="Build">
<Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." />
</Target>
</Project>

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

@ -20,10 +20,8 @@ namespace AngleSharp.Css
/// <param name="address">The address of the resource.</param>
/// <param name="element">The hosting element.</param>
/// <returns>The async task.</returns>
public static Task<IStyleSheet> OpenStyleSheetAsync(this IBrowsingContext context, Url address, IElement element)
{
return context.OpenStyleSheetAsync(address, element, CancellationToken.None);
}
public static Task<IStyleSheet> OpenStyleSheetAsync(this IBrowsingContext context, Url address, IElement element) =>
context.OpenStyleSheetAsync(address, element, CancellationToken.None);
/// <summary>
/// Loads a stylesheet resource via its URL.

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

@ -126,11 +126,17 @@ namespace AngleSharp.Css
/// The break-all keyword.
/// </summary>
public static readonly String BreakWord = "break-word";
/// <summary>
/// The important keyword.
/// </summary>
public static readonly String Important = "important";
/// <summary>
/// The !important keyword.
/// </summary>
public static readonly String BangImportant = "!important";
/// <summary>
/// The inherit keyword.
/// </summary>

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

@ -32,23 +32,14 @@ namespace AngleSharp.Css
public static Int32 AsInteger(this ICssValue value)
{
return (Int32)value.AsNumber();
return (int)value.AsNumber();
}
public static Boolean AsBoolean(this ICssValue value)
{
return false;
}
public static Boolean AsBoolean(this ICssValue value) => false;
public static T AsEnum<T>(this ICssValue value)
where T : struct, IComparable
{
return default(T);
}
where T : struct, IComparable => default(T);
public static Boolean Is(this ICssValue value, String keyword)
{
return false;
}
public static Boolean Is(this ICssValue value, String keyword) => false;
}
}

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

@ -1,4 +1,4 @@
namespace AngleSharp.Css
namespace AngleSharp.Css
{
using AngleSharp.Css.Dom;
using System;
@ -21,7 +21,7 @@
{ FunctionNames.Url, str => new UrlFunction(str) },
{ FunctionNames.Domain, str => new DomainFunction(str) },
{ FunctionNames.UrlPrefix, str => new UrlPrefixFunction(str) },
{ FunctionNames.Regexp, str => new RegexpFunction(str) }
{ FunctionNames.Regexp, str => new RegexpFunction(str) },
};
/// <summary>

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

@ -1,4 +1,4 @@
namespace AngleSharp.Css
namespace AngleSharp.Css
{
using AngleSharp.Css.FeatureValidators;
using System;
@ -56,7 +56,7 @@
{ FeatureNames.UpdateFrequency, () => new UpdateFrequencyFeatureValidator() },
{ FeatureNames.Scripting, () => new ScanFeatureValidator() },
{ FeatureNames.Pointer, () => new PointerFeatureValidator() },
{ FeatureNames.Hover, () => new HoverFeatureValidator() }
{ FeatureNames.Hover, () => new HoverFeatureValidator() },
};
/// <summary>

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

@ -1,4 +1,4 @@
namespace AngleSharp.Css
namespace AngleSharp.Css
{
using AngleSharp.Css.Dom;
using AngleSharp.Dom;
@ -21,7 +21,7 @@
{
{ PseudoElementNames.Before, element => new PseudoElement(element, PseudoElementNames.Before) },
{ PseudoElementNames.After, element => new PseudoElement(element, PseudoElementNames.After) },
{ PseudoElementNames.Slotted, element => new PseudoElement(element, PseudoElementNames.Slotted) }
{ PseudoElementNames.Slotted, element => new PseudoElement(element, PseudoElementNames.Slotted) },
};
/// <summary>

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

@ -0,0 +1,94 @@
namespace AngleSharp.Css
{
using System;
/// <summary>
/// Represents the default render device.
/// </summary>
public class DefaultRenderDevice : IRenderDevice
{
/// <inheritdoc />
public DeviceCategory Category
{
get;
set;
} = DeviceCategory.Screen;
/// <inheritdoc />
public Int32 ColorBits
{
get;
set;
} = 32;
/// <inheritdoc />
public Int32 DeviceHeight
{
get;
set;
} = 0;
/// <inheritdoc />
public Int32 DeviceWidth
{
get;
set;
} = 0;
/// <inheritdoc />
public Int32 Frequency
{
get;
set;
} = 60;
/// <inheritdoc />
public Boolean IsGrid
{
get;
set;
} = false;
/// <inheritdoc />
public Boolean IsInterlaced
{
get;
set;
} = false;
/// <inheritdoc />
public Boolean IsScripting
{
get;
set;
} = true;
/// <inheritdoc />
public Int32 MonochromeBits
{
get;
set;
} = 16;
/// <inheritdoc />
public Int32 Resolution
{
get;
set;
} = 96;
/// <inheritdoc />
public Int32 ViewPortHeight
{
get;
set;
} = 0;
/// <inheritdoc />
public Int32 ViewPortWidth
{
get;
set;
} = 0;
}
}

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

@ -2,6 +2,7 @@ namespace AngleSharp.Css.Dom
{
using AngleSharp.Attributes;
using AngleSharp.Css.Parser;
using AngleSharp.Dom;
using AngleSharp.Text;
using System;
@ -9,6 +10,7 @@ namespace AngleSharp.Css.Dom
/// A set of globally exposed CSS utilities.
/// </summary>
[DomName("CSS")]
[DomExposed("Window")]
public static class CssHelpers
{
/// <summary>
@ -61,13 +63,15 @@ namespace AngleSharp.Css.Dom
/// Returns a boolean value indicating if the browser supports a given CSS feature,
/// or not.
/// </summary>
/// <param name="window">The hosting window.</param>
/// <param name="propertyName">The name of the CSS property to check.</param>
/// <param name="value">The value of the CSS property to check.</param>
/// <returns>True if the CSS feature is supported, otherwise false.</returns>
[DomName("supports")]
public static Boolean Supports(String propertyName, String value)
public static Boolean Supports(this IWindow window, String propertyName, String value)
{
var condition = new DeclarationCondition(propertyName, value);
var context = window.Document?.Context;
var condition = new DeclarationCondition(context, propertyName, value);
return condition.Check(null);
}
@ -75,12 +79,14 @@ namespace AngleSharp.Css.Dom
/// Returns a boolean value indicating if the browser supports a given CSS feature,
/// or not.
/// </summary>
/// <param name="window">The hosting window.</param>
/// <param name="conditionText">The condition to check.</param>
/// <returns>True if the CSS feature is supported, otherwise false.</returns>
[DomName("supports")]
public static Boolean Supports(String conditionText)
public static Boolean Supports(this IWindow window, String conditionText)
{
var condition = ConditionParser.Parse(conditionText);
var context = window.Document?.Context;
var condition = ConditionParser.Parse(conditionText, context);
return condition.Check(null);
}
}

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

@ -8,16 +8,18 @@ namespace AngleSharp.Css.Dom
{
private readonly String _name;
private readonly String _value;
private readonly IBrowsingContext _context;
public DeclarationCondition(String name, String value)
public DeclarationCondition(IBrowsingContext context, String name, String value)
{
_context = context;
_name = name;
_value = value;
}
public Boolean Check(IRenderDevice device)
{
var factory = device?.Context?.GetService<IDeclarationFactory>() ?? Factory.Declaration;
var factory = _context?.GetService<IDeclarationFactory>() ?? Factory.Declaration;
var info = factory?.Create(_name);
if (info != null && !Object.Equals(info.Converter, ValueConverters.Any))
@ -30,18 +32,16 @@ namespace AngleSharp.Css.Dom
return false;
}
public void ToCss(TextWriter writer, IStyleFormatter formatter)
{
public void ToCss(TextWriter writer, IStyleFormatter formatter) =>
writer.Write(formatter.Declaration(_name, _value, false));
}
private static String Normalize(String value)
{
var important = "!important";
var keyword = CssKeywords.BangImportant;
if (value.EndsWith(important))
if (value.EndsWith(keyword))
{
return value.Remove(value.Length - important.Length).Trim();
return value.Remove(value.Length - keyword.Length).Trim();
}
return value;

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

@ -1,4 +1,4 @@
namespace AngleSharp.Css.Dom
namespace AngleSharp.Css.Dom
{
using AngleSharp.Css.Parser;
using AngleSharp.Dom;
@ -41,7 +41,8 @@
public Boolean SetConditionText(String value, Boolean throwOnError)
{
var condition = ConditionParser.Parse(value);
var context = Owner?.Context;
var condition = ConditionParser.Parse(value, context);
if (condition == null)
{

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

@ -1,4 +1,4 @@
namespace AngleSharp.Css
namespace AngleSharp.Css
{
using System;
@ -67,10 +67,5 @@
/// Gets the category of the device.
/// </summary>
DeviceCategory Category { get; }
/// <summary>
/// Gets the associated browsing context.
/// </summary>
IBrowsingContext Context { get; }
}
}

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

@ -636,7 +636,7 @@ namespace AngleSharp.Css.Parser
private String CreateValue(ref CssToken token, out Boolean important)
{
const String keyword = "!important";
var keyword = CssKeywords.BangImportant;
var value = _tokenizer.ContentFrom(token.Position.Position);
important = value.EndsWith(keyword, StringComparison.OrdinalIgnoreCase);
token = NextToken();

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

@ -1,4 +1,4 @@
namespace AngleSharp.Css.Parser
namespace AngleSharp.Css.Parser
{
using AngleSharp.Css.Dom;
using AngleSharp.Text;
@ -7,25 +7,21 @@
static class ConditionParser
{
public static IConditionFunction Parse(String str)
public static IConditionFunction Parse(String str, IBrowsingContext context)
{
var source = new StringSource(str);
source.SkipSpacesAndComments();
var result = source.ParseConditionFunction();
var result = source.ParseConditionFunction(context);
return source.IsDone ? result : null;
}
public static IConditionFunction ParseConditionFunction(this StringSource source)
{
return Condition(source);
}
public static IConditionFunction ParseConditionFunction(this StringSource source, IBrowsingContext context) =>
source.Condition(context);
private static IConditionFunction Condition(StringSource source)
{
return Negation(source) ?? ConjunctionOrDisjunction(source);
}
private static IConditionFunction Condition(this StringSource source, IBrowsingContext context) =>
source.Negation(context) ?? source.ConjunctionOrDisjunction(context);
private static IConditionFunction Negation(StringSource source)
private static IConditionFunction Negation(this StringSource source, IBrowsingContext context)
{
var pos = source.Index;
var ident = source.ParseIdent();
@ -33,7 +29,7 @@
if (ident != null && ident.Isi(CssKeywords.Not))
{
source.SkipSpacesAndComments();
var condition = Group(source);
var condition = source.Group(context);
if (condition != null)
{
@ -45,9 +41,9 @@
return null;
}
private static IConditionFunction ConjunctionOrDisjunction(StringSource source)
private static IConditionFunction ConjunctionOrDisjunction(this StringSource source, IBrowsingContext context)
{
var condition = Group(source);
var condition = source.Group(context);
source.SkipSpacesAndComments();
var pos = source.Index;
var ident = source.ParseIdent();
@ -59,7 +55,7 @@
if (isAnd || isOr)
{
var conditions = Scan(source, ident, condition);
var conditions = source.Scan(ident, condition, context);
if (isAnd)
{
@ -76,7 +72,7 @@
return condition;
}
private static IConditionFunction Group(StringSource source)
private static IConditionFunction Group(this StringSource source, IBrowsingContext context)
{
if (source.Current == Symbols.RoundBracketOpen)
{
@ -85,7 +81,7 @@
if (current != Symbols.RoundBracketClose)
{
condition = Condition(source) ?? Declaration(source);
condition = source.Condition(context) ?? source.Declaration(context);
current = source.SkipSpacesAndComments();
if (condition == null)
@ -104,7 +100,7 @@
return null;
}
private static IConditionFunction Declaration(StringSource source)
private static IConditionFunction Declaration(this StringSource source, IBrowsingContext context)
{
var name = source.ParseIdent();
var colon = source.SkipSpacesAndComments();
@ -114,13 +110,13 @@
if (name != null && value != null && colon == Symbols.Colon)
{
return new DeclarationCondition(name, value);
return new DeclarationCondition(context, name, value);
}
return null;
}
private static IEnumerable<IConditionFunction> Scan(StringSource source, String keyword, IConditionFunction condition)
private static IEnumerable<IConditionFunction> Scan(this StringSource source, String keyword, IConditionFunction condition, IBrowsingContext context)
{
var conditions = new List<IConditionFunction>();
var ident = String.Empty;
@ -129,7 +125,7 @@
do
{
source.SkipSpacesAndComments();
condition = Group(source);
condition = source.Group(context);
if (condition == null)
break;