diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 44871a98..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: Bug Report -about: Create a report to help us improve -title: '' -labels: 'bug' -assignees: '' ---- - -# Bug Report - -## Prerequisites - -- [ ] Can you reproduce the problem in a [MWE](https://en.wikipedia.org/wiki/Minimal_working_example)? -- [ ] Are you running the latest version of AngleSharp? -- [ ] Did you check the FAQs to see if that helps you? -- [ ] Are you reporting to the correct repository? (there are multiple AngleSharp libraries, e.g., `AngleSharp.Css` for CSS support) -- [ ] Did you perform a search in the issues? - -For more information, see the `CONTRIBUTING` guide. - -## Description - -[Description of the bug] - -## Steps to Reproduce - -1. [First Step] -2. [Second Step] -3. [and so on...] - -**Expected behavior:** [What you expected to happen] - -**Actual behavior:** [What actually happened] - -**Environment details:** [OS, .NET Runtime, ...] - -## Possible Solution - -[Optionally, share your idea to fix the issue] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..32f16a15 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,69 @@ +name: Bug Report +description: "Create a report to help us improve" +labels: ["bug"] +body: + - type: checkboxes + id: prerequisites + attributes: + label: Prerequisites + description: For more information, see the [CONTRIBUTING guide](https://github.com/AngleSharp/AngleSharp.Css/blob/devel/.github/CONTRIBUTING.md). + options: + - label: Can you reproduce the problem in a [MWE](https://en.wikipedia.org/wiki/Minimal_working_example)? + required: true + - label: Are you running the latest version of AngleSharp.Css? + required: true + - label: Did you check the FAQs to see if that helps you? + required: true + - label: Are you reporting to the correct repository? (there are multiple AngleSharp libraries, e.g., `AngleSharp.Xml` for Xml support) + required: true + - label: Did you perform a search in the issues? + required: true + + - type: textarea + id: description + attributes: + label: Description + description: Share a clear and concise description of the problem. + placeholder: Description + validations: + required: true + + - type: textarea + id: reproduction-steps + attributes: + label: Steps to Reproduce + description: | + Include minimal steps to reproduce the problem if possible. E.g.: the smallest possible code snippet; or a small project, with steps to run it. Make sure to include logs and exceptions as text rather than screenshots. + placeholder: Minimal Reproduction + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: | + Provide a description of the expected behavior. + placeholder: Expected Behavior + validations: + required: true + + - type: textarea + id: actual-behavior + attributes: + label: Actual Behavior + description: | + Provide a description of the actual behavior observed. If applicable, include the error messages and the exception stacktrace. + placeholder: Actual Behavior + validations: + required: true + + - type: textarea + id: known-workarounds + attributes: + label: Possible Solution / Known Workarounds + description: | + Provide a description of any possible solution or known workarounds. + placeholder: Possible Solution / Known Workarounds + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..73d2a3a1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Documentation + url: https://anglesharp.github.io/ + about: Read our comprehensive documentation \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 7722f052..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Feature Request -about: Suggest an idea for this project -title: '' -labels: 'enhancement' -assignees: '' ---- - -# New Feature Proposal - -## Description - -[Description of the proposed feature] - -## Background - -Provide any additional background for the feature. e.g., user scenarios. - -## Specification - -In case of updates that adhere to specification changes, please reference the used specification. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..99fd75aa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,20 @@ +name: Feature Idea +description: "Suggest an idea for this project" +labels: ["enhancement"] +body: + - type: textarea + attributes: + label: Description + description: What should the new feature do? + validations: + required: true + + - type: textarea + attributes: + label: Background + description: Provide any additional background for the feature. e.g., user scenarios. + + - type: textarea + attributes: + label: Specification + description: In case of updates that adhere to specification changes, please reference the used specification. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f01d24d3..efce7ef1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,6 +56,7 @@ jobs: dotnet-version: | 6.0.x 7.0.x + 8.0.x - name: Build run: ./build.sh @@ -72,6 +73,7 @@ jobs: dotnet-version: | 6.0.x 7.0.x + 8.0.x - name: Build run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff21124..d7dbd41f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +# 1.0.0 + +Released on Sunday, January 21 2024. + +- Updated to use AngleSharp 1.0 (#150) +- Updated media parsing to media L4 spec (#133) +- Updated naming of CSS values (e.g., `Color` to `CssColorValue`) +- Fixed issue when updating shorthands with invalid values (#129) +- Fixed issue with appended EOF character in `CssText` (#123) +- Fixed missing semicolon in `@page` rule (#135) +- Fixed integer serialization of keyframe stops (#128) +- Fixed ordering of rows and columns in `grid` and `grid-gap` (#137) +- Fixed inclusion of CSS from stylesheets (#116, #140) +- Fixed style empty if `text-align` is `start` (#151) +- Fixed computation of priority in CSS rules using multi selector +- Fixed `GetInnerText` multi-line / text node behavior (#155) @Seyden +- Fixed computation of relative (`em`) values to absolute (`px`) for `Length` (#136) +- Added further compactification of CSS tuples (#89, #93) +- Added new value types `CssPercentageValue`, `CssNumberValue`, and `CssIntegerValue` +- Added support for CSS nesting in style rules (#148) +- Added resolution of CSS variable names (#62) +- Added support for 8-digit hex color codes (#132) +- Added support for `margin-block` and `margin-inline` declarations +- Added support for `padding-block` and `padding-inline` declarations +- Added more CSSOM possibilities and helpers (#6) +- Added parts of recent color spec update such as `rgb` with spaces (#131) +- Added now Color L4 parsing with `hsl`, `hwb`, `lab`, `lch`, `oklab`, and `oklch` +- Added support for recent CSS `list-type` values (#152) + # 0.17.0 Released on Sunday, January 15 2023. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 25959ec7..0058dde2 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -16,6 +16,8 @@ AngleSharp.Css contains code written by (in order of first pull request / commit * [Bastian Buchholz](https://github.com/campersau) * [Fraaankes](https://github.com/Fraaankes) * [Eric Mutta](https://github.com/ericmutta) +* [Seyden](https://github.com/Seyden) +* [Dave Dunkin](https://github.com/ddunkin) Without these awesome people AngleSharp.Css could not exist. Thanks to everyone for your contributions! :beers: diff --git a/LICENSE b/LICENSE index 470f190b..b64c5a7f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 - 2023 AngleSharp +Copyright (c) 2013 - 2025 AngleSharp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 5eee1792..431b3aa8 100644 --- a/README.md +++ b/README.md @@ -97,12 +97,4 @@ This project is supported by the [.NET Foundation](https://dotnetfoundation.org) ## License -The MIT License (MIT) - -Copyright (c) 2016 - 2022 AngleSharp - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +AngleSharp.Css is released using the MIT license. For more information see the [license file](./LICENSE). diff --git a/docs/README.md b/docs/README.md index 0d8585c9..29f5ccde 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,4 +2,8 @@ We have more detailed information regarding the following subjects: +- [Getting Started](general/01-Basics.md) +- [Value Model](general/02-Values.md) - [API Documentation](tutorials/01-API.md) +- [Examples](tutorials/02-Examples.md) +- [FAQ](tutorials/03-Questions.md) diff --git a/nuke/Build.cs b/nuke/Build.cs index b08f6bde..a186fdf3 100644 --- a/nuke/Build.cs +++ b/nuke/Build.cs @@ -89,11 +89,11 @@ protected override void OnBuildInitialized() if (ScheduledTargets.Contains(Default)) { - Version = $"{Version}-ci-{buildNumber}"; + Version = $"{Version}-ci.{buildNumber}"; } else if (ScheduledTargets.Contains(PrePublish)) { - Version = $"{Version}-alpha-{buildNumber}"; + Version = $"{Version}-beta.{buildNumber}"; } } diff --git a/nuke/ReleaseNotesParser.cs b/nuke/ReleaseNotesParser.cs index 4b00a073..d911c90b 100644 --- a/nuke/ReleaseNotesParser.cs +++ b/nuke/ReleaseNotesParser.cs @@ -17,16 +17,6 @@ /// public sealed class ReleaseNotesParser { - private readonly Regex _versionRegex; - - /// - /// Initializes a new instance of the class. - /// - public ReleaseNotesParser() - { - _versionRegex = new Regex(@"(?\d+(\s*\.\s*\d+){0,3})(?-[a-z][0-9a-z-]*)?"); - } - /// /// Parses all release notes. /// diff --git a/src/AngleSharp.Css.Tests/AngleSharp.Css.Tests.csproj b/src/AngleSharp.Css.Tests/AngleSharp.Css.Tests.csproj index 8bf7df15..b61b451a 100644 --- a/src/AngleSharp.Css.Tests/AngleSharp.Css.Tests.csproj +++ b/src/AngleSharp.Css.Tests/AngleSharp.Css.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/AngleSharp.Css.Tests/CssConstructionFunctions.cs b/src/AngleSharp.Css.Tests/CssConstructionFunctions.cs index e62c5ee5..dd915242 100644 --- a/src/AngleSharp.Css.Tests/CssConstructionFunctions.cs +++ b/src/AngleSharp.Css.Tests/CssConstructionFunctions.cs @@ -2,6 +2,7 @@ namespace AngleSharp.Css.Tests { using AngleSharp.Css.Dom; using AngleSharp.Css.Parser; + using AngleSharp.Css.Values; using AngleSharp.Html.Dom; using AngleSharp.Html.Parser; using System; @@ -70,7 +71,6 @@ internal static CssProperty ParseDeclaration(String source, CssParserOptions opt internal static CssStyleDeclaration ParseDeclarations(String declarations) { var context = BrowsingContext.New(Configuration.Default.WithCss()); - var parser = context.GetService(); var style = new CssStyleDeclaration(context); style.Update(declarations); return style; @@ -93,7 +93,7 @@ internal static CssImportRule ParseImportRule(String source) internal static Predicate CreateValidator(String name, String value) { var validator = CreateMediaFeatureValidator(name); - var feature = new MediaFeature(name, value); + var feature = new MediaFeature(name, new CssAnyValue(value)); return device => validator.Validate(feature, device); } diff --git a/src/AngleSharp.Css.Tests/Declarations/CssBackgroundProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssBackgroundProperty.cs index bb5c2d02..03d9da7c 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssBackgroundProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssBackgroundProperty.cs @@ -6,6 +6,30 @@ namespace AngleSharp.Css.Tests.Declarations [TestFixture] public class CssBackgroundPropertyTests { + [Test] + public void CssBackgroundSizeCoverTest() + { + var snippet = "background-size : cover"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("background-size", property.Name); + Assert.IsFalse(property.IsImportant); + Assert.IsFalse(property.IsInherited); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("cover", property.Value); + } + + [Test] + public void CssBackgroundSizeContainTest() + { + var snippet = "background-size : contain"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("background-size", property.Name); + Assert.IsFalse(property.IsImportant); + Assert.IsFalse(property.IsInherited); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("contain", property.Value); + } + [Test] public void CssBackgroundAttachmentScrollLegal() { diff --git a/src/AngleSharp.Css.Tests/Declarations/CssGridProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssGridProperty.cs index 7d366d01..e75811f9 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssGridProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssGridProperty.cs @@ -534,7 +534,7 @@ public void CssGridAreaTextValueLegal1() var css = ParseStyleSheet(source); var text = css.Rules[0].CssText; - var expected = "#nav-header { grid-area: aaa / aaa / aaa / aaa }"; + var expected = "#nav-header { grid-area: aaa }"; Assert.AreEqual(expected, text); } @@ -909,5 +909,21 @@ public void CssGridTemplateLonghands_Issue68() var style = ParseDeclarations(snippet); Assert.AreEqual("grid-template: none", style.CssText); } + + [Test] + public void CssGridPreservesParts_Issue137() + { + var snippet = "grid: 10px / 80px"; + var style = ParseDeclarations(snippet); + Assert.AreEqual("grid: 10px / 80px", style.CssText); + } + + [Test] + public void CssGridGapPreservesParts_Issue137() + { + var snippet = "grid-gap: 10px 80px"; + var style = ParseDeclarations(snippet); + Assert.AreEqual("grid-gap: 10px 80px", style.CssText); + } } } diff --git a/src/AngleSharp.Css.Tests/Declarations/CssListProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssListProperty.cs index 1fca39c3..e23f95ac 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssListProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssListProperty.cs @@ -1,4 +1,4 @@ -namespace AngleSharp.Css.Tests.Declarations +namespace AngleSharp.Css.Tests.Declarations { using NUnit.Framework; using static CssConstructionFunctions; @@ -125,14 +125,15 @@ public void CssListStyleTypeDecimalLeadingZeroLegal() } [Test] - public void CssListStyleTypeNumberIllegal() + public void CssListStyleTypeSomeValueLegal() { var snippet = "list-style-type: number "; var property = ParseDeclaration(snippet); Assert.AreEqual("list-style-type", property.Name); Assert.IsFalse(property.IsImportant); - Assert.IsTrue(property.IsInherited); - Assert.IsFalse(property.HasValue); + Assert.IsFalse(property.IsInherited); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("number", property.Value); } [Test] @@ -277,5 +278,94 @@ public void CssCounterIncrementLegal() Assert.IsTrue(property.HasValue); Assert.AreEqual("chapter 1 section 2 page 1", property.Value); } + + [Test] + public void CssListStyleStringValue_Issue152() + { + var snippet = "list-style-type: \"-\""; + var property = ParseDeclaration(snippet); + Assert.AreEqual("list-style-type", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("\"-\"", property.Value); + } + + [Test] + public void CssListStyleKannada_Issue152() + { + var snippet = "list-style-type: kannada"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("list-style-type", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("kannada", property.Value); + } + + [Test] + public void CssListStyleTradChineseInformal_Issue152() + { + var snippet = "list-style-type: trad-chinese-informal;"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("list-style-type", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("trad-chinese-informal", property.Value); + } + + [Test] + public void CssListStyleGeorgian_Issue152() + { + var snippet = "list-style-type: georgian"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("list-style-type", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("georgian", property.Value); + } + + [Test] + public void CssListStyleDecimal_Issue152() + { + var snippet = "list-style-type: decimal"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("list-style-type", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("decimal", property.Value); + } + + [Test] + public void CssListStyleSquare_Issue152() + { + var snippet = "list-style-type: square"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("list-style-type", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("square", property.Value); + } + + [Test] + public void CssListStyleCircle_Issue152() + { + var snippet = "list-style-type: circle"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("list-style-type", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("circle", property.Value); + } + + [Test] + public void CssListStyleSymbolsFunction_Issue152() + { + var snippet = "list-style-type: symbols(cyclic \"*\" \"†\" \"‡\")"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("list-style-type", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("symbols(cyclic \"*\" \"†\" \"‡\")", property.Value); + } + + [Test] + public void CssListStyleSymbolsFailingForWrongType_Issue152() + { + var snippet = "list-style-type: symbols(foo \"*\" \"†\" \"‡\")"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("list-style-type", property.Name); + Assert.IsFalse(property.HasValue); + } } } diff --git a/src/AngleSharp.Css.Tests/Declarations/CssTextProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssTextProperty.cs index 545cca7c..b8dd8507 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssTextProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssTextProperty.cs @@ -690,6 +690,39 @@ public void CssOverflowWrapAlternateNameNoneIllegal() Assert.IsFalse(property.IsInherited); Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.HasValue); - } - } + } + + [Test] + public void CssTextAlignLegalStart_Issue151() + { + var snippet = "text-align:start"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("text-align", property.Name); + Assert.IsTrue(property.HasValue); + Assert.IsFalse(property.IsImportant); + Assert.IsFalse(property.IsInherited); + Assert.AreEqual("start", property.Value); + } + + [Test] + public void CssTextAlignLegalJustifyAll_Issue151() + { + var snippet = "text-align:justify-all"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("text-align", property.Name); + Assert.IsTrue(property.HasValue); + Assert.IsFalse(property.IsImportant); + Assert.IsFalse(property.IsInherited); + Assert.AreEqual("justify-all", property.Value); + } + + [Test] + public void CssTextAlignIllegalJustifyNone_Issue151() + { + var snippet = "text-align:justify-none"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("text-align", property.Name); + Assert.IsFalse(property.HasValue); + } + } } diff --git a/src/AngleSharp.Css.Tests/Declarations/CssVariables.cs b/src/AngleSharp.Css.Tests/Declarations/CssVariables.cs index f3ba1fed..e0315c71 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssVariables.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssVariables.cs @@ -59,7 +59,7 @@ public void LegitVariableReferenceWithFallback() Assert.IsNotNull(variable); Assert.AreEqual(1, variable.References.Length); Assert.AreEqual("--my-bar", variable.References[0].VariableName); - Assert.AreEqual("24px", variable.References[0].DefaultValue); + Assert.AreEqual("24px", variable.References[0].DefaultValue.CssText); } [Test] @@ -72,7 +72,7 @@ public void LegitVariableReferenceWithFallbackContainingComma() Assert.IsNotNull(variable); Assert.AreEqual(1, variable.References.Length); Assert.AreEqual("--color", variable.References[0].VariableName); - Assert.AreEqual("red, blue", variable.References[0].DefaultValue); + Assert.AreEqual("red, blue", variable.References[0].DefaultValue.CssText); } [Test] @@ -113,7 +113,7 @@ public void LegitMultipleVariableReferenceInBorderShorthand() Assert.AreEqual("--width", variable.References[0].VariableName); Assert.IsNull(variable.References[0].DefaultValue); Assert.AreEqual("--color", variable.References[1].VariableName); - Assert.AreEqual("black", variable.References[1].DefaultValue); + Assert.AreEqual("black", variable.References[1].DefaultValue.CssText); } } } diff --git a/src/AngleSharp.Css.Tests/Extensions/AnalysisWindow.cs b/src/AngleSharp.Css.Tests/Extensions/AnalysisWindow.cs index d842410e..d755ac7a 100644 --- a/src/AngleSharp.Css.Tests/Extensions/AnalysisWindow.cs +++ b/src/AngleSharp.Css.Tests/Extensions/AnalysisWindow.cs @@ -279,15 +279,134 @@ public void GetCascadedValueOfTextTransformFromElementStyle() Assert.AreEqual("uppercase", styleNormal.GetTextTransform()); } + [Test] + public void GetCascadedValueOfTextTransformFromElementStyleWithElementApi() + { + var sourceCode = "

Bold text"; + + var document = ParseDocument(sourceCode); + var element = document.QuerySelector("span"); + var styleNormal = element.ComputeStyle(); + Assert.IsNotNull(styleNormal); + Assert.AreEqual("uppercase", styleNormal.GetTextTransform()); + } + [Test] public async Task NullSelectorStillWorks_Issue52() { var sheet = ParseStyleSheet("a {}"); var document = await sheet.Context.OpenAsync(res => res.Content("")); - sheet.Add(new CssStyleRule(sheet)); var sc = new StyleCollection(new[] { sheet }, new DefaultRenderDevice()); var decl = sc.ComputeCascadedStyle(document.Body); Assert.IsNotNull(decl); } + + [Test] + public async Task PriorityInMultiSelectorIsEvaluatedPerMatch() + { + var sheet = ParseStyleSheet(@"#target {color: blue} h3, #nottarget { color: purple; } "); + var document = await sheet.Context.OpenAsync(res => res.Content(@"

Test

")); + var sc = new StyleCollection(new[] { sheet }, new DefaultRenderDevice()); + var style = sc.ComputeCascadedStyle(document.QuerySelector("h3")); + Assert.AreEqual("rgba(0, 0, 255, 1)", style.GetColor()); + } + + [Test] + public async Task ComputesAbsoluteValuesFromRelative_Issue136() + { + var sheet = ParseStyleSheet(@"p { font-size: 1.5em }"); + var document = await sheet.Context.OpenAsync(res => res.Content(@"

This is only a test.

")); + var sc = new StyleCollection(new[] { sheet }, new DefaultRenderDevice()); + var style = sc.ComputeDeclarations(document.QuerySelector("span")); + Assert.AreEqual("24px", style.GetFontSize()); + } + + [Test] + public async Task ResolvesCssVariables_Issue62() + { + var sheet = ParseStyleSheet(@" + :root { + --color: #FFFFFF; + } + + p { + color: var(--color); + }"); + var document = await sheet.Context.OpenAsync(res => res.Content(@"

This is a test

")); + var sc = new StyleCollection(new[] { sheet }, new DefaultRenderDevice()); + var style = sc.ComputeDeclarations(document.QuerySelector("p")); + Assert.AreEqual("rgba(255, 255, 255, 1)", style.GetColor()); + } + + [Test] + public async Task ResolvesCssVariablesWithUnusedFallback_Issue62() + { + var sheet = ParseStyleSheet(@" + :root { + --color: #FFFFFF; + } + + p { + color: var(--color, green); + }"); + var document = await sheet.Context.OpenAsync(res => res.Content(@"

This is a test

")); + var sc = new StyleCollection(new[] { sheet }, new DefaultRenderDevice()); + var style = sc.ComputeDeclarations(document.QuerySelector("p")); + Assert.AreEqual("rgba(255, 255, 255, 1)", style.GetColor()); + } + + [Test] + public async Task ResolvesCssVariablesWithUsedFallback_Issue62() + { + var sheet = ParseStyleSheet(@" + :root {} + + p { + color: var(--color, green); + }"); + var document = await sheet.Context.OpenAsync(res => res.Content(@"

This is a test

")); + var sc = new StyleCollection(new[] { sheet }, new DefaultRenderDevice()); + var style = sc.ComputeDeclarations(document.QuerySelector("p")); + Assert.AreEqual("rgba(0, 128, 0, 1)", style.GetColor()); + } + + [Test] + public async Task ResolvesCssVariablesWithUsedFallbackVarReference_Issue62() + { + var sheet = ParseStyleSheet(@" + :root { + --defaultColor: green; + } + + p { + color: var(--color, var(--defaultColor)); + }"); + var document = await sheet.Context.OpenAsync(res => res.Content(@"

This is a test

")); + var sc = new StyleCollection(new[] { sheet }, new DefaultRenderDevice()); + var style = sc.ComputeDeclarations(document.QuerySelector("p")); + Assert.AreEqual("rgba(0, 128, 0, 1)", style.GetColor()); + } + + [Test] + public async Task ResolvesCssVariablesWithCascade_Issue62() + { + var sheet = ParseStyleSheet(@" + :root { + --color: blue; + --defaultColor: red; + } + + body { + --color: green; + } + + p { + color: var(--color, var(--defaultColor)); + }"); + var document = await sheet.Context.OpenAsync(res => res.Content(@"

This is a test

")); + var sc = new StyleCollection(new[] { sheet }, new DefaultRenderDevice()); + var style = sc.ComputeDeclarations(document.QuerySelector("p")); + Assert.AreEqual("rgba(0, 128, 0, 1)", style.GetColor()); + } } } diff --git a/src/AngleSharp.Css.Tests/Extensions/Elements.cs b/src/AngleSharp.Css.Tests/Extensions/Elements.cs index 0134dc61..c5dff0e6 100644 --- a/src/AngleSharp.Css.Tests/Extensions/Elements.cs +++ b/src/AngleSharp.Css.Tests/Extensions/Elements.cs @@ -39,6 +39,7 @@ public async Task DownloadResources() }; var config = Configuration.Default .WithDefaultLoader(loaderOptions) + .WithRenderDevice() .WithCss(); var document = "
".ToHtmlDocument(config); var tree = document.DefaultView.Render(); diff --git a/src/AngleSharp.Css.Tests/Extensions/InnerText.cs b/src/AngleSharp.Css.Tests/Extensions/InnerText.cs index ab55c6c9..f12fb385 100644 --- a/src/AngleSharp.Css.Tests/Extensions/InnerText.cs +++ b/src/AngleSharp.Css.Tests/Extensions/InnerText.cs @@ -37,6 +37,9 @@ public void SetInnerText(String fixture, String expectedInnerText, String expect // paragraph [TestCase("

test

", "test")] [TestCase("

test1

test2

", "test1\n\ntest2")] + [TestCase("

test1

\n

test2

", "test1\n\ntest2")] + [TestCase("

test1

\n \n

test2

", "test1\n\ntest2")] + [TestCase("

test1

a\n \n b

test2

", "test1\n\na b\n\ntest2")] // block-level [TestCase("
test1
test2
test3
", "test1\ntest2\ntest3")] [TestCase(@"test1test2test3", "test1\ntest2\ntest3")] diff --git a/src/AngleSharp.Css.Tests/Extensions/Nesting.cs b/src/AngleSharp.Css.Tests/Extensions/Nesting.cs new file mode 100644 index 00000000..577f386d --- /dev/null +++ b/src/AngleSharp.Css.Tests/Extensions/Nesting.cs @@ -0,0 +1,118 @@ +namespace AngleSharp.Css.Tests.Extensions +{ + using AngleSharp.Css.Dom; + using AngleSharp.Dom; + using NUnit.Framework; + using static CssConstructionFunctions; + + [TestFixture] + public class NestingTests + { + [Test] + public void SimpleSelectorNestingImplicit() + { + var source = @"
Larger and green"; + var document = ParseDocument(source); + var window = document.DefaultView; + var element = document.QuerySelector(".bar"); + var style = window.GetComputedStyle(element); + + Assert.AreEqual("22.4px", style.GetFontSize()); + } + + [Test] + public void SimpleSelectorNestingImplicitDeclarations() + { + var source = @"
Larger and green"; + var document = ParseDocument(source); + var window = document.DefaultView; + var element = document.QuerySelector(".bar"); + var styleCollection = window.GetStyleCollection(); + var style = styleCollection.GetDeclarations(element); + + Assert.AreEqual("1.4rem", style.GetFontSize()); + } + + [Test] + public void SimpleSelectorNestingExplicit() + { + var source = @"
Larger and green"; + var document = ParseDocument(source); + var window = document.DefaultView; + var element = document.QuerySelector(".bar"); + var style = window.GetComputedStyle(element); + + Assert.AreEqual("22.4px", 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.AreEqual("22.4px", 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.Tests/Functions/CssColorFunction.cs b/src/AngleSharp.Css.Tests/Functions/CssColorFunction.cs index 7396400c..cf36e11b 100644 --- a/src/AngleSharp.Css.Tests/Functions/CssColorFunction.cs +++ b/src/AngleSharp.Css.Tests/Functions/CssColorFunction.cs @@ -19,6 +19,171 @@ public void ColorNotParsedCorrectly_Issue109() var color = s.GetColor(); Assert.AreEqual("rgba(0, 17, 0, 1)", color); } + + [Test] + public void ParseRgbWithSpacesL4_Issue131() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(255, 122, 127, 0.8)", color); + } + + [Test] + public void ParseRgbWithSpacesInPercentL4_Issue131() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(255, 26, 128, 0.7)", color); + } + + [Test] + public void ParseRgbWithSpacesAndNoneL4_Issue131() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(255, 0, 128, 0.35)", color); + } + + [Test] + public void ParseOklabToRgb_First() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(125, 35, 40, 1)", color); + } + + [Test] + public void ParseOklabToRgb_Second() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(198, 93, 7, 1)", color); + } + + [Test] + public void ParseOklabToRgb_Alpha() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(198, 93, 7, 0.5)", color); + } + + [Test] + public void ParseOklchToRgb_First() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(125, 35, 40, 1)", color); + } + + [Test] + public void ParseOklchToRgb_Second() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(198, 93, 7, 1)", color); + } + + [Test] + public void ParseOklchToRgb_Alpha() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(198, 93, 7, 0.5)", color); + } + + [Test] + public void ParseLabToRgb_First() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(125, 35, 40, 1)", color); + } + + [Test] + public void ParseLabToRgb_Second() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(198, 93, 6, 1)", color); + } + + [Test] + public void ParseLabToRgb_Alpha() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(198, 93, 6, 0.5)", color); + } + + [Test] + public void ParseLchToRgb_First() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(125, 35, 40, 1)", color); + } + + [Test] + public void ParseLchToRgb_Second() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(198, 93, 6, 1)", color); + } + + [Test] + public void ParseLchToRgb_Alpha() + { + var html = @"

    Text

    "; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var s = p.GetStyle(); + var color = s.GetColor(); + Assert.AreEqual("rgba(198, 93, 6, 0.5)", color); + } } } diff --git a/src/AngleSharp.Css.Tests/Library/ComputedStyle.cs b/src/AngleSharp.Css.Tests/Library/ComputedStyle.cs new file mode 100644 index 00000000..6ee3f880 --- /dev/null +++ b/src/AngleSharp.Css.Tests/Library/ComputedStyle.cs @@ -0,0 +1,29 @@ +namespace AngleSharp.Css.Tests.Library +{ + using AngleSharp.Dom; + using AngleSharp.Html.Dom; + using NUnit.Framework; + using System.Threading.Tasks; + + [TestFixture] + public class ComputedStyleTests + { + [Test] + public async Task TransformEmToPx_Issue136() + { + // .With() + var config = Configuration.Default.WithCss(); + var context = BrowsingContext.New(config); + var source = "

    This is only a test.

    "; + var cssSheet = "p { font-size: 1.5em }"; + var document = await context.OpenAsync(req => req.Content(source)); + var style = document.CreateElement(); + style.TextContent = cssSheet; + document.Head.AppendChild(style); + var span = document.QuerySelector("span"); + var fontSize = span.ComputeCurrentStyle().GetProperty("font-size"); + + Assert.AreEqual("24px", fontSize.Value); + } + } +} diff --git a/src/AngleSharp.Css.Tests/Library/ExtractInfos.cs b/src/AngleSharp.Css.Tests/Library/ExtractInfos.cs new file mode 100644 index 00000000..86ecf548 --- /dev/null +++ b/src/AngleSharp.Css.Tests/Library/ExtractInfos.cs @@ -0,0 +1,31 @@ +namespace AngleSharp.Css.Tests.Library +{ + using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; + using AngleSharp.Dom; + using AngleSharp.Html.Dom; + using NUnit.Framework; + + [TestFixture] + public class ExtractInfosTests + { + [Test] + public void GetFontUrl_Issue126() + { + var css = @"@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + src: url(https://example.org/some-font.ttf) format('truetype'); +}"; + var html = $""; + var document = html.ToHtmlDocument(Configuration.Default.WithCss()); + var style = document.QuerySelector("style"); + var sheet = style.Sheet as ICssStyleSheet; + var fontFace = sheet.Rules[0] as ICssFontFaceRule; + var src = fontFace.GetProperty("src").RawValue; + var url = ((src as ICssMultipleValue)[0] as ICssMultipleValue)[0] as CssUrlValue; + Assert.AreEqual("https://example.org/some-font.ttf", url.Path); + } + } +} diff --git a/src/AngleSharp.Css.Tests/Library/StringRepresentation.cs b/src/AngleSharp.Css.Tests/Library/StringRepresentation.cs index 8aae59f3..f2628c5a 100644 --- a/src/AngleSharp.Css.Tests/Library/StringRepresentation.cs +++ b/src/AngleSharp.Css.Tests/Library/StringRepresentation.cs @@ -2,9 +2,14 @@ namespace AngleSharp.Css.Tests.Library { using AngleSharp.Css.Dom; using AngleSharp.Css.Parser; + using AngleSharp.Css.RenderTree; + using AngleSharp.Css.Tests.Mocks; using AngleSharp.Css.Values; + using AngleSharp.Dom; + using AngleSharp.Html.Dom; using NUnit.Framework; using System.IO; + using System.Threading.Tasks; using static CssConstructionFunctions; [TestFixture] @@ -27,21 +32,21 @@ public void PrettyStyleFormatterStringifyShouldWork_Issue41() [Test] public void SimpleColorWorksWithHexOutput_Issue96() { - var color = new Color(65, 12, 48); - Color.UseHex = true; + var color = new CssColorValue(65, 12, 48); + CssColorValue.UseHex = true; var text = color.CssText; - Color.UseHex = false; + CssColorValue.UseHex = false; Assert.AreEqual("#410C30", text); } [Test] - public void TransparentColorDoesNotWorkWithHexOutput_Issue96() + public void TransparentColorWorksWithHexOutput_Issue132() { - var color = new Color(65, 12, 48, 10); - Color.UseHex = true; + var color = new CssColorValue(65, 12, 48, 10); + CssColorValue.UseHex = true; var text = color.CssText; - Color.UseHex = false; - Assert.AreEqual("rgba(65, 12, 48, 0.04)", text); + CssColorValue.UseHex = false; + Assert.AreEqual("#410C300A", text); } [Test] @@ -105,5 +110,111 @@ public void EscapePropertyNames_UnknownDeclaration_Issue120() Assert.AreEqual(css, generatedCss); } + + [Test] + public void CssTextShouldNotAddReplacementCharacter_Issue123() + { + var html = @"Ipsum"; + var dom = html.ToHtmlDocument(Configuration.Default.WithCss(new CssParserOptions + { + IsIncludingUnknownDeclarations = true, + IsIncludingUnknownRules = true, + IsToleratingInvalidSelectors = true, + })); + var div = dom.Body?.FirstElementChild; + var style = div.GetStyle(); + var css = style.ToCss(); + + Assert.AreEqual("background-image: var(--urlSpellingErrorV2,url(\"https://www.example.com/))", css); + } + + [Test] + public void CssTextShouldNotTrailingSemicolonCharacter_Issue123() + { + var html = @"Ipsum"; + var dom = html.ToHtmlDocument(Configuration.Default.WithCss(new CssParserOptions + { + IsIncludingUnknownDeclarations = true, + IsIncludingUnknownRules = true, + IsToleratingInvalidSelectors = true, + })); + var div = dom.Body?.FirstElementChild; + var style = div.GetStyle(); + var css = style.ToCss(); + + Assert.AreEqual("color: rgba(255, 0, 0, 1)", css); + } + + [Test] + public void BorderWithEmptyPx_Issue129() + { + var html = "
    "; + var dom = html.ToHtmlDocument(Configuration.Default.WithCss()); + var div = dom.Body?.FirstElementChild; + var style = div.GetStyle(); + var css = style.ToCss(); + + Assert.AreEqual("border-width: 1px", css); + } + + [Test] + public async Task MediaListForLinkedStyleSheet_Issue133() + { + var html = ""; + var mockRequester = new MockRequester(); + mockRequester.BuildResponse(request => + { + if (request.Address.Path.EndsWith("style.css")) + { + return "div#A { color: blue; }"; + } + + return null; + }); + var config = Configuration.Default.WithCss().WithMockRequester(mockRequester); + var context = BrowsingContext.New(config); + var document = await context.OpenAsync((res) => res.Content(html)); + var link = document.QuerySelector("link"); + Assert.AreEqual("", link.Sheet.Media.MediaText); + Assert.IsTrue(link.Sheet.Media.Validate(new DefaultRenderDevice())); + } + + [Test] + public async Task ExternalCssNotConsidered_Issue140() + { + var html = @" + + + "; + var mockRequester = new MockRequester(); + mockRequester.BuildResponse(request => + { + if (request.Address.Path.EndsWith("url.css")) + { + return @"label, .test { + min-width: 50px; + border: 1px solid green; +}"; + } + + return null; + }); + var config = Configuration.Default + .WithRenderDevice(new DefaultRenderDevice + { + DeviceWidth = 1920, + DeviceHeight = 1080, + }) + .WithCss() + .WithMockRequester(mockRequester); + var context = BrowsingContext.New(config); + var document = await context.OpenAsync((res) => res.Content(html)); + var window = document.DefaultView; + var tree = window.Render(); + var label = tree.Find(document.QuerySelector("label")); + var minWidth = window.GetComputedStyle(label.Ref as IHtmlElement).GetMinWidth(); + + Assert.AreEqual("50px", minWidth); + } } } diff --git a/src/AngleSharp.Css.Tests/Rules/CssKeyframeRule.cs b/src/AngleSharp.Css.Tests/Rules/CssKeyframeRule.cs index d2b63469..efcc9a55 100644 --- a/src/AngleSharp.Css.Tests/Rules/CssKeyframeRule.cs +++ b/src/AngleSharp.Css.Tests/Rules/CssKeyframeRule.cs @@ -1,4 +1,4 @@ -namespace AngleSharp.Css.Tests.Rules +namespace AngleSharp.Css.Tests.Rules { using NUnit.Framework; using System.Linq; @@ -74,5 +74,15 @@ public void KeyframeRuleWith0AndNoDeclarations() Assert.AreEqual(1, rule.Key.Stops.Count()); Assert.AreEqual(0, rule.Style.Length); } + + [Test] + public void KeyframeRuleWithPercentage_Issue128() + { + var rule = ParseKeyframeRule(@" 0.52%, 50.0%,92.82% { }"); + Assert.IsNotNull(rule); + Assert.AreEqual("0.52%, 50%, 92.82%", rule.KeyText); + Assert.AreEqual(3, rule.Key.Stops.Count()); + Assert.AreEqual(0, rule.Style.Length); + } } } diff --git a/src/AngleSharp.Css.Tests/Rules/CssMediaList.cs b/src/AngleSharp.Css.Tests/Rules/CssMediaList.cs index b8cd8cf4..b6acf22f 100644 --- a/src/AngleSharp.Css.Tests/Rules/CssMediaList.cs +++ b/src/AngleSharp.Css.Tests/Rules/CssMediaList.cs @@ -1,4 +1,4 @@ -namespace AngleSharp.Css.Tests.Rules +namespace AngleSharp.Css.Tests.Rules { using AngleSharp.Css.Dom; using NUnit.Framework; @@ -132,7 +132,7 @@ public void FeatureMinWidthMediaList() } [Test] - public void OnlyFeatureWidthMediaList() + public void OnlyFeatureWidthMediaListInvalid() { var source = @"@media only (width: 640px) { h1 { color: green } @@ -141,7 +141,23 @@ public void OnlyFeatureWidthMediaList() Assert.AreEqual(1, sheet.Rules.Length); Assert.IsInstanceOf(sheet.Rules[0]); var media = (CssMediaRule)sheet.Rules[0]; - Assert.AreEqual("only (width: 640px)", media.Media.MediaText); + Assert.AreEqual("not all", media.Media.MediaText); + var list = media.Media; + Assert.AreEqual(1, list.Length); + Assert.AreEqual(1, media.Rules.Length); + } + + [Test] + public void OnlyFeatureWidthScreenAndMediaList() + { + var source = @"@media only screen and (width: 640px) { + h1 { color: green } +}"; + var sheet = ParseStyleSheet(source); + Assert.AreEqual(1, sheet.Rules.Length); + Assert.IsInstanceOf(sheet.Rules[0]); + var media = (CssMediaRule)sheet.Rules[0]; + Assert.AreEqual("only screen and (width: 640px)", media.Media.MediaText); var list = media.Media; Assert.AreEqual(1, list.Length); Assert.AreEqual(1, media.Rules.Length); @@ -187,7 +203,7 @@ public void NoMediaQueryGivenSkip() Assert.AreEqual(1, sheet.Rules.Length); Assert.AreEqual(CssRuleType.Media, sheet.Rules[0].Type); var media = sheet.Rules[0] as ICssMediaRule; - Assert.AreEqual("not all", media.ConditionText); + Assert.AreEqual("", media.ConditionText); Assert.AreEqual(1, media.Rules.Length); } @@ -393,5 +409,53 @@ public void CssMediaListApiWithAppendDeleteAndTextShouldWork() Assert.AreEqual(media[2], list[1]); Assert.AreEqual(String.Concat(media[0], ", ", media[2]), list.MediaText); } + + [Test] + public void ReplacesInvalidPartsCommaWithNotAll() + { + var source = @"@media (example, all,), speech { + h1 { color: green } +}"; + var sheet = ParseStyleSheet(source); + Assert.AreEqual(1, sheet.Rules.Length); + Assert.IsInstanceOf(sheet.Rules[0]); + var media = (CssMediaRule)sheet.Rules[0]; + Assert.AreEqual("not all, speech", media.Media.MediaText); + var list = media.Media; + Assert.AreEqual(2, list.Length); + Assert.AreEqual(1, media.Rules.Length); + } + + [Test] + public void ReplacesInvalidPartsAmpersandWithNotAll() + { + var source = @"@media test&, speech { + h1 { color: green } +}"; + var sheet = ParseStyleSheet(source); + Assert.AreEqual(1, sheet.Rules.Length); + Assert.IsInstanceOf(sheet.Rules[0]); + var media = (CssMediaRule)sheet.Rules[0]; + Assert.AreEqual("not all, speech", media.Media.MediaText); + var list = media.Media; + Assert.AreEqual(2, list.Length); + Assert.AreEqual(1, media.Rules.Length); + } + + [Test] + public void ReplacesUnclosedParansWithNotAll() + { + var source = @"@media (example, speech { + h1 { color: green } +}"; + var sheet = ParseStyleSheet(source); + Assert.AreEqual(1, sheet.Rules.Length); + Assert.IsInstanceOf(sheet.Rules[0]); + var media = (CssMediaRule)sheet.Rules[0]; + Assert.AreEqual("not all", media.Media.MediaText); + var list = media.Media; + Assert.AreEqual(1, list.Length); + Assert.AreEqual(1, media.Rules.Length); + } } } diff --git a/src/AngleSharp.Css.Tests/Rules/CssPageRule.cs b/src/AngleSharp.Css.Tests/Rules/CssPageRule.cs new file mode 100644 index 00000000..29ef1f69 --- /dev/null +++ b/src/AngleSharp.Css.Tests/Rules/CssPageRule.cs @@ -0,0 +1,32 @@ +namespace AngleSharp.Css.Tests.Rules +{ + using AngleSharp.Css.Dom; + using NUnit.Framework; + using System.Linq; + + [TestFixture] + public class CssPageRuleTests + { + [Test] + public void SemiColonsShouldNotMissInPageRule_Issue135() + { + var html = ""; + var document = html.ToHtmlDocument(Configuration.Default.WithCss()); + var styleSheet = document.StyleSheets.OfType().First(); + var css = styleSheet.ToCss(); + + Assert.AreEqual("@page { margin-top: 25mm; margin-right: 25mm }", css); + } + + [Test] + public void PageRuleShouldRecombineShorthandDeclaration_Issue135() + { + var html = ""; + var document = html.ToHtmlDocument(Configuration.Default.WithCss()); + var styleSheet = document.StyleSheets.OfType().First(); + var css = styleSheet.ToCss(); + + Assert.AreEqual("@page { margin: 25mm }", css); + } + } +} diff --git a/src/AngleSharp.Css.Tests/Styling/BasicStyling.cs b/src/AngleSharp.Css.Tests/Styling/BasicStyling.cs index 9af111fd..1133852f 100644 --- a/src/AngleSharp.Css.Tests/Styling/BasicStyling.cs +++ b/src/AngleSharp.Css.Tests/Styling/BasicStyling.cs @@ -210,5 +210,13 @@ public void MinifyWithMultipleDeclarations() var result = sheet.ToCss(new MinifyStyleFormatter()); Assert.AreEqual("h1{top:0;left:2px;border:none}h2{border:1px solid rgba(255, 0, 0, 1)}", result); } + + [Test] + public void MinifyMinimizesProperties_Issue89() + { + var sheet = ParseStyleSheet("a { grid-area: aa / aa / aa / aa }"); + var result = sheet.ToCss(new MinifyStyleFormatter()); + Assert.AreEqual("a{grid-area:aa}", result); + } } } diff --git a/src/AngleSharp.Css.Tests/Styling/CssCases.cs b/src/AngleSharp.Css.Tests/Styling/CssCases.cs index cf46959b..fb1e6593 100644 --- a/src/AngleSharp.Css.Tests/Styling/CssCases.cs +++ b/src/AngleSharp.Css.Tests/Styling/CssCases.cs @@ -15,6 +15,7 @@ private static ICssStyleSheet ParseSheet(String text) { IsIncludingUnknownDeclarations = true, IsIncludingUnknownRules = true, + IsExcludingNesting = true, IsToleratingInvalidSelectors = false, }); } @@ -319,9 +320,9 @@ public void StyleSheetEscapes() /*#\{\}{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 +347,57 @@ public void StyleSheetEscapes() 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(@"#

    ", ((ICssStyleRule)sheet.Rules[23]).SelectorText); + Assert.AreEqual(@"#\", ((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"]); } diff --git a/src/AngleSharp.Css.Tests/Values/Color.cs b/src/AngleSharp.Css.Tests/Values/Color.cs index 2e252070..9ef1a0a8 100644 --- a/src/AngleSharp.Css.Tests/Values/Color.cs +++ b/src/AngleSharp.Css.Tests/Values/Color.cs @@ -4,331 +4,326 @@ namespace AngleSharp.Css.Tests.Values using NUnit.Framework; [TestFixture] - public class ColorTests + public class CssColorValueTests { [Test] - public void ColorInvalidHexDigitString() + public void CssColorValueInvalidHexDigitString() { - Color hc; var color = "BCDEFG"; - var result = Color.TryFromHex(color, out hc); + var result = CssColorValue.TryFromHex(color, out var hc); Assert.IsFalse(result); } [Test] - public void ColorValidFourLetterString() + public void CssColorValueValidFourLetterString() { - Color hc; var color = "abcd"; - var result = Color.TryFromHex(color, out hc); - Assert.AreEqual(new Color(170, 187, 204, 221), hc); + var result = CssColorValue.TryFromHex(color, out var hc); + Assert.AreEqual(new CssColorValue(170, 187, 204, 221), hc); Assert.IsTrue(result); } [Test] - public void ColorInvalidLengthString() + public void CssColorValueInvalidLengthString() { - Color hc; var color = "abcde"; - var result = Color.TryFromHex(color, out hc); + var result = CssColorValue.TryFromHex(color, out var hc); Assert.IsFalse(result); } [Test] - public void ColorValidLengthShortString() + public void CssColorValueValidLengthShortString() { - Color hc; var color = "fff"; - var result = Color.TryFromHex(color, out hc); + var result = CssColorValue.TryFromHex(color, out var hc); Assert.IsTrue(result); } [Test] - public void ColorValidLengthLongString() + public void CssColorValueValidLengthLongString() { - Color hc; var color = "fffabc"; - var result = Color.TryFromHex(color, out hc); + var result = CssColorValue.TryFromHex(color, out var hc); Assert.IsTrue(result); } [Test] - public void ColorWhiteShortString() + public void CssColorValueWhiteShortString() { var color = "fff"; - var result = Color.FromHex(color); - Assert.AreEqual(Color.FromRgb(255, 255, 255), result); + var result = CssColorValue.FromHex(color); + Assert.AreEqual(CssColorValue.FromRgb(255, 255, 255), result); } [Test] - public void ColorRedShortString() + public void CssColorValueRedShortString() { var color = "f00"; - var result = Color.FromHex(color); - Assert.AreEqual(Color.FromRgb(255, 0, 0), result); + var result = CssColorValue.FromHex(color); + Assert.AreEqual(CssColorValue.FromRgb(255, 0, 0), result); } [Test] - public void ColorFromRedName() + public void CssColorValueFromRedName() { var color = "red"; - var result = Color.FromName(color); + var result = CssColorValue.FromName(color); Assert.IsTrue(result.HasValue); - Assert.AreEqual(Color.Red, result); + Assert.AreEqual(CssColorValue.Red, result); } [Test] - public void ColorFromWhiteName() + public void CssColorValueFromWhiteName() { var color = "white"; - var result = Color.FromName(color); + var result = CssColorValue.FromName(color); Assert.IsTrue(result.HasValue); - Assert.AreEqual(Color.White, result); + Assert.AreEqual(CssColorValue.White, result); } [Test] - public void ColorFromUnknownName() + public void CssColorValueFromUnknownName() { var color = "bla"; - var result = Color.FromName(color); + var result = CssColorValue.FromName(color); Assert.IsFalse(result.HasValue); } [Test] - public void ColorMixedLongString() + public void CssColorValueMixedLongString() { var color = "facc36"; - var result = Color.FromHex(color); - Assert.AreEqual(Color.FromRgb(250, 204, 54), result); + var result = CssColorValue.FromHex(color); + Assert.AreEqual(CssColorValue.FromRgb(250, 204, 54), result); } [Test] - public void ColorMixedEightDigitLongStringTransparent() + public void CssColorValueMixedEightDigitLongStringTransparent() { var color = "facc3600"; - var result = Color.FromHex(color); - Assert.AreEqual(Color.FromRgba(250, 204, 54, 0), result); + var result = CssColorValue.FromHex(color); + Assert.AreEqual(CssColorValue.FromRgba(250, 204, 54, 0), result); } [Test] - public void ColorMixedEightDigitLongStringOpaque() + public void CssColorValueMixedEightDigitLongStringOpaque() { var color = "facc36ff"; - var result = Color.FromHex(color); - Assert.AreEqual(Color.FromRgba(250, 204, 54, 1), result); + var result = CssColorValue.FromHex(color); + Assert.AreEqual(CssColorValue.FromRgba(250, 204, 54, 1), result); } [Test] - public void ColorMixBlackOnWhite50Percent() + public void CssColorValueMixBlackOnWhite50Percent() { - var color1 = Color.Black; - var color2 = Color.White; - var mix = Color.Mix(0.5, color1, color2); - Assert.AreEqual(Color.FromRgb(127, 127, 127), mix); + var color1 = CssColorValue.Black; + var color2 = CssColorValue.White; + var mix = CssColorValue.Mix(0.5, color1, color2); + Assert.AreEqual(CssColorValue.FromRgb(127, 127, 127), mix); } [Test] - public void ColorMixRedOnWhite75Percent() + public void CssColorValueMixRedOnWhite75Percent() { - var color1 = Color.Red; - var color2 = Color.White; - var mix = Color.Mix(0.75, color1, color2); - Assert.AreEqual(Color.FromRgb(255, 63, 63), mix); + var color1 = CssColorValue.Red; + var color2 = CssColorValue.White; + var mix = CssColorValue.Mix(0.75, color1, color2); + Assert.AreEqual(CssColorValue.FromRgb(255, 63, 63), mix); } [Test] - public void ColorMixBlueOnWhite10Percent() + public void CssColorValueMixBlueOnWhite10Percent() { - var color1 = Color.Blue; - var color2 = Color.White; - var mix = Color.Mix(0.1, color1, color2); - Assert.AreEqual(Color.FromRgb(229, 229, 255), mix); + var color1 = CssColorValue.Blue; + var color2 = CssColorValue.White; + var mix = CssColorValue.Mix(0.1, color1, color2); + Assert.AreEqual(CssColorValue.FromRgb(229, 229, 255), mix); } [Test] - public void ColorMixGreenOnRed30Percent() + public void CssColorValueMixGreenOnRed30Percent() { - var color1 = Color.PureGreen; - var color2 = Color.Red; - var mix = Color.Mix(0.3, color1, color2); - Assert.AreEqual(Color.FromRgb(178, 76, 0), mix); + var color1 = CssColorValue.PureGreen; + var color2 = CssColorValue.Red; + var mix = CssColorValue.Mix(0.3, color1, color2); + Assert.AreEqual(CssColorValue.FromRgb(178, 76, 0), mix); } [Test] - public void ColorMixWhiteOnBlack20Percent() + public void CssColorValueMixWhiteOnBlack20Percent() { - var color1 = Color.White; - var color2 = Color.Black; - var mix = Color.Mix(0.2, color1, color2); - Assert.AreEqual(Color.FromRgb(51, 51, 51), mix); + var color1 = CssColorValue.White; + var color2 = CssColorValue.Black; + var mix = CssColorValue.Mix(0.2, color1, color2); + Assert.AreEqual(CssColorValue.FromRgb(51, 51, 51), mix); } [Test] - public void ColorHslBlackMixed() + public void CssColorValueHslBlackMixed() { - var color = Color.FromHsl(0, 1, 0); - Assert.AreEqual(Color.Black, color); + var color = CssColorValue.FromHsl(0, 1, 0); + Assert.AreEqual(CssColorValue.Black, color); } [Test] - public void ColorHslBlackMixed1() + public void CssColorValueHslBlackMixed1() { - var color = Color.FromHsl(0, 1, 0); - Assert.AreEqual(Color.Black, color); + var color = CssColorValue.FromHsl(0, 1, 0); + Assert.AreEqual(CssColorValue.Black, color); } [Test] - public void ColorHslBlackMixed2() + public void CssColorValueHslBlackMixed2() { - var color = Color.FromHsl(0.5f, 1, 0); - Assert.AreEqual(Color.Black, color); + var color = CssColorValue.FromHsl(0.5f, 1, 0); + Assert.AreEqual(CssColorValue.Black, color); } [Test] - public void ColorHslRedPure() + public void CssColorValueHslRedPure() { - var color = Color.FromHsl(0, 1, 0.5f); - Assert.AreEqual(Color.Red, color); + var color = CssColorValue.FromHsl(0, 1, 0.5f); + Assert.AreEqual(CssColorValue.Red, color); } [Test] - public void ColorHslGreenPure() + public void CssColorValueHslGreenPure() { - var color = Color.FromHsl(1f / 3f, 1, 0.5f); - Assert.AreEqual(Color.PureGreen, color); + var color = CssColorValue.FromHsl(1f / 3f, 1, 0.5f); + Assert.AreEqual(CssColorValue.PureGreen, color); } [Test] - public void ColorHslBluePure() + public void CssColorValueHslBluePure() { - var color = Color.FromHsl(2f / 3f, 1, 0.5f); - Assert.AreEqual(Color.Blue, color); + var color = CssColorValue.FromHsl(2f / 3f, 1, 0.5f); + Assert.AreEqual(CssColorValue.Blue, color); } [Test] - public void ColorHslBlackPure() + public void CssColorValueHslBlackPure() { - var color = Color.FromHsl(0, 0, 0); - Assert.AreEqual(Color.Black, color); + var color = CssColorValue.FromHsl(0, 0, 0); + Assert.AreEqual(CssColorValue.Black, color); } [Test] - public void ColorHslMagentaPure() + public void CssColorValueHslMagentaPure() { - var color = Color.FromHsl(300f / 360f, 1, 0.5f); - Assert.AreEqual(Color.Magenta, color); + var color = CssColorValue.FromHsl(300f / 360f, 1, 0.5f); + Assert.AreEqual(CssColorValue.Magenta, color); } [Test] - public void ColorHslYellowGreenMixed() + public void CssColorValueHslYellowGreenMixed() { - var color = Color.FromHsl(1f / 4f, 0.75f, 0.63f); - Assert.AreEqual(Color.FromRgb(161, 232, 90), color); + var color = CssColorValue.FromHsl(1f / 4f, 0.75f, 0.63f); + Assert.AreEqual(CssColorValue.FromRgb(161, 232, 90), color); } [Test] - public void ColorHslGrayBlueMixed() + public void CssColorValueHslGrayBlueMixed() { - var color = Color.FromHsl(210f / 360f, 0.25f, 0.25f); - Assert.AreEqual(Color.FromRgb(48, 64, 80), color); + var color = CssColorValue.FromHsl(210f / 360f, 0.25f, 0.25f); + Assert.AreEqual(CssColorValue.FromRgb(48, 64, 80), color); } [Test] - public void ColorFlexHexOneLetter() + public void CssColorValueFlexHexOneLetter() { - var color = Color.FromFlexHex("F"); - Assert.AreEqual(Color.FromRgb(0xf, 0x0, 0x0), color); + var color = CssColorValue.FromFlexHex("F"); + Assert.AreEqual(CssColorValue.FromRgb(0xf, 0x0, 0x0), color); } [Test] - public void ColorFlexHexTwoLetters() + public void CssColorValueFlexHexTwoLetters() { - var color = Color.FromFlexHex("0F"); - Assert.AreEqual(Color.FromRgb(0x0, 0xf, 0x0), color); + var color = CssColorValue.FromFlexHex("0F"); + Assert.AreEqual(CssColorValue.FromRgb(0x0, 0xf, 0x0), color); } [Test] - public void ColorFlexHexFourLetters() + public void CssColorValueFlexHexFourLetters() { - var color = Color.FromFlexHex("0F0F"); - Assert.AreEqual(Color.FromRgb(0xf, 0xf, 0x0), color); + var color = CssColorValue.FromFlexHex("0F0F"); + Assert.AreEqual(CssColorValue.FromRgb(0xf, 0xf, 0x0), color); } [Test] - public void ColorFlexHexSevenLetters() + public void CssColorValueFlexHexSevenLetters() { - var color = Color.FromFlexHex("0F0F0F0"); - Assert.AreEqual(Color.FromRgb(0xf, 0xf0, 0x0), color); + var color = CssColorValue.FromFlexHex("0F0F0F0"); + Assert.AreEqual(CssColorValue.FromRgb(0xf, 0xf0, 0x0), color); } [Test] - public void ColorFlexHexFifteenLetters() + public void CssColorValueFlexHexFifteenLetters() { - var color = Color.FromFlexHex("1234567890ABCDE"); - Assert.AreEqual(Color.FromRgb(0x12, 0x67, 0xab), color); + var color = CssColorValue.FromFlexHex("1234567890ABCDE"); + Assert.AreEqual(CssColorValue.FromRgb(0x12, 0x67, 0xab), color); } [Test] - public void ColorFlexHexExtremelyLong() + public void CssColorValueFlexHexExtremelyLong() { - var color = Color.FromFlexHex("1234567890ABCDE1234567890ABCDE"); - Assert.AreEqual(Color.FromRgb(0x34, 0xcd, 0x89), color); + var color = CssColorValue.FromFlexHex("1234567890ABCDE1234567890ABCDE"); + Assert.AreEqual(CssColorValue.FromRgb(0x34, 0xcd, 0x89), color); } [Test] - public void ColorFlexHexRandomString() + public void CssColorValueFlexHexRandomString() { - var color = Color.FromFlexHex("6db6ec49efd278cd0bc92d1e5e072d68"); - Assert.AreEqual(Color.FromRgb(0x6e, 0xcd, 0xe0), color); + var color = CssColorValue.FromFlexHex("6db6ec49efd278cd0bc92d1e5e072d68"); + Assert.AreEqual(CssColorValue.FromRgb(0x6e, 0xcd, 0xe0), color); } [Test] - public void ColorFlexHexSixLettersInvalid() + public void CssColorValueFlexHexSixLettersInvalid() { - var color = Color.FromFlexHex("zqbttv"); - Assert.AreEqual(Color.FromRgb(0x0, 0xb0, 0x0), color); + var color = CssColorValue.FromFlexHex("zqbttv"); + Assert.AreEqual(CssColorValue.FromRgb(0x0, 0xb0, 0x0), color); } [Test] - public void ColorFromGraySimple() + public void CssColorValueFromGraySimple() { - var color = Color.FromGray(25); - Assert.AreEqual(Color.FromRgb(25, 25, 25), color); + var color = CssColorValue.FromGray(25); + Assert.AreEqual(CssColorValue.FromRgb(25, 25, 25), color); } [Test] - public void ColorFromGrayWithAlpha() + public void CssColorValueFromGrayWithAlpha() { - var color = Color.FromGray(25, 0.5f); - Assert.AreEqual(Color.FromRgba(25, 25, 25, 0.5f), color); + var color = CssColorValue.FromGray(25, 0.5f); + Assert.AreEqual(CssColorValue.FromRgba(25, 25, 25, 0.5f), color); } [Test] - public void ColorFromGrayPercent() + public void CssColorValueFromGrayPercent() { - var color = Color.FromGray(0.5f, 0.5f); - Assert.AreEqual(Color.FromRgba(128, 128, 128, 0.5f), color); + var color = CssColorValue.FromGray(0.5f, 0.5f); + Assert.AreEqual(CssColorValue.FromRgba(128, 128, 128, 0.5f), color); } [Test] - public void ColorFromHwbRed() + public void CssColorValueFromHwbRed() { - var color = Color.FromHwb(0f, 0.2f, 0.2f); - Assert.AreEqual(Color.FromRgb(204, 51, 51), color); + var color = CssColorValue.FromHwb(0f, 0.2f, 0.2f); + Assert.AreEqual(CssColorValue.FromRgb(204, 51, 51), color); } [Test] - public void ColorFromHwbGreen() + public void CssColorValueFromHwbGreen() { - var color = Color.FromHwb(1f / 3f, 0.2f, 0.6f); - Assert.AreEqual(Color.FromRgb(51, 102, 51), color); + var color = CssColorValue.FromHwb(1f / 3f, 0.2f, 0.6f); + Assert.AreEqual(CssColorValue.FromRgb(51, 102, 51), color); } [Test] - public void ColorFromHwbMagentaTransparent() + public void CssColorValueFromHwbMagentaTransparent() { - var color = Color.FromHwba(5f / 6f, 0.4f, 0.2f, 0.5f); - Assert.AreEqual(Color.FromRgba(204, 102, 204, 0.5f), color); + var color = CssColorValue.FromHwba(5f / 6f, 0.4f, 0.2f, 0.5f); + Assert.AreEqual(CssColorValue.FromRgba(204, 102, 204, 0.5f), color); } } } diff --git a/src/AngleSharp.Css.Tests/Values/Gradient.cs b/src/AngleSharp.Css.Tests/Values/Gradient.cs index 3c3644f5..017e8481 100644 --- a/src/AngleSharp.Css.Tests/Values/Gradient.cs +++ b/src/AngleSharp.Css.Tests/Values/Gradient.cs @@ -29,8 +29,8 @@ public void InRadialGradient() [Test] public void BackgroundImageLinearGradientWithAngle() { - var red = Color.Red; - var blue = Color.Blue; + var red = CssColorValue.Red; + var blue = CssColorValue.Blue; var source = $"background-image: linear-gradient(135deg, {red.CssText}, {blue.CssText})"; var property = ParseDeclaration(source); Assert.IsTrue(property.HasValue); @@ -41,10 +41,10 @@ public void BackgroundImageLinearGradientWithAngle() var gradient = value.Items[0] as CssLinearGradientValue; Assert.IsNotNull(gradient); Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(Angle.TripleHalfQuarter, gradient.Angle); + Assert.AreEqual(CssAngleValue.TripleHalfQuarter, gradient.Angle); Assert.AreEqual(2, gradient.Stops.Length); - Assert.AreEqual(red, gradient.Stops.First().Color); - Assert.AreEqual(blue, gradient.Stops.Last().Color); + Assert.AreEqual(red, gradient.Stops.OfType().First().Color); + Assert.AreEqual(blue, gradient.Stops.OfType().Last().Color); Assert.AreEqual(source, property.CssText); } @@ -61,16 +61,16 @@ public void BackgroundImageLinearGradientWithSide() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssLinearGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(Angle.Quarter, gradient.Angle); + Assert.AreEqual(CssAngleValue.Quarter, gradient.Angle); var stops = gradient.Stops.ToArray(); Assert.AreEqual(7, stops.Length); - Assert.AreEqual(CssColors.GetColor("red").Value, stops[0].Color); - Assert.AreEqual(CssColors.GetColor("orange").Value, stops[1].Color); - Assert.AreEqual(CssColors.GetColor("yellow").Value, stops[2].Color); - Assert.AreEqual(CssColors.GetColor("green").Value, stops[3].Color); - Assert.AreEqual(CssColors.GetColor("blue").Value, stops[4].Color); - Assert.AreEqual(CssColors.GetColor("indigo").Value, stops[5].Color); - Assert.AreEqual(CssColors.GetColor("violet").Value, stops[6].Color); + Assert.AreEqual(CssColors.GetColor("red").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColors.GetColor("orange").Value, ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColors.GetColor("yellow").Value, ((CssGradientStopValue)stops[2]).Color); + Assert.AreEqual(CssColors.GetColor("green").Value, ((CssGradientStopValue)stops[3]).Color); + Assert.AreEqual(CssColors.GetColor("blue").Value, ((CssGradientStopValue)stops[4]).Color); + Assert.AreEqual(CssColors.GetColor("indigo").Value, ((CssGradientStopValue)stops[5]).Color); + Assert.AreEqual(CssColors.GetColor("violet").Value, ((CssGradientStopValue)stops[6]).Color); } [Test] @@ -85,10 +85,10 @@ public void BackgroundImageLinearGradientWithCornerAndRgba() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssLinearGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(Angle.TripleHalfQuarter, gradient.Angle); + Assert.AreEqual(CssAngleValue.TripleHalfQuarter, gradient.Angle); Assert.AreEqual(2, gradient.Stops.Count()); - Assert.AreEqual(Color.Red, gradient.Stops.First().Color); - Assert.AreEqual(Color.FromRgba(255, 0, 0, 0), gradient.Stops.Last().Color); + Assert.AreEqual(CssColorValue.Red, gradient.Stops.OfType().First().Color); + Assert.AreEqual(CssColorValue.FromRgba(255, 0, 0, 0), gradient.Stops.OfType().Last().Color); } [Test] @@ -103,10 +103,10 @@ public void BackgroundImageLinearGradientWithSideAndHsl() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssLinearGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(Angle.Half, gradient.Angle); + Assert.AreEqual(CssAngleValue.Half, gradient.Angle); Assert.AreEqual(2, gradient.Stops.Count()); - Assert.AreEqual(Color.FromHsl(0f, 0.8f, 0.7f), gradient.Stops.First().Color); - Assert.AreEqual(Color.FromHex("bada55"), gradient.Stops.Last().Color); + Assert.AreEqual(CssColorValue.FromHsl(0f, 0.8f, 0.7f), gradient.Stops.OfType().First().Color); + Assert.AreEqual(CssColorValue.FromHex("bada55"), gradient.Stops.OfType().Last().Color); } [Test] @@ -121,11 +121,11 @@ public void BackgroundImageLinearGradientNoAngle() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssLinearGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(Angle.Half, gradient.Angle); + Assert.AreEqual(CssAngleValue.Half, gradient.Angle); Assert.AreEqual(3, gradient.Stops.Count()); - Assert.AreEqual(CssColors.GetColor("yellow").Value, gradient.Stops.First().Color); - Assert.AreEqual(CssColors.GetColor("blue").Value, gradient.Stops.Skip(1).First().Color); - Assert.AreEqual(Color.FromRgb(0, 255, 0), gradient.Stops.Skip(2).First().Color); + Assert.AreEqual(CssColors.GetColor("yellow").Value, gradient.Stops.OfType().First().Color); + Assert.AreEqual(CssColors.GetColor("blue").Value, gradient.Stops.OfType().Skip(1).First().Color); + Assert.AreEqual(CssColorValue.FromRgb(0, 255, 0), gradient.Stops.OfType().Skip(2).First().Color); } [Test] @@ -140,15 +140,15 @@ public void BackgroundImageRadialGradientCircleFarthestCorner() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(new Length(45, Length.Unit.Px), gradient.Position.X); - Assert.AreEqual(new Length(45, Length.Unit.Px), gradient.Position.Y); + Assert.AreEqual(new CssLengthValue(45, CssLengthValue.Unit.Px), gradient.Position.X); + Assert.AreEqual(new CssLengthValue(45, CssLengthValue.Unit.Px), gradient.Position.Y); Assert.AreEqual(true, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.FarthestCorner, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(3, stops.Length); - Assert.AreEqual(Color.FromRgb(0, 255, 255), stops[0].Color); - Assert.AreEqual(Color.FromRgba(0, 0, 255, 0), stops[1].Color); - Assert.AreEqual(Color.FromRgb(0, 0, 255), stops[2].Color); + Assert.AreEqual(CssColorValue.FromRgb(0, 255, 255), ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromRgba(0, 0, 255, 0), ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColorValue.FromRgb(0, 0, 255), ((CssGradientStopValue)stops[2]).Color); } [Test] @@ -163,15 +163,15 @@ public void BackgroundImageRadialGradientEllipseFarthestCorner() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(new Length(470, Length.Unit.Px), gradient.Position.X); - Assert.AreEqual(new Length(47, Length.Unit.Px), gradient.Position.Y); + Assert.AreEqual(new CssLengthValue(470, CssLengthValue.Unit.Px), gradient.Position.X); + Assert.AreEqual(new CssLengthValue(47, CssLengthValue.Unit.Px), gradient.Position.Y); Assert.AreEqual(false, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.FarthestCorner, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(3, stops.Length); - Assert.AreEqual(Color.FromRgb(0xFF, 0xFF, 0x80), stops[0].Color); - Assert.AreEqual(Color.FromRgba(204, 153, 153, 0.4f), stops[1].Color); - Assert.AreEqual(Color.FromRgb(0xE6, 0xE6, 0xFF), stops[2].Color); + Assert.AreEqual(CssColorValue.FromRgb(0xFF, 0xFF, 0x80), ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromRgba(204, 153, 153, 0.4f), ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColorValue.FromRgb(0xE6, 0xE6, 0xFF), ((CssGradientStopValue)stops[2]).Color); } [Test] @@ -186,14 +186,14 @@ public void BackgroundImageRadialGradientFarthestCornerWithPoint() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(new Length(45, Length.Unit.Px), gradient.Position.X); - Assert.AreEqual(new Length(45, Length.Unit.Px), gradient.Position.Y); + Assert.AreEqual(new CssLengthValue(45, CssLengthValue.Unit.Px), gradient.Position.X); + Assert.AreEqual(new CssLengthValue(45, CssLengthValue.Unit.Px), gradient.Position.Y); Assert.AreEqual(false, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.FarthestCorner, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(2, stops.Length); - Assert.AreEqual(Color.FromRgb(255, 0, 0), stops[0].Color); - Assert.AreEqual(Color.FromRgb(0, 0, 255), stops[1].Color); + Assert.AreEqual(CssColorValue.FromRgb(255, 0, 0), ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromRgb(0, 0, 255), ((CssGradientStopValue)stops[1]).Color); } [Test] @@ -208,18 +208,18 @@ public void BackgroundImageRadialGradientSingleSize() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(new Length(60f, Length.Unit.Px), gradient.Position.X); - Assert.AreEqual(Length.Half, gradient.Position.Y); + Assert.AreEqual(new CssLengthValue(60f, CssLengthValue.Unit.Px), gradient.Position.X); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.Y); Assert.AreEqual(true, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.None, gradient.Mode); - Assert.AreEqual(new Length(16f, Length.Unit.Px), gradient.MajorRadius); - Assert.AreEqual(Length.Full, gradient.MinorRadius); + Assert.AreEqual(new CssLengthValue(16f, CssLengthValue.Unit.Px), gradient.MajorRadius); + Assert.AreEqual(CssLengthValue.Full, gradient.MinorRadius); var stops = gradient.Stops.ToArray(); Assert.AreEqual(4, stops.Length); - Assert.AreEqual(Color.FromRgb(0, 0, 0), stops[0].Color); - Assert.AreEqual(Color.FromRgb(0, 0, 0), stops[1].Color); - Assert.AreEqual(Color.FromRgba(0, 0, 0, 0.3), stops[2].Color); - Assert.AreEqual(Color.Transparent, stops[3].Color); + Assert.AreEqual(CssColorValue.FromRgb(0, 0, 0), ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromRgb(0, 0, 0), ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColorValue.FromRgba(0, 0, 0, 0.3), ((CssGradientStopValue)stops[2]).Color); + Assert.AreEqual(CssColorValue.Transparent, ((CssGradientStopValue)stops[3]).Color); } [Test] @@ -234,14 +234,14 @@ public void BackgroundImageRadialGradientCircle() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(Length.Half, gradient.Position.X); - Assert.AreEqual(Length.Half, gradient.Position.Y); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.X); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.Y); Assert.AreEqual(true, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.None, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(2, stops.Length); - Assert.AreEqual(Color.FromName("yellow").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("green").Value, stops[1].Color); + Assert.AreEqual(CssColorValue.FromName("yellow").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("green").Value, ((CssGradientStopValue)stops[1]).Color); } [Test] @@ -256,14 +256,14 @@ public void BackgroundImageRadialGradientOnlyGradientStops() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(Length.Half, gradient.Position.X); - Assert.AreEqual(Length.Half, gradient.Position.Y); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.X); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.Y); Assert.AreEqual(false, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.None, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(2, stops.Length); - Assert.AreEqual(Color.FromName("yellow").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("green").Value, stops[1].Color); + Assert.AreEqual(CssColorValue.FromName("yellow").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("green").Value, ((CssGradientStopValue)stops[1]).Color); } [Test] @@ -278,14 +278,14 @@ public void BackgroundImageRadialGradientEllipseAtCenter() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(Length.Half, gradient.Position.X); - Assert.AreEqual(Length.Half, gradient.Position.Y); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.X); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.Y); Assert.AreEqual(false, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.None, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(2, stops.Length); - Assert.AreEqual(Color.FromName("yellow").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("green").Value, stops[1].Color); + Assert.AreEqual(CssColorValue.FromName("yellow").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("green").Value, ((CssGradientStopValue)stops[1]).Color); } [Test] @@ -300,14 +300,14 @@ public void BackgroundImageRadialGradientFarthestCornerWithoutPoint() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(Length.Half, gradient.Position.X); - Assert.AreEqual(Length.Half, gradient.Position.Y); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.X); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.Y); Assert.AreEqual(false, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.FarthestCorner, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(2, stops.Length); - Assert.AreEqual(Color.FromName("yellow").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("green").Value, stops[1].Color); + Assert.AreEqual(CssColorValue.FromName("yellow").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("green").Value, ((CssGradientStopValue)stops[1]).Color); } [Test] @@ -322,15 +322,15 @@ public void BackgroundImageRadialGradientClosestSideWithPoint() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(new Length(20f, Length.Unit.Px), gradient.Position.X); - Assert.AreEqual(new Length(30f, Length.Unit.Px), gradient.Position.Y); + Assert.AreEqual(new CssLengthValue(20f, CssLengthValue.Unit.Px), gradient.Position.X); + Assert.AreEqual(new CssLengthValue(30f, CssLengthValue.Unit.Px), gradient.Position.Y); Assert.AreEqual(false, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.ClosestSide, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(3, stops.Length); - Assert.AreEqual(Color.FromName("red").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("yellow").Value, stops[1].Color); - Assert.AreEqual(Color.FromName("green").Value, stops[2].Color); + Assert.AreEqual(CssColorValue.FromName("red").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("yellow").Value, ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColorValue.FromName("green").Value, ((CssGradientStopValue)stops[2]).Color); } [Test] @@ -345,17 +345,17 @@ public void BackgroundImageRadialGradientSizeAndPoint() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(new Length(20f, Length.Unit.Px), gradient.Position.X); - Assert.AreEqual(new Length(30f, Length.Unit.Px), gradient.Position.Y); + Assert.AreEqual(new CssLengthValue(20f, CssLengthValue.Unit.Px), gradient.Position.X); + Assert.AreEqual(new CssLengthValue(30f, CssLengthValue.Unit.Px), gradient.Position.Y); Assert.AreEqual(false, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.None, gradient.Mode); - Assert.AreEqual(new Length(20f, Length.Unit.Px), gradient.MajorRadius); - Assert.AreEqual(new Length(30f, Length.Unit.Px), gradient.MinorRadius); + Assert.AreEqual(new CssLengthValue(20f, CssLengthValue.Unit.Px), gradient.MajorRadius); + Assert.AreEqual(new CssLengthValue(30f, CssLengthValue.Unit.Px), gradient.MinorRadius); var stops = gradient.Stops.ToArray(); Assert.AreEqual(3, stops.Length); - Assert.AreEqual(Color.FromName("red").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("yellow").Value, stops[1].Color); - Assert.AreEqual(Color.FromName("green").Value, stops[2].Color); + Assert.AreEqual(CssColorValue.FromName("red").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("yellow").Value, ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColorValue.FromName("green").Value, ((CssGradientStopValue)stops[2]).Color); } [Test] @@ -370,15 +370,15 @@ public void BackgroundImageRadialGradientClosestSideCircleShuffledWithPoint() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(new Length(20f, Length.Unit.Px), gradient.Position.X); - Assert.AreEqual(new Length(30f, Length.Unit.Px), gradient.Position.Y); + Assert.AreEqual(new CssLengthValue(20f, CssLengthValue.Unit.Px), gradient.Position.X); + Assert.AreEqual(new CssLengthValue(30f, CssLengthValue.Unit.Px), gradient.Position.Y); Assert.AreEqual(true, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.ClosestSide, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(3, stops.Length); - Assert.AreEqual(Color.FromName("red").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("yellow").Value, stops[1].Color); - Assert.AreEqual(Color.FromName("green").Value, stops[2].Color); + Assert.AreEqual(CssColorValue.FromName("red").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("yellow").Value, ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColorValue.FromName("green").Value, ((CssGradientStopValue)stops[2]).Color); } [Test] @@ -393,15 +393,15 @@ public void BackgroundImageRadialGradientFarthestSideLeftBottom() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsFalse(gradient.IsRepeating); - Assert.AreEqual(Length.Zero, gradient.Position.X); - Assert.AreEqual(Length.Full, gradient.Position.Y); + Assert.AreEqual(CssLengthValue.Zero, gradient.Position.X); + Assert.AreEqual(CssLengthValue.Full, gradient.Position.Y); Assert.AreEqual(false, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.FarthestSide, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(3, stops.Length); - Assert.AreEqual(Color.FromName("red").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("yellow").Value, stops[1].Color); - Assert.AreEqual(Color.FromName("green").Value, stops[2].Color); + Assert.AreEqual(CssColorValue.FromName("red").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("yellow").Value, ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColorValue.FromName("green").Value, ((CssGradientStopValue)stops[2]).Color); } [Test] @@ -418,9 +418,9 @@ public void BackgroundImageRepeatingLinearGradientRedBlue() Assert.IsTrue(gradient.IsRepeating); var stops = gradient.Stops.ToArray(); Assert.AreEqual(3, stops.Length); - Assert.AreEqual(Color.FromName("red").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("blue").Value, stops[1].Color); - Assert.AreEqual(Color.FromName("red").Value, stops[2].Color); + Assert.AreEqual(CssColorValue.FromName("red").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("blue").Value, ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColorValue.FromName("red").Value, ((CssGradientStopValue)stops[2]).Color); } [Test] @@ -435,15 +435,15 @@ public void BackgroundImageRepeatingRadialGradientRedBlue() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsTrue(gradient.IsRepeating); - Assert.AreEqual(Length.Half, gradient.Position.X); - Assert.AreEqual(Length.Half, gradient.Position.Y); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.X); + Assert.AreEqual(CssLengthValue.Half, gradient.Position.Y); Assert.AreEqual(false, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.None, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(3, stops.Length); - Assert.AreEqual(Color.FromName("red").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("blue").Value, stops[1].Color); - Assert.AreEqual(Color.FromName("red").Value, stops[2].Color); + Assert.AreEqual(CssColorValue.FromName("red").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("blue").Value, ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColorValue.FromName("red").Value, ((CssGradientStopValue)stops[2]).Color); } [Test] @@ -458,17 +458,17 @@ public void BackgroundImageRepeatingRadialGradientFunky() Assert.AreEqual(1, value.Items.Length); var gradient = value.Items[0] as CssRadialGradientValue; Assert.IsTrue(gradient.IsRepeating); - Assert.AreEqual(new Length(20f, Length.Unit.Px), gradient.Position.X); - Assert.AreEqual(new Length(30f, Length.Unit.Px), gradient.Position.Y); + Assert.AreEqual(new CssLengthValue(20f, CssLengthValue.Unit.Px), gradient.Position.X); + Assert.AreEqual(new CssLengthValue(30f, CssLengthValue.Unit.Px), gradient.Position.Y); Assert.AreEqual(true, gradient.IsCircle); Assert.AreEqual(CssRadialGradientValue.SizeMode.ClosestSide, gradient.Mode); var stops = gradient.Stops.ToArray(); Assert.AreEqual(5, stops.Length); - Assert.AreEqual(Color.FromName("red").Value, stops[0].Color); - Assert.AreEqual(Color.FromName("yellow").Value, stops[1].Color); - Assert.AreEqual(Color.FromName("green").Value, stops[2].Color); - Assert.AreEqual(Color.FromName("yellow").Value, stops[3].Color); - Assert.AreEqual(Color.FromName("red").Value, stops[4].Color); + Assert.AreEqual(CssColorValue.FromName("red").Value, ((CssGradientStopValue)stops[0]).Color); + Assert.AreEqual(CssColorValue.FromName("yellow").Value, ((CssGradientStopValue)stops[1]).Color); + Assert.AreEqual(CssColorValue.FromName("green").Value, ((CssGradientStopValue)stops[2]).Color); + Assert.AreEqual(CssColorValue.FromName("yellow").Value, ((CssGradientStopValue)stops[3]).Color); + Assert.AreEqual(CssColorValue.FromName("red").Value, ((CssGradientStopValue)stops[4]).Color); } } } diff --git a/src/AngleSharp.Css.Tests/Values/UnitConversion.cs b/src/AngleSharp.Css.Tests/Values/UnitConversion.cs index b4db576a..59eeeccd 100644 --- a/src/AngleSharp.Css.Tests/Values/UnitConversion.cs +++ b/src/AngleSharp.Css.Tests/Values/UnitConversion.cs @@ -11,19 +11,19 @@ public class UnitConversionTests public void LengthParseCorrectPxValue() { var s = "12px"; - var v = default(Length); - var r = Length.TryParse(s, out v); + var v = default(CssLengthValue); + var r = CssLengthValue.TryParse(s, out v); Assert.IsTrue(r); Assert.AreEqual(12f, v.Value); - Assert.AreEqual(Length.Unit.Px, v.Type); + Assert.AreEqual(CssLengthValue.Unit.Px, v.Type); } [Test] public void LengthParseIncorrectValue() { var s = "123.5"; - var v = default(Length); - var r = Length.TryParse(s, out v); + var v = default(CssLengthValue); + var r = CssLengthValue.TryParse(s, out v); Assert.IsFalse(r); } @@ -31,17 +31,17 @@ public void LengthParseIncorrectValue() public void LengthParseCorrectVwValue() { var s = "12.2vw"; - var v = default(Length); - var r = Length.TryParse(s, out v); + var v = default(CssLengthValue); + var r = CssLengthValue.TryParse(s, out v); Assert.IsTrue(r); Assert.AreEqual(12.2, v.Value); - Assert.AreEqual(Length.Unit.Vw, v.Type); + Assert.AreEqual(CssLengthValue.Unit.Vw, v.Type); } [Test] public void LengthToPixelsPercentThrowsOnInvalidRenderDimensions() { - var l = new Length(50, Length.Unit.Percent); + var l = new CssLengthValue(50, CssLengthValue.Unit.Percent); var renderDevice = new DefaultRenderDevice(); Assert.Throws(() => l.ToPixel(renderDevice, RenderMode.Undefined)); Assert.Throws(() => l.ToPixel(null, RenderMode.Undefined)); @@ -50,7 +50,7 @@ public void LengthToPixelsPercentThrowsOnInvalidRenderDimensions() [Test] public void LengthToPixelsEmThrowsOnInvalidRenderDimensions() { - var l = new Length(50, Length.Unit.Em); + var l = new CssLengthValue(50, CssLengthValue.Unit.Em); var renderDevice = new DefaultRenderDevice { FontSize = 0 }; Assert.Throws(() => l.ToPixel(renderDevice, RenderMode.Undefined)); Assert.Throws(() => l.ToPixel(null, RenderMode.Undefined)); @@ -59,7 +59,7 @@ public void LengthToPixelsEmThrowsOnInvalidRenderDimensions() [Test] public void LengthToPixelsCorrectPercentWidth() { - var l = new Length(50, Length.Unit.Percent); + var l = new CssLengthValue(50, CssLengthValue.Unit.Percent); var renderDevice = new DefaultRenderDevice {ViewPortWidth = 500}; Assert.AreEqual(250, l.ToPixel(renderDevice, RenderMode.Horizontal)); } @@ -67,7 +67,7 @@ public void LengthToPixelsCorrectPercentWidth() [Test] public void LengthToPixelsCorrectPercentHeight() { - var l = new Length(25, Length.Unit.Percent); + var l = new CssLengthValue(25, CssLengthValue.Unit.Percent); var renderDevice = new DefaultRenderDevice {ViewPortHeight = 600}; Assert.AreEqual(150, l.ToPixel(renderDevice, RenderMode.Vertical)); } @@ -75,7 +75,7 @@ public void LengthToPixelsCorrectPercentHeight() [Test] public void LengthToPixelsCorrectRem() { - var l = new Length(25, Length.Unit.Rem); + var l = new CssLengthValue(25, CssLengthValue.Unit.Rem); var renderDevice = new DefaultRenderDevice {FontSize = 10}; Assert.AreEqual(250, l.ToPixel(renderDevice, RenderMode.Undefined)); } @@ -83,7 +83,7 @@ public void LengthToPixelsCorrectRem() [Test] public void LengthToPixelsCorrectEm() { - var l = new Length(10, Length.Unit.Em); + var l = new CssLengthValue(10, CssLengthValue.Unit.Em); var renderDevice = new DefaultRenderDevice {FontSize = 10}; Assert.AreEqual(100, l.ToPixel(renderDevice, RenderMode.Undefined)); } @@ -91,7 +91,7 @@ public void LengthToPixelsCorrectEm() [Test] public void LengthToPixelsCorrectVh() { - var l = new Length(10, Length.Unit.Vh); + var l = new CssLengthValue(10, CssLengthValue.Unit.Vh); var renderDevice = new DefaultRenderDevice {ViewPortHeight = 1000}; Assert.AreEqual(100, l.ToPixel(renderDevice, RenderMode.Undefined)); } @@ -99,7 +99,7 @@ public void LengthToPixelsCorrectVh() [Test] public void LengthToPixelsCorrectVw() { - var l = new Length(20, Length.Unit.Vw); + var l = new CssLengthValue(20, CssLengthValue.Unit.Vw); var renderDevice = new DefaultRenderDevice {ViewPortWidth = 1000}; Assert.AreEqual(200, l.ToPixel(renderDevice, RenderMode.Undefined)); } @@ -107,7 +107,7 @@ public void LengthToPixelsCorrectVw() [Test] public void LengthToPixelsCorrectVmax() { - var l = new Length(20, Length.Unit.Vmax); + var l = new CssLengthValue(20, CssLengthValue.Unit.Vmax); var renderDevice = new DefaultRenderDevice { ViewPortHeight = 1000, @@ -119,7 +119,7 @@ public void LengthToPixelsCorrectVmax() [Test] public void LengthToPixelsCorrectVmin() { - var l = new Length(20, Length.Unit.Vmin); + var l = new CssLengthValue(20, CssLengthValue.Unit.Vmin); var renderDevice = new DefaultRenderDevice { ViewPortHeight = 1000, @@ -131,125 +131,125 @@ public void LengthToPixelsCorrectVmin() [Test] public void LengthToPercentCorrectWidth() { - var l = new Length(100, Length.Unit.Px); + var l = new CssLengthValue(100, CssLengthValue.Unit.Px); var renderDevice = new DefaultRenderDevice {ViewPortWidth = 500}; - Assert.AreEqual(20, l.To(Length.Unit.Percent, renderDevice, RenderMode.Horizontal)); + Assert.AreEqual(20, l.To(CssLengthValue.Unit.Percent, renderDevice, RenderMode.Horizontal)); } [Test] public void LengthToPercentCorrectHeight() { - var l = new Length(100, Length.Unit.Px); + var l = new CssLengthValue(100, CssLengthValue.Unit.Px); var renderDevice = new DefaultRenderDevice {ViewPortHeight = 1000}; - Assert.AreEqual(10, l.To(Length.Unit.Percent, renderDevice, RenderMode.Vertical)); + Assert.AreEqual(10, l.To(CssLengthValue.Unit.Percent, renderDevice, RenderMode.Vertical)); } [Test] public void LengthToRemCorrectValue() { - var l = new Length(100, Length.Unit.Px); + var l = new CssLengthValue(100, CssLengthValue.Unit.Px); var renderDevice = new DefaultRenderDevice {FontSize = 16}; - Assert.AreEqual(6.25d, l.To(Length.Unit.Rem, renderDevice, RenderMode.Undefined)); + Assert.AreEqual(6.25d, l.To(CssLengthValue.Unit.Rem, renderDevice, RenderMode.Undefined)); } [Test] public void LengthToEmCorrectValue() { - var l = new Length(1600, Length.Unit.Px); + var l = new CssLengthValue(1600, CssLengthValue.Unit.Px); var renderDevice = new DefaultRenderDevice {FontSize = 16}; - Assert.AreEqual(100, l.To(Length.Unit.Em, renderDevice, RenderMode.Undefined)); + Assert.AreEqual(100, l.To(CssLengthValue.Unit.Em, renderDevice, RenderMode.Undefined)); } [Test] public void LengthToVhCorrectValue() { - var l = new Length(100, Length.Unit.Px); + var l = new CssLengthValue(100, CssLengthValue.Unit.Px); var renderDevice = new DefaultRenderDevice {ViewPortHeight = 1000}; - Assert.AreEqual(10, l.To(Length.Unit.Vh, renderDevice, RenderMode.Undefined)); + Assert.AreEqual(10, l.To(CssLengthValue.Unit.Vh, renderDevice, RenderMode.Undefined)); } [Test] public void LengthToVwCorrectValue() { - var l = new Length(50, Length.Unit.Px); + var l = new CssLengthValue(50, CssLengthValue.Unit.Px); var renderDevice = new DefaultRenderDevice {ViewPortWidth = 1000}; - Assert.AreEqual(5, l.To(Length.Unit.Vw, renderDevice, RenderMode.Undefined)); + Assert.AreEqual(5, l.To(CssLengthValue.Unit.Vw, renderDevice, RenderMode.Undefined)); } [Test] public void LengthToVmaxCorrectValue() { - var l = new Length(50, Length.Unit.Px); + var l = new CssLengthValue(50, CssLengthValue.Unit.Px); var renderDevice = new DefaultRenderDevice { ViewPortWidth = 1000, ViewPortHeight = 500 }; - Assert.AreEqual(5, l.To(Length.Unit.Vmax, renderDevice, RenderMode.Undefined)); + Assert.AreEqual(5, l.To(CssLengthValue.Unit.Vmax, renderDevice, RenderMode.Undefined)); } [Test] public void LengthToVminCorrectValue() { - var l = new Length(50, Length.Unit.Px); + var l = new CssLengthValue(50, CssLengthValue.Unit.Px); var renderDevice = new DefaultRenderDevice { ViewPortWidth = 1000, ViewPortHeight = 500 }; - Assert.AreEqual(10, l.To(Length.Unit.Vmin, renderDevice, RenderMode.Undefined)); + Assert.AreEqual(10, l.To(CssLengthValue.Unit.Vmin, renderDevice, RenderMode.Undefined)); } [Test] public void AngleParseCorrectDegValue() { var s = "1.35e2deg"; - var v = default(Angle); - var r = Angle.TryParse(s, out v); + var v = default(CssAngleValue); + var r = CssAngleValue.TryParse(s, out v); Assert.IsTrue(r); Assert.AreEqual(135f, v.Value); - Assert.AreEqual(Angle.Unit.Deg, v.Type); + Assert.AreEqual(CssAngleValue.Unit.Deg, v.Type); } [Test] public void ResolutionParseCorrectDpiValue() { var s = "-24.0dpi"; - var v = default(Resolution); - var r = Resolution.TryParse(s, out v); + var v = default(CssResolutionValue); + var r = CssResolutionValue.TryParse(s, out v); Assert.IsTrue(r); Assert.AreEqual(-24f, v.Value); - Assert.AreEqual(Resolution.Unit.Dpi, v.Type); + Assert.AreEqual(CssResolutionValue.Unit.Dpi, v.Type); } [Test] public void FrequencyParseCorrectKhzValue() { var s = "17.123khz"; - var v = default(Frequency); - var r = Frequency.TryParse(s, out v); + var v = default(CssFrequencyValue); + var r = CssFrequencyValue.TryParse(s, out v); Assert.IsTrue(r); Assert.AreEqual(17.123, v.Value); - Assert.AreEqual(Frequency.Unit.Khz, v.Type); + Assert.AreEqual(CssFrequencyValue.Unit.Khz, v.Type); } [Test] public void TimeParseCorrectSecondsValue() { var s = "0s"; - var v = default(Time); - var r = Time.TryParse(s, out v); + var v = default(CssTimeValue); + var r = CssTimeValue.TryParse(s, out v); Assert.IsTrue(r); Assert.AreEqual(0f, v.Value); - Assert.AreEqual(Time.Unit.S, v.Type); + Assert.AreEqual(CssTimeValue.Unit.S, v.Type); } [Test] public void AngleParseIncorrectValue() { var s = "123.deg"; - var v = default(Angle); - var r = Angle.TryParse(s, out v); + var v = default(CssAngleValue); + var r = CssAngleValue.TryParse(s, out v); Assert.IsFalse(r); } } diff --git a/src/AngleSharp.Css.nuspec b/src/AngleSharp.Css.nuspec index ae1d28b0..78a98a3e 100644 --- a/src/AngleSharp.Css.nuspec +++ b/src/AngleSharp.Css.nuspec @@ -6,16 +6,17 @@ AngleSharp Florian Rappl MIT + https://anglesharp.github.io logo.png README.md false Extends the CSSOM from the core AngleSharp library. https://github.com/AngleSharp/AngleSharp.Css/blob/main/CHANGELOG.md - Copyright 2016-2023, AngleSharp + Copyright 2016-2025, AngleSharp html html5 css css3 dom styling library anglesharp angle - + diff --git a/src/AngleSharp.Css/AngleSharp.Css.csproj b/src/AngleSharp.Css/AngleSharp.Css.csproj index 5422d00f..519ab954 100644 --- a/src/AngleSharp.Css/AngleSharp.Css.csproj +++ b/src/AngleSharp.Css/AngleSharp.Css.csproj @@ -2,8 +2,8 @@ AngleSharp.Css AngleSharp.Css - netstandard2.0;net6.0;net7.0 - netstandard2.0;net461;net472;net6.0;net7.0 + netstandard2.0;net6.0;net7.0;net8.0 + netstandard2.0;net461;net472;net6.0;net7.0;net8.0 true Key.snk true @@ -21,7 +21,7 @@ - + diff --git a/src/AngleSharp.Css/Constants/CssKeywords.cs b/src/AngleSharp.Css/Constants/CssKeywords.cs index b2087620..775680b3 100644 --- a/src/AngleSharp.Css/Constants/CssKeywords.cs +++ b/src/AngleSharp.Css/Constants/CssKeywords.cs @@ -27,6 +27,26 @@ public static class CssKeywords /// public static readonly String Clip = "clip"; + ///

    + /// The cyclic keyword. + /// + public static readonly String Cyclic = "cyclic"; + + /// + /// The numeric keyword. + /// + public static readonly String Numeric = "numeric"; + + /// + /// The alphabetic keyword. + /// + public static readonly String Alphabetic = "alphabetic"; + + /// + /// The symbolic keyword. + /// + public static readonly String Symbolic = "symbolic"; + /// /// The legacy keyword. /// @@ -37,6 +57,11 @@ public static class CssKeywords /// public static readonly String Normal = "normal"; + /// + /// The arabic-indic keyword. + /// + public static readonly String ArabicIndic = "arabic-indic"; + /// /// The pre keyword. /// @@ -97,6 +122,16 @@ public static class CssKeywords /// public static readonly String InterCharacter = "inter-character"; + /// + /// The katakana keyword. + /// + public static readonly String Katakana = "katakana"; + + /// + /// The katakana-iroha keyword. + /// + public static readonly String KatakanaIroha = "katakana-iroha"; + /// /// The kashida keyword. /// @@ -667,6 +702,11 @@ public static class CssKeywords /// public static readonly String Justify = "justify"; + /// + /// The justify-all keyword. + /// + public static readonly String JustifyAll = "justify-all"; + /// /// The underline keyword. /// @@ -1127,16 +1167,101 @@ public static class CssKeywords /// public static readonly String UpperLatin = "upper-latin"; + /// + /// The malayalam keyword. + /// + public static readonly String Malayalam = "malayalam"; + + /// + /// The myanmar keyword. + /// + public static readonly String Myanmar = "myanmar"; + + /// + /// The mongolian keyword. + /// + public static readonly String Mongolian = "mongolian"; + + /// + /// The oriya keyword. + /// + public static readonly String Oriya = "oriya"; + + /// + /// The persian keyword. + /// + public static readonly String Persian = "persian"; + + /// + /// The tamil keyword. + /// + public static readonly String Tamil = "tamil"; + + /// + /// The thai keyword. + /// + public static readonly String Thai = "thai"; + + /// + /// The telugu keyword. + /// + public static readonly String Telugu = "telugu"; + + /// + /// The lao keyword. + /// + public static readonly String Lao = "lao"; + + /// + /// The tibetan keyword. + /// + public static readonly String Tibetan = "tibetan"; + + /// + /// The trad-chinese-formal keyword. + /// + public static readonly String TradChineseFormal = "trad-chinese-formal"; + + /// + /// The trad-chinese-informal keyword. + /// + public static readonly String TradChineseInformal = "trad-chinese-informal"; + /// /// The armenian keyword. /// public static readonly String Armenian = "armenian"; + /// + /// The lower-armenian keyword. + /// + public static readonly String LowerArmenian = "lower-armenian"; + + /// + /// The upper-armenian keyword. + /// + public static readonly String UpperArmenian = "upper-armenian"; + /// /// The georgian keyword. /// public static readonly String Georgian = "georgian"; + /// + /// The kannada keyword. + /// + public static readonly String Kannada = "kannada"; + + /// + /// The disclosure-open keyword. + /// + public static readonly String DisclosureOpen = "disclosure-open"; + + /// + /// The disclosure-closed keyword. + /// + public static readonly String DisclosureClosed = "disclosure-closed"; + /// /// The lower-alpha keyword. /// @@ -1302,6 +1427,11 @@ public static class CssKeywords /// public static readonly String Separate = "separate"; + /// + /// The match-parent keyword. + /// + public static readonly String MatchParent = "match-parent"; + /// /// The start keyword. /// @@ -1317,6 +1447,106 @@ public static class CssKeywords /// public static readonly String Fill = "fill"; + /// + /// The cjk-decimal keyword. + /// + public static readonly String CjkDecimal = "cjk-decimal"; + + /// + /// The cjk-earthly-branch keyword. + /// + public static readonly String CjkEarthlyBranch = "cjk-earthly-branch"; + + /// + /// The cjk-heavenly-stem keyword. + /// + public static readonly String CjkHeavenlyStem = "cjk-heavenly-stem"; + + /// + /// The cjk-ideographic keyword. + /// + public static readonly String CjkIdeographic = "cjk-ideographic"; + + /// + /// The bengali keyword. + /// + public static readonly String Bengali = "bengali"; + + /// + /// The cambodian keyword. + /// + public static readonly String Cambodian = "cambodian"; + + /// + /// The devanagari keyword. + /// + public static readonly String Devanagari = "devanagari"; + + /// + /// The ethiopic-numeric keyword. + /// + public static readonly String EthiopicNumeric = "ethiopic-numeric"; + + /// + /// The gurmukhi keyword. + /// + public static readonly String Gurmukhi = "gurmukhi"; + + /// + /// The gujarati keyword. + /// + public static readonly String Gujarati = "gujarati"; + + /// + /// The hebrew keyword. + /// + public static readonly String Hebrew = "hebrew"; + + /// + /// The hiragana keyword. + /// + public static readonly String Hiragana = "hiragana"; + + /// + /// The hiragana-iroha keyword. + /// + public static readonly String HiraganaIroha = "hiragana-iroha"; + + /// + /// The japanese-formal keyword. + /// + public static readonly String JapaneseFormal = "japanese-formal"; + + /// + /// The japanese-informal keyword. + /// + public static readonly String JapaneseInformal = "japanese-informal"; + + /// + /// The simp-chinese-informal keyword. + /// + public static readonly String SimpChineseInformal = "simp-chinese-informal"; + + /// + /// The simp-chinese-formal keyword. + /// + public static readonly String SimpChineseFormal = "simp-chinese-formal"; + + /// + /// The korean-hangul-formal keyword. + /// + public static readonly String KoreanHangulFormal = "korean-hangul-formal"; + + /// + /// The korean-hanja-formal keyword. + /// + public static readonly String KoreanHanjaFormal = "korean-hanja-formal"; + + /// + /// The korean-hanja-informal keyword. + /// + public static readonly String KoreanHanjaInformal = "korean-hanja-informal"; + /// /// The screen keyword. /// diff --git a/src/AngleSharp.Css/Constants/FunctionNames.cs b/src/AngleSharp.Css/Constants/FunctionNames.cs index dc0a98c5..207fd2df 100644 --- a/src/AngleSharp.Css/Constants/FunctionNames.cs +++ b/src/AngleSharp.Css/Constants/FunctionNames.cs @@ -57,6 +57,11 @@ public static class FunctionNames /// public static readonly String Attr = "attr"; + /// + /// The symbols function. + /// + public static readonly String Symbols = "symbols"; + /// /// The fit-content function. /// @@ -262,6 +267,26 @@ public static class FunctionNames /// public static readonly String Hwba = "hwba"; + /// + /// The LAB (Lightness, A-axis, B-axis) function. + /// + public static readonly String Lab = "lab"; + + /// + /// The LCH (Lightness, Chroma, Hue) function. + /// + public static readonly String Lch = "lch"; + + /// + /// The Oklab (Lightness, A-axis, B-axis) function. + /// + public static readonly String Oklab = "oklab"; + + /// + /// The Oklch (Lightness, Chroma, Hue) function. + /// + public static readonly String Oklch = "oklch"; + /// /// The content function. /// diff --git a/src/AngleSharp.Css/Constants/InitialValues.cs b/src/AngleSharp.Css/Constants/InitialValues.cs index 6fa6a640..029691e5 100644 --- a/src/AngleSharp.Css/Constants/InitialValues.cs +++ b/src/AngleSharp.Css/Constants/InitialValues.cs @@ -10,204 +10,212 @@ namespace AngleSharp.Css /// static class InitialValues { - public static readonly ICssValue ColorDecl = Color.Black; - public static readonly ICssValue BackgroundColorDecl = Color.Transparent; - public static readonly ICssValue BackgroundImageDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue BackgroundRepeatHorizontalDecl = new Identifier(CssKeywords.Repeat); - public static readonly ICssValue BackgroundRepeatVerticalDecl = new Identifier(CssKeywords.Repeat); + public static readonly ICssValue ColorDecl = CssColorValue.Black; + public static readonly ICssValue BackgroundColorDecl = CssColorValue.Transparent; + public static readonly ICssValue BackgroundImageDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue BackgroundRepeatHorizontalDecl = new CssIdentifierValue(CssKeywords.Repeat); + public static readonly ICssValue BackgroundRepeatVerticalDecl = new CssIdentifierValue(CssKeywords.Repeat); public static readonly ICssValue BackgroundRepeatDecl = new CssImageRepeatsValue(BackgroundRepeatHorizontalDecl, BackgroundRepeatVerticalDecl); - public static readonly ICssValue BackgroundPositionXDecl = new Length(0, Length.Unit.Percent); - public static readonly ICssValue BackgroundPositionYDecl = new Length(0, Length.Unit.Percent); + public static readonly ICssValue BackgroundPositionXDecl = new CssLengthValue(0, CssLengthValue.Unit.Percent); + public static readonly ICssValue BackgroundPositionYDecl = new CssLengthValue(0, CssLengthValue.Unit.Percent); public static readonly ICssValue BackgroundPositionDecl = new CssTupleValue(new [] { BackgroundPositionXDecl, BackgroundPositionYDecl }); - public static readonly ICssValue BackgroundSizeDecl = new CssBackgroundSizeValue(new Constant(CssKeywords.Auto, Length.Auto), new Constant(CssKeywords.Auto, Length.Auto)); - public static readonly ICssValue BackgroundOriginDecl = new Constant(CssKeywords.BorderBox, BoxModel.PaddingBox); - public static readonly ICssValue BackgroundClipDecl = new Constant(CssKeywords.BorderBox, BoxModel.BorderBox); - public static readonly ICssValue BackgroundAttachmentDecl = new Constant(CssKeywords.Scroll, BackgroundAttachment.Scroll); - public static readonly ICssValue BookmarkStateDecl = new Constant(CssKeywords.Open, BookmarkState.Open); - public static readonly ICssValue BookmarkLabelDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue BookmarkLevelDecl = new Constant(CssKeywords.None, 0); - public static readonly ICssValue FootnotePolicyDecl = new Constant(CssKeywords.Auto, FootnotePolicy.Auto); - public static readonly ICssValue FootnoteDisplayDecl = new Constant(CssKeywords.Block, FootnoteDisplay.Block); - public static readonly ICssValue RunningDecl = new Identifier(CssKeywords.None); - public static readonly ICssValue StringSetDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue FontStyleDecl = new Constant(CssKeywords.Normal, FontStyle.Normal); - public static readonly ICssValue FontVariantDecl = new Constant(CssKeywords.Normal, FontVariant.Normal); - public static readonly ICssValue FontWeightDecl = new Constant(CssKeywords.Normal, FontWeight.Normal); - public static readonly ICssValue FontStretchDecl = new Constant(CssKeywords.Normal, FontStretch.Normal); - public static readonly ICssValue FontSizeDecl = new Constant(CssKeywords.Medium, Length.Medium); - public static readonly ICssValue FontFamilyDecl = new Label("Times New Roman"); - public static readonly ICssValue BorderWidthDecl = new Constant(CssKeywords.Medium, Length.Medium); - public static readonly ICssValue BorderStyleDecl = new Constant(CssKeywords.None, LineStyle.None); - public static readonly ICssValue BorderColorDecl = new Constant(CssKeywords.CurrentColor, Color.CurrentColor); - public static readonly ICssValue LineHeightDecl = new Constant(CssKeywords.Normal, Length.Normal); - public static readonly ICssValue BorderTopWidthDecl = new Constant(CssKeywords.Medium, Length.Medium); - public static readonly ICssValue BorderRightWidthDecl = new Constant(CssKeywords.Medium, Length.Medium); - public static readonly ICssValue BorderBottomWidthDecl = new Constant(CssKeywords.Medium, Length.Medium); - public static readonly ICssValue BorderLeftWidthDecl = new Constant(CssKeywords.Medium, Length.Medium); - public static readonly ICssValue BorderTopStyleDecl = new Constant(CssKeywords.None, LineStyle.None); - public static readonly ICssValue BorderRightStyleDecl = new Constant(CssKeywords.None, LineStyle.None); - public static readonly ICssValue BorderBottomStyleDecl = new Constant(CssKeywords.None, LineStyle.None); - public static readonly ICssValue BorderLeftStyleDecl = new Constant(CssKeywords.None, LineStyle.None); - public static readonly ICssValue BorderTopColorDecl = new Constant(CssKeywords.CurrentColor, Color.CurrentColor); - public static readonly ICssValue BorderRightColorDecl = new Constant(CssKeywords.CurrentColor, Color.CurrentColor); - public static readonly ICssValue BorderBottomColorDecl = new Constant(CssKeywords.CurrentColor, Color.CurrentColor); - public static readonly ICssValue BorderLeftColorDecl = new Constant(CssKeywords.CurrentColor, Color.CurrentColor); - public static readonly ICssValue ColumnWidthDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue ColumnCountDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue ColumnRuleWidthDecl = new Constant(CssKeywords.Medium, Length.Medium); - public static readonly ICssValue ColumnRuleStyleDecl = new Constant(CssKeywords.None, LineStyle.None); - public static readonly ICssValue ColumnRuleColorDecl = new Constant(CssKeywords.CurrentColor, Color.CurrentColor); - public static readonly ICssValue AnimationNameDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue AnimationDurationDecl = Time.Zero; + public static readonly ICssValue BackgroundSizeDecl = new CssBackgroundSizeValue(new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto), new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto)); + public static readonly ICssValue BackgroundOriginDecl = new CssConstantValue(CssKeywords.BorderBox, BoxModel.PaddingBox); + public static readonly ICssValue BackgroundClipDecl = new CssConstantValue(CssKeywords.BorderBox, BoxModel.BorderBox); + public static readonly ICssValue BackgroundAttachmentDecl = new CssConstantValue(CssKeywords.Scroll, BackgroundAttachment.Scroll); + public static readonly ICssValue BookmarkStateDecl = new CssConstantValue(CssKeywords.Open, BookmarkState.Open); + public static readonly ICssValue BookmarkLabelDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue BookmarkLevelDecl = new CssConstantValue(CssKeywords.None, 0); + public static readonly ICssValue FootnotePolicyDecl = new CssConstantValue(CssKeywords.Auto, FootnotePolicy.Auto); + public static readonly ICssValue FootnoteDisplayDecl = new CssConstantValue(CssKeywords.Block, FootnoteDisplay.Block); + public static readonly ICssValue RunningDecl = new CssIdentifierValue(CssKeywords.None); + public static readonly ICssValue StringSetDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue FontStyleDecl = new CssConstantValue(CssKeywords.Normal, FontStyle.Normal); + public static readonly ICssValue FontVariantDecl = new CssConstantValue(CssKeywords.Normal, FontVariant.Normal); + public static readonly ICssValue FontWeightDecl = new CssConstantValue(CssKeywords.Normal, FontWeight.Normal); + public static readonly ICssValue FontStretchDecl = new CssConstantValue(CssKeywords.Normal, FontStretch.Normal); + public static readonly ICssValue FontSizeDecl = new CssConstantValue(CssKeywords.Medium, CssLengthValue.Medium); + public static readonly ICssValue FontFamilyDecl = new CssStringValue("Times New Roman"); + public static readonly ICssValue BorderWidthDecl = new CssConstantValue(CssKeywords.Medium, CssLengthValue.Medium); + public static readonly ICssValue BorderStyleDecl = new CssConstantValue(CssKeywords.None, LineStyle.None); + public static readonly ICssValue BorderColorDecl = new CssConstantValue(CssKeywords.CurrentColor, CssColorValue.CurrentColor); + public static readonly ICssValue LineHeightDecl = new CssConstantValue(CssKeywords.Normal, CssLengthValue.Normal); + public static readonly ICssValue BorderTopWidthDecl = new CssConstantValue(CssKeywords.Medium, CssLengthValue.Medium); + public static readonly ICssValue BorderRightWidthDecl = new CssConstantValue(CssKeywords.Medium, CssLengthValue.Medium); + public static readonly ICssValue BorderBottomWidthDecl = new CssConstantValue(CssKeywords.Medium, CssLengthValue.Medium); + public static readonly ICssValue BorderLeftWidthDecl = new CssConstantValue(CssKeywords.Medium, CssLengthValue.Medium); + public static readonly ICssValue BorderTopStyleDecl = new CssConstantValue(CssKeywords.None, LineStyle.None); + public static readonly ICssValue BorderRightStyleDecl = new CssConstantValue(CssKeywords.None, LineStyle.None); + public static readonly ICssValue BorderBottomStyleDecl = new CssConstantValue(CssKeywords.None, LineStyle.None); + public static readonly ICssValue BorderLeftStyleDecl = new CssConstantValue(CssKeywords.None, LineStyle.None); + public static readonly ICssValue BorderTopColorDecl = new CssConstantValue(CssKeywords.CurrentColor, CssColorValue.CurrentColor); + public static readonly ICssValue BorderRightColorDecl = new CssConstantValue(CssKeywords.CurrentColor, CssColorValue.CurrentColor); + public static readonly ICssValue BorderBottomColorDecl = new CssConstantValue(CssKeywords.CurrentColor, CssColorValue.CurrentColor); + public static readonly ICssValue BorderLeftColorDecl = new CssConstantValue(CssKeywords.CurrentColor, CssColorValue.CurrentColor); + public static readonly ICssValue ColumnWidthDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue ColumnCountDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue ColumnRuleWidthDecl = new CssConstantValue(CssKeywords.Medium, CssLengthValue.Medium); + public static readonly ICssValue ColumnRuleStyleDecl = new CssConstantValue(CssKeywords.None, LineStyle.None); + public static readonly ICssValue ColumnRuleColorDecl = new CssConstantValue(CssKeywords.CurrentColor, CssColorValue.CurrentColor); + public static readonly ICssValue AnimationNameDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue AnimationDurationDecl = CssTimeValue.Zero; public static readonly ICssValue AnimationTimingFunctionDecl = CssCubicBezierValue.Ease; - public static readonly ICssValue AnimationDelayDecl = Time.Zero; - public static readonly ICssValue AnimationIterationCountDecl = new Length(1, Length.Unit.None); - public static readonly ICssValue AnimationDirectionDecl = new Constant(CssKeywords.Normal, AnimationDirection.Normal); - public static readonly ICssValue AnimationFillModeDecl = new Constant(CssKeywords.None, AnimationFillStyle.None); - public static readonly ICssValue AnimationPlayStateDecl = new Constant(CssKeywords.Running, PlayState.Running); - public static readonly ICssValue TransitionDelayDecl = Time.Zero; - public static readonly ICssValue TransitionDurationDecl = Time.Zero; - public static readonly ICssValue TransitionPropertyDecl = new Identifier(CssKeywords.All); + public static readonly ICssValue AnimationDelayDecl = CssTimeValue.Zero; + public static readonly ICssValue AnimationIterationCountDecl = new CssLengthValue(1, CssLengthValue.Unit.None); + public static readonly ICssValue AnimationDirectionDecl = new CssConstantValue(CssKeywords.Normal, AnimationDirection.Normal); + public static readonly ICssValue AnimationFillModeDecl = new CssConstantValue(CssKeywords.None, AnimationFillStyle.None); + public static readonly ICssValue AnimationPlayStateDecl = new CssConstantValue(CssKeywords.Running, PlayState.Running); + public static readonly ICssValue TransitionDelayDecl = CssTimeValue.Zero; + public static readonly ICssValue TransitionDurationDecl = CssTimeValue.Zero; + public static readonly ICssValue TransitionPropertyDecl = new CssIdentifierValue(CssKeywords.All); public static readonly ICssValue TransitionTimingFunctionDecl = CssCubicBezierValue.Ease; - public static readonly ICssValue DirectionDecl = new Constant(CssKeywords.Ltr, DirectionMode.Ltr); - public static readonly ICssValue EmptyCellsDecl = new Constant(CssKeywords.Show, true); - public static readonly ICssValue FlexGrowDecl = new Length(0, Length.Unit.None); - public static readonly ICssValue FlexShrinkDecl = new Length(1, Length.Unit.None); - public static readonly ICssValue FlexBasisDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue FlexWrapDecl = new Constant(CssKeywords.Nowrap, FlexWrapMode.NoWrap); - public static readonly ICssValue FlexDirectionDecl = new Constant(CssKeywords.Row, FlexDirection.Row); - public static readonly ICssValue FloatDecl = new Constant(CssKeywords.None, Floating.None); - public static readonly ICssValue BorderSpacingDecl = Length.Zero; - public static readonly ICssValue BoxDecorationBreakDecl = new Constant(CssKeywords.Slice, false); - public static readonly ICssValue BoxShadowDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue BoxSizingDecl = new Constant(CssKeywords.ContentBox, BoxModel.ContentBox); - public static readonly ICssValue BreakAfterDecl = new Constant(CssKeywords.Auto, BreakMode.Auto); - public static readonly ICssValue BreakBeforeDecl = new Constant(CssKeywords.Auto, BreakMode.Auto); - public static readonly ICssValue BreakInsideDecl = new Constant(CssKeywords.Auto, BreakMode.Auto); - public static readonly ICssValue PageBreakInsideDecl = new Constant(CssKeywords.Auto, BreakMode.Auto); - public static readonly ICssValue PageBreakBeforeDecl = new Constant(CssKeywords.Auto, BreakMode.Auto); - public static readonly ICssValue PageBreakAfterDecl = new Constant(CssKeywords.Auto, BreakMode.Auto); - public static readonly ICssValue BottomDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue TopDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue LeftDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue RightDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue MinHeightDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue MinWidthDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue MaxHeightDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue MaxWidthDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue MarginLeftDecl = Length.Zero; - public static readonly ICssValue MarginBottomDecl = Length.Zero; - public static readonly ICssValue MarginRightDecl = Length.Zero; - public static readonly ICssValue MarginTopDecl = Length.Zero; - public static readonly ICssValue PaddingLeftDecl = Length.Zero; - public static readonly ICssValue PaddingBottomDecl = Length.Zero; - public static readonly ICssValue PaddingRightDecl = Length.Zero; - public static readonly ICssValue PaddingTopDecl = Length.Zero; - public static readonly ICssValue CaptionSideDecl = new Constant(CssKeywords.Top, true); - public static readonly ICssValue CursorDecl = new Constant(CssKeywords.Auto, SystemCursor.Auto); - public static readonly ICssValue OverflowWrapDecl = new Constant(CssKeywords.Normal, OverflowWrap.Normal); - public static readonly ICssValue WordSpacingDecl = new Constant(CssKeywords.Normal, Length.Normal); - public static readonly ICssValue WordBreakDecl = new Constant(CssKeywords.Normal, WordBreak.Normal); - public static readonly ICssValue VisibilityDecl = new Constant(CssKeywords.Visible, Visibility.Visible); - public static readonly ICssValue VerticalAlignDecl = new Constant(CssKeywords.Baseline, VerticalAlignment.Baseline); - public static readonly ICssValue OpacityDecl = new Length(1.0, Length.Unit.None); - public static readonly ICssValue OverflowDecl = new Constant(CssKeywords.Visible, OverflowMode.Visible); - public static readonly ICssValue OutlineWidthDecl = new Constant(CssKeywords.Medium, Length.Medium); - public static readonly ICssValue OutlineStyleDecl = new Constant(CssKeywords.None, LineStyle.None); - public static readonly ICssValue OutlineColorDecl = new Constant(CssKeywords.Invert, Color.InvertedColor); - public static readonly ICssValue TextTransformDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue TextShadowDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue TextRenderingDecl = new Constant(CssKeywords.Auto, null); - public static readonly ICssValue TextOverflowDecl = new Constant(CssKeywords.Auto, OverflowMode.Clip); - public static readonly ICssValue TextOrientationDecl = new Constant(CssKeywords.Mixed, null); - public static readonly ICssValue TextJustifyDecl = new Constant(CssKeywords.Auto, TextJustify.Auto); - public static readonly ICssValue TextIndentDecl = Length.Zero; - public static readonly ICssValue TextAlignDecl = new Constant(CssKeywords.Left, HorizontalAlignment.Left); - public static readonly ICssValue TextAlignLastDecl = new Constant(CssKeywords.Auto, TextAlignLast.Auto); - public static readonly ICssValue TextDecorationLineDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue TextDecorationStyleDecl = new Constant(CssKeywords.Solid, LineStyle.Solid); - public static readonly ICssValue TextDecorationColorDecl = new Constant(CssKeywords.CurrentColor, Color.CurrentColor); - public static readonly ICssValue TextAnchorDecl = new Constant(CssKeywords.Start, TextAnchor.Start); - public static readonly ICssValue ListStyleTypeDecl = new Constant(CssKeywords.Disc, ListStyle.Disc); - public static readonly ICssValue ListStylePositionDecl = new Constant(CssKeywords.Outside, ListPosition.Outside); - public static readonly ICssValue ListStyleImageDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue LineBreakDecl = new Constant(CssKeywords.Auto, BreakMode.Auto); - public static readonly ICssValue GridTemplateRowsDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue GridTemplateColumnsDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue GridTemplateAreasDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue GridAutoRowsDecl = new Constant(CssKeywords.Auto, null); - public static readonly ICssValue GridAutoColumnsDecl = new Constant(CssKeywords.Auto, null); - public static readonly ICssValue GridAutoFlowDecl = new Constant(CssKeywords.Row, false); - public static readonly ICssValue GridColumnGapDecl = Length.Zero; - public static readonly ICssValue GridRowGapDecl = Length.Zero; - public static readonly ICssValue ColumnGapDecl = new Constant(CssKeywords.Normal, Length.Normal); - public static readonly ICssValue RowGapDecl = new Constant(CssKeywords.Normal, Length.Normal); - public static readonly ICssValue PerspectiveDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue PerspectiveOriginDecl = Point.Center; - public static readonly ICssValue PositionDecl = new Constant(CssKeywords.Inline, PositionMode.Static); - public static readonly ICssValue TransformDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue TransformStyleDecl = new Constant(CssKeywords.Flat, true); - public static readonly ICssValue TransformOriginDecl = Point.Center; - public static readonly ICssValue TableLayoutDecl = new Constant(CssKeywords.Auto, false); - public static readonly ICssValue ClearDecl = new Constant(CssKeywords.None, ClearMode.None); - public static readonly ICssValue ClipDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue StrokeOpacityDecl = new Length(1.0, Length.Unit.None); - public static readonly ICssValue StrokeLinecapDecl = new Constant(CssKeywords.Butt, StrokeLinecap.Butt); - public static readonly ICssValue StrokeLinejoinDecl = new Constant(CssKeywords.Miter, StrokeLinejoin.Miter); - public static readonly ICssValue StrokeDashoffsetDecl = Length.Zero; - public static readonly ICssValue StrokeDasharrayDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue StrokeWidthDecl = new Length(1.0, Length.Unit.Px); - public static readonly ICssValue StrokeMiterlimitDecl = new Length(1.0, Length.Unit.None); - public static readonly ICssValue RubyPositionDecl = new Constant(CssKeywords.Over, RubyPosition.Over); - public static readonly ICssValue RubyOverhangDecl = new Constant(CssKeywords.None, RubyOverhangMode.None); - public static readonly ICssValue RubyAlignDecl = new Constant(CssKeywords.SpaceAround, RubyAlignment.SpaceAround); - public static readonly ICssValue ResizeDecl = new Constant(CssKeywords.None, ResizeMode.None); - public static readonly ICssValue QuotesDecl = new Quote("«", "»"); - public static readonly ICssValue PointerEventsDecl = new Constant(CssKeywords.Auto, PointerEvent.Auto); - public static readonly ICssValue ContentDecl = new Constant(CssKeywords.Normal, null); - public static readonly ICssValue ContentVisibilityDecl = new Constant(CssKeywords.Visible, Visibility.Visible); - public static readonly ICssValue CounterIncrementDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue CounterResetDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue DisplayDecl = new Constant(CssKeywords.Inline, DisplayMode.Inline); - public static readonly ICssValue ColumnFillDecl = new Constant(CssKeywords.Balance, true); - public static readonly ICssValue ColumnSpanDecl = new Constant(CssKeywords.None, false); - public static readonly ICssValue BackfaceVisibilityDecl = new Constant(CssKeywords.Visible, Visibility.Visible); - public static readonly ICssValue BorderImageSourceDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue BorderImageSliceDecl = Length.Full; - public static readonly ICssValue BorderImageWidthDecl = new Length(1, Length.Unit.None); - public static readonly ICssValue BorderImageOutsetDecl = Length.Zero; - public static readonly ICssValue BorderImageRepeatDecl = new Constant(CssKeywords.Stretch, BorderRepeat.Stretch); - public static readonly ICssValue BorderCollapseDecl = new Constant(CssKeywords.Separate, true); - public static readonly ICssValue BorderRadiusDecl = Length.Zero; - public static readonly ICssValue AlignSelfDecl = new Constant(CssKeywords.Auto, FlexContentMode.Auto); - public static readonly ICssValue AlignItemsDecl = new Constant(CssKeywords.Normal, FlexContentMode.Stretch); - public static readonly ICssValue AlignContentDecl = new Constant(CssKeywords.Normal, FlexContentMode.Stretch); - public static readonly ICssValue JustifyContentDecl = new Constant(CssKeywords.Normal, null); - public static readonly ICssValue JustifyItemsDecl = new Constant(CssKeywords.Legacy, null); - public static readonly ICssValue JustifySelfDecl = new Constant(CssKeywords.Auto, FlexContentMode.Auto); - public static readonly ICssValue UnicodeBidiDecl = new Constant(CssKeywords.Normal, UnicodeMode.Normal); - public static readonly ICssValue WordWrapDecl = new Constant(CssKeywords.Normal, OverflowWrap.Normal); - public static readonly ICssValue WidowsDecl = new Length(2, Length.Unit.None); - public static readonly ICssValue OrphansDecl = new Length(2, Length.Unit.None); - public static readonly ICssValue OrderDecl = new Length(0, Length.Unit.None); - public static readonly ICssValue ObjectFitDecl = new Constant(CssKeywords.Fill, ObjectFitting.Fill); - public static readonly ICssValue ObjectPositionDecl = Point.Center; - public static readonly ICssValue WhiteSpaceDecl = new Constant(CssKeywords.Normal, Whitespace.Normal); - public static readonly ICssValue ZIndexDecl = new Constant(CssKeywords.Auto, Length.Auto); - public static readonly ICssValue WidthDecl = Length.Auto; - public static readonly ICssValue HeightDecl = Length.Auto; + public static readonly ICssValue DirectionDecl = new CssConstantValue(CssKeywords.Ltr, DirectionMode.Ltr); + public static readonly ICssValue EmptyCellsDecl = new CssConstantValue(CssKeywords.Show, true); + public static readonly ICssValue FlexGrowDecl = new CssLengthValue(0, CssLengthValue.Unit.None); + public static readonly ICssValue FlexShrinkDecl = new CssLengthValue(1, CssLengthValue.Unit.None); + public static readonly ICssValue FlexBasisDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue FlexWrapDecl = new CssConstantValue(CssKeywords.Nowrap, FlexWrapMode.NoWrap); + public static readonly ICssValue FlexDirectionDecl = new CssConstantValue(CssKeywords.Row, FlexDirection.Row); + public static readonly ICssValue FloatDecl = new CssConstantValue(CssKeywords.None, Floating.None); + public static readonly ICssValue BorderSpacingDecl = CssLengthValue.Zero; + public static readonly ICssValue BoxDecorationBreakDecl = new CssConstantValue(CssKeywords.Slice, false); + public static readonly ICssValue BoxShadowDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue BoxSizingDecl = new CssConstantValue(CssKeywords.ContentBox, BoxModel.ContentBox); + public static readonly ICssValue BreakAfterDecl = new CssConstantValue(CssKeywords.Auto, BreakMode.Auto); + public static readonly ICssValue BreakBeforeDecl = new CssConstantValue(CssKeywords.Auto, BreakMode.Auto); + public static readonly ICssValue BreakInsideDecl = new CssConstantValue(CssKeywords.Auto, BreakMode.Auto); + public static readonly ICssValue PageBreakInsideDecl = new CssConstantValue(CssKeywords.Auto, BreakMode.Auto); + public static readonly ICssValue PageBreakBeforeDecl = new CssConstantValue(CssKeywords.Auto, BreakMode.Auto); + public static readonly ICssValue PageBreakAfterDecl = new CssConstantValue(CssKeywords.Auto, BreakMode.Auto); + public static readonly ICssValue BottomDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue TopDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue LeftDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue RightDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue MinHeightDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue MinWidthDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue MaxHeightDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue MaxWidthDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue MarginBlockEndDecl = CssLengthValue.Zero; + public static readonly ICssValue MarginBlockStartDecl = CssLengthValue.Zero; + public static readonly ICssValue MarginInlineEndDecl = CssLengthValue.Zero; + public static readonly ICssValue MarginInlineStartDecl = CssLengthValue.Zero; + public static readonly ICssValue MarginLeftDecl = CssLengthValue.Zero; + public static readonly ICssValue MarginBottomDecl = CssLengthValue.Zero; + public static readonly ICssValue MarginRightDecl = CssLengthValue.Zero; + public static readonly ICssValue MarginTopDecl = CssLengthValue.Zero; + public static readonly ICssValue PaddingBlockEndDecl = CssLengthValue.Zero; + public static readonly ICssValue PaddingBlockStartDecl = CssLengthValue.Zero; + public static readonly ICssValue PaddingInlineEndDecl = CssLengthValue.Zero; + public static readonly ICssValue PaddingInlineStartDecl = CssLengthValue.Zero; + public static readonly ICssValue PaddingLeftDecl = CssLengthValue.Zero; + public static readonly ICssValue PaddingBottomDecl = CssLengthValue.Zero; + public static readonly ICssValue PaddingRightDecl = CssLengthValue.Zero; + public static readonly ICssValue PaddingTopDecl = CssLengthValue.Zero; + public static readonly ICssValue CaptionSideDecl = new CssConstantValue(CssKeywords.Top, true); + public static readonly ICssValue CursorDecl = new CssConstantValue(CssKeywords.Auto, SystemCursor.Auto); + public static readonly ICssValue OverflowWrapDecl = new CssConstantValue(CssKeywords.Normal, OverflowWrap.Normal); + public static readonly ICssValue WordSpacingDecl = new CssConstantValue(CssKeywords.Normal, CssLengthValue.Normal); + public static readonly ICssValue WordBreakDecl = new CssConstantValue(CssKeywords.Normal, WordBreak.Normal); + public static readonly ICssValue VisibilityDecl = new CssConstantValue(CssKeywords.Visible, Visibility.Visible); + public static readonly ICssValue VerticalAlignDecl = new CssConstantValue(CssKeywords.Baseline, VerticalAlignment.Baseline); + public static readonly ICssValue OpacityDecl = new CssLengthValue(1.0, CssLengthValue.Unit.None); + public static readonly ICssValue OverflowDecl = new CssConstantValue(CssKeywords.Visible, OverflowMode.Visible); + public static readonly ICssValue OutlineWidthDecl = new CssConstantValue(CssKeywords.Medium, CssLengthValue.Medium); + public static readonly ICssValue OutlineStyleDecl = new CssConstantValue(CssKeywords.None, LineStyle.None); + public static readonly ICssValue OutlineColorDecl = new CssConstantValue(CssKeywords.Invert, CssColorValue.InvertedColor); + public static readonly ICssValue TextTransformDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue TextShadowDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue TextRenderingDecl = new CssConstantValue(CssKeywords.Auto, null); + public static readonly ICssValue TextOverflowDecl = new CssConstantValue(CssKeywords.Auto, OverflowMode.Clip); + public static readonly ICssValue TextOrientationDecl = new CssConstantValue(CssKeywords.Mixed, null); + public static readonly ICssValue TextJustifyDecl = new CssConstantValue(CssKeywords.Auto, TextJustify.Auto); + public static readonly ICssValue TextIndentDecl = CssLengthValue.Zero; + public static readonly ICssValue TextAlignDecl = new CssConstantValue(CssKeywords.Left, HorizontalAlignment.Left); + public static readonly ICssValue TextAlignLastDecl = new CssConstantValue(CssKeywords.Auto, TextAlignLast.Auto); + public static readonly ICssValue TextDecorationLineDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue TextDecorationStyleDecl = new CssConstantValue(CssKeywords.Solid, LineStyle.Solid); + public static readonly ICssValue TextDecorationColorDecl = new CssConstantValue(CssKeywords.CurrentColor, CssColorValue.CurrentColor); + public static readonly ICssValue TextAnchorDecl = new CssConstantValue(CssKeywords.Start, TextAnchor.Start); + public static readonly ICssValue ListStyleTypeDecl = new CssConstantValue(CssKeywords.Disc, ListStyle.Disc); + public static readonly ICssValue ListStylePositionDecl = new CssConstantValue(CssKeywords.Outside, ListPosition.Outside); + public static readonly ICssValue ListStyleImageDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue LineBreakDecl = new CssConstantValue(CssKeywords.Auto, BreakMode.Auto); + public static readonly ICssValue GridTemplateRowsDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue GridTemplateColumnsDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue GridTemplateAreasDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue GridAutoRowsDecl = new CssConstantValue(CssKeywords.Auto, null); + public static readonly ICssValue GridAutoColumnsDecl = new CssConstantValue(CssKeywords.Auto, null); + public static readonly ICssValue GridAutoFlowDecl = new CssConstantValue(CssKeywords.Row, false); + public static readonly ICssValue GridColumnGapDecl = CssLengthValue.Zero; + public static readonly ICssValue GridRowGapDecl = CssLengthValue.Zero; + public static readonly ICssValue ColumnGapDecl = new CssConstantValue(CssKeywords.Normal, CssLengthValue.Normal); + public static readonly ICssValue RowGapDecl = new CssConstantValue(CssKeywords.Normal, CssLengthValue.Normal); + public static readonly ICssValue PerspectiveDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue PerspectiveOriginDecl = CssPoint2D.Center; + public static readonly ICssValue PositionDecl = new CssConstantValue(CssKeywords.Inline, PositionMode.Static); + public static readonly ICssValue TransformDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue TransformStyleDecl = new CssConstantValue(CssKeywords.Flat, true); + public static readonly ICssValue TransformOriginDecl = CssPoint2D.Center; + public static readonly ICssValue TableLayoutDecl = new CssConstantValue(CssKeywords.Auto, false); + public static readonly ICssValue ClearDecl = new CssConstantValue(CssKeywords.None, ClearMode.None); + public static readonly ICssValue ClipDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue StrokeOpacityDecl = new CssLengthValue(1.0, CssLengthValue.Unit.None); + public static readonly ICssValue StrokeLinecapDecl = new CssConstantValue(CssKeywords.Butt, StrokeLinecap.Butt); + public static readonly ICssValue StrokeLinejoinDecl = new CssConstantValue(CssKeywords.Miter, StrokeLinejoin.Miter); + public static readonly ICssValue StrokeDashoffsetDecl = CssLengthValue.Zero; + public static readonly ICssValue StrokeDasharrayDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue StrokeWidthDecl = new CssLengthValue(1.0, CssLengthValue.Unit.Px); + public static readonly ICssValue StrokeMiterlimitDecl = new CssLengthValue(1.0, CssLengthValue.Unit.None); + public static readonly ICssValue RubyPositionDecl = new CssConstantValue(CssKeywords.Over, RubyPosition.Over); + public static readonly ICssValue RubyOverhangDecl = new CssConstantValue(CssKeywords.None, RubyOverhangMode.None); + public static readonly ICssValue RubyAlignDecl = new CssConstantValue(CssKeywords.SpaceAround, RubyAlignment.SpaceAround); + public static readonly ICssValue ResizeDecl = new CssConstantValue(CssKeywords.None, ResizeMode.None); + public static readonly ICssValue QuotesDecl = new CssQuoteValue("«", "»"); + public static readonly ICssValue PointerEventsDecl = new CssConstantValue(CssKeywords.Auto, PointerEvent.Auto); + public static readonly ICssValue ContentDecl = new CssConstantValue(CssKeywords.Normal, null); + public static readonly ICssValue ContentVisibilityDecl = new CssConstantValue(CssKeywords.Visible, Visibility.Visible); + public static readonly ICssValue CounterIncrementDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue CounterResetDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue DisplayDecl = new CssConstantValue(CssKeywords.Inline, DisplayMode.Inline); + public static readonly ICssValue ColumnFillDecl = new CssConstantValue(CssKeywords.Balance, true); + public static readonly ICssValue ColumnSpanDecl = new CssConstantValue(CssKeywords.None, false); + public static readonly ICssValue BackfaceVisibilityDecl = new CssConstantValue(CssKeywords.Visible, Visibility.Visible); + public static readonly ICssValue BorderImageSourceDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue BorderImageSliceDecl = CssLengthValue.Full; + public static readonly ICssValue BorderImageWidthDecl = new CssLengthValue(1, CssLengthValue.Unit.None); + public static readonly ICssValue BorderImageOutsetDecl = CssLengthValue.Zero; + public static readonly ICssValue BorderImageRepeatDecl = new CssConstantValue(CssKeywords.Stretch, BorderRepeat.Stretch); + public static readonly ICssValue BorderCollapseDecl = new CssConstantValue(CssKeywords.Separate, true); + public static readonly ICssValue BorderRadiusDecl = CssLengthValue.Zero; + public static readonly ICssValue AlignSelfDecl = new CssConstantValue(CssKeywords.Auto, FlexContentMode.Auto); + public static readonly ICssValue AlignItemsDecl = new CssConstantValue(CssKeywords.Normal, FlexContentMode.Stretch); + public static readonly ICssValue AlignContentDecl = new CssConstantValue(CssKeywords.Normal, FlexContentMode.Stretch); + public static readonly ICssValue JustifyContentDecl = new CssConstantValue(CssKeywords.Normal, null); + public static readonly ICssValue JustifyItemsDecl = new CssConstantValue(CssKeywords.Legacy, null); + public static readonly ICssValue JustifySelfDecl = new CssConstantValue(CssKeywords.Auto, FlexContentMode.Auto); + public static readonly ICssValue UnicodeBidiDecl = new CssConstantValue(CssKeywords.Normal, UnicodeMode.Normal); + public static readonly ICssValue WordWrapDecl = new CssConstantValue(CssKeywords.Normal, OverflowWrap.Normal); + public static readonly ICssValue WidowsDecl = new CssLengthValue(2, CssLengthValue.Unit.None); + public static readonly ICssValue OrphansDecl = new CssLengthValue(2, CssLengthValue.Unit.None); + public static readonly ICssValue OrderDecl = new CssLengthValue(0, CssLengthValue.Unit.None); + public static readonly ICssValue ObjectFitDecl = new CssConstantValue(CssKeywords.Fill, ObjectFitting.Fill); + public static readonly ICssValue ObjectPositionDecl = CssPoint2D.Center; + public static readonly ICssValue WhiteSpaceDecl = new CssConstantValue(CssKeywords.Normal, Whitespace.Normal); + public static readonly ICssValue ZIndexDecl = new CssConstantValue(CssKeywords.Auto, CssLengthValue.Auto); + public static readonly ICssValue WidthDecl = CssLengthValue.Auto; + public static readonly ICssValue HeightDecl = CssLengthValue.Auto; public static readonly ICssValue ScrollbarTrackColorDecl = CssColors.GetColor("scrollbar"); public static readonly ICssValue ScrollbarShadowColorDecl = CssColors.GetColor("threeddarkshadow"); public static readonly ICssValue ScrollbarHighlightColorDecl = CssColors.GetColor("threedhighlight"); public static readonly ICssValue ScrollbarFaceColorDecl = CssColors.GetColor("threedface"); public static readonly ICssValue ScrollbarDarkshadowColorDecl = CssColors.GetColor("threeddarkshadow"); - public static readonly ICssValue ScrollbarBaseColorDecl = Color.Transparent; + public static readonly ICssValue ScrollbarBaseColorDecl = CssColorValue.Transparent; public static readonly ICssValue ScrollbarArrowColorDecl = CssColors.GetColor("buttontext"); - public static readonly ICssValue Scrollbar3dLightColorDecl = Color.White; - public static readonly ICssValue LetterSpacingDecl = Length.Normal; - public static readonly ICssValue FontSizeAdjustDecl = new Length(1.0, Length.Unit.Em); - public static readonly ICssValue ScrollSnapTypeDecl = new Constant(CssKeywords.None, null); - public static readonly ICssValue ScrollMarginDecl = Length.Zero; - public static readonly ICssValue ScrollSnapAlignDecl = new Constant(CssKeywords.None, ScrollSnapAlign.None); + public static readonly ICssValue Scrollbar3dLightColorDecl = CssColorValue.White; + public static readonly ICssValue LetterSpacingDecl = CssLengthValue.Normal; + public static readonly ICssValue FontSizeAdjustDecl = new CssLengthValue(1.0, CssLengthValue.Unit.Em); + public static readonly ICssValue ScrollSnapTypeDecl = new CssConstantValue(CssKeywords.None, null); + public static readonly ICssValue ScrollMarginDecl = CssLengthValue.Zero; + public static readonly ICssValue ScrollSnapAlignDecl = new CssConstantValue(CssKeywords.None, ScrollSnapAlign.None); } } diff --git a/src/AngleSharp.Css/Constants/Map.cs b/src/AngleSharp.Css/Constants/Map.cs index f5f6d81c..5c7b47c6 100644 --- a/src/AngleSharp.Css/Constants/Map.cs +++ b/src/AngleSharp.Css/Constants/Map.cs @@ -14,7 +14,7 @@ static class Map /// /// Contains the string-Whitespace mapping. /// - public static readonly Dictionary Whitespaces = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary Whitespaces = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Normal, Whitespace.Normal }, { CssKeywords.Pre, Whitespace.Pre }, @@ -26,22 +26,22 @@ static class Map /// /// Contains the string-Angle mapping for linear-gradients.s /// - public static readonly Dictionary GradientAngles = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary GradientAngles = new(StringComparer.OrdinalIgnoreCase) { - { CssKeywords.Left, new Angle(270.0, Angle.Unit.Deg) }, - { CssKeywords.Top, new Angle(0.0, Angle.Unit.Deg) }, - { CssKeywords.Right, new Angle(90.0, Angle.Unit.Deg) }, - { CssKeywords.Bottom, new Angle(180.0, Angle.Unit.Deg) }, - { CssKeywords.LeftTop, new Angle(315.0, Angle.Unit.Deg) }, - { CssKeywords.LeftBottom, new Angle(225.0, Angle.Unit.Deg) }, - { CssKeywords.RightTop, new Angle(45.0, Angle.Unit.Deg) }, - { CssKeywords.RightBottom, new Angle(135.0, Angle.Unit.Deg) }, + { CssKeywords.Left, new CssAngleValue(270.0, CssAngleValue.Unit.Deg) }, + { CssKeywords.Top, new CssAngleValue(0.0, CssAngleValue.Unit.Deg) }, + { CssKeywords.Right, new CssAngleValue(90.0, CssAngleValue.Unit.Deg) }, + { CssKeywords.Bottom, new CssAngleValue(180.0, CssAngleValue.Unit.Deg) }, + { CssKeywords.LeftTop, new CssAngleValue(315.0, CssAngleValue.Unit.Deg) }, + { CssKeywords.LeftBottom, new CssAngleValue(225.0, CssAngleValue.Unit.Deg) }, + { CssKeywords.RightTop, new CssAngleValue(45.0, CssAngleValue.Unit.Deg) }, + { CssKeywords.RightBottom, new CssAngleValue(135.0, CssAngleValue.Unit.Deg) }, }; /// /// Contains the string-TextTransform mapping. /// - public static readonly Dictionary TextTransforms = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary TextTransforms = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, TextTransform.None }, { CssKeywords.Capitalize, TextTransform.Capitalize }, @@ -53,7 +53,7 @@ static class Map /// /// Contains the string-TextAlignLast mapping. /// - public static readonly Dictionary TextAlignLasts = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary TextAlignLasts = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Auto, TextAlignLast.Auto }, { CssKeywords.Start, TextAlignLast.Start }, @@ -67,7 +67,7 @@ static class Map /// /// Contains the string-TextAnchor mapping. /// - public static readonly Dictionary TextAnchors = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary TextAnchors = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Start, TextAnchor.Start }, { CssKeywords.Middle, TextAnchor.Middle }, @@ -77,7 +77,7 @@ static class Map /// /// Contains the string-TextJustify mapping. /// - public static readonly Dictionary TextJustifies = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary TextJustifies = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Auto, TextJustify.Auto }, { CssKeywords.Distribute, TextJustify.Distribute }, @@ -93,18 +93,22 @@ static class Map /// /// Contains the string-HorizontalAlignment mapping. /// - public static readonly Dictionary HorizontalAlignments = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary HorizontalAlignments = new(StringComparer.OrdinalIgnoreCase) { - { CssKeywords.Left, HorizontalAlignment.Left }, - { CssKeywords.Right, HorizontalAlignment.Right }, - { CssKeywords.Center, HorizontalAlignment.Center }, - { CssKeywords.Justify, HorizontalAlignment.Justify }, + { CssKeywords.Left, TextAlign.Left }, + { CssKeywords.Right, TextAlign.Right }, + { CssKeywords.Center, TextAlign.Center }, + { CssKeywords.Justify, TextAlign.Justify }, + { CssKeywords.Start, TextAlign.Start }, + { CssKeywords.End, TextAlign.End }, + { CssKeywords.JustifyAll, TextAlign.JustifyAll }, + { CssKeywords.MatchParent, TextAlign.MatchParent }, }; /// /// Contains the string-VerticalAlignment mapping. /// - public static readonly Dictionary VerticalAlignments = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary VerticalAlignments = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Baseline, VerticalAlignment.Baseline }, { CssKeywords.Sub, VerticalAlignment.Sub }, @@ -119,7 +123,7 @@ static class Map /// /// Contains the string-LineStyle mapping. /// - public static readonly Dictionary LineStyles = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary LineStyles = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, LineStyle.None }, { CssKeywords.Solid, LineStyle.Solid }, @@ -136,7 +140,7 @@ static class Map /// /// Contains the string-BoxModel mapping. /// - public static readonly Dictionary BoxModels = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary BoxModels = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.BorderBox, BoxModel.BorderBox }, { CssKeywords.PaddingBox, BoxModel.PaddingBox }, @@ -146,21 +150,21 @@ static class Map /// /// Contains the string-TimingFunction mapping. /// - public static readonly Dictionary TimingFunctions = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary TimingFunctions = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Ease, new CssCubicBezierValue(0.25, 0.1, 0.25, 1.0) }, { CssKeywords.EaseIn, new CssCubicBezierValue(0.42, 0.0, 1.0, 1.0) }, { CssKeywords.EaseOut, new CssCubicBezierValue(0.0, 0.0, 0.58, 1.0) }, { CssKeywords.EaseInOut, new CssCubicBezierValue(0.42, 0.0, 0.58, 1.0) }, { CssKeywords.Linear, new CssCubicBezierValue(0.0, 0.0, 1.0, 1.0) }, - { CssKeywords.StepStart, new CssStepsValue(1, true) }, - { CssKeywords.StepEnd, new CssStepsValue(1, false) }, + { CssKeywords.StepStart, new CssStepsValue(CssIntegerValue.One, true) }, + { CssKeywords.StepEnd, new CssStepsValue(CssIntegerValue.One, false) }, }; /// /// Contains the string-AnimationFillStyle mapping. /// - public static readonly Dictionary AnimationFillStyles = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary AnimationFillStyles = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, AnimationFillStyle.None }, { CssKeywords.Forwards, AnimationFillStyle.Forwards }, @@ -171,7 +175,7 @@ static class Map /// /// Contains the string-AnimationDirection mapping. /// - public static readonly Dictionary AnimationDirections = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary AnimationDirections = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Normal, AnimationDirection.Normal }, { CssKeywords.Reverse, AnimationDirection.Reverse }, @@ -182,7 +186,7 @@ static class Map /// /// Contains the string-Visibility mapping. /// - public static readonly Dictionary Visibilities = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary Visibilities = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Visible, Visibility.Visible }, { CssKeywords.Hidden, Visibility.Hidden }, @@ -192,7 +196,7 @@ static class Map /// /// Contains the string-PlayState mapping. /// - public static readonly Dictionary PlayStates = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary PlayStates = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Running, PlayState.Running }, { CssKeywords.Paused, PlayState.Paused }, @@ -201,7 +205,7 @@ static class Map /// /// Contains the string-FontVariant mapping. /// - public static readonly Dictionary FontVariants = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary FontVariants = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Normal, FontVariant.Normal }, { CssKeywords.SmallCaps, FontVariant.SmallCaps }, @@ -210,38 +214,90 @@ static class Map /// /// Contains the string-DirectionMode mapping. /// - public static readonly Dictionary DirectionModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary DirectionModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Ltr, DirectionMode.Ltr }, { CssKeywords.Rtl, DirectionMode.Rtl }, }; + /// + /// Contains the string-SymbolsType mapping. + /// + public static readonly Dictionary SymbolsTypes = new(StringComparer.OrdinalIgnoreCase) + { + { CssKeywords.Cyclic, SymbolsType.Cyclic }, + { CssKeywords.Numeric, SymbolsType.Numeric }, + { CssKeywords.Alphabetic, SymbolsType.Alphabetic }, + { CssKeywords.Symbolic, SymbolsType.Symbolic }, + { CssKeywords.Fixed, SymbolsType.Fixed }, + }; + /// /// Contains the string-ListStyle mapping. /// - public static readonly Dictionary ListStyles = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary ListStyles = new(StringComparer.OrdinalIgnoreCase) { + { CssKeywords.None, ListStyle.None }, { CssKeywords.Disc, ListStyle.Disc }, { CssKeywords.Circle, ListStyle.Circle }, { CssKeywords.Square, ListStyle.Square }, { CssKeywords.Decimal, ListStyle.Decimal }, + { CssKeywords.CjkDecimal, ListStyle.CjkDecimal }, { CssKeywords.DecimalLeadingZero, ListStyle.DecimalLeadingZero }, { CssKeywords.LowerRoman, ListStyle.LowerRoman }, { CssKeywords.UpperRoman, ListStyle.UpperRoman }, { CssKeywords.LowerGreek, ListStyle.LowerGreek }, { CssKeywords.LowerLatin, ListStyle.LowerLatin }, + { CssKeywords.LowerAlpha, ListStyle.LowerLatin }, { CssKeywords.UpperLatin, ListStyle.UpperLatin }, + { CssKeywords.UpperAlpha, ListStyle.UpperLatin }, + { CssKeywords.ArabicIndic, ListStyle.ArabicIndic }, { CssKeywords.Armenian, ListStyle.Armenian }, + { CssKeywords.Bengali, ListStyle.Bengali }, + { CssKeywords.Cambodian, ListStyle.Cambodian }, + { CssKeywords.CjkEarthlyBranch, ListStyle.CjkEarthlyBranch }, + { CssKeywords.CjkHeavenlyStem, ListStyle.CjkHeavenlyStem }, + { CssKeywords.CjkIdeographic, ListStyle.TradChineseInformal }, + { CssKeywords.Devanagari, ListStyle.Devanagari }, + { CssKeywords.EthiopicNumeric, ListStyle.EthiopicNumeric }, { CssKeywords.Georgian, ListStyle.Georgian }, - { CssKeywords.LowerAlpha, ListStyle.LowerLatin }, - { CssKeywords.UpperAlpha, ListStyle.UpperLatin }, - { CssKeywords.None, ListStyle.None }, + { CssKeywords.Gurmukhi, ListStyle.Gurmukhi }, + { CssKeywords.Hebrew, ListStyle.Hebrew }, + { CssKeywords.Gujarati, ListStyle.Gujarati }, + { CssKeywords.Hiragana, ListStyle.Hiragana }, + { CssKeywords.HiraganaIroha, ListStyle.IrohaHiragana }, + { CssKeywords.JapaneseFormal, ListStyle.JapaneseFormal }, + { CssKeywords.JapaneseInformal, ListStyle.JapaneseInformal }, + { CssKeywords.Kannada, ListStyle.Kannada }, + { CssKeywords.KatakanaIroha, ListStyle.KatakanaIroha }, + { CssKeywords.Katakana, ListStyle.Katakana }, + { CssKeywords.KoreanHangulFormal, ListStyle.KoreanHangulFormal }, + { CssKeywords.KoreanHanjaFormal, ListStyle.KoreanHanjaFormal }, + { CssKeywords.KoreanHanjaInformal, ListStyle.KoreanHanjaInformal }, + { CssKeywords.Lao, ListStyle.Lao }, + { CssKeywords.UpperArmenian, ListStyle.UpperArmenian }, + { CssKeywords.LowerArmenian, ListStyle.LowerArmenian }, + { CssKeywords.Malayalam, ListStyle.Malayalam }, + { CssKeywords.Mongolian, ListStyle.Mongolian }, + { CssKeywords.Myanmar, ListStyle.Myanmar }, + { CssKeywords.Oriya, ListStyle.Oriya }, + { CssKeywords.Persian, ListStyle.Persian }, + { CssKeywords.SimpChineseFormal, ListStyle.SimpChineseFormal }, + { CssKeywords.SimpChineseInformal, ListStyle.SimpChineseInformal }, + { CssKeywords.Tamil, ListStyle.Tamil }, + { CssKeywords.Telugu, ListStyle.Telugu }, + { CssKeywords.Thai, ListStyle.Thai }, + { CssKeywords.Tibetan, ListStyle.Tibetan }, + { CssKeywords.TradChineseFormal, ListStyle.TradChineseFormal }, + { CssKeywords.TradChineseInformal, ListStyle.TradChineseInformal }, + { CssKeywords.DisclosureClosed, ListStyle.DisclosureClosed }, + { CssKeywords.DisclosureOpen, ListStyle.DisclosureOpen }, }; /// /// Contains the string-ListPosition mapping. /// - public static readonly Dictionary ListPositions = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary ListPositions = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Inside, ListPosition.Inside }, { CssKeywords.Outside, ListPosition.Outside }, @@ -250,24 +306,24 @@ static class Map /// /// Contains the string-length mapping. /// - public static readonly Dictionary FontSizes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary FontSizes = new(StringComparer.OrdinalIgnoreCase) { - { CssKeywords.XxSmall, new Length(0.6, Length.Unit.Em) }, - { CssKeywords.XSmall, new Length(0.75, Length.Unit.Em) }, - { CssKeywords.Small, new Length(8.0 / 9.0, Length.Unit.Em) }, - { CssKeywords.Medium, new Length(1.0, Length.Unit.Em) }, - { CssKeywords.Large, new Length(1.2, Length.Unit.Em) }, - { CssKeywords.XLarge, new Length(1.5, Length.Unit.Em) }, - { CssKeywords.XxLarge, new Length(2.0, Length.Unit.Em) }, - { CssKeywords.XxxLarge, new Length(3.0, Length.Unit.Em) }, - { CssKeywords.Larger, new Length(120.0, Length.Unit.Percent) }, - { CssKeywords.Smaller, new Length(80.0, Length.Unit.Percent) }, + { CssKeywords.XxSmall, new CssLengthValue(0.6, CssLengthValue.Unit.Em) }, + { CssKeywords.XSmall, new CssLengthValue(0.75, CssLengthValue.Unit.Em) }, + { CssKeywords.Small, new CssLengthValue(8.0 / 9.0, CssLengthValue.Unit.Em) }, + { CssKeywords.Medium, new CssLengthValue(1.0, CssLengthValue.Unit.Em) }, + { CssKeywords.Large, new CssLengthValue(1.2, CssLengthValue.Unit.Em) }, + { CssKeywords.XLarge, new CssLengthValue(1.5, CssLengthValue.Unit.Em) }, + { CssKeywords.XxLarge, new CssLengthValue(2.0, CssLengthValue.Unit.Em) }, + { CssKeywords.XxxLarge, new CssLengthValue(3.0, CssLengthValue.Unit.Em) }, + { CssKeywords.Larger, new CssLengthValue(120.0, CssLengthValue.Unit.Percent) }, + { CssKeywords.Smaller, new CssLengthValue(80.0, CssLengthValue.Unit.Percent) }, }; /// /// Contains the string-TextDecorationStyle mapping. /// - public static readonly Dictionary TextDecorationStyles = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary TextDecorationStyles = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Solid, TextDecorationStyle.Solid }, { CssKeywords.Double, TextDecorationStyle.Double }, @@ -279,7 +335,7 @@ static class Map /// /// Contains the string-TextDecorationLine mapping. /// - public static readonly Dictionary TextDecorationLines = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary TextDecorationLines = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Underline, TextDecorationLine.Underline }, { CssKeywords.Overline, TextDecorationLine.Overline }, @@ -290,7 +346,7 @@ static class Map /// /// Contains the string-BorderRepeat mapping. /// - public static readonly Dictionary BorderRepeats = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary BorderRepeats = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Stretch, BorderRepeat.Stretch }, { CssKeywords.Repeat, BorderRepeat.Repeat }, @@ -300,17 +356,17 @@ static class Map /// /// Contains the string-border width mapping. /// - public static readonly Dictionary BorderWidths = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary BorderWidths = new(StringComparer.OrdinalIgnoreCase) { - { CssKeywords.Thin, Length.Thin }, - { CssKeywords.Medium, Length.Medium }, - { CssKeywords.Thick, Length.Thick }, + { CssKeywords.Thin, CssLengthValue.Thin }, + { CssKeywords.Medium, CssLengthValue.Medium }, + { CssKeywords.Thick, CssLengthValue.Thick }, }; /// /// Contains the string-font family mapping. /// - public static readonly Dictionary FontFamilies = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary FontFamilies = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Serif, "Times New Roman" }, { CssKeywords.SansSerif, "Arial" }, @@ -322,7 +378,7 @@ static class Map /// /// Contains the string-BackgroundAttachment mapping. /// - public static readonly Dictionary BackgroundAttachments = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary BackgroundAttachments = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Fixed, BackgroundAttachment.Fixed }, { CssKeywords.Local, BackgroundAttachment.Local }, @@ -332,7 +388,7 @@ static class Map /// /// Contains the string-FontStyle mapping. /// - public static readonly Dictionary FontStyles = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary FontStyles = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Normal, FontStyle.Normal }, { CssKeywords.Italic, FontStyle.Italic }, @@ -342,7 +398,7 @@ static class Map /// /// Contains the string-FontStretch mapping. /// - public static readonly Dictionary FontStretches = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary FontStretches = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Normal, FontStretch.Normal }, { CssKeywords.UltraCondensed, FontStretch.UltraCondensed }, @@ -358,7 +414,7 @@ static class Map /// /// Contains the string-BreakMode (general) mapping. /// - public static readonly Dictionary BreakModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary BreakModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Auto, BreakMode.Auto }, { CssKeywords.Always, BreakMode.Always }, @@ -374,7 +430,7 @@ static class Map /// /// Contains the string-BreakMode (page) mapping. /// - public static readonly Dictionary PageBreakModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary PageBreakModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Auto, BreakMode.Auto }, { CssKeywords.Always, BreakMode.Always }, @@ -386,7 +442,7 @@ static class Map /// /// Contains the string-BreakMode (inside) mapping. /// - public static readonly Dictionary BreakInsideModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary BreakInsideModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Auto, BreakMode.Auto }, { CssKeywords.Avoid, BreakMode.Avoid }, @@ -398,7 +454,7 @@ static class Map /// /// Contains the string-BreakMode (page/inside) mapping. /// - public static readonly Dictionary PageBreakInsideModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary PageBreakInsideModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Auto, BreakMode.Auto }, { CssKeywords.Avoid, BreakMode.Avoid }, @@ -407,7 +463,7 @@ static class Map /// /// Contains the string-horizontal modes mapping. /// - public static readonly Dictionary HorizontalModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary HorizontalModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Left, 0.0 }, { CssKeywords.Center, 0.5 }, @@ -417,7 +473,7 @@ static class Map /// /// Contains the string-vertical modes mapping. /// - public static readonly Dictionary VerticalModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary VerticalModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Top, 0.0 }, { CssKeywords.Center, 0.5 }, @@ -427,7 +483,7 @@ static class Map /// /// Contains the string-UnicodeMode mapping. /// - public static readonly Dictionary UnicodeModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary UnicodeModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Normal, UnicodeMode.Normal }, { CssKeywords.Embed, UnicodeMode.Embed }, @@ -440,7 +496,7 @@ static class Map /// /// Contains the string-whitespace mapping. /// - public static readonly Dictionary SystemCursors = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary SystemCursors = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Auto, SystemCursor.Auto }, { CssKeywords.Default, SystemCursor.Default }, @@ -483,7 +539,7 @@ static class Map /// /// Contains the string-PositionMode mapping. /// - public static readonly Dictionary PositionModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary PositionModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Static, PositionMode.Static }, { CssKeywords.Relative, PositionMode.Relative }, @@ -495,7 +551,7 @@ static class Map /// /// Contains the string-OverflowMode mapping. /// - public static readonly Dictionary OverflowModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary OverflowModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Visible, OverflowMode.Visible }, { CssKeywords.Hidden, OverflowMode.Hidden }, @@ -506,7 +562,7 @@ static class Map /// /// Contains the extended string-OverflowMode mapping. /// - public static readonly Dictionary OverflowExtendedModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary OverflowExtendedModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Visible, OverflowMode.Visible }, { CssKeywords.Hidden, OverflowMode.Hidden }, @@ -518,7 +574,7 @@ static class Map /// /// Contains the string-Floating mapping. /// - public static readonly Dictionary Floatings = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary Floatings = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, Floating.None }, { CssKeywords.Left, Floating.Left }, @@ -529,7 +585,7 @@ static class Map /// /// Contains the string-DisplayMode mapping. /// - public static readonly Dictionary DisplayModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary DisplayModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, DisplayMode.None }, { CssKeywords.Inline, DisplayMode.Inline }, @@ -555,7 +611,7 @@ static class Map /// /// Contains the string-ClearMode mapping. /// - public static readonly Dictionary ClearModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary ClearModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, ClearMode.None }, { CssKeywords.Left, ClearMode.Left }, @@ -566,7 +622,7 @@ static class Map /// /// Contains the string-BackgroundRepeat mapping. /// - public static readonly Dictionary BackgroundRepeats = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary BackgroundRepeats = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.NoRepeat, BackgroundRepeat.NoRepeat }, { CssKeywords.Repeat, BackgroundRepeat.Repeat }, @@ -577,7 +633,7 @@ static class Map /// /// Contains the string-BlendMode mapping. /// - public static readonly Dictionary BlendModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary BlendModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Color, BlendMode.Color }, { CssKeywords.ColorBurn, BlendMode.ColorBurn }, @@ -600,7 +656,7 @@ static class Map /// /// Contains the string-UpdateFrequency mapping. /// - public static readonly Dictionary UpdateFrequencies = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary UpdateFrequencies = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, UpdateFrequency.None }, { CssKeywords.Slow, UpdateFrequency.Slow }, @@ -610,7 +666,7 @@ static class Map /// /// Contains the string-ScriptingState mapping. /// - public static readonly Dictionary ScriptingStates = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary ScriptingStates = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, ScriptingState.None }, { CssKeywords.InitialOnly, ScriptingState.InitialOnly }, @@ -620,7 +676,7 @@ static class Map /// /// Contains the string-PointerAccuracy mapping. /// - public static readonly Dictionary PointerAccuracies = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary PointerAccuracies = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, PointerAccuracy.None }, { CssKeywords.Coarse, PointerAccuracy.Coarse }, @@ -630,7 +686,7 @@ static class Map /// /// Contains the string-HoverAbility mapping. /// - public static readonly Dictionary HoverAbilities = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary HoverAbilities = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, HoverAbility.None }, { CssKeywords.OnDemand, HoverAbility.OnDemand }, @@ -640,7 +696,7 @@ static class Map /// /// Contains the string-SizeMode mapping. /// - public static readonly Dictionary RadialGradientSizeModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary RadialGradientSizeModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.ClosestSide, CssRadialGradientValue.SizeMode.ClosestSide }, { CssKeywords.FarthestSide, CssRadialGradientValue.SizeMode.FarthestSide }, @@ -651,7 +707,7 @@ static class Map /// /// Contains the string-ObjectFitting mapping. /// - public static readonly Dictionary ObjectFittings = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary ObjectFittings = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, ObjectFitting.None }, { CssKeywords.Cover, ObjectFitting.Cover }, @@ -663,7 +719,7 @@ static class Map /// /// Contains the string-FontWeight mapping. /// - public static readonly Dictionary FontWeights = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary FontWeights = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Normal, FontWeight.Normal }, { CssKeywords.Bold, FontWeight.Bold }, @@ -674,7 +730,7 @@ static class Map /// /// Contains the string-SystemFont mapping. /// - public static readonly Dictionary SystemFonts = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary SystemFonts = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Caption, SystemFont.Caption }, { CssKeywords.Icon, SystemFont.Icon }, @@ -687,7 +743,7 @@ static class Map /// /// Contains the string-StrokeLinecap mapping. /// - public static readonly Dictionary StrokeLinecaps = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary StrokeLinecaps = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Butt, StrokeLinecap.Butt }, { CssKeywords.Round, StrokeLinecap.Round }, @@ -697,7 +753,7 @@ static class Map /// /// Contains the string-StrokeLinejoin mapping. /// - public static readonly Dictionary StrokeLinejoins = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary StrokeLinejoins = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Miter, StrokeLinejoin.Miter }, { CssKeywords.Round, StrokeLinejoin.Round }, @@ -707,7 +763,7 @@ static class Map /// /// Contains the string-WordBreak mapping. /// - public static readonly Dictionary WordBreaks = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary WordBreaks = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Normal, WordBreak.Normal }, { CssKeywords.BreakAll, WordBreak.BreakAll }, @@ -717,7 +773,7 @@ static class Map /// /// Contains the string-WordBreak mapping. /// - public static readonly Dictionary OverflowWraps = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary OverflowWraps = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Normal, OverflowWrap.Normal }, { CssKeywords.BreakWord, OverflowWrap.BreakWord }, @@ -726,7 +782,7 @@ static class Map /// /// Contains the string-ResizeMode mapping. /// - public static readonly Dictionary ResizeModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary ResizeModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, ResizeMode.None }, { CssKeywords.Both, ResizeMode.Both }, @@ -739,7 +795,7 @@ static class Map /// /// Contains the string-RubyAlignment mapping. /// - public static readonly Dictionary RubyAlignments = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary RubyAlignments = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Center, RubyAlignment.Center }, { CssKeywords.Start, RubyAlignment.Start }, @@ -750,7 +806,7 @@ static class Map /// /// Contains the string-RubyPosition mapping. /// - public static readonly Dictionary RubyPositions = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary RubyPositions = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.InterCharacter, RubyPosition.InterCharacter }, { CssKeywords.Over, RubyPosition.Over }, @@ -760,7 +816,7 @@ static class Map /// /// Contains the string-RubyOverhangMode mapping. /// - public static readonly Dictionary RubyOverhangModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary RubyOverhangModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Auto, RubyOverhangMode.Auto }, { CssKeywords.End, RubyOverhangMode.End }, @@ -771,7 +827,7 @@ static class Map /// /// Contains the string-PointerEvent mapping. /// - public static readonly Dictionary PointerEvents = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary PointerEvents = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, PointerEvent.None }, { CssKeywords.Auto, PointerEvent.Auto }, @@ -788,7 +844,7 @@ static class Map /// /// Contains the string-FlexDirection mapping. /// - public static readonly Dictionary FlexDirections = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary FlexDirections = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Column, FlexDirection.Column }, { CssKeywords.ColumnReverse, FlexDirection.ColumnReverse }, @@ -799,7 +855,7 @@ static class Map /// /// Contains the string-FlexWrapMode mapping. /// - public static readonly Dictionary FlexWrapModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary FlexWrapModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Nowrap, FlexWrapMode.NoWrap }, { CssKeywords.Wrap, FlexWrapMode.Wrap }, @@ -809,7 +865,7 @@ static class Map /// /// Contains the string-FlexContentMode mapping. /// - public static readonly Dictionary JustifyContentModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary JustifyContentModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.FlexStart, FlexContentMode.Start }, { CssKeywords.FlexEnd, FlexContentMode.End }, @@ -821,7 +877,7 @@ static class Map /// /// Contains the string-FlexContentMode mapping. /// - public static readonly Dictionary AlignContentModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary AlignContentModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.FlexStart, FlexContentMode.Start }, { CssKeywords.FlexEnd, FlexContentMode.End }, @@ -834,7 +890,7 @@ static class Map /// /// Contains the string-FlexContentMode mapping. /// - public static readonly Dictionary AlignItemsModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary AlignItemsModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.FlexStart, FlexContentMode.Start }, { CssKeywords.FlexEnd, FlexContentMode.End }, @@ -846,7 +902,7 @@ static class Map /// /// Contains the string-FlexContentMode mapping. /// - public static readonly Dictionary AlignSelfModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary AlignSelfModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Auto, FlexContentMode.Auto }, { CssKeywords.FlexStart, FlexContentMode.Start }, @@ -859,7 +915,7 @@ static class Map /// /// Contains the string-BookmarkState mapping. /// - public static readonly Dictionary BookmarkStates = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary BookmarkStates = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Open, BookmarkState.Open }, { CssKeywords.Closed, BookmarkState.Closed }, @@ -868,7 +924,7 @@ static class Map /// /// Contains the string-FootnotePolicy mapping. /// - public static readonly Dictionary FootnotePolicies = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary FootnotePolicies = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Auto, FootnotePolicy.Auto }, { CssKeywords.Block, FootnotePolicy.Block }, @@ -878,7 +934,7 @@ static class Map /// /// Contains the string-FootnoteDisplay mapping. /// - public static readonly Dictionary FootnoteDisplays = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary FootnoteDisplays = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Block, FootnoteDisplay.Block }, { CssKeywords.Compact, FootnoteDisplay.Compact }, @@ -888,7 +944,7 @@ static class Map /// /// Contains the string-length mapping. /// - public static readonly Dictionary Sizings = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary Sizings = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.FitContent, Sizing.FitContent }, { CssKeywords.MinContent, Sizing.MinContent}, @@ -898,7 +954,7 @@ static class Map /// /// Contains the string-ContentVisibility mapping. /// - public static readonly Dictionary ContentVisibilities = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary ContentVisibilities = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Visible, Visibility.Visible }, { CssKeywords.Hidden, Visibility.Hidden }, @@ -908,7 +964,7 @@ static class Map /// /// Contains the scroll-snap-axis mapping. /// - public static readonly Dictionary ScrollSnapAxises = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary ScrollSnapAxises = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.X, ScrollSnapAxis.X }, { CssKeywords.Y, ScrollSnapAxis.Y }, @@ -920,7 +976,7 @@ static class Map /// /// Contains the scroll-snap-strictness mapping. /// - public static readonly Dictionary ScrollSnapStrictnesses = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary ScrollSnapStrictnesses = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.Proximity, ScrollSnapStrictness.Proximity}, { CssKeywords.Mandatory, ScrollSnapStrictness.Mandatory }, @@ -929,7 +985,7 @@ static class Map /// /// Contains the scroll-snap-align mapping. /// - public static readonly Dictionary ScrollSnapAlignments = new Dictionary(StringComparer.OrdinalIgnoreCase) + public static readonly Dictionary ScrollSnapAlignments = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.None, ScrollSnapAlign.None }, { CssKeywords.Start, ScrollSnapAlign.Start}, diff --git a/src/AngleSharp.Css/Constants/PropertyNames.cs b/src/AngleSharp.Css/Constants/PropertyNames.cs index e08efff8..e5642e3d 100644 --- a/src/AngleSharp.Css/Constants/PropertyNames.cs +++ b/src/AngleSharp.Css/Constants/PropertyNames.cs @@ -802,6 +802,36 @@ public static class PropertyNames /// public static readonly String ListStyle = "list-style"; + /// + /// The margin-block declaration. + /// + public static readonly String MarginBlock = "margin-block"; + + /// + /// The margin-block-end declaration. + /// + public static readonly String MarginBlockEnd = "margin-block-end"; + + /// + /// The margin-block-start declaration. + /// + public static readonly String MarginBlockStart = "margin-block-start"; + + /// + /// The margin-inline declaration. + /// + public static readonly String MarginInline = "margin-inline"; + + /// + /// The margin-inline-end declaration. + /// + public static readonly String MarginInlineEnd = "margin-inline-end"; + + /// + /// The margin-inline-start declaration. + /// + public static readonly String MarginInlineStart = "margin-inline-start"; + /// /// The margin-right declaration. /// @@ -927,10 +957,40 @@ public static class PropertyNames /// public static readonly String OverflowWrap = "overflow-wrap"; - /// - /// The padding-top declaration. - /// - public static readonly String PaddingTop = "padding-top"; + /// + /// The padding-block declaration. + /// + public static readonly String PaddingBlock = "padding-block"; + + /// + /// The padding-block-end declaration. + /// + public static readonly String PaddingBlockEnd = "padding-block-end"; + + /// + /// The padding-block-start declaration. + /// + public static readonly String PaddingBlockStart = "padding-block-start"; + + /// + /// The padding-inline declaration. + /// + public static readonly String PaddingInline = "padding-inline"; + + /// + /// The padding-inline-end declaration. + /// + public static readonly String PaddingInlineEnd = "padding-inline-end"; + + /// + /// The padding-inline-start declaration. + /// + public static readonly String PaddingInlineStart = "padding-inline-start"; + + /// + /// The padding-top declaration. + /// + public static readonly String PaddingTop = "padding-top"; /// /// The padding-right declaration. diff --git a/src/AngleSharp.Css/Converters/CounterValueConverter.cs b/src/AngleSharp.Css/Converters/CounterValueConverter.cs index 90a98646..9fd221e8 100644 --- a/src/AngleSharp.Css/Converters/CounterValueConverter.cs +++ b/src/AngleSharp.Css/Converters/CounterValueConverter.cs @@ -9,18 +9,18 @@ namespace AngleSharp.Css.Converters sealed class CounterValueConverter : IValueConverter { - private static readonly CounterValue[] NoneValue = Array.Empty(); + private static readonly CssCounterValue[] NoneValue = Array.Empty(); - private readonly Int32 _defaultValue; + private readonly ICssValue _defaultValue; - public CounterValueConverter(Int32 defaultValue) + public CounterValueConverter(ICssValue defaultValue) { _defaultValue = defaultValue; } public ICssValue Convert(StringSource source) { - var counters = new List(); + var counters = new List(); if (!source.IsIdentifier(CssKeywords.None)) { @@ -36,13 +36,13 @@ public ICssValue Convert(StringSource source) return null; } - counters.Add(new CounterValue(name, value)); + counters.Add(new CssCounterValue(name, value)); } - return new CssTupleValue(counters.ToArray()); + return new CssTupleValue(counters.ToArray()); } - return new Constant(CssKeywords.None, NoneValue); + return new CssConstantValue(CssKeywords.None, NoneValue); } } } diff --git a/src/AngleSharp.Css/Converters/DictionaryValueConverter.cs b/src/AngleSharp.Css/Converters/DictionaryValueConverter.cs index ad7d1454..9d9713a6 100644 --- a/src/AngleSharp.Css/Converters/DictionaryValueConverter.cs +++ b/src/AngleSharp.Css/Converters/DictionaryValueConverter.cs @@ -24,7 +24,7 @@ public ICssValue Convert(StringSource source) if (ident != null && _values.TryGetValue(ident, out mode)) { - return new Constant(ident.ToLowerInvariant(), mode); + return new CssConstantValue(ident.ToLowerInvariant(), mode); } source.BackTo(pos); diff --git a/src/AngleSharp.Css/Converters/FlowRelativeValueConverter.cs b/src/AngleSharp.Css/Converters/FlowRelativeValueConverter.cs new file mode 100644 index 00000000..5994deeb --- /dev/null +++ b/src/AngleSharp.Css/Converters/FlowRelativeValueConverter.cs @@ -0,0 +1,44 @@ +namespace AngleSharp.Css.Converters +{ + using AngleSharp.Css.Dom; + using AngleSharp.Css.Parser; + using AngleSharp.Css.Values; + using AngleSharp.Text; + using System; + + sealed class FlowRelativeValueConverter : IValueConverter + { + private readonly IValueConverter _converter; + + public FlowRelativeValueConverter(IValueConverter converter) + { + _converter = converter; + } + + public ICssValue Convert(StringSource source) + { + var options = new ICssValue[2]; + var length = 0; + + for (var i = 0; i < options.Length; i++) + { + options[i] = _converter.Convert(source); + source.SkipSpacesAndComments(); + + if (options[length] != null) + { + length++; + } + } + + if (length > 0) + { + var values = new ICssValue[length]; + Array.Copy(options, values, length); + return new CssFlowRelativeValue(values); + } + + return null; + } + } +} diff --git a/src/AngleSharp.Css/Converters/IdentifierValueConverter.cs b/src/AngleSharp.Css/Converters/IdentifierValueConverter.cs index 72b3f537..6ddd8f26 100644 --- a/src/AngleSharp.Css/Converters/IdentifierValueConverter.cs +++ b/src/AngleSharp.Css/Converters/IdentifierValueConverter.cs @@ -21,7 +21,7 @@ public ICssValue Convert(StringSource source) if (result != null) { - return new Identifier(result); + return new CssIdentifierValue(result); } return null; @@ -43,7 +43,7 @@ public ICssValue Convert(StringSource source) { if (source.IsIdentifier(_identifier)) { - return new Constant(_identifier, _result); + return new CssConstantValue(_identifier, _result); } return null; diff --git a/src/AngleSharp.Css/Declarations/BackgroundDeclaration.cs b/src/AngleSharp.Css/Declarations/BackgroundDeclaration.cs index 52afec96..985f23a8 100644 --- a/src/AngleSharp.Css/Declarations/BackgroundDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BackgroundDeclaration.cs @@ -102,7 +102,7 @@ public ICssValue Convert(StringSource source) } var image = default(ICssImageValue); - var position = default(Point?); + var position = default(CssPoint2D?); var size = default(CssBackgroundSizeValue); var repeat = default(CssImageRepeatsValue); var attachment = default(ICssValue); diff --git a/src/AngleSharp.Css/Declarations/BackgroundPositionDeclaration.cs b/src/AngleSharp.Css/Declarations/BackgroundPositionDeclaration.cs index 3f344d29..31feacb6 100644 --- a/src/AngleSharp.Css/Declarations/BackgroundPositionDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BackgroundPositionDeclaration.cs @@ -47,7 +47,7 @@ public ICssValue Merge(ICssValue[] values) for (var i = 0; i < points.Length; i++) { - points[i] = new Point(x.Items[i], y.Items[i]); + points[i] = new CssPoint2D(x.Items[i], y.Items[i]); } return new CssListValue(points); @@ -60,7 +60,7 @@ public ICssValue[] Split(ICssValue value) { if (value is CssListValue list) { - var points = list.Items.OfType(); + var points = list.Items.OfType(); var x = points.Select(m => m.X).ToArray(); var y = points.Select(m => m.Y).ToArray(); return new ICssValue[] diff --git a/src/AngleSharp.Css/Declarations/BorderImageDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderImageDeclaration.cs index 750be019..4e6ee29b 100644 --- a/src/AngleSharp.Css/Declarations/BorderImageDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderImageDeclaration.cs @@ -146,7 +146,7 @@ public ICssValue[] Split(ICssValue value) img.Widths, }; } - else if (value is Constant constant) + else if (value is CssConstantValue constant) { return new ICssValue[] { diff --git a/src/AngleSharp.Css/Declarations/ContentDeclaration.cs b/src/AngleSharp.Css/Declarations/ContentDeclaration.cs index 89255772..b04a7d77 100644 --- a/src/AngleSharp.Css/Declarations/ContentDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/ContentDeclaration.cs @@ -8,6 +8,7 @@ namespace AngleSharp.Css.Declarations using AngleSharp.Text; using System; using System.Collections.Generic; + using System.Linq; static class ContentDeclaration { @@ -21,7 +22,7 @@ static class ContentDeclaration sealed class ContentValueConverter : IValueConverter { - private static readonly Dictionary ContentModes = new Dictionary(StringComparer.OrdinalIgnoreCase) + private static readonly Dictionary ContentModes = new(StringComparer.OrdinalIgnoreCase) { { CssKeywords.OpenQuote, new OpenQuoteContentMode() }, { CssKeywords.NoOpenQuote, new NoOpenQuoteContentMode() }, @@ -107,9 +108,9 @@ public ICssValue Convert(StringSource source) return null; } - private sealed class ContentValue : ICssValue + private sealed class ContentValue : ICssValue, IEquatable { - private ICssValue[] _modes; + private readonly ICssValue[] _modes; public ContentValue(ICssValue[] modes) { @@ -117,86 +118,109 @@ public ContentValue(ICssValue[] modes) } public String CssText => _modes.Length == 0 ? CssKeywords.None : _modes.Join(" "); + + public ICssValue Compute(ICssComputeContext context) + { + var modes = _modes.Select(mode => mode.Compute(context)).ToArray(); + return new ContentValue(modes); + } + public Boolean Equals(ContentValue other) + { + var l = _modes.Length; + + if (l == other._modes.Length) + { + for (var i = 0; i < l; i++) + { + var a = _modes[i]; + var b = other._modes[i]; + + if (!a.Equals(b)) + { + return false; + } + } + + return true; + } + + return false; + } + + Boolean IEquatable.Equals(ICssValue other) => other is ContentValue value && Equals(value); } - private interface IContentMode : ICssValue + private abstract class ContentMode : ICssValue { - String Stringify(IElement element); + public String CssText => GetCssText(); + + public abstract String Stringify(IElement element); + + public abstract String GetCssText(); + + ICssValue ICssValue.Compute(ICssComputeContext context) => this; + + public virtual Boolean Equals(ICssValue other) => Object.ReferenceEquals(this, other); } /// /// Computes to none for the :before and :after pseudo-elements. /// - private sealed class NormalContentMode : IContentMode + private sealed class NormalContentMode : ContentMode { - public String CssText => CssKeywords.Normal; + public override String GetCssText() => CssKeywords.Normal; - public String Stringify(IElement element) - { - return String.Empty; - } + public override String Stringify(IElement element) => String.Empty; } /// /// The value is replaced by the open quote string from the quotes /// property. /// - private sealed class OpenQuoteContentMode : IContentMode + private sealed class OpenQuoteContentMode : ContentMode { - public String CssText => CssKeywords.OpenQuote; + public override String GetCssText() => CssKeywords.OpenQuote; - public String Stringify(IElement element) - { - return String.Empty; - } + public override String Stringify(IElement element) => String.Empty; } /// /// The value is replaced by the close string from the quotes /// property. /// - private sealed class CloseQuoteContentMode : IContentMode + private sealed class CloseQuoteContentMode : ContentMode { - public String CssText => CssKeywords.CloseQuote; + public override String GetCssText() => CssKeywords.CloseQuote; - public String Stringify(IElement element) - { - return String.Empty; - } + public override String Stringify(IElement element) => String.Empty; } /// /// Introduces no content, but increments the level of nesting for /// quotes. /// - private sealed class NoOpenQuoteContentMode : IContentMode + private sealed class NoOpenQuoteContentMode : ContentMode { - public String CssText => CssKeywords.NoOpenQuote; + public override String GetCssText() => CssKeywords.NoOpenQuote; - public String Stringify(IElement element) - { - return String.Empty; - } + public override String Stringify(IElement element) => String.Empty; } /// /// Introduces no content, but decrements the level of nesting for /// quotes. /// - private sealed class NoCloseQuoteContentMode : IContentMode + private sealed class NoCloseQuoteContentMode : ContentMode { - public String CssText => CssKeywords.NoCloseQuote; + public override String GetCssText() => CssKeywords.NoCloseQuote; - public String Stringify(IElement element) - { - return String.Empty; - } + public override String Stringify(IElement element) => String.Empty; } /// /// Text content. /// - private sealed class TextContentMode : IContentMode + private sealed class TextContentMode : ContentMode { private readonly String _text; @@ -205,11 +229,18 @@ public TextContentMode(String text) _text = text; } - public String CssText => _text.CssString(); + public override String GetCssText() => _text.CssString(); - public String Stringify(IElement element) + public override String Stringify(IElement element) => _text; + + public override Boolean Equals(ICssValue other) { - return _text; + if (other is TextContentMode o) + { + return _text.Equals(o._text); + } + + return false; } } @@ -218,20 +249,27 @@ public String Stringify(IElement element) /// in scope at this pseudo-element, from outermost to innermost /// separated by the specified string. /// - private sealed class CounterContentMode : IContentMode + private sealed class CounterContentMode : ContentMode { - private readonly CounterDefinition _counter; + private readonly CssCounterDefinitionValue _counter; - public CounterContentMode(CounterDefinition counter) + public CounterContentMode(CssCounterDefinitionValue counter) { _counter = counter; } - public String CssText => _counter.CssText; + public override String GetCssText() => _counter.CssText; + + public override String Stringify(IElement element) => String.Empty; - public String Stringify(IElement element) + public override Boolean Equals(ICssValue other) { - return String.Empty; + if (other is CounterContentMode o) + { + return _counter.Equals(o._counter); + } + + return false; } } @@ -239,7 +277,7 @@ public String Stringify(IElement element) /// Returns the value of the element's attribute X as a string. If /// there is no attribute X, an empty string is returned. /// - private sealed class AttributeContentMode : IContentMode + private sealed class AttributeContentMode : ContentMode { private readonly String _attribute; @@ -248,11 +286,18 @@ public AttributeContentMode(String attribute) _attribute = attribute; } - public String CssText => FunctionNames.Attr.CssFunction(_attribute); + public override String GetCssText() => FunctionNames.Attr.CssFunction(_attribute); - public String Stringify(IElement element) + public override String Stringify(IElement element) => element.GetAttribute(_attribute) ?? String.Empty; + + public override Boolean Equals(ICssValue other) { - return element.GetAttribute(_attribute) ?? String.Empty; + if (other is AttributeContentMode o) + { + return _attribute.Equals(o._attribute); + } + + return false; } } @@ -261,7 +306,7 @@ public String Stringify(IElement element) /// image). If the resource or image can't be displayed, it is either /// ignored or some placeholder shows up. /// - private sealed class UrlContentMode : IContentMode + private sealed class UrlContentMode : ContentMode { private readonly CssUrlValue _url; @@ -270,11 +315,18 @@ public UrlContentMode(CssUrlValue url) _url = url; } - public String CssText => _url.CssText; + public override String GetCssText() => _url.CssText; + + public override String Stringify(IElement element) => String.Empty; - public String Stringify(IElement element) + public override Boolean Equals(ICssValue other) { - return String.Empty; + if (other is UrlContentMode o) + { + return _url.Equals(o._url); + } + + return false; } } } diff --git a/src/AngleSharp.Css/Declarations/CounterIncrementDeclaration.cs b/src/AngleSharp.Css/Declarations/CounterIncrementDeclaration.cs index 500db09d..91c81ad1 100644 --- a/src/AngleSharp.Css/Declarations/CounterIncrementDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/CounterIncrementDeclaration.cs @@ -2,13 +2,14 @@ namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; using System; static class CounterIncrementDeclaration { public static String Name = PropertyNames.CounterIncrement; - public static IValueConverter Converter = new CounterValueConverter(1); + public static IValueConverter Converter = new CounterValueConverter(CssIntegerValue.One); public static ICssValue InitialValue = InitialValues.CounterIncrementDecl; diff --git a/src/AngleSharp.Css/Declarations/CounterResetDeclaration.cs b/src/AngleSharp.Css/Declarations/CounterResetDeclaration.cs index 6dc518bb..29453431 100644 --- a/src/AngleSharp.Css/Declarations/CounterResetDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/CounterResetDeclaration.cs @@ -2,13 +2,14 @@ namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; using System; static class CounterResetDeclaration { public static String Name = PropertyNames.CounterReset; - public static IValueConverter Converter = new CounterValueConverter(0); + public static IValueConverter Converter = new CounterValueConverter(CssIntegerValue.Zero); public static ICssValue InitialValue = InitialValues.CounterResetDecl; diff --git a/src/AngleSharp.Css/Declarations/CursorDeclaration.cs b/src/AngleSharp.Css/Declarations/CursorDeclaration.cs index d16867cc..3ef60676 100644 --- a/src/AngleSharp.Css/Declarations/CursorDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/CursorDeclaration.cs @@ -21,39 +21,36 @@ sealed class CursorValueConverter : IValueConverter { public ICssValue Convert(StringSource source) { - var cursor = default(ICssValue); var definitions = new List(); while (!source.IsDone) { var imageSource = source.ParseImageSource(); - var c = source.SkipSpacesAndComments(); + source.SkipSpacesAndComments(); if (imageSource != null) { var x = source.ParseNumber(); - c = source.SkipSpacesAndComments(); + source.SkipSpacesAndComments(); var y = source.ParseNumber(); - c = source.SkipSpacesAndComments(); + var c = source.SkipSpacesAndComments(); if (x.HasValue != y.HasValue || c != Symbols.Comma) break; source.SkipCurrentAndSpaces(); - var position = default(Point?); + var position = default(CssPoint2D?); if (x.HasValue) { - var xp = new Length(x.Value, Length.Unit.None); - var yp = new Length(y.Value, Length.Unit.None); - position = new Point(xp, yp); + position = new CssPoint2D(x, y); } definitions.Add(new CssCustomCursorValue(imageSource, position)); } else { - cursor = source.ParseConstant(Map.SystemCursors); + var cursor = source.ParseConstant(Map.SystemCursors); if (cursor != null) { diff --git a/src/AngleSharp.Css/Declarations/FontDeclaration.cs b/src/AngleSharp.Css/Declarations/FontDeclaration.cs index 42f8eb8d..887aa8db 100644 --- a/src/AngleSharp.Css/Declarations/FontDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/FontDeclaration.cs @@ -121,7 +121,7 @@ public ICssValue Merge(ICssValue[] values) var style = values[5]; var height = values[6]; - if (families != null && size != null || families is Constant) + if (families != null && size != null || families is CssConstantValue) { return new CssFontValue(style, variant, weight, stretch, size, height, families); } @@ -134,7 +134,7 @@ public ICssValue[] Split(ICssValue value) if (!(value is CssFontValue font)) { - if (!(value is Constant systemFont)) + if (!(value is CssConstantValue systemFont)) { return null; } diff --git a/src/AngleSharp.Css/Declarations/GapDeclaration.cs b/src/AngleSharp.Css/Declarations/GapDeclaration.cs index 1512f71c..3fcd63b3 100644 --- a/src/AngleSharp.Css/Declarations/GapDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GapDeclaration.cs @@ -30,8 +30,8 @@ sealed class GapAggregagtor : IValueAggregator, IValueConverter public ICssValue Merge(ICssValue[] values) { - var col = values[0]; - var row = values[1]; + var row = values[0]; + var col = values[1]; if (row != null || col != null) { diff --git a/src/AngleSharp.Css/Declarations/GridAreaDeclaration.cs b/src/AngleSharp.Css/Declarations/GridAreaDeclaration.cs index 59b875b7..32b4e55c 100644 --- a/src/AngleSharp.Css/Declarations/GridAreaDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridAreaDeclaration.cs @@ -37,7 +37,16 @@ sealed class GridAreaAggregator : IValueAggregator, IValueConverter public ICssValue Convert(StringSource source) => converter.Convert(source); - public ICssValue Merge(ICssValue[] values) => new CssTupleValue(values, seperator); + public ICssValue Merge(ICssValue[] values) + { + // Make single value if all resolve to the same text + if (values.Length == 4 && values[0]?.CssText == values[1]?.CssText && values[0]?.CssText == values[2]?.CssText && values[0]?.CssText == values[3]?.CssText) + { + return values[0]; + } + + return new CssTupleValue(values, seperator); + } public ICssValue[] Split(ICssValue value) { @@ -63,7 +72,7 @@ private static ICssValue GetItem(CssTupleValue tuple, Int32 index) { if (value > MaximumGridSize) { - return new Constant(MaximumGridSize.ToString(), null); + return new CssConstantValue(MaximumGridSize.ToString(), null); } } return tuple.Items[index]; @@ -75,12 +84,13 @@ private static ICssValue GetItem(CssTupleValue tuple, Int32 index) private static ICssValue GetItemSimple(CssTupleValue tuple, Int32 index) { var val = UnitParser.ParseUnit(new StringSource(tuple.Items[0].CssText)); + if (index <= 2) { if (tuple.Items.Length <= index) { - if (!int.TryParse(tuple.Items[0].CssText, out int _)) + if (!Int32.TryParse(tuple.Items[0].CssText, out var _)) { return tuple.Items[0]; } @@ -90,18 +100,18 @@ private static ICssValue GetItemSimple(CssTupleValue tuple, Int32 index) { if (tuple.Items.Length > 1) { - if (!int.TryParse(tuple.Items[1].CssText, out int _)) + if (!Int32.TryParse(tuple.Items[1].CssText, out var _)) { return tuple.Items[1]; } } - else if (!int.TryParse(tuple.Items[0].CssText, out int _)) + else if (!Int32.TryParse(tuple.Items[0].CssText, out var _)) { return tuple.Items[0]; } } - return new Constant(CssKeywords.Auto, null); + return new CssConstantValue(CssKeywords.Auto, null); } } } diff --git a/src/AngleSharp.Css/Declarations/GridColumnDeclaration.cs b/src/AngleSharp.Css/Declarations/GridColumnDeclaration.cs index e531bd32..43cb2820 100644 --- a/src/AngleSharp.Css/Declarations/GridColumnDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridColumnDeclaration.cs @@ -64,7 +64,7 @@ private static ICssValue GetItem(CssTupleValue tuple, Int32 index) } } - return new Constant(CssKeywords.Auto, null); + return new CssConstantValue(CssKeywords.Auto, null); } } } diff --git a/src/AngleSharp.Css/Declarations/GridDeclaration.cs b/src/AngleSharp.Css/Declarations/GridDeclaration.cs index d5053f4f..d4f4e031 100644 --- a/src/AngleSharp.Css/Declarations/GridDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridDeclaration.cs @@ -6,6 +6,7 @@ namespace AngleSharp.Css.Declarations using AngleSharp.Text; using System; using System.Collections.Generic; + using System.Linq; static class GridDeclaration { @@ -37,11 +38,11 @@ public ICssValue Convert(StringSource source) { var template = source.ParseGridTemplate(); - if (template == null) + if (template is null) { var rows = source.ParseTrackList() ?? source.ParseAutoTrackList(); - if (rows != null) + if (rows is not null) { if (source.SkipSpacesAndComments() == Symbols.Solidus) { @@ -153,46 +154,46 @@ public ICssValue[] Split(ICssValue value) gt.TemplateRows, gt.TemplateColumns, gt.TemplateAreas, - null, - null, - null, - null, - null, - null, - null, + null, //new Identifier(CssKeywords.Auto), + null, //new Identifier(CssKeywords.Auto), + null, //new Identifier(CssKeywords.Row), + null, //Length.Zero, + null, //Length.Zero, + null, //new Identifier(CssKeywords.Normal), + null, //new Identifier(CssKeywords.Normal), }; } else if (value is CssGridValue grid) { - var dense = grid.Rows != null ? CssKeywords.Row : CssKeywords.Column; + var dense = grid.Rows is not null ? CssKeywords.Row : CssKeywords.Column; return new[] { grid.Rows, grid.Columns, - null, - grid.Columns != null ? new CssTupleValue(grid.Sizes) : null, - grid.Rows != null ? new CssTupleValue(grid.Sizes) : null, - grid.IsDense ? new Identifier(dense) as ICssValue : null, - null, - null, - null, - null, + null, //new Identifier(CssKeywords.None), + grid.Columns is not null ? new CssTupleValue(grid.Sizes) : null, //new Identifier(CssKeywords.Auto), + grid.Rows is not null ? new CssTupleValue(grid.Sizes) : null, //new Identifier(CssKeywords.Auto), + grid.IsDense ? new CssIdentifierValue(dense) : null, + null, //Length.Zero, + null, //Length.Zero, + null, //new Identifier(CssKeywords.Normal), + null, //new Identifier(CssKeywords.Normal), }; } - else if (value is Identifier) + else if (value is CssIdentifierValue) { return new[] { value, value, value, - null, - null, - null, - null, - null, - null, - null, + null, //new Identifier(CssKeywords.Auto), + null, //new Identifier(CssKeywords.Auto), + null, //new Identifier(CssKeywords.Row), + null, //Length.Zero, + null, //Length.Zero, + null, //new Identifier(CssKeywords.Normal), + null, //new Identifier(CssKeywords.Normal), }; } diff --git a/src/AngleSharp.Css/Declarations/GridGapDeclaration.cs b/src/AngleSharp.Css/Declarations/GridGapDeclaration.cs index fc0a9b41..f7178963 100644 --- a/src/AngleSharp.Css/Declarations/GridGapDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridGapDeclaration.cs @@ -30,8 +30,8 @@ sealed class GridGapAggregagtor : IValueAggregator, IValueConverter public ICssValue Merge(ICssValue[] values) { - var col = values[0]; - var row = values[1]; + var row = values[0]; + var col = values[1]; if (row != null || col != null) { diff --git a/src/AngleSharp.Css/Declarations/GridRowDeclaration.cs b/src/AngleSharp.Css/Declarations/GridRowDeclaration.cs index 9f8bfdee..e4f8b150 100644 --- a/src/AngleSharp.Css/Declarations/GridRowDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridRowDeclaration.cs @@ -70,7 +70,7 @@ private static ICssValue GetItem(CssTupleValue tuple, Int32 index) } } - return new Constant(CssKeywords.Auto, null); + return new CssConstantValue(CssKeywords.Auto, null); } } } diff --git a/src/AngleSharp.Css/Declarations/GridTemplateAreasDeclaration.cs b/src/AngleSharp.Css/Declarations/GridTemplateAreasDeclaration.cs index 151372a1..9772062f 100644 --- a/src/AngleSharp.Css/Declarations/GridTemplateAreasDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridTemplateAreasDeclaration.cs @@ -11,6 +11,7 @@ static class GridTemplateAreasDeclaration public static readonly String[] Shorthands = new[] { + PropertyNames.Grid, PropertyNames.GridTemplate, }; diff --git a/src/AngleSharp.Css/Declarations/GridTemplateColumnsDeclaration.cs b/src/AngleSharp.Css/Declarations/GridTemplateColumnsDeclaration.cs index 85ac0130..3f49b08e 100644 --- a/src/AngleSharp.Css/Declarations/GridTemplateColumnsDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridTemplateColumnsDeclaration.cs @@ -10,6 +10,7 @@ static class GridTemplateColumnsDeclaration public static readonly String[] Shorthands = new[] { + PropertyNames.Grid, PropertyNames.GridTemplate, }; diff --git a/src/AngleSharp.Css/Declarations/GridTemplateDeclaration.cs b/src/AngleSharp.Css/Declarations/GridTemplateDeclaration.cs index fe704a70..cab6cc83 100644 --- a/src/AngleSharp.Css/Declarations/GridTemplateDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridTemplateDeclaration.cs @@ -67,7 +67,7 @@ public ICssValue[] Split(ICssValue value) { return new[] { template.TemplateRows, template.TemplateColumns, template.TemplateAreas }; } - else if (value is Identifier) + else if (value is CssIdentifierValue) { return new[] { value, value, value }; } diff --git a/src/AngleSharp.Css/Declarations/MarginBlockDeclaration.cs b/src/AngleSharp.Css/Declarations/MarginBlockDeclaration.cs new file mode 100644 index 00000000..a8762a3c --- /dev/null +++ b/src/AngleSharp.Css/Declarations/MarginBlockDeclaration.cs @@ -0,0 +1,56 @@ +namespace AngleSharp.Css.Declarations +{ + using AngleSharp.Css.Converters; + using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; + using AngleSharp.Text; + using System; + using static ValueConverters; + + static class MarginBlockDeclaration + { + public static String Name = PropertyNames.MarginBlock; + + public static IValueConverter Converter = new MarginBlockAggregator(); + + public static ICssValue InitialValue = null; + + public static PropertyFlags Flags = PropertyFlags.Shorthand; + + public static String[] Longhands = new[] + { + PropertyNames.MarginBlockStart, + PropertyNames.MarginBlockEnd, + }; + + sealed class MarginBlockAggregator : IValueAggregator, IValueConverter + { + private static readonly IValueConverter converter = Or(AutoLengthOrPercentConverter, AssignInitial(CssLengthValue.Zero)).FlowRelative(); + + public ICssValue Convert(StringSource source) => converter.Convert(source); + + public ICssValue Merge(ICssValue[] values) + { + var start = values[0]; + var end = values[1]; + + if (start != null && end != null) + { + return new CssFlowRelativeValue(new[] { start, end }); + } + + return null; + } + + public ICssValue[] Split(ICssValue value) + { + if (value is CssFlowRelativeValue flowRelative) + { + return new[] { flowRelative.Start, flowRelative.End }; + } + + return null; + } + } + } +} diff --git a/src/AngleSharp.Css/Declarations/MarginBlockEndDeclaration.cs b/src/AngleSharp.Css/Declarations/MarginBlockEndDeclaration.cs new file mode 100644 index 00000000..fe51f054 --- /dev/null +++ b/src/AngleSharp.Css/Declarations/MarginBlockEndDeclaration.cs @@ -0,0 +1,22 @@ +namespace AngleSharp.Css.Declarations +{ + using System; + using Dom; + using static ValueConverters; + + static class MarginBlockEndDeclaration + { + public static String Name = PropertyNames.MarginBlockEnd; + + public static String[] Shorthands = new[] + { + PropertyNames.MarginBlock, + }; + + public static IValueConverter Converter = AutoLengthOrPercentConverter; + + public static ICssValue InitialValue = InitialValues.MarginBlockEndDecl; + + public static PropertyFlags Flags = PropertyFlags.Unitless | PropertyFlags.Animatable; + } +} diff --git a/src/AngleSharp.Css/Declarations/MarginBlockStartDeclaration.cs b/src/AngleSharp.Css/Declarations/MarginBlockStartDeclaration.cs new file mode 100644 index 00000000..968a30bd --- /dev/null +++ b/src/AngleSharp.Css/Declarations/MarginBlockStartDeclaration.cs @@ -0,0 +1,22 @@ +namespace AngleSharp.Css.Declarations +{ + using System; + using Dom; + using static ValueConverters; + + static class MarginBlockStartDeclaration + { + public static String Name = PropertyNames.MarginBlockStart; + + public static String[] Shorthands = new[] + { + PropertyNames.MarginBlock, + }; + + public static IValueConverter Converter = AutoLengthOrPercentConverter; + + public static ICssValue InitialValue = InitialValues.MarginBlockStartDecl; + + public static PropertyFlags Flags = PropertyFlags.Unitless | PropertyFlags.Animatable; + } +} \ No newline at end of file diff --git a/src/AngleSharp.Css/Declarations/MarginDeclaration.cs b/src/AngleSharp.Css/Declarations/MarginDeclaration.cs index dd0d9fca..3443c684 100644 --- a/src/AngleSharp.Css/Declarations/MarginDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/MarginDeclaration.cs @@ -27,7 +27,7 @@ static class MarginDeclaration sealed class MarginAggregator : IValueAggregator, IValueConverter { - private static readonly IValueConverter converter = Or(AutoLengthOrPercentConverter, AssignInitial(Length.Zero)).Periodic(); + private static readonly IValueConverter converter = Or(AutoLengthOrPercentConverter, AssignInitial(CssLengthValue.Zero)).Periodic(); public ICssValue Convert(StringSource source) => converter.Convert(source); diff --git a/src/AngleSharp.Css/Declarations/MarginInlineDeclaration.cs b/src/AngleSharp.Css/Declarations/MarginInlineDeclaration.cs new file mode 100644 index 00000000..bb6c87f4 --- /dev/null +++ b/src/AngleSharp.Css/Declarations/MarginInlineDeclaration.cs @@ -0,0 +1,56 @@ +namespace AngleSharp.Css.Declarations +{ + using AngleSharp.Css.Converters; + using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; + using AngleSharp.Text; + using System; + using static ValueConverters; + + static class MarginInlineDeclaration + { + public static String Name = PropertyNames.MarginInline; + + public static IValueConverter Converter = new MarginInlineAggregator(); + + public static ICssValue InitialValue = null; + + public static PropertyFlags Flags = PropertyFlags.Shorthand; + + public static String[] Longhands = new[] + { + PropertyNames.MarginInlineStart, + PropertyNames.MarginInlineEnd, + }; + + sealed class MarginInlineAggregator : IValueAggregator, IValueConverter + { + private static readonly IValueConverter converter = Or(AutoLengthOrPercentConverter, AssignInitial(CssLengthValue.Zero)).FlowRelative(); + + public ICssValue Convert(StringSource source) => converter.Convert(source); + + public ICssValue Merge(ICssValue[] values) + { + var start = values[0]; + var end = values[1]; + + if (start != null && end != null) + { + return new CssFlowRelativeValue(new[] { start, end }); + } + + return null; + } + + public ICssValue[] Split(ICssValue value) + { + if (value is CssFlowRelativeValue flowRelative) + { + return new[] { flowRelative.Start, flowRelative.End }; + } + + return null; + } + } + } +} diff --git a/src/AngleSharp.Css/Declarations/MarginInlineEndDeclaration.cs b/src/AngleSharp.Css/Declarations/MarginInlineEndDeclaration.cs new file mode 100644 index 00000000..978b08c2 --- /dev/null +++ b/src/AngleSharp.Css/Declarations/MarginInlineEndDeclaration.cs @@ -0,0 +1,22 @@ +namespace AngleSharp.Css.Declarations +{ + using Dom; + using System; + using static ValueConverters; + + static class MarginInlineEndDeclaration + { + public static String Name = PropertyNames.MarginInlineEnd; + + public static String[] Shorthands = new[] + { + PropertyNames.MarginInline, + }; + + public static IValueConverter Converter = AutoLengthOrPercentConverter; + + public static ICssValue InitialValue = InitialValues.MarginInlineEndDecl; + + public static PropertyFlags Flags = PropertyFlags.Unitless | PropertyFlags.Animatable; + } +} diff --git a/src/AngleSharp.Css/Declarations/MarginInlineStartDeclaration.cs b/src/AngleSharp.Css/Declarations/MarginInlineStartDeclaration.cs new file mode 100644 index 00000000..5216e7a4 --- /dev/null +++ b/src/AngleSharp.Css/Declarations/MarginInlineStartDeclaration.cs @@ -0,0 +1,22 @@ +namespace AngleSharp.Css.Declarations +{ + using System; + using Dom; + using static ValueConverters; + + static class MarginInlineStartDeclaration + { + public static String Name = PropertyNames.MarginInlineStart; + + public static String[] Shorthands = new[] + { + PropertyNames.MarginInline, + }; + + public static IValueConverter Converter = AutoLengthOrPercentConverter; + + public static ICssValue InitialValue = InitialValues.MarginInlineStartDecl; + + public static PropertyFlags Flags = PropertyFlags.Unitless | PropertyFlags.Animatable; + } +} \ No newline at end of file diff --git a/src/AngleSharp.Css/Declarations/PaddingBlockDeclaration.cs b/src/AngleSharp.Css/Declarations/PaddingBlockDeclaration.cs new file mode 100644 index 00000000..a1631840 --- /dev/null +++ b/src/AngleSharp.Css/Declarations/PaddingBlockDeclaration.cs @@ -0,0 +1,56 @@ +namespace AngleSharp.Css.Declarations +{ + using AngleSharp.Css.Converters; + using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; + using AngleSharp.Text; + using System; + using static ValueConverters; + + static class PaddingBlockDeclaration + { + public static String Name = PropertyNames.PaddingBlock; + + public static IValueConverter Converter = new PaddingBlockAggregator(); + + public static ICssValue InitialValue = null; + + public static PropertyFlags Flags = PropertyFlags.Shorthand; + + public static String[] Longhands = new[] + { + PropertyNames.PaddingBlockStart, + PropertyNames.PaddingBlockEnd, + }; + + sealed class PaddingBlockAggregator : IValueAggregator, IValueConverter + { + private static readonly IValueConverter converter = Or(AutoLengthOrPercentConverter, AssignInitial(CssLengthValue.Zero)).FlowRelative(); + + public ICssValue Convert(StringSource source) => converter.Convert(source); + + public ICssValue Merge(ICssValue[] values) + { + var start = values[0]; + var end = values[1]; + + if (start != null && end != null) + { + return new CssFlowRelativeValue(new[] { start, end }); + } + + return null; + } + + public ICssValue[] Split(ICssValue value) + { + if (value is CssFlowRelativeValue flowRelative) + { + return new[] { flowRelative.Start, flowRelative.End }; + } + + return null; + } + } + } +} diff --git a/src/AngleSharp.Css/Declarations/PaddingBlockEndDeclaration.cs b/src/AngleSharp.Css/Declarations/PaddingBlockEndDeclaration.cs new file mode 100644 index 00000000..5c631b13 --- /dev/null +++ b/src/AngleSharp.Css/Declarations/PaddingBlockEndDeclaration.cs @@ -0,0 +1,22 @@ +namespace AngleSharp.Css.Declarations +{ + using System; + using Dom; + using static ValueConverters; + + static class PaddingBlockEndDeclaration + { + public static String Name = PropertyNames.PaddingBlockEnd; + + public static String[] Shorthands = new[] + { + PropertyNames.PaddingBlock, + }; + + public static IValueConverter Converter = AutoLengthOrPercentConverter; + + public static ICssValue InitialValue = InitialValues.PaddingBlockEndDecl; + + public static PropertyFlags Flags = PropertyFlags.Unitless | PropertyFlags.Animatable; + } +} diff --git a/src/AngleSharp.Css/Declarations/PaddingBlockStartDeclaration.cs b/src/AngleSharp.Css/Declarations/PaddingBlockStartDeclaration.cs new file mode 100644 index 00000000..492d5e5b --- /dev/null +++ b/src/AngleSharp.Css/Declarations/PaddingBlockStartDeclaration.cs @@ -0,0 +1,22 @@ +namespace AngleSharp.Css.Declarations +{ + using System; + using Dom; + using static ValueConverters; + + static class PaddingBlockStartDeclaration + { + public static String Name = PropertyNames.PaddingBlockStart; + + public static String[] Shorthands = new[] + { + PropertyNames.PaddingBlock, + }; + + public static IValueConverter Converter = AutoLengthOrPercentConverter; + + public static ICssValue InitialValue = InitialValues.PaddingBlockStartDecl; + + public static PropertyFlags Flags = PropertyFlags.Unitless | PropertyFlags.Animatable; + } +} \ No newline at end of file diff --git a/src/AngleSharp.Css/Declarations/PaddingDeclaration.cs b/src/AngleSharp.Css/Declarations/PaddingDeclaration.cs index 6f846a3c..c2f70691 100644 --- a/src/AngleSharp.Css/Declarations/PaddingDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/PaddingDeclaration.cs @@ -27,7 +27,7 @@ static class PaddingDeclaration sealed class PaddingAggregator : IValueAggregator, IValueConverter { - private static readonly IValueConverter converter = Or(LengthOrPercentConverter, AssignInitial(Length.Zero)).Periodic(); + private static readonly IValueConverter converter = Or(LengthOrPercentConverter, AssignInitial(CssLengthValue.Zero)).Periodic(); public ICssValue Convert(StringSource source) => converter.Convert(source); diff --git a/src/AngleSharp.Css/Declarations/PaddingInlineDeclaration.cs b/src/AngleSharp.Css/Declarations/PaddingInlineDeclaration.cs new file mode 100644 index 00000000..4d0a1867 --- /dev/null +++ b/src/AngleSharp.Css/Declarations/PaddingInlineDeclaration.cs @@ -0,0 +1,56 @@ +namespace AngleSharp.Css.Declarations +{ + using AngleSharp.Css.Converters; + using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; + using AngleSharp.Text; + using System; + using static ValueConverters; + + static class PaddingInlineDeclaration + { + public static String Name = PropertyNames.PaddingInline; + + public static IValueConverter Converter = new PaddingInlineAggregator(); + + public static ICssValue InitialValue = null; + + public static PropertyFlags Flags = PropertyFlags.Shorthand; + + public static String[] Longhands = new[] + { + PropertyNames.PaddingInlineStart, + PropertyNames.PaddingInlineEnd, + }; + + sealed class PaddingInlineAggregator : IValueAggregator, IValueConverter + { + private static readonly IValueConverter converter = Or(AutoLengthOrPercentConverter, AssignInitial(CssLengthValue.Zero)).FlowRelative(); + + public ICssValue Convert(StringSource source) => converter.Convert(source); + + public ICssValue Merge(ICssValue[] values) + { + var start = values[0]; + var end = values[1]; + + if (start != null && end != null) + { + return new CssFlowRelativeValue(new[] { start, end }); + } + + return null; + } + + public ICssValue[] Split(ICssValue value) + { + if (value is CssFlowRelativeValue flowRelative) + { + return new[] { flowRelative.Start, flowRelative.End }; + } + + return null; + } + } + } +} \ No newline at end of file diff --git a/src/AngleSharp.Css/Declarations/PaddingInlineEndDeclaration.cs b/src/AngleSharp.Css/Declarations/PaddingInlineEndDeclaration.cs new file mode 100644 index 00000000..ebf6002d --- /dev/null +++ b/src/AngleSharp.Css/Declarations/PaddingInlineEndDeclaration.cs @@ -0,0 +1,22 @@ +namespace AngleSharp.Css.Declarations +{ + using System; + using Dom; + using static ValueConverters; + + static class PaddingInlineEndDeclaration + { + public static String Name = PropertyNames.PaddingInlineEnd; + + public static String[] Shorthands = new[] + { + PropertyNames.PaddingInline, + }; + + public static IValueConverter Converter = AutoLengthOrPercentConverter; + + public static ICssValue InitialValue = InitialValues.PaddingInlineEndDecl; + + public static PropertyFlags Flags = PropertyFlags.Unitless | PropertyFlags.Animatable; + } +} diff --git a/src/AngleSharp.Css/Declarations/PaddingInlineStartDeclaration.cs b/src/AngleSharp.Css/Declarations/PaddingInlineStartDeclaration.cs new file mode 100644 index 00000000..43eb90bd --- /dev/null +++ b/src/AngleSharp.Css/Declarations/PaddingInlineStartDeclaration.cs @@ -0,0 +1,22 @@ +namespace AngleSharp.Css.Declarations +{ + using System; + using Dom; + using static ValueConverters; + + static class PaddingInlineStartDeclaration + { + public static String Name = PropertyNames.PaddingInlineStart; + + public static String[] Shorthands = new[] + { + PropertyNames.PaddingInline, + }; + + public static IValueConverter Converter = AutoLengthOrPercentConverter; + + public static ICssValue InitialValue = InitialValues.PaddingInlineStartDecl; + + public static PropertyFlags Flags = PropertyFlags.Unitless | PropertyFlags.Animatable; + } +} \ No newline at end of file diff --git a/src/AngleSharp.Css/Declarations/ScrollSnapTypeDeclaration.cs b/src/AngleSharp.Css/Declarations/ScrollSnapTypeDeclaration.cs index 38c9f32d..a33c5c34 100644 --- a/src/AngleSharp.Css/Declarations/ScrollSnapTypeDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/ScrollSnapTypeDeclaration.cs @@ -12,7 +12,7 @@ namespace AngleSharp.Css.Declarations /// static class ScrollSnapTypeDeclaration { - private static readonly ICssValue defaultStrictness = new Constant(CssKeywords.Proximity, ScrollSnapStrictness.Proximity); + private static readonly ICssValue defaultStrictness = new CssConstantValue(CssKeywords.Proximity, ScrollSnapStrictness.Proximity); public static String Name = PropertyNames.ScrollSnapType; diff --git a/src/AngleSharp.Css/Dom/ElementCssInlineStyleExtensions.cs b/src/AngleSharp.Css/Dom/ElementCssInlineStyleExtensions.cs index a6cab36f..80da8dae 100644 --- a/src/AngleSharp.Css/Dom/ElementCssInlineStyleExtensions.cs +++ b/src/AngleSharp.Css/Dom/ElementCssInlineStyleExtensions.cs @@ -14,7 +14,7 @@ namespace AngleSharp.Css.Dom [DomExposed("SVGElement")] public static class ElementCssInlineStyleExtensions { - private static readonly ConditionalWeakTable _styles = new ConditionalWeakTable(); + private static readonly ConditionalWeakTable _styles = new(); /// /// Gets the style declaration of an element. diff --git a/src/AngleSharp.Css/Dom/ICssProperty.cs b/src/AngleSharp.Css/Dom/ICssProperty.cs index 0a91b87f..4ae8f2fb 100644 --- a/src/AngleSharp.Css/Dom/ICssProperty.cs +++ b/src/AngleSharp.Css/Dom/ICssProperty.cs @@ -1,6 +1,7 @@ namespace AngleSharp.Css.Dom { using AngleSharp.Attributes; + using AngleSharp.Css.Values; using System; /// @@ -57,5 +58,12 @@ public interface ICssProperty : IStyleFormattable /// Gets if the property is a shorthand. /// Boolean IsShorthand { get; } + + /// + /// Creates a computed version of the property. + /// + /// The context to compute for. + /// The computed version of the property if uncomputed, otherwise the same. + ICssProperty Compute(ICssComputeContext context); } } diff --git a/src/AngleSharp.Css/Dom/ICssStyleRule.cs b/src/AngleSharp.Css/Dom/ICssStyleRule.cs index 4747ad24..ed3efe65 100644 --- a/src/AngleSharp.Css/Dom/ICssStyleRule.cs +++ b/src/AngleSharp.Css/Dom/ICssStyleRule.cs @@ -1,6 +1,7 @@ -namespace AngleSharp.Css.Dom +namespace AngleSharp.Css.Dom { using AngleSharp.Attributes; + using AngleSharp.Dom; using System; /// @@ -26,5 +27,17 @@ public interface ICssStyleRule : ICssRule /// Gets the selector for matching elements. /// ISelector Selector { get; } + + /// + /// Gets the selector for matching elements. + /// + Boolean TryMatch(IElement element, IElement? scope, out Priority specificity); + + /// + /// Gets a CSSRuleList of the CSS rules in the style sheet. + /// + [DomName("cssRules")] + [DomName("rules")] + ICssRuleList Rules { get; } } } diff --git a/src/AngleSharp.Css/Dom/ICssValue.cs b/src/AngleSharp.Css/Dom/ICssValue.cs index faad35b6..a56cde7a 100644 --- a/src/AngleSharp.Css/Dom/ICssValue.cs +++ b/src/AngleSharp.Css/Dom/ICssValue.cs @@ -1,15 +1,23 @@ namespace AngleSharp.Css.Dom { + using AngleSharp.Css.Values; using System; /// /// Represents a value of a CSS property. /// - public interface ICssValue + public interface ICssValue : IEquatable { /// /// The text representation of the value. /// String CssText { get; } + + /// + /// Computes the current value using the given context. + /// + /// The used compute context. + /// The computed value or the original value, if already computed. + ICssValue Compute(ICssComputeContext context); } } diff --git a/src/AngleSharp.Css/Dom/Internal/CssMedium.cs b/src/AngleSharp.Css/Dom/Internal/CssMedium.cs index 8e50b2d7..67c744d6 100644 --- a/src/AngleSharp.Css/Dom/Internal/CssMedium.cs +++ b/src/AngleSharp.Css/Dom/Internal/CssMedium.cs @@ -18,6 +18,7 @@ public sealed class CssMedium : ICssMedium private readonly String _type; private readonly Boolean _exclusive; private readonly Boolean _inverse; + private readonly String _connector; #endregion @@ -30,7 +31,7 @@ public sealed class CssMedium : ICssMedium /// Specifies if it should be inverted. /// Specifies if the rule is exclusive. public CssMedium(String type, Boolean inverse, Boolean exclusive) - : this(type, inverse, exclusive, Enumerable.Empty()) + : this(type, inverse, exclusive, Enumerable.Empty(), "and") { } @@ -41,12 +42,14 @@ public CssMedium(String type, Boolean inverse, Boolean exclusive) /// Specifies if it should be inverted. /// Specifies if the rule is exclusive. /// The features of the medium. - public CssMedium(String type, Boolean inverse, Boolean exclusive, IEnumerable features) + /// The connector of the features ("and" or "or"). + public CssMedium(String type, Boolean inverse, Boolean exclusive, IEnumerable features, String connector) { _features = new List(features); _type = type; _inverse = inverse; _exclusive = exclusive; + _connector = connector; } #endregion @@ -63,6 +66,11 @@ public CssMedium(String type, Boolean inverse, Boolean exclusive, IEnumerable public String Type => _type; + /// + /// Gets the connector of the contained features. + /// + public String Connector => _connector; + /// /// Gets if the medium is exclusive to other media. /// @@ -76,7 +84,7 @@ public CssMedium(String type, Boolean inverse, Boolean exclusive, IEnumerable /// Gets the constraints - i.e., the stringified features. /// - 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 @@ public override Boolean Equals(Object obj) { 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 @@ public void ToCss(TextWriter writer, IStyleFormatter formatter) for (var i = offset; i < _features.Count; i++) { - writer.Write(" and "); + writer.Write(' '); + writer.Write(_connector); + writer.Write(' '); _features[i].ToCss(writer, formatter); } } diff --git a/src/AngleSharp.Css/Dom/Internal/CssProperty.cs b/src/AngleSharp.Css/Dom/Internal/CssProperty.cs index f4fa91e1..16c6b05e 100644 --- a/src/AngleSharp.Css/Dom/Internal/CssProperty.cs +++ b/src/AngleSharp.Css/Dom/Internal/CssProperty.cs @@ -2,6 +2,7 @@ namespace AngleSharp.Css.Dom { using AngleSharp.Css; using AngleSharp.Css.Converters; + using AngleSharp.Css.Values; using AngleSharp.Text; using System; using System.IO; @@ -27,7 +28,7 @@ internal class CssProperty : ICssProperty internal CssProperty(String name, IValueConverter converter, PropertyFlags flags = PropertyFlags.None, ICssValue value = null, Boolean important = false) { - _name = name.ToLowerInvariant(); + _name = name.StartsWith("--") ? name : name.ToLowerInvariant(); _converter = converter; _flags = flags; _value = value; @@ -52,6 +53,8 @@ public String Value public Boolean HasValue => _value != null; + public PropertyFlags Flags => _flags; + public Boolean IsInherited => (((_flags & PropertyFlags.Inherited) == PropertyFlags.Inherited) && IsInitial) || (HasValue && _value.CssText.Is(CssKeywords.Inherit)); public Boolean CanBeInherited => (_flags & PropertyFlags.Inherited) == PropertyFlags.Inherited; @@ -84,6 +87,47 @@ public Boolean IsImportant #endregion + #region Methods + + public ICssProperty Compute(ICssComputeContext context) + { + var propertyContext = new PropertyComputeContext(context, _converter); + var computedValue = _value?.Compute(propertyContext); + + if (computedValue != _value) + { + return new CssProperty(_name, _converter, _flags, computedValue, _important); + } + + return this; + } + + #endregion + + #region Compute Context + + sealed class PropertyComputeContext : ICssComputeContext + { + private readonly ICssComputeContext _parent; + private readonly IValueConverter _converter; + + public PropertyComputeContext(ICssComputeContext parent, IValueConverter converter) + { + _parent = parent; + _converter = converter; + } + + public IRenderDevice Device => _parent.Device; + + public IBrowsingContext Context => _parent.Context; + + public IValueConverter Converter => _converter; + + public ICssValue Resolve(String name) =>_parent.Resolve(name); + } + + #endregion + #region String Representation public void ToCss(TextWriter writer, IStyleFormatter formatter) => writer.Write(formatter.Declaration(CssUtilities.Escape(Name), Value, IsImportant)); diff --git a/src/AngleSharp.Css/Dom/Internal/CssStyleDeclaration.cs b/src/AngleSharp.Css/Dom/Internal/CssStyleDeclaration.cs index 05c51c0b..11f937ee 100644 --- a/src/AngleSharp.Css/Dom/Internal/CssStyleDeclaration.cs +++ b/src/AngleSharp.Css/Dom/Internal/CssStyleDeclaration.cs @@ -134,7 +134,7 @@ private ICssProperty TryCreateShorthand(String shorthandName, IEnumerable m.Name == name).FirstOrDefault(); - if (property != null) + if (property?.Value is not null) { usedProperties.Add(name); count = count + 1; @@ -175,7 +175,7 @@ public String ToCssBlock(IStyleFormatter formatter) var usedProperties = new List(); var shorthand = TryCreateShorthand(shorthandName, serialized, usedProperties, false); - if (shorthand != null) + if (shorthand is not null) { list.Add(shorthand); @@ -278,17 +278,17 @@ public void SetProperty(String propertyName, String propertyValue, String priori if (!String.IsNullOrEmpty(propertyValue)) { - if (priority == null || priority.Isi(CssKeywords.Important)) + if (priority is null || priority.Isi(CssKeywords.Important)) { var property = CreateProperty(propertyName); - if (property != null) + if (property is not null) { property.Value = propertyValue; - if (property.RawValue != null) + if (property.RawValue is not null) { - property.IsImportant = priority != null; + property.IsImportant = priority is not null; SetProperty(property); RaiseChanged(); } @@ -301,6 +301,16 @@ public void SetProperty(String propertyName, String propertyValue, String priori } } + public void AddProperty(ICssProperty declaration) + { + _declarations.Add(declaration); + } + + public void RemoveProperty(ICssProperty declaration) + { + _declarations.Remove(declaration); + } + #endregion #region Internal Methods @@ -318,8 +328,18 @@ internal void UpdateDeclarations(IEnumerable decls) => private ICssProperty GetPropertyShorthand(String name) => TryCreateShorthand(name, Enumerable.Empty(), new List(), true); - private ICssProperty CreateProperty(String propertyName) => - GetProperty(propertyName) ?? _context.CreateProperty(propertyName); + private ICssProperty CreateProperty(String propertyName) + { + var newProperty = _context.CreateProperty(propertyName); + var existing = GetProperty(propertyName); + + if (existing is not null) + { + newProperty.RawValue = existing.RawValue; + } + + return newProperty; + } private void SetProperty(ICssProperty property) { @@ -357,6 +377,11 @@ private void RemovePropertyByName(String propertyName) private void ChangeDeclarations(IEnumerable decls, Predicate defaultSkip, Func removeExisting) { + if (decls is null) + { + return; + } + var declarations = new List(); foreach (var newdecl in decls) @@ -411,7 +436,7 @@ private void SetShorthand(ICssProperty shorthand) { var properties = _context.CreateLonghands(shorthand); - if (properties != null) + if (properties is not null) { foreach (var property in properties) { diff --git a/src/AngleSharp.Css/Dom/Internal/KeyframeSelector.cs b/src/AngleSharp.Css/Dom/Internal/KeyframeSelector.cs index 14ed2e7f..d2792cec 100644 --- a/src/AngleSharp.Css/Dom/Internal/KeyframeSelector.cs +++ b/src/AngleSharp.Css/Dom/Internal/KeyframeSelector.cs @@ -56,7 +56,7 @@ public void ToCss(TextWriter writer, IStyleFormatter formatter) private static void Write(TextWriter writer, Double value) { - var pc = Math.Truncate(100.0 * value); + var pc = 100.0 * value; var str = pc.ToString(CultureInfo.InvariantCulture); writer.Write(str); writer.Write(Symbols.Percent); diff --git a/src/AngleSharp.Css/Dom/Internal/MediaFeature.cs b/src/AngleSharp.Css/Dom/Internal/MediaFeature.cs index ed97d792..11c1dd02 100644 --- a/src/AngleSharp.Css/Dom/Internal/MediaFeature.cs +++ b/src/AngleSharp.Css/Dom/Internal/MediaFeature.cs @@ -1,5 +1,6 @@ -namespace AngleSharp.Css.Dom +namespace AngleSharp.Css.Dom { + using AngleSharp.Css.Values; using AngleSharp.Text; using System; using System.IO; @@ -13,32 +14,48 @@ sealed class MediaFeature : IMediaFeature private readonly Boolean _min; private readonly Boolean _max; - private readonly String _name; - private readonly String _value; + private readonly ICssValue _name; + private readonly ICssValue _value; + private readonly String _op; #endregion #region ctor - internal MediaFeature(String name, String value) + internal MediaFeature(String name) + : this(name, null) + {} + + internal MediaFeature(String name, ICssValue value) { - _name = name; + _name = new CssAnyValue(name); _value = value; _min = name.StartsWith("min-"); _max = name.StartsWith("max-"); } + internal MediaFeature(String name, ICssValue value, String op) + : this(new CssAnyValue(name), value, op) + {} + + internal MediaFeature(ICssValue name, ICssValue value, String op) + { + _name = name; + _value = value; + _op = op; + } + #endregion #region Properties - public String Name => _name; + public String Name => _name?.CssText ?? String.Empty; public Boolean IsMinimum => _min; public Boolean IsMaximum => _max; - public String Value => _value; + public String Value => _value?.CssText ?? String.Empty; public Boolean HasValue => _value != null; @@ -49,12 +66,19 @@ internal MediaFeature(String name, String value) public void ToCss(TextWriter writer, IStyleFormatter formatter) { writer.Write(Symbols.RoundBracketOpen); - writer.Write(_name); + writer.Write(Name); - if (_value != null) + if (_op is not null) + { + writer.Write(" "); + writer.Write(_op); + writer.Write(" "); + writer.Write(Value); + } + else if (_value is not null) { writer.Write(": "); - writer.Write(_value); + writer.Write(Value); } writer.Write(Symbols.RoundBracketClose); diff --git a/src/AngleSharp.Css/Dom/Internal/MediaList.cs b/src/AngleSharp.Css/Dom/Internal/MediaList.cs index 215b4716..c34bfe3e 100644 --- a/src/AngleSharp.Css/Dom/Internal/MediaList.cs +++ b/src/AngleSharp.Css/Dom/Internal/MediaList.cs @@ -6,6 +6,7 @@ namespace AngleSharp.Css.Dom using System.Collections; using System.Collections.Generic; using System.IO; + using System.Linq; /// /// Represents a list of media elements. @@ -17,6 +18,8 @@ sealed class MediaList : IMediaList private readonly IBrowsingContext _context; private readonly List _media; + private static readonly CssMedium replacementMedium = new(CssKeywords.All, inverse: true, exclusive: false); + #endregion #region ctor @@ -56,21 +59,15 @@ public String MediaText public void SetMediaText(String value, Boolean throwOnError) { _media.Clear(); - var media = MediaParser.Parse(value ?? CssKeywords.All, ValidatorFactory); + var v = String.IsNullOrEmpty(value) ? String.Empty : value; + var media = MediaParser.Parse(v, ValidatorFactory) ?? Enumerable.Repeat(null, 1); - if (media != null) - { - _media.AddRange(media); - } - else if (throwOnError) + if (throwOnError && media.Contains(null)) { throw new DomException(DomError.Syntax); } - if (_media.Count == 0) - { - _media.Add(new CssMedium(CssKeywords.All, inverse: true, exclusive: false)); - } + _media.AddRange(media.Select(m => m ?? replacementMedium)); } public void Add(String newMedium) diff --git a/src/AngleSharp.Css/Dom/Internal/PseudoElement.cs b/src/AngleSharp.Css/Dom/Internal/PseudoElement.cs index ac376292..00dcd530 100644 --- a/src/AngleSharp.Css/Dom/Internal/PseudoElement.cs +++ b/src/AngleSharp.Css/Dom/Internal/PseudoElement.cs @@ -53,6 +53,8 @@ public String Slot public ITokenList ClassList => _host.ClassList; + public String GivenNamespaceUri => _host.GivenNamespaceUri; + public String ClassName { get => _host.ClassName; 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/CssFontFaceRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssFontFaceRule.cs index a463aa78..809644ff 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssFontFaceRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssFontFaceRule.cs @@ -13,7 +13,7 @@ sealed class CssFontFaceRule : CssDeclarationRule, ICssFontFaceRule { #region Fields - private static readonly HashSet ContainedProperties = new HashSet(StringComparer.OrdinalIgnoreCase) + private static readonly HashSet ContainedProperties = new(StringComparer.OrdinalIgnoreCase) { PropertyNames.FontFamily, PropertyNames.Src, diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssPageRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssPageRule.cs index c422d079..9a116c6f 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssPageRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssPageRule.cs @@ -60,7 +60,7 @@ protected override void ReplaceWith(ICssRule rule) public override void ToCss(TextWriter writer, IStyleFormatter formatter) { - var rules = formatter.BlockRules(_style); + var rules = _style.ToCssBlock(formatter); writer.Write(formatter.Rule(RuleNames.Page, SelectorText, rules)); } diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs index af5bf90d..b9b9a135 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs @@ -3,19 +3,24 @@ namespace AngleSharp.Css.Dom using AngleSharp.Css; using AngleSharp.Dom; using System; + using System.Collections.Generic; using System.Diagnostics; using System.IO; + using System.Linq; /// /// Represents a CSS style rule. /// [DebuggerDisplay(null, Name = "CssStyleRule ({SelectorText})")] - sealed class CssStyleRule : CssRule, ICssStyleRule + sealed class CssStyleRule : CssRule, ICssStyleRule, ISelectorVisitor { #region Fields private readonly CssStyleDeclaration _style; + private readonly CssRuleList _rules; private ISelector _selector; + private IEnumerable _selectorList; + private Boolean _nested; #endregion @@ -25,28 +30,54 @@ internal CssStyleRule(ICssStyleSheet owner) : base(owner, CssRuleType.Style) { _style = new CssStyleDeclaration(this); + _rules = new CssRuleList(); + _selectorList = null; } #endregion #region Properties - public ISelector Selector => _selector; + public Boolean IsNested + { + get => _nested; + set => _nested = value; + } + + public ISelector Selector + { + get => _selector; + private set => ChangeSelector(value); + } public String SelectorText { get => _selector?.Text; - set => _selector = ParseSelector(value); + set => ChangeSelector(ParseSelector(value)); } ICssStyleDeclaration ICssStyleRule.Style => _style; public CssStyleDeclaration Style => _style; + public ICssRuleList Rules => _rules; + #endregion #region Methods + public void Add(ICssRule rule) + { + _rules.Add(rule); + rule.SetParent(this); + } + + public void Remove(ICssRule rule) + { + _rules.Remove(rule); + rule.SetParent(null); + } + internal void SetInvalidSelector(String selectorText) { _selector = new InvalidSelector(selectorText); @@ -55,10 +86,57 @@ internal void SetInvalidSelector(String selectorText) protected override void ReplaceWith(ICssRule rule) { var newRule = (ICssStyleRule)rule; - _selector = newRule.Selector; + ChangeSelector(newRule.Selector); _style.SetDeclarations(newRule.Style); } + 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; + return true; + } + } + } + + if (_selector is not null && _selector.Match(element, scope)) + { + specificity += _selector.Specificity; + return true; + } + + return false; + } + public override void ToCss(TextWriter writer, IStyleFormatter formatter) { var block = _style.ToCssBlock(formatter); @@ -69,7 +147,55 @@ public override void ToCss(TextWriter writer, IStyleFormatter formatter) #region Selector - class InvalidSelector : ISelector + internal void ChangeSelector(ISelector value) + { + _selectorList = null; + _selector = value; + value?.Accept(this); + } + + void ISelectorVisitor.Attribute(string name, string op, string value) + { + } + + void ISelectorVisitor.Type(string name) + { + } + + void ISelectorVisitor.Id(string value) + { + } + + void ISelectorVisitor.Child(string name, int step, int offset, ISelector selector) + { + } + + void ISelectorVisitor.Class(string name) + { + } + + void ISelectorVisitor.PseudoClass(string name) + { + } + + void ISelectorVisitor.PseudoElement(string name) + { + } + + void ISelectorVisitor.List(IEnumerable selectors) + { + _selectorList = selectors; + } + + void ISelectorVisitor.Combinator(IEnumerable selectors, IEnumerable symbols) + { + } + + void ISelectorVisitor.Many(IEnumerable selectors) + { + } + + sealed class InvalidSelector : ISelector { private readonly String _text; diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssViewportRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssViewportRule.cs index 7718d748..e7d272ae 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssViewportRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssViewportRule.cs @@ -9,7 +9,7 @@ namespace AngleSharp.Css.Dom /// sealed class CssViewportRule : CssDeclarationRule { - private static readonly HashSet ContainedProperties = new HashSet(StringComparer.OrdinalIgnoreCase) + private static readonly HashSet ContainedProperties = new(StringComparer.OrdinalIgnoreCase) { PropertyNames.MinWidth, PropertyNames.MaxWidth, diff --git a/src/AngleSharp.Css/Dom/ListStyle.cs b/src/AngleSharp.Css/Dom/ListStyle.cs index fcd81273..66bccf17 100644 --- a/src/AngleSharp.Css/Dom/ListStyle.cs +++ b/src/AngleSharp.Css/Dom/ListStyle.cs @@ -1,4 +1,4 @@ -namespace AngleSharp.Css.Dom +namespace AngleSharp.Css.Dom { /// /// An enumeration over possible list styles. @@ -56,6 +56,162 @@ public enum ListStyle : byte /// /// Traditional Georgian numbering, E.g.an, ban, gan, ... he, tan, in… /// - Georgian + Georgian, + /// + /// Kannada numbering. + /// + Kannada, + /// + /// Traditional Chinese formal numbering. + /// + TradChineseFormal, + /// + /// Traditional Chinese informal numbering. + /// + TradChineseInformal, + /// + /// Han decimal numbers. + /// + CjkDecimal, + /// + /// Arabic-Indic numbers. + /// + ArabicIndic, + /// + /// Bengali numbering. + /// + Bengali, + /// + /// Cambodian/Khmer numbering. + /// + Cambodian, + /// + /// Han "Earthly Branch" ordinals. + /// + CjkEarthlyBranch, + /// + /// Han "Heavenly Stem" ordinals. + /// + CjkHeavenlyStem, + /// + /// Devanagari numbering. + /// + Devanagari, + /// + /// Ethiopic numbering. + /// + EthiopicNumeric, + /// + /// Gurmukhi numbering. + /// + Gurmukhi, + /// + /// Traditional Hebrew numbering. + /// + Hebrew, + /// + /// Gujarati numbering. + /// + Gujarati, + /// + /// Dictionary-order hiragana lettering. + /// + Hiragana, + /// + /// Iroha-order hiragana lettering. + /// + IrohaHiragana, + /// + /// Japanese informal numbering. + /// + JapaneseInformal, + /// + /// Japanese formal numbering to be used in legal or financial documents. The kanjis are designed so that they can't be modified to look like another correct one. + /// + JapaneseFormal, + /// + /// Iroha-order katakana lettering. + /// + KatakanaIroha, + /// + /// Dictionary-order katakana lettering. + /// + Katakana, + /// + /// Korean hangul numbering. + /// + KoreanHangulFormal, + /// + /// Formal Korean Han numbering. + /// + KoreanHanjaFormal, + /// + /// Korean hanja numbering. + /// + KoreanHanjaInformal, + /// + /// Laotian numbering. + /// + Lao, + /// + /// Uppercase Armenian numbering. + /// + UpperArmenian, + /// + /// Lowercase Armenian numbering. + /// + LowerArmenian, + /// + /// Malayalam numbering. + /// + Malayalam, + /// + /// Mongolian numbering. + /// + Mongolian, + /// + /// Myanmar (Burmese) numbering. + /// + Myanmar, + /// + /// Oriya numbering. + /// + Oriya, + /// + /// Persian numbering. + /// + Persian, + /// + /// Simplified Chinese formal numbering. + /// + SimpChineseFormal, + /// + /// Simplified Chinese informal numbering. + /// + SimpChineseInformal, + /// + /// Tamil numbering. + /// + Tamil, + /// + /// Telugu numbering. + /// + Telugu, + /// + /// Thai numbering. + /// + Thai, + /// + /// Tibetan numbering. + /// + Tibetan, + /// + /// Symbol indicating that a disclosure widget, like <details> is closed. + /// + DisclosureClosed, + /// + /// Symbol indicating that a disclosure widget such as <details> is opened. + /// + DisclosureOpen, } } diff --git a/src/AngleSharp.Css/Dom/SymbolsType.cs b/src/AngleSharp.Css/Dom/SymbolsType.cs new file mode 100644 index 00000000..7be06392 --- /dev/null +++ b/src/AngleSharp.Css/Dom/SymbolsType.cs @@ -0,0 +1,29 @@ +namespace AngleSharp.Css.Dom +{ + /// + /// An enumeration with all possible symbol types. + /// + public enum SymbolsType : byte + { + /// + /// The system cycles through the given values in the order of their definition, and returns to the start when it reaches the end. + /// + Cyclic, + /// + /// The system interprets the given values as the successive units of a place-value numbering system. + /// + Numeric, + /// + /// The system interprets the given values as the digits of an alphabetic numbering system, like a place-value numbering system but without 0. + /// + Alphabetic, + /// + /// The system cycles through the values, printing them an additional time at each cycle (one time for the first cycle, two times for the second, etc.). + /// + Symbolic, + /// + /// The system cycles through the given values once, then falls back to Arabic numerals. + /// + Fixed, + } +} diff --git a/src/AngleSharp.Css/Dom/TextAlign.cs b/src/AngleSharp.Css/Dom/TextAlign.cs new file mode 100644 index 00000000..e2bb8d2c --- /dev/null +++ b/src/AngleSharp.Css/Dom/TextAlign.cs @@ -0,0 +1,44 @@ +namespace AngleSharp.Css.Dom +{ + /// + /// An enumeration with all possible text alignments. + /// + public enum TextAlign: byte + { + /// + /// The inline contents are aligned to the left edge of the line box. + /// This is the default value for table data. + /// + Left, + /// + /// The inline contents are centered within the line box. This is + /// the default value for table headers. + /// + Center, + /// + /// The inline contents are aligned to the right edge of the line box. + /// + Right, + /// + /// The text is justified. Text should line up their left and right + /// edges to the left and right content edges of the paragraph. + /// + Justify, + /// + /// The same as left if direction is left-to-right and right if direction is right-to-left. + /// + Start, + /// + /// The same as right if direction is left-to-right and left if direction is right-to-left. + /// + End, + /// + /// Same as justify, but also forces the last line to be justified. + /// + JustifyAll, + /// + /// Similar to inherit, but the values start and end are calculated according to the parent's direction and are replaced by the appropriate left or right value. + /// + MatchParent, + } +} \ No newline at end of file diff --git a/src/AngleSharp.Css/Extensions/CssOmExtensions.cs b/src/AngleSharp.Css/Extensions/CssOmExtensions.cs index e9fce611..2867ec33 100644 --- a/src/AngleSharp.Css/Extensions/CssOmExtensions.cs +++ b/src/AngleSharp.Css/Extensions/CssOmExtensions.cs @@ -1,6 +1,8 @@ namespace AngleSharp.Css.Dom { using AngleSharp.Css.Parser; + using AngleSharp.Css.Values; + using AngleSharp.Dom; using AngleSharp.Text; using System; using System.Linq; @@ -10,6 +12,18 @@ namespace AngleSharp.Css.Dom /// public static class CssOmExtensions { + /// + /// Gets the computed style of the element. + /// + /// The element to compute the style for. + /// The optional pseudo selector to use. + /// The computed style of the element. + public static ICssStyleDeclaration ComputeStyle(this IElement element, String pseudo = null) + { + var window = element?.Owner?.DefaultView; + return window?.GetComputedStyle(element, pseudo); + } + /// /// Gets the style rule with the provided selector text. /// @@ -66,15 +80,21 @@ public static ICssValue GetValueOf(this ICssStyleRule rule, String propertyName) } /// - /// Computes the values with knowledge of the device. + /// Computes the declarations using the given compute context. /// /// The base (raw) style. - /// The device to use for the calculation. + /// The context to use for the calculation. /// A new style declaration with the existing or computed values. - public static ICssStyleDeclaration Compute(this ICssStyleDeclaration style, IRenderDevice device) + public static ICssStyleDeclaration Compute(this ICssStyleDeclaration style, ICssComputeContext context) { - //TODO - return style; + var computedStyle = new CssStyleDeclaration(context.Context); + + foreach (var property in style) + { + computedStyle.AddProperty(property.Compute(context)); + } + + return computedStyle; } } } diff --git a/src/AngleSharp.Css/Extensions/CssValueExtensions.cs b/src/AngleSharp.Css/Extensions/CssValueExtensions.cs index ae021904..58638781 100644 --- a/src/AngleSharp.Css/Extensions/CssValueExtensions.cs +++ b/src/AngleSharp.Css/Extensions/CssValueExtensions.cs @@ -16,14 +16,42 @@ public static class CssValueExtensions /// The resulting number. public static Double AsDouble(this ICssValue value) { - if (value is Length length && length.Type == Length.Unit.None) + if (value is null) { - return length.Value; + return 0.0; } - else if (value is Fraction fr) + else if (value is CssLengthValue l && l.Type == CssLengthValue.Unit.None) + { + return l.Value; + } + else if (value is CssTimeValue t && t.Type == CssTimeValue.Unit.None) + { + return t.Value; + } + else if (value is CssFrequencyValue f && f.Type == CssFrequencyValue.Unit.None) + { + return f.Value; + } + else if (value is CssNumberValue n) + { + return n.Value; + } + else if (value is CssIntegerValue i) + { + return i.Value; + } + else if (value is CssPercentageValue p) + { + return p.Value; + } + else if (value is CssFractionValue fr) { return fr.Value; } + else if (value is CssRatioValue r) + { + return r.Top.AsDouble() / r.Bottom.AsDouble(); + } else if (value is ICssMultipleValue multiple && multiple.Count == 1) { return multiple[0].AsDouble(); @@ -45,7 +73,11 @@ public static Double AsDouble(this ICssValue value) /// The resulting number. public static Double AsPx(this ICssValue value, IRenderDimensions renderDimensions, RenderMode mode) { - if (value is Length length && length.Type != Length.Unit.None) + if (value is null) + { + return 0.0; + } + else if (value is CssLengthValue length && length.Type != CssLengthValue.Unit.None) { return length.ToPixel(renderDimensions, mode); } @@ -68,7 +100,11 @@ public static Double AsPx(this ICssValue value, IRenderDimensions renderDimensio /// The resulting number. public static Double AsMs(this ICssValue value) { - if (value is Time time && time.Type != Time.Unit.None) + if (value is null) + { + return 0.0; + } + else if (value is CssTimeValue time && time.Type != CssTimeValue.Unit.None) { return time.ToMilliseconds(); } @@ -91,7 +127,11 @@ public static Double AsMs(this ICssValue value) /// The resulting number. public static Double AsHz(this ICssValue value) { - if (value is Frequency freq && freq.Type != Frequency.Unit.None) + if (value is null) + { + return 0.0; + } + else if (value is CssFrequencyValue freq && freq.Type != CssFrequencyValue.Unit.None) { return freq.ToHertz(); } @@ -114,9 +154,13 @@ public static Double AsHz(this ICssValue value) /// The resulting number. public static Double AsDeg(this ICssValue value) { - if (value is Angle angle && angle.Type != Angle.Unit.None) + if (value is null) + { + return 0.0; + } + else if (value is CssAngleValue angle && angle.Type != CssAngleValue.Unit.None) { - return angle.Value; + return angle.ToDegree(); } else if (value is ICssMultipleValue multiple && multiple.Count == 1) { @@ -130,6 +174,33 @@ public static Double AsDeg(this ICssValue value) return 0.0; } + /// + /// Tries to convert the value to a number of degrees. + /// + /// The value to convert. + /// The resulting number. + public static Double AsRad(this ICssValue value) + { + if (value is null) + { + return 0.0; + } + else if (value is CssAngleValue angle && angle.Type != CssAngleValue.Unit.None) + { + return angle.ToRadian(); + } + else if (value is ICssMultipleValue multiple && multiple.Count == 1) + { + return multiple[0].AsRad(); + } + else if (value is ICssSpecialValue special && special.Value != null) + { + return special.Value.AsRad(); + } + + return 0.0; + } + /// /// Tries to convert the value to a number of dots per inch. /// @@ -137,7 +208,11 @@ public static Double AsDeg(this ICssValue value) /// The resulting number. public static Double AsDpi(this ICssValue value) { - if (value is Resolution res && res.Type != Resolution.Unit.None) + if (value is null) + { + return 0.0; + } + else if (value is CssResolutionValue res && res.Type != CssResolutionValue.Unit.None) { return res.ToDotsPerPixel(); } @@ -160,7 +235,7 @@ public static Double AsDpi(this ICssValue value) /// The resulting number. public static Int32 AsRgba(this ICssValue value) { - if (value is Color res) + if (value is CssColorValue res) { return res.Value; } @@ -173,7 +248,7 @@ public static Int32 AsRgba(this ICssValue value) return special.Value.AsRgba(); } - return Color.Black.Value; + return CssColorValue.Black.Value; } /// @@ -245,7 +320,7 @@ public static TransformMatrix AsMatrix(this ICssValue value, IRenderDimensions r public static T AsEnum(this ICssValue value) where T : struct, IComparable { - if (value is Constant constant) + if (value is CssConstantValue constant) { return constant.Value; } @@ -269,11 +344,11 @@ public static T AsEnum(this ICssValue value) /// True if the keyword was matched, false otherwise. public static Boolean Is(this ICssValue value, String keyword) { - if (value is Identifier ident && ident.Value.Isi(keyword)) + if (value is CssIdentifierValue ident && ident.Value.Isi(keyword)) { return true; } - else if (value?.GetType() == typeof(Constant<>) && value.CssText.Isi(keyword)) + else if (value?.GetType() == typeof(CssConstantValue<>) && value.CssText.Isi(keyword)) { return true; } diff --git a/src/AngleSharp.Css/Extensions/ElementExtensions.cs b/src/AngleSharp.Css/Extensions/ElementExtensions.cs index 7e687f21..cc257851 100644 --- a/src/AngleSharp.Css/Extensions/ElementExtensions.cs +++ b/src/AngleSharp.Css/Extensions/ElementExtensions.cs @@ -163,7 +163,7 @@ private static void ItcInCssBox(ICssStyleDeclaration elementStyle, ICssStyleDecl { var elementHidden = new Nullable(); - if (elementStyle != null) + if (elementStyle is not null) { if (!String.IsNullOrEmpty(elementStyle.GetDisplay())) { @@ -196,13 +196,13 @@ private static void ItcInCssBox(ICssStyleDeclaration elementStyle, ICssStyleDecl var lastLine = node.NextSibling is null || String.IsNullOrEmpty(node.NextSibling.TextContent) || node.NextSibling is IHtmlBreakRowElement; - ProcessText(textElement.Data, sb, parentStyle, lastLine); + ProcessText(textElement.Data, sb, parentStyle, lastLine, requiredLineBreakCounts); } else if (node is IHtmlBreakRowElement) { sb.Append(Symbols.LineFeed); } - else if (elementStyle != null && ((node is IHtmlTableCellElement && String.IsNullOrEmpty(elementStyle.GetDisplay())) || elementStyle.GetDisplay() == CssKeywords.TableCell)) + else if (elementStyle is not null && ((node is IHtmlTableCellElement && String.IsNullOrEmpty(elementStyle.GetDisplay())) || elementStyle.GetDisplay() == CssKeywords.TableCell)) { if (node.NextSibling is IElement nextSibling) { @@ -214,7 +214,7 @@ private static void ItcInCssBox(ICssStyleDeclaration elementStyle, ICssStyleDecl } } } - else if (elementStyle != null && ((node is IHtmlTableRowElement && String.IsNullOrEmpty(elementStyle.GetDisplay())) || elementStyle.GetDisplay() == CssKeywords.TableRow)) + else if (elementStyle is not null && ((node is IHtmlTableRowElement && String.IsNullOrEmpty(elementStyle.GetDisplay())) || elementStyle.GetDisplay() == CssKeywords.TableRow)) { if (node.NextSibling is IElement nextSibling) { @@ -243,7 +243,7 @@ private static void ItcInCssBox(ICssStyleDeclaration elementStyle, ICssStyleDecl } } - if (elementStyle != null) + if (elementStyle is not null) { if (IsBlockLevelDisplay(elementStyle.GetDisplay())) { @@ -297,105 +297,42 @@ public static IEnumerable SetStyle(this IEnumerable false, + _ => true, + }; } private static Boolean IsBlockLevelDisplay(String display) { // https://www.w3.org/TR/css-display-3/#display-value-summary // https://hg.mozilla.org/mozilla-central/file/0acceb224b7d/servo/components/layout/query.rs#l1016 - switch (display) + return display switch { - case "block": - case "flow-root": - case "flex": - case "grid": - case "table": - case "table-caption": - return true; - default: - return false; - } + "block" or "flow-root" or "flex" or "grid" or "table" or "table-caption" => true, + _ => false, + }; } private static Boolean IsBlockLevel(INode node) { // https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements - switch (node.NodeName) + return node.NodeName switch { - case "ADDRESS": - case "ARTICLE": - case "ASIDE": - case "BLOCKQUOTE": - case "CANVAS": - case "DD": - case "DIV": - case "DL": - case "DT": - case "FIELDSET": - case "FIGCAPTION": - case "FIGURE": - case "FOOTER": - case "FORM": - case "H1": - case "H2": - case "H3": - case "H4": - case "H5": - case "H6": - case "HEADER": - case "GROUP": - case "HR": - case "LI": - case "MAIN": - case "NAV": - case "NOSCRIPT": - case "OL": - case "OPTION": - case "OUTPUT": - case "P": - case "PRE": - case "SECTION": - case "TABLE": - case "TFOOT": - case "UL": - case "VIDEO": - return true; - default: - return false; - } + "ADDRESS" or "ARTICLE" or "ASIDE" or "BLOCKQUOTE" or "CANVAS" or "DD" or "DIV" or "DL" or "DT" or "FIELDSET" or "FIGCAPTION" or "FIGURE" or "FOOTER" or "FORM" or "H1" or "H2" or "H3" or "H4" or "H5" or "H6" or "HEADER" or "GROUP" or "HR" or "LI" or "MAIN" or "NAV" or "NOSCRIPT" or "OL" or "OPTION" or "OUTPUT" or "P" or "PRE" or "SECTION" or "TABLE" or "TFOOT" or "UL" or "VIDEO" => true, + _ => false, + }; } - private static void ProcessText(String text, StringBuilder sb, ICssStyleDeclaration style, Boolean lastLine) + private static Boolean IsWhiteSpace(Char c) => Char.IsWhiteSpace(c) && c != Symbols.NoBreakSpace; + + private static void ProcessText(String text, StringBuilder sb, ICssStyleDeclaration style, Boolean lastLine, Dictionary requiredLineBreakCounts) { var startIndex = sb.Length; var whiteSpace = style?.GetWhiteSpace(); var textTransform = style?.GetTextTransform(); - var isWhiteSpace = startIndex > 0 ? Char.IsWhiteSpace(sb[startIndex - 1]) && sb[startIndex - 1] != Symbols.NoBreakSpace : true; + var isWhiteSpace = startIndex <= 0 || IsWhiteSpace(sb[startIndex - 1]) || (requiredLineBreakCounts.ContainsKey(startIndex) && IsWhiteSpace(text[0])); for (var i = 0; i < text.Length; i++) { diff --git a/src/AngleSharp.Css/Extensions/MediaListExtensions.cs b/src/AngleSharp.Css/Extensions/MediaListExtensions.cs index 8ed669c3..036a3d8b 100644 --- a/src/AngleSharp.Css/Extensions/MediaListExtensions.cs +++ b/src/AngleSharp.Css/Extensions/MediaListExtensions.cs @@ -8,7 +8,7 @@ namespace AngleSharp.Css.Dom static class MediaListExtensions { private readonly static ConditionalWeakTable AssociatedValidators = - new ConditionalWeakTable(); + new(); private readonly static String[] KnownTypes = { diff --git a/src/AngleSharp.Css/Extensions/StringExtensions.cs b/src/AngleSharp.Css/Extensions/StringExtensions.cs index b5f45fd5..a5040ba1 100644 --- a/src/AngleSharp.Css/Extensions/StringExtensions.cs +++ b/src/AngleSharp.Css/Extensions/StringExtensions.cs @@ -18,9 +18,7 @@ public static class StringExtensions /// The CSS color representation. public static String CssColor(this String value) { - var color = default(Color); - - if (Color.TryFromHex(value, out color)) + if (CssColorValue.TryFromHex(value, out var color)) { return color.CssText; } @@ -28,6 +26,28 @@ public static String CssColor(this String value) return value; } + /// + /// Parses the integer according to the rules. + /// + /// The string to parse. + /// The result of parsing the string. + /// The indicator if the string was indeed an integer. + public static Boolean CssInteger(this String value, out Int32 result) + { + return Int32.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); + } + + /// + /// Parses the number according to the rules. + /// + /// The string to parse. + /// The result of parsing the string. + /// The indicator if the string was indeed a number. + public static Boolean CssNumber(this String value, out Double result) + { + return Double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result); + } + /// /// Splits the string in a numeric value (result) and a unit. Returns /// null if the provided string is ill-formatted. @@ -46,7 +66,7 @@ public static String CssUnit(this String value, out Double result) // Intentional empty. } - if (firstLetter > 0 && Double.TryParse(value.Substring(0, firstLetter), NumberStyles.Any, CultureInfo.InvariantCulture, out result)) + if (firstLetter > 0 && value.Substring(0, firstLetter).CssNumber(out result)) { return value.Substring(firstLetter); } diff --git a/src/AngleSharp.Css/Extensions/StyleCollectionExtensions.cs b/src/AngleSharp.Css/Extensions/StyleCollectionExtensions.cs index 91570714..09e33620 100644 --- a/src/AngleSharp.Css/Extensions/StyleCollectionExtensions.cs +++ b/src/AngleSharp.Css/Extensions/StyleCollectionExtensions.cs @@ -1,6 +1,7 @@ namespace AngleSharp.Css { using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; using AngleSharp.Dom; using AngleSharp.Html.Dom; using AngleSharp.Svg.Dom; @@ -36,30 +37,48 @@ public static IStyleCollection GetStyleCollection(this IWindow window) /// Computes the declarations for the given element in the context of /// the specified styling rules. /// - /// The styles to use. + /// The styles to use. /// The element that is questioned. /// The optional pseudo selector to use. /// The style declaration containing all the declarations. - public static ICssStyleDeclaration ComputeDeclarations(this IEnumerable rules, IElement element, String pseudoSelector = null) + public static ICssStyleDeclaration ComputeDeclarations(this IStyleCollection styles, IElement element, String pseudoSelector = null) { - var computedStyle = new CssStyleDeclaration(element.Owner?.Context); + var ctx = element.Owner?.Context; + var declarations = GetDeclarations(styles, element, pseudoSelector); + var context = new CssComputeContext(styles.Device, ctx, declarations); + + return declarations.Compute(context); + } + + /// + /// Gets the declarations for the given element in the context of + /// the specified styling rules. + /// + /// The styles to use. + /// The element that is questioned. + /// The optional pseudo selector to use. + /// The style declaration containing all the declarations. + public static ICssStyleDeclaration GetDeclarations(this IStyleCollection styles, IElement element, String pseudoSelector = null) + { + var ctx = element.Owner?.Context; + var computedStyle = new CssStyleDeclaration(ctx); var nodes = element.GetAncestors().OfType(); if (!String.IsNullOrEmpty(pseudoSelector)) { var pseudoElement = element?.Pseudo(pseudoSelector.TrimStart(':')); - if (pseudoElement != null) + if (pseudoElement is not null) { element = pseudoElement; } } - computedStyle.SetDeclarations(rules.ComputeCascadedStyle(element)); + computedStyle.SetDeclarations(styles.ComputeCascadedStyle(element)); foreach (var node in nodes) { - computedStyle.UpdateDeclarations(rules.ComputeCascadedStyle(node)); + computedStyle.UpdateDeclarations(styles.ComputeCascadedStyle(node)); } return computedStyle; @@ -70,14 +89,15 @@ public static ICssStyleDeclaration ComputeDeclarations(this IEnumerable - /// The style rules to apply. + /// The style rules to apply. /// The element to compute the cascade for. /// The potential parent for the cascade. /// Returns the cascaded read-only style declaration. - public static ICssStyleDeclaration ComputeCascadedStyle(this IEnumerable styleCollection, IElement element, ICssStyleDeclaration parent = null) + public static ICssStyleDeclaration ComputeCascadedStyle(this IStyleCollection styles, IElement element, ICssStyleDeclaration parent = null) { - var computedStyle = new CssStyleDeclaration(element.Owner?.Context); - var rules = styleCollection.SortBySpecificity(element); + var ctx = element.Owner?.Context; + var computedStyle = new CssStyleDeclaration(ctx); + var rules = styles.SortBySpecificity(element); foreach (var rule in rules) { @@ -90,7 +110,7 @@ public static ICssStyleDeclaration ComputeCascadedStyle(this IEnumerable SortBySpecificity(this IEnumerable rules, IElement element) => - rules.Where(m => m.Selector?.Match(element) ?? false).OrderBy(m => m.Selector.Specificity); + private static IEnumerable SortBySpecificity(this IEnumerable rules, IElement element) + { + IEnumerable> MapPriority(ICssStyleRule rule) + { + if (rule.TryMatch(element, null, out var specificity)) + { + yield return Tuple.Create(rule, specificity); + } + + foreach (var subRule in rule.Rules) + { + if (subRule is ICssStyleRule style) + { + foreach (var item in MapPriority(style)) + { + yield return item; + } + } + } + } + + return rules.SelectMany(MapPriority).OrderBy(GetPriority).Select(GetRule); + } + + private static Priority GetPriority(Tuple item) => item.Item2; + + private static ICssStyleRule GetRule(Tuple item) => item.Item1; + + #endregion + + #region Context + + sealed class CssComputeContext : ICssComputeContext + { + private readonly IRenderDevice _device; + private readonly IBrowsingContext _context; + private readonly ICssProperties _properties; + + public CssComputeContext(IRenderDevice device, IBrowsingContext context, ICssProperties properties) + { + _device = device ?? new DefaultRenderDevice(); + _context = context; + _properties = properties; + } + + public IRenderDevice Device => _device; + + public IBrowsingContext Context => _context; + + public IValueConverter Converter => null; + + public ICssValue Resolve(String name) + { + if (name.StartsWith("--")) + { + var property = _properties.FirstOrDefault(m => m.Name.Equals(name, StringComparison.Ordinal)); + return property?.RawValue; + } + + return null; + } + } #endregion } diff --git a/src/AngleSharp.Css/Extensions/ValueConverterExtensions.cs b/src/AngleSharp.Css/Extensions/ValueConverterExtensions.cs index 17b59636..92f8f5ab 100644 --- a/src/AngleSharp.Css/Extensions/ValueConverterExtensions.cs +++ b/src/AngleSharp.Css/Extensions/ValueConverterExtensions.cs @@ -37,6 +37,9 @@ public static IValueConverter FromList(this IValueConverter converter) => public static IValueConverter ToConverter(this Dictionary values) => new DictionaryValueConverter(values); + public static IValueConverter FlowRelative(this IValueConverter converter) => + new FlowRelativeValueConverter(converter); + public static IValueConverter Periodic(this IValueConverter converter) => new PeriodicValueConverter(converter); diff --git a/src/AngleSharp.Css/Factories/DefaultDeclarationFactory.cs b/src/AngleSharp.Css/Factories/DefaultDeclarationFactory.cs index 2cdcb0f7..80cbe8a1 100644 --- a/src/AngleSharp.Css/Factories/DefaultDeclarationFactory.cs +++ b/src/AngleSharp.Css/Factories/DefaultDeclarationFactory.cs @@ -9,7 +9,7 @@ namespace AngleSharp.Css /// public class DefaultDeclarationFactory : IDeclarationFactory { - private readonly Dictionary _declarations = new Dictionary(StringComparer.OrdinalIgnoreCase) + private readonly Dictionary _declarations = new(StringComparer.OrdinalIgnoreCase) { { BookmarkLabelDeclaration.Name, new DeclarationInfo( @@ -770,6 +770,38 @@ public class DefaultDeclarationFactory : IDeclarationFactory flags: ColumnRuleColorDeclaration.Flags, shorthands: ColumnRuleColorDeclaration.Shorthands) }, + { + PaddingBlockEndDeclaration.Name, new DeclarationInfo( + name: PaddingBlockEndDeclaration.Name, + converter: PaddingBlockEndDeclaration.Converter, + initialValue: PaddingBlockEndDeclaration.InitialValue, + flags: PaddingBlockEndDeclaration.Flags, + shorthands: PaddingBlockEndDeclaration.Shorthands) + }, + { + PaddingBlockStartDeclaration.Name, new DeclarationInfo( + name: PaddingBlockStartDeclaration.Name, + converter: PaddingBlockStartDeclaration.Converter, + initialValue: PaddingBlockStartDeclaration.InitialValue, + flags: PaddingBlockStartDeclaration.Flags, + shorthands: PaddingBlockStartDeclaration.Shorthands) + }, + { + PaddingInlineEndDeclaration.Name, new DeclarationInfo( + name: PaddingInlineEndDeclaration.Name, + converter: PaddingInlineEndDeclaration.Converter, + initialValue: PaddingInlineEndDeclaration.InitialValue, + flags: PaddingInlineEndDeclaration.Flags, + shorthands: PaddingInlineEndDeclaration.Shorthands) + }, + { + PaddingInlineStartDeclaration.Name, new DeclarationInfo( + name: PaddingInlineStartDeclaration.Name, + converter: PaddingInlineStartDeclaration.Converter, + initialValue: PaddingInlineStartDeclaration.InitialValue, + flags: PaddingInlineStartDeclaration.Flags, + shorthands: PaddingInlineStartDeclaration.Shorthands) + }, { PaddingTopDeclaration.Name, new DeclarationInfo( name: PaddingTopDeclaration.Name, @@ -801,6 +833,38 @@ public class DefaultDeclarationFactory : IDeclarationFactory initialValue: PaddingBottomDeclaration.InitialValue, flags: PaddingBottomDeclaration.Flags, shorthands: PaddingBottomDeclaration.Shorthands) + }, + { + MarginBlockEndDeclaration.Name, new DeclarationInfo( + name: MarginBlockEndDeclaration.Name, + converter: MarginBlockEndDeclaration.Converter, + initialValue: MarginBlockEndDeclaration.InitialValue, + flags: MarginBlockEndDeclaration.Flags, + shorthands: MarginBlockEndDeclaration.Shorthands) + }, + { + MarginBlockStartDeclaration.Name, new DeclarationInfo( + name: MarginBlockStartDeclaration.Name, + converter: MarginBlockStartDeclaration.Converter, + initialValue: MarginBlockStartDeclaration.InitialValue, + flags: MarginBlockStartDeclaration.Flags, + shorthands: MarginBlockStartDeclaration.Shorthands) + }, + { + MarginInlineEndDeclaration.Name, new DeclarationInfo( + name: MarginInlineEndDeclaration.Name, + converter: MarginInlineEndDeclaration.Converter, + initialValue: MarginInlineEndDeclaration.InitialValue, + flags: MarginInlineEndDeclaration.Flags, + shorthands: MarginInlineEndDeclaration.Shorthands) + }, + { + MarginInlineStartDeclaration.Name, new DeclarationInfo( + name: MarginInlineStartDeclaration.Name, + converter: MarginInlineStartDeclaration.Converter, + initialValue: MarginInlineStartDeclaration.InitialValue, + flags: MarginInlineStartDeclaration.Flags, + shorthands: MarginInlineStartDeclaration.Shorthands) }, { MarginTopDeclaration.Name, new DeclarationInfo( @@ -1324,6 +1388,22 @@ public class DefaultDeclarationFactory : IDeclarationFactory flags: PaddingDeclaration.Flags, longhands: PaddingDeclaration.Longhands) }, + { + PaddingBlockDeclaration.Name, new DeclarationInfo( + name: PaddingBlockDeclaration.Name, + converter: PaddingBlockDeclaration.Converter, + initialValue: PaddingBlockDeclaration.InitialValue, + flags: PaddingBlockDeclaration.Flags, + longhands: PaddingBlockDeclaration.Longhands) + }, + { + PaddingInlineDeclaration.Name, new DeclarationInfo( + name: PaddingInlineDeclaration.Name, + converter: PaddingInlineDeclaration.Converter, + initialValue: PaddingInlineDeclaration.InitialValue, + flags: PaddingInlineDeclaration.Flags, + longhands: PaddingInlineDeclaration.Longhands) + }, { MarginDeclaration.Name, new DeclarationInfo( name: MarginDeclaration.Name, @@ -1332,6 +1412,22 @@ public class DefaultDeclarationFactory : IDeclarationFactory flags: MarginDeclaration.Flags, longhands: MarginDeclaration.Longhands) }, + { + MarginBlockDeclaration.Name, new DeclarationInfo( + name: MarginBlockDeclaration.Name, + converter: MarginBlockDeclaration.Converter, + initialValue: MarginBlockDeclaration.InitialValue, + flags: MarginBlockDeclaration.Flags, + longhands: MarginBlockDeclaration.Longhands) + }, + { + MarginInlineDeclaration.Name, new DeclarationInfo( + name: MarginInlineDeclaration.Name, + converter: MarginInlineDeclaration.Converter, + initialValue: MarginInlineDeclaration.InitialValue, + flags: MarginInlineDeclaration.Flags, + longhands: MarginInlineDeclaration.Longhands) + }, { BorderRadiusDeclaration.Name, new DeclarationInfo( name: BorderRadiusDeclaration.Name, diff --git a/src/AngleSharp.Css/Factories/DefaultDocumentFunctionFactory.cs b/src/AngleSharp.Css/Factories/DefaultDocumentFunctionFactory.cs index 63ce44e7..c4816c3a 100644 --- a/src/AngleSharp.Css/Factories/DefaultDocumentFunctionFactory.cs +++ b/src/AngleSharp.Css/Factories/DefaultDocumentFunctionFactory.cs @@ -17,7 +17,7 @@ public class DefaultDocumentFunctionFactory : IDocumentFunctionFactory /// The created document function for the given url. public delegate IDocumentFunction Creator(String url); - private readonly Dictionary _creators = new Dictionary(StringComparer.OrdinalIgnoreCase) + private readonly Dictionary _creators = new(StringComparer.OrdinalIgnoreCase) { { FunctionNames.Url, str => new UrlFunction(str) }, { FunctionNames.Domain, str => new DomainFunction(str) }, diff --git a/src/AngleSharp.Css/Factories/DefaultFeatureValidatorFactory.cs b/src/AngleSharp.Css/Factories/DefaultFeatureValidatorFactory.cs index 187abdea..c3e2795a 100644 --- a/src/AngleSharp.Css/Factories/DefaultFeatureValidatorFactory.cs +++ b/src/AngleSharp.Css/Factories/DefaultFeatureValidatorFactory.cs @@ -15,7 +15,7 @@ public class DefaultFeatureValidatorFactory : IFeatureValidatorFactory /// The created media feature validator. public delegate IFeatureValidator Creator(); - private readonly Dictionary _creators = new Dictionary(StringComparer.OrdinalIgnoreCase) + private readonly Dictionary _creators = new(StringComparer.OrdinalIgnoreCase) { { FeatureNames.MinWidth, () => new WidthFeatureValidator() }, { FeatureNames.MaxWidth, () => new WidthFeatureValidator() }, diff --git a/src/AngleSharp.Css/Factories/DefaultPseudoElementFactory.cs b/src/AngleSharp.Css/Factories/DefaultPseudoElementFactory.cs index 0b7cb5b2..abd4cf8d 100644 --- a/src/AngleSharp.Css/Factories/DefaultPseudoElementFactory.cs +++ b/src/AngleSharp.Css/Factories/DefaultPseudoElementFactory.cs @@ -17,7 +17,7 @@ public class DefaultPseudoElementFactory : IPseudoElementFactory /// The new pseudo element. public delegate IPseudoElement Creator(IElement host); - private readonly Dictionary _creators = new Dictionary(StringComparer.OrdinalIgnoreCase) + private readonly Dictionary _creators = new(StringComparer.OrdinalIgnoreCase) { { PseudoElementNames.Before, element => new PseudoElement(element, PseudoElementNames.Before) }, { PseudoElementNames.After, element => new PseudoElement(element, PseudoElementNames.After) }, diff --git a/src/AngleSharp.Css/Factories/Factory.cs b/src/AngleSharp.Css/Factories/Factory.cs index 5ba847f8..8f32d626 100644 --- a/src/AngleSharp.Css/Factories/Factory.cs +++ b/src/AngleSharp.Css/Factories/Factory.cs @@ -2,14 +2,14 @@ namespace AngleSharp.Css { static class Factory { - public static DefaultFeatureValidatorFactory FeatureValidator = new DefaultFeatureValidatorFactory(); + public static DefaultFeatureValidatorFactory FeatureValidator = new(); - public static DefaultPseudoElementFactory PseudoElement = new DefaultPseudoElementFactory(); + public static DefaultPseudoElementFactory PseudoElement = new(); - public static DefaultDocumentFunctionFactory DocumentFunction = new DefaultDocumentFunctionFactory(); + public static DefaultDocumentFunctionFactory DocumentFunction = new(); - public static DefaultDeclarationFactory Declaration = new DefaultDeclarationFactory(); + public static DefaultDeclarationFactory Declaration = new(); - public static StyleAttributeObserver Observer = new StyleAttributeObserver(); + public static StyleAttributeObserver Observer = new(); } } diff --git a/src/AngleSharp.Css/FeatureValidators/AspectRatioFeatureValidator.cs b/src/AngleSharp.Css/FeatureValidators/AspectRatioFeatureValidator.cs index 1611c816..9cd3596e 100644 --- a/src/AngleSharp.Css/FeatureValidators/AspectRatioFeatureValidator.cs +++ b/src/AngleSharp.Css/FeatureValidators/AspectRatioFeatureValidator.cs @@ -11,7 +11,7 @@ public Boolean Validate(IMediaFeature feature, IRenderDevice renderDevice) { var ratio = RatioConverter.Convert(feature.Value); - if (ratio != null) + if (ratio is not null) { var desired = ratio.AsDouble(); var available = renderDevice.ViewPortWidth / (Double)renderDevice.ViewPortHeight; diff --git a/src/AngleSharp.Css/FeatureValidators/ColorFeatureValidator.cs b/src/AngleSharp.Css/FeatureValidators/ColorFeatureValidator.cs index 6318f58b..77ec9a2c 100644 --- a/src/AngleSharp.Css/FeatureValidators/ColorFeatureValidator.cs +++ b/src/AngleSharp.Css/FeatureValidators/ColorFeatureValidator.cs @@ -10,7 +10,7 @@ sealed class ColorFeatureValidator : IFeatureValidator { public Boolean Validate(IMediaFeature feature, IRenderDevice renderDevice) { - var defaultValue = new Length(1.0, Length.Unit.None); + var defaultValue = new CssLengthValue(1.0, CssLengthValue.Unit.None); var converter = feature.IsMinimum || feature.IsMaximum ? PositiveIntegerConverter : PositiveIntegerConverter.Option(defaultValue); var color = converter.Convert(feature.Value); diff --git a/src/AngleSharp.Css/FeatureValidators/ColorIndexFeatureValidator.cs b/src/AngleSharp.Css/FeatureValidators/ColorIndexFeatureValidator.cs index d4d83499..15e4ea47 100644 --- a/src/AngleSharp.Css/FeatureValidators/ColorIndexFeatureValidator.cs +++ b/src/AngleSharp.Css/FeatureValidators/ColorIndexFeatureValidator.cs @@ -10,7 +10,7 @@ sealed class ColorIndexFeatureValidator : IFeatureValidator { public Boolean Validate(IMediaFeature feature, IRenderDevice renderDevice) { - var defaultValue = new Length(1.0, Length.Unit.None); + var defaultValue = new CssLengthValue(1.0, CssLengthValue.Unit.None); var converter = feature.IsMinimum || feature.IsMaximum ? NaturalIntegerConverter : NaturalIntegerConverter.Option(defaultValue); var index = converter.Convert(feature.Value); diff --git a/src/AngleSharp.Css/FeatureValidators/DeviceAspectRatioFeatureValidator.cs b/src/AngleSharp.Css/FeatureValidators/DeviceAspectRatioFeatureValidator.cs index 9aefe13d..235f0bf9 100644 --- a/src/AngleSharp.Css/FeatureValidators/DeviceAspectRatioFeatureValidator.cs +++ b/src/AngleSharp.Css/FeatureValidators/DeviceAspectRatioFeatureValidator.cs @@ -11,7 +11,7 @@ public Boolean Validate(IMediaFeature feature, IRenderDevice renderDevice) { var ratio = RatioConverter.Convert(feature.Value); - if (ratio != null) + if (ratio is not null) { var desired = ratio.AsDouble(); var available = renderDevice.DeviceWidth / (Double)renderDevice.DeviceHeight; diff --git a/src/AngleSharp.Css/FeatureValidators/MonochromeFeatureValidator.cs b/src/AngleSharp.Css/FeatureValidators/MonochromeFeatureValidator.cs index 35c94597..425edffd 100644 --- a/src/AngleSharp.Css/FeatureValidators/MonochromeFeatureValidator.cs +++ b/src/AngleSharp.Css/FeatureValidators/MonochromeFeatureValidator.cs @@ -10,7 +10,7 @@ sealed class MonochromeFeatureValidator : IFeatureValidator { public Boolean Validate(IMediaFeature feature, IRenderDevice renderDevice) { - var defaultValue = new Length(1.0, Length.Unit.None); + var defaultValue = new CssLengthValue(1.0, CssLengthValue.Unit.None); var converter = feature.IsMinimum || feature.IsMaximum ? NaturalIntegerConverter : NaturalIntegerConverter.Option(defaultValue); var index = converter.Convert(feature.Value); diff --git a/src/AngleSharp.Css/Parser/CssBuilder.cs b/src/AngleSharp.Css/Parser/CssBuilder.cs index a287e6c7..66ccf126 100644 --- a/src/AngleSharp.Css/Parser/CssBuilder.cs +++ b/src/AngleSharp.Css/Parser/CssBuilder.cs @@ -491,7 +491,7 @@ public TextPosition CreateRules(CssStyleSheet sheet) token = NextToken(); CollectTrivia(ref token); - if (rule != null) + if (rule is not null) { sheet.Add(rule); } @@ -524,6 +524,32 @@ public void CreateDeclarationWith(ICssProperties properties, ref CssToken token) CollectTrivia(ref token); var start = token.Position; + 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) + { + style.Add(result); + token = NextToken(); + return; + } + } + if (token.IsNot(CssTokenType.EndOfFile, CssTokenType.CurlyBracketClose, CssTokenType.Colon) && token.IsNot(CssTokenType.Semicolon, CssTokenType.CurlyBracketOpen)) { diff --git a/src/AngleSharp.Css/Parser/CssParser.cs b/src/AngleSharp.Css/Parser/CssParser.cs index afa35d2c..3a4dd1f6 100644 --- a/src/AngleSharp.Css/Parser/CssParser.cs +++ b/src/AngleSharp.Css/Parser/CssParser.cs @@ -203,7 +203,6 @@ internal ICssStyleSheet ParseStylesheet(TextSource source) { var sheet = new CssStyleSheet(_context, source); var tokenizer = CreateTokenizer(source); - var start = tokenizer.GetCurrentPosition(); var builder = new CssBuilder(_options, tokenizer, _context); InvokeEventListener(new CssParseEvent(sheet, completed: false)); builder.CreateRules(sheet); diff --git a/src/AngleSharp.Css/Parser/CssParserOptions.cs b/src/AngleSharp.Css/Parser/CssParserOptions.cs index e14811af..a3432e85 100644 --- a/src/AngleSharp.Css/Parser/CssParserOptions.cs +++ b/src/AngleSharp.Css/Parser/CssParserOptions.cs @@ -36,5 +36,18 @@ public Boolean IsToleratingInvalidSelectors get; set; } + + /// + /// Gets or sets if nesting of style rules should be + /// disabled; if disabled the interpretation is closer to + /// older browser also accepting declarations such as + /// "*x-overflow", which would otherwise be an invalid + /// nesting rule. + /// + public Boolean IsExcludingNesting + { + get; + set; + } } } \ No newline at end of file diff --git a/src/AngleSharp.Css/Parser/CssTokenExtensions.cs b/src/AngleSharp.Css/Parser/CssTokenExtensions.cs index cae2dc71..716f4e71 100644 --- a/src/AngleSharp.Css/Parser/CssTokenExtensions.cs +++ b/src/AngleSharp.Css/Parser/CssTokenExtensions.cs @@ -1,6 +1,7 @@ -namespace AngleSharp.Css.Parser +namespace AngleSharp.Css.Parser { using AngleSharp.Css.Parser.Tokens; + using AngleSharp.Text; using System; /// @@ -8,6 +9,24 @@ /// static class CssTokenExtensions { + public static Boolean IsPotentiallyNested(this CssToken token) + { + if (token.Is(CssTokenType.Hash, CssTokenType.Colon, CssTokenType.SquareBracketOpen)) + { + return true; + } + else if (token.Type == CssTokenType.Delim && token.Data.Length == 1) + { + return token.Data[0] switch + { + Symbols.Asterisk or Symbols.Plus or Symbols.Dollar or Symbols.Dot or Symbols.Tilde or Symbols.GreaterThan or Symbols.Ampersand => true, + _ => false, + }; + } + + return false; + } + /// /// Checks if the provided token is either of the first or the second /// type of token. @@ -22,6 +41,39 @@ public static Boolean Is(this CssToken token, CssTokenType a, CssTokenType b) return type == a || type == b; } + /// + /// Checks if the provided token is one of the list of tokens. + /// + /// The token to examine. + /// The 1st type to match. + /// The 2nd type to match. + /// The 3rd type to match. + /// Result of the examination. + public static Boolean Is(this CssToken token, CssTokenType a, CssTokenType b, CssTokenType c) + { + var type = token.Type; + return type == a || type == b || type == c; + } + + /// + /// Checks if the provided token is one of the list of tokens. + /// + /// The token to examine. + /// The 1st type to match. + /// The 2nd type to match. + /// The 3rd type to match. + /// The 4th type to match. + /// The 5th type to match. + /// The 6th type to match. + /// The 7th type to match. + /// The 8th type to match. + /// Result of the examination. + public static Boolean Is(this CssToken token, CssTokenType a, CssTokenType b, CssTokenType c, CssTokenType d, CssTokenType e, CssTokenType f, CssTokenType g, CssTokenType h) + { + var type = token.Type; + return type == a || type == b || type == c || type == d || type == e || type == f || type == g || type == h; + } + /// /// Checks if the provided token is neither of the first nor the second /// type of token. diff --git a/src/AngleSharp.Css/Parser/CssTokenizer.cs b/src/AngleSharp.Css/Parser/CssTokenizer.cs index c41d099a..504f8d31 100644 --- a/src/AngleSharp.Css/Parser/CssTokenizer.cs +++ b/src/AngleSharp.Css/Parser/CssTokenizer.cs @@ -70,6 +70,11 @@ public String ContentFrom(Int32 position) { var token = Data(current); + if (Current is Symbols.EndOfFile) + { + Back(); + } + if (token.Type == CssTokenType.Whitespace) { spaced++; diff --git a/src/AngleSharp.Css/Parser/Micro/CalcParser.cs b/src/AngleSharp.Css/Parser/Micro/CalcParser.cs index 05b9085f..3c56100a 100644 --- a/src/AngleSharp.Css/Parser/Micro/CalcParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/CalcParser.cs @@ -141,8 +141,11 @@ private static ICssValue ParseBracketExpression(this StringSource source) return source.ParseAtomicExpression(); } - - private static ICssValue ParseAtomicExpression(this StringSource source) + + /// + /// Parses a unit value into a ICssValue. + /// + public static ICssValue ParseAtomicExpression(this StringSource source) { var unit = source.ParseUnit(); @@ -151,32 +154,32 @@ private static ICssValue ParseAtomicExpression(this StringSource source) if (String.IsNullOrEmpty(unit.Dimension)) { source.SkipSpacesAndComments(); - return new Length(result, Length.Unit.None); + return new CssLengthValue(result, CssLengthValue.Unit.None); } - else if (Length.GetUnit(unit.Dimension) != Length.Unit.None) + else if (CssLengthValue.GetUnit(unit.Dimension) != CssLengthValue.Unit.None) { source.SkipSpacesAndComments(); - return new Length(result, Length.GetUnit(unit.Dimension)); + return new CssLengthValue(result, CssLengthValue.GetUnit(unit.Dimension)); } - else if (Time.GetUnit(unit.Dimension) != Time.Unit.None) + else if (CssTimeValue.GetUnit(unit.Dimension) != CssTimeValue.Unit.None) { source.SkipSpacesAndComments(); - return new Time(result, Time.GetUnit(unit.Dimension)); + return new CssTimeValue(result, CssTimeValue.GetUnit(unit.Dimension)); } - else if (Angle.GetUnit(unit.Dimension) != Angle.Unit.None) + else if (CssAngleValue.GetUnit(unit.Dimension) != CssAngleValue.Unit.None) { source.SkipSpacesAndComments(); - return new Angle(result, Angle.GetUnit(unit.Dimension)); + return new CssAngleValue(result, CssAngleValue.GetUnit(unit.Dimension)); } - else if (Frequency.GetUnit(unit.Dimension) != Frequency.Unit.None) + else if (CssFrequencyValue.GetUnit(unit.Dimension) != CssFrequencyValue.Unit.None) { source.SkipSpacesAndComments(); - return new Frequency(result, Frequency.GetUnit(unit.Dimension)); + return new CssFrequencyValue(result, CssFrequencyValue.GetUnit(unit.Dimension)); } - else if (Resolution.GetUnit(unit.Dimension) != Resolution.Unit.None) + else if (CssResolutionValue.GetUnit(unit.Dimension) != CssResolutionValue.Unit.None) { source.SkipSpacesAndComments(); - return new Resolution(result, Resolution.GetUnit(unit.Dimension)); + return new CssResolutionValue(result, CssResolutionValue.GetUnit(unit.Dimension)); } } diff --git a/src/AngleSharp.Css/Parser/Micro/ColorParser.cs b/src/AngleSharp.Css/Parser/Micro/ColorParser.cs index bffb1b37..2a8bcdad 100644 --- a/src/AngleSharp.Css/Parser/Micro/ColorParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/ColorParser.cs @@ -11,7 +11,7 @@ namespace AngleSharp.Css.Parser /// static class ColorParser { - private static readonly Dictionary> ColorFunctions = new Dictionary>(StringComparer.OrdinalIgnoreCase) + private static readonly Dictionary> ColorFunctions = new(StringComparer.OrdinalIgnoreCase) { { FunctionNames.Rgb, ParseRgba }, { FunctionNames.Rgba, ParseRgba }, @@ -20,12 +20,16 @@ static class ColorParser { FunctionNames.Gray, ParseGray }, { FunctionNames.Hwb, ParseHwba }, { FunctionNames.Hwba, ParseHwba }, + { FunctionNames.Lab, ParseLab }, + { FunctionNames.Lch, ParseLch }, + { FunctionNames.Oklab, ParseOklab}, + { FunctionNames.Oklch, ParseOklch }, }; /// /// Parses a color value, if any. /// - public static Color? ParseColor(this StringSource source) + public static CssColorValue? ParseColor(this StringSource source) { var pos = source.Index; var result = Start(source); @@ -41,10 +45,10 @@ static class ColorParser /// /// Parses a the current color value, if any. /// - public static Color? ParseCurrentColor(this StringSource source) => - source.IsIdentifier(CssKeywords.CurrentColor) ? Color.CurrentColor : ColorParser.ParseColor(source); + public static CssColorValue? ParseCurrentColor(this StringSource source) => + source.IsIdentifier(CssKeywords.CurrentColor) ? CssColorValue.CurrentColor : ColorParser.ParseColor(source); - private static Color? Start(StringSource source) + private static CssColorValue? Start(StringSource source) { if (source.Current != Symbols.Num) { @@ -63,7 +67,7 @@ static class ColorParser return null; } - return Color.FromName(ident); + return CssColorValue.FromName(ident); } return null; @@ -72,7 +76,7 @@ static class ColorParser return Literal(source); } - private static Color? Literal(StringSource source) + private static CssColorValue? Literal(StringSource source) { var current = source.Next(); var buffer = StringBuilderPool.Obtain(); @@ -83,7 +87,7 @@ static class ColorParser current = source.Next(); } - if (Color.TryFromHex(buffer.ToPool(), out var result)) + if (CssColorValue.TryFromHex(buffer.ToPool(), out var result)) { return result; } @@ -91,7 +95,53 @@ static class ColorParser return null; } - private static Color? ParseRgba(StringSource source) + private static CssColorValue? ParseRgba(StringSource source) + { + var pos = source.Index; + var color = ParseRgbaLegacy(source); + + if (!color.HasValue) + { + source.BackTo(pos); + return ParseRgbaModern(source); + } + + return color.Value; + } + + private static CssColorValue? ParseRgbaModern(StringSource source) + { + var r = ParseRgbOrNoneComponent(source); + source.SkipSpacesAndComments(); + var g = ParseRgbOrNoneComponent(source); + source.SkipSpacesAndComments(); + var b = ParseRgbOrNoneComponent(source); + source.SkipSpacesAndComments(); + var c = source.Current; + var a = new Nullable(1.0); + + if (r != null && g != null && b != null) + { + source.SkipCurrentAndSpaces(); + + if (c == Symbols.Solidus) + { + a = ParseAlpha(source); + source.SkipSpacesAndComments(); + c = source.Current; + source.SkipCurrentAndSpaces(); + } + + if (c == Symbols.RoundBracketClose) + { + return CssColorValue.FromRgba(r.Value, g.Value, b.Value, a.Value); + } + } + + return null; + } + + private static CssColorValue? ParseRgbaLegacy(StringSource source) { var r = ParseRgbComponent(source); var c1 = source.SkipGetSkip(); @@ -104,7 +154,7 @@ static class ColorParser { if (Check(c3, c1, c2)) { - return Color.FromRgb(r.Value, g.Value, b.Value); + return CssColorValue.FromRgb(r.Value, g.Value, b.Value); } else { @@ -114,7 +164,7 @@ static class ColorParser if (a != null && Check(f, c1, c2, c3)) { - return Color.FromRgba(r.Value, g.Value, b.Value, a.Value); + return CssColorValue.FromRgba(r.Value, g.Value, b.Value, a.Value); } } } @@ -122,7 +172,7 @@ static class ColorParser return null; } - private static Color? ParseHsla(StringSource source) + private static CssColorValue? ParseHsla(StringSource source) { var h = ParseAngle(source); var c1 = source.SkipGetSkip(); @@ -135,7 +185,7 @@ static class ColorParser { if (Check(c3, c1, c2)) { - return Color.FromHsl(h.Value, s.Value, l.Value); + return CssColorValue.FromHsl(h.Value, s.Value, l.Value); } else { @@ -144,7 +194,7 @@ static class ColorParser if (a != null && Check(f, c1, c2, c3)) { - return Color.FromHsla(h.Value, s.Value, l.Value, a.Value); + return CssColorValue.FromHsla(h.Value, s.Value, l.Value, a.Value); } } } @@ -152,7 +202,7 @@ static class ColorParser return null; } - private static Color? ParseGray(StringSource source) + private static CssColorValue? ParseGray(StringSource source) { var n = ParseRgbComponent(source); var c = source.SkipGetSkip(); @@ -163,18 +213,147 @@ static class ColorParser { if (c == Symbols.RoundBracketClose) { - return Color.FromGray(n.Value); + return CssColorValue.FromGray(n.Value); } else if (a != null && Check(f, c)) { - return Color.FromGray(n.Value, a.Value); + return CssColorValue.FromGray(n.Value, a.Value); } } return null; } - private static Color? ParseHwba(StringSource source) + private static CssColorValue? ParseLab(StringSource source) + { + var l = ParseLabComponent(source); + source.SkipSpacesAndComments(); + var a = ParseLabComponent(source); + source.SkipSpacesAndComments(); + var b = ParseLabComponent(source); + source.SkipSpacesAndComments(); + var c = source.Current; + var alpha = new Nullable(1.0); + + if (l != null && a != null && b != null) + { + source.SkipCurrentAndSpaces(); + + if (c == Symbols.Solidus) + { + alpha = ParseAlpha(source); + source.SkipSpacesAndComments(); + c = source.Current; + source.SkipCurrentAndSpaces(); + } + + if (c == Symbols.RoundBracketClose) + { + return CssColorValue.FromLab(l.Value, a.Value, b.Value, alpha.Value); + } + } + + return null; + } + + private static CssColorValue? ParseLch(StringSource source) + { + var l = ParseLabComponent(source); + source.SkipSpacesAndComments(); + var c = ParseLabComponent(source); + source.SkipSpacesAndComments(); + var h = ParseAngle(source); + source.SkipSpacesAndComments(); + var chr = source.Current; + var a = new Nullable(1.0); + + if (l != null && c != null && h != null) + { + source.SkipCurrentAndSpaces(); + + if (chr == Symbols.Solidus) + { + a = ParseAlpha(source); + source.SkipSpacesAndComments(); + chr = source.Current; + source.SkipCurrentAndSpaces(); + } + + if (chr == Symbols.RoundBracketClose) + { + return CssColorValue.FromLch(l.Value, c.Value, h.Value, a.Value); + } + } + + return null; + } + + + private static CssColorValue? ParseOklab(StringSource source) + { + var l = ParseLabComponent(source); + source.SkipSpacesAndComments(); + var a = ParseLabComponent(source); + source.SkipSpacesAndComments(); + var b = ParseLabComponent(source); + source.SkipSpacesAndComments(); + var c = source.Current; + var alpha = new Nullable(1.0); + + if (l != null && a != null && b != null) + { + source.SkipCurrentAndSpaces(); + + if (c == Symbols.Solidus) + { + alpha = ParseAlpha(source); + source.SkipSpacesAndComments(); + c = source.Current; + source.SkipCurrentAndSpaces(); + } + + if (c == Symbols.RoundBracketClose) + { + return CssColorValue.FromOklab(l.Value, a.Value, b.Value, alpha.Value); + } + } + + return null; + } + + private static CssColorValue? ParseOklch(StringSource source) + { + var l = ParseLabComponent(source); + source.SkipSpacesAndComments(); + var c = ParseLabComponent(source); + source.SkipSpacesAndComments(); + var h = ParseAngle(source); + source.SkipSpacesAndComments(); + var chr = source.Current; + var a = new Nullable(1.0); + + if (l != null && c != null && h != null) + { + source.SkipCurrentAndSpaces(); + + if (chr == Symbols.Solidus) + { + a = ParseAlpha(source); + source.SkipSpacesAndComments(); + chr = source.Current; + source.SkipCurrentAndSpaces(); + } + + if (chr == Symbols.RoundBracketClose) + { + return CssColorValue.FromOklch(l.Value, c.Value, h.Value, a.Value); + } + } + + return null; + } + + private static CssColorValue? ParseHwba(StringSource source) { var h = ParseAngle(source); var c1 = source.SkipGetSkip(); @@ -187,7 +366,7 @@ static class ColorParser { if (Check(c3, c1, c2)) { - return Color.FromHwb(h.Value, s.Value, l.Value); + return CssColorValue.FromHwb(h.Value, s.Value, l.Value); } else { @@ -197,7 +376,7 @@ static class ColorParser if (a != null && Check(f, c1, c2, c3)) { - return Color.FromHwba(h.Value, s.Value, l.Value, a.Value); + return CssColorValue.FromHwba(h.Value, s.Value, l.Value, a.Value); } } } @@ -205,21 +384,68 @@ static class ColorParser return null; } - private static Byte? ParseRgbComponent(StringSource source) + private static Double? ParseLabComponent(StringSource source) { + var pos = source.Index; var unit = source.ParseUnit(); - if (unit != null && - Int32.TryParse(unit.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) + if (unit == null) { - if (unit.Dimension == "%") - { - return (Byte)(255f / 100f * value); - } - else if (unit.Dimension == String.Empty) + source.BackTo(pos); + + if (source.IsIdentifier(CssKeywords.None)) { - return (Byte)Math.Max(Math.Min(value, 255f), 0f); + return 0; } + + return null; + } + + if ((unit.Dimension == String.Empty || unit.Dimension == "%") && Double.TryParse(unit.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var value)) + { + return value; + } + + return null; + } + + private static Byte? ParseRgbOrNoneComponent(StringSource source) + { + var pos = source.Index; + var value = ParseRgbComponent(source); + + if (value.HasValue) + { + return value; + } + + source.BackTo(pos); + + if (source.IsIdentifier(CssKeywords.None)) + { + return 0; + } + + return null; + } + + private static Byte? ParseRgbComponent(StringSource source) + { + var unit = source.ParseUnit(); + + if (unit == null) + { + return null; + } + + if (unit.Dimension == String.Empty && Int32.TryParse(unit.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num)) + { + return (Byte)Math.Max(Math.Min(num, 255f), 0f); + } + + if (unit.Dimension == "%" && Double.TryParse(unit.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var val)) + { + return (Byte)Math.Round((255.0 * val) / 100.0); } return null; @@ -252,11 +478,11 @@ static class ColorParser if (unit != null && Double.TryParse(unit.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var value)) { - var dim = Angle.Unit.Deg; + var dim = CssAngleValue.Unit.Deg; - if (unit.Dimension == String.Empty || (dim = Angle.GetUnit(unit.Dimension)) != Angle.Unit.None) + if (unit.Dimension == String.Empty || (dim = CssAngleValue.GetUnit(unit.Dimension)) != CssAngleValue.Unit.None) { - var angle = new Angle(value, dim); + var angle = new CssAngleValue(value, dim); return angle.ToTurns(); } } diff --git a/src/AngleSharp.Css/Parser/Micro/CompoundParser.cs b/src/AngleSharp.Css/Parser/Micro/CompoundParser.cs index a241eb29..28ee289a 100644 --- a/src/AngleSharp.Css/Parser/Micro/CompoundParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/CompoundParser.cs @@ -30,7 +30,7 @@ public static CssTupleValue ParseQuotes(this StringSource source) return null; } - quotes.Add(new Quote(open, close)); + quotes.Add(new CssQuoteValue(open, close)); } return new CssTupleValue(quotes.ToArray()); @@ -41,7 +41,7 @@ public static CssTupleValue ParseQuotes(this StringSource source) /// public static CssBorderImageSliceValue ParseBorderImageSlice(this StringSource source) { - var lengths = new Length[4]; + var lengths = new ICssValue[4]; var filled = false; var completed = 0; var pos = 0; @@ -73,7 +73,7 @@ public static CssBorderImageSliceValue ParseBorderImageSlice(this StringSource s { while (completed < 4) { - lengths[completed++] = Length.Auto; + lengths[completed++] = CssLengthValue.Auto; } return new CssBorderImageSliceValue(lengths[0], lengths[1], lengths[2], lengths[3], filled); @@ -89,14 +89,14 @@ public static CssImageRepeatsValue ParseBackgroundRepeat(this StringSource sourc { if (source.IsIdentifier(CssKeywords.RepeatX)) { - var h = new Constant(CssKeywords.Repeat, BackgroundRepeat.Repeat); - var v = new Constant(CssKeywords.NoRepeat, BackgroundRepeat.NoRepeat); + var h = new CssConstantValue(CssKeywords.Repeat, BackgroundRepeat.Repeat); + var v = new CssConstantValue(CssKeywords.NoRepeat, BackgroundRepeat.NoRepeat); return new CssImageRepeatsValue(h, v); } else if (source.IsIdentifier(CssKeywords.RepeatY)) { - var h = new Constant(CssKeywords.NoRepeat, BackgroundRepeat.NoRepeat); - var v = new Constant(CssKeywords.Repeat, BackgroundRepeat.Repeat); + var h = new CssConstantValue(CssKeywords.NoRepeat, BackgroundRepeat.NoRepeat); + var v = new CssConstantValue(CssKeywords.Repeat, BackgroundRepeat.Repeat); return new CssImageRepeatsValue(h, v); } else diff --git a/src/AngleSharp.Css/Parser/Micro/CssUriParser.cs b/src/AngleSharp.Css/Parser/Micro/CssUriParser.cs index 3c0c3802..14a2b5e4 100644 --- a/src/AngleSharp.Css/Parser/Micro/CssUriParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/CssUriParser.cs @@ -19,23 +19,14 @@ public static CssUrlValue ParseUri(this StringSource source) { var current = source.SkipSpacesAndComments(); - switch (current) - { - case Symbols.DoubleQuote: - return DoubleQuoted(source); - - case Symbols.SingleQuote: - return SingleQuoted(source); - - case Symbols.RoundBracketClose: - return new CssUrlValue(String.Empty); - - case Symbols.EndOfFile: - return new CssUrlValue(String.Empty); - - default: - return Unquoted(source); - } + return current switch + { + Symbols.DoubleQuote => DoubleQuoted(source), + Symbols.SingleQuote => SingleQuoted(source), + Symbols.RoundBracketClose => new CssUrlValue(String.Empty), + Symbols.EndOfFile => new CssUrlValue(String.Empty), + _ => Unquoted(source), + }; } return null; diff --git a/src/AngleSharp.Css/Parser/Micro/FunctionParser.cs b/src/AngleSharp.Css/Parser/Micro/FunctionParser.cs index 5ba83e1d..234cc7bf 100644 --- a/src/AngleSharp.Css/Parser/Micro/FunctionParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/FunctionParser.cs @@ -1,5 +1,6 @@ namespace AngleSharp.Css.Parser { + using AngleSharp.Css.Dom; using AngleSharp.Css.Values; using AngleSharp.Text; using System; @@ -93,15 +94,15 @@ public static CssVarValue ParseVar(this StringSource source) var name = source.ParseCustomIdent(); var f = source.SkipGetSkip(); - if (name != null) + if (name is not null) { switch (f) { case Symbols.RoundBracketClose: return new CssVarValue(name); case Symbols.Comma: - var defaultValue = source.TakeUntilClosed(); - source.SkipCurrentAndSpaces(); + source.SkipSpacesAndComments(); + var defaultValue = ParseVarFallback(source); return new CssVarValue(name, defaultValue); } } @@ -109,6 +110,22 @@ public static CssVarValue ParseVar(this StringSource source) return null; } + /// + /// Parses a CSS var (variable) fallback value. + /// + public static ICssValue ParseVarFallback(this StringSource source) + { + if (!source.IsFunction(FunctionNames.Var)) + { + var content = source.TakeUntilClosed(); + source.SkipCurrentAndSpaces(); + return new CssAnyValue(content); + } + + return source.ParseVar(); + + } + /// /// Parses a CSS content value. /// @@ -151,7 +168,7 @@ public static CssRunningValue ParseRunning(this StringSource source) /// Parses a counter object. /// http://www.w3.org/TR/CSS2/syndata.html#value-def-counter /// - public static CounterDefinition? ParseCounter(this StringSource source) + public static CssCounterDefinitionValue? ParseCounter(this StringSource source) { if (source.IsFunction(FunctionNames.Counter)) { @@ -179,7 +196,7 @@ public static CssRunningValue ParseRunning(this StringSource source) return null; } - private static CounterDefinition? ParseCounterStyle(StringSource source, String ident, String separator, Char f) + private static CssCounterDefinitionValue? ParseCounterStyle(StringSource source, String ident, String separator, Char f) { var style = CssKeywords.Decimal; @@ -191,7 +208,7 @@ public static CssRunningValue ParseRunning(this StringSource source) if (f == Symbols.RoundBracketClose) { - return new CounterDefinition(ident, style, separator); + return new CssCounterDefinitionValue(ident, style, separator); } return null; diff --git a/src/AngleSharp.Css/Parser/Micro/GradientParser.cs b/src/AngleSharp.Css/Parser/Micro/GradientParser.cs index d14ec8b1..44a7dbd8 100644 --- a/src/AngleSharp.Css/Parser/Micro/GradientParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/GradientParser.cs @@ -11,7 +11,7 @@ namespace AngleSharp.Css.Parser /// public static class GradientParser { - private static readonly Dictionary> GradientFunctions = new Dictionary>(StringComparer.OrdinalIgnoreCase) + private static readonly Dictionary> GradientFunctions = new(StringComparer.OrdinalIgnoreCase) { { FunctionNames.LinearGradient, ParseLinearGradient }, { FunctionNames.RepeatingLinearGradient, ParseRepeatingLinearGradient }, @@ -125,7 +125,7 @@ private static ICssGradientFunctionValue ParseRadialGradient(StringSource source if (stops != null && source.Current == Symbols.RoundBracketClose) { var circle = options?.Circle ?? false; - var center = options?.Center ?? Point.Center; + var center = options?.Center ?? CssPoint2D.Center; var width = options?.Width; var height = options?.Height; var sizeMode = options?.Size ?? CssRadialGradientValue.SizeMode.None; @@ -302,7 +302,7 @@ private static ICssValue ParseLinearAngleKeywords(StringSource source) private static RadialOptions? ParseRadialOptions(StringSource source) { var circle = false; - var center = Point.Center; + var center = CssPoint2D.Center; var width = default(ICssValue); var height = default(ICssValue); var size = CssRadialGradientValue.SizeMode.None; @@ -445,7 +445,7 @@ public struct RadialOptions /// /// Gets or sets the center of the gradient. /// - public Point Center; + public CssPoint2D Center; /// /// Gets or sets the width of the gradient. diff --git a/src/AngleSharp.Css/Parser/Micro/GridParser.cs b/src/AngleSharp.Css/Parser/Micro/GridParser.cs index 6b16ee8d..68718e0b 100644 --- a/src/AngleSharp.Css/Parser/Micro/GridParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/GridParser.cs @@ -20,7 +20,7 @@ public static ICssValue ParseGridTemplate(this StringSource source) if (source.IsIdentifier(CssKeywords.None)) { - return new Identifier(CssKeywords.None); + return new CssIdentifierValue(CssKeywords.None); } var rows = source.ParseTrackList() ?? source.ParseAutoTrackList(); @@ -60,7 +60,7 @@ public static ICssValue ParseGridTemplate(this StringSource source) hasValue = true; source.SkipSpacesAndComments(); rowValues.Add(new CssTupleValue(new[] { value.Item1, value.Item3, value.Item4 })); - areaValues.Add(new Label(value.Item2)); + areaValues.Add(new CssStringValue(value.Item2)); } if (hasValue) @@ -87,7 +87,7 @@ public static ICssValue ParseGridTemplate(this StringSource source) return null; } - private static Tuple ParseGridTemplateAlternative(this StringSource source) + private static Tuple ParseGridTemplateAlternative(this StringSource source) { var namesHead = source.ParseLineNames(); source.SkipSpacesAndComments(); @@ -109,7 +109,7 @@ public static ICssValue ParseGridTemplate(this StringSource source) /// /// Parses a grid line name value. /// - public static LineNames? ParseLineNames(this StringSource source) + public static CssLineNamesValue? ParseLineNames(this StringSource source) { var pos = source.Index; @@ -133,7 +133,7 @@ public static ICssValue ParseGridTemplate(this StringSource source) if (current == Symbols.SquareBracketClose) { source.Next(); - return new LineNames(names); + return new CssLineNamesValue(names); } } } @@ -166,7 +166,7 @@ public static ICssValue ParseFixedSize(this StringSource source) var max = source.ParseTrackBreadth(); c = source.SkipSpacesAndComments(); - if (max != null && c == Symbols.RoundBracketClose && (min is Length? || max is Length?)) + if (max != null && c == Symbols.RoundBracketClose && (min is CssLengthValue? || max is CssLengthValue?)) { source.Next(); return new CssMinMaxValue(min, max); @@ -255,28 +255,18 @@ private static ICssValue ParseAutoCount(this StringSource source) { if (arg.Isi(CssKeywords.AutoFill)) { - return new Identifier(CssKeywords.AutoFill); + return new CssIdentifierValue(CssKeywords.AutoFill); } else if (arg.Isi(CssKeywords.AutoFit)) { - return new Identifier(CssKeywords.AutoFit); + return new CssIdentifierValue(CssKeywords.AutoFit); } } return null; } - private static ICssValue ParseIntegerCount(this StringSource source) - { - var arg = source.ParsePositiveInteger(); - - if (arg.HasValue) - { - return new Length(arg.Value, Length.Unit.None); - } - - return null; - } + private static ICssValue ParseIntegerCount(StringSource source) => source.ParsePositiveInteger(); private static ICssValue ParseRepeat(this StringSource source, Func parseCount, Func parseValue) { diff --git a/src/AngleSharp.Css/Parser/Micro/IdentParser.cs b/src/AngleSharp.Css/Parser/Micro/IdentParser.cs index 8899ce71..066ab9cd 100644 --- a/src/AngleSharp.Css/Parser/Micro/IdentParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/IdentParser.cs @@ -48,6 +48,21 @@ public static String ParseIdent(this StringSource source) return Start(source, current, buffer); } + /// + /// Parses a CSS identifier value. + /// + public static ICssValue ParseIdentAsValue(this StringSource source) + { + var value = source.ParseIdent(); + + if (value is not null) + { + return new CssIdentifierValue(value); + } + + return null; + } + /// /// Parses a CSS constant value from a given dictionary. /// @@ -59,7 +74,7 @@ public static ICssValue ParseConstant(this StringSource source, IDictionary(ident.ToLowerInvariant(), mode); + return mode as ICssValue ?? new CssConstantValue(ident.ToLowerInvariant(), mode); } source.BackTo(pos); @@ -69,13 +84,13 @@ public static ICssValue ParseConstant(this StringSource source, IDictionary /// Parses a CSS static value from a given dictionary. /// - public static Constant? ParseStatic(this StringSource source, IDictionary values) + public static CssConstantValue? ParseStatic(this StringSource source, IDictionary values) { var ident = source.ParseIdent(); if (ident != null && values.TryGetValue(ident, out T mode)) { - return new Constant(ident.ToLowerInvariant(), mode); + return new CssConstantValue(ident.ToLowerInvariant(), mode); } return null; @@ -155,13 +170,13 @@ public static ICssValue ParseFontFamily(this StringSource source) if (literal != null) { - return new Identifier(literal); + return new CssIdentifierValue(literal); } return null; } - return new Label(str); + return new CssStringValue(str); } /// @@ -291,7 +306,7 @@ private static String Rest(StringSource source, Char current, StringBuilder buff } } - private static readonly HashSet Animatables = new HashSet(StringComparer.OrdinalIgnoreCase) + private static readonly HashSet Animatables = new(StringComparer.OrdinalIgnoreCase) { "backdrop-filter", "background", diff --git a/src/AngleSharp.Css/Parser/Micro/MediaParser.cs b/src/AngleSharp.Css/Parser/Micro/MediaParser.cs index d549bf53..2a216d26 100644 --- a/src/AngleSharp.Css/Parser/Micro/MediaParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/MediaParser.cs @@ -4,6 +4,8 @@ namespace AngleSharp.Css.Parser using AngleSharp.Text; using System; using System.Collections.Generic; + using System.Diagnostics.Tracing; + using System.Linq; /// /// Represents extensions to for media values. @@ -31,20 +33,26 @@ public static IEnumerable ParseMedia(this StringSource source, IFeatu { if (current != Symbols.Comma) { - return null; - } + media[media.Count - 1] = null; - source.SkipCurrentAndSpaces(); - } + while (!source.IsDone && current != Symbols.Comma) + { + if (current == Symbols.RoundBracketOpen) + { + source.Next(); + source.TakeUntilClosed(); + } - var medium = source.ParseMedium(factory); + current = source.Next(); + } - if (medium == null) - { - return null; + continue; + } + + source.SkipCurrentAndSpaces(); } - media.Add(medium); + media.Add(source.ParseMedium(factory)); current = source.SkipSpacesAndComments(); } diff --git a/src/AngleSharp.Css/Parser/Micro/MediumParser.cs b/src/AngleSharp.Css/Parser/Micro/MediumParser.cs index b1222c37..2bf265c5 100644 --- a/src/AngleSharp.Css/Parser/Micro/MediumParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/MediumParser.cs @@ -1,9 +1,11 @@ namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; using AngleSharp.Text; using System; using System.Collections.Generic; + using System.Linq; /// /// Represents extensions to for medium (media type) values. @@ -22,95 +24,373 @@ internal static CssMedium Parse(String str, IFeatureValidatorFactory factory) /// public static CssMedium ParseMedium(this StringSource source, IFeatureValidatorFactory factory) { + // Syntax: + // = | + source.SkipSpacesAndComments(); - var ident = source.ParseIdent(); - var inverse = false; - var exclusive = false; - var type = String.Empty; + var medium = source.ParseMediaCondition() ?? source.ParseMediaType(); + + if (medium is not null) + { + foreach (var feature in medium.Features) + { + var validator = factory?.Create(feature.Name); + feature.AssociateValidator(validator); + } + } + + return medium; + } + + private static CssMedium ParseMediaCondition(this StringSource source) + { + // Syntax: + // = | [ * | * ] + + var medium = source.ParseMediaNot(); + + if (medium is null) + { + medium = source.ParseMediaInParens(); + + if (medium is not null) + { + source.SkipSpacesAndComments(); + var other = source.ParseMediaConnectorMultiple(CssKeywords.And) ?? source.ParseMediaConnectorMultiple(CssKeywords.Or); + + if (other is not null) + { + medium = new CssMedium(medium.Type, medium.IsInverse, medium.IsExclusive, medium.Features.Concat(other.Features), other.Connector); + } + } + } + + return medium; + } + + private static CssMedium ParseMediaConditionWithoutOr(this StringSource source) + { + // Syntax: + // = | * + + var medium = source.ParseMediaNot(); + + if (medium is null) + { + medium = source.ParseMediaInParens(); + + if (medium is not null) + { + do + { + source.SkipSpacesAndComments(); + var other = source.ParseMediaConnector(CssKeywords.And); + + if (other is null) + { + break; + } + + medium = new CssMedium(medium.Type, medium.IsInverse, medium.IsExclusive, medium.Features.Concat(other.Features), medium.Connector); + } + while (!source.IsDone); + } + } + + return medium; + } + + private static CssMedium ParseMediaNot(this StringSource source) + { + // Syntax: + // = not + + var pos = source.Index; - if (ident != null) + if (source.IsIdentifier(CssKeywords.Not)) { - if (ident.Isi(CssKeywords.Not)) + source.SkipSpacesAndComments(); + var medium = source.ParseMediaInParens(); + + if (medium is not null) { - inverse = true; source.SkipSpacesAndComments(); - ident = source.ParseIdent(); + return new CssMedium(medium.Type, !medium.IsInverse, medium.IsExclusive, medium.Features, medium.Connector); } - else if (ident.Isi(CssKeywords.Only)) + } + + source.BackTo(pos); + return null; + } + + private static CssMedium ParseMediaInParens(this StringSource source) + { + // Syntax: + // = ( ) | | + + if (source.Current == Symbols.RoundBracketOpen) + { + var pos = source.Index; + source.SkipCurrentAndSpaces(); + var medium = source.ParseMediaCondition(); + + if (medium is not null && source.Current == Symbols.RoundBracketClose) + { + source.SkipCurrentAndSpaces(); + return medium; + } + + source.BackTo(pos); + } + + return source.ParseMediaFeature() ?? source.ParseGeneralEnclosed(); + } + + private static CssMedium ParseMediaConnectorMultiple(this StringSource source, String connector) + { + // Syntax: + // [ * | * ] + + var part = source.ParseMediaConnector(connector); + + if (part is not null) + { + var features = Enumerable.Empty(); + + while (part is not null) { - exclusive = true; source.SkipSpacesAndComments(); - ident = source.ParseIdent(); + features = features.Concat(part.Features); + part = source.ParseMediaConnector(connector); } + + return new CssMedium(String.Empty, false, false, features, connector); } - if (ident != null) + return null; + } + + private static CssMedium ParseMediaConnector(this StringSource source, String connector) + { + // Syntax: + // = and + // or + // = or + + var pos = source.Index; + + if (source.IsIdentifier(connector)) { - type = ident; source.SkipSpacesAndComments(); - var position = source.Index; - ident = source.ParseIdent(); + var medium = source.ParseMediaInParens(); + + if (medium is not null) + { + source.SkipSpacesAndComments(); + return new CssMedium(medium.Type, medium.IsInverse, medium.IsExclusive, medium.Features, connector); + } + } + + source.BackTo(pos); + return null; + } + + private static CssMedium ParseMediaType(this StringSource source) + { + // Syntax: + // = [ not | only ]? [ and ]? + + var pos = source.Index; + var inverse = source.IsIdentifier(CssKeywords.Not); + source.SkipSpacesAndComments(); + var only = !inverse && source.IsIdentifier(CssKeywords.Only); + source.SkipSpacesAndComments(); + var type = source.ParseIdent(); + source.SkipSpacesAndComments(); - if (ident == null || !ident.Isi(CssKeywords.And)) + if (type is not null) + { + if (!source.IsIdentifier(CssKeywords.And)) { - source.BackTo(position); - return new CssMedium(type, inverse, exclusive); + return new CssMedium(type, inverse, only); } source.SkipSpacesAndComments(); + var features = source.ParseMediaConditionWithoutOr(); + + if (features is not null) + { + return new CssMedium(type, inverse, only, features.Features, features.Connector); + } + } - var features = new List(); + source.BackTo(pos); + return null; + } - do + private static CssMedium ParseMediaFeature(this StringSource source) + { + // Syntax: + // = ( [ | | ] ) + + if (source.Current == Symbols.RoundBracketOpen) { - var start = source.Current; + var pos = source.Index; source.SkipCurrentAndSpaces(); - var feature = ParseFeature(source); - var end = source.Current; + var feature = source.ParseMediaFeaturePlain() ?? source.ParseMediaFeatureBoolean() ?? source.ParseMediaFeatureRange(); - if (feature == null || - start != Symbols.RoundBracketOpen || - end != Symbols.RoundBracketClose) + if (feature is not null && source.Current == Symbols.RoundBracketClose) { - return null; + source.SkipCurrentAndSpaces(); + return new CssMedium(String.Empty, false, false, Enumerable.Repeat(feature, 1), CssKeywords.And); } - var validator = factory?.Create(feature.Name); - feature.AssociateValidator(validator); - features.Add(feature); + source.BackTo(pos); + } + + return null; + } + + private static CssMedium ParseGeneralEnclosed(this StringSource source) + { + // Syntax: + // = [ ) ] | ( ) + //TODO + return null; + } + + private static MediaFeature ParseMediaFeaturePlain(this StringSource source) + { + // Syntax: + // mf-plain> = : + + var pos = source.Index; + var ident = source.ParseIdent(); + source.SkipSpacesAndComments(); + + if (ident is not null && source.Current == Symbols.Colon) + { source.SkipCurrentAndSpaces(); - var position = source.Index; - ident = source.ParseIdent(); + var value = source.ParseMediaFeatureValue(); - if (ident == null || !ident.Isi(CssKeywords.And)) + if (value is not null) { - source.BackTo(position); - break; + source.SkipSpacesAndComments(); + return new MediaFeature(ident, value); } + } + + source.BackTo(pos); + return null; + } + + private static MediaFeature ParseMediaFeatureBoolean(this StringSource source) + { + // = + + var ident = source.ParseIdent(); + if (ident is not null) + { source.SkipSpacesAndComments(); + return new MediaFeature(ident); } - while (!source.IsDone); - return new CssMedium(type, inverse, exclusive, features); + return null; } - private static IMediaFeature ParseFeature(StringSource source) + private static MediaFeature ParseMediaFeatureRange(this StringSource source) { - var name = source.ParseIdent(); - var value = default(String); + // = + // '<' '='? | '>' '='? | '=' + // | '<' '='? | '>' '='? | '=' + // | '<' '='? '<' '='? + // | '>' '='? '>' '='? + + var pos = source.Index; + var name = source.ParseIdentAsValue() ?? source.ParseMediaFeatureValue(); - if (name != null) + if (name is not null) { + source.SkipSpacesAndComments(); + var op = ""; - if (source.Current == Symbols.Colon) + if (source.Current == Symbols.LessThan || source.Current == Symbols.GreaterThan) { + var c = source.Current; + + if (source.Next() == Symbols.Equality) + { + op = $"{c}="; + source.SkipCurrentAndSpaces(); + } + else + { + op = $"{c}"; + source.SkipSpacesAndComments(); + } + } + else if (source.Current == Symbols.Equality) + { + op = "="; source.SkipCurrentAndSpaces(); - value = source.TakeUntilClosed(); } + else + { + source.BackTo(pos); + return null; + } + + if (name is CssIdentifierValue) + { + var value = source.ParseMediaFeatureValue(); + + if (value is not null) + { + return new MediaFeature(name, value, op); + } + } + else + { + var value = source.ParseIdentAsValue(); + + if (value is not null) + { + return new MediaFeature(name, value, op); + } + } + + source.BackTo(pos); + } + + return null; + } + + private static ICssValue ParseMediaFeatureValue(this StringSource source) + { + // Syntax: + // = | | | - return new MediaFeature(name, value); + var ident = source.ParseIdent(); + + if (ident is not null) + { + return new CssIdentifierValue(ident); + } + + var ratio = source.ParseRatio(); + + if (ratio.HasValue) + { + return ratio.Value; + } + + var unit = source.ParseAtomicExpression(); + + if (unit is not null) + { + return unit; } return null; diff --git a/src/AngleSharp.Css/Parser/Micro/NumberParser.cs b/src/AngleSharp.Css/Parser/Micro/NumberParser.cs index bdb94895..b3ceafa4 100644 --- a/src/AngleSharp.Css/Parser/Micro/NumberParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/NumberParser.cs @@ -1,5 +1,6 @@ namespace AngleSharp.Css.Parser { + using AngleSharp.Css.Values; using AngleSharp.Text; using System; using System.Globalization; @@ -13,7 +14,7 @@ public static class NumberParser /// /// Parses the number (double) value. /// - public static Double? ParseNumber(this StringSource source) + public static CssNumberValue? ParseNumber(this StringSource source) { var unit = source.ParseUnit(); @@ -21,7 +22,7 @@ public static class NumberParser unit.Dimension == String.Empty && Double.TryParse(unit.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { - return result; + return new CssNumberValue(result); } return null; @@ -30,7 +31,7 @@ public static class NumberParser /// /// Parses the ratio (double) value. /// - public static Double? ParseRatio(this StringSource source) + public static CssRatioValue? ParseRatio(this StringSource source) { var pos = source.Index; var top = source.ParseNumber(); @@ -39,7 +40,7 @@ public static class NumberParser if (top.HasValue && bottom.HasValue && c == Symbols.Solidus) { - return top.Value / bottom.Value; + return new CssRatioValue(top.Value, bottom.Value); } source.BackTo(pos); @@ -49,12 +50,12 @@ public static class NumberParser /// /// Parses the integer (double) value. /// - public static Double? ParseNaturalNumber(this StringSource source) + public static CssNumberValue? ParseNaturalNumber(this StringSource source) { var pos = source.Index; var element = source.ParseNumber(); - if (element.HasValue && element.Value >= 0f) + if (element.HasValue && element.Value.Value >= 0f) { return element; } @@ -66,12 +67,12 @@ public static class NumberParser /// /// Parses the natural number (double) value. /// - public static Double? ParseGreaterOrEqualOneNumber(this StringSource source) + public static CssNumberValue? ParseGreaterOrEqualOneNumber(this StringSource source) { var pos = source.Index; var element = source.ParseNumber(); - if (element.HasValue && element.Value >= 1f) + if (element.HasValue && element.Value.Value >= 1f) { return element; } @@ -83,12 +84,12 @@ public static class NumberParser /// /// Parses the natural integer (int) value. /// - public static Int32? ParseNaturalInteger(this StringSource source) + public static CssIntegerValue? ParseNaturalInteger(this StringSource source) { var pos = source.Index; var element = source.ParseInteger(); - if (element.HasValue && element.Value >= 0) + if (element.HasValue && element.Value.IntValue >= 0) { return element; } @@ -100,12 +101,12 @@ public static class NumberParser /// /// Parses the positive integer (int) value. /// - public static Int32? ParsePositiveInteger(this StringSource source) + public static CssIntegerValue? ParsePositiveInteger(this StringSource source) { var pos = source.Index; var element = source.ParseInteger(); - if (element.HasValue && element.Value > 0) + if (element.HasValue && element.Value.IntValue > 0) { return element; } @@ -117,12 +118,12 @@ public static class NumberParser /// /// Parses the weight (int) value. /// - public static Int32? ParseWeightInteger(this StringSource source) + public static CssIntegerValue? ParseWeightInteger(this StringSource source) { var pos = source.Index; var element = source.ParsePositiveInteger(); - if (element.HasValue && IsWeight(element.Value)) + if (element.HasValue && IsWeight(element.Value.IntValue)) { return element; } @@ -134,14 +135,19 @@ public static class NumberParser /// /// Parses the binary (int) value. /// - public static Int32? ParseBinary(this StringSource source) + public static CssIntegerValue? ParseBinary(this StringSource source) { var pos = source.Index; var element = source.ParseInteger(); - if (element.HasValue && (element.Value == 0 || element.Value == 1)) + if (element.HasValue) { - return element; + var val = element.Value.IntValue; + + if (val == 0 || val == 1) + { + return element; + } } source.BackTo(pos); @@ -151,7 +157,7 @@ public static class NumberParser /// /// Parses the integer (int) value. /// - public static Int32? ParseInteger(this StringSource source) + public static CssIntegerValue? ParseInteger(this StringSource source) { var unit = source.ParseUnit(); @@ -161,7 +167,7 @@ public static class NumberParser if (Int32.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { - return result; + return new CssIntegerValue(result); } var negative = value.StartsWith("-"); @@ -169,7 +175,7 @@ public static class NumberParser if (allNumbers) { - return negative ? Int32.MinValue : Int32.MaxValue; + return new CssIntegerValue(negative ? Int32.MinValue : Int32.MaxValue); } } diff --git a/src/AngleSharp.Css/Parser/Micro/PointParser.cs b/src/AngleSharp.Css/Parser/Micro/PointParser.cs index 66fbcf7f..d60b4364 100644 --- a/src/AngleSharp.Css/Parser/Micro/PointParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/PointParser.cs @@ -28,7 +28,7 @@ public static ICssValue ParsePointY(this StringSource source) => private static ICssValue ParsePointDir(this StringSource source, Predicate checkKeyword) { var pos = source.Index; - var x = new Length(50f, Length.Unit.Percent); + var x = new CssLengthValue(50f, CssLengthValue.Unit.Percent); var l = source.ParseIdent(); if (l == null) @@ -69,11 +69,11 @@ public static CssOriginValue ParseOrigin(this StringSource source) /// /// Parses a point value. /// - public static Point? ParsePoint(this StringSource source) + public static CssPoint2D? ParsePoint(this StringSource source) { var pos = source.Index; - var x = new Length(50f, Length.Unit.Percent); - var y = new Length(50f, Length.Unit.Percent); + var x = new CssLengthValue(50f, CssLengthValue.Unit.Percent); + var y = new CssLengthValue(50f, CssLengthValue.Unit.Percent); var l = source.ParseIdent(); source.SkipSpacesAndComments(); var r = source.ParseIdent(); @@ -91,7 +91,7 @@ public static CssOriginValue ParseOrigin(this StringSource source) { x = KeywordToLength(l).Value; y = KeywordToLength(r).Value; - return new Point(x, y); + return new CssPoint2D(x, y); } } else if (l != null) @@ -101,12 +101,12 @@ public static CssOriginValue ParseOrigin(this StringSource source) if (IsHorizontal(l)) { x = KeywordToLength(l).Value; - return new Point(x, s ?? y); + return new CssPoint2D(x, s ?? y); } else if (IsVertical(l)) { y = KeywordToLength(l).Value; - return new Point(s ?? x, y); + return new CssPoint2D(s ?? x, y); } } else @@ -117,7 +117,7 @@ public static CssOriginValue ParseOrigin(this StringSource source) if (s != null) { - return new Point(f ?? x, s ?? y); + return new CssPoint2D(f ?? x, s ?? y); } else if (f != null) { @@ -126,22 +126,22 @@ public static CssOriginValue ParseOrigin(this StringSource source) if (r == null) { - return new Point(f, y); + return new CssPoint2D(f, y); } else if (IsVertical(r)) { y = KeywordToLength(r).Value; - return new Point(f ?? x, y); + return new CssPoint2D(f ?? x, y); } else if (IsHorizontal(r)) { x = KeywordToLength(r).Value; - return new Point(x, f ?? y); + return new CssPoint2D(x, f ?? y); } else { source.BackTo(pos); - return new Point(f ?? x, y); + return new CssPoint2D(f ?? x, y); } } } @@ -167,7 +167,7 @@ public static CssBackgroundSizeValue ParseSize(this StringSource source) { var w = source.ParseDistanceOrCalc(); - if (w == null && !source.IsIdentifier(CssKeywords.Auto)) + if (w is null && !source.IsIdentifier(CssKeywords.Auto)) { return null; } @@ -175,12 +175,12 @@ public static CssBackgroundSizeValue ParseSize(this StringSource source) source.SkipSpacesAndComments(); var h = source.ParseDistanceOrCalc(); - if (h == null) + if (h is null) { source.IsIdentifier(CssKeywords.Auto); } - return new CssBackgroundSizeValue(w ?? Length.Auto, h ?? Length.Auto); + return new CssBackgroundSizeValue(w ?? CssLengthValue.Auto, h ?? CssLengthValue.Auto); } } @@ -190,19 +190,19 @@ private static Boolean IsHorizontal(String str) => private static Boolean IsVertical(String str) => str.Isi(CssKeywords.Top) || str.Isi(CssKeywords.Bottom) || str.Isi(CssKeywords.Center); - private static Length? KeywordToLength(String keyword) + private static CssLengthValue? KeywordToLength(String keyword) { if (keyword.Isi(CssKeywords.Left) || keyword.Isi(CssKeywords.Top)) { - return new Length(0f, Length.Unit.Percent); + return new CssLengthValue(0f, CssLengthValue.Unit.Percent); } else if (keyword.Isi(CssKeywords.Right) || keyword.Isi(CssKeywords.Bottom)) { - return new Length(100f, Length.Unit.Percent); + return new CssLengthValue(100f, CssLengthValue.Unit.Percent); } else if (keyword.Isi(CssKeywords.Center)) { - return new Length(50f, Length.Unit.Percent); + return new CssLengthValue(50f, CssLengthValue.Unit.Percent); } return null; diff --git a/src/AngleSharp.Css/Parser/Micro/ShadowParser.cs b/src/AngleSharp.Css/Parser/Micro/ShadowParser.cs index 8aab19bc..1258531d 100644 --- a/src/AngleSharp.Css/Parser/Micro/ShadowParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/ShadowParser.cs @@ -20,7 +20,7 @@ public static CssShadowValue ParseShadow(this StringSource source) var offsetY = default(ICssValue); var blurRadius = default(ICssValue); var spreadRadius = default(ICssValue); - var color = default(Color?); + var color = default(CssColorValue?); var pos = start; do diff --git a/src/AngleSharp.Css/Parser/Micro/StringParser.cs b/src/AngleSharp.Css/Parser/Micro/StringParser.cs index 9015f7f4..358be517 100644 --- a/src/AngleSharp.Css/Parser/Micro/StringParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/StringParser.cs @@ -16,13 +16,12 @@ public static String ParseString(this StringSource source) { var current = source.Current; - switch (current) + return current switch { - case Symbols.DoubleQuote: return DoubleQuoted(source); - case Symbols.SingleQuote: return SingleQuoted(source); - } - - return null; + Symbols.DoubleQuote => DoubleQuoted(source), + Symbols.SingleQuote => SingleQuoted(source), + _ => null, + }; } private static String DoubleQuoted(StringSource source) diff --git a/src/AngleSharp.Css/Parser/Micro/SymbolsParser.cs b/src/AngleSharp.Css/Parser/Micro/SymbolsParser.cs new file mode 100644 index 00000000..7b81abf0 --- /dev/null +++ b/src/AngleSharp.Css/Parser/Micro/SymbolsParser.cs @@ -0,0 +1,63 @@ +namespace AngleSharp.Css.Parser +{ + using AngleSharp.Css.Values; + using AngleSharp.Text; + using System; + using System.Collections.Generic; + + /// + /// Represents extensions to for symbols() values. + /// + public static class SymbolsParser + { + /// + /// Parse a CSS symbols() value. + /// + public static CssSymbolsValue ParseSymbols(this StringSource source) + { + var start = source.Index; + + if (source.IsFunction(FunctionNames.Symbols)) + { + source.SkipSpacesAndComments(); + var pos = source.Index; + var type = ValueConverters.SymbolsTypeConverter.Convert(source); + var entries = new List(); + + if (type is null) + { + source.BackTo(pos); + } + else + { + source.SkipSpacesAndComments(); + } + + do + { + var entry = StringParser.ParseString(source); + + if (entry is null) + { + break; + } + + entries.Add(entry); + + var c = source.SkipSpacesAndComments(); + + if (c == Symbols.RoundBracketClose) + { + source.Next(); + return new CssSymbolsValue(type, entries); + } + } + while (true); + + source.BackTo(start); + } + + return null; + } + } +} diff --git a/src/AngleSharp.Css/Parser/Micro/TimingFunctionParser.cs b/src/AngleSharp.Css/Parser/Micro/TimingFunctionParser.cs index 013997bb..821a93db 100644 --- a/src/AngleSharp.Css/Parser/Micro/TimingFunctionParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/TimingFunctionParser.cs @@ -10,7 +10,7 @@ namespace AngleSharp.Css.Parser /// public static class TimingFunctionParser { - private static readonly Dictionary> TimingFunctions = new Dictionary>(StringComparer.OrdinalIgnoreCase) + private static readonly Dictionary> TimingFunctions = new(StringComparer.OrdinalIgnoreCase) { { FunctionNames.Steps, ParseSteps }, { FunctionNames.CubicBezier, ParseCubicBezier }, diff --git a/src/AngleSharp.Css/Parser/Micro/TransformParser.cs b/src/AngleSharp.Css/Parser/Micro/TransformParser.cs index ae3cbb5e..68b81928 100644 --- a/src/AngleSharp.Css/Parser/Micro/TransformParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/TransformParser.cs @@ -11,7 +11,7 @@ namespace AngleSharp.Css.Parser /// public static class TransformParser { - private static readonly Dictionary> TransformFunctions = new Dictionary>(StringComparer.OrdinalIgnoreCase) + private static readonly Dictionary> TransformFunctions = new(StringComparer.OrdinalIgnoreCase) { { FunctionNames.Skew, ParseSkew2d }, { FunctionNames.SkewX, ParseSkewX }, @@ -69,7 +69,7 @@ public static ICssTransformFunctionValue ParseTransform(this StringSource source private static CssSkewValue ParseSkew2d(StringSource source) { ICssValue x = source.ParseAngleOrCalc(); - ICssValue y = Angle.Zero; + ICssValue y = CssAngleValue.Zero; var c = source.SkipGetSkip(); if (x == null) @@ -148,7 +148,7 @@ private static CssMatrixValue ParseMatrix3d(StringSource source) /// private static CssMatrixValue ParseMatrix(StringSource source, Int32 count) { - var numbers = new Double[count]; + var numbers = new ICssValue[count]; var num = source.ParseNumber(); if (num.HasValue) @@ -184,7 +184,7 @@ private static CssMatrixValue ParseMatrix(StringSource source, Int32 count) /// private static CssRotateValue ParseRotate2d(StringSource source) { - return ParseRotate(source, Double.NaN, Double.NaN, Double.NaN); + return ParseRotate(source, null, null, null); } /// @@ -214,7 +214,7 @@ private static CssRotateValue ParseRotate3d(StringSource source) /// private static CssRotateValue ParseRotateX(StringSource source) { - return ParseRotate(source, 1f, 0f, 0f); + return ParseRotate(source, CssNumberValue.One, null, null); } /// @@ -223,7 +223,7 @@ private static CssRotateValue ParseRotateX(StringSource source) /// private static CssRotateValue ParseRotateY(StringSource source) { - return ParseRotate(source, 0f, 1f, 0f); + return ParseRotate(source, null, CssNumberValue.One, null); } /// @@ -232,21 +232,21 @@ private static CssRotateValue ParseRotateY(StringSource source) /// private static CssRotateValue ParseRotateZ(StringSource source) { - return ParseRotate(source, 0f, 0f, 1f); + return ParseRotate(source, null, null, CssNumberValue.One); } /// /// A broad variety of rotate transforms. /// http://www.w3.org/TR/css3-transforms/#funcdef-rotate3d /// - private static CssRotateValue ParseRotate(StringSource source, Double x, Double y, Double z) + private static CssRotateValue ParseRotate(StringSource source, ICssValue x, ICssValue y, ICssValue z) { var angle = source.ParseAngleOrCalc(); var f = source.SkipGetSkip(); if (angle != null && f == Symbols.RoundBracketClose) { - return new CssRotateValue(x, z, y, angle); + return new CssRotateValue(x, y, z, angle); } return null; @@ -263,7 +263,7 @@ private static CssScaleValue ParseScale2d(StringSource source) if (x.HasValue) { - var y = default(Double?); + var y = default(ICssValue); if (f == Symbols.Comma) { @@ -273,7 +273,7 @@ private static CssScaleValue ParseScale2d(StringSource source) if (f == Symbols.RoundBracketClose) { - return new CssScaleValue(x.Value, y ?? x.Value, 1.0); + return new CssScaleValue(x.Value, y ?? x.Value, null); } } @@ -291,15 +291,15 @@ private static CssScaleValue ParseScale3d(StringSource source) if (x.HasValue) { - var y = default(Double?); - var z = default(Double?); + var y = default(ICssValue); + var z = default(ICssValue); if (f == Symbols.Comma) { y = source.ParseNumber(); f = source.SkipGetSkip(); - if (!y.HasValue) + if (y is null) { return null; } @@ -331,7 +331,7 @@ private static CssScaleValue ParseScaleX(StringSource source) if (x.HasValue && f == Symbols.RoundBracketClose) { - return new CssScaleValue(x.Value, 1.0, 1.0); + return new CssScaleValue(x.Value, null, null); } return null; @@ -348,7 +348,7 @@ private static CssScaleValue ParseScaleY(StringSource source) if (y.HasValue && f == Symbols.RoundBracketClose) { - return new CssScaleValue(1.0, y.Value, 1.0); + return new CssScaleValue(null, y.Value, null); } return null; @@ -365,7 +365,7 @@ private static CssScaleValue ParseScaleZ(StringSource source) if (z.HasValue && f == Symbols.RoundBracketClose) { - return new CssScaleValue(1.0, 1.0, z.Value); + return new CssScaleValue(null, null, z.Value); } return null; diff --git a/src/AngleSharp.Css/Parser/Micro/UnitParser.cs b/src/AngleSharp.Css/Parser/Micro/UnitParser.cs index 86079949..f1f738a2 100644 --- a/src/AngleSharp.Css/Parser/Micro/UnitParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/UnitParser.cs @@ -32,7 +32,7 @@ public static ICssValue ParseAutoLength(this StringSource source) { if (source.IsIdentifier(CssKeywords.Auto)) { - return Length.Auto; + return CssLengthValue.Auto; } return null; @@ -41,11 +41,11 @@ public static ICssValue ParseAutoLength(this StringSource source) /// /// Parses a length value. /// - public static Length? ParseNormalLength(this StringSource source) + public static CssLengthValue? ParseNormalLength(this StringSource source) { if (source.IsIdentifier(CssKeywords.Normal)) { - return Length.Normal; + return CssLengthValue.Normal; } return null; @@ -56,7 +56,7 @@ public static ICssValue ParseAutoLength(this StringSource source) /// public static ICssValue ParseBorderWidth(this StringSource source) => source.ParseLengthOrCalc() ?? - source.ParsePercentOrNumber() ?? + source.ParsePercentOrNumberForLength() ?? source.ParseAutoLength(); /// @@ -71,7 +71,7 @@ public static ICssValue ParseLineWidth(this StringSource source) => /// public static ICssValue ParseLineHeight(this StringSource source) => source.ParseLengthOrCalc() ?? - source.ParsePercentOrNumber() ?? + source.ParsePercentOrNumberForLength() ?? source.ParseNormalLength(); /// @@ -95,20 +95,44 @@ public static ICssValue ParseLineHeight(this StringSource source) => /// /// Parses a percent or number value. /// - public static Length? ParsePercentOrNumber(this StringSource source) + private static CssLengthValue? ParsePercentOrNumberForLength(this StringSource source) { var pos = source.Index; var test = source.ParseUnit(); - if (test != null && Double.TryParse(test.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var value)) + if (test is not null && test.Value.CssNumber(out var value)) { if (test.Dimension == "%") { - return new Length(value, Length.Unit.Percent); + return new CssLengthValue(value, CssLengthValue.Unit.Percent); } else if (test.Dimension.Length == 0) { - return new Length(value, Length.Unit.None); + return new CssLengthValue(value, CssLengthValue.Unit.None); + } + } + + source.BackTo(pos); + return null; + } + + /// + /// Parses a percent or number value. + /// + public static CssPercentageValue? ParsePercentOrNumber(this StringSource source) + { + var pos = source.Index; + var test = source.ParseUnit(); + + if (test is not null && test.Value.CssNumber(out var value)) + { + if (test.Dimension == "%") + { + return new CssPercentageValue(value, CssPercentageValue.Unit.Percent); + } + else if (test.Dimension.Length == 0) + { + return new CssPercentageValue(value, CssPercentageValue.Unit.None); } } @@ -119,19 +143,19 @@ public static ICssValue ParseLineHeight(this StringSource source) => /// /// Parses an angle value. /// - public static Angle? ParseAngle(this StringSource source) + public static CssAngleValue? ParseAngle(this StringSource source) { var pos = source.Index; var test = source.ParseUnit(); if (test != null) { - var unit = Angle.GetUnit(test.Dimension); + var unit = CssAngleValue.GetUnit(test.Dimension); - if (unit != Angle.Unit.None && + if (unit != CssAngleValue.Unit.None && Double.TryParse(test.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var value)) { - return new Angle(value, unit); + return new CssAngleValue(value, unit); } source.BackTo(pos); @@ -149,19 +173,19 @@ public static ICssValue ParseAngleOrCalc(this StringSource source) => /// /// Parses a frequency value. /// - public static Frequency? ParseFrequency(this StringSource source) + public static CssFrequencyValue? ParseFrequency(this StringSource source) { var pos = source.Index; var test = source.ParseUnit(); if (test != null) { - var unit = Frequency.GetUnit(test.Dimension); + var unit = CssFrequencyValue.GetUnit(test.Dimension); - if (unit != Frequency.Unit.None && + if (unit != CssFrequencyValue.Unit.None && Double.TryParse(test.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var value)) { - return new Frequency(value, unit); + return new CssFrequencyValue(value, unit); } source.BackTo(pos); @@ -192,12 +216,12 @@ public static ICssValue ParseTrackBreadth(this StringSource source, Boolean flex } else if (test != null) { - var unit = Fraction.GetUnit(test.Dimension); + var unit = CssFractionValue.GetUnit(test.Dimension); - if (flexible && unit != Fraction.Unit.None && + if (flexible && unit != CssFractionValue.Unit.None && Double.TryParse(test.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var value)) { - return new Fraction(value, unit); + return new CssFractionValue(value, unit); } } else @@ -208,15 +232,15 @@ public static ICssValue ParseTrackBreadth(this StringSource source, Boolean flex { if (ident.Isi(CssKeywords.MinContent)) { - return new Identifier(CssKeywords.MinContent); + return new CssIdentifierValue(CssKeywords.MinContent); } else if (ident.Isi(CssKeywords.MaxContent)) { - return new Identifier(CssKeywords.MaxContent); + return new CssIdentifierValue(CssKeywords.MaxContent); } else if (ident.Isi(CssKeywords.Auto)) { - return new Identifier(CssKeywords.Auto); + return new CssIdentifierValue(CssKeywords.Auto); } } } @@ -228,7 +252,7 @@ public static ICssValue ParseTrackBreadth(this StringSource source, Boolean flex /// /// Parses a distance value. /// - public static Length? ParseDistance(this StringSource source) + public static CssLengthValue? ParseDistance(this StringSource source) { var pos = source.Index; var test = source.ParseUnit(); @@ -251,13 +275,13 @@ public static ICssValue ParseDistanceOrCalc(this StringSource source) => /// /// Parses a length value. /// - public static Length? ParseLength(this StringSource source) + public static CssLengthValue? ParseLength(this StringSource source) { var pos = source.Index; var test = source.ParseUnit(); var length = GetLength(test); - if (!length.HasValue || length.Value.Type == Length.Unit.Percent) + if (!length.HasValue || length.Value.Type == CssLengthValue.Unit.Percent) { source.BackTo(pos); return null; @@ -275,19 +299,19 @@ public static ICssValue ParseLengthOrCalc(this StringSource source) => /// /// Parses a resolution value. /// - public static Resolution? ParseResolution(this StringSource source) + public static CssResolutionValue? ParseResolution(this StringSource source) { var pos = source.Index; var test = source.ParseUnit(); if (test != null) { - var unit = Resolution.GetUnit(test.Dimension); + var unit = CssResolutionValue.GetUnit(test.Dimension); - if (unit != Resolution.Unit.None && + if (unit != CssResolutionValue.Unit.None && Double.TryParse(test.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var value)) { - return new Resolution(value, unit); + return new CssResolutionValue(value, unit); } source.BackTo(pos); @@ -299,19 +323,19 @@ public static ICssValue ParseLengthOrCalc(this StringSource source) => /// /// Parses a time value. /// - public static Time? ParseTime(this StringSource source) + public static CssTimeValue? ParseTime(this StringSource source) { var pos = source.Index; var test = source.ParseUnit(); if (test != null) { - var unit = Time.GetUnit(test.Dimension); + var unit = CssTimeValue.GetUnit(test.Dimension); - if (unit != Time.Unit.None && + if (unit != CssTimeValue.Unit.None && Double.TryParse(test.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var value)) { - return new Time(value, unit); + return new CssTimeValue(value, unit); } source.BackTo(pos); @@ -325,17 +349,17 @@ public static ICssValue ParseLengthOrCalc(this StringSource source) => /// public static ICssValue ParseTimeOrCalc(this StringSource source) => source.ParseTime().OrCalc(source); - private static Length? GetLength(Unit test) + private static CssLengthValue? GetLength(Unit test) { if (test != null && Double.TryParse(test.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var value)) { - var unit = Length.Unit.Px; + var unit = CssLengthValue.Unit.Px; if ((test.Dimension == String.Empty && test.Value == "0") || - (unit = Length.GetUnit(test.Dimension)) != Length.Unit.None) + (unit = CssLengthValue.GetUnit(test.Dimension)) != CssLengthValue.Unit.None) { - return new Length(value, unit); + return new CssLengthValue(value, unit); } } diff --git a/src/AngleSharp.Css/RenderTree/ElementRenderNode.cs b/src/AngleSharp.Css/RenderTree/ElementRenderNode.cs index ad6363f3..6e42cf19 100644 --- a/src/AngleSharp.Css/RenderTree/ElementRenderNode.cs +++ b/src/AngleSharp.Css/RenderTree/ElementRenderNode.cs @@ -3,20 +3,27 @@ namespace AngleSharp.Css.RenderTree using AngleSharp.Css.Dom; using AngleSharp.Dom; using System.Collections.Generic; - using System.Linq; - class ElementRenderNode : IRenderNode + sealed class ElementRenderNode : IRenderNode { - public INode Ref { get; set; } + public ElementRenderNode(IElement reference, IEnumerable children, ICssStyleDeclaration specifiedStyle, ICssStyleDeclaration computedStyle) + { + Ref = reference; + Children = children; + SpecifiedStyle = specifiedStyle; + ComputedStyle = computedStyle; + } - public IEnumerable Children { get; set; } = Enumerable.Empty(); + public IElement Ref { get; } - public ICssStyleDeclaration SpecifiedStyle { get; set; } + INode IRenderNode.Ref => Ref; - public ICssStyleDeclaration ComputedStyle { get; set; } + public IEnumerable Children { get; } - public RenderValues UsedValue { get; set; } + public IRenderNode? Parent { get; set; } - public RenderValues ActualValue { get; set; } + public ICssStyleDeclaration SpecifiedStyle { get; } + + public ICssStyleDeclaration ComputedStyle { get; } } } diff --git a/src/AngleSharp.Css/RenderTree/RenderTreeBuilder.cs b/src/AngleSharp.Css/RenderTree/RenderTreeBuilder.cs index fc4e3f64..2aa32754 100644 --- a/src/AngleSharp.Css/RenderTree/RenderTreeBuilder.cs +++ b/src/AngleSharp.Css/RenderTree/RenderTreeBuilder.cs @@ -1,12 +1,15 @@ namespace AngleSharp.Css.RenderTree { using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; using AngleSharp.Dom; + using System; using System.Collections.Generic; using System.Linq; - class RenderTreeBuilder + sealed class RenderTreeBuilder { + private readonly IBrowsingContext _context; private readonly IWindow _window; private readonly IEnumerable _defaultSheets; private readonly IRenderDevice _device; @@ -15,8 +18,9 @@ public RenderTreeBuilder(IWindow window, IRenderDevice device = null) { var ctx = window.Document.Context; var defaultStyleSheetProvider = ctx.GetServices(); - _device = device ?? ctx.GetService(); - _defaultSheets = defaultStyleSheetProvider.Select(m => m.Default).Where(m => m != null); + _context = ctx; + _device = device ?? ctx.GetService() ?? throw new ArgumentNullException(nameof(device)); + _defaultSheets = defaultStyleSheetProvider.Select(m => m.Default).Where(m => m is not null); _window = window; } @@ -24,40 +28,166 @@ public IRenderNode RenderDocument() { var document = _window.Document; var currentSheets = document.GetStyleSheets().OfType(); - var stylesheets = _defaultSheets.Concat(currentSheets); + var stylesheets = _defaultSheets.Concat(currentSheets).ToList(); var collection = new StyleCollection(stylesheets, _device); - return RenderElement(document.DocumentElement, collection); + var rootStyle = collection.ComputeCascadedStyle(document.DocumentElement); + var rootFontSize = ((CssLengthValue?)rootStyle.GetProperty(PropertyNames.FontSize)?.RawValue)?.Value ?? 16; + return RenderElement(rootFontSize, document.DocumentElement, collection); } - private ElementRenderNode RenderElement(IElement reference, StyleCollection collection, ICssStyleDeclaration parent = null) + private ElementRenderNode RenderElement(double rootFontSize, IElement reference, StyleCollection collection, ICssStyleDeclaration? parent = null) { - var style = collection.ComputeCascadedStyle(reference, parent); + var style = collection.ComputeCascadedStyle(reference); + var computedStyle = Compute(rootFontSize, style, parent); + if (parent != null) + { + computedStyle.UpdateDeclarations(parent); + } var children = new List(); foreach (var child in reference.ChildNodes) { if (child is IText text) { - children.Add(RenderText(text, collection)); + children.Add(RenderText(text)); } - else if (child is IElement element) + else if (child is IElement element) { - children.Add(RenderElement(element, collection, style)); + children.Add(RenderElement(rootFontSize, element, collection, computedStyle)); } } - return new ElementRenderNode + // compute unitless line-height after rendering children + if (computedStyle.GetProperty(PropertyNames.LineHeight).RawValue is CssLengthValue { Type: CssLengthValue.Unit.None } unitlessLineHeight) { - Ref = reference, - SpecifiedStyle = style, - ComputedStyle = style.Compute(_device), - Children = children, - }; + var fontSize = computedStyle.GetProperty(PropertyNames.FontSize).RawValue is CssLengthValue { Type: CssLengthValue.Unit.Px } fontSizeLength ? fontSizeLength.Value : rootFontSize; + var pixelValue = unitlessLineHeight.Value * fontSize; + var computedLineHeight = new CssLengthValue(pixelValue, CssLengthValue.Unit.Px); + + // create a new property because SetProperty would change the parent value + var lineHeightProperty = _context.CreateProperty(PropertyNames.LineHeight); + lineHeightProperty.RawValue = computedLineHeight; + computedStyle.SetDeclarations(new[] { lineHeightProperty }); + } + + var node = new ElementRenderNode(reference, children, style, computedStyle); + + foreach (var child in children) + { + if (child is ElementRenderNode elementChild) + { + elementChild.Parent = node; + } + else if (child is TextRenderNode textChild) + { + textChild.Parent = node; + } + else + { + throw new InvalidOperationException(); + } + } + + return node; } - private IRenderNode RenderText(IText text, StyleCollection collection) => new TextRenderNode + private IRenderNode RenderText(IText text) => new TextRenderNode(text); + + private CssStyleDeclaration Compute(Double rootFontSize, ICssStyleDeclaration style, ICssStyleDeclaration? parentStyle) { - Ref = text, - }; + var computedStyle = new CssStyleDeclaration(_context); + var parentFontSize = ((CssLengthValue?)parentStyle?.GetProperty(PropertyNames.FontSize)?.RawValue)?.ToPixel(_device) ?? rootFontSize; + var fontSize = parentFontSize; + // compute font-size first because other properties may depend on it + if (style.GetProperty(PropertyNames.FontSize) is { RawValue: not null } fontSizeProperty) + { + fontSize = GetFontSizeInPixels(fontSizeProperty.RawValue); + } + var declarations = style.OfType().Select(property => + { + var name = property.Name; + var value = property.RawValue; + if (name == PropertyNames.FontSize) + { + // font-size was already computed + value = new CssLengthValue(fontSize, CssLengthValue.Unit.Px); + } + else if (value is CssLengthValue { IsAbsolute: true, Type: not CssLengthValue.Unit.Px } absoluteLength) + { + value = new CssLengthValue(absoluteLength.ToPixel(_device), CssLengthValue.Unit.Px); + } + else if (value is CssLengthValue { Type: CssLengthValue.Unit.Percent } percentLength) + { + if (name == PropertyNames.VerticalAlign || name == PropertyNames.LineHeight) + { + var pixelValue = percentLength.Value / 100 * fontSize; + value = new CssLengthValue(pixelValue, CssLengthValue.Unit.Px); + } + else + { + // TODO: compute for other properties that should be absolute + } + } + else if (value is CssLengthValue { IsRelative: true, Type: not CssLengthValue.Unit.None } relativeLength) + { + var pixelValue = relativeLength.Type switch + { + CssLengthValue.Unit.Em => relativeLength.Value * fontSize, + CssLengthValue.Unit.Rem => relativeLength.Value * rootFontSize, + _ => relativeLength.ToPixel(_device), + }; + value = new CssLengthValue(pixelValue, CssLengthValue.Unit.Px); + } + + return new CssProperty(name, property.Converter, property.Flags, value, property.IsImportant); + }); + + computedStyle.SetDeclarations(declarations); + + return computedStyle; + + Double GetFontSizeInPixels(ICssValue value) => value switch + { + CssConstantValue constLength when constLength.CssText == CssKeywords.XxSmall => 9D / 16 * rootFontSize, + CssConstantValue constLength when constLength.CssText == CssKeywords.XSmall => 10D / 16 * rootFontSize, + CssConstantValue constLength when constLength.CssText == CssKeywords.Small => 13D / 16 * rootFontSize, + CssConstantValue constLength when constLength.CssText == CssKeywords.Medium => 16D / 16 * rootFontSize, + CssConstantValue constLength when constLength.CssText == CssKeywords.Large => 18D / 16 * rootFontSize, + CssConstantValue constLength when constLength.CssText == CssKeywords.XLarge => 24D / 16 * rootFontSize, + CssConstantValue constLength when constLength.CssText == CssKeywords.XxLarge => 32D / 16 * rootFontSize, + CssConstantValue constLength when constLength.CssText == CssKeywords.XxxLarge => 48D / 16 * rootFontSize, + CssConstantValue constLength when constLength.CssText == CssKeywords.Smaller => ComputeRelativeFontSize(constLength), + CssConstantValue constLength when constLength.CssText == CssKeywords.Larger => ComputeRelativeFontSize(constLength), + CssLengthValue { Type: CssLengthValue.Unit.Px } length => length.Value, + CssLengthValue { IsAbsolute: true } length => length.ToPixel(_device), + CssLengthValue { Type: CssLengthValue.Unit.Vh or CssLengthValue.Unit.Vw or CssLengthValue.Unit.Vmax or CssLengthValue.Unit.Vmin } length => length.ToPixel(_device), + CssLengthValue { IsRelative: true } length => ComputeRelativeFontSize(length), + ICssSpecialValue specialValue when specialValue.CssText == CssKeywords.Inherit || specialValue.CssText == CssKeywords.Unset => parentFontSize, + ICssSpecialValue specialValue when specialValue.CssText == CssKeywords.Initial => rootFontSize, + _ => throw new InvalidOperationException("Font size must be a length"), + }; + + Double ComputeRelativeFontSize(ICssValue value) + { + var ancestorValue = parentStyle?.GetProperty(PropertyNames.FontSize)?.RawValue; + var ancestorPixels = ancestorValue switch + { + CssLengthValue { IsAbsolute: true } ancestorLength => ancestorLength.ToPixel(_device), + null => rootFontSize, + _ => throw new InvalidOperationException(), + }; + + // set a minimum size of 9px for relative sizes + return Math.Max(9, value switch + { + CssConstantValue constLength when constLength.CssText == CssKeywords.Smaller => ancestorPixels / 1.2, + CssConstantValue constLength when constLength.CssText == CssKeywords.Larger => ancestorPixels * 1.2, + CssLengthValue { Type: CssLengthValue.Unit.Rem } length => length.Value * rootFontSize, + CssLengthValue { Type: CssLengthValue.Unit.Em } length => length.Value * ancestorPixels, + CssLengthValue { Type: CssLengthValue.Unit.Percent } length => length.Value / 100 * ancestorPixels, + _ => throw new InvalidOperationException(), + }); + } + } } } diff --git a/src/AngleSharp.Css/RenderTree/RenderValues.cs b/src/AngleSharp.Css/RenderTree/RenderValues.cs index 3f8d3c95..27c470e3 100644 --- a/src/AngleSharp.Css/RenderTree/RenderValues.cs +++ b/src/AngleSharp.Css/RenderTree/RenderValues.cs @@ -4,10 +4,10 @@ namespace AngleSharp.Css.RenderTree class RenderValues { - public Color Color { get; set; } + public CssColorValue Color { get; set; } - public Length Width { get; set; } + public CssLengthValue Width { get; set; } - public Length Height { get; set; } + public CssLengthValue Height { get; set; } } } diff --git a/src/AngleSharp.Css/RenderTree/TextRenderNode.cs b/src/AngleSharp.Css/RenderTree/TextRenderNode.cs index 9dc7f36d..9bfa8568 100644 --- a/src/AngleSharp.Css/RenderTree/TextRenderNode.cs +++ b/src/AngleSharp.Css/RenderTree/TextRenderNode.cs @@ -4,10 +4,17 @@ namespace AngleSharp.Css.RenderTree using System.Collections.Generic; using System.Linq; - class TextRenderNode : IRenderNode + sealed class TextRenderNode : IRenderNode { - public INode Ref { get; set; } + public TextRenderNode(INode reference) + { + Ref = reference; + } + + public INode Ref { get; } public IEnumerable Children => Enumerable.Empty(); + + public IRenderNode? Parent { get; set; } } } diff --git a/src/AngleSharp.Css/ValueConverters.cs b/src/AngleSharp.Css/ValueConverters.cs index 10dd13bf..40126355 100644 --- a/src/AngleSharp.Css/ValueConverters.cs +++ b/src/AngleSharp.Css/ValueConverters.cs @@ -61,12 +61,12 @@ static class ValueConverters /// /// Represents a converter for the auto keyword with no value. /// - public static IValueConverter Auto = new IdentifierValueConverter(CssKeywords.Auto, Length.Auto); + public static IValueConverter Auto = new IdentifierValueConverter(CssKeywords.Auto, CssLengthValue.Auto); /// /// Represents a converter for the content keyword with no value. /// - public static IValueConverter Content = new IdentifierValueConverter(CssKeywords.Content, Length.Content); + public static IValueConverter Content = new IdentifierValueConverter(CssKeywords.Content, CssLengthValue.Content); /// /// Represents a length object with line-width additions. @@ -84,29 +84,29 @@ static class ValueConverters /// Represents a length object. /// https://developer.mozilla.org/en-US/docs/Web/CSS/length /// - public static readonly IValueConverter OnlyLengthConverter = new StructValueConverter(UnitParser.ParseLength); + public static readonly IValueConverter OnlyLengthConverter = new StructValueConverter(UnitParser.ParseLength); /// /// Represents a resolution object. /// https://developer.mozilla.org/en-US/docs/Web/CSS/resolution /// - public static readonly IValueConverter OnlyResolutionConverter = new StructValueConverter(UnitParser.ParseResolution); + public static readonly IValueConverter OnlyResolutionConverter = new StructValueConverter(UnitParser.ParseResolution); /// /// Represents a time object. /// https://developer.mozilla.org/en-US/docs/Web/CSS/time /// - public static readonly IValueConverter OnlyTimeConverter = new StructValueConverter